Skip to content

Commit 7b392cd

Browse files
feat(cli): add non-interactive mode for init command (#1619)
* feat: add non interactive for init command * fix: fix none interactive for init command * fix: fix project manager command in init non interactive * fix: improve error message for missing path argument in non-interactive init --------- Co-authored-by: Mohamad Mohebifar <mo@codemod.com>
1 parent 9eea0b7 commit 7b392cd

File tree

1 file changed

+63
-23
lines changed

1 file changed

+63
-23
lines changed

crates/cli/src/commands/init.rs

Lines changed: 63 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ pub struct Command {
2121
#[arg(long)]
2222
project_type: Option<ProjectType>,
2323

24+
/// Package manager
25+
#[arg(long)]
26+
package_manager: Option<String>,
27+
2428
/// Target language
2529
#[arg(long)]
2630
language: Option<String>,
@@ -68,6 +72,7 @@ struct ProjectConfig {
6872
project_type: ProjectType,
6973
language: String,
7074
private: bool,
75+
package_manager: Option<String>,
7176
}
7277

7378
// Template constants using include_str!
@@ -114,18 +119,26 @@ static CHECKMARK: Emoji<'_, '_> = Emoji("✓ ", "");
114119

115120
pub fn handler(args: &Command) -> Result<()> {
116121
let (project_path, project_name) = if args.no_interactive {
117-
let project_path = args
118-
.path
119-
.clone()
120-
.unwrap_or_else(|| PathBuf::from("my-codemod"));
122+
let project_path = match args.path.clone() {
123+
Some(path) => path,
124+
None => return Err(anyhow!("Path argument is required")),
125+
};
121126

122-
let project_name = args.name.clone().unwrap_or_else(|| {
123-
project_path
124-
.file_name()
125-
.and_then(|n| n.to_str())
126-
.unwrap_or("my-codemod")
127-
.to_string()
128-
});
127+
let project_name = match args.name.clone() {
128+
Some(name) => name,
129+
None => {
130+
let file_name = project_path
131+
.file_name()
132+
.and_then(|n| n.to_str())
133+
.ok_or_else(|| {
134+
anyhow!(
135+
"Could not determine project name from path {}",
136+
project_path.display()
137+
)
138+
})?;
139+
file_name.to_string()
140+
}
141+
};
129142

130143
(project_path, project_name)
131144
} else {
@@ -158,23 +171,40 @@ pub fn handler(args: &Command) -> Result<()> {
158171
}
159172

160173
let config = if args.no_interactive {
174+
let project_type = args
175+
.project_type
176+
.clone()
177+
.ok_or_else(|| anyhow!("Project type is required --project-type"))?;
178+
let package_manager = match (&project_type, args.package_manager.clone()) {
179+
(ProjectType::AstGrepJs, Some(pm)) => Some(pm),
180+
(ProjectType::AstGrepJs, None) => {
181+
return Err(anyhow!(
182+
"--package-manager is required when --project-type is ast-grep-js"
183+
));
184+
}
185+
_ => None,
186+
};
161187
ProjectConfig {
162188
name: project_name,
163189
description: args
164190
.description
165191
.clone()
166-
.unwrap_or_else(|| "A new codemod".to_string()),
192+
.ok_or_else(|| anyhow!("Description is required --description"))?,
167193
author: args
168194
.author
169195
.clone()
170-
.unwrap_or_else(|| "Author <author@example.com>".to_string()),
171-
license: args.license.clone().unwrap_or_else(|| "MIT".to_string()),
172-
project_type: args.project_type.clone().unwrap_or(ProjectType::Shell),
196+
.ok_or_else(|| anyhow!("Author is required --author"))?,
197+
license: args
198+
.license
199+
.clone()
200+
.ok_or_else(|| anyhow!("License is required --license"))?,
201+
project_type: project_type.clone(),
173202
language: args
174203
.language
175204
.clone()
176-
.unwrap_or_else(|| "javascript".to_string()),
205+
.ok_or_else(|| anyhow!("Language is required --language"))?,
177206
private: args.private,
207+
package_manager,
178208
}
179209
} else {
180210
interactive_setup(&project_name, args)?
@@ -183,7 +213,7 @@ pub fn handler(args: &Command) -> Result<()> {
183213
create_project(&project_path, &config)?;
184214

185215
// Run post init commands
186-
run_post_init_commands(&project_path, &config)?;
216+
run_post_init_commands(&project_path, &config, args.no_interactive)?;
187217

188218
print_next_steps(&project_path, &config)?;
189219

@@ -259,6 +289,7 @@ fn interactive_setup(project_name: &str, args: &Command) -> Result<ProjectConfig
259289
project_type,
260290
language,
261291
private,
292+
package_manager: args.package_manager.clone(),
262293
})
263294
}
264295

@@ -471,14 +502,23 @@ fn create_readme(project_path: &Path, config: &ProjectConfig) -> Result<()> {
471502
Ok(())
472503
}
473504

474-
fn run_post_init_commands(project_path: &Path, config: &ProjectConfig) -> Result<()> {
505+
fn run_post_init_commands(
506+
project_path: &Path,
507+
config: &ProjectConfig,
508+
no_interactive: bool,
509+
) -> Result<()> {
475510
match config.project_type {
476511
ProjectType::AstGrepJs => {
477-
let package_manager = Select::new(
478-
"Which package manager would you like to use?",
479-
vec!["npm", "yarn", "pnpm"],
480-
)
481-
.prompt()?;
512+
let package_manager = if no_interactive {
513+
config.package_manager.clone().unwrap_or("npm".to_string())
514+
} else {
515+
Select::new(
516+
"Which package manager would you like to use?",
517+
vec!["npm", "yarn", "pnpm"],
518+
)
519+
.prompt()?
520+
.to_string()
521+
};
482522

483523
let output = ProcessCommand::new(package_manager)
484524
.arg("install")

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy