1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-12-23 15:49:44 -05:00

refactor(cli/tools): move flag and config logic to CliOptions (#17008)

Co-authored-by: David Sherret <dsherret@gmail.com>
This commit is contained in:
Geert-Jan Zwiers 2023-01-07 21:22:09 +01:00 committed by David Sherret
parent ce8acee44d
commit 941e28792c
15 changed files with 914 additions and 772 deletions

View file

@ -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]);

View file

@ -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,
@ -6386,6 +6462,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",]);

View file

@ -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()
} else {
Ok(None)
}
pub fn get_maybe_config_file(&self) -> &Option<ConfigFile> {
&self.maybe_config_file
}
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_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_bench_config(&self) -> Result<Option<BenchConfig>, AnyError> {
if let Some(config_file) = &self.maybe_config_file {
config_file.to_bench_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_fmt_config(&self) -> Result<Option<FmtConfig>, AnyError> {
if let Some(config) = &self.maybe_config_file {
config.to_fmt_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 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 {
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 || {

View file

@ -5,9 +5,7 @@ use super::documents::Documents;
use super::language_server;
use super::tsc;
use crate::args::LintConfig;
use crate::tools::lint::create_linter;
use crate::tools::lint::get_configured_rules;
use deno_ast::SourceRange;
use deno_ast::SourceRangedForSpanned;
@ -18,10 +16,12 @@ use deno_core::error::AnyError;
use deno_core::serde::Deserialize;
use deno_core::serde_json::json;
use deno_core::ModuleSpecifier;
use deno_lint::rules::LintRule;
use once_cell::sync::Lazy;
use regex::Regex;
use std::cmp::Ordering;
use std::collections::HashMap;
use std::sync::Arc;
use tower_lsp::lsp_types as lsp;
use tower_lsp::lsp_types::Position;
use tower_lsp::lsp_types::Range;
@ -127,9 +127,8 @@ fn as_lsp_range(range: &deno_lint::diagnostic::Range) -> Range {
pub fn get_lint_references(
parsed_source: &deno_ast::ParsedSource,
maybe_lint_config: Option<&LintConfig>,
lint_rules: Vec<Arc<dyn LintRule>>,
) -> Result<Vec<Reference>, AnyError> {
let lint_rules = get_configured_rules(maybe_lint_config, None, None, None)?;
let linter = create_linter(parsed_source.media_type(), lint_rules);
let lint_diagnostics = linter.lint_with_ast(parsed_source);

View file

@ -12,8 +12,9 @@ use super::performance::Performance;
use super::tsc;
use super::tsc::TsServer;
use crate::args::LintConfig;
use crate::args::LintOptions;
use crate::npm::NpmPackageReference;
use crate::tools::lint::get_configured_rules;
use deno_ast::MediaType;
use deno_core::anyhow::anyhow;
@ -24,6 +25,7 @@ use deno_core::serde_json;
use deno_core::serde_json::json;
use deno_core::ModuleSpecifier;
use deno_graph::Resolved;
use deno_lint::rules::LintRule;
use deno_runtime::tokio_util::create_basic_runtime;
use log::error;
use std::collections::HashMap;
@ -36,7 +38,7 @@ use tokio_util::sync::CancellationToken;
use tower_lsp::lsp_types as lsp;
pub type SnapshotForDiagnostics =
(Arc<StateSnapshot>, Arc<ConfigSnapshot>, Option<LintConfig>);
(Arc<StateSnapshot>, Arc<ConfigSnapshot>, LintOptions);
pub type DiagnosticRecord =
(ModuleSpecifier, Option<i32>, Vec<lsp::Diagnostic>);
pub type DiagnosticVec = Vec<DiagnosticRecord>;
@ -198,7 +200,7 @@ impl DiagnosticsServer {
match rx.recv().await {
// channel has closed
None => break,
Some((snapshot, config, maybe_lint_config)) => {
Some((snapshot, config, lint_options)) => {
// cancel the previous run
token.cancel();
token = CancellationToken::new();
@ -300,7 +302,7 @@ impl DiagnosticsServer {
let diagnostics = generate_lint_diagnostics(
&snapshot,
&config,
maybe_lint_config,
&lint_options,
token.clone(),
)
.await;
@ -443,12 +445,12 @@ fn ts_json_to_diagnostics(
async fn generate_lint_diagnostics(
snapshot: &language_server::StateSnapshot,
config: &ConfigSnapshot,
maybe_lint_config: Option<LintConfig>,
lint_options: &LintOptions,
token: CancellationToken,
) -> DiagnosticVec {
let documents = snapshot.documents.documents(true, true);
let workspace_settings = config.settings.workspace.clone();
let lint_rules = get_configured_rules(lint_options.rules.clone());
let mut diagnostics_vec = Vec::new();
if workspace_settings.lint {
for document in documents {
@ -470,7 +472,8 @@ async fn generate_lint_diagnostics(
version,
generate_document_lint_diagnostics(
config,
&maybe_lint_config,
lint_options,
lint_rules.clone(),
&document,
),
));
@ -481,23 +484,21 @@ async fn generate_lint_diagnostics(
fn generate_document_lint_diagnostics(
config: &ConfigSnapshot,
maybe_lint_config: &Option<LintConfig>,
lint_options: &LintOptions,
lint_rules: Vec<Arc<dyn LintRule>>,
document: &Document,
) -> Vec<lsp::Diagnostic> {
if !config.specifier_enabled(document.specifier()) {
return Vec::new();
}
if let Some(lint_config) = &maybe_lint_config {
if !lint_config.files.matches_specifier(document.specifier()) {
return Vec::new();
}
if !lint_options.files.matches_specifier(document.specifier()) {
return Vec::new();
}
match document.maybe_parsed_source() {
Some(Ok(parsed_source)) => {
if let Ok(references) = analysis::get_lint_references(
&parsed_source,
maybe_lint_config.as_ref(),
) {
if let Ok(references) =
analysis::get_lint_references(&parsed_source, lint_rules)
{
references
.into_iter()
.map(|r| r.to_diagnostic())
@ -1080,7 +1081,7 @@ let c: number = "a";
let diagnostics = generate_lint_diagnostics(
&snapshot,
&enabled_config,
None,
&Default::default(),
Default::default(),
)
.await;
@ -1121,7 +1122,7 @@ let c: number = "a";
let diagnostics = generate_lint_diagnostics(
&snapshot,
&disabled_config,
None,
&Default::default(),
Default::default(),
)
.await;

View file

@ -62,8 +62,8 @@ use crate::args::CacheSetting;
use crate::args::CliOptions;
use crate::args::ConfigFile;
use crate::args::Flags;
use crate::args::FmtConfig;
use crate::args::LintConfig;
use crate::args::FmtOptions;
use crate::args::LintOptions;
use crate::args::TsConfig;
use crate::cache::DenoDir;
use crate::file_fetcher::get_source_from_data_url;
@ -122,14 +122,14 @@ pub struct Inner {
/// An optional configuration file which has been specified in the client
/// options.
maybe_config_file: Option<ConfigFile>,
/// An optional configuration for formatter which has been taken from specified config file.
maybe_fmt_config: Option<FmtConfig>,
/// An optional import map which is used to resolve modules.
pub maybe_import_map: Option<Arc<ImportMap>>,
/// The URL for the import map which is used to determine relative imports.
maybe_import_map_uri: Option<Url>,
/// Configuration for formatter which has been taken from specified config file.
fmt_options: FmtOptions,
/// An optional configuration for linter which has been taken from specified config file.
pub maybe_lint_config: Option<LintConfig>,
lint_options: LintOptions,
/// A lazily create "server" for handling test run requests.
maybe_testing_server: Option<testing::TestServer>,
/// Resolver for npm packages.
@ -347,8 +347,8 @@ impl Inner {
maybe_config_file: None,
maybe_import_map: None,
maybe_import_map_uri: None,
maybe_lint_config: None,
maybe_fmt_config: None,
fmt_options: Default::default(),
lint_options: Default::default(),
maybe_testing_server: None,
module_registries,
module_registries_location,
@ -713,26 +713,30 @@ impl Inner {
fn update_config_file(&mut self) -> Result<(), AnyError> {
self.maybe_config_file = None;
self.maybe_fmt_config = None;
self.maybe_lint_config = None;
self.fmt_options = Default::default();
self.lint_options = Default::default();
if let Some(config_file) = self.get_config_file()? {
let lint_config = config_file
let lint_options = config_file
.to_lint_config()
.and_then(|maybe_lint_config| {
LintOptions::resolve(maybe_lint_config, None)
})
.map_err(|err| {
anyhow!("Unable to update lint configuration: {:?}", err)
})?
.unwrap_or_default();
let fmt_config = config_file
})?;
let fmt_options = config_file
.to_fmt_config()
.and_then(|maybe_fmt_config| {
FmtOptions::resolve(maybe_fmt_config, None)
})
.map_err(|err| {
anyhow!("Unable to update formatter configuration: {:?}", err)
})?
.unwrap_or_default();
})?;
self.maybe_config_file = Some(config_file);
self.maybe_lint_config = Some(lint_config);
self.maybe_fmt_config = Some(fmt_config);
self.lint_options = lint_options;
self.fmt_options = fmt_options;
}
Ok(())
@ -1196,19 +1200,14 @@ impl Inner {
LspError::invalid_request()
})?;
let fmt_options = if let Some(fmt_config) = self.maybe_fmt_config.as_ref() {
// skip formatting any files ignored by the config file
if !fmt_config.files.matches_specifier(&specifier) {
return Ok(None);
}
fmt_config.options.clone()
} else {
Default::default()
};
// skip formatting any files ignored by the config file
if !self.fmt_options.files.matches_specifier(&specifier) {
return Ok(None);
}
let format_result = match document.maybe_parsed_source() {
Some(Ok(parsed_source)) => {
format_parsed_source(&parsed_source, fmt_options)
format_parsed_source(&parsed_source, &self.fmt_options.options)
}
Some(Err(err)) => Err(anyhow!("{}", err)),
None => {
@ -1221,7 +1220,7 @@ impl Inner {
.map(|ext| file_path.with_extension(ext))
.unwrap_or(file_path);
// it's not a js/ts file, so attempt to format its contents
format_file(&file_path, &document.content(), &fmt_options)
format_file(&file_path, &document.content(), &self.fmt_options.options)
}
};
@ -2521,7 +2520,7 @@ impl Inner {
let snapshot = (
self.snapshot(),
self.config.snapshot(),
self.maybe_lint_config.clone(),
self.lint_options.clone(),
);
if let Err(err) = self.diagnostics_server.update(snapshot) {
error!("Cannot update diagnostics: {}", err);

View file

@ -35,6 +35,7 @@ use deno_runtime::tokio_util::run_local;
use indexmap::IndexMap;
use std::collections::HashMap;
use std::collections::HashSet;
use std::num::NonZeroUsize;
use std::sync::Arc;
use std::time::Duration;
use std::time::Instant;
@ -273,7 +274,13 @@ impl TestRun {
let (concurrent_jobs, fail_fast) =
if let DenoSubcommand::Test(test_flags) = ps.options.sub_command() {
(test_flags.concurrent_jobs.into(), test_flags.fail_fast)
(
test_flags
.concurrent_jobs
.unwrap_or_else(|| NonZeroUsize::new(1).unwrap())
.into(),
test_flags.fail_fast,
)
} else {
unreachable!("Should always be Test subcommand.");
};

View file

@ -71,10 +71,13 @@ fn init_v8_flags(v8_flags: &[String]) {
async fn run_subcommand(flags: Flags) -> Result<i32, AnyError> {
match flags.subcommand.clone() {
DenoSubcommand::Bench(bench_flags) => {
if flags.watch.is_some() {
tools::bench::run_benchmarks_with_watch(flags, bench_flags).await?;
let cli_options = CliOptions::from_flags(flags)?;
let bench_options = cli_options.resolve_bench_options(bench_flags)?;
if cli_options.watch_paths().is_some() {
tools::bench::run_benchmarks_with_watch(cli_options, bench_options)
.await?;
} else {
tools::bench::run_benchmarks(flags, bench_flags).await?;
tools::bench::run_benchmarks(cli_options, bench_options).await?;
}
Ok(0)
}
@ -109,19 +112,9 @@ async fn run_subcommand(flags: Flags) -> Result<i32, AnyError> {
Ok(0)
}
DenoSubcommand::Fmt(fmt_flags) => {
let config = CliOptions::from_flags(flags)?;
if fmt_flags.files.len() == 1
&& fmt_flags.files[0].to_string_lossy() == "-"
{
let maybe_fmt_config = config.to_fmt_config()?;
tools::fmt::format_stdin(
fmt_flags,
maybe_fmt_config.map(|c| c.options).unwrap_or_default(),
)?;
} else {
tools::fmt::format(&config, fmt_flags).await?;
}
let cli_options = CliOptions::from_flags(flags)?;
let fmt_options = cli_options.resolve_fmt_options(fmt_flags)?;
tools::fmt::format(cli_options, fmt_options).await?;
Ok(0)
}
DenoSubcommand::Init(init_flags) => {
@ -148,7 +141,9 @@ async fn run_subcommand(flags: Flags) -> Result<i32, AnyError> {
if lint_flags.rules {
tools::lint::print_rules_list(lint_flags.json);
} else {
tools::lint::lint(flags, lint_flags).await?;
let cli_options = CliOptions::from_flags(flags)?;
let lint_options = cli_options.resolve_lint_options(lint_flags)?;
tools::lint::lint(cli_options, lint_options).await?;
}
Ok(0)
}
@ -176,11 +171,13 @@ async fn run_subcommand(flags: Flags) -> Result<i32, AnyError> {
PathBuf::from(coverage_dir).canonicalize()?,
);
}
let cli_options = CliOptions::from_flags(flags)?;
let test_options = cli_options.resolve_test_options(test_flags)?;
if flags.watch.is_some() {
tools::test::run_tests_with_watch(flags, test_flags).await?;
if cli_options.watch_paths().is_some() {
tools::test::run_tests_with_watch(cli_options, test_options).await?;
} else {
tools::test::run_tests(flags, test_flags).await?;
tools::test::run_tests(cli_options, test_options).await?;
}
Ok(0)

View file

@ -1,8 +1,7 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
use crate::args::BenchConfig;
use crate::args::BenchFlags;
use crate::args::Flags;
use crate::args::BenchOptions;
use crate::args::CliOptions;
use crate::args::TypeCheckMode;
use crate::colors;
use crate::graph_util::contains_specifier;
@ -15,7 +14,6 @@ use crate::util::file_watcher;
use crate::util::file_watcher::ResolutionResult;
use crate::util::fs::collect_specifiers;
use crate::util::path::is_supported_ext;
use crate::util::path::specifier_to_file_path;
use crate::worker::create_main_worker_for_test_or_bench;
use deno_core::error::generic_error;
@ -37,6 +35,7 @@ use serde::Serialize;
use std::collections::HashSet;
use std::path::Path;
use std::path::PathBuf;
use std::sync::Arc;
use tokio::sync::mpsc::unbounded_channel;
use tokio::sync::mpsc::UnboundedSender;
@ -487,24 +486,18 @@ fn is_supported_bench_path(path: &Path) -> bool {
}
pub async fn run_benchmarks(
flags: Flags,
bench_flags: BenchFlags,
cli_options: CliOptions,
bench_options: BenchOptions,
) -> Result<(), AnyError> {
let ps = ProcState::build(flags).await?;
let ps = ProcState::from_options(Arc::new(cli_options)).await?;
// Various bench files should not share the same permissions in terms of
// `PermissionsContainer` - otherwise granting/revoking permissions in one
// file would have impact on other files, which is undesirable.
let permissions =
Permissions::from_options(&ps.options.permissions_options())?;
let selection =
collect_include_ignore(&bench_flags, ps.options.to_bench_config()?);
let specifiers = collect_specifiers(
selection.include,
&selection.ignore,
is_supported_bench_path,
)?;
let specifiers =
collect_specifiers(&bench_options.files, is_supported_bench_path)?;
if specifiers.is_empty() {
return Err(generic_error("No bench modules found"));
@ -517,7 +510,7 @@ pub async fn run_benchmarks(
permissions,
specifiers,
BenchSpecifierOptions {
filter: bench_flags.filter,
filter: bench_options.filter,
},
)
.await?;
@ -527,21 +520,22 @@ pub async fn run_benchmarks(
// TODO(bartlomieju): heavy duplication of code with `cli/tools/test.rs`
pub async fn run_benchmarks_with_watch(
flags: Flags,
bench_flags: BenchFlags,
cli_options: CliOptions,
bench_options: BenchOptions,
) -> Result<(), AnyError> {
let ps = ProcState::build(flags).await?;
let ps = ProcState::from_options(Arc::new(cli_options)).await?;
// Various bench files should not share the same permissions in terms of
// `PermissionsContainer` - otherwise granting/revoking permissions in one
// file would have impact on other files, which is undesirable.
let permissions =
Permissions::from_options(&ps.options.permissions_options())?;
let selection =
collect_include_ignore(&bench_flags, ps.options.to_bench_config()?);
let paths_to_watch: Vec<_> =
selection.include.iter().map(PathBuf::from).collect();
let paths_to_watch: Vec<_> = bench_options
.files
.include
.iter()
.map(PathBuf::from)
.collect();
let no_check = ps.options.type_check_mode() == TypeCheckMode::None;
let resolver = |changed: Option<Vec<PathBuf>>| {
@ -549,13 +543,11 @@ pub async fn run_benchmarks_with_watch(
let paths_to_watch_clone = paths_to_watch.clone();
let files_changed = changed.is_some();
let include = selection.include.clone();
let ignore = selection.ignore.clone();
let files = bench_options.files.clone();
let ps = ps.clone();
async move {
let bench_modules =
collect_specifiers(include.clone(), &ignore, is_supported_bench_path)?;
let bench_modules = collect_specifiers(&files, is_supported_bench_path)?;
let mut paths_to_watch = paths_to_watch_clone;
let mut modules_to_reload = if files_changed {
@ -615,7 +607,6 @@ pub async fn run_benchmarks_with_watch(
}
}
}
// This bench module and all it's dependencies
let mut modules = HashSet::new();
modules.insert(&specifier);
@ -664,27 +655,27 @@ pub async fn run_benchmarks_with_watch(
};
let operation = |modules_to_reload: Vec<(ModuleSpecifier, ModuleKind)>| {
let filter = bench_flags.filter.clone();
let include = selection.include.clone();
let ignore = selection.ignore.clone();
let permissions = permissions.clone();
let ps = ps.clone();
let filter = bench_options.filter.clone();
let files = bench_options.files.clone();
async move {
let specifiers =
collect_specifiers(include.clone(), &ignore, is_supported_bench_path)?
.iter()
.filter(|specifier| contains_specifier(&modules_to_reload, specifier))
.cloned()
.collect::<Vec<ModuleSpecifier>>();
let specifiers = collect_specifiers(&files, is_supported_bench_path)?
.iter()
.filter(|specifier| contains_specifier(&modules_to_reload, specifier))
.cloned()
.collect::<Vec<ModuleSpecifier>>();
check_specifiers(&ps, permissions.clone(), specifiers.clone()).await?;
let specifier_options = BenchSpecifierOptions {
filter: filter.clone(),
};
bench_specifiers(ps, permissions.clone(), specifiers, specifier_options)
.await?;
bench_specifiers(
ps,
permissions.clone(),
specifiers,
BenchSpecifierOptions { filter },
)
.await?;
Ok(())
}
@ -702,42 +693,3 @@ pub async fn run_benchmarks_with_watch(
Ok(())
}
struct IncludeIgnoreCollection {
include: Vec<String>,
ignore: Vec<PathBuf>,
}
fn collect_include_ignore(
bench_flags: &BenchFlags,
maybe_bench_config: Option<BenchConfig>,
) -> IncludeIgnoreCollection {
let mut include = bench_flags.include.clone().unwrap_or_default();
let mut ignore = bench_flags.ignore.clone();
if let Some(bench_config) = maybe_bench_config {
if include.is_empty() {
include = bench_config
.files
.include
.iter()
.map(|s| s.to_string())
.collect::<Vec<_>>();
}
if ignore.is_empty() {
ignore = bench_config
.files
.exclude
.iter()
.filter_map(|s| specifier_to_file_path(s).ok())
.collect::<Vec<_>>();
}
}
if include.is_empty() {
include.push(".".to_string());
}
IncludeIgnoreCollection { include, ignore }
}

View file

@ -8,7 +8,8 @@
//! the same functions as ops available in JS runtime.
use crate::args::CliOptions;
use crate::args::FmtFlags;
use crate::args::FilesConfig;
use crate::args::FmtOptions;
use crate::args::FmtOptionsConfig;
use crate::args::ProseWrap;
use crate::colors;
@ -17,7 +18,6 @@ use crate::util::file_watcher;
use crate::util::file_watcher::ResolutionResult;
use crate::util::fs::FileCollector;
use crate::util::path::get_extension;
use crate::util::path::specifier_to_file_path;
use crate::util::text_encoding;
use deno_ast::ParsedSource;
use deno_core::anyhow::bail;
@ -44,80 +44,41 @@ use crate::cache::IncrementalCache;
/// Format JavaScript/TypeScript files.
pub async fn format(
config: &CliOptions,
fmt_flags: FmtFlags,
cli_options: CliOptions,
fmt_options: FmtOptions,
) -> Result<(), AnyError> {
let maybe_fmt_config = config.to_fmt_config()?;
let deno_dir = config.resolve_deno_dir()?;
let FmtFlags {
files,
ignore,
check,
..
} = fmt_flags.clone();
// First, prepare final configuration.
// Collect included and ignored files. CLI flags take precendence
// over config file, ie. if there's `files.ignore` in config file
// and `--ignore` CLI flag, only the flag value is taken into account.
let mut include_files = files.clone();
let mut exclude_files = ignore;
if let Some(fmt_config) = maybe_fmt_config.as_ref() {
if include_files.is_empty() {
include_files = fmt_config
.files
.include
.iter()
.filter_map(|s| specifier_to_file_path(s).ok())
.collect::<Vec<_>>();
}
if exclude_files.is_empty() {
exclude_files = fmt_config
.files
.exclude
.iter()
.filter_map(|s| specifier_to_file_path(s).ok())
.collect::<Vec<_>>();
}
if fmt_options.is_stdin {
return format_stdin(fmt_options);
}
if include_files.is_empty() {
include_files = [std::env::current_dir()?].to_vec();
}
// Now do the same for options
let fmt_options = resolve_fmt_options(
&fmt_flags,
maybe_fmt_config.map(|c| c.options).unwrap_or_default(),
);
let files = fmt_options.files;
let check = fmt_options.check;
let fmt_config_options = fmt_options.options;
let resolver = |changed: Option<Vec<PathBuf>>| {
let files_changed = changed.is_some();
let result =
collect_fmt_files(&include_files, &exclude_files).map(|files| {
let refmt_files = if let Some(paths) = changed {
if check {
files
.iter()
.any(|path| paths.contains(path))
.then_some(files)
.unwrap_or_else(|| [].to_vec())
} else {
files
.into_iter()
.filter(|path| paths.contains(path))
.collect::<Vec<_>>()
}
let result = collect_fmt_files(&files).map(|files| {
let refmt_files = if let Some(paths) = changed {
if check {
files
.iter()
.any(|path| paths.contains(path))
.then_some(files)
.unwrap_or_else(|| [].to_vec())
} else {
files
};
(refmt_files, fmt_options.clone())
});
.into_iter()
.filter(|path| paths.contains(path))
.collect::<Vec<_>>()
}
} else {
files
};
(refmt_files, fmt_config_options.clone())
});
let paths_to_watch = include_files.clone();
let paths_to_watch = files.include.clone();
async move {
if files_changed
&& matches!(result, Ok((ref files, _)) if files.is_empty())
@ -131,6 +92,7 @@ pub async fn format(
}
}
};
let deno_dir = cli_options.resolve_deno_dir()?;
let deno_dir = &deno_dir;
let operation = |(paths, fmt_options): (Vec<PathBuf>, FmtOptionsConfig)| async move {
let incremental_cache = Arc::new(IncrementalCache::new(
@ -148,40 +110,36 @@ pub async fn format(
Ok(())
};
if config.watch_paths().is_some() {
if cli_options.watch_paths().is_some() {
file_watcher::watch_func(
resolver,
operation,
file_watcher::PrintConfig {
job_name: "Fmt".to_string(),
clear_screen: !config.no_clear_screen(),
clear_screen: !cli_options.no_clear_screen(),
},
)
.await?;
} else {
let files =
collect_fmt_files(&include_files, &exclude_files).and_then(|files| {
if files.is_empty() {
Err(generic_error("No target files found."))
} else {
Ok(files)
}
})?;
operation((files, fmt_options.clone())).await?;
let files = collect_fmt_files(&files).and_then(|files| {
if files.is_empty() {
Err(generic_error("No target files found."))
} else {
Ok(files)
}
})?;
operation((files, fmt_config_options)).await?;
}
Ok(())
}
fn collect_fmt_files(
include_files: &[PathBuf],
exclude_files: &[PathBuf],
) -> Result<Vec<PathBuf>, AnyError> {
fn collect_fmt_files(files: &FilesConfig) -> Result<Vec<PathBuf>, AnyError> {
FileCollector::new(is_supported_ext_fmt)
.ignore_git_folder()
.ignore_node_modules()
.add_ignore_paths(exclude_files)
.collect_files(include_files)
.add_ignore_paths(&files.exclude)
.collect_files(&files.include)
}
/// Formats markdown (using <https://github.com/dprint/dprint-plugin-markdown>) and its code blocks
@ -275,11 +233,11 @@ pub fn format_file(
pub fn format_parsed_source(
parsed_source: &ParsedSource,
fmt_options: FmtOptionsConfig,
fmt_options: &FmtOptionsConfig,
) -> Result<Option<String>, AnyError> {
dprint_plugin_typescript::format_parsed_source(
parsed_source,
&get_resolved_typescript_config(&fmt_options),
&get_resolved_typescript_config(fmt_options),
)
}
@ -501,19 +459,14 @@ fn format_ensure_stable(
/// Format stdin and write result to stdout.
/// Treats input as TypeScript or as set by `--ext` flag.
/// Compatible with `--check` flag.
pub fn format_stdin(
fmt_flags: FmtFlags,
fmt_options: FmtOptionsConfig,
) -> Result<(), AnyError> {
fn format_stdin(fmt_options: FmtOptions) -> Result<(), AnyError> {
let mut source = String::new();
if stdin().read_to_string(&mut source).is_err() {
bail!("Failed to read from stdin");
}
let file_path = PathBuf::from(format!("_stdin.{}", fmt_flags.ext));
let fmt_options = resolve_fmt_options(&fmt_flags, fmt_options);
let formatted_text = format_file(&file_path, &source, &fmt_options)?;
if fmt_flags.check {
let file_path = PathBuf::from(format!("_stdin.{}", fmt_options.ext));
let formatted_text = format_file(&file_path, &source, &fmt_options.options)?;
if fmt_options.check {
if formatted_text.is_some() {
println!("Not formatted stdin");
}
@ -531,41 +484,6 @@ fn files_str(len: usize) -> &'static str {
}
}
fn resolve_fmt_options(
fmt_flags: &FmtFlags,
options: FmtOptionsConfig,
) -> FmtOptionsConfig {
let mut options = options;
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
}
fn get_resolved_typescript_config(
options: &FmtOptionsConfig,
) -> dprint_plugin_typescript::configuration::Configuration {

View file

@ -6,19 +6,19 @@
//! At the moment it is only consumed using CLI but in
//! the future it can be easily extended to provide
//! the same functions as ops available in JS runtime.
use crate::args::Flags;
use crate::args::LintConfig;
use crate::args::LintFlags;
use crate::args::CliOptions;
use crate::args::FilesConfig;
use crate::args::LintOptions;
use crate::args::LintReporterKind;
use crate::args::LintRulesConfig;
use crate::colors;
use crate::proc_state::ProcState;
use crate::tools::fmt::run_parallelized;
use crate::util::file_watcher;
use crate::util::file_watcher::ResolutionResult;
use crate::util::fs::FileCollector;
use crate::util::path::is_supported_ext;
use crate::util::path::specifier_to_file_path;
use deno_ast::MediaType;
use deno_core::anyhow::anyhow;
use deno_core::anyhow::bail;
use deno_core::error::generic_error;
use deno_core::error::AnyError;
use deno_core::error::JsStackFrame;
@ -45,13 +45,6 @@ use crate::cache::IncrementalCache;
static STDIN_FILE_NAME: &str = "_stdin.ts";
#[derive(Clone, Debug)]
pub enum LintReporterKind {
Pretty,
Json,
Compact,
}
fn create_reporter(kind: LintReporterKind) -> Box<dyn LintReporter + Send> {
match kind {
LintReporterKind::Pretty => Box::new(PrettyLintReporter::new()),
@ -60,102 +53,35 @@ fn create_reporter(kind: LintReporterKind) -> Box<dyn LintReporter + Send> {
}
}
pub async fn lint(flags: Flags, lint_flags: LintFlags) -> Result<(), AnyError> {
let LintFlags {
maybe_rules_tags,
maybe_rules_include,
maybe_rules_exclude,
files: args,
ignore,
json,
compact,
..
} = lint_flags;
// First, prepare final configuration.
// Collect included and ignored files. CLI flags take precendence
// 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.
let mut include_files = args.clone();
let mut exclude_files = ignore.clone();
let mut maybe_reporter_kind = if json {
Some(LintReporterKind::Json)
} else if compact {
Some(LintReporterKind::Compact)
} else {
None
};
pub async fn lint(
cli_options: CliOptions,
lint_options: LintOptions,
) -> Result<(), AnyError> {
// Try to get lint rules. If none were set use recommended rules.
let lint_rules = get_configured_rules(lint_options.rules);
let ps = ProcState::build(flags).await?;
let maybe_lint_config = ps.options.to_lint_config()?;
if let Some(lint_config) = maybe_lint_config.as_ref() {
if include_files.is_empty() {
include_files = lint_config
.files
.include
.iter()
.filter_map(|s| specifier_to_file_path(s).ok())
.collect::<Vec<_>>();
}
if exclude_files.is_empty() {
exclude_files = lint_config
.files
.exclude
.iter()
.filter_map(|s| specifier_to_file_path(s).ok())
.collect::<Vec<_>>();
}
if maybe_reporter_kind.is_none() {
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(_) => {
return Err(anyhow!("Invalid lint report type in config file"))
}
None => Some(LintReporterKind::Pretty),
}
}
if lint_rules.is_empty() {
bail!("No rules have been configured")
}
if include_files.is_empty() {
include_files = [std::env::current_dir()?].to_vec();
}
let reporter_kind = match maybe_reporter_kind {
Some(report) => report,
None => LintReporterKind::Pretty,
};
let has_error = Arc::new(AtomicBool::new(false));
// Try to get configured rules. CLI flags take precendence
// over config file, ie. if there's `rules.include` in config file
// and `--rules-include` CLI flag, only the flag value is taken into account.
let lint_rules = get_configured_rules(
maybe_lint_config.as_ref(),
maybe_rules_tags,
maybe_rules_include,
maybe_rules_exclude,
)?;
let files = lint_options.files;
let reporter_kind = lint_options.reporter_kind;
let resolver = |changed: Option<Vec<PathBuf>>| {
let files_changed = changed.is_some();
let result =
collect_lint_files(&include_files, &exclude_files).map(|files| {
if let Some(paths) = changed {
files
.iter()
.any(|path| paths.contains(path))
.then_some(files)
.unwrap_or_else(|| [].to_vec())
} else {
files
}
});
let result = collect_lint_files(&files).map(|files| {
if let Some(paths) = changed {
files
.iter()
.any(|path| paths.contains(path))
.then_some(files)
.unwrap_or_else(|| [].to_vec())
} else {
files
}
});
let paths_to_watch = include_files.clone();
let paths_to_watch = files.include.clone();
async move {
if files_changed && matches!(result, Ok(ref files) if files.is_empty()) {
@ -169,9 +95,12 @@ pub async fn lint(flags: Flags, lint_flags: LintFlags) -> Result<(), AnyError> {
}
};
let has_error = Arc::new(AtomicBool::new(false));
let deno_dir = cli_options.resolve_deno_dir()?;
let operation = |paths: Vec<PathBuf>| async {
let incremental_cache = Arc::new(IncrementalCache::new(
&ps.dir.lint_incremental_cache_db_file_path(),
&deno_dir.lint_incremental_cache_db_file_path(),
// use a hash of the rule names in order to bust the cache
&{
// ensure this is stable by sorting it
@ -221,8 +150,8 @@ pub async fn lint(flags: Flags, lint_flags: LintFlags) -> Result<(), AnyError> {
Ok(())
};
if ps.options.watch_paths().is_some() {
if args.len() == 1 && args[0].to_string_lossy() == "-" {
if cli_options.watch_paths().is_some() {
if lint_options.is_stdin {
return Err(generic_error(
"Lint watch on standard input is not supported.",
));
@ -232,12 +161,12 @@ pub async fn lint(flags: Flags, lint_flags: LintFlags) -> Result<(), AnyError> {
operation,
file_watcher::PrintConfig {
job_name: "Lint".to_string(),
clear_screen: !ps.options.no_clear_screen(),
clear_screen: !cli_options.no_clear_screen(),
},
)
.await?;
} else {
if args.len() == 1 && args[0].to_string_lossy() == "-" {
if lint_options.is_stdin {
let reporter_lock =
Arc::new(Mutex::new(create_reporter(reporter_kind.clone())));
let r = lint_stdin(lint_rules);
@ -249,14 +178,13 @@ pub async fn lint(flags: Flags, lint_flags: LintFlags) -> Result<(), AnyError> {
);
reporter_lock.lock().unwrap().close(1);
} else {
let target_files = collect_lint_files(&include_files, &exclude_files)
.and_then(|files| {
if files.is_empty() {
Err(generic_error("No target files found."))
} else {
Ok(files)
}
})?;
let target_files = collect_lint_files(&files).and_then(|files| {
if files.is_empty() {
Err(generic_error("No target files found."))
} else {
Ok(files)
}
})?;
debug!("Found {} files", target_files.len());
operation(target_files).await?;
};
@ -269,15 +197,12 @@ pub async fn lint(flags: Flags, lint_flags: LintFlags) -> Result<(), AnyError> {
Ok(())
}
fn collect_lint_files(
include_files: &[PathBuf],
exclude_files: &[PathBuf],
) -> Result<Vec<PathBuf>, AnyError> {
fn collect_lint_files(files: &FilesConfig) -> Result<Vec<PathBuf>, AnyError> {
FileCollector::new(is_supported_ext)
.ignore_git_folder()
.ignore_node_modules()
.add_ignore_paths(exclude_files)
.collect_files(include_files)
.add_ignore_paths(&files.exclude)
.collect_files(&files.include)
}
pub fn print_rules_list(json: bool) {
@ -606,60 +531,17 @@ fn sort_diagnostics(diagnostics: &mut [LintDiagnostic]) {
});
}
pub fn get_configured_rules(
maybe_lint_config: Option<&LintConfig>,
maybe_rules_tags: Option<Vec<String>>,
maybe_rules_include: Option<Vec<String>>,
maybe_rules_exclude: Option<Vec<String>>,
) -> Result<Vec<Arc<dyn LintRule>>, AnyError> {
if maybe_lint_config.is_none()
&& maybe_rules_tags.is_none()
&& maybe_rules_include.is_none()
&& maybe_rules_exclude.is_none()
pub fn get_configured_rules(rules: LintRulesConfig) -> Vec<Arc<dyn LintRule>> {
if rules.tags.is_none() && rules.include.is_none() && rules.exclude.is_none()
{
return Ok(rules::get_recommended_rules());
rules::get_recommended_rules()
} else {
rules::get_filtered_rules(
rules.tags.or_else(|| Some(vec!["recommended".to_string()])),
rules.exclude,
rules.include,
)
}
let (config_file_tags, config_file_include, config_file_exclude) =
if let Some(lint_config) = maybe_lint_config {
(
lint_config.rules.tags.clone(),
lint_config.rules.include.clone(),
lint_config.rules.exclude.clone(),
)
} else {
(None, None, None)
};
let maybe_configured_include = if maybe_rules_include.is_some() {
maybe_rules_include
} else {
config_file_include
};
let maybe_configured_exclude = if maybe_rules_exclude.is_some() {
maybe_rules_exclude
} else {
config_file_exclude
};
let maybe_configured_tags = if maybe_rules_tags.is_some() {
maybe_rules_tags
} else {
config_file_tags
};
let configured_rules = rules::get_filtered_rules(
maybe_configured_tags.or_else(|| Some(vec!["recommended".to_string()])),
maybe_configured_exclude,
maybe_configured_include,
);
if configured_rules.is_empty() {
return Err(anyhow!("No rules have been configured"));
}
Ok(configured_rules)
}
#[cfg(test)]
@ -671,15 +553,12 @@ mod test {
#[test]
fn recommended_rules_when_no_tags_in_config() {
let lint_config = LintConfig {
rules: LintRulesConfig {
exclude: Some(vec!["no-debugger".to_string()]),
..Default::default()
},
..Default::default()
let rules_config = LintRulesConfig {
exclude: Some(vec!["no-debugger".to_string()]),
include: None,
tags: None,
};
let rules =
get_configured_rules(Some(&lint_config), None, None, None).unwrap();
let rules = get_configured_rules(rules_config);
let mut rule_names = rules
.into_iter()
.map(|r| r.code().to_string())

View file

@ -1,7 +1,8 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
use crate::args::Flags;
use crate::args::TestFlags;
use crate::args::CliOptions;
use crate::args::FilesConfig;
use crate::args::TestOptions;
use crate::args::TypeCheckMode;
use crate::colors;
use crate::display;
@ -16,7 +17,6 @@ use crate::util::file_watcher::ResolutionResult;
use crate::util::fs::collect_specifiers;
use crate::util::path::get_extension;
use crate::util::path::is_supported_ext;
use crate::util::path::specifier_to_file_path;
use crate::worker::create_main_worker_for_test_or_bench;
use deno_ast::swc::common::comments::CommentKind;
@ -1237,15 +1237,13 @@ fn is_supported_test_ext(path: &Path) -> bool {
/// - Specifiers matching the `is_supported_test_path` are marked as `TestMode::Executable`.
/// - Specifiers matching both predicates are marked as `TestMode::Both`
fn collect_specifiers_with_test_mode(
include: Vec<String>,
ignore: Vec<PathBuf>,
files: FilesConfig,
include_inline: bool,
) -> Result<Vec<(ModuleSpecifier, TestMode)>, AnyError> {
let module_specifiers =
collect_specifiers(include.clone(), &ignore, is_supported_test_path)?;
let module_specifiers = collect_specifiers(&files, is_supported_test_path)?;
if include_inline {
return collect_specifiers(include, &ignore, is_supported_test_ext).map(
return collect_specifiers(&files, is_supported_test_ext).map(
|specifiers| {
specifiers
.into_iter()
@ -1281,44 +1279,10 @@ fn collect_specifiers_with_test_mode(
/// as well.
async fn fetch_specifiers_with_test_mode(
ps: &ProcState,
include: Vec<String>,
ignore: Vec<PathBuf>,
include_inline: bool,
files: FilesConfig,
doc: bool,
) -> Result<Vec<(ModuleSpecifier, TestMode)>, AnyError> {
let maybe_test_config = ps.options.to_test_config()?;
let mut include_files = include.clone();
let mut exclude_files = ignore.clone();
if let Some(test_config) = maybe_test_config.as_ref() {
if include_files.is_empty() {
include_files = test_config
.files
.include
.iter()
.map(|s| s.to_string())
.collect::<Vec<_>>();
}
if exclude_files.is_empty() {
exclude_files = test_config
.files
.exclude
.iter()
.filter_map(|s| specifier_to_file_path(s).ok())
.collect::<Vec<_>>();
}
}
if include_files.is_empty() {
include_files.push(".".to_string());
}
let mut specifiers_with_mode = collect_specifiers_with_test_mode(
include_files,
exclude_files,
include_inline,
)?;
let mut specifiers_with_mode = collect_specifiers_with_test_mode(files, doc)?;
for (specifier, mode) in &mut specifiers_with_mode {
let file = ps
.file_fetcher
@ -1336,31 +1300,28 @@ async fn fetch_specifiers_with_test_mode(
}
pub async fn run_tests(
flags: Flags,
test_flags: TestFlags,
cli_options: CliOptions,
test_options: TestOptions,
) -> Result<(), AnyError> {
let ps = ProcState::build(flags).await?;
let ps = ProcState::from_options(Arc::new(cli_options)).await?;
// Various test files should not share the same permissions in terms of
// `PermissionsContainer` - otherwise granting/revoking permissions in one
// file would have impact on other files, which is undesirable.
let permissions =
Permissions::from_options(&ps.options.permissions_options())?;
let specifiers_with_mode = fetch_specifiers_with_test_mode(
&ps,
test_flags.include,
test_flags.ignore.clone(),
test_flags.doc,
)
.await?;
if !test_flags.allow_none && specifiers_with_mode.is_empty() {
let specifiers_with_mode =
fetch_specifiers_with_test_mode(&ps, test_options.files, test_options.doc)
.await?;
if !test_options.allow_none && specifiers_with_mode.is_empty() {
return Err(generic_error("No test modules found"));
}
check_specifiers(&ps, permissions.clone(), specifiers_with_mode.clone())
.await?;
if test_flags.no_run {
if test_options.no_run {
return Ok(());
}
@ -1369,9 +1330,9 @@ pub async fn run_tests(
permissions,
specifiers_with_mode,
TestSpecifierOptions {
concurrent_jobs: test_flags.concurrent_jobs,
fail_fast: test_flags.fail_fast,
filter: TestFilter::from_flag(&test_flags.filter),
concurrent_jobs: test_options.concurrent_jobs,
fail_fast: test_options.fail_fast,
filter: TestFilter::from_flag(&test_options.filter),
},
)
.await?;
@ -1380,35 +1341,32 @@ pub async fn run_tests(
}
pub async fn run_tests_with_watch(
flags: Flags,
test_flags: TestFlags,
cli_options: CliOptions,
test_options: TestOptions,
) -> Result<(), AnyError> {
let ps = ProcState::build(flags).await?;
let ps = ProcState::from_options(Arc::new(cli_options)).await?;
// Various test files should not share the same permissions in terms of
// `PermissionsContainer` - otherwise granting/revoking permissions in one
// file would have impact on other files, which is undesirable.
let permissions =
Permissions::from_options(&ps.options.permissions_options())?;
let include = test_flags.include;
let ignore = test_flags.ignore.clone();
let paths_to_watch: Vec<_> = include.iter().map(PathBuf::from).collect();
let paths_to_watch: Vec<_> = test_options.files.include.clone();
let no_check = ps.options.type_check_mode() == TypeCheckMode::None;
let test_options = &test_options;
let resolver = |changed: Option<Vec<PathBuf>>| {
let paths_to_watch = paths_to_watch.clone();
let paths_to_watch_clone = paths_to_watch.clone();
let files_changed = changed.is_some();
let include = include.clone();
let ignore = ignore.clone();
let ps = ps.clone();
async move {
let test_modules = if test_flags.doc {
collect_specifiers(include.clone(), &ignore, is_supported_test_ext)
let test_modules = if test_options.doc {
collect_specifiers(&test_options.files, is_supported_test_ext)
} else {
collect_specifiers(include.clone(), &ignore, is_supported_test_path)
collect_specifiers(&test_options.files, is_supported_test_path)
}?;
let mut paths_to_watch = paths_to_watch_clone;
@ -1517,20 +1475,16 @@ pub async fn run_tests_with_watch(
})
};
let cli_options = ps.options.clone();
let operation = |modules_to_reload: Vec<(ModuleSpecifier, ModuleKind)>| {
let filter = test_flags.filter.clone();
let include = include.clone();
let ignore = ignore.clone();
let permissions = permissions.clone();
let ps = ps.clone();
let test_options = test_options.clone();
async move {
let specifiers_with_mode = fetch_specifiers_with_test_mode(
&ps,
include.clone(),
ignore.clone(),
test_flags.doc,
test_options.files,
test_options.doc,
)
.await?
.iter()
@ -1543,7 +1497,7 @@ pub async fn run_tests_with_watch(
check_specifiers(&ps, permissions.clone(), specifiers_with_mode.clone())
.await?;
if test_flags.no_run {
if test_options.no_run {
return Ok(());
}
@ -1552,9 +1506,9 @@ pub async fn run_tests_with_watch(
permissions.clone(),
specifiers_with_mode,
TestSpecifierOptions {
concurrent_jobs: test_flags.concurrent_jobs,
fail_fast: test_flags.fail_fast,
filter: TestFilter::from_flag(&filter),
concurrent_jobs: test_options.concurrent_jobs,
fail_fast: test_options.fail_fast,
filter: TestFilter::from_flag(&test_options.filter),
},
)
.await?;
@ -1568,7 +1522,7 @@ pub async fn run_tests_with_watch(
operation,
file_watcher::PrintConfig {
job_name: "Test".to_string(),
clear_screen: !cli_options.no_clear_screen(),
clear_screen: !ps.options.no_clear_screen(),
},
)
.await?;

View file

@ -153,9 +153,12 @@ fn maybe_update_config_file(output_dir: &Path, ps: &ProcState) -> bool {
Some(f) => f,
None => return false,
};
let fmt_config = ps
.options
.to_fmt_config()
.get_maybe_config_file()
.as_ref()
.and_then(|config| config.to_fmt_config().ok())
.unwrap_or_default()
.unwrap_or_default();
let result = update_config_file(

View file

@ -107,9 +107,13 @@ where
log::debug!("File change ignored")
}
ResolutionResult::Restart {
paths_to_watch,
mut paths_to_watch,
result,
} => {
// watch the current directory when empty
if paths_to_watch.is_empty() {
paths_to_watch.push(PathBuf::from("."));
}
return (paths_to_watch, result);
}
}
@ -190,9 +194,13 @@ where
print_after_restart();
}
ResolutionResult::Restart {
paths_to_watch: paths,
paths_to_watch: mut paths,
result,
} => {
// watch the current directory when empty
if paths.is_empty() {
paths.push(PathBuf::from("."));
}
paths_to_watch = paths;
resolution_result = result;
}

View file

@ -6,6 +6,7 @@ pub use deno_core::normalize_path;
use deno_core::ModuleSpecifier;
use deno_runtime::deno_crypto::rand;
use deno_runtime::deno_node::PathClean;
use std::borrow::Cow;
use std::env::current_dir;
use std::fs::OpenOptions;
use std::io::Error;
@ -16,6 +17,8 @@ use std::path::PathBuf;
use std::time::Duration;
use walkdir::WalkDir;
use crate::args::FilesConfig;
use super::path::specifier_to_file_path;
pub fn atomic_write_file<T: AsRef<[u8]>>(
@ -181,6 +184,7 @@ impl<TFilter: Fn(&Path) -> bool> FileCollector<TFilter> {
ignore_node_modules: false,
}
}
pub fn add_ignore_paths(mut self, paths: &[PathBuf]) -> Self {
// retain only the paths which exist and ignore the rest
self
@ -204,7 +208,13 @@ impl<TFilter: Fn(&Path) -> bool> FileCollector<TFilter> {
files: &[PathBuf],
) -> Result<Vec<PathBuf>, AnyError> {
let mut target_files = Vec::new();
for file in files {
let files = if files.is_empty() {
// collect files in the current directory when empty
Cow::Owned(vec![PathBuf::from(".")])
} else {
Cow::Borrowed(files)
};
for file in files.iter() {
if let Ok(file) = canonicalize_path(file) {
// use an iterator like this in order to minimize the number of file system operations
let mut iterator = WalkDir::new(&file).into_iter();
@ -254,18 +264,24 @@ impl<TFilter: Fn(&Path) -> bool> FileCollector<TFilter> {
/// Specifiers that start with http and https are left intact.
/// Note: This ignores all .git and node_modules folders.
pub fn collect_specifiers(
include: Vec<String>,
ignore: &[PathBuf],
files: &FilesConfig,
predicate: impl Fn(&Path) -> bool,
) -> Result<Vec<ModuleSpecifier>, AnyError> {
let mut prepared = vec![];
let file_collector = FileCollector::new(predicate)
.add_ignore_paths(ignore)
.add_ignore_paths(&files.exclude)
.ignore_git_folder()
.ignore_node_modules();
let root_path = current_dir()?;
for path in include {
let include_files = if files.include.is_empty() {
// collect files in the current directory when empty
Cow::Owned(vec![root_path.clone()])
} else {
Cow::Borrowed(&files.include)
};
for path in include_files.iter() {
let path = path.to_string_lossy();
let lowercase_path = path.to_lowercase();
if lowercase_path.starts_with("http://")
|| lowercase_path.starts_with("https://")
@ -278,7 +294,7 @@ pub fn collect_specifiers(
let p = if lowercase_path.starts_with("file://") {
specifier_to_file_path(&ModuleSpecifier::parse(&path)?)?
} else {
root_path.join(path)
root_path.join(path.as_ref())
};
let p = normalize_path(p);
if p.is_dir() {
@ -675,12 +691,14 @@ mod tests {
};
let result = collect_specifiers(
vec![
"http://localhost:8080".to_string(),
root_dir_path.to_str().unwrap().to_string(),
"https://localhost:8080".to_string(),
],
&[ignore_dir_path],
&FilesConfig {
include: vec![
PathBuf::from("http://localhost:8080"),
root_dir_path.clone(),
PathBuf::from("https://localhost:8080".to_string()),
],
exclude: vec![ignore_dir_path],
},
predicate,
)
.unwrap();
@ -713,16 +731,18 @@ mod tests {
"file://"
};
let result = collect_specifiers(
vec![format!(
"{}{}",
scheme,
root_dir_path
.join("child")
.to_str()
.unwrap()
.replace('\\', "/")
)],
&[],
&FilesConfig {
include: vec![PathBuf::from(format!(
"{}{}",
scheme,
root_dir_path
.join("child")
.to_str()
.unwrap()
.replace('\\', "/")
))],
exclude: vec![],
},
predicate,
)
.unwrap();