diff options
Diffstat (limited to 'cli/lsp/config.rs')
-rw-r--r-- | cli/lsp/config.rs | 839 |
1 files changed, 646 insertions, 193 deletions
diff --git a/cli/lsp/config.rs b/cli/lsp/config.rs index a87c49108..5fa2a00a6 100644 --- a/cli/lsp/config.rs +++ b/cli/lsp/config.rs @@ -2,22 +2,34 @@ use super::logging::lsp_log; use crate::args::ConfigFile; +use crate::args::FmtOptions; +use crate::args::LintOptions; use crate::cache::FastInsecureHasher; +use crate::file_fetcher::FileFetcher; use crate::lsp::logging::lsp_warn; +use crate::tools::lint::get_configured_rules; +use crate::tools::lint::ConfiguredRules; use crate::util::fs::canonicalize_path_maybe_not_exists; use crate::util::path::specifier_to_file_path; use deno_ast::MediaType; use deno_config::FmtOptionsConfig; +use deno_config::TsConfig; +use deno_core::anyhow::anyhow; use deno_core::parking_lot::Mutex; use deno_core::serde::de::DeserializeOwned; use deno_core::serde::Deserialize; use deno_core::serde::Serialize; use deno_core::serde_json; +use deno_core::serde_json::json; use deno_core::serde_json::Value; use deno_core::ModuleSpecifier; use deno_lockfile::Lockfile; +use deno_runtime::deno_node::PackageJson; +use deno_runtime::permissions::PermissionsContainer; +use import_map::ImportMap; use lsp::Url; use std::collections::BTreeMap; +use std::collections::BTreeSet; use std::collections::HashMap; use std::path::PathBuf; use std::sync::Arc; @@ -717,9 +729,9 @@ impl WorkspaceSettings { #[derive(Debug, Clone, Default)] pub struct ConfigSnapshot { pub client_capabilities: ClientCapabilities, - pub config_file: Option<ConfigFile>, pub settings: Settings, pub workspace_folders: Vec<(ModuleSpecifier, lsp::WorkspaceFolder)>, + pub tree: Arc<ConfigTree>, } impl ConfigSnapshot { @@ -732,7 +744,8 @@ impl ConfigSnapshot { /// Determine if the provided specifier is enabled or not. pub fn specifier_enabled(&self, specifier: &ModuleSpecifier) -> bool { - if let Some(cf) = &self.config_file { + let config_file = self.tree.config_file_for_specifier(specifier); + if let Some(cf) = &config_file { if let Ok(files) = cf.to_files_config() { if !files.matches_specifier(specifier) { return false; @@ -742,14 +755,14 @@ impl ConfigSnapshot { self .settings .specifier_enabled(specifier) - .unwrap_or_else(|| self.config_file.is_some()) + .unwrap_or_else(|| config_file.is_some()) } pub fn specifier_enabled_for_test( &self, specifier: &ModuleSpecifier, ) -> bool { - if let Some(cf) = &self.config_file { + if let Some(cf) = self.tree.config_file_for_specifier(specifier) { if let Some(options) = cf.to_test_config().ok().flatten() { if !options.files.matches_specifier(specifier) { return false; @@ -861,47 +874,18 @@ impl Settings { } } -#[derive(Debug)] -struct WithCanonicalizedSpecifier<T> { - /// Stored canonicalized specifier, which is used for file watcher events. - canonicalized_specifier: ModuleSpecifier, - file: T, -} - -/// Contains the config file and dependent information. -#[derive(Debug)] -struct LspConfigFileInfo { - config_file: WithCanonicalizedSpecifier<ConfigFile>, - /// An optional deno.lock file, which is resolved relative to the config file. - maybe_lockfile: Option<WithCanonicalizedSpecifier<Arc<Mutex<Lockfile>>>>, - /// The canonicalized node_modules directory, which is found relative to the config file. - maybe_node_modules_dir: Option<PathBuf>, -} - -#[derive(Debug)] +#[derive(Debug, Default)] pub struct Config { pub client_capabilities: ClientCapabilities, pub settings: Settings, pub workspace_folders: Vec<(ModuleSpecifier, lsp::WorkspaceFolder)>, - /// An optional configuration file which has been specified in the client - /// options along with some data that is computed after the config file is set. - maybe_config_file_info: Option<LspConfigFileInfo>, + pub tree: Arc<ConfigTree>, } impl Config { - pub fn new() -> Self { - Self { - client_capabilities: ClientCapabilities::default(), - // Root provided by the initialization parameters. - settings: Default::default(), - workspace_folders: vec![], - maybe_config_file_info: None, - } - } - #[cfg(test)] pub fn new_with_roots(root_uris: impl IntoIterator<Item = Url>) -> Self { - let mut config = Self::new(); + let mut config = Self::default(); let mut folders = vec![]; for root_uri in root_uris { let name = root_uri.path_segments().and_then(|s| s.last()); @@ -1001,103 +985,18 @@ impl Config { self.workspace_folders.first().map(|p| &p.0) } - pub fn maybe_node_modules_dir_path(&self) -> Option<&PathBuf> { - self - .maybe_config_file_info - .as_ref() - .and_then(|p| p.maybe_node_modules_dir.as_ref()) - } - - pub fn maybe_vendor_dir_path(&self) -> Option<PathBuf> { - self.maybe_config_file().and_then(|c| c.vendor_dir_path()) - } - - pub fn maybe_config_file(&self) -> Option<&ConfigFile> { - self - .maybe_config_file_info - .as_ref() - .map(|c| &c.config_file.file) - } - - /// Canonicalized specifier of the config file, which should only be used for - /// file watcher events. Otherwise, prefer using the non-canonicalized path - /// as the rest of the CLI does for config files. - pub fn maybe_config_file_canonicalized_specifier( - &self, - ) -> Option<&ModuleSpecifier> { - self - .maybe_config_file_info - .as_ref() - .map(|c| &c.config_file.canonicalized_specifier) - } - - pub fn maybe_lockfile(&self) -> Option<&Arc<Mutex<Lockfile>>> { - self - .maybe_config_file_info - .as_ref() - .and_then(|c| c.maybe_lockfile.as_ref().map(|l| &l.file)) - } - - /// Canonicalized specifier of the lockfile, which should only be used for - /// file watcher events. Otherwise, prefer using the non-canonicalized path - /// as the rest of the CLI does for config files. - pub fn maybe_lockfile_canonicalized_specifier( - &self, - ) -> Option<&ModuleSpecifier> { - self.maybe_config_file_info.as_ref().and_then(|c| { - c.maybe_lockfile - .as_ref() - .map(|l| &l.canonicalized_specifier) - }) - } - - pub fn clear_config_file(&mut self) { - self.maybe_config_file_info = None; - } - - pub fn has_config_file(&self) -> bool { - self.maybe_config_file_info.is_some() - } - - pub fn set_config_file(&mut self, config_file: ConfigFile) { - self.maybe_config_file_info = Some(LspConfigFileInfo { - maybe_lockfile: resolve_lockfile_from_config(&config_file).map( - |lockfile| { - let path = canonicalize_path_maybe_not_exists(&lockfile.filename) - .unwrap_or_else(|_| lockfile.filename.clone()); - WithCanonicalizedSpecifier { - canonicalized_specifier: ModuleSpecifier::from_file_path(path) - .unwrap(), - file: Arc::new(Mutex::new(lockfile)), - } - }, - ), - maybe_node_modules_dir: resolve_node_modules_dir(&config_file), - config_file: WithCanonicalizedSpecifier { - canonicalized_specifier: config_file - .specifier - .to_file_path() - .ok() - .and_then(|p| canonicalize_path_maybe_not_exists(&p).ok()) - .and_then(|p| ModuleSpecifier::from_file_path(p).ok()) - .unwrap_or_else(|| config_file.specifier.clone()), - file: config_file, - }, - }); - } - pub fn snapshot(&self) -> Arc<ConfigSnapshot> { Arc::new(ConfigSnapshot { client_capabilities: self.client_capabilities.clone(), - config_file: self.maybe_config_file().cloned(), settings: self.settings.clone(), workspace_folders: self.workspace_folders.clone(), + tree: self.tree.clone(), }) } pub fn specifier_enabled(&self, specifier: &ModuleSpecifier) -> bool { - let config_file = self.maybe_config_file(); - if let Some(cf) = config_file { + let config_file = self.tree.config_file_for_specifier(specifier); + if let Some(cf) = &config_file { if let Ok(files) = cf.to_files_config() { if !files.matches_specifier(specifier) { return false; @@ -1114,7 +1013,7 @@ impl Config { &self, specifier: &ModuleSpecifier, ) -> bool { - if let Some(cf) = self.maybe_config_file() { + if let Some(cf) = self.tree.config_file_for_specifier(specifier) { if let Some(options) = cf.to_test_config().ok().flatten() { if !options.files.matches_specifier(specifier) { return false; @@ -1186,6 +1085,551 @@ impl Config { } } +pub fn default_ts_config() -> TsConfig { + TsConfig::new(json!({ + "allowJs": true, + "esModuleInterop": true, + "experimentalDecorators": false, + "isolatedModules": true, + "jsx": "react", + "lib": ["deno.ns", "deno.window", "deno.unstable"], + "module": "esnext", + "moduleDetection": "force", + "noEmit": true, + "resolveJsonModule": true, + "strict": true, + "target": "esnext", + "useDefineForClassFields": true, + "useUnknownInCatchVariables": false, + })) +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ConfigWatchedFileType { + DenoJson, + Lockfile, + PackageJson, + ImportMap, +} + +/// Contains the config file and dependent information. +#[derive(Debug, Clone)] +pub struct ConfigData { + pub config_file: Option<Arc<ConfigFile>>, + pub fmt_options: Arc<FmtOptions>, + pub lint_options: Arc<LintOptions>, + pub lint_rules: Arc<ConfiguredRules>, + pub ts_config: Arc<TsConfig>, + pub node_modules_dir: Option<PathBuf>, + pub vendor_dir: Option<PathBuf>, + pub lockfile: Option<Arc<Mutex<Lockfile>>>, + pub package_json: Option<Arc<PackageJson>>, + pub import_map: Option<Arc<ImportMap>>, + watched_files: HashMap<ModuleSpecifier, ConfigWatchedFileType>, +} + +impl ConfigData { + async fn load( + config_file_specifier: Option<&ModuleSpecifier>, + scope: &ModuleSpecifier, + settings: &Settings, + file_fetcher: Option<&FileFetcher>, + ) -> Self { + if let Some(specifier) = config_file_specifier { + match ConfigFile::from_specifier(specifier.clone()) { + Ok(config_file) => { + lsp_log!( + " Resolved Deno configuration file: \"{}\"", + config_file.specifier.as_str() + ); + Self::load_inner(Some(config_file), scope, settings, file_fetcher) + .await + } + Err(err) => { + lsp_warn!( + " Couldn't read Deno configuration file \"{}\": {}", + specifier.as_str(), + err + ); + let mut data = + Self::load_inner(None, scope, settings, file_fetcher).await; + data + .watched_files + .insert(specifier.clone(), ConfigWatchedFileType::DenoJson); + let canonicalized_specifier = specifier + .to_file_path() + .ok() + .and_then(|p| canonicalize_path_maybe_not_exists(&p).ok()) + .and_then(|p| ModuleSpecifier::from_file_path(p).ok()); + if let Some(specifier) = canonicalized_specifier { + data + .watched_files + .insert(specifier, ConfigWatchedFileType::DenoJson); + } + data + } + } + } else { + Self::load_inner(None, scope, settings, file_fetcher).await + } + } + + async fn load_inner( + config_file: Option<ConfigFile>, + scope: &ModuleSpecifier, + settings: &Settings, + file_fetcher: Option<&FileFetcher>, + ) -> Self { + let (settings, workspace_folder) = settings.get_for_specifier(scope); + let mut watched_files = HashMap::with_capacity(6); + if let Some(config_file) = &config_file { + watched_files + .entry(config_file.specifier.clone()) + .or_insert(ConfigWatchedFileType::DenoJson); + } + let config_file_canonicalized_specifier = config_file + .as_ref() + .and_then(|c| c.specifier.to_file_path().ok()) + .and_then(|p| canonicalize_path_maybe_not_exists(&p).ok()) + .and_then(|p| ModuleSpecifier::from_file_path(p).ok()); + if let Some(specifier) = config_file_canonicalized_specifier { + watched_files + .entry(specifier) + .or_insert(ConfigWatchedFileType::DenoJson); + } + + // Resolve some config file fields ahead of time + let fmt_options = config_file + .as_ref() + .and_then(|config_file| { + config_file + .to_fmt_config() + .and_then(|o| { + let base_path = config_file + .specifier + .to_file_path() + .map_err(|_| anyhow!("Invalid base path."))?; + FmtOptions::resolve(o, None, &base_path) + }) + .inspect_err(|err| { + lsp_warn!(" Couldn't read formatter configuration: {}", err) + }) + .ok() + }) + .unwrap_or_default(); + let lint_options = config_file + .as_ref() + .and_then(|config_file| { + config_file + .to_lint_config() + .and_then(|o| { + let base_path = config_file + .specifier + .to_file_path() + .map_err(|_| anyhow!("Invalid base path."))?; + LintOptions::resolve(o, None, &base_path) + }) + .inspect_err(|err| { + lsp_warn!(" Couldn't read lint configuration: {}", err) + }) + .ok() + }) + .unwrap_or_default(); + let lint_rules = + get_configured_rules(lint_options.rules.clone(), config_file.as_ref()); + let mut ts_config = default_ts_config(); + if let Some(config_file) = &config_file { + match config_file.to_compiler_options() { + Ok((value, maybe_ignored_options)) => { + ts_config.merge(&value); + if let Some(ignored_options) = maybe_ignored_options { + lsp_warn!("{}", ignored_options); + } + } + Err(err) => lsp_warn!("{}", err), + } + } + let node_modules_dir = + config_file.as_ref().and_then(resolve_node_modules_dir); + let vendor_dir = config_file.as_ref().and_then(|c| c.vendor_dir_path()); + + // Load lockfile + let lockfile = config_file.as_ref().and_then(resolve_lockfile_from_config); + if let Some(lockfile) = &lockfile { + if let Ok(specifier) = ModuleSpecifier::from_file_path(&lockfile.filename) + { + watched_files + .entry(specifier) + .or_insert(ConfigWatchedFileType::Lockfile); + } + } + let lockfile_canonicalized_specifier = lockfile + .as_ref() + .and_then(|lockfile| { + canonicalize_path_maybe_not_exists(&lockfile.filename).ok() + }) + .and_then(|p| ModuleSpecifier::from_file_path(p).ok()); + if let Some(specifier) = lockfile_canonicalized_specifier { + watched_files + .entry(specifier) + .or_insert(ConfigWatchedFileType::Lockfile); + } + + // Load package.json + let mut package_json = None; + if let Ok(path) = specifier_to_file_path(scope) { + let path = path.join("package.json"); + if let Ok(specifier) = ModuleSpecifier::from_file_path(&path) { + watched_files + .entry(specifier) + .or_insert(ConfigWatchedFileType::PackageJson); + } + let package_json_canonicalized_specifier = + canonicalize_path_maybe_not_exists(&path) + .ok() + .and_then(|p| ModuleSpecifier::from_file_path(p).ok()); + if let Some(specifier) = package_json_canonicalized_specifier { + watched_files + .entry(specifier) + .or_insert(ConfigWatchedFileType::PackageJson); + } + if let Ok(source) = std::fs::read_to_string(&path) { + match PackageJson::load_from_string(path.clone(), source) { + Ok(result) => { + lsp_log!(" Resolved package.json: \"{}\"", path.display()); + package_json = Some(result); + } + Err(err) => { + lsp_warn!( + " Couldn't read package.json \"{}\": {}", + path.display(), + err + ); + } + } + } + } + + // Load import map + let mut import_map = None; + let mut import_map_value = None; + let mut import_map_specifier = None; + if let Some(config_file) = &config_file { + if config_file.is_an_import_map() { + import_map_value = Some(config_file.to_import_map_value_from_imports()); + import_map_specifier = Some(config_file.specifier.clone()); + } else if let Ok(Some(specifier)) = config_file.to_import_map_specifier() + { + import_map_specifier = Some(specifier); + } + } else if let Some(import_map_str) = &settings.import_map { + if let Ok(specifier) = Url::parse(import_map_str) { + import_map_specifier = Some(specifier); + } else if let Some(folder_uri) = workspace_folder { + if let Ok(specifier) = folder_uri.join(import_map_str) { + import_map_specifier = Some(specifier); + } + } + } + if let Some(specifier) = &import_map_specifier { + if let Ok(path) = specifier_to_file_path(specifier) { + watched_files + .entry(specifier.clone()) + .or_insert(ConfigWatchedFileType::ImportMap); + let import_map_canonicalized_specifier = + canonicalize_path_maybe_not_exists(&path) + .ok() + .and_then(|p| ModuleSpecifier::from_file_path(p).ok()); + if let Some(specifier) = import_map_canonicalized_specifier { + watched_files + .entry(specifier) + .or_insert(ConfigWatchedFileType::ImportMap); + } + } + if import_map_value.is_none() { + if let Some(file_fetcher) = file_fetcher { + let fetch_result = file_fetcher + .fetch(specifier, PermissionsContainer::allow_all()) + .await; + let value_result = fetch_result.and_then(|f| { + serde_json::from_slice::<Value>(&f.source).map_err(|e| e.into()) + }); + match value_result { + Ok(value) => { + import_map_value = Some(value); + } + Err(err) => { + lsp_warn!( + " Couldn't read import map \"{}\": {}", + specifier.as_str(), + err + ); + } + } + } + } + } + if let (Some(value), Some(specifier)) = + (import_map_value, import_map_specifier) + { + match import_map::parse_from_value(specifier.clone(), value) { + Ok(result) => { + if config_file.as_ref().map(|c| &c.specifier) == Some(&specifier) { + lsp_log!(" Resolved import map from configuration file"); + } else { + lsp_log!(" Resolved import map: \"{}\"", specifier.as_str()); + } + if !result.diagnostics.is_empty() { + lsp_warn!( + " Import map diagnostics:\n{}", + result + .diagnostics + .iter() + .map(|d| format!(" - {d}")) + .collect::<Vec<_>>() + .join("\n") + ); + } + import_map = Some(result.import_map); + } + Err(err) => { + lsp_warn!( + "Couldn't read import map \"{}\": {}", + specifier.as_str(), + err + ); + } + } + } + + ConfigData { + config_file: config_file.map(Arc::new), + fmt_options: Arc::new(fmt_options), + lint_options: Arc::new(lint_options), + lint_rules: Arc::new(lint_rules), + ts_config: Arc::new(ts_config), + node_modules_dir, + vendor_dir, + lockfile: lockfile.map(Mutex::new).map(Arc::new), + package_json: package_json.map(Arc::new), + import_map: import_map.map(Arc::new), + watched_files, + } + } +} + +#[derive(Debug, Default)] +pub struct ConfigTree { + root: Mutex<Option<(ModuleSpecifier, Arc<ConfigData>)>>, +} + +impl ConfigTree { + pub fn root_data(&self) -> Option<Arc<ConfigData>> { + self.root.lock().as_ref().map(|(_, d)| d.clone()) + } + + pub fn root_config_file(&self) -> Option<Arc<ConfigFile>> { + self + .root + .lock() + .as_ref() + .and_then(|(_, d)| d.config_file.clone()) + } + + pub fn root_ts_config(&self) -> Arc<TsConfig> { + self + .root + .lock() + .as_ref() + .map(|(_, d)| d.ts_config.clone()) + .unwrap_or_else(|| Arc::new(default_ts_config())) + } + + pub fn root_vendor_dir(&self) -> Option<PathBuf> { + self + .root + .lock() + .as_ref() + .and_then(|(_, d)| d.vendor_dir.clone()) + } + + pub fn root_lockfile(&self) -> Option<Arc<Mutex<Lockfile>>> { + self + .root + .lock() + .as_ref() + .and_then(|(_, d)| d.lockfile.clone()) + } + + pub fn scope_for_specifier( + &self, + _specifier: &ModuleSpecifier, + ) -> Option<ModuleSpecifier> { + self.root.lock().as_ref().map(|r| r.0.clone()) + } + + pub fn data_for_specifier( + &self, + _specifier: &ModuleSpecifier, + ) -> Option<Arc<ConfigData>> { + self.root_data() + } + + pub fn data_by_scope(&self) -> BTreeMap<ModuleSpecifier, Arc<ConfigData>> { + self.root.lock().iter().cloned().collect() + } + + pub fn config_file_for_specifier( + &self, + _specifier: &ModuleSpecifier, + ) -> Option<Arc<ConfigFile>> { + self.root_config_file() + } + + pub fn has_config_file_for_specifier( + &self, + _specifier: &ModuleSpecifier, + ) -> bool { + self + .root + .lock() + .as_ref() + .map(|(_, d)| d.config_file.is_some()) + .unwrap_or(false) + } + + pub fn config_files(&self) -> Vec<Arc<ConfigFile>> { + self.root_config_file().into_iter().collect() + } + + pub fn package_jsons(&self) -> Vec<Arc<PackageJson>> { + self + .root + .lock() + .as_ref() + .and_then(|(_, d)| d.package_json.clone()) + .into_iter() + .collect() + } + + pub fn fmt_options_for_specifier( + &self, + _specifier: &ModuleSpecifier, + ) -> Arc<FmtOptions> { + self + .root + .lock() + .as_ref() + .map(|(_, d)| d.fmt_options.clone()) + .unwrap_or_default() + } + + pub fn lockfile_for_specifier( + &self, + _specifier: &ModuleSpecifier, + ) -> Option<Arc<Mutex<Lockfile>>> { + self.root_lockfile() + } + + pub fn import_map_for_specifier( + &self, + _specifier: &ModuleSpecifier, + ) -> Option<Arc<ImportMap>> { + self + .root + .lock() + .as_ref() + .and_then(|(_, d)| d.import_map.clone()) + } + + pub async fn refresh( + &self, + settings: &Settings, + root_uri: &ModuleSpecifier, + workspace_files: &BTreeSet<ModuleSpecifier>, + file_fetcher: &FileFetcher, + ) { + lsp_log!("Refreshing configuration tree..."); + let mut root = None; + if let Some(config_path) = &settings.unscoped.config { + if let Ok(config_uri) = root_uri.join(config_path) { + root = Some(( + root_uri.clone(), + Arc::new( + ConfigData::load( + Some(&config_uri), + root_uri, + settings, + Some(file_fetcher), + ) + .await, + ), + )); + } + } else { + let get_uri_if_exists = |name| { + let uri = root_uri.join(name).ok(); + uri.filter(|s| workspace_files.contains(s)) + }; + let config_uri = get_uri_if_exists("deno.jsonc") + .or_else(|| get_uri_if_exists("deno.json")); + root = Some(( + root_uri.clone(), + Arc::new( + ConfigData::load( + config_uri.as_ref(), + root_uri, + settings, + Some(file_fetcher), + ) + .await, + ), + )); + } + *self.root.lock() = root; + } + + /// Returns (scope_uri, type). + pub fn watched_file_type( + &self, + specifier: &ModuleSpecifier, + ) -> Option<(ModuleSpecifier, ConfigWatchedFileType)> { + if let Some((scope_uri, data)) = &*self.root.lock() { + if let Some(typ) = data.watched_files.get(specifier) { + return Some((scope_uri.clone(), *typ)); + } + } + None + } + + pub fn is_watched_file(&self, specifier: &ModuleSpecifier) -> bool { + if specifier.path().ends_with("/deno.json") + || specifier.path().ends_with("/deno.jsonc") + || specifier.path().ends_with("/package.json") + { + return true; + } + self + .root + .lock() + .as_ref() + .is_some_and(|(_, d)| d.watched_files.contains_key(specifier)) + } + + #[cfg(test)] + pub async fn inject_config_file(&self, config_file: ConfigFile) { + let scope = config_file.specifier.join(".").unwrap(); + let data = ConfigData::load_inner( + Some(config_file), + &scope, + &Default::default(), + None, + ) + .await; + *self.root.lock() = Some((scope, Arc::new(data))); + } +} + fn resolve_lockfile_from_config(config_file: &ConfigFile) -> Option<Lockfile> { let lockfile_path = match config_file.resolve_lockfile_path() { Ok(Some(value)) => value, @@ -1224,7 +1668,7 @@ fn resolve_lockfile_from_path(lockfile_path: PathBuf) -> Option<Lockfile> { match Lockfile::new(lockfile_path, false) { Ok(value) => { if let Ok(specifier) = ModuleSpecifier::from_file_path(&value.filename) { - lsp_log!(" Resolved lock file: \"{}\"", specifier); + lsp_log!(" Resolved lockfile: \"{}\"", specifier); } Some(value) } @@ -1310,7 +1754,7 @@ mod tests { #[test] fn test_set_workspace_settings_defaults() { - let mut config = Config::new(); + let mut config = Config::default(); config.set_workspace_settings( serde_json::from_value(json!({})).unwrap(), vec![], @@ -1445,7 +1889,7 @@ mod tests { #[test] fn test_empty_cache() { - let mut config = Config::new(); + let mut config = Config::default(); config.set_workspace_settings( serde_json::from_value(json!({ "cache": "" })).unwrap(), vec![], @@ -1458,7 +1902,7 @@ mod tests { #[test] fn test_empty_import_map() { - let mut config = Config::new(); + let mut config = Config::default(); config.set_workspace_settings( serde_json::from_value(json!({ "import_map": "" })).unwrap(), vec![], @@ -1471,7 +1915,7 @@ mod tests { #[test] fn test_empty_tls_certificate() { - let mut config = Config::new(); + let mut config = Config::default(); config.set_workspace_settings( serde_json::from_value(json!({ "tls_certificate": "" })).unwrap(), vec![], @@ -1484,7 +1928,7 @@ mod tests { #[test] fn test_empty_config() { - let mut config = Config::new(); + let mut config = Config::default(); config.set_workspace_settings( serde_json::from_value(json!({ "config": "" })).unwrap(), vec![], @@ -1495,16 +1939,19 @@ mod tests { ); } - #[test] - fn config_enable_via_config_file_detection() { + #[tokio::test] + async fn config_enable_via_config_file_detection() { let root_uri = resolve_url("file:///root/").unwrap(); let mut config = Config::new_with_roots(vec![root_uri.clone()]); config.settings.unscoped.enable = None; assert!(!config.specifier_enabled(&root_uri)); - config.set_config_file( - ConfigFile::new("{}", root_uri.join("deno.json").unwrap()).unwrap(), - ); + config + .tree + .inject_config_file( + ConfigFile::new("{}", root_uri.join("deno.json").unwrap()).unwrap(), + ) + .await; assert!(config.specifier_enabled(&root_uri)); } @@ -1517,8 +1964,8 @@ mod tests { assert!(!config.specifier_enabled(&root_uri.join("mod.ts").unwrap())); } - #[test] - fn config_specifier_enabled_for_test() { + #[tokio::test] + async fn config_specifier_enabled_for_test() { let root_uri = resolve_url("file:///root/").unwrap(); let mut config = Config::new_with_roots(vec![root_uri.clone()]); config.settings.unscoped.enable = Some(true); @@ -1537,19 +1984,22 @@ mod tests { ); config.settings.unscoped.enable_paths = None; - config.set_config_file( - ConfigFile::new( - &json!({ - "exclude": ["mod2.ts"], - "test": { - "exclude": ["mod3.ts"], - }, - }) - .to_string(), - root_uri.join("deno.json").unwrap(), + config + .tree + .inject_config_file( + ConfigFile::new( + &json!({ + "exclude": ["mod2.ts"], + "test": { + "exclude": ["mod3.ts"], + }, + }) + .to_string(), + root_uri.join("deno.json").unwrap(), + ) + .unwrap(), ) - .unwrap(), - ); + .await; assert!( config.specifier_enabled_for_test(&root_uri.join("mod1.ts").unwrap()) ); @@ -1560,38 +2010,38 @@ mod tests { !config.specifier_enabled_for_test(&root_uri.join("mod3.ts").unwrap()) ); - config.set_config_file( - ConfigFile::new( - &json!({ - "test": { - "include": ["mod1.ts"], - }, - }) - .to_string(), - root_uri.join("deno.json").unwrap(), + config + .tree + .inject_config_file( + ConfigFile::new( + &json!({ + "test": { + "include": ["mod1.ts"], + }, + }) + .to_string(), + root_uri.join("deno.json").unwrap(), + ) + .unwrap(), ) - .unwrap(), - ); - assert!( - config.specifier_enabled_for_test(&root_uri.join("mod1.ts").unwrap()) - ); - assert!( - !config.specifier_enabled_for_test(&root_uri.join("mod2.ts").unwrap()) - ); + .await; - config.set_config_file( - ConfigFile::new( - &json!({ - "test": { - "exclude": ["mod2.ts"], - "include": ["mod2.ts"], - }, - }) - .to_string(), - root_uri.join("deno.json").unwrap(), + config + .tree + .inject_config_file( + ConfigFile::new( + &json!({ + "test": { + "exclude": ["mod2.ts"], + "include": ["mod2.ts"], + }, + }) + .to_string(), + root_uri.join("deno.json").unwrap(), + ) + .unwrap(), ) - .unwrap(), - ); + .await; assert!( !config.specifier_enabled_for_test(&root_uri.join("mod1.ts").unwrap()) ); @@ -1600,24 +2050,27 @@ mod tests { ); } - #[test] - fn config_snapshot_specifier_enabled_for_test() { + #[tokio::test] + async fn config_snapshot_specifier_enabled_for_test() { let root_uri = resolve_url("file:///root/").unwrap(); let mut config = Config::new_with_roots(vec![root_uri.clone()]); config.settings.unscoped.enable = Some(true); - config.set_config_file( - ConfigFile::new( - &json!({ - "exclude": ["mod2.ts"], - "test": { - "exclude": ["mod3.ts"], - }, - }) - .to_string(), - root_uri.join("deno.json").unwrap(), + config + .tree + .inject_config_file( + ConfigFile::new( + &json!({ + "exclude": ["mod2.ts"], + "test": { + "exclude": ["mod3.ts"], + }, + }) + .to_string(), + root_uri.join("deno.json").unwrap(), + ) + .unwrap(), ) - .unwrap(), - ); + .await; let config_snapshot = config.snapshot(); assert!(config_snapshot .specifier_enabled_for_test(&root_uri.join("mod1.ts").unwrap())); |