|
@@ -1,5 +1,7 @@
|
|
use super::*;
|
|
use super::*;
|
|
use cargo_generate::{GenerateArgs, TemplatePath};
|
|
use cargo_generate::{GenerateArgs, TemplatePath};
|
|
|
|
+use cargo_metadata::Metadata;
|
|
|
|
+use std::path::Path;
|
|
|
|
|
|
pub(crate) static DEFAULT_TEMPLATE: &str = "gh:dioxuslabs/dioxus-template";
|
|
pub(crate) static DEFAULT_TEMPLATE: &str = "gh:dioxuslabs/dioxus-template";
|
|
|
|
|
|
@@ -8,26 +10,70 @@ pub(crate) static DEFAULT_TEMPLATE: &str = "gh:dioxuslabs/dioxus-template";
|
|
pub struct Create {
|
|
pub struct Create {
|
|
/// Project name (required when `--yes` is used)
|
|
/// Project name (required when `--yes` is used)
|
|
name: Option<String>,
|
|
name: Option<String>,
|
|
|
|
+
|
|
|
|
+ /// Generate the template directly at the given path.
|
|
|
|
+ #[arg(long, value_parser)]
|
|
|
|
+ destination: Option<PathBuf>,
|
|
|
|
+
|
|
|
|
+ /// Generate the template directly into the current dir. No subfolder will be created and no vcs is initialized.
|
|
|
|
+ #[arg(long, action)]
|
|
|
|
+ init: bool,
|
|
|
|
+
|
|
/// Template path
|
|
/// Template path
|
|
#[clap(default_value = DEFAULT_TEMPLATE, short, long)]
|
|
#[clap(default_value = DEFAULT_TEMPLATE, short, long)]
|
|
template: String,
|
|
template: String,
|
|
|
|
+
|
|
/// Pass <option>=<value> for the used template (e.g., `foo=bar`)
|
|
/// Pass <option>=<value> for the used template (e.g., `foo=bar`)
|
|
#[clap(short, long)]
|
|
#[clap(short, long)]
|
|
option: Vec<String>,
|
|
option: Vec<String>,
|
|
|
|
+
|
|
/// Specify a sub-template within the template repository to be used as the actual template
|
|
/// Specify a sub-template within the template repository to be used as the actual template
|
|
#[clap(long)]
|
|
#[clap(long)]
|
|
subtemplate: Option<String>,
|
|
subtemplate: Option<String>,
|
|
|
|
+
|
|
/// Skip user interaction by using the default values for the used template.
|
|
/// Skip user interaction by using the default values for the used template.
|
|
/// Default values can be overridden with `--option`
|
|
/// Default values can be overridden with `--option`
|
|
#[clap(short, long)]
|
|
#[clap(short, long)]
|
|
yes: bool,
|
|
yes: bool,
|
|
- // TODO: turn on/off cargo-generate's output (now is invisible)
|
|
|
|
- // #[clap(default_value = "false", short, long)]
|
|
|
|
- // silent: bool,
|
|
|
|
}
|
|
}
|
|
|
|
|
|
impl Create {
|
|
impl Create {
|
|
- pub fn create(self) -> Result<()> {
|
|
|
|
|
|
+ pub fn create(mut self) -> Result<()> {
|
|
|
|
+ let metadata = cargo_metadata::MetadataCommand::new().exec().ok();
|
|
|
|
+
|
|
|
|
+ // If we're getting pass a `.` name, that's actually a path
|
|
|
|
+ // We're actually running an init - we should clear the name
|
|
|
|
+ if self.name.as_deref() == Some(".") {
|
|
|
|
+ self.name = None;
|
|
|
|
+ self.init = true;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // A default destination is set for nameless projects
|
|
|
|
+ if self.name.is_none() {
|
|
|
|
+ self.destination = Some(PathBuf::from("."));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Split the name into path components
|
|
|
|
+ // such that dx new packages/app will create a directory called packages/app
|
|
|
|
+ let destination = self.destination.unwrap_or_else(|| {
|
|
|
|
+ let mut path = PathBuf::from(self.name.as_deref().unwrap());
|
|
|
|
+
|
|
|
|
+ if path.is_relative() {
|
|
|
|
+ path = std::env::current_dir().unwrap().join(path);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // split the path into the parent and the name
|
|
|
|
+ let parent = path.parent().unwrap();
|
|
|
|
+ let name = path.file_name().unwrap();
|
|
|
|
+ self.name = Some(name.to_str().unwrap().to_string());
|
|
|
|
+
|
|
|
|
+ // create the parent directory if it doesn't exist
|
|
|
|
+ std::fs::create_dir_all(parent).unwrap();
|
|
|
|
+
|
|
|
|
+ // And then the "destination" is the parent directory
|
|
|
|
+ parent.to_path_buf()
|
|
|
|
+ });
|
|
|
|
+
|
|
let args = GenerateArgs {
|
|
let args = GenerateArgs {
|
|
define: self.option,
|
|
define: self.option,
|
|
name: self.name,
|
|
name: self.name,
|
|
@@ -37,11 +83,20 @@ impl Create {
|
|
subfolder: self.subtemplate,
|
|
subfolder: self.subtemplate,
|
|
..Default::default()
|
|
..Default::default()
|
|
},
|
|
},
|
|
|
|
+ init: self.init,
|
|
|
|
+ destination: Some(destination),
|
|
|
|
+ vcs: if metadata.is_some() {
|
|
|
|
+ Some(cargo_generate::Vcs::None)
|
|
|
|
+ } else {
|
|
|
|
+ None
|
|
|
|
+ },
|
|
..Default::default()
|
|
..Default::default()
|
|
};
|
|
};
|
|
|
|
+
|
|
if self.yes && args.name.is_none() {
|
|
if self.yes && args.name.is_none() {
|
|
return Err("You have to provide the project's name when using `--yes` option.".into());
|
|
return Err("You have to provide the project's name when using `--yes` option.".into());
|
|
}
|
|
}
|
|
|
|
+
|
|
// https://github.com/console-rs/dialoguer/issues/294
|
|
// https://github.com/console-rs/dialoguer/issues/294
|
|
ctrlc::set_handler(move || {
|
|
ctrlc::set_handler(move || {
|
|
let _ = console::Term::stdout().show_cursor();
|
|
let _ = console::Term::stdout().show_cursor();
|
|
@@ -49,13 +104,32 @@ impl Create {
|
|
})
|
|
})
|
|
.expect("ctrlc::set_handler");
|
|
.expect("ctrlc::set_handler");
|
|
let path = cargo_generate::generate(args)?;
|
|
let path = cargo_generate::generate(args)?;
|
|
- post_create(&path)
|
|
|
|
|
|
+
|
|
|
|
+ post_create(&path, metadata)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-// being also used by `init`
|
|
|
|
-pub fn post_create(path: &PathBuf) -> Result<()> {
|
|
|
|
- // first run cargo fmt
|
|
|
|
|
|
+/// Post-creation actions for newly setup crates.
|
|
|
|
+// Also used by `init`.
|
|
|
|
+pub fn post_create(path: &Path, metadata: Option<Metadata>) -> Result<()> {
|
|
|
|
+ // 1. Add the new project to the workspace, if it exists.
|
|
|
|
+ // This must be executed first in order to run `cargo fmt` on the new project.
|
|
|
|
+ metadata.and_then(|metadata| {
|
|
|
|
+ let cargo_toml_path = &metadata.workspace_root.join("Cargo.toml");
|
|
|
|
+ let cargo_toml_str = std::fs::read_to_string(cargo_toml_path).ok()?;
|
|
|
|
+ let relative_path = path.strip_prefix(metadata.workspace_root).ok()?;
|
|
|
|
+
|
|
|
|
+ let mut cargo_toml: toml_edit::DocumentMut = cargo_toml_str.parse().ok()?;
|
|
|
|
+ cargo_toml
|
|
|
|
+ .get_mut("workspace")?
|
|
|
|
+ .get_mut("members")?
|
|
|
|
+ .as_array_mut()?
|
|
|
|
+ .push(relative_path.display().to_string());
|
|
|
|
+
|
|
|
|
+ std::fs::write(cargo_toml_path, cargo_toml.to_string()).ok()
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ // 2. Run `cargo fmt` on the new project.
|
|
let mut cmd = Command::new("cargo");
|
|
let mut cmd = Command::new("cargo");
|
|
let cmd = cmd.arg("fmt").current_dir(path);
|
|
let cmd = cmd.arg("fmt").current_dir(path);
|
|
let output = cmd.output().expect("failed to execute process");
|
|
let output = cmd.output().expect("failed to execute process");
|
|
@@ -65,7 +139,7 @@ pub fn post_create(path: &PathBuf) -> Result<()> {
|
|
tracing::error!("stderr: {}", String::from_utf8_lossy(&output.stderr));
|
|
tracing::error!("stderr: {}", String::from_utf8_lossy(&output.stderr));
|
|
}
|
|
}
|
|
|
|
|
|
- // then format the toml
|
|
|
|
|
|
+ // 3. Format the `Cargo.toml` and `Dioxus.toml` files.
|
|
let toml_paths = [path.join("Cargo.toml"), path.join("Dioxus.toml")];
|
|
let toml_paths = [path.join("Cargo.toml"), path.join("Dioxus.toml")];
|
|
for toml_path in &toml_paths {
|
|
for toml_path in &toml_paths {
|
|
let toml = std::fs::read_to_string(toml_path)?;
|
|
let toml = std::fs::read_to_string(toml_path)?;
|
|
@@ -85,7 +159,7 @@ pub fn post_create(path: &PathBuf) -> Result<()> {
|
|
file.write_all(new_string.as_bytes())?;
|
|
file.write_all(new_string.as_bytes())?;
|
|
}
|
|
}
|
|
|
|
|
|
- // remove any triple newlines from the readme
|
|
|
|
|
|
+ // 4. Remove any triple newlines from the readme.
|
|
let readme_path = path.join("README.md");
|
|
let readme_path = path.join("README.md");
|
|
let readme = std::fs::read_to_string(&readme_path)?;
|
|
let readme = std::fs::read_to_string(&readme_path)?;
|
|
let new_readme = remove_triple_newlines(&readme);
|
|
let new_readme = remove_triple_newlines(&readme);
|