diff options
author | Geert-Jan Zwiers <geertjanzwiers@protonmail.com> | 2023-01-07 21:22:09 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-01-07 15:22:09 -0500 |
commit | 84ef26ac9b5f0e1199d77837cd97cb203baa8729 (patch) | |
tree | eb8f3422d397724d004dba0e9667b3f6c2b49a57 /cli/args | |
parent | fac64478157ee563b185edb5734688e4523df3a1 (diff) |
refactor(cli/tools): move flag and config logic to CliOptions (#17008)
Co-authored-by: David Sherret <dsherret@gmail.com>
Diffstat (limited to 'cli/args')
-rw-r--r-- | cli/args/config_file.rs | 72 | ||||
-rw-r--r-- | cli/args/flags.rs | 363 | ||||
-rw-r--r-- | cli/args/mod.rs | 368 |
3 files changed, 604 insertions, 199 deletions
diff --git a/cli/args/config_file.rs b/cli/args/config_file.rs index 0c0e5d2fa..47f8e9daa 100644 --- a/cli/args/config_file.rs +++ b/cli/args/config_file.rs @@ -295,43 +295,45 @@ impl SerializedFilesConfig { include: self .include .into_iter() - .map(|p| config_dir.join(&p)) - .collect::<Result<Vec<ModuleSpecifier>, _>>()?, + .map(|p| { + let url = config_dir.join(&p)?; + specifier_to_file_path(&url) + }) + .collect::<Result<Vec<_>, _>>()?, exclude: self .exclude .into_iter() - .map(|p| config_dir.join(&p)) - .collect::<Result<Vec<ModuleSpecifier>, _>>()?, + .map(|p| { + let url = config_dir.join(&p)?; + specifier_to_file_path(&url) + }) + .collect::<Result<Vec<_>, _>>()?, }) } } -#[derive(Clone, Debug, Default)] +#[derive(Clone, Debug, Default, Eq, PartialEq)] pub struct FilesConfig { - pub include: Vec<ModuleSpecifier>, - pub exclude: Vec<ModuleSpecifier>, + pub include: Vec<PathBuf>, + pub exclude: Vec<PathBuf>, } impl FilesConfig { /// Gets if the provided specifier is allowed based on the includes /// and excludes in the configuration file. pub fn matches_specifier(&self, specifier: &ModuleSpecifier) -> bool { + let file_path = match specifier_to_file_path(specifier) { + Ok(file_path) => file_path, + Err(_) => return false, + }; // Skip files which is in the exclude list. - let specifier_text = specifier.as_str(); - if self - .exclude - .iter() - .any(|i| specifier_text.starts_with(i.as_str())) - { + if self.exclude.iter().any(|i| file_path.starts_with(i)) { return false; } // Ignore files not in the include list if it's not empty. self.include.is_empty() - || self - .include - .iter() - .any(|i| specifier_text.starts_with(i.as_str())) + || self.include.iter().any(|i| file_path.starts_with(i)) } } @@ -663,6 +665,16 @@ impl ConfigFile { self.json.import_map.clone() } + pub fn to_fmt_config(&self) -> Result<Option<FmtConfig>, AnyError> { + if let Some(config) = self.json.fmt.clone() { + let fmt_config: SerializedFmtConfig = serde_json::from_value(config) + .context("Failed to parse \"fmt\" configuration")?; + Ok(Some(fmt_config.into_resolved(&self.specifier)?)) + } else { + Ok(None) + } + } + pub fn to_lint_config(&self) -> Result<Option<LintConfig>, AnyError> { if let Some(config) = self.json.lint.clone() { let lint_config: SerializedLintConfig = serde_json::from_value(config) @@ -767,16 +779,6 @@ impl ConfigFile { }) } - pub fn to_fmt_config(&self) -> Result<Option<FmtConfig>, AnyError> { - if let Some(config) = self.json.fmt.clone() { - let fmt_config: SerializedFmtConfig = serde_json::from_value(config) - .context("Failed to parse \"fmt\" configuration")?; - Ok(Some(fmt_config.into_resolved(&self.specifier)?)) - } else { - Ok(None) - } - } - pub fn resolve_tasks_config( &self, ) -> Result<BTreeMap<String, String>, AnyError> { @@ -1068,13 +1070,10 @@ mod tests { .to_lint_config() .expect("error parsing lint object") .expect("lint object should be defined"); - assert_eq!( - lint_config.files.include, - vec![config_dir.join("src/").unwrap()] - ); + assert_eq!(lint_config.files.include, vec![PathBuf::from("/deno/src/")]); assert_eq!( lint_config.files.exclude, - vec![config_dir.join("src/testdata/").unwrap()] + vec![PathBuf::from("/deno/src/testdata/")] ); assert_eq!( lint_config.rules.include, @@ -1090,13 +1089,10 @@ mod tests { .to_fmt_config() .expect("error parsing fmt object") .expect("fmt object should be defined"); - assert_eq!( - fmt_config.files.include, - vec![config_dir.join("src/").unwrap()] - ); + assert_eq!(fmt_config.files.include, vec![PathBuf::from("/deno/src/")]); assert_eq!( fmt_config.files.exclude, - vec![config_dir.join("src/testdata/").unwrap()] + vec![PathBuf::from("/deno/src/testdata/")], ); assert_eq!(fmt_config.options.use_tabs, Some(true)); assert_eq!(fmt_config.options.line_width, Some(80)); @@ -1190,6 +1186,8 @@ mod tests { let expected_exclude = ModuleSpecifier::from_file_path( testdata.join("fmt/with_config/subdir/b.ts"), ) + .unwrap() + .to_file_path() .unwrap(); assert_eq!(fmt_config.files.exclude, vec![expected_exclude]); diff --git a/cli/args/flags.rs b/cli/args/flags.rs index bae6bb0d5..b5753c00b 100644 --- a/cli/args/flags.rs +++ b/cli/args/flags.rs @@ -6,8 +6,6 @@ use clap::ColorChoice; use clap::Command; use clap::ValueHint; use deno_core::error::AnyError; -use deno_core::serde::Deserialize; -use deno_core::serde::Serialize; use deno_core::url::Url; use deno_runtime::permissions::parse_sys_kind; use log::debug; @@ -46,30 +44,35 @@ static SHORT_VERSION: Lazy<String> = Lazy::new(|| { .to_string() }); -#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)] -pub struct BenchFlags { +#[derive(Clone, Debug, Default, Eq, PartialEq)] +pub struct FileFlags { pub ignore: Vec<PathBuf>, - pub include: Option<Vec<String>>, + pub include: Vec<PathBuf>, +} + +#[derive(Clone, Debug, Default, Eq, PartialEq)] +pub struct BenchFlags { + pub files: FileFlags, pub filter: Option<String>, } -#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct BundleFlags { pub source_file: String, pub out_file: Option<PathBuf>, } -#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct CacheFlags { pub files: Vec<String>, } -#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct CheckFlags { pub files: Vec<String>, } -#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct CompileFlags { pub source_file: String, pub output: Option<PathBuf>, @@ -77,12 +80,12 @@ pub struct CompileFlags { pub target: Option<String>, } -#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct CompletionsFlags { pub buf: Box<[u8]>, } -#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct CoverageFlags { pub files: Vec<PathBuf>, pub output: Option<PathBuf>, @@ -92,7 +95,7 @@ pub struct CoverageFlags { pub lcov: bool, } -#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct DocFlags { pub private: bool, pub json: bool, @@ -100,19 +103,18 @@ pub struct DocFlags { pub filter: Option<String>, } -#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct EvalFlags { pub print: bool, pub code: String, pub ext: String, } -#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct FmtFlags { pub check: bool, - pub files: Vec<PathBuf>, - pub ignore: Vec<PathBuf>, pub ext: String, + pub files: FileFlags, pub use_tabs: Option<bool>, pub line_width: Option<NonZeroU32>, pub indent_width: Option<NonZeroU8>, @@ -120,18 +122,18 @@ pub struct FmtFlags { pub prose_wrap: Option<String>, } -#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct InitFlags { pub dir: Option<String>, } -#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct InfoFlags { pub json: bool, pub file: Option<String>, } -#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct InstallFlags { pub module_url: String, pub args: Vec<String>, @@ -140,16 +142,15 @@ pub struct InstallFlags { pub force: bool, } -#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct UninstallFlags { pub name: String, pub root: Option<PathBuf>, } -#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct LintFlags { - pub files: Vec<PathBuf>, - pub ignore: Vec<PathBuf>, + pub files: FileFlags, pub rules: bool, pub maybe_rules_tags: Option<Vec<String>>, pub maybe_rules_include: Option<Vec<String>>, @@ -158,14 +159,14 @@ pub struct LintFlags { pub compact: bool, } -#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct ReplFlags { pub eval_files: Option<Vec<String>>, pub eval: Option<String>, pub is_default_command: bool, } -#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct RunFlags { pub script: String, } @@ -176,27 +177,26 @@ impl RunFlags { } } -#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct TaskFlags { pub cwd: Option<String>, pub task: String, } -#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)] +#[derive(Clone, Debug, Default, Eq, PartialEq)] pub struct TestFlags { - pub ignore: Vec<PathBuf>, pub doc: bool, pub no_run: bool, pub fail_fast: Option<NonZeroUsize>, + pub files: FileFlags, pub allow_none: bool, - pub include: Vec<String>, pub filter: Option<String>, pub shuffle: Option<u64>, - pub concurrent_jobs: NonZeroUsize, + pub concurrent_jobs: Option<NonZeroUsize>, pub trace_ops: bool, } -#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct UpgradeFlags { pub dry_run: bool, pub force: bool, @@ -205,14 +205,14 @@ pub struct UpgradeFlags { pub output: Option<PathBuf>, } -#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct VendorFlags { pub specifiers: Vec<String>, pub output_path: Option<PathBuf>, pub force: bool, } -#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)] +#[derive(Clone, Debug, Eq, PartialEq)] pub enum DenoSubcommand { Bench(BenchFlags), Bundle(BundleFlags), @@ -455,9 +455,9 @@ impl Flags { pub fn config_path_args(&self) -> Option<Vec<PathBuf>> { use DenoSubcommand::*; if let Fmt(FmtFlags { files, .. }) = &self.subcommand { - Some(files.clone()) + Some(files.include.clone()) } else if let Lint(LintFlags { files, .. }) = &self.subcommand { - Some(files.clone()) + Some(files.include.clone()) } else if let Run(RunFlags { script }) = &self.subcommand { if let Ok(module_specifier) = deno_core::resolve_url_or_path(script) { if module_specifier.scheme() == "file" @@ -2298,20 +2298,19 @@ fn bench_parse(flags: &mut Flags, matches: &clap::ArgMatches) { } let include = if matches.is_present("files") { - let files: Vec<String> = matches + let files = matches .values_of("files") .unwrap() - .map(String::from) + .map(PathBuf::from) .collect(); - Some(files) + files } else { - None + Vec::new() }; watch_arg_parse(flags, matches, false); flags.subcommand = DenoSubcommand::Bench(BenchFlags { - include, - ignore, + files: FileFlags { include, ignore }, filter, }); } @@ -2493,7 +2492,7 @@ fn fmt_parse(flags: &mut Flags, matches: &clap::ArgMatches) { config_args_parse(flags, matches); watch_arg_parse(flags, matches, false); - let files = match matches.values_of("files") { + let include = match matches.values_of("files") { Some(f) => f.map(PathBuf::from).collect(), None => vec![], }; @@ -2544,8 +2543,7 @@ fn fmt_parse(flags: &mut Flags, matches: &clap::ArgMatches) { flags.subcommand = DenoSubcommand::Fmt(FmtFlags { check: matches.is_present("check"), ext, - files, - ignore, + files: FileFlags { include, ignore }, use_tabs, line_width, indent_width, @@ -2647,12 +2645,15 @@ fn lint_parse(flags: &mut Flags, matches: &clap::ArgMatches) { let json = matches.is_present("json"); let compact = matches.is_present("compact"); flags.subcommand = DenoSubcommand::Lint(LintFlags { - files, + files: FileFlags { + include: files, + ignore, + }, rules, maybe_rules_tags, maybe_rules_include, maybe_rules_exclude, - ignore, + json, compact, }); @@ -2811,12 +2812,9 @@ fn test_parse(flags: &mut Flags, matches: &clap::ArgMatches) { let concurrent_jobs = if matches.is_present("parallel") { if let Ok(value) = env::var("DENO_JOBS") { - value - .parse::<NonZeroUsize>() - .unwrap_or(NonZeroUsize::new(1).unwrap()) + value.parse::<NonZeroUsize>().ok() } else { - std::thread::available_parallelism() - .unwrap_or(NonZeroUsize::new(1).unwrap()) + std::thread::available_parallelism().ok() } } else if matches.is_present("jobs") { // We can't change this to use the log crate because its not configured @@ -2828,20 +2826,19 @@ fn test_parse(flags: &mut Flags, matches: &clap::ArgMatches) { crate::colors::yellow("Warning: --jobs flag is deprecated. Use the --parallel flag with possibly the 'DENO_JOBS' environment variable."), ); if let Some(value) = matches.value_of("jobs") { - value.parse().unwrap() + Some(value.parse().unwrap()) } else { - std::thread::available_parallelism() - .unwrap_or(NonZeroUsize::new(1).unwrap()) + std::thread::available_parallelism().ok() } } else { - NonZeroUsize::new(1).unwrap() + None }; - let include: Vec<String> = if matches.is_present("files") { + let include = if matches.is_present("files") { matches .values_of("files") .unwrap() - .map(String::from) + .map(PathBuf::from) .collect::<Vec<_>>() } else { Vec::new() @@ -2853,8 +2850,7 @@ fn test_parse(flags: &mut Flags, matches: &clap::ArgMatches) { no_run, doc, fail_fast, - include, - ignore, + files: FileFlags { include, ignore }, filter, shuffle, allow_none, @@ -3267,6 +3263,7 @@ mod tests { fn global_flags() { #[rustfmt::skip] let r = flags_from_vec(svec!["deno", "--unstable", "--log-level", "debug", "--quiet", "run", "script.ts"]); + let flags = r.unwrap(); assert_eq!( flags, @@ -3562,13 +3559,15 @@ mod tests { r.unwrap(), Flags { subcommand: DenoSubcommand::Fmt(FmtFlags { - ignore: vec![], check: false, - files: vec![ - PathBuf::from("script_1.ts"), - PathBuf::from("script_2.ts") - ], ext: "ts".to_string(), + files: FileFlags { + include: vec![ + PathBuf::from("script_1.ts"), + PathBuf::from("script_2.ts") + ], + ignore: vec![], + }, use_tabs: None, line_width: None, indent_width: None, @@ -3584,10 +3583,12 @@ mod tests { r.unwrap(), Flags { subcommand: DenoSubcommand::Fmt(FmtFlags { - ignore: vec![], check: true, - files: vec![], ext: "ts".to_string(), + files: FileFlags { + include: vec![], + ignore: vec![], + }, use_tabs: None, line_width: None, indent_width: None, @@ -3603,10 +3604,12 @@ mod tests { r.unwrap(), Flags { subcommand: DenoSubcommand::Fmt(FmtFlags { - ignore: vec![], check: false, - files: vec![], ext: "ts".to_string(), + files: FileFlags { + include: vec![], + ignore: vec![], + }, use_tabs: None, line_width: None, indent_width: None, @@ -3622,10 +3625,12 @@ mod tests { r.unwrap(), Flags { subcommand: DenoSubcommand::Fmt(FmtFlags { - ignore: vec![], check: false, - files: vec![], ext: "ts".to_string(), + files: FileFlags { + include: vec![], + ignore: vec![], + }, use_tabs: None, line_width: None, indent_width: None, @@ -3643,10 +3648,12 @@ mod tests { r.unwrap(), Flags { subcommand: DenoSubcommand::Fmt(FmtFlags { - ignore: vec![], check: false, - files: vec![], ext: "ts".to_string(), + files: FileFlags { + include: vec![], + ignore: vec![], + }, use_tabs: None, line_width: None, indent_width: None, @@ -3671,10 +3678,12 @@ mod tests { r.unwrap(), Flags { subcommand: DenoSubcommand::Fmt(FmtFlags { - ignore: vec![PathBuf::from("bar.js")], check: true, - files: vec![PathBuf::from("foo.ts")], ext: "ts".to_string(), + files: FileFlags { + include: vec![PathBuf::from("foo.ts")], + ignore: vec![PathBuf::from("bar.js")], + }, use_tabs: None, line_width: None, indent_width: None, @@ -3691,10 +3700,12 @@ mod tests { r.unwrap(), Flags { subcommand: DenoSubcommand::Fmt(FmtFlags { - ignore: vec![], check: false, - files: vec![], ext: "ts".to_string(), + files: FileFlags { + include: vec![], + ignore: vec![], + }, use_tabs: None, line_width: None, indent_width: None, @@ -3718,10 +3729,12 @@ mod tests { r.unwrap(), Flags { subcommand: DenoSubcommand::Fmt(FmtFlags { - ignore: vec![], check: false, - files: vec![PathBuf::from("foo.ts")], ext: "ts".to_string(), + files: FileFlags { + include: vec![PathBuf::from("foo.ts")], + ignore: vec![], + }, use_tabs: None, line_width: None, indent_width: None, @@ -3750,10 +3763,12 @@ mod tests { r.unwrap(), Flags { subcommand: DenoSubcommand::Fmt(FmtFlags { - ignore: vec![], check: false, - files: vec![], ext: "ts".to_string(), + files: FileFlags { + include: vec![], + ignore: vec![], + }, use_tabs: Some(true), line_width: Some(NonZeroU32::new(60).unwrap()), indent_width: Some(NonZeroU8::new(4).unwrap()), @@ -3772,17 +3787,19 @@ mod tests { r.unwrap(), Flags { subcommand: DenoSubcommand::Lint(LintFlags { - files: vec![ - PathBuf::from("script_1.ts"), - PathBuf::from("script_2.ts") - ], + files: FileFlags { + include: vec![ + PathBuf::from("script_1.ts"), + PathBuf::from("script_2.ts") + ], + ignore: vec![], + }, rules: false, maybe_rules_tags: None, maybe_rules_include: None, maybe_rules_exclude: None, json: false, compact: false, - ignore: vec![], }), ..Flags::default() } @@ -3799,17 +3816,19 @@ mod tests { r.unwrap(), Flags { subcommand: DenoSubcommand::Lint(LintFlags { - files: vec![ - PathBuf::from("script_1.ts"), - PathBuf::from("script_2.ts") - ], + files: FileFlags { + include: vec![ + PathBuf::from("script_1.ts"), + PathBuf::from("script_2.ts") + ], + ignore: vec![], + }, rules: false, maybe_rules_tags: None, maybe_rules_include: None, maybe_rules_exclude: None, json: false, compact: false, - ignore: vec![], }), watch: Some(vec![]), ..Flags::default() @@ -3828,17 +3847,19 @@ mod tests { r.unwrap(), Flags { subcommand: DenoSubcommand::Lint(LintFlags { - files: vec![ - PathBuf::from("script_1.ts"), - PathBuf::from("script_2.ts") - ], + files: FileFlags { + include: vec![ + PathBuf::from("script_1.ts"), + PathBuf::from("script_2.ts") + ], + ignore: vec![], + }, rules: false, maybe_rules_tags: None, maybe_rules_include: None, maybe_rules_exclude: None, json: false, compact: false, - ignore: vec![], }), watch: Some(vec![]), no_clear_screen: true, @@ -3852,17 +3873,19 @@ mod tests { r.unwrap(), Flags { subcommand: DenoSubcommand::Lint(LintFlags { - files: vec![], + files: FileFlags { + include: vec![], + ignore: vec![ + PathBuf::from("script_1.ts"), + PathBuf::from("script_2.ts") + ], + }, rules: false, maybe_rules_tags: None, maybe_rules_include: None, maybe_rules_exclude: None, json: false, compact: false, - ignore: vec![ - PathBuf::from("script_1.ts"), - PathBuf::from("script_2.ts") - ], }), ..Flags::default() } @@ -3873,14 +3896,16 @@ mod tests { r.unwrap(), Flags { subcommand: DenoSubcommand::Lint(LintFlags { - files: vec![], + files: FileFlags { + include: vec![], + ignore: vec![], + }, rules: true, maybe_rules_tags: None, maybe_rules_include: None, maybe_rules_exclude: None, json: false, compact: false, - ignore: vec![], }), ..Flags::default() } @@ -3897,14 +3922,16 @@ mod tests { r.unwrap(), Flags { subcommand: DenoSubcommand::Lint(LintFlags { - files: vec![], + files: FileFlags { + include: vec![], + ignore: vec![], + }, rules: false, maybe_rules_tags: Some(svec![""]), maybe_rules_include: Some(svec!["ban-untagged-todo", "no-undef"]), maybe_rules_exclude: Some(svec!["no-const-assign"]), json: false, compact: false, - ignore: vec![], }), ..Flags::default() } @@ -3915,14 +3942,16 @@ mod tests { r.unwrap(), Flags { subcommand: DenoSubcommand::Lint(LintFlags { - files: vec![PathBuf::from("script_1.ts")], + files: FileFlags { + include: vec![PathBuf::from("script_1.ts")], + ignore: vec![], + }, rules: false, maybe_rules_tags: None, maybe_rules_include: None, maybe_rules_exclude: None, json: true, compact: false, - ignore: vec![], }), ..Flags::default() } @@ -3940,14 +3969,16 @@ mod tests { r.unwrap(), Flags { subcommand: DenoSubcommand::Lint(LintFlags { - files: vec![PathBuf::from("script_1.ts")], + files: FileFlags { + include: vec![PathBuf::from("script_1.ts")], + ignore: vec![], + }, rules: false, maybe_rules_tags: None, maybe_rules_include: None, maybe_rules_exclude: None, json: true, compact: false, - ignore: vec![], }), config_flag: ConfigFlag::Path("Deno.jsonc".to_string()), ..Flags::default() @@ -3966,14 +3997,16 @@ mod tests { r.unwrap(), Flags { subcommand: DenoSubcommand::Lint(LintFlags { - files: vec![PathBuf::from("script_1.ts")], + files: FileFlags { + include: vec![PathBuf::from("script_1.ts")], + ignore: vec![], + }, rules: false, maybe_rules_tags: None, maybe_rules_include: None, maybe_rules_exclude: None, json: false, compact: true, - ignore: vec![], }), config_flag: ConfigFlag::Path("Deno.jsonc".to_string()), ..Flags::default() @@ -5510,10 +5543,12 @@ mod tests { fail_fast: None, filter: Some("- foo".to_string()), allow_none: true, - include: svec!["dir1/", "dir2/"], - ignore: vec![], + files: FileFlags { + include: vec![PathBuf::from("dir1/"), PathBuf::from("dir2/")], + ignore: vec![], + }, shuffle: None, - concurrent_jobs: NonZeroUsize::new(1).unwrap(), + concurrent_jobs: None, trace_ops: true, }), unstable: true, @@ -5582,9 +5617,11 @@ mod tests { filter: None, allow_none: false, shuffle: None, - include: vec![], - ignore: vec![], - concurrent_jobs: NonZeroUsize::new(4).unwrap(), + files: FileFlags { + include: vec![], + ignore: vec![], + }, + concurrent_jobs: Some(NonZeroUsize::new(4).unwrap()), trace_ops: false, }), type_check_mode: TypeCheckMode::Local, @@ -5610,9 +5647,11 @@ mod tests { filter: None, allow_none: false, shuffle: None, - include: vec![], - ignore: vec![], - concurrent_jobs: NonZeroUsize::new(1).unwrap(), + files: FileFlags { + include: vec![], + ignore: vec![], + }, + concurrent_jobs: None, trace_ops: false, }), type_check_mode: TypeCheckMode::Local, @@ -5642,9 +5681,11 @@ mod tests { filter: None, allow_none: false, shuffle: None, - include: vec![], - ignore: vec![], - concurrent_jobs: NonZeroUsize::new(1).unwrap(), + files: FileFlags { + include: vec![], + ignore: vec![], + }, + concurrent_jobs: None, trace_ops: false, }), no_prompt: true, @@ -5668,9 +5709,11 @@ mod tests { filter: None, allow_none: false, shuffle: Some(1), - include: vec![], - ignore: vec![], - concurrent_jobs: NonZeroUsize::new(1).unwrap(), + files: FileFlags { + include: vec![], + ignore: vec![], + }, + concurrent_jobs: None, trace_ops: false, }), no_prompt: true, @@ -5694,9 +5737,38 @@ mod tests { filter: None, allow_none: false, shuffle: None, - include: vec![], - ignore: vec![], - concurrent_jobs: NonZeroUsize::new(1).unwrap(), + files: FileFlags { + include: vec![], + ignore: vec![], + }, + concurrent_jobs: None, + trace_ops: false, + }), + no_prompt: true, + type_check_mode: TypeCheckMode::Local, + watch: Some(vec![]), + ..Flags::default() + } + ); + } + #[test] + fn test_watch_explicit_cwd() { + let r = flags_from_vec(svec!["deno", "test", "--watch", "./"]); + assert_eq!( + r.unwrap(), + Flags { + subcommand: DenoSubcommand::Test(TestFlags { + no_run: false, + doc: false, + fail_fast: None, + filter: None, + allow_none: false, + shuffle: None, + files: FileFlags { + include: vec![PathBuf::from("./")], + ignore: vec![], + }, + concurrent_jobs: None, trace_ops: false, }), no_prompt: true, @@ -5721,9 +5793,11 @@ mod tests { filter: None, allow_none: false, shuffle: None, - include: vec![], - ignore: vec![], - concurrent_jobs: NonZeroUsize::new(1).unwrap(), + files: FileFlags { + include: vec![], + ignore: vec![], + }, + concurrent_jobs: None, trace_ops: false, }), watch: Some(vec![]), @@ -6372,8 +6446,10 @@ mod tests { Flags { subcommand: DenoSubcommand::Bench(BenchFlags { filter: Some("- foo".to_string()), - include: Some(svec!["dir1/", "dir2/"]), - ignore: vec![], + files: FileFlags { + include: vec![PathBuf::from("dir1/"), PathBuf::from("dir2/")], + ignore: vec![], + }, }), unstable: true, type_check_mode: TypeCheckMode::Local, @@ -6387,6 +6463,27 @@ mod tests { } #[test] + fn bench_watch() { + let r = flags_from_vec(svec!["deno", "bench", "--watch"]); + assert_eq!( + r.unwrap(), + Flags { + subcommand: DenoSubcommand::Bench(BenchFlags { + filter: None, + files: FileFlags { + include: vec![], + ignore: vec![], + }, + }), + no_prompt: true, + type_check_mode: TypeCheckMode::Local, + watch: Some(vec![]), + ..Flags::default() + } + ); + } + + #[test] fn run_with_check() { let r = flags_from_vec(svec!["deno", "run", "--check", "script.ts",]); assert_eq!( diff --git a/cli/args/mod.rs b/cli/args/mod.rs index d677cf832..0f60d09c3 100644 --- a/cli/args/mod.rs +++ b/cli/args/mod.rs @@ -10,23 +10,15 @@ pub use config_file::BenchConfig; pub use config_file::CompilerOptions; pub use config_file::ConfigFile; pub use config_file::EmitConfigOptions; -pub use config_file::FmtConfig; +pub use config_file::FilesConfig; pub use config_file::FmtOptionsConfig; -pub use config_file::IgnoredCompilerOptions; pub use config_file::JsxImportSourceConfig; -pub use config_file::LintConfig; pub use config_file::LintRulesConfig; -pub use config_file::MaybeImportsResult; pub use config_file::ProseWrap; -pub use config_file::TestConfig; pub use config_file::TsConfig; pub use config_file::TsConfigForEmit; pub use config_file::TsConfigType; pub use config_file::TsTypeLib; -use deno_runtime::deno_tls::rustls; -use deno_runtime::deno_tls::rustls_native_certs::load_native_certs; -use deno_runtime::deno_tls::rustls_pemfile; -use deno_runtime::deno_tls::webpki_roots; pub use flags::*; pub use lockfile::Lockfile; pub use lockfile::LockfileError; @@ -40,13 +32,18 @@ use deno_core::normalize_path; use deno_core::parking_lot::Mutex; use deno_core::url::Url; use deno_runtime::colors; +use deno_runtime::deno_tls::rustls; use deno_runtime::deno_tls::rustls::RootCertStore; +use deno_runtime::deno_tls::rustls_native_certs::load_native_certs; +use deno_runtime::deno_tls::rustls_pemfile; +use deno_runtime::deno_tls::webpki_roots; use deno_runtime::inspector_server::InspectorServer; use deno_runtime::permissions::PermissionsOptions; use std::collections::BTreeMap; use std::env; use std::io::BufReader; use std::net::SocketAddr; +use std::num::NonZeroUsize; use std::path::PathBuf; use std::sync::Arc; @@ -54,6 +51,11 @@ use crate::cache::DenoDir; use crate::util::fs::canonicalize_path_maybe_not_exists; use crate::version; +use self::config_file::FmtConfig; +use self::config_file::LintConfig; +use self::config_file::MaybeImportsResult; +use self::config_file::TestConfig; + /// Indicates how cached source files should be handled. #[derive(Debug, Clone, Eq, PartialEq)] pub enum CacheSetting { @@ -95,6 +97,274 @@ impl CacheSetting { } } +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct BenchOptions { + pub files: FilesConfig, + pub filter: Option<String>, +} + +impl BenchOptions { + pub fn resolve( + maybe_bench_config: Option<BenchConfig>, + maybe_bench_flags: Option<BenchFlags>, + ) -> Result<Self, AnyError> { + let bench_flags = maybe_bench_flags.unwrap_or_default(); + Ok(Self { + files: resolve_files( + maybe_bench_config.map(|c| c.files), + Some(bench_flags.files), + ), + filter: bench_flags.filter, + }) + } +} + +#[derive(Clone, Debug, Default)] +pub struct FmtOptions { + pub is_stdin: bool, + pub check: bool, + pub ext: String, + pub options: FmtOptionsConfig, + pub files: FilesConfig, +} + +impl FmtOptions { + pub fn resolve( + maybe_fmt_config: Option<FmtConfig>, + mut maybe_fmt_flags: Option<FmtFlags>, + ) -> Result<Self, AnyError> { + let is_stdin = if let Some(fmt_flags) = maybe_fmt_flags.as_mut() { + let args = &mut fmt_flags.files.include; + if args.len() == 1 && args[0].to_string_lossy() == "-" { + args.pop(); // remove the "-" arg + true + } else { + false + } + } else { + false + }; + let (maybe_config_options, maybe_config_files) = + maybe_fmt_config.map(|c| (c.options, c.files)).unzip(); + + Ok(Self { + is_stdin, + check: maybe_fmt_flags.as_ref().map(|f| f.check).unwrap_or(false), + ext: maybe_fmt_flags + .as_ref() + .map(|f| f.ext.to_string()) + .unwrap_or_else(|| "ts".to_string()), + options: resolve_fmt_options( + maybe_fmt_flags.as_ref(), + maybe_config_options, + ), + files: resolve_files( + maybe_config_files, + maybe_fmt_flags.map(|f| f.files), + ), + }) + } +} + +fn resolve_fmt_options( + fmt_flags: Option<&FmtFlags>, + options: Option<FmtOptionsConfig>, +) -> FmtOptionsConfig { + let mut options = options.unwrap_or_default(); + + if let Some(fmt_flags) = fmt_flags { + if let Some(use_tabs) = fmt_flags.use_tabs { + options.use_tabs = Some(use_tabs); + } + + if let Some(line_width) = fmt_flags.line_width { + options.line_width = Some(line_width.get()); + } + + if let Some(indent_width) = fmt_flags.indent_width { + options.indent_width = Some(indent_width.get()); + } + + if let Some(single_quote) = fmt_flags.single_quote { + options.single_quote = Some(single_quote); + } + + if let Some(prose_wrap) = &fmt_flags.prose_wrap { + options.prose_wrap = Some(match prose_wrap.as_str() { + "always" => ProseWrap::Always, + "never" => ProseWrap::Never, + "preserve" => ProseWrap::Preserve, + // validators in `flags.rs` makes other values unreachable + _ => unreachable!(), + }); + } + } + + options +} + +#[derive(Clone)] +pub struct TestOptions { + pub files: FilesConfig, + pub doc: bool, + pub no_run: bool, + pub fail_fast: Option<NonZeroUsize>, + pub allow_none: bool, + pub filter: Option<String>, + pub shuffle: Option<u64>, + pub concurrent_jobs: NonZeroUsize, + pub trace_ops: bool, +} + +impl TestOptions { + pub fn resolve( + maybe_test_config: Option<TestConfig>, + maybe_test_flags: Option<TestFlags>, + ) -> Result<Self, AnyError> { + let test_flags = maybe_test_flags.unwrap_or_default(); + + Ok(Self { + files: resolve_files( + maybe_test_config.map(|c| c.files), + Some(test_flags.files), + ), + allow_none: test_flags.allow_none, + concurrent_jobs: test_flags + .concurrent_jobs + .unwrap_or_else(|| NonZeroUsize::new(1).unwrap()), + doc: test_flags.doc, + fail_fast: test_flags.fail_fast, + filter: test_flags.filter, + no_run: test_flags.no_run, + shuffle: test_flags.shuffle, + trace_ops: test_flags.trace_ops, + }) + } +} + +#[derive(Clone, Debug)] +pub enum LintReporterKind { + Pretty, + Json, + Compact, +} + +impl Default for LintReporterKind { + fn default() -> Self { + LintReporterKind::Pretty + } +} + +#[derive(Clone, Debug, Default)] +pub struct LintOptions { + pub rules: LintRulesConfig, + pub files: FilesConfig, + pub is_stdin: bool, + pub reporter_kind: LintReporterKind, +} + +impl LintOptions { + pub fn resolve( + maybe_lint_config: Option<LintConfig>, + mut maybe_lint_flags: Option<LintFlags>, + ) -> Result<Self, AnyError> { + let is_stdin = if let Some(lint_flags) = maybe_lint_flags.as_mut() { + let args = &mut lint_flags.files.include; + if args.len() == 1 && args[0].to_string_lossy() == "-" { + args.pop(); // remove the "-" arg + true + } else { + false + } + } else { + false + }; + + let mut maybe_reporter_kind = + maybe_lint_flags.as_ref().and_then(|lint_flags| { + if lint_flags.json { + Some(LintReporterKind::Json) + } else if lint_flags.compact { + Some(LintReporterKind::Compact) + } else { + None + } + }); + + if maybe_reporter_kind.is_none() { + // Flag not set, so try to get lint reporter from the config file. + if let Some(lint_config) = &maybe_lint_config { + maybe_reporter_kind = match lint_config.report.as_deref() { + Some("json") => Some(LintReporterKind::Json), + Some("compact") => Some(LintReporterKind::Compact), + Some("pretty") => Some(LintReporterKind::Pretty), + Some(_) => { + bail!("Invalid lint report type in config file") + } + None => None, + } + } + } + + let ( + maybe_file_flags, + maybe_rules_tags, + maybe_rules_include, + maybe_rules_exclude, + ) = maybe_lint_flags + .map(|f| { + ( + f.files, + f.maybe_rules_tags, + f.maybe_rules_include, + f.maybe_rules_exclude, + ) + }) + .unwrap_or_default(); + + let (maybe_config_files, maybe_config_rules) = + maybe_lint_config.map(|c| (c.files, c.rules)).unzip(); + Ok(Self { + reporter_kind: maybe_reporter_kind.unwrap_or_default(), + is_stdin, + files: resolve_files(maybe_config_files, Some(maybe_file_flags)), + rules: resolve_lint_rules_options( + maybe_config_rules, + maybe_rules_tags, + maybe_rules_include, + maybe_rules_exclude, + ), + }) + } +} + +fn resolve_lint_rules_options( + maybe_lint_rules_config: Option<LintRulesConfig>, + mut maybe_rules_tags: Option<Vec<String>>, + mut maybe_rules_include: Option<Vec<String>>, + mut maybe_rules_exclude: Option<Vec<String>>, +) -> LintRulesConfig { + if let Some(config_rules) = maybe_lint_rules_config { + // Try to get configured rules. CLI flags take precedence + // over config file, i.e. if there's `rules.include` in config file + // and `--rules-include` CLI flag, only the flag value is taken into account. + if maybe_rules_include.is_none() { + maybe_rules_include = config_rules.include; + } + if maybe_rules_exclude.is_none() { + maybe_rules_exclude = config_rules.exclude; + } + if maybe_rules_tags.is_none() { + maybe_rules_tags = config_rules.tags; + } + } + LintRulesConfig { + exclude: maybe_rules_exclude, + include: maybe_rules_include, + tags: maybe_rules_tags, + } +} + /// Create and populate a root cert store based on the passed options and /// environment. pub fn get_root_cert_store( @@ -394,36 +664,57 @@ impl CliOptions { } } - pub fn to_lint_config(&self) -> Result<Option<LintConfig>, AnyError> { - if let Some(config_file) = &self.maybe_config_file { - config_file.to_lint_config() + pub fn get_maybe_config_file(&self) -> &Option<ConfigFile> { + &self.maybe_config_file + } + + pub fn resolve_fmt_options( + &self, + fmt_flags: FmtFlags, + ) -> Result<FmtOptions, AnyError> { + let maybe_fmt_config = if let Some(config_file) = &self.maybe_config_file { + config_file.to_fmt_config()? } else { - Ok(None) - } + None + }; + FmtOptions::resolve(maybe_fmt_config, Some(fmt_flags)) } - pub fn to_test_config(&self) -> Result<Option<TestConfig>, AnyError> { - if let Some(config_file) = &self.maybe_config_file { - config_file.to_test_config() + pub fn resolve_lint_options( + &self, + lint_flags: LintFlags, + ) -> Result<LintOptions, AnyError> { + let maybe_lint_config = if let Some(config_file) = &self.maybe_config_file { + config_file.to_lint_config()? } else { - Ok(None) - } + None + }; + LintOptions::resolve(maybe_lint_config, Some(lint_flags)) } - pub fn to_bench_config(&self) -> Result<Option<BenchConfig>, AnyError> { - if let Some(config_file) = &self.maybe_config_file { - config_file.to_bench_config() + pub fn resolve_test_options( + &self, + test_flags: TestFlags, + ) -> Result<TestOptions, AnyError> { + let maybe_test_config = if let Some(config_file) = &self.maybe_config_file { + config_file.to_test_config()? } else { - Ok(None) - } + None + }; + TestOptions::resolve(maybe_test_config, Some(test_flags)) } - pub fn to_fmt_config(&self) -> Result<Option<FmtConfig>, AnyError> { - if let Some(config) = &self.maybe_config_file { - config.to_fmt_config() + pub fn resolve_bench_options( + &self, + bench_flags: BenchFlags, + ) -> Result<BenchOptions, AnyError> { + let maybe_bench_config = if let Some(config_file) = &self.maybe_config_file + { + config_file.to_bench_config()? } else { - Ok(None) - } + None + }; + BenchOptions::resolve(maybe_bench_config, Some(bench_flags)) } /// Vector of user script CLI arguments. @@ -639,6 +930,25 @@ fn resolve_import_map_specifier( Ok(None) } +/// Collect included and ignored files. CLI flags take precedence +/// over config file, i.e. if there's `files.ignore` in config file +/// and `--ignore` CLI flag, only the flag value is taken into account. +fn resolve_files( + maybe_files_config: Option<FilesConfig>, + maybe_file_flags: Option<FileFlags>, +) -> FilesConfig { + let mut result = maybe_files_config.unwrap_or_default(); + if let Some(file_flags) = maybe_file_flags { + if !file_flags.include.is_empty() { + result.include = file_flags.include; + } + if !file_flags.ignore.is_empty() { + result.exclude = file_flags.ignore; + } + } + result +} + /// Resolves the no_prompt value based on the cli flags and environment. pub fn resolve_no_prompt(flags: &Flags) -> bool { flags.no_prompt || { |