diff options
author | David Sherret <dsherret@users.noreply.github.com> | 2022-06-28 16:45:55 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-06-28 16:45:55 -0400 |
commit | 01adbb1efb116d72dc24843294f335bd63b24b0a (patch) | |
tree | 920346be399301867567b45356b6613ca03bc109 /cli | |
parent | 5b7bcefa111b1e4fc1e02bb7fb1c8f152e5fd6aa (diff) |
refactor: add `RootConfig` (#14985)
Diffstat (limited to 'cli')
-rw-r--r-- | cli/args/config_file.rs | 315 | ||||
-rw-r--r-- | cli/args/flags.rs | 10 | ||||
-rw-r--r-- | cli/args/mod.rs | 438 | ||||
-rw-r--r-- | cli/emit.rs | 115 | ||||
-rw-r--r-- | cli/graph_util.rs | 11 | ||||
-rw-r--r-- | cli/lsp/cache.rs | 4 | ||||
-rw-r--r-- | cli/lsp/language_server.rs | 2 | ||||
-rw-r--r-- | cli/lsp/testing/execution.rs | 8 | ||||
-rw-r--r-- | cli/main.rs | 223 | ||||
-rw-r--r-- | cli/module_loader.rs | 27 | ||||
-rw-r--r-- | cli/proc_state.rs | 222 | ||||
-rw-r--r-- | cli/standalone.rs | 8 | ||||
-rw-r--r-- | cli/tools/bench.rs | 87 | ||||
-rw-r--r-- | cli/tools/coverage/mod.rs | 3 | ||||
-rw-r--r-- | cli/tools/doc.rs | 4 | ||||
-rw-r--r-- | cli/tools/fmt.rs | 15 | ||||
-rw-r--r-- | cli/tools/lint.rs | 32 | ||||
-rw-r--r-- | cli/tools/standalone.rs | 1 | ||||
-rw-r--r-- | cli/tools/task.rs | 112 | ||||
-rw-r--r-- | cli/tools/test.rs | 76 | ||||
-rw-r--r-- | cli/tools/vendor/mod.rs | 21 |
21 files changed, 916 insertions, 818 deletions
diff --git a/cli/args/config_file.rs b/cli/args/config_file.rs index bed155d32..570aeba0d 100644 --- a/cli/args/config_file.rs +++ b/cli/args/config_file.rs @@ -11,7 +11,6 @@ use deno_core::anyhow::bail; use deno_core::anyhow::Context; use deno_core::error::custom_error; use deno_core::error::AnyError; -use deno_core::normalize_path; use deno_core::serde::Deserialize; use deno_core::serde::Serialize; use deno_core::serde::Serializer; @@ -161,66 +160,6 @@ pub const IGNORED_RUNTIME_COMPILER_OPTIONS: &[&str] = &[ "watch", ]; -/// Filenames that Deno will recognize when discovering config. -const CONFIG_FILE_NAMES: [&str; 2] = ["deno.json", "deno.jsonc"]; - -pub fn discover(flags: &Flags) -> Result<Option<ConfigFile>, AnyError> { - match &flags.config_flag { - ConfigFlag::Disabled => Ok(None), - ConfigFlag::Path(config_path) => Ok(Some(ConfigFile::read(config_path)?)), - ConfigFlag::Discover => { - if let Some(config_path_args) = flags.config_path_args() { - let mut checked = HashSet::new(); - for f in config_path_args { - if let Some(cf) = discover_from(&f, &mut checked)? { - return Ok(Some(cf)); - } - } - // From CWD walk up to root looking for deno.json or deno.jsonc - let cwd = std::env::current_dir()?; - discover_from(&cwd, &mut checked) - } else { - Ok(None) - } - } - } -} - -pub fn discover_from( - start: &Path, - checked: &mut HashSet<PathBuf>, -) -> Result<Option<ConfigFile>, AnyError> { - for ancestor in start.ancestors() { - if checked.insert(ancestor.to_path_buf()) { - for config_filename in CONFIG_FILE_NAMES { - let f = ancestor.join(config_filename); - match ConfigFile::read(f) { - Ok(cf) => { - return Ok(Some(cf)); - } - Err(e) => { - if let Some(ioerr) = e.downcast_ref::<std::io::Error>() { - use std::io::ErrorKind::*; - match ioerr.kind() { - InvalidInput | PermissionDenied | NotFound => { - // ok keep going - } - _ => { - return Err(e); // Unknown error. Stop. - } - } - } else { - return Err(e); // Parse error or something else. Stop. - } - } - } - } - } - } - // No config file found. - Ok(None) -} - /// A function that works like JavaScript's `Object.assign()`. pub fn json_merge(a: &mut Value, b: &Value) { match (a, b) { @@ -235,56 +174,6 @@ pub fn json_merge(a: &mut Value, b: &Value) { } } -/// Based on an optional command line import map path and an optional -/// configuration file, return a resolved module specifier to an import map. -pub fn resolve_import_map_specifier( - maybe_import_map_path: Option<&str>, - maybe_config_file: Option<&ConfigFile>, -) -> Result<Option<ModuleSpecifier>, AnyError> { - if let Some(import_map_path) = maybe_import_map_path { - if let Some(config_file) = &maybe_config_file { - if config_file.to_import_map_path().is_some() { - log::warn!("{} the configuration file \"{}\" contains an entry for \"importMap\" that is being ignored.", crate::colors::yellow("Warning"), config_file.specifier); - } - } - let specifier = deno_core::resolve_url_or_path(import_map_path) - .context(format!("Bad URL (\"{}\") for import map.", import_map_path))?; - return Ok(Some(specifier)); - } else if let Some(config_file) = &maybe_config_file { - // when the import map is specifier in a config file, it needs to be - // resolved relative to the config file, versus the CWD like with the flag - // and with config files, we support both local and remote config files, - // so we have treat them differently. - if let Some(import_map_path) = config_file.to_import_map_path() { - let specifier = - // with local config files, it might be common to specify an import - // map like `"importMap": "import-map.json"`, which is resolvable if - // the file is resolved like a file path, so we will coerce the config - // file into a file path if possible and join the import map path to - // the file path. - if let Ok(config_file_path) = config_file.specifier.to_file_path() { - let import_map_file_path = normalize_path(config_file_path - .parent() - .ok_or_else(|| { - anyhow!("Bad config file specifier: {}", config_file.specifier) - })? - .join(&import_map_path)); - ModuleSpecifier::from_file_path(import_map_file_path).unwrap() - // otherwise if the config file is remote, we have no choice but to - // use "import resolution" with the config file as the base. - } else { - deno_core::resolve_import(&import_map_path, config_file.specifier.as_str()) - .context(format!( - "Bad URL (\"{}\") for import map.", - import_map_path - ))? - }; - return Ok(Some(specifier)); - } - } - Ok(None) -} - fn parse_compiler_options( compiler_options: &HashMap<String, Value>, maybe_specifier: Option<ModuleSpecifier>, @@ -547,6 +436,66 @@ pub struct ConfigFile { } impl ConfigFile { + pub fn discover(flags: &Flags) -> Result<Option<ConfigFile>, AnyError> { + match &flags.config_flag { + ConfigFlag::Disabled => Ok(None), + ConfigFlag::Path(config_path) => Ok(Some(ConfigFile::read(config_path)?)), + ConfigFlag::Discover => { + if let Some(config_path_args) = flags.config_path_args() { + let mut checked = HashSet::new(); + for f in config_path_args { + if let Some(cf) = Self::discover_from(&f, &mut checked)? { + return Ok(Some(cf)); + } + } + // From CWD walk up to root looking for deno.json or deno.jsonc + let cwd = std::env::current_dir()?; + Self::discover_from(&cwd, &mut checked) + } else { + Ok(None) + } + } + } + } + + pub fn discover_from( + start: &Path, + checked: &mut HashSet<PathBuf>, + ) -> Result<Option<ConfigFile>, AnyError> { + /// Filenames that Deno will recognize when discovering config. + const CONFIG_FILE_NAMES: [&str; 2] = ["deno.json", "deno.jsonc"]; + + for ancestor in start.ancestors() { + if checked.insert(ancestor.to_path_buf()) { + for config_filename in CONFIG_FILE_NAMES { + let f = ancestor.join(config_filename); + match ConfigFile::read(f) { + Ok(cf) => { + return Ok(Some(cf)); + } + Err(e) => { + if let Some(ioerr) = e.downcast_ref::<std::io::Error>() { + use std::io::ErrorKind::*; + match ioerr.kind() { + InvalidInput | PermissionDenied | NotFound => { + // ok keep going + } + _ => { + return Err(e); // Unknown error. Stop. + } + } + } else { + return Err(e); // Parse error or something else. Stop. + } + } + } + } + } + } + // No config file found. + Ok(None) + } + pub fn read(path_ref: impl AsRef<Path>) -> Result<Self, AnyError> { let path = Path::new(path_ref.as_ref()); let config_file = if path.is_absolute() { @@ -744,12 +693,36 @@ impl ConfigFile { Ok(None) } } + + pub fn resolve_tasks_config( + &self, + ) -> Result<BTreeMap<String, String>, AnyError> { + let maybe_tasks_config = self.to_tasks_config()?; + if let Some(tasks_config) = maybe_tasks_config { + for key in tasks_config.keys() { + if key.is_empty() { + bail!("Configuration file task names cannot be empty"); + } else if !key + .chars() + .all(|c| c.is_ascii_alphanumeric() || matches!(c, '_' | '-' | ':')) + { + bail!("Configuration file task names must only contain alpha-numeric characters, colons (:), underscores (_), or dashes (-). Task: {}", key); + } else if !key.chars().next().unwrap().is_ascii_alphabetic() { + bail!("Configuration file task names must start with an alphabetic character. Task: {}", key); + } + } + Ok(tasks_config) + } else { + bail!("No tasks found in configuration file") + } + } } #[cfg(test)] mod tests { use super::*; use deno_core::serde_json::json; + use pretty_assertions::assert_eq; #[test] fn read_config_file_relative() { @@ -996,7 +969,9 @@ mod tests { let testdata = test_util::testdata_path(); let c_md = testdata.join("fmt/with_config/subdir/c.md"); let mut checked = HashSet::new(); - let config_file = discover_from(&c_md, &mut checked).unwrap().unwrap(); + let config_file = ConfigFile::discover_from(&c_md, &mut checked) + .unwrap() + .unwrap(); assert!(checked.contains(c_md.parent().unwrap())); assert!(!checked.contains(&testdata)); let fmt_config = config_file.to_fmt_config().unwrap().unwrap(); @@ -1012,7 +987,9 @@ mod tests { } // If we call discover_from again starting at testdata, we ought to get None. - assert!(discover_from(&testdata, &mut checked).unwrap().is_none()); + assert!(ConfigFile::discover_from(&testdata, &mut checked) + .unwrap() + .is_none()); } #[test] @@ -1020,83 +997,71 @@ mod tests { let testdata = test_util::testdata_path(); let d = testdata.join("malformed_config/"); let mut checked = HashSet::new(); - let err = discover_from(&d, &mut checked).unwrap_err(); + let err = ConfigFile::discover_from(&d, &mut checked).unwrap_err(); assert!(err.to_string().contains("Unable to parse config file")); } - #[cfg(not(windows))] #[test] - fn resolve_import_map_config_file() { - let config_text = r#"{ - "importMap": "import_map.json" - }"#; - let config_specifier = - ModuleSpecifier::parse("file:///deno/deno.jsonc").unwrap(); - let config_file = ConfigFile::new(config_text, &config_specifier).unwrap(); - let actual = resolve_import_map_specifier(None, Some(&config_file)); - assert!(actual.is_ok()); - let actual = actual.unwrap(); - assert_eq!( - actual, - Some(ModuleSpecifier::parse("file:///deno/import_map.json").unwrap()) - ); + fn tasks_no_tasks() { + run_task_error_test(r#"{}"#, "No tasks found in configuration file"); } #[test] - fn resolve_import_map_config_file_remote() { - let config_text = r#"{ - "importMap": "./import_map.json" - }"#; - let config_specifier = - ModuleSpecifier::parse("https://example.com/deno.jsonc").unwrap(); - let config_file = ConfigFile::new(config_text, &config_specifier).unwrap(); - let actual = resolve_import_map_specifier(None, Some(&config_file)); - assert!(actual.is_ok()); - let actual = actual.unwrap(); - assert_eq!( - actual, - Some( - ModuleSpecifier::parse("https://example.com/import_map.json").unwrap() - ) + fn task_name_invalid_chars() { + run_task_error_test( + r#"{ + "tasks": { + "build": "deno test", + "some%test": "deno bundle mod.ts" + } + }"#, + concat!( + "Configuration file task names must only contain alpha-numeric ", + "characters, colons (:), underscores (_), or dashes (-). Task: some%test", + ), ); } #[test] - fn resolve_import_map_flags_take_precedence() { - let config_text = r#"{ - "importMap": "import_map.json" - }"#; - let config_specifier = - ModuleSpecifier::parse("file:///deno/deno.jsonc").unwrap(); - let config_file = ConfigFile::new(config_text, &config_specifier).unwrap(); - let actual = - resolve_import_map_specifier(Some("import-map.json"), Some(&config_file)); - let import_map_path = - std::env::current_dir().unwrap().join("import-map.json"); - let expected_specifier = - ModuleSpecifier::from_file_path(&import_map_path).unwrap(); - assert!(actual.is_ok()); - let actual = actual.unwrap(); - assert_eq!(actual, Some(expected_specifier)); + fn task_name_non_alpha_starting_char() { + run_task_error_test( + r#"{ + "tasks": { + "build": "deno test", + "1test": "deno bundle mod.ts" + } + }"#, + concat!( + "Configuration file task names must start with an ", + "alphabetic character. Task: 1test", + ), + ); } #[test] - fn resolve_import_map_none() { - let config_text = r#"{}"#; - let config_specifier = - ModuleSpecifier::parse("file:///deno/deno.jsonc").unwrap(); - let config_file = ConfigFile::new(config_text, &config_specifier).unwrap(); - let actual = resolve_import_map_specifier(None, Some(&config_file)); - assert!(actual.is_ok()); - let actual = actual.unwrap(); - assert_eq!(actual, None); + fn task_name_empty() { + run_task_error_test( + r#"{ + "tasks": { + "build": "deno test", + "": "deno bundle mod.ts" + } + }"#, + "Configuration file task names cannot be empty", + ); } - #[test] - fn resolve_import_map_no_config() { - let actual = resolve_import_map_specifier(None, None); - assert!(actual.is_ok()); - let actual = actual.unwrap(); - assert_eq!(actual, None); + fn run_task_error_test(config_text: &str, expected_error: &str) { + let config_dir = ModuleSpecifier::parse("file:///deno/").unwrap(); + let config_specifier = config_dir.join("tsconfig.json").unwrap(); + let config_file = ConfigFile::new(config_text, &config_specifier).unwrap(); + assert_eq!( + config_file + .resolve_tasks_config() + .err() + .unwrap() + .to_string(), + expected_error, + ); } } diff --git a/cli/args/flags.rs b/cli/args/flags.rs index f61ead385..8b8cf8d86 100644 --- a/cli/args/flags.rs +++ b/cli/args/flags.rs @@ -234,7 +234,7 @@ impl Default for DenoSubcommand { } } -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, Copy, PartialEq)] pub enum TypeCheckMode { /// Type-check all modules. All, @@ -305,7 +305,6 @@ pub struct Flags { pub compat: bool, pub no_prompt: bool, pub reload: bool, - pub repl: bool, pub seed: Option<u64>, pub unstable: bool, pub unsafely_ignore_certificate_errors: Option<Vec<String>>, @@ -571,7 +570,6 @@ pub fn flags_from_vec(args: Vec<String>) -> clap::Result<Flags> { } fn handle_repl_flags(flags: &mut Flags, repl_flags: ReplFlags) { - flags.repl = true; flags.subcommand = DenoSubcommand::Repl(repl_flags); flags.allow_net = Some(vec![]); flags.allow_env = Some(vec![]); @@ -4022,7 +4020,6 @@ mod tests { assert_eq!( r.unwrap(), Flags { - repl: true, subcommand: DenoSubcommand::Repl(ReplFlags { eval_files: None, eval: None @@ -4047,7 +4044,6 @@ mod tests { assert_eq!( r.unwrap(), Flags { - repl: true, subcommand: DenoSubcommand::Repl(ReplFlags { eval_files: None, eval: None @@ -4085,7 +4081,6 @@ mod tests { assert_eq!( r.unwrap(), Flags { - repl: true, subcommand: DenoSubcommand::Repl(ReplFlags { eval_files: None, eval: Some("console.log('hello');".to_string()), @@ -4110,7 +4105,6 @@ mod tests { assert_eq!( r.unwrap(), Flags { - repl: true, subcommand: DenoSubcommand::Repl(ReplFlags { eval_files: Some(vec![ "./a.js".to_string(), @@ -4770,7 +4764,6 @@ mod tests { assert_eq!( r.unwrap(), Flags { - repl: true, subcommand: DenoSubcommand::Repl(ReplFlags { eval_files: None, eval: Some("console.log('hello');".to_string()), @@ -4845,7 +4838,6 @@ mod tests { assert_eq!( r.unwrap(), Flags { - repl: true, subcommand: DenoSubcommand::Repl(ReplFlags { eval_files: None, eval: None diff --git a/cli/args/mod.rs b/cli/args/mod.rs index 375a53122..757c6a8f4 100644 --- a/cli/args/mod.rs +++ b/cli/args/mod.rs @@ -5,5 +5,441 @@ pub mod flags; mod flags_allow_net; -pub use config_file::*; +pub use config_file::CompilerOptions; +pub use config_file::ConfigFile; +pub use config_file::EmitConfigOptions; +pub use config_file::FmtConfig; +pub use config_file::FmtOptionsConfig; +pub use config_file::LintConfig; +pub use config_file::LintRulesConfig; +pub use config_file::MaybeImportsResult; +pub use config_file::ProseWrap; +pub use config_file::TsConfig; pub use flags::*; + +use deno_ast::ModuleSpecifier; +use deno_core::anyhow::anyhow; +use deno_core::anyhow::bail; +use deno_core::anyhow::Context; +use deno_core::error::AnyError; +use deno_core::normalize_path; +use deno_core::url::Url; +use deno_runtime::colors; +use deno_runtime::deno_tls::rustls::RootCertStore; +use deno_runtime::inspector_server::InspectorServer; +use deno_runtime::permissions::PermissionsOptions; +use std::collections::BTreeMap; +use std::env; +use std::net::SocketAddr; +use std::path::PathBuf; + +use crate::compat; +use crate::deno_dir::DenoDir; +use crate::emit::get_ts_config_for_emit; +use crate::emit::TsConfigType; +use crate::emit::TsConfigWithIgnoredOptions; +use crate::emit::TsTypeLib; +use crate::file_fetcher::get_root_cert_store; +use crate::file_fetcher::CacheSetting; +use crate::lockfile::Lockfile; +use crate::version; + +/// Holds the common configuration used by many sub commands +/// and provides some helper function for creating common objects. +pub struct RootConfig { + // the source of the configuration is a detail the rest of the + // application need not concern itself with, so keep these private + flags: Flags, + maybe_config_file: Option<ConfigFile>, +} + +impl RootConfig { + pub fn from_flags(flags: Flags) -> Result<Self, AnyError> { + if let Some(insecure_allowlist) = + flags.unsafely_ignore_certificate_errors.as_ref() + { + let domains = if insecure_allowlist.is_empty() { + "for all hostnames".to_string() + } else { + format!("for: {}", insecure_allowlist.join(", ")) + }; + let msg = + format!("DANGER: TLS certificate validation is disabled {}", domains); + eprintln!("{}", colors::yellow(msg)); + } + + let maybe_config_file = ConfigFile::discover(&flags)?; + Ok(Self { + maybe_config_file, + flags, + }) + } + + pub fn maybe_config_file_specifier(&self) -> Option<ModuleSpecifier> { + self.maybe_config_file.as_ref().map(|f| f.specifier.clone()) + } + + pub fn ts_type_lib_window(&self) -> TsTypeLib { + if self.flags.unstable { + TsTypeLib::UnstableDenoWindow + } else { + TsTypeLib::DenoWindow + } + } + + pub fn ts_type_lib_worker(&self) -> TsTypeLib { + if self.flags.unstable { + TsTypeLib::UnstableDenoWorker + } else { + TsTypeLib::DenoWorker + } + } + + pub fn cache_setting(&self) -> CacheSetting { + if self.flags.cached_only { + CacheSetting::Only + } else if !self.flags.cache_blocklist.is_empty() { + CacheSetting::ReloadSome(self.flags.cache_blocklist.clone()) + } else if self.flags.reload { + CacheSetting::ReloadAll + } else { + CacheSetting::Use + } + } + + pub fn resolve_deno_dir(&self) -> Result<DenoDir, AnyError> { + Ok(DenoDir::new(self.maybe_custom_root())?) + } + + /// Based on an optional command line import map path and an optional + /// configuration file, return a resolved module specifier to an import map. + pub fn resolve_import_map_path( + &self, + ) -> Result<Option<ModuleSpecifier>, AnyError> { + resolve_import_map_specifier( + self.flags.import_map_path.as_deref(), + self.maybe_config_file.as_ref(), + ) + } + + pub fn resolve_root_cert_store(&self) -> Result<RootCertStore, AnyError> { + get_root_cert_store( + None, + self.flags.ca_stores.clone(), + self.flags.ca_file.clone(), + ) + } + + pub fn resolve_ts_config_for_emit( + &self, + config_type: TsConfigType, + ) -> Result<TsConfigWithIgnoredOptions, AnyError> { + get_ts_config_for_emit(config_type, self.maybe_config_file.as_ref()) + } + + /// Resolves the storage key to use based on the current flags, config, or main module. + pub fn resolve_storage_key( + &self, + main_module: &ModuleSpecifier, + ) -> Option<String> { + if let Some(location) = &self.flags.location { + // if a location is set, then the ascii serialization of the location is + // used, unless the origin is opaque, and then no storage origin is set, as + // we can't expect the origin to be reproducible + let storage_origin = location.origin().ascii_serialization(); + if storage_origin == "null" { + None + } else { + Some(storage_origin) + } + } else if let Some(config_file) = &self.maybe_config_file { + // otherwise we will use the path to the config file + Some(config_file.specifier.to_string()) + } else { + // otherwise we will use the path to the main module + Some(main_module.to_string()) + } + } + + pub fn resolve_inspector_server(&self) -> Option<InspectorServer> { + let maybe_inspect_host = self.flags.inspect.or(self.flags.inspect_brk); + maybe_inspect_host + .map(|host| InspectorServer::new(host, version::get_user_agent())) + } + + pub fn resolve_lock_file(&self) -> Result<Option<Lockfile>, AnyError> { + if let Some(filename) = &self.flags.lock { + let lockfile = Lockfile::new(filename.clone(), self.flags.lock_write)?; + Ok(Some(lockfile)) + } else { + Ok(None) + } + } + + pub fn resolve_tasks_config( + &self, + ) -> Result<BTreeMap<String, String>, AnyError> { + if let Some(config_file) = &self.maybe_config_file { + config_file.resolve_tasks_config() + } else { + bail!("No config file found") + } + } + + /// Return the implied JSX import source module. + pub fn to_maybe_jsx_import_source_module(&self) -> Option<String> { + self + .maybe_config_file + .as_ref() + .and_then(|c| c.to_maybe_jsx_import_source_module()) + } + + /// Return any imports that should be brought into the scope of the module + /// graph. + pub fn to_maybe_imports(&self) -> MaybeImportsResult { + let mut imports = Vec::new(); + if let Some(config_file) = &self.maybe_config_file { + if let Some(config_imports) = config_file.to_maybe_imports()? { + imports.extend(config_imports); + } + } + if self.flags.compat { + imports.extend(compat::get_node_imports()); + } + if imports.is_empty() { + Ok(None) + } else { + Ok(Some(imports)) + } + } + + 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 to_fmt_config(&self) -> Result<Option<FmtConfig>, AnyError> { + if let Some(config) = &self.maybe_config_file { + config.to_fmt_config() + } else { + Ok(None) + } + } + + /// Vector of user script CLI arguments. + pub fn argv(&self) -> &Vec<String> { + &self.flags.argv + } + + pub fn check_js(&self) -> bool { + self + .maybe_config_file + .as_ref() + .map(|cf| cf.get_check_js()) + .unwrap_or(false) + } + + pub fn compat(&self) -> bool { + self.flags.compat + } + + pub fn coverage_dir(&self) -> Option<&String> { + self.flags.coverage_dir.as_ref() + } + + pub fn enable_testing_features(&self) -> bool { + self.flags.enable_testing_features + } + + pub fn inspect_brk(&self) -> Option<SocketAddr> { + self.flags.inspect_brk + } + + pub fn log_level(&self) -> Option<log::Level> { + self.flags.log_level + } + + pub fn location_flag(&self) -> Option<&Url> { + self.flags.location.as_ref() + } + + pub fn maybe_custom_root(&self) -> Option<PathBuf> { + self + .flags + .cache_path + .clone() + .or_else(|| env::var("DENO_DIR").map(String::into).ok()) + } + + pub fn no_clear_screen(&self) -> bool { + self.flags.no_clear_screen + } + + pub fn no_remote(&self) -> bool { + self.flags.no_remote + } + + pub fn permissions_options(&self) -> PermissionsOptions { + self.flags.permissions_options() + } + + pub fn reload_flag(&self) -> bool { + self.flags.reload + } + + pub fn seed(&self) -> Option<u64> { + self.flags.seed + } + + pub fn sub_command(&self) -> &DenoSubcommand { + &self.flags.subcommand + } + + pub fn type_check_mode(&self) -> TypeCheckMode { + self.flags.type_check_mode + } + + pub fn unsafely_ignore_certificate_errors(&self) -> Option<&Vec<String>> { + self.flags.unsafely_ignore_certificate_errors.as_ref() + } + + pub fn unstable(&self) -> bool { + self.flags.unstable + } + + pub fn watch_paths(&self) -> Option<&Vec<PathBuf>> { + self.flags.watch.as_ref() + } +} + +fn resolve_import_map_specifier( + maybe_import_map_path: Option<&str>, + maybe_config_file: Option<&ConfigFile>, +) -> Result<Option<ModuleSpecifier>, AnyError> { + if let Some(import_map_path) = maybe_import_map_path { + if let Some(config_file) = &maybe_config_file { + if config_file.to_import_map_path().is_some() { + log::warn!("{} the configuration file \"{}\" contains an entry for \"importMap\" that is being ignored.", crate::colors::yellow("Warning"), config_file.specifier); + } + } + let specifier = deno_core::resolve_url_or_path(import_map_path) + .context(format!("Bad URL (\"{}\") for import map.", import_map_path))?; + return Ok(Some(specifier)); + } else if let Some(config_file) = &maybe_config_file { + // when the import map is specifier in a config file, it needs to be + // resolved relative to the config file, versus the CWD like with the flag + // and with config files, we support both local and remote config files, + // so we have treat them differently. + if let Some(import_map_path) = config_file.to_import_map_path() { + let specifier = + // with local config files, it might be common to specify an import + // map like `"importMap": "import-map.json"`, which is resolvable if + // the file is resolved like a file path, so we will coerce the config + // file into a file path if possible and join the import map path to + // the file path. + if let Ok(config_file_path) = config_file.specifier.to_file_path() { + let import_map_file_path = normalize_path(config_file_path + .parent() + .ok_or_else(|| { + anyhow!("Bad config file specifier: {}", config_file.specifier) + })? + .join(&import_map_path)); + ModuleSpecifier::from_file_path(import_map_file_path).unwrap() + // otherwise if the config file is remote, we have no choice but to + // use "import resolution" with the config file as the base. + } else { + deno_core::resolve_import(&import_map_path, config_file.specifier.as_str()) + .context(format!( + "Bad URL (\"{}\") for import map.", + import_map_path + ))? + }; + return Ok(Some(specifier)); + } + } + Ok(None) +} + +#[cfg(test)] +mod test { + use super::*; + + #[cfg(not(windows))] + #[test] + fn resolve_import_map_config_file() { + let config_text = r#"{ + "importMap": "import_map.json" + }"#; + let config_specifier = + ModuleSpecifier::parse("file:///deno/deno.jsonc").unwrap(); + let config_file = ConfigFile::new(config_text, &config_specifier).unwrap(); + let actual = resolve_import_map_specifier(None, Some(&config_file)); + assert!(actual.is_ok()); + let actual = actual.unwrap(); + assert_eq!( + actual, + Some(ModuleSpecifier::parse("file:///deno/import_map.json").unwrap()) + ); + } + + #[test] + fn resolve_import_map_config_file_remote() { + let config_text = r#"{ + "importMap": "./import_map.json" + }"#; + let config_specifier = + ModuleSpecifier::parse("https://example.com/deno.jsonc").unwrap(); + let config_file = ConfigFile::new(config_text, &config_specifier).unwrap(); + let actual = resolve_import_map_specifier(None, Some(&config_file)); + assert!(actual.is_ok()); + let actual = actual.unwrap(); + assert_eq!( + actual, + Some( + ModuleSpecifier::parse("https://example.com/import_map.json").unwrap() + ) + ); + } + + #[test] + fn resolve_import_map_flags_take_precedence() { + let config_text = r#"{ + "importMap": "import_map.json" + }"#; + let config_specifier = + ModuleSpecifier::parse("file:///deno/deno.jsonc").unwrap(); + let config_file = ConfigFile::new(config_text, &config_specifier).unwrap(); + let actual = + resolve_import_map_specifier(Some("import-map.json"), Some(&config_file)); + let import_map_path = + std::env::current_dir().unwrap().join("import-map.json"); + let expected_specifier = + ModuleSpecifier::from_file_path(&import_map_path).unwrap(); + assert!(actual.is_ok()); + let actual = actual.unwrap(); + assert_eq!(actual, Some(expected_specifier)); + } + + #[test] + fn resolve_import_map_none() { + let config_text = r#"{}"#; + let config_specifier = + ModuleSpecifier::parse("file:///deno/deno.jsonc").unwrap(); + let config_file = ConfigFile::new(config_text, &config_specifier).unwrap(); + let actual = resolve_import_map_specifier(None, Some(&config_file)); + assert!(actual.is_ok()); + let actual = actual.unwrap(); + assert_eq!(actual, None); + } + + #[test] + fn resolve_import_map_no_config() { + let actual = resolve_import_map_specifier(None, None); + assert!(actual.is_ok()); + let actual = actual.unwrap(); + assert_eq!(actual, None); + } +} diff --git a/cli/emit.rs b/cli/emit.rs index 8246e1720..329eb4f5d 100644 --- a/cli/emit.rs +++ b/cli/emit.rs @@ -4,9 +4,9 @@ //! populate a cache, emit files, and transform a graph into the structures for //! loading into an isolate. +use crate::args::config_file::IgnoredCompilerOptions; use crate::args::ConfigFile; use crate::args::EmitConfigOptions; -use crate::args::IgnoredCompilerOptions; use crate::args::TsConfig; use crate::args::TypeCheckMode; use crate::cache::CacheType; @@ -29,7 +29,6 @@ use deno_core::serde::Serialize; use deno_core::serde::Serializer; use deno_core::serde_json; use deno_core::serde_json::json; -use deno_core::serde_json::Value; use deno_core::ModuleSpecifier; use deno_graph::MediaType; use deno_graph::ModuleGraph; @@ -127,42 +126,6 @@ impl<T: Cacher> EmitCache for T { } } -/// Represents the "default" type library that should be used when type -/// checking the code in the module graph. Note that a user provided config -/// of `"lib"` would override this value. -#[derive(Debug, Clone, Eq, Hash, PartialEq)] -pub enum TypeLib { - DenoWindow, - DenoWorker, - UnstableDenoWindow, - UnstableDenoWorker, -} - -impl Default for TypeLib { - fn default() -> Self { - Self::DenoWindow - } -} - -impl Serialize for TypeLib { - fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> - where - S: Serializer, - { - let value = match self { - Self::DenoWindow => vec!["deno.window".to_string()], - Self::DenoWorker => vec!["deno.worker".to_string()], - Self::UnstableDenoWindow => { - vec!["deno.window".to_string(), "deno.unstable".to_string()] - } - Self::UnstableDenoWorker => { - vec!["deno.worker".to_string(), "deno.unstable".to_string()] - } - }; - Serialize::serialize(&value, serializer) - } -} - /// A structure representing stats from an emit operation for a graph. #[derive(Clone, Debug, Default, Eq, PartialEq)] pub struct Stats(pub Vec<(String, u32)>); @@ -197,28 +160,68 @@ impl fmt::Display for Stats { } } +/// Represents the "default" type library that should be used when type +/// checking the code in the module graph. Note that a user provided config +/// of `"lib"` would override this value. +#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)] +pub enum TsTypeLib { + DenoWindow, + DenoWorker, + UnstableDenoWindow, + UnstableDenoWorker, +} + +impl Default for TsTypeLib { + fn default() -> Self { + Self::DenoWindow + } +} + +impl Serialize for TsTypeLib { + fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> + where + S: Serializer, + { + let value = match self { + Self::DenoWindow => vec!["deno.window".to_string()], + Self::DenoWorker => vec!["deno.worker".to_string()], + Self::UnstableDenoWindow => { + vec!["deno.window".to_string(), "deno.unstable".to_string()] + } + Self::UnstableDenoWorker => { + vec!["deno.worker".to_string(), "deno.unstable".to_string()] + } + }; + Serialize::serialize(&value, serializer) + } +} + /// An enum that represents the base tsc configuration to return. -pub enum ConfigType { +pub enum TsConfigType { /// Return a configuration for bundling, using swc to emit the bundle. This is /// independent of type checking. Bundle, /// Return a configuration to use tsc to type check and optionally emit. This /// is independent of either bundling or just emitting via swc - Check { lib: TypeLib, tsc_emit: bool }, + Check { lib: TsTypeLib, tsc_emit: bool }, /// Return a configuration to use swc to emit single module files. Emit, } -/// For a given configuration type and optionally a configuration file, return a -/// tuple of the resulting `TsConfig` struct and optionally any user -/// configuration options that were ignored. -pub fn get_ts_config( - config_type: ConfigType, +pub struct TsConfigWithIgnoredOptions { + pub ts_config: TsConfig, + pub maybe_ignored_options: Option<IgnoredCompilerOptions>, +} + +/// For a given configuration type and optionally a configuration file, +/// return a `TsConfig` struct and optionally any user configuration +/// options that were ignored. +pub fn get_ts_config_for_emit( + config_type: TsConfigType, maybe_config_file: Option<&ConfigFile>, - maybe_user_config: Option<&HashMap<String, Value>>, -) -> Result<(TsConfig, Option<IgnoredCompilerOptions>), AnyError> { +) -> Result<TsConfigWithIgnoredOptions, AnyError> { let mut ts_config = match config_type { - ConfigType::Bundle => TsConfig::new(json!({ + TsConfigType::Bundle => TsConfig::new(json!({ "checkJs": false, "emitDecoratorMetadata": false, "importsNotUsedAsValues": "remove", @@ -229,7 +232,7 @@ pub fn get_ts_config( "jsxFactory": "React.createElement", "jsxFragmentFactory": "React.Fragment", })), - ConfigType::Check { tsc_emit, lib } => { + TsConfigType::Check { tsc_emit, lib } => { let mut ts_config = TsConfig::new(json!({ "allowJs": true, "allowSyntheticDefaultImports": true, @@ -263,7 +266,7 @@ pub fn get_ts_config( } ts_config } - ConfigType::Emit => TsConfig::new(json!({ + TsConfigType::Emit => TsConfig::new(json!({ "checkJs": false, "emitDecoratorMetadata": false, "importsNotUsedAsValues": "remove", @@ -276,15 +279,15 @@ pub fn get_ts_config( "resolveJsonModule": true, })), }; - let maybe_ignored_options = if let Some(user_options) = maybe_user_config { - ts_config.merge_user_config(user_options)? - } else { - ts_config.merge_tsconfig_from_config_file(maybe_config_file)? - }; + let maybe_ignored_options = + ts_config.merge_tsconfig_from_config_file(maybe_config_file)?; ts_config.merge(&json!({ "moduleDetection": "force", })); - Ok((ts_config, maybe_ignored_options)) + Ok(TsConfigWithIgnoredOptions { + ts_config, + maybe_ignored_options, + }) } /// Transform the graph into root specifiers that we can feed `tsc`. We have to diff --git a/cli/graph_util.rs b/cli/graph_util.rs index 533c9a493..991115319 100644 --- a/cli/graph_util.rs +++ b/cli/graph_util.rs @@ -1,8 +1,9 @@ // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. use crate::colors; -use crate::emit::TypeLib; +use crate::emit::TsTypeLib; use crate::errors::get_error_class_name; + use deno_core::error::custom_error; use deno_core::error::AnyError; use deno_core::ModuleSpecifier; @@ -43,7 +44,7 @@ pub enum ModuleEntry { ts_check: bool, /// A set of type libs that the module has passed a type check with this /// session. This would consist of window, worker or both. - checked_libs: HashSet<TypeLib>, + checked_libs: HashSet<TsTypeLib>, maybe_types: Option<Resolved>, }, Configuration { @@ -385,7 +386,7 @@ impl GraphData { pub fn set_type_checked( &mut self, roots: &[(ModuleSpecifier, ModuleKind)], - lib: &TypeLib, + lib: TsTypeLib, ) { let specifiers: Vec<ModuleSpecifier> = match self.walk(roots, true, true, true) { @@ -396,7 +397,7 @@ impl GraphData { if let ModuleEntry::Module { checked_libs, .. } = self.modules.get_mut(&specifier).unwrap() { - checked_libs.insert(lib.clone()); + checked_libs.insert(lib); } } } @@ -405,7 +406,7 @@ impl GraphData { pub fn is_type_checked( &self, roots: &[(ModuleSpecifier, ModuleKind)], - lib: &TypeLib, + lib: &TsTypeLib, ) -> bool { roots.iter().all(|(r, _)| { let found = self.follow_redirect(r); diff --git a/cli/lsp/cache.rs b/cli/lsp/cache.rs index 0f7608278..fc8b50c4a 100644 --- a/cli/lsp/cache.rs +++ b/cli/lsp/cache.rs @@ -49,13 +49,13 @@ impl CacheServer { let _join_handle = thread::spawn(move || { let runtime = create_basic_runtime(); runtime.block_on(async { - let ps = ProcState::build(Arc::new(Flags { + let ps = ProcState::build(Flags { cache_path: maybe_cache_path, ca_stores: maybe_ca_stores, ca_file: maybe_ca_file, unsafely_ignore_certificate_errors, ..Default::default() - })) + }) .await .unwrap(); let maybe_import_map_resolver = diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs index 4a703c719..909d17bfe 100644 --- a/cli/lsp/language_server.rs +++ b/cli/lsp/language_server.rs @@ -364,7 +364,7 @@ impl Inner { if let Some(root_uri) = &self.config.root_uri { let root_path = fs_util::specifier_to_file_path(root_uri)?; let mut checked = std::collections::HashSet::new(); - let maybe_config = crate::args::discover_from(&root_path, &mut checked)?; + let maybe_config = ConfigFile::discover_from(&root_path, &mut checked)?; Ok(maybe_config.map(|c| { lsp_log!(" Auto-resolved configuration file: \"{}\"", c.specifier); c diff --git a/cli/lsp/testing/execution.rs b/cli/lsp/testing/execution.rs index 83f74e5ed..94301e198 100644 --- a/cli/lsp/testing/execution.rs +++ b/cli/lsp/testing/execution.rs @@ -8,7 +8,6 @@ use crate::args::flags_from_vec; use crate::args::DenoSubcommand; use crate::checksum; use crate::create_main_worker; -use crate::emit; use crate::located_script_name; use crate::lsp::client::Client; use crate::lsp::client::TestingNotification; @@ -314,9 +313,9 @@ impl TestRun { let args = self.get_args(); lsp_log!("Executing test run with arguments: {}", args.join(" ")); let flags = flags_from_vec(args.into_iter().map(String::from).collect())?; - let ps = proc_state::ProcState::build(Arc::new(flags)).await?; + let ps = proc_state::ProcState::build(flags).await?; let permissions = - Permissions::from_options(&ps.flags.permissions_options()); + Permissions::from_options(&ps.config.permissions_options()); test::check_specifiers( &ps, permissions.clone(), @@ -325,7 +324,6 @@ impl TestRun { .iter() .map(|s| (s.clone(), test::TestMode::Executable)) .collect(), - emit::TypeLib::DenoWindow, ) .await?; @@ -333,7 +331,7 @@ impl TestRun { let sender = TestEventSender::new(sender); let (concurrent_jobs, fail_fast) = - if let DenoSubcommand::Test(test_flags) = &ps.flags.subcommand { + if let DenoSubcommand::Test(test_flags) = ps.config.sub_command() { ( test_flags.concurrent_jobs.into(), test_flags.fail_fast.map(|count| count.into()), diff --git a/cli/main.rs b/cli/main.rs index de44add17..ede162d1b 100644 --- a/cli/main.rs +++ b/cli/main.rs @@ -36,7 +36,6 @@ mod version; mod windows_util; use crate::args::flags_from_vec; -use crate::args::resolve_import_map_specifier; use crate::args::BenchFlags; use crate::args::BundleFlags; use crate::args::CacheFlags; @@ -60,6 +59,7 @@ use crate::args::TypeCheckMode; use crate::args::UninstallFlags; use crate::args::UpgradeFlags; use crate::args::VendorFlags; +use crate::emit::TsConfigType; use crate::file_fetcher::File; use crate::file_watcher::ResolutionResult; use crate::fmt_errors::format_js_error; @@ -70,6 +70,7 @@ use crate::proc_state::ProcState; use crate::resolver::ImportMapResolver; use crate::resolver::JsxResolver; +use args::RootConfig; use deno_ast::MediaType; use deno_core::error::generic_error; use deno_core::error::AnyError; @@ -108,7 +109,7 @@ use std::sync::Arc; fn create_web_worker_preload_module_callback( ps: ProcState, ) -> Arc<PreloadModuleCb> { - let compat = ps.flags.compat; + let compat = ps.config.compat(); Arc::new(move |mut worker| { let fut = async move { @@ -143,30 +144,30 @@ fn create_web_worker_callback( let options = WebWorkerOptions { bootstrap: BootstrapOptions { - args: ps.flags.argv.clone(), + args: ps.config.argv().clone(), cpu_count: std::thread::available_parallelism() .map(|p| p.get()) .unwrap_or(1), debug_flag: ps - .flags - .log_level + .config + .log_level() .map_or(false, |l| l == log::Level::Debug), - enable_testing_features: ps.flags.enable_testing_features, + enable_testing_features: ps.config.enable_testing_features(), location: Some(args.main_module.clone()), no_color: !colors::use_color(), is_tty: colors::is_tty(), runtime_version: version::deno(), ts_version: version::TYPESCRIPT.to_string(), - unstable: ps.flags.unstable, + unstable: ps.config.unstable(), user_agent: version::get_user_agent(), }, extensions, unsafely_ignore_certificate_errors: ps - .flags - .unsafely_ignore_certificate_errors - .clone(), - root_cert_store: ps.root_cert_store.clone(), - seed: ps.flags.seed, + .config + .unsafely_ignore_certificate_errors() + .map(ToOwned::to_owned), + root_cert_store: Some(ps.root_cert_store.clone()), + seed: ps.config.seed(), module_loader, create_web_worker_cb, preload_module_cb, @@ -203,31 +204,14 @@ pub fn create_main_worker( let module_loader = CliModuleLoader::new(ps.clone()); let maybe_inspector_server = ps.maybe_inspector_server.clone(); - let should_break_on_first_statement = ps.flags.inspect_brk.is_some(); + let should_break_on_first_statement = ps.config.inspect_brk().is_some(); let create_web_worker_cb = create_web_worker_callback(ps.clone(), stdio.clone()); let web_worker_preload_module_cb = create_web_worker_preload_module_callback(ps.clone()); - let maybe_storage_key = if let Some(location) = &ps.flags.location { - // if a location is set, then the ascii serialization of the location is - // used, unless the origin is opaque, and then no storage origin is set, as - // we can't expect the origin to be reproducible - let storage_origin = location.origin().ascii_serialization(); - if storage_origin == "null" { - None - } else { - Some(storage_origin) - } - } else if let Some(config_file) = &ps.maybe_config_file { - // otherwise we will use the path to the config file - Some(config_file.specifier.to_string()) - } else { - // otherwise we will use the path to the main module - Some(main_module.to_string()) - }; - + let maybe_storage_key = ps.config.resolve_storage_key(&main_module); let origin_storage_dir = maybe_storage_key.map(|key| { ps.dir .root @@ -241,27 +225,30 @@ pub fn create_main_worker( let options = WorkerOptions { bootstrap: BootstrapOptions { - args: ps.flags.argv.clone(), + args: ps.config.argv().clone(), cpu_count: std::thread::available_parallelism() .map(|p| p.get()) .unwrap_or(1), - debug_flag: ps.flags.log_level.map_or(false, |l| l == log::Level::Debug), - enable_testing_features: ps.flags.enable_testing_features, - location: ps.flags.location.clone(), + debug_flag: ps + .config + .log_level() + .map_or(false, |l| l == log::Level::Debug), + enable_testing_features: ps.config.enable_testing_features(), + location: ps.config.location_flag().map(ToOwned::to_owned), no_color: !colors::use_color(), is_tty: colors::is_tty(), runtime_version: version::deno(), ts_version: version::TYPESCRIPT.to_string(), - unstable: ps.flags.unstable, + unstable: ps.config.unstable(), user_agent: version::get_user_agent(), }, extensions, unsafely_ignore_certificate_errors: ps - .flags - .unsafely_ignore_certificate_errors - .clone(), - root_cert_store: ps.root_cert_store.clone(), - seed: ps.flags.seed, + .config + .unsafely_ignore_certificate_errors() + .map(ToOwned::to_owned), + root_cert_store: Some(ps.root_cert_store.clone()), + seed: ps.config.seed(), source_map_getter: Some(Box::new(ps.clone())), format_js_error_fn: Some(Arc::new(format_js_error)), create_web_worker_cb, @@ -412,7 +399,7 @@ async fn compile_command( )?; let module_specifier = resolve_url_or_path(&compile_flags.source_file)?; - let ps = ProcState::build(Arc::new(flags)).await?; + let ps = ProcState::build(flags).await?; let deno_dir = &ps.dir; let output_path = @@ -460,7 +447,7 @@ async fn info_command( flags: Flags, info_flags: InfoFlags, ) -> Result<i32, AnyError> { - let ps = ProcState::build(Arc::new(flags)).await?; + let ps = ProcState::build(flags).await?; if let Some(specifier) = info_flags.file { let specifier = resolve_url_or_path(&specifier)?; let mut cache = cache::FetchCacher::new( @@ -472,10 +459,10 @@ async fn info_command( let maybe_locker = lockfile::as_maybe_locker(ps.lockfile.clone()); let maybe_import_map_resolver = ps.maybe_import_map.clone().map(ImportMapResolver::new); - let maybe_jsx_resolver = ps.maybe_config_file.as_ref().and_then(|cf| { - cf.to_maybe_jsx_import_source_module() - .map(|im| JsxResolver::new(im, maybe_import_map_resolver.clone())) - }); + let maybe_jsx_resolver = ps + .config + .to_maybe_jsx_import_source_module() + .map(|im| JsxResolver::new(im, maybe_import_map_resolver.clone())); let maybe_resolver = if maybe_jsx_resolver.is_some() { maybe_jsx_resolver.as_ref().map(|jr| jr.as_resolver()) } else { @@ -502,7 +489,7 @@ async fn info_command( } } else { // If it was just "deno info" print location of caches and exit - print_cache_info(&ps, info_flags.json, ps.flags.location.as_ref())?; + print_cache_info(&ps, info_flags.json, ps.config.location_flag())?; } Ok(0) } @@ -516,7 +503,7 @@ async fn install_command( preload_flags.inspect_brk = None; let permissions = Permissions::from_options(&preload_flags.permissions_options()); - let ps = ProcState::build(Arc::new(preload_flags)).await?; + let ps = ProcState::build(preload_flags).await?; let main_module = resolve_url_or_path(&install_flags.module_url)?; let mut worker = create_main_worker( &ps, @@ -560,19 +547,15 @@ async fn cache_command( flags: Flags, cache_flags: CacheFlags, ) -> Result<i32, AnyError> { - let lib = if flags.unstable { - emit::TypeLib::UnstableDenoWindow - } else { - emit::TypeLib::DenoWindow - }; - let ps = ProcState::build(Arc::new(flags)).await?; + let ps = ProcState::build(flags).await?; + let lib = ps.config.ts_type_lib_window(); for file in cache_flags.files { let specifier = resolve_url_or_path(&file)?; ps.prepare_module_load( vec![specifier], false, - lib.clone(), + lib, Permissions::allow_all(), Permissions::allow_all(), false, @@ -605,7 +588,7 @@ async fn eval_command( let main_module = resolve_url_or_path(&format!("./$deno$eval.{}", eval_flags.ext))?; let permissions = Permissions::from_options(&flags.permissions_options()); - let ps = ProcState::build(Arc::new(flags)).await?; + let ps = ProcState::build(flags).await?; let mut worker = create_main_worker( &ps, main_module.clone(), @@ -634,7 +617,7 @@ async fn eval_command( // to allow module access by TS compiler. ps.file_fetcher.insert_cached(file); debug!("main_module {}", &main_module); - if ps.flags.compat { + if ps.config.compat() { worker.execute_side_module(&compat::GLOBAL_URL).await?; } worker.execute_main_module(&main_module).await?; @@ -662,17 +645,13 @@ async fn create_graph_and_maybe_check( Permissions::allow_all(), ); let maybe_locker = lockfile::as_maybe_locker(ps.lockfile.clone()); - let maybe_imports = if let Some(config_file) = &ps.maybe_config_file { - config_file.to_maybe_imports()? - } else { - None - }; + let maybe_imports = ps.config.to_maybe_imports()?; let maybe_import_map_resolver = ps.maybe_import_map.clone().map(ImportMapResolver::new); - let maybe_jsx_resolver = ps.maybe_config_file.as_ref().and_then(|cf| { - cf.to_maybe_jsx_import_source_module() - .map(|im| JsxResolver::new(im, maybe_import_map_resolver.clone())) - }); + let maybe_jsx_resolver = ps + .config + .to_maybe_jsx_import_source_module() + .map(|im| JsxResolver::new(im, maybe_import_map_resolver.clone())); let maybe_resolver = if maybe_jsx_resolver.is_some() { maybe_jsx_resolver.as_ref().map(|jr| jr.as_resolver()) } else { @@ -694,49 +673,36 @@ async fn create_graph_and_maybe_check( .await, ); - let check_js = ps - .maybe_config_file - .as_ref() - .map(|cf| cf.get_check_js()) - .unwrap_or(false); + let check_js = ps.config.check_js(); graph_valid( &graph, - ps.flags.type_check_mode != TypeCheckMode::None, + ps.config.type_check_mode() != TypeCheckMode::None, check_js, )?; graph_lock_or_exit(&graph); - if ps.flags.type_check_mode != TypeCheckMode::None { - let lib = if ps.flags.unstable { - emit::TypeLib::UnstableDenoWindow - } else { - emit::TypeLib::DenoWindow - }; - let (ts_config, maybe_ignored_options) = emit::get_ts_config( - emit::ConfigType::Check { + if ps.config.type_check_mode() != TypeCheckMode::None { + let ts_config_result = + ps.config.resolve_ts_config_for_emit(TsConfigType::Check { tsc_emit: false, - lib, - }, - ps.maybe_config_file.as_ref(), - None, - )?; - if let Some(ignored_options) = maybe_ignored_options { + lib: ps.config.ts_type_lib_window(), + })?; + if let Some(ignored_options) = ts_config_result.maybe_ignored_options { eprintln!("{}", ignored_options); } - let maybe_config_specifier = - ps.maybe_config_file.as_ref().map(|cf| cf.specifier.clone()); + let maybe_config_specifier = ps.config.maybe_config_file_specifier(); let check_result = emit::check_and_maybe_emit( &graph.roots, Arc::new(RwLock::new(graph.as_ref().into())), &ps.dir.gen_cache, emit::CheckOptions { - type_check_mode: ps.flags.type_check_mode.clone(), + type_check_mode: ps.config.type_check_mode(), debug, emit_with_diagnostics: false, maybe_config_specifier, - ts_config, + ts_config: ts_config_result.ts_config, log_checks: true, - reload: ps.flags.reload, + reload: ps.config.reload_flag(), reload_exclusions: Default::default(), }, )?; @@ -752,17 +718,13 @@ async fn create_graph_and_maybe_check( fn bundle_module_graph( graph: &deno_graph::ModuleGraph, ps: &ProcState, - flags: &Flags, ) -> Result<deno_emit::BundleEmit, AnyError> { info!("{} {}", colors::green("Bundle"), graph.roots[0].0); - let (ts_config, maybe_ignored_options) = emit::get_ts_config( - emit::ConfigType::Bundle, - ps.maybe_config_file.as_ref(), - None, - )?; - if flags.type_check_mode == TypeCheckMode::None { - if let Some(ignored_options) = maybe_ignored_options { + let ts_config_result = + ps.config.resolve_ts_config_for_emit(TsConfigType::Bundle)?; + if ps.config.type_check_mode() == TypeCheckMode::None { + if let Some(ignored_options) = ts_config_result.maybe_ignored_options { eprintln!("{}", ignored_options); } } @@ -771,7 +733,7 @@ fn bundle_module_graph( graph, deno_emit::BundleOptions { bundle_type: deno_emit::BundleType::Module, - emit_options: ts_config.into(), + emit_options: ts_config_result.ts_config.into(), emit_ignore_directives: true, }, ) @@ -782,16 +744,16 @@ async fn bundle_command( bundle_flags: BundleFlags, ) -> Result<i32, AnyError> { let debug = flags.log_level == Some(log::Level::Debug); - let flags = Arc::new(flags); + let root_config = Arc::new(RootConfig::from_flags(flags)?); let resolver = |_| { - let flags = flags.clone(); + let root_config = root_config.clone(); let source_file1 = bundle_flags.source_file.clone(); let source_file2 = bundle_flags.source_file.clone(); async move { let module_specifier = resolve_url_or_path(&source_file1)?; debug!(">>>>> bundle START"); - let ps = ProcState::build(flags).await?; + let ps = ProcState::from_root_config(root_config).await?; let graph = create_graph_and_maybe_check(module_specifier, &ps, debug).await?; @@ -804,11 +766,10 @@ async fn bundle_command( }) .collect(); - if let Ok(Some(import_map_path)) = resolve_import_map_specifier( - ps.flags.import_map_path.as_deref(), - ps.maybe_config_file.as_ref(), - ) - .map(|ms| ms.and_then(|ref s| s.to_file_path().ok())) + if let Ok(Some(import_map_path)) = ps + .config + .resolve_import_map_path() + .map(|ms| ms.and_then(|ref s| s.to_file_path().ok())) { paths_to_watch.push(import_map_path); } @@ -830,7 +791,7 @@ async fn bundle_command( let operation = |(ps, graph): (ProcState, Arc<deno_graph::ModuleGraph>)| { let out_file = bundle_flags.out_file.clone(); async move { - let bundle_output = bundle_module_graph(graph.as_ref(), &ps, &ps.flags)?; + let bundle_output = bundle_module_graph(graph.as_ref(), &ps)?; debug!(">>>>> bundle END"); if let Some(out_file) = out_file.as_ref() { @@ -868,13 +829,13 @@ async fn bundle_command( } }; - if flags.watch.is_some() { + if root_config.watch_paths().is_some() { file_watcher::watch_func( resolver, operation, file_watcher::PrintConfig { job_name: "Bundle".to_string(), - clear_screen: !flags.no_clear_screen, + clear_screen: !root_config.no_clear_screen(), }, ) .await?; @@ -903,14 +864,10 @@ async fn format_command( flags: Flags, fmt_flags: FmtFlags, ) -> Result<i32, AnyError> { - let ps = ProcState::build(Arc::new(flags)).await?; - let maybe_fmt_config = if let Some(config_file) = &ps.maybe_config_file { - config_file.to_fmt_config()? - } else { - None - }; + let config = RootConfig::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(), @@ -918,8 +875,7 @@ async fn format_command( return Ok(0); } - tools::fmt::format(ps.flags.as_ref(), fmt_flags, maybe_fmt_config, &ps.dir) - .await?; + tools::fmt::format(&config, fmt_flags).await?; Ok(0) } @@ -928,16 +884,15 @@ async fn repl_command( repl_flags: ReplFlags, ) -> Result<i32, AnyError> { let main_module = resolve_url_or_path("./$deno$repl.ts").unwrap(); - let permissions = Permissions::from_options(&flags.permissions_options()); - let ps = ProcState::build(Arc::new(flags)).await?; + let ps = ProcState::build(flags).await?; let mut worker = create_main_worker( &ps, main_module.clone(), - permissions, + Permissions::from_options(&ps.config.permissions_options()), vec![], Default::default(), ); - if ps.flags.compat { + if ps.config.compat() { worker.execute_side_module(&compat::GLOBAL_URL).await?; compat::add_global_require(&mut worker.js_runtime, main_module.as_str())?; worker.run_event_loop(false).await?; @@ -949,13 +904,12 @@ async fn repl_command( } async fn run_from_stdin(flags: Flags) -> Result<i32, AnyError> { - let ps = ProcState::build(Arc::new(flags)).await?; - let permissions = Permissions::from_options(&ps.flags.permissions_options()); + let ps = ProcState::build(flags).await?; let main_module = resolve_url_or_path("./$deno$stdin.ts").unwrap(); let mut worker = create_main_worker( &ps.clone(), main_module.clone(), - permissions, + Permissions::from_options(&ps.config.permissions_options()), vec![], Default::default(), ); @@ -976,7 +930,7 @@ async fn run_from_stdin(flags: Flags) -> Result<i32, AnyError> { ps.file_fetcher.insert_cached(source_file); debug!("main_module {}", main_module); - if ps.flags.compat { + if ps.config.compat() { worker.execute_side_module(&compat::GLOBAL_URL).await?; } worker.execute_main_module(&main_module).await?; @@ -1068,8 +1022,9 @@ async fn run_with_watch(flags: Flags, script: String) -> Result<i32, AnyError> { let flags = flags.clone(); let permissions = Permissions::from_options(&flags.permissions_options()); async move { - let ps = ProcState::build_for_file_watcher(flags.clone(), sender.clone()) - .await?; + let ps = + ProcState::build_for_file_watcher((*flags).clone(), sender.clone()) + .await?; // We make use an module executor guard to ensure that unload is always fired when an // operation is called. let mut executor = FileWatcherModuleExecutor::new( @@ -1122,8 +1077,8 @@ async fn run_command( // map specified and bare specifier is used on the command line - this should // probably call `ProcState::resolve` instead let main_module = resolve_url_or_path(&run_flags.script)?; - let ps = ProcState::build(Arc::new(flags)).await?; - let permissions = Permissions::from_options(&ps.flags.permissions_options()); + let ps = ProcState::build(flags).await?; + let permissions = Permissions::from_options(&ps.config.permissions_options()); let mut worker = create_main_worker( &ps, main_module.clone(), @@ -1149,7 +1104,7 @@ async fn run_command( debug!("main_module {}", main_module); - if ps.flags.compat { + if ps.config.compat() { // TODO(bartlomieju): fix me assert_eq!(main_module.scheme(), "file"); @@ -1280,7 +1235,7 @@ async fn vendor_command( flags: Flags, vendor_flags: VendorFlags, ) -> Result<i32, AnyError> { - let ps = ProcState::build(Arc::new(flags)).await?; + let ps = ProcState::build(flags).await?; tools::vendor::vendor(ps, vendor_flags).await?; Ok(0) } diff --git a/cli/module_loader.rs b/cli/module_loader.rs index 77eb2d460..eda693ee9 100644 --- a/cli/module_loader.rs +++ b/cli/module_loader.rs @@ -1,6 +1,6 @@ // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. -use crate::emit::TypeLib; +use crate::emit::TsTypeLib; use crate::proc_state::ProcState; use deno_core::error::AnyError; @@ -16,7 +16,7 @@ use std::rc::Rc; use std::str; pub struct CliModuleLoader { - pub lib: TypeLib, + pub lib: TsTypeLib, /// The initial set of permissions used to resolve the static imports in the /// worker. They are decoupled from the worker (dynamic) permissions since /// read access errors must be raised based on the parent thread permissions. @@ -26,28 +26,16 @@ pub struct CliModuleLoader { impl CliModuleLoader { pub fn new(ps: ProcState) -> Rc<Self> { - let lib = if ps.flags.unstable { - TypeLib::UnstableDenoWindow - } else { - TypeLib::DenoWindow - }; - Rc::new(CliModuleLoader { - lib, + lib: ps.config.ts_type_lib_window(), root_permissions: Permissions::allow_all(), ps, }) } pub fn new_for_worker(ps: ProcState, permissions: Permissions) -> Rc<Self> { - let lib = if ps.flags.unstable { - TypeLib::UnstableDenoWorker - } else { - TypeLib::DenoWorker - }; - Rc::new(CliModuleLoader { - lib, + lib: ps.config.ts_type_lib_worker(), root_permissions: permissions, ps, }) @@ -97,13 +85,8 @@ impl ModuleLoader for CliModuleLoader { } else { self.root_permissions.clone() }; + let lib = self.lib; - let lib = match self.lib { - TypeLib::DenoWindow => crate::emit::TypeLib::DenoWindow, - TypeLib::DenoWorker => crate::emit::TypeLib::DenoWorker, - TypeLib::UnstableDenoWindow => crate::emit::TypeLib::UnstableDenoWindow, - TypeLib::UnstableDenoWorker => crate::emit::TypeLib::UnstableDenoWorker, - }; drop(state); async move { diff --git a/cli/proc_state.rs b/cli/proc_state.rs index 112307eb4..e857a2184 100644 --- a/cli/proc_state.rs +++ b/cli/proc_state.rs @@ -1,19 +1,17 @@ // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. -use crate::args::resolve_import_map_specifier; -use crate::args::ConfigFile; +use crate::args::DenoSubcommand; use crate::args::Flags; -use crate::args::MaybeImportsResult; +use crate::args::RootConfig; use crate::args::TypeCheckMode; use crate::cache; -use crate::colors; use crate::compat; use crate::compat::NodeEsmResolver; use crate::deno_dir; use crate::emit; use crate::emit::EmitCache; -use crate::file_fetcher::get_root_cert_store; -use crate::file_fetcher::CacheSetting; +use crate::emit::TsConfigType; +use crate::emit::TsTypeLib; use crate::file_fetcher::FileFetcher; use crate::graph_util::graph_lock_or_exit; use crate::graph_util::GraphData; @@ -23,7 +21,6 @@ use crate::lockfile::as_maybe_locker; use crate::lockfile::Lockfile; use crate::resolver::ImportMapResolver; use crate::resolver::JsxResolver; -use crate::version; use deno_ast::MediaType; use deno_core::anyhow::anyhow; @@ -68,17 +65,15 @@ use std::sync::Arc; pub struct ProcState(Arc<Inner>); pub struct Inner { - /// Flags parsed from `argv` contents. - pub flags: Arc<Flags>, pub dir: deno_dir::DenoDir, pub coverage_dir: Option<String>, pub file_fetcher: FileFetcher, + pub config: Arc<RootConfig>, graph_data: Arc<RwLock<GraphData>>, pub lockfile: Option<Arc<Mutex<Lockfile>>>, - pub maybe_config_file: Option<ConfigFile>, pub maybe_import_map: Option<Arc<ImportMap>>, pub maybe_inspector_server: Option<Arc<InspectorServer>>, - pub root_cert_store: Option<RootCertStore>, + pub root_cert_store: RootCertStore, pub blob_store: BlobStore, pub broadcast_channel: InMemoryBroadcastChannel, pub shared_array_buffer_store: SharedArrayBufferStore, @@ -95,30 +90,35 @@ impl Deref for ProcState { } impl ProcState { - pub async fn build(flags: Arc<Flags>) -> Result<Self, AnyError> { - Self::build_with_sender(flags, None).await + pub async fn build(flags: Flags) -> Result<Self, AnyError> { + Self::from_root_config(Arc::new(RootConfig::from_flags(flags)?)).await + } + + pub async fn from_root_config( + root_config: Arc<RootConfig>, + ) -> Result<Self, AnyError> { + Self::build_with_sender(root_config, None).await } pub async fn build_for_file_watcher( - flags: Arc<Flags>, + flags: Flags, files_to_watch_sender: tokio::sync::mpsc::UnboundedSender<Vec<PathBuf>>, ) -> Result<Self, AnyError> { - let ps = Self::build_with_sender( - flags.clone(), - Some(files_to_watch_sender.clone()), - ) - .await?; + // resolve the config each time + let root_config = Arc::new(RootConfig::from_flags(flags)?); + let ps = + Self::build_with_sender(root_config, Some(files_to_watch_sender.clone())) + .await?; // Add the extra files listed in the watch flag - if let Some(watch_paths) = &flags.watch { + if let Some(watch_paths) = ps.config.watch_paths() { files_to_watch_sender.send(watch_paths.clone()).unwrap(); } - if let Ok(Some(import_map_path)) = resolve_import_map_specifier( - ps.flags.import_map_path.as_deref(), - ps.maybe_config_file.as_ref(), - ) - .map(|ms| ms.and_then(|ref s| s.to_file_path().ok())) + if let Ok(Some(import_map_path)) = ps + .config + .resolve_import_map_path() + .map(|ms| ms.and_then(|ref s| s.to_file_path().ok())) { files_to_watch_sender.send(vec![import_map_path]).unwrap(); } @@ -127,73 +127,33 @@ impl ProcState { } async fn build_with_sender( - flags: Arc<Flags>, + root_config: Arc<RootConfig>, maybe_sender: Option<tokio::sync::mpsc::UnboundedSender<Vec<PathBuf>>>, ) -> Result<Self, AnyError> { - let maybe_custom_root = flags - .cache_path - .clone() - .or_else(|| env::var("DENO_DIR").map(String::into).ok()); - let dir = deno_dir::DenoDir::new(maybe_custom_root)?; - let deps_cache_location = dir.root.join("deps"); - let http_cache = http_cache::HttpCache::new(&deps_cache_location); - - let root_cert_store = get_root_cert_store( - None, - flags.ca_stores.clone(), - flags.ca_file.clone(), - )?; - - if let Some(insecure_allowlist) = - flags.unsafely_ignore_certificate_errors.as_ref() - { - let domains = if insecure_allowlist.is_empty() { - "for all hostnames".to_string() - } else { - format!("for: {}", insecure_allowlist.join(", ")) - }; - let msg = - format!("DANGER: TLS certificate validation is disabled {}", domains); - eprintln!("{}", colors::yellow(msg)); - } - - let cache_usage = if flags.cached_only { - CacheSetting::Only - } else if !flags.cache_blocklist.is_empty() { - CacheSetting::ReloadSome(flags.cache_blocklist.clone()) - } else if flags.reload { - CacheSetting::ReloadAll - } else { - CacheSetting::Use - }; - let blob_store = BlobStore::default(); let broadcast_channel = InMemoryBroadcastChannel::default(); let shared_array_buffer_store = SharedArrayBufferStore::default(); let compiled_wasm_module_store = CompiledWasmModuleStore::default(); - + let dir = root_config.resolve_deno_dir()?; + let deps_cache_location = dir.root.join("deps"); + let http_cache = http_cache::HttpCache::new(&deps_cache_location); + let root_cert_store = root_config.resolve_root_cert_store()?; + let cache_usage = root_config.cache_setting(); let file_fetcher = FileFetcher::new( http_cache, cache_usage, - !flags.no_remote, + !root_config.no_remote(), Some(root_cert_store.clone()), blob_store.clone(), - flags.unsafely_ignore_certificate_errors.clone(), + root_config + .unsafely_ignore_certificate_errors() + .map(ToOwned::to_owned), )?; - let lockfile = if let Some(filename) = &flags.lock { - let lockfile = Lockfile::new(filename.clone(), flags.lock_write)?; - Some(Arc::new(Mutex::new(lockfile))) - } else { - None - }; - - let maybe_config_file = crate::args::discover(&flags)?; - - let maybe_import_map_specifier = crate::args::resolve_import_map_specifier( - flags.import_map_path.as_deref(), - maybe_config_file.as_ref(), - )?; + let lockfile = root_config + .resolve_lock_file()? + .map(|f| Arc::new(Mutex::new(f))); + let maybe_import_map_specifier = root_config.resolve_import_map_path()?; let maybe_import_map = if let Some(import_map_specifier) = maybe_import_map_specifier { @@ -211,14 +171,12 @@ impl ProcState { None }; - let maybe_inspect_host = flags.inspect.or(flags.inspect_brk); - let maybe_inspector_server = maybe_inspect_host.map(|host| { - Arc::new(InspectorServer::new(host, version::get_user_agent())) - }); + let maybe_inspector_server = + root_config.resolve_inspector_server().map(Arc::new); - let coverage_dir = flags - .coverage_dir - .clone() + let coverage_dir = root_config + .coverage_dir() + .map(ToOwned::to_owned) .or_else(|| env::var("DENO_UNSTABLE_COVERAGE_DIR").ok()); // FIXME(bartlomieju): `NodeEsmResolver` is not aware of JSX resolver @@ -228,13 +186,12 @@ impl ProcState { ); let maybe_import_map_resolver = maybe_import_map.clone().map(ImportMapResolver::new); - let maybe_jsx_resolver = maybe_config_file.as_ref().and_then(|cf| { - cf.to_maybe_jsx_import_source_module() - .map(|im| JsxResolver::new(im, maybe_import_map_resolver.clone())) - }); + let maybe_jsx_resolver = root_config + .to_maybe_jsx_import_source_module() + .map(|im| JsxResolver::new(im, maybe_import_map_resolver.clone())); let maybe_resolver: Option< Arc<dyn deno_graph::source::Resolver + Send + Sync>, - > = if flags.compat { + > = if root_config.compat() { Some(Arc::new(node_resolver)) } else if let Some(jsx_resolver) = maybe_jsx_resolver { // the JSX resolver offloads to the import map if present, otherwise uses @@ -255,14 +212,13 @@ impl ProcState { Ok(ProcState(Arc::new(Inner { dir, coverage_dir, - flags, + config: root_config, file_fetcher, graph_data: Default::default(), lockfile, - maybe_config_file, maybe_import_map, maybe_inspector_server, - root_cert_store: Some(root_cert_store.clone()), + root_cert_store, blob_store, broadcast_channel, shared_array_buffer_store, @@ -272,25 +228,6 @@ impl ProcState { }))) } - /// Return any imports that should be brought into the scope of the module - /// graph. - fn get_maybe_imports(&self) -> MaybeImportsResult { - let mut imports = Vec::new(); - if let Some(config_file) = &self.maybe_config_file { - if let Some(config_imports) = config_file.to_maybe_imports()? { - imports.extend(config_imports); - } - } - if self.flags.compat { - imports.extend(compat::get_node_imports()); - } - if imports.is_empty() { - Ok(None) - } else { - Ok(Some(imports)) - } - } - /// This method must be called for a module or a static importer of that /// module before attempting to `load()` it from a `JsRuntime`. It will /// populate `self.graph_data` in memory with the necessary source code, write @@ -299,7 +236,7 @@ impl ProcState { &self, roots: Vec<ModuleSpecifier>, is_dynamic: bool, - lib: emit::TypeLib, + lib: TsTypeLib, root_permissions: Permissions, dynamic_permissions: Permissions, reload_on_watch: bool, @@ -339,7 +276,7 @@ impl ProcState { // TODO(bartlomieju): this is very make-shift, is there an existing API // that we could include it like with "maybe_imports"? - let roots = if self.flags.compat { + let roots = if self.config.compat() { let mut r = vec![(compat::GLOBAL_URL.clone(), ModuleKind::Esm)]; r.extend(roots); r @@ -348,12 +285,12 @@ impl ProcState { }; if !reload_on_watch { let graph_data = self.graph_data.read(); - if self.flags.type_check_mode == TypeCheckMode::None + if self.config.type_check_mode() == TypeCheckMode::None || graph_data.is_type_checked(&roots, &lib) { if let Some(result) = graph_data.check( &roots, - self.flags.type_check_mode != TypeCheckMode::None, + self.config.type_check_mode() != TypeCheckMode::None, false, ) { return result; @@ -367,7 +304,7 @@ impl ProcState { dynamic_permissions.clone(), ); let maybe_locker = as_maybe_locker(self.lockfile.clone()); - let maybe_imports = self.get_maybe_imports()?; + let maybe_imports = self.config.to_maybe_imports()?; struct ProcStateLoader<'a> { inner: &'a mut cache::FetchCacher, @@ -462,57 +399,50 @@ impl ProcState { { let mut graph_data = self.graph_data.write(); graph_data.add_graph(&graph, reload_on_watch); - let check_js = self - .maybe_config_file - .as_ref() - .map(|cf| cf.get_check_js()) - .unwrap_or(false); + let check_js = self.config.check_js(); graph_data .check( &roots, - self.flags.type_check_mode != TypeCheckMode::None, + self.config.type_check_mode() != TypeCheckMode::None, check_js, ) .unwrap()?; } - let config_type = if self.flags.type_check_mode == TypeCheckMode::None { - emit::ConfigType::Emit + let config_type = if self.config.type_check_mode() == TypeCheckMode::None { + TsConfigType::Emit } else { - emit::ConfigType::Check { + TsConfigType::Check { tsc_emit: true, - lib: lib.clone(), + lib, } }; - let (ts_config, maybe_ignored_options) = - emit::get_ts_config(config_type, self.maybe_config_file.as_ref(), None)?; + let ts_config_result = + self.config.resolve_ts_config_for_emit(config_type)?; - if let Some(ignored_options) = maybe_ignored_options { + if let Some(ignored_options) = ts_config_result.maybe_ignored_options { log::warn!("{}", ignored_options); } - if self.flags.type_check_mode == TypeCheckMode::None { + if self.config.type_check_mode() == TypeCheckMode::None { let options = emit::EmitOptions { - ts_config, - reload: self.flags.reload, + ts_config: ts_config_result.ts_config, + reload: self.config.reload_flag(), reload_exclusions, }; let emit_result = emit::emit(&graph, &self.dir.gen_cache, options)?; log::debug!("{}", emit_result.stats); } else { - let maybe_config_specifier = self - .maybe_config_file - .as_ref() - .map(|cf| cf.specifier.clone()); + let maybe_config_specifier = self.config.maybe_config_file_specifier(); let options = emit::CheckOptions { - type_check_mode: self.flags.type_check_mode.clone(), - debug: self.flags.log_level == Some(log::Level::Debug), + type_check_mode: self.config.type_check_mode(), + debug: self.config.log_level() == Some(log::Level::Debug), emit_with_diagnostics: false, maybe_config_specifier, - ts_config, + ts_config: ts_config_result.ts_config, log_checks: true, - reload: self.flags.reload, + reload: self.config.reload_flag(), reload_exclusions, }; let emit_result = emit::check_and_maybe_emit( @@ -527,9 +457,9 @@ impl ProcState { log::debug!("{}", emit_result.stats); } - if self.flags.type_check_mode != TypeCheckMode::None { + if self.config.type_check_mode() != TypeCheckMode::None { let mut graph_data = self.graph_data.write(); - graph_data.set_type_checked(&roots, &lib); + graph_data.set_type_checked(&roots, lib); } // any updates to the lockfile should be updated now @@ -571,7 +501,9 @@ impl ProcState { // FIXME(bartlomieju): this is a hacky way to provide compatibility with REPL // and `Deno.core.evalContext` API. Ideally we should always have a referrer filled // but sadly that's not the case due to missing APIs in V8. - let referrer = if referrer.is_empty() && self.flags.repl { + let referrer = if referrer.is_empty() + && matches!(self.config.sub_command(), DenoSubcommand::Repl(_)) + { deno_core::resolve_url_or_path("./$deno$repl.ts").unwrap() } else { deno_core::resolve_url_or_path(referrer).unwrap() diff --git a/cli/standalone.rs b/cli/standalone.rs index 50baf70ea..d130fbe2e 100644 --- a/cli/standalone.rs +++ b/cli/standalone.rs @@ -23,7 +23,6 @@ use deno_core::ModuleLoader; use deno_core::ModuleSpecifier; use deno_graph::source::Resolver; use deno_runtime::deno_broadcast_channel::InMemoryBroadcastChannel; -use deno_runtime::deno_tls::create_default_root_cert_store; use deno_runtime::deno_tls::rustls_pemfile; use deno_runtime::deno_web::BlobStore; use deno_runtime::permissions::Permissions; @@ -224,7 +223,7 @@ pub async fn run( ) -> Result<(), AnyError> { let flags = metadata_to_flags(&metadata); let main_module = &metadata.entrypoint; - let ps = ProcState::build(Arc::new(flags)).await?; + let ps = ProcState::build(flags).await?; let permissions = Permissions::from_options(&metadata.permissions); let blob_store = BlobStore::default(); let broadcast_channel = InMemoryBroadcastChannel::default(); @@ -252,10 +251,7 @@ pub async fn run( .collect::<Vec<_>>(), ); - let mut root_cert_store = ps - .root_cert_store - .clone() - .unwrap_or_else(create_default_root_cert_store); + let mut root_cert_store = ps.root_cert_store.clone(); if let Some(cert) = metadata.ca_data { let reader = &mut BufReader::new(Cursor::new(cert)); diff --git a/cli/tools/bench.rs b/cli/tools/bench.rs index 3a40a4e97..d2cf4f00b 100644 --- a/cli/tools/bench.rs +++ b/cli/tools/bench.rs @@ -7,7 +7,6 @@ use crate::cache; use crate::colors; use crate::compat; use crate::create_main_worker; -use crate::emit; use crate::file_watcher; use crate::file_watcher::ResolutionResult; use crate::fs_util::collect_specifiers; @@ -40,7 +39,6 @@ use serde::Deserialize; use serde::Serialize; use std::collections::HashSet; use std::path::PathBuf; -use std::sync::Arc; use tokio::sync::mpsc::unbounded_channel; use tokio::sync::mpsc::UnboundedSender; @@ -337,8 +335,8 @@ async fn check_specifiers( ps: &ProcState, permissions: Permissions, specifiers: Vec<ModuleSpecifier>, - lib: emit::TypeLib, ) -> Result<(), AnyError> { + let lib = ps.config.ts_type_lib_window(); ps.prepare_module_load( specifiers, false, @@ -365,7 +363,11 @@ async fn bench_specifier( &ps, specifier.clone(), permissions, - vec![ops::bench::init(channel.clone(), filter, ps.flags.unstable)], + vec![ops::bench::init( + channel.clone(), + filter, + ps.config.unstable(), + )], Default::default(), ); @@ -422,7 +424,7 @@ async fn bench_specifiers( specifiers: Vec<ModuleSpecifier>, options: BenchSpecifierOptions, ) -> Result<(), AnyError> { - let log_level = ps.flags.log_level; + let log_level = ps.config.log_level(); let (sender, mut receiver) = unbounded_channel::<BenchEvent>(); @@ -524,8 +526,8 @@ pub async fn run_benchmarks( flags: Flags, bench_flags: BenchFlags, ) -> Result<(), AnyError> { - let ps = ProcState::build(Arc::new(flags)).await?; - let permissions = Permissions::from_options(&ps.flags.permissions_options()); + let ps = ProcState::build(flags).await?; + let permissions = Permissions::from_options(&ps.config.permissions_options()); let specifiers = collect_specifiers( bench_flags.include.unwrap_or_else(|| vec![".".to_string()]), &bench_flags.ignore.clone(), @@ -536,15 +538,9 @@ pub async fn run_benchmarks( return Err(generic_error("No bench modules found")); } - let lib = if ps.flags.unstable { - emit::TypeLib::UnstableDenoWindow - } else { - emit::TypeLib::DenoWindow - }; - - check_specifiers(&ps, permissions.clone(), specifiers.clone(), lib).await?; + check_specifiers(&ps, permissions.clone(), specifiers.clone()).await?; - let compat = ps.flags.compat; + let compat = ps.config.compat(); bench_specifiers( ps, permissions, @@ -564,20 +560,13 @@ pub async fn run_benchmarks_with_watch( flags: Flags, bench_flags: BenchFlags, ) -> Result<(), AnyError> { - let flags = Arc::new(flags); - let ps = ProcState::build(flags.clone()).await?; - let permissions = Permissions::from_options(&flags.permissions_options()); - - let lib = if flags.unstable { - emit::TypeLib::UnstableDenoWindow - } else { - emit::TypeLib::DenoWindow - }; + let ps = ProcState::build(flags).await?; + let permissions = Permissions::from_options(&ps.config.permissions_options()); let include = bench_flags.include.unwrap_or_else(|| vec![".".to_string()]); let ignore = bench_flags.ignore.clone(); let paths_to_watch: Vec<_> = include.iter().map(PathBuf::from).collect(); - let no_check = ps.flags.type_check_mode == TypeCheckMode::None; + let no_check = ps.config.type_check_mode() == TypeCheckMode::None; let resolver = |changed: Option<Vec<PathBuf>>| { let mut cache = cache::FetchCacher::new( @@ -592,23 +581,16 @@ pub async fn run_benchmarks_with_watch( let maybe_import_map_resolver = ps.maybe_import_map.clone().map(ImportMapResolver::new); - let maybe_jsx_resolver = ps.maybe_config_file.as_ref().and_then(|cf| { - cf.to_maybe_jsx_import_source_module() - .map(|im| JsxResolver::new(im, maybe_import_map_resolver.clone())) - }); + let maybe_jsx_resolver = ps + .config + .to_maybe_jsx_import_source_module() + .map(|im| JsxResolver::new(im, maybe_import_map_resolver.clone())); let maybe_locker = lockfile::as_maybe_locker(ps.lockfile.clone()); - let maybe_imports = ps - .maybe_config_file - .as_ref() - .map(|cf| cf.to_maybe_imports()); + let maybe_imports_result = ps.config.to_maybe_imports(); let files_changed = changed.is_some(); let include = include.clone(); let ignore = ignore.clone(); - let check_js = ps - .maybe_config_file - .as_ref() - .map(|cf| cf.get_check_js()) - .unwrap_or(false); + let check_js = ps.config.check_js(); async move { let bench_modules = @@ -623,11 +605,7 @@ pub async fn run_benchmarks_with_watch( .map(|url| (url.clone(), ModuleKind::Esm)) .collect() }; - let maybe_imports = if let Some(result) = maybe_imports { - result? - } else { - None - }; + let maybe_imports = maybe_imports_result?; let maybe_resolver = if maybe_jsx_resolver.is_some() { maybe_jsx_resolver.as_ref().map(|jr| jr.as_resolver()) } else { @@ -739,11 +717,9 @@ pub async fn run_benchmarks_with_watch( }; let operation = |modules_to_reload: Vec<(ModuleSpecifier, ModuleKind)>| { - let flags = flags.clone(); let filter = bench_flags.filter.clone(); let include = include.clone(); let ignore = ignore.clone(); - let lib = lib.clone(); let permissions = permissions.clone(); let ps = ps.clone(); @@ -755,19 +731,14 @@ pub async fn run_benchmarks_with_watch( .cloned() .collect::<Vec<ModuleSpecifier>>(); - check_specifiers(&ps, permissions.clone(), specifiers.clone(), lib) - .await?; + check_specifiers(&ps, permissions.clone(), specifiers.clone()).await?; - bench_specifiers( - ps, - permissions.clone(), - specifiers, - BenchSpecifierOptions { - compat_mode: flags.compat, - filter: filter.clone(), - }, - ) - .await?; + let specifier_options = BenchSpecifierOptions { + compat_mode: ps.config.compat(), + filter: filter.clone(), + }; + bench_specifiers(ps, permissions.clone(), specifiers, specifier_options) + .await?; Ok(()) } @@ -778,7 +749,7 @@ pub async fn run_benchmarks_with_watch( operation, file_watcher::PrintConfig { job_name: "Bench".to_string(), - clear_screen: !flags.no_clear_screen, + clear_screen: !ps.config.no_clear_screen(), }, ) .await?; diff --git a/cli/tools/coverage/mod.rs b/cli/tools/coverage/mod.rs index efaf19922..0b1a9bc35 100644 --- a/cli/tools/coverage/mod.rs +++ b/cli/tools/coverage/mod.rs @@ -23,7 +23,6 @@ use std::fs::File; use std::io::BufWriter; use std::io::{self, Error, Write}; use std::path::PathBuf; -use std::sync::Arc; use text_lines::TextLines; use uuid::Uuid; @@ -597,7 +596,7 @@ pub async fn cover_files( flags: Flags, coverage_flags: CoverageFlags, ) -> Result<(), AnyError> { - let ps = ProcState::build(Arc::new(flags)).await?; + let ps = ProcState::build(flags).await?; let script_coverages = collect_coverages(coverage_flags.files, coverage_flags.ignore)?; diff --git a/cli/tools/doc.rs b/cli/tools/doc.rs index 684c3040e..068a98389 100644 --- a/cli/tools/doc.rs +++ b/cli/tools/doc.rs @@ -96,7 +96,7 @@ pub async fn print_docs( flags: Flags, doc_flags: DocFlags, ) -> Result<(), AnyError> { - let ps = ProcState::build(Arc::new(flags)).await?; + let ps = ProcState::build(flags).await?; let source_file = doc_flags .source_file .unwrap_or_else(|| "--builtin".to_string()); @@ -122,7 +122,7 @@ pub async fn print_docs( doc_parser.parse_source( &source_file_specifier, MediaType::Dts, - get_types(ps.flags.unstable).into(), + get_types(ps.config.unstable()).into(), ) } else { let module_specifier = resolve_url_or_path(&source_file)?; diff --git a/cli/tools/fmt.rs b/cli/tools/fmt.rs index b2aa47373..267b21d88 100644 --- a/cli/tools/fmt.rs +++ b/cli/tools/fmt.rs @@ -7,13 +7,11 @@ //! 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::FmtConfig; use crate::args::FmtFlags; use crate::args::FmtOptionsConfig; use crate::args::ProseWrap; +use crate::args::RootConfig; use crate::colors; -use crate::deno_dir::DenoDir; use crate::diff::diff; use crate::file_watcher; use crate::file_watcher::ResolutionResult; @@ -45,11 +43,11 @@ use super::incremental_cache::IncrementalCache; /// Format JavaScript/TypeScript files. pub async fn format( - flags: &Flags, + config: &RootConfig, fmt_flags: FmtFlags, - maybe_fmt_config: Option<FmtConfig>, - deno_dir: &DenoDir, ) -> Result<(), AnyError> { + let maybe_fmt_config = config.to_fmt_config()?; + let deno_dir = config.resolve_deno_dir()?; let FmtFlags { files, ignore, @@ -138,6 +136,7 @@ pub async fn format( } } }; + let deno_dir = &deno_dir; let operation = |(paths, fmt_options): (Vec<PathBuf>, FmtOptionsConfig)| async move { let incremental_cache = Arc::new(IncrementalCache::new( &deno_dir.fmt_incremental_cache_db_file_path(), @@ -154,13 +153,13 @@ pub async fn format( Ok(()) }; - if flags.watch.is_some() { + if config.watch_paths().is_some() { file_watcher::watch_func( resolver, operation, file_watcher::PrintConfig { job_name: "Fmt".to_string(), - clear_screen: !flags.no_clear_screen, + clear_screen: !config.no_clear_screen(), }, ) .await?; diff --git a/cli/tools/lint.rs b/cli/tools/lint.rs index 60cda9f90..4e8f8fb0c 100644 --- a/cli/tools/lint.rs +++ b/cli/tools/lint.rs @@ -6,14 +6,18 @@ //! 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::{Flags, LintFlags}; +use crate::args::LintFlags; +use crate::colors; +use crate::file_watcher; use crate::file_watcher::ResolutionResult; use crate::fmt_errors; -use crate::fs_util::{collect_files, is_supported_ext, specifier_to_file_path}; +use crate::fs_util::collect_files; +use crate::fs_util::is_supported_ext; +use crate::fs_util::specifier_to_file_path; use crate::proc_state::ProcState; use crate::tools::fmt::run_parallelized; -use crate::{colors, file_watcher}; use deno_ast::MediaType; use deno_core::anyhow::anyhow; use deno_core::error::generic_error; @@ -29,10 +33,13 @@ use log::debug; use log::info; use serde::Serialize; use std::fs; -use std::io::{stdin, Read}; +use std::io::stdin; +use std::io::Read; use std::path::PathBuf; -use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::{Arc, Mutex}; +use std::sync::atomic::AtomicBool; +use std::sync::atomic::Ordering; +use std::sync::Arc; +use std::sync::Mutex; use super::incremental_cache::IncrementalCache; @@ -52,7 +59,6 @@ fn create_reporter(kind: LintReporterKind) -> Box<dyn LintReporter + Send> { } pub async fn lint(flags: Flags, lint_flags: LintFlags) -> Result<(), AnyError> { - let flags = Arc::new(flags); let LintFlags { maybe_rules_tags, maybe_rules_include, @@ -69,12 +75,8 @@ pub async fn lint(flags: Flags, lint_flags: LintFlags) -> Result<(), AnyError> { let mut include_files = args.clone(); let mut exclude_files = ignore.clone(); - let ps = ProcState::build(flags.clone()).await?; - let maybe_lint_config = if let Some(config_file) = &ps.maybe_config_file { - config_file.to_lint_config()? - } else { - None - }; + let ps = ProcState::build(flags).await?; + let maybe_lint_config = ps.config.to_lint_config()?; if let Some(lint_config) = maybe_lint_config.as_ref() { if include_files.is_empty() { @@ -200,7 +202,7 @@ pub async fn lint(flags: Flags, lint_flags: LintFlags) -> Result<(), AnyError> { Ok(()) }; - if flags.watch.is_some() { + if ps.config.watch_paths().is_some() { if args.len() == 1 && args[0].to_string_lossy() == "-" { return Err(generic_error( "Lint watch on standard input is not supported.", @@ -211,7 +213,7 @@ pub async fn lint(flags: Flags, lint_flags: LintFlags) -> Result<(), AnyError> { operation, file_watcher::PrintConfig { job_name: "Lint".to_string(), - clear_screen: !flags.no_clear_screen, + clear_screen: !ps.config.no_clear_screen(), }, ) .await?; diff --git a/cli/tools/standalone.rs b/cli/tools/standalone.rs index 2561188ca..99f2ce33d 100644 --- a/cli/tools/standalone.rs +++ b/cli/tools/standalone.rs @@ -282,7 +282,6 @@ pub fn compile_to_runtime_flags( no_remote: false, no_prompt: flags.no_prompt, reload: false, - repl: false, seed: flags.seed, unstable: flags.unstable, v8_flags: flags.v8_flags.clone(), diff --git a/cli/tools/task.rs b/cli/tools/task.rs index 1b6846e7c..fa9f30d7d 100644 --- a/cli/tools/task.rs +++ b/cli/tools/task.rs @@ -1,6 +1,5 @@ // Copyright 2018-2022 the Deno authors. All rights reserved. MIT license. -use crate::args::ConfigFile; use crate::args::Flags; use crate::args::TaskFlags; use crate::colors; @@ -12,34 +11,6 @@ use deno_core::error::AnyError; use std::collections::BTreeMap; use std::collections::HashMap; use std::path::PathBuf; -use std::sync::Arc; - -fn get_tasks_config( - maybe_config_file: Option<&ConfigFile>, -) -> Result<BTreeMap<String, String>, AnyError> { - if let Some(config_file) = maybe_config_file { - let maybe_tasks_config = config_file.to_tasks_config()?; - if let Some(tasks_config) = maybe_tasks_config { - for key in tasks_config.keys() { - if key.is_empty() { - bail!("Configuration file task names cannot be empty"); - } else if !key - .chars() - .all(|c| c.is_ascii_alphanumeric() || matches!(c, '_' | '-' | ':')) - { - bail!("Configuration file task names must only contain alpha-numeric characters, colons (:), underscores (_), or dashes (-). Task: {}", key); - } else if !key.chars().next().unwrap().is_ascii_alphabetic() { - bail!("Configuration file task names must start with an alphabetic character. Task: {}", key); - } - } - Ok(tasks_config) - } else { - bail!("No tasks found in configuration file") - } - } else { - bail!("No config file found") - } -} fn print_available_tasks(tasks_config: BTreeMap<String, String>) { eprintln!("{}", colors::green("Available tasks:")); @@ -58,10 +29,9 @@ pub async fn execute_script( "{} deno task is unstable and may drastically change in the future", crate::colors::yellow("Warning"), ); - let flags = Arc::new(flags); - let ps = ProcState::build(flags.clone()).await?; - let tasks_config = get_tasks_config(ps.maybe_config_file.as_ref())?; - let config_file_url = &ps.maybe_config_file.as_ref().unwrap().specifier; + let ps = ProcState::build(flags).await?; + let tasks_config = ps.config.resolve_tasks_config()?; + let config_file_url = ps.config.maybe_config_file_specifier().unwrap(); let config_file_path = if config_file_url.scheme() == "file" { config_file_url.to_file_path().unwrap() } else { @@ -81,8 +51,9 @@ pub async fn execute_script( let maybe_script = tasks_config.get(&task_name); if let Some(script) = maybe_script { - let additional_args = flags - .argv + let additional_args = ps + .config + .argv() .iter() // surround all the additional arguments in double quotes // and santize any command substition @@ -108,74 +79,3 @@ pub async fn execute_script( Ok(1) } } - -#[cfg(test)] -mod test { - use deno_ast::ModuleSpecifier; - use pretty_assertions::assert_eq; - - use super::*; - - #[test] - fn tasks_no_tasks() { - run_task_error_test(r#"{}"#, "No tasks found in configuration file"); - } - - #[test] - fn task_name_invalid_chars() { - run_task_error_test( - r#"{ - "tasks": { - "build": "deno test", - "some%test": "deno bundle mod.ts" - } - }"#, - concat!( - "Configuration file task names must only contain alpha-numeric ", - "characters, colons (:), underscores (_), or dashes (-). Task: some%test", - ), - ); - } - - #[test] - fn task_name_non_alpha_starting_char() { - run_task_error_test( - r#"{ - "tasks": { - "build": "deno test", - "1test": "deno bundle mod.ts" - } - }"#, - concat!( - "Configuration file task names must start with an ", - "alphabetic character. Task: 1test", - ), - ); - } - - #[test] - fn task_name_empty() { - run_task_error_test( - r#"{ - "tasks": { - "build": "deno test", - "": "deno bundle mod.ts" - } - }"#, - "Configuration file task names cannot be empty", - ); - } - - fn run_task_error_test(config_text: &str, expected_error: &str) { - let config_dir = ModuleSpecifier::parse("file:///deno/").unwrap(); - let config_specifier = config_dir.join("tsconfig.json").unwrap(); - let config_file = ConfigFile::new(config_text, &config_specifier).unwrap(); - assert_eq!( - get_tasks_config(Some(&config_file)) - .err() - .unwrap() - .to_string(), - expected_error, - ); - } -} diff --git a/cli/tools/test.rs b/cli/tools/test.rs index 71374d94e..3b3f8dc4b 100644 --- a/cli/tools/test.rs +++ b/cli/tools/test.rs @@ -8,7 +8,6 @@ use crate::colors; use crate::compat; use crate::create_main_worker; use crate::display; -use crate::emit; use crate::file_fetcher::File; use crate::file_watcher; use crate::file_watcher::ResolutionResult; @@ -1020,8 +1019,8 @@ pub async fn check_specifiers( ps: &ProcState, permissions: Permissions, specifiers: Vec<(ModuleSpecifier, TestMode)>, - lib: emit::TypeLib, ) -> Result<(), AnyError> { + let lib = ps.config.ts_type_lib_window(); let inline_files = fetch_inline_files( ps.clone(), specifiers @@ -1050,7 +1049,7 @@ pub async fn check_specifiers( ps.prepare_module_load( specifiers, false, - lib.clone(), + lib, Permissions::allow_all(), permissions.clone(), false, @@ -1089,7 +1088,7 @@ async fn test_specifiers( specifiers_with_mode: Vec<(ModuleSpecifier, TestMode)>, options: TestSpecifierOptions, ) -> Result<(), AnyError> { - let log_level = ps.flags.log_level; + let log_level = ps.config.log_level(); let specifiers_with_mode = if let Some(seed) = options.shuffle { let mut rng = SmallRng::seed_from_u64(seed); let mut specifiers_with_mode = specifiers_with_mode.clone(); @@ -1333,8 +1332,8 @@ pub async fn run_tests( flags: Flags, test_flags: TestFlags, ) -> Result<(), AnyError> { - let ps = ProcState::build(Arc::new(flags)).await?; - let permissions = Permissions::from_options(&ps.flags.permissions_options()); + let ps = ProcState::build(flags).await?; + let permissions = Permissions::from_options(&ps.config.permissions_options()); let specifiers_with_mode = fetch_specifiers_with_test_mode( &ps, test_flags.include.unwrap_or_else(|| vec![".".to_string()]), @@ -1347,20 +1346,14 @@ pub async fn run_tests( return Err(generic_error("No test modules found")); } - let lib = if ps.flags.unstable { - emit::TypeLib::UnstableDenoWindow - } else { - emit::TypeLib::DenoWindow - }; - - check_specifiers(&ps, permissions.clone(), specifiers_with_mode.clone(), lib) + check_specifiers(&ps, permissions.clone(), specifiers_with_mode.clone()) .await?; if test_flags.no_run { return Ok(()); } - let compat = ps.flags.compat; + let compat = ps.config.compat(); test_specifiers( ps, permissions, @@ -1383,20 +1376,13 @@ pub async fn run_tests_with_watch( flags: Flags, test_flags: TestFlags, ) -> Result<(), AnyError> { - let flags = Arc::new(flags); - let ps = ProcState::build(flags.clone()).await?; - let permissions = Permissions::from_options(&flags.permissions_options()); - - let lib = if flags.unstable { - emit::TypeLib::UnstableDenoWindow - } else { - emit::TypeLib::DenoWindow - }; + let ps = ProcState::build(flags).await?; + let permissions = Permissions::from_options(&ps.config.permissions_options()); let include = test_flags.include.unwrap_or_else(|| vec![".".to_string()]); let ignore = test_flags.ignore.clone(); let paths_to_watch: Vec<_> = include.iter().map(PathBuf::from).collect(); - let no_check = ps.flags.type_check_mode == TypeCheckMode::None; + let no_check = ps.config.type_check_mode() == TypeCheckMode::None; let resolver = |changed: Option<Vec<PathBuf>>| { let mut cache = cache::FetchCacher::new( @@ -1411,23 +1397,16 @@ pub async fn run_tests_with_watch( let maybe_import_map_resolver = ps.maybe_import_map.clone().map(ImportMapResolver::new); - let maybe_jsx_resolver = ps.maybe_config_file.as_ref().and_then(|cf| { - cf.to_maybe_jsx_import_source_module() - .map(|im| JsxResolver::new(im, maybe_import_map_resolver.clone())) - }); + let maybe_jsx_resolver = ps + .config + .to_maybe_jsx_import_source_module() + .map(|im| JsxResolver::new(im, maybe_import_map_resolver.clone())); let maybe_locker = lockfile::as_maybe_locker(ps.lockfile.clone()); - let maybe_imports = ps - .maybe_config_file - .as_ref() - .map(|cf| cf.to_maybe_imports()); + let maybe_imports_result = ps.config.to_maybe_imports(); let files_changed = changed.is_some(); let include = include.clone(); let ignore = ignore.clone(); - let check_js = ps - .maybe_config_file - .as_ref() - .map(|cf| cf.get_check_js()) - .unwrap_or(false); + let check_js = ps.config.check_js(); async move { let test_modules = if test_flags.doc { @@ -1445,11 +1424,7 @@ pub async fn run_tests_with_watch( .map(|url| (url.clone(), ModuleKind::Esm)) .collect() }; - let maybe_imports = if let Some(result) = maybe_imports { - result? - } else { - None - }; + let maybe_imports = maybe_imports_result?; let maybe_resolver = if maybe_jsx_resolver.is_some() { maybe_jsx_resolver.as_ref().map(|jr| jr.as_resolver()) } else { @@ -1560,12 +1535,12 @@ pub async fn run_tests_with_watch( }) }; + let root_config = ps.config.clone(); let operation = |modules_to_reload: Vec<(ModuleSpecifier, ModuleKind)>| { - let flags = flags.clone(); + let root_config = root_config.clone(); let filter = test_flags.filter.clone(); let include = include.clone(); let ignore = ignore.clone(); - let lib = lib.clone(); let permissions = permissions.clone(); let ps = ps.clone(); @@ -1584,13 +1559,8 @@ pub async fn run_tests_with_watch( .cloned() .collect::<Vec<(ModuleSpecifier, TestMode)>>(); - check_specifiers( - &ps, - permissions.clone(), - specifiers_with_mode.clone(), - lib, - ) - .await?; + check_specifiers(&ps, permissions.clone(), specifiers_with_mode.clone()) + .await?; if test_flags.no_run { return Ok(()); @@ -1601,7 +1571,7 @@ pub async fn run_tests_with_watch( permissions.clone(), specifiers_with_mode, TestSpecifierOptions { - compat_mode: flags.compat, + compat_mode: root_config.compat(), concurrent_jobs: test_flags.concurrent_jobs, fail_fast: test_flags.fail_fast, filter: filter.clone(), @@ -1620,7 +1590,7 @@ pub async fn run_tests_with_watch( operation, file_watcher::PrintConfig { job_name: "Test".to_string(), - clear_screen: !flags.no_clear_screen, + clear_screen: !root_config.no_clear_screen(), }, ) .await?; diff --git a/cli/tools/vendor/mod.rs b/cli/tools/vendor/mod.rs index 15b149e2e..fc7684b5b 100644 --- a/cli/tools/vendor/mod.rs +++ b/cli/tools/vendor/mod.rs @@ -134,16 +134,17 @@ fn validate_output_dir( fn maybe_update_config_file(output_dir: &Path, ps: &ProcState) -> bool { assert!(output_dir.is_absolute()); - let config_file = match &ps.maybe_config_file { + let config_file_specifier = match ps.config.maybe_config_file_specifier() { Some(f) => f, None => return false, }; - let fmt_config = config_file + let fmt_config = ps + .config .to_fmt_config() .unwrap_or_default() .unwrap_or_default(); let result = update_config_file( - &config_file.specifier, + &config_file_specifier, &ModuleSpecifier::from_file_path(output_dir.join("import_map.json")) .unwrap(), &fmt_config.options, @@ -262,17 +263,13 @@ async fn create_graph( Permissions::allow_all(), ); let maybe_locker = lockfile::as_maybe_locker(ps.lockfile.clone()); - let maybe_imports = if let Some(config_file) = &ps.maybe_config_file { - config_file.to_maybe_imports()? - } else { - None - }; + let maybe_imports = ps.config.to_maybe_imports()?; let maybe_import_map_resolver = ps.maybe_import_map.clone().map(ImportMapResolver::new); - let maybe_jsx_resolver = ps.maybe_config_file.as_ref().and_then(|cf| { - cf.to_maybe_jsx_import_source_module() - .map(|im| JsxResolver::new(im, maybe_import_map_resolver.clone())) - }); + let maybe_jsx_resolver = ps + .config + .to_maybe_jsx_import_source_module() + .map(|im| JsxResolver::new(im, maybe_import_map_resolver.clone())); let maybe_resolver = if maybe_jsx_resolver.is_some() { maybe_jsx_resolver.as_ref().map(|jr| jr.as_resolver()) } else { |