diff --git a/Cargo.lock b/Cargo.lock index d72d3fc8cd..21483e927f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1229,12 +1229,13 @@ dependencies = [ [[package]] name = "deno_config" -version = "0.10.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba7641dd37ffcc1aeb06dff206a3bdd9e9a52f177f5edd43b734933174c38067" +checksum = "bc52f2cedd7f47b50fb67191f9cb1c5633b47017fb7da5b586278763110879e5" dependencies = [ "anyhow", "glob", + "import_map", "indexmap", "jsonc-parser", "log", @@ -1338,9 +1339,9 @@ dependencies = [ [[package]] name = "deno_doc" -version = "0.110.1" +version = "0.113.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c55c586e5bbcb6571ec57da57ebcae27f9ad5e8d218794300f444fad53e913" +checksum = "394381b3a23173f7bcb309e49ee29f33b8815495ca61fbdc8cb030439dc9d6ed" dependencies = [ "ammonia", "anyhow", @@ -1362,9 +1363,9 @@ dependencies = [ [[package]] name = "deno_emit" -version = "0.38.1" +version = "0.38.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b115e4df6b17e49bb6732a30725e70c28c5704f2b4d4f9a8547e0d532a1484b" +checksum = "23593513889bc5d607f9750c858f9cc113f47cb045d87eea7c6cd6605639915c" dependencies = [ "anyhow", "base64", @@ -1433,9 +1434,9 @@ dependencies = [ [[package]] name = "deno_graph" -version = "0.69.0" +version = "0.69.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98448adebbc12b8b1fc0eb42ce06cf3830dff0dcb48f3276039360b90405612f" +checksum = "73c83b0538ee10854dcf0f24aa05ec79f62e308b358cefd65d30f9d0959a9dc3" dependencies = [ "anyhow", "async-trait", @@ -2463,9 +2464,9 @@ dependencies = [ [[package]] name = "eszip" -version = "0.64.1" +version = "0.64.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d0670761c56597adcc00a65e42ac04d1f4b0ffa3f577cd7f386e85f7e4a1eb5" +checksum = "97374d7fd863b2b44ca217e739a1eb83361d0e5e2358d201145dac1aa3fa6a21" dependencies = [ "anyhow", "base64", @@ -3385,12 +3386,13 @@ dependencies = [ [[package]] name = "import_map" -version = "0.18.3" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bad4ef70a3e0f2ee403925d77d1e7b74e471b57ea75593f332aac31b57958b4" +checksum = "696717335b077e26921a60be7b7bdc15d1246074f1ac79d9e8560792535f7d07" dependencies = [ "indexmap", "log", + "percent-encoding", "serde", "serde_json", "url", diff --git a/cli/Cargo.toml b/cli/Cargo.toml index ad1e024b7b..1097d8d981 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -64,11 +64,11 @@ winres.workspace = true [dependencies] deno_ast = { workspace = true, features = ["bundler", "cjs", "codegen", "proposal", "react", "sourcemap", "transforms", "typescript", "view", "visit"] } deno_cache_dir = { workspace = true } -deno_config = "=0.10.0" +deno_config = "=0.11.0" deno_core = { workspace = true, features = ["include_js_files_for_snapshotting"] } -deno_doc = { version = "=0.110.1", features = ["html"] } -deno_emit = "=0.38.1" -deno_graph = { version = "=0.69.0", features = ["tokio_executor"] } +deno_doc = { version = "=0.113.1", features = ["html"] } +deno_emit = "=0.38.2" +deno_graph = { version = "=0.69.4", features = ["tokio_executor"] } deno_lint = { version = "=0.57.1", features = ["docs"] } deno_lockfile.workspace = true deno_npm = "=0.17.0" @@ -76,7 +76,7 @@ deno_runtime = { workspace = true, features = ["include_js_files_for_snapshottin deno_semver = "=0.5.4" deno_task_shell = "=0.14.3" deno_terminal.workspace = true -eszip = "=0.64.1" +eszip = "=0.64.2" napi_sym.workspace = true async-trait.workspace = true @@ -107,7 +107,7 @@ fs3.workspace = true glob = "0.3.1" hex.workspace = true ignore = "0.4" -import_map = { version = "=0.18.3", features = ["ext"] } +import_map = { version = "=0.19.0", features = ["ext"] } indexmap.workspace = true jsonc-parser = { version = "=0.23.0", features = ["serde"] } lazy-regex.workspace = true diff --git a/cli/args/import_map.rs b/cli/args/import_map.rs index da4f0eb85b..517c3bff68 100644 --- a/cli/args/import_map.rs +++ b/cli/args/import_map.rs @@ -1,5 +1,6 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +use deno_core::anyhow::Context; use deno_core::error::AnyError; use deno_core::serde_json; use deno_core::url::Url; @@ -11,35 +12,66 @@ use log::warn; use super::ConfigFile; use crate::file_fetcher::FileFetcher; -pub async fn resolve_import_map_from_specifier( - specifier: &Url, +pub async fn resolve_import_map( + specified_specifier: Option<&Url>, maybe_config_file: Option<&ConfigFile>, file_fetcher: &FileFetcher, +) -> Result, AnyError> { + if let Some(specifier) = specified_specifier { + resolve_import_map_from_specifier(specifier.clone(), file_fetcher) + .await + .with_context(|| format!("Unable to load '{}' import map", specifier)) + .map(Some) + } else if let Some(config_file) = maybe_config_file { + let maybe_url_and_value = config_file + .to_import_map_value(|specifier| { + let specifier = specifier.clone(); + async move { + let file = file_fetcher + .fetch(&specifier, PermissionsContainer::allow_all()) + .await? + .into_text_decoded()?; + Ok(file.source.to_string()) + } + }) + .await + .with_context(|| { + format!( + "Unable to resolve import map in '{}'", + config_file.specifier + ) + })?; + match maybe_url_and_value { + Some((url, value)) => { + import_map_from_value(url.into_owned(), value).map(Some) + } + None => Ok(None), + } + } else { + Ok(None) + } +} + +async fn resolve_import_map_from_specifier( + specifier: Url, + file_fetcher: &FileFetcher, ) -> Result { let value: serde_json::Value = if specifier.scheme() == "data" { let data_url_text = - deno_graph::source::RawDataUrl::parse(specifier)?.decode()?; + deno_graph::source::RawDataUrl::parse(&specifier)?.decode()?; serde_json::from_str(&data_url_text)? } else { - let import_map_config = maybe_config_file - .as_ref() - .filter(|c| c.specifier == *specifier); - match import_map_config { - Some(config) => config.to_import_map_value(), - None => { - let file = file_fetcher - .fetch(specifier, PermissionsContainer::allow_all()) - .await? - .into_text_decoded()?; - serde_json::from_str(&file.source)? - } - } + let file = file_fetcher + .fetch(&specifier, PermissionsContainer::allow_all()) + .await? + .into_text_decoded()?; + serde_json::from_str(&file.source)? }; import_map_from_value(specifier, value) } pub fn import_map_from_value( - specifier: &Url, + specifier: Url, json_value: serde_json::Value, ) -> Result { debug_assert!( diff --git a/cli/args/mod.rs b/cli/args/mod.rs index c5044fe658..9aa819a300 100644 --- a/cli/args/mod.rs +++ b/cli/args/mod.rs @@ -6,7 +6,7 @@ mod import_map; mod lockfile; pub mod package_json; -pub use self::import_map::resolve_import_map_from_specifier; +pub use self::import_map::resolve_import_map; use self::package_json::PackageJsonDeps; use ::import_map::ImportMap; use deno_core::resolve_url_or_path; @@ -34,11 +34,9 @@ pub use lockfile::LockfileError; pub use package_json::PackageJsonDepsProvider; 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::parking_lot::Mutex; use deno_core::serde_json; use deno_core::url::Url; @@ -875,10 +873,11 @@ impl CliOptions { } } - /// Based on an optional command line import map path and an optional - /// configuration file, return a resolved module specifier to an import map - /// and a boolean indicating if unknown keys should not result in diagnostics. - pub fn resolve_import_map_specifier( + /// Resolve the specifier for a specified import map. + /// + /// This will NOT include the config file if it + /// happens to be an import map. + pub fn resolve_specified_import_map_specifier( &self, ) -> Result, AnyError> { match self.overrides.import_map_specifier.clone() { @@ -896,94 +895,55 @@ impl CliOptions { file_fetcher: &FileFetcher, ) -> Result, AnyError> { if let Some(workspace_config) = self.maybe_workspace_config.as_ref() { + let root_config_file = self.maybe_config_file.as_ref().unwrap(); let base_import_map_config = ::import_map::ext::ImportMapConfig { - base_url: self.maybe_config_file.as_ref().unwrap().specifier.clone(), - import_map_value: workspace_config.base_import_map_value.clone(), + base_url: root_config_file.specifier.clone(), + import_map_value: root_config_file.to_import_map_value_from_imports(), }; let children_configs = workspace_config .members .iter() - .map(|member| { - let mut import_map_value = member.config_file.to_import_map_value(); - - let expanded_import_map_value = ::import_map::ext::expand_imports( - ::import_map::ext::ImportMapConfig { - base_url: member.config_file.specifier.clone(), - import_map_value: import_map_value.clone(), - }, - ); - - import_map_value - .as_object_mut() - .unwrap() - .insert("imports".to_string(), expanded_import_map_value); - ::import_map::ext::ImportMapConfig { - base_url: member.config_file.specifier.clone(), - import_map_value, - } + .map(|member| ::import_map::ext::ImportMapConfig { + base_url: member.config_file.specifier.clone(), + import_map_value: member + .config_file + .to_import_map_value_from_imports(), }) .collect(); - let maybe_import_map = ::import_map::ext::create_synthetic_import_map( - base_import_map_config, - children_configs, - ); - if let Some((_import_map_url, import_map)) = maybe_import_map { - log::debug!( - "Workspace config generated this import map {}", - serde_json::to_string_pretty(&import_map).unwrap() + let (import_map_url, import_map) = + ::import_map::ext::create_synthetic_import_map( + base_import_map_config, + children_configs, ); - let maybe_import_map_result = import_map::import_map_from_value( - // TODO(bartlomieju): maybe should be stored on the workspace config? - &self.maybe_config_file.as_ref().unwrap().specifier, - import_map, - ) - .map(Some); + log::debug!( + "Workspace config generated this import map {}", + serde_json::to_string_pretty(&import_map).unwrap() + ); + let maybe_import_map_result = + import_map::import_map_from_value(import_map_url, import_map).map(Some); - return match maybe_import_map_result { - Ok(maybe_import_map) => { - if let Some(mut import_map) = maybe_import_map { - import_map.ext_expand_imports(); - Ok(Some(import_map)) - } else { - Ok(None) - } - } - Err(err) => Err(err), - }; - } + return maybe_import_map_result; } - let import_map_specifier = match self.resolve_import_map_specifier()? { - Some(specifier) => specifier, - None => return Ok(None), - }; + if self + .overrides + .import_map_specifier + .as_ref() + .map(|s| s.is_none()) + == Some(true) + { + // overrode to not use an import map + return Ok(None); + } - let maybe_import_map_result = resolve_import_map_from_specifier( - &import_map_specifier, + let import_map_specifier = self.resolve_specified_import_map_specifier()?; + resolve_import_map( + import_map_specifier.as_ref(), self.maybe_config_file().as_ref(), file_fetcher, ) .await - .with_context(|| { - format!("Unable to load '{import_map_specifier}' import map") - }) - .map(Some); - - match maybe_import_map_result { - Ok(maybe_import_map) => { - if let Some(mut import_map) = maybe_import_map { - let url = import_map.base_url().as_str(); - if url.ends_with("deno.json") || url.ends_with("deno.jsonc") { - import_map.ext_expand_imports(); - } - Ok(Some(import_map)) - } else { - Ok(None) - } - } - Err(err) => Err(err), - } } pub fn node_ipc_fd(&self) -> Option { @@ -1124,7 +1084,7 @@ impl CliOptions { self .maybe_config_file .as_ref() - .and_then(|c| c.node_modules_dir_flag()) + .and_then(|c| c.json.node_modules_dir) }) } @@ -1613,7 +1573,7 @@ impl CliOptions { Vec::with_capacity(2) }; if let Ok(Some(import_map_path)) = self - .resolve_import_map_specifier() + .resolve_specified_import_map_specifier() .map(|ms| ms.and_then(|ref s| s.to_file_path().ok())) { paths.push(import_map_path); @@ -1638,9 +1598,9 @@ fn resolve_node_modules_folder( ) -> Result, AnyError> { let use_node_modules_dir = flags .node_modules_dir - .or_else(|| maybe_config_file.and_then(|c| c.node_modules_dir_flag())) + .or_else(|| maybe_config_file.and_then(|c| c.json.node_modules_dir)) .or(flags.vendor) - .or_else(|| maybe_config_file.and_then(|c| c.vendor_dir_flag())); + .or_else(|| maybe_config_file.and_then(|c| c.json.vendor)); let path = if use_node_modules_dir == Some(false) { return Ok(None); } else if let Some(state) = &*NPM_PROCESS_STATE { @@ -1668,7 +1628,7 @@ fn resolve_vendor_folder( ) -> Option { let use_vendor_dir = flags .vendor - .or_else(|| maybe_config_file.and_then(|c| c.vendor_dir_flag())) + .or_else(|| maybe_config_file.and_then(|c| c.json.vendor)) .unwrap_or(false); // Unlike the node_modules directory, there is no need to canonicalize // this directory because it's just used as a cache and the resolved @@ -1693,7 +1653,7 @@ fn resolve_import_map_specifier( ) -> Result, 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() { + if config_file.json.import_map.is_some() { log::warn!("{} the configuration file \"{}\" contains an entry for \"importMap\" that is being ignored.", colors::yellow("Warning"), config_file.specifier); } } @@ -1702,53 +1662,16 @@ fn resolve_import_map_specifier( .with_context(|| { format!("Bad URL (\"{import_map_path}\") for import map.") })?; - return Ok(Some(specifier)); + Ok(Some(specifier)) } else if let Some(config_file) = &maybe_config_file { - // if the config file is an import map we prefer to use it, over `importMap` - // field - if config_file.is_an_import_map() { - if let Some(_import_map_path) = config_file.to_import_map_path() { - log::warn!("{} \"importMap\" setting is ignored when \"imports\" or \"scopes\" are specified in the config file.", colors::yellow("Warning")); - } - - return Ok(Some(config_file.specifier.clone())); - } - - // 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() { - // if the import map is an absolute URL, use it as is - if let Ok(specifier) = deno_core::resolve_url(&import_map_path) { - return Ok(Some(specifier)); - } - 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()) - .with_context(|| format!( - "Bad URL (\"{import_map_path}\") for import map." - ))? - }; - return Ok(Some(specifier)); + // if the config file is an import map we prefer to use it, over `importMap` field + if config_file.is_an_import_map() && config_file.json.import_map.is_some() { + log::warn!("{} \"importMap\" setting is ignored when \"imports\" or \"scopes\" are specified in the config file.", colors::yellow("Warning")); } + Ok(None) + } else { + Ok(None) } - Ok(None) } pub struct StorageKeyResolver(Option>); @@ -1847,74 +1770,6 @@ mod test { use super::*; use pretty_assertions::assert_eq; - #[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), - &PathBuf::from("/"), - ); - 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_remote_config_file_local() { - let config_text = r#"{ - "importMap": "https://example.com/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), - &PathBuf::from("/"), - ); - 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_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), - &PathBuf::from("/"), - ); - 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#"{ @@ -1937,26 +1792,6 @@ mod test { assert_eq!(actual, Some(expected_specifier)); } - #[test] - fn resolve_import_map_embedded_take_precedence() { - let config_text = r#"{ - "importMap": "import_map.json", - "imports": {}, - }"#; - let config_specifier = - ModuleSpecifier::parse("file:///deno/deno.jsonc").unwrap(); - let config_file = - ConfigFile::new(config_text, config_specifier.clone()).unwrap(); - let actual = resolve_import_map_specifier( - None, - Some(&config_file), - &PathBuf::from("/"), - ); - assert!(actual.is_ok()); - let actual = actual.unwrap(); - assert_eq!(actual, Some(config_specifier)); - } - #[test] fn resolve_import_map_none() { let config_text = r#"{}"#; @@ -2087,7 +1922,7 @@ mod test { "pages/[id].ts", ] .into_iter() - .map(|p| normalize_path(temp_dir_path.join(p))) + .map(|p| deno_core::normalize_path(temp_dir_path.join(p))) .collect::>() ); } diff --git a/cli/factory.rs b/cli/factory.rs index 22bc6cdaf0..7302c03129 100644 --- a/cli/factory.rs +++ b/cli/factory.rs @@ -48,7 +48,6 @@ use crate::tools::run::hmr::HmrRunner; use crate::util::file_watcher::WatcherCommunicator; use crate::util::fs::canonicalize_path_maybe_not_exists; use crate::util::import_map::deno_json_deps; -use crate::util::import_map::import_map_deps; use crate::util::progress_bar::ProgressBar; use crate::util::progress_bar::ProgressBarStyle; use crate::worker::CliMainWorkerFactory; @@ -343,8 +342,8 @@ impl CliFactory { Some(workspace_config) => deno_lockfile::WorkspaceConfig { root: WorkspaceMemberConfig { package_json_deps, - dependencies: import_map_deps( - &workspace_config.base_import_map_value, + dependencies: deno_json_deps( + self.options.maybe_config_file().as_ref().unwrap(), ) .into_iter() .map(|req| req.to_string()) diff --git a/cli/lsp/config.rs b/cli/lsp/config.rs index d0909b926f..3d24c8c205 100644 --- a/cli/lsp/config.rs +++ b/cli/lsp/config.rs @@ -1238,12 +1238,12 @@ fn resolve_node_modules_dir(config_file: &ConfigFile) -> Option { // `nodeModulesDir: true` setting in the deno.json file. This is to // reduce the chance of modifying someone's node_modules directory // without them having asked us to do so. - let explicitly_disabled = config_file.node_modules_dir_flag() == Some(false); + let explicitly_disabled = config_file.json.node_modules_dir == Some(false); if explicitly_disabled { return None; } - let enabled = config_file.node_modules_dir_flag() == Some(true) - || config_file.vendor_dir_flag() == Some(true); + let enabled = config_file.json.node_modules_dir == Some(true) + || config_file.json.vendor == Some(true); if !enabled { return None; } diff --git a/cli/lsp/documents.rs b/cli/lsp/documents.rs index ddff92342b..66ebe5115c 100644 --- a/cli/lsp/documents.rs +++ b/cli/lsp/documents.rs @@ -1420,7 +1420,7 @@ impl Documents { options.document_preload_limit, options.maybe_import_map.as_deref(), maybe_jsx_config.as_ref(), - options.maybe_config_file.and_then(|c| c.vendor_dir_flag()), + options.maybe_config_file.and_then(|c| c.json.vendor), maybe_package_json_deps.as_ref(), options.maybe_config_file.map(|c| &c.json.unstable), ); diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs index 7e24c6e517..185cde6f9b 100644 --- a/cli/lsp/language_server.rs +++ b/cli/lsp/language_server.rs @@ -89,7 +89,7 @@ use super::tsc::TsServer; use super::urls; use crate::args::get_root_cert_store; use crate::args::package_json; -use crate::args::resolve_import_map_from_specifier; +use crate::args::resolve_import_map; use crate::args::CaData; use crate::args::CacheSetting; use crate::args::CliOptions; @@ -250,8 +250,6 @@ pub struct Inner { maybe_global_cache_path: Option, /// An optional import map which is used to resolve modules. maybe_import_map: Option>, - /// The URL for the import map which is used to determine relative imports. - maybe_import_map_uri: Option, /// An optional package.json configuration file. maybe_package_json: Option, /// Configuration for formatter which has been taken from specified config file. @@ -539,7 +537,6 @@ impl Inner { initial_cwd: initial_cwd.clone(), maybe_global_cache_path: None, maybe_import_map: None, - maybe_import_map_uri: None, maybe_package_json: None, fmt_options: FmtOptions::new_with_base(initial_cwd.clone()), task_queue: Default::default(), @@ -894,18 +891,18 @@ impl Inner { let mark = self.performance.mark("lsp.update_import_map"); let maybe_import_map_url = self.resolve_import_map_specifier()?; - if let Some(import_map_url) = maybe_import_map_url { - if import_map_url.scheme() != "data" { - lsp_log!(" Resolved import map: \"{}\"", import_map_url); + let maybe_import_map = self + .fetch_import_map( + maybe_import_map_url.as_ref(), + CacheSetting::RespectHeaders, + ) + .await?; + if let Some(import_map) = maybe_import_map { + if import_map.base_url().scheme() != "data" { + lsp_log!(" Resolved import map: \"{}\"", import_map.base_url()); } - - let import_map = self - .fetch_import_map(&import_map_url, CacheSetting::RespectHeaders) - .await?; - self.maybe_import_map_uri = Some(import_map_url); self.maybe_import_map = Some(Arc::new(import_map)); } else { - self.maybe_import_map_uri = None; self.maybe_import_map = None; } self.performance.measure(mark); @@ -914,22 +911,15 @@ impl Inner { async fn fetch_import_map( &self, - import_map_url: &ModuleSpecifier, + import_map_url: Option<&ModuleSpecifier>, cache_setting: CacheSetting, - ) -> Result { - resolve_import_map_from_specifier( + ) -> Result, AnyError> { + resolve_import_map( import_map_url, self.config.maybe_config_file(), &self.create_file_fetcher(cache_setting), ) .await - .map_err(|err| { - anyhow!( - "Failed to load the import map at: {}. {:#}", - import_map_url, - err - ) - }) } fn create_file_fetcher(&self, cache_setting: CacheSetting) -> FileFetcher { @@ -948,76 +938,40 @@ impl Inner { fn resolve_import_map_specifier( &self, ) -> Result, AnyError> { - Ok( - if let Some(import_map_str) = self - .config - .workspace_settings() - .import_map - .clone() - .and_then(|s| if s.is_empty() { None } else { Some(s) }) - { - lsp_log!( - "Setting import map from workspace settings: \"{}\"", - import_map_str - ); - if let Some(config_file) = self.config.maybe_config_file() { - if let Some(import_map_path) = config_file.to_import_map_path() { - lsp_log!("Warning: Import map \"{}\" configured in \"{}\" being ignored due to an import map being explicitly configured in workspace settings.", import_map_path, config_file.specifier); - } - } - if let Ok(url) = Url::parse(&import_map_str) { - Some(url) - } else if let Some(root_uri) = self.config.root_uri() { - let root_path = specifier_to_file_path(root_uri)?; - let import_map_path = root_path.join(&import_map_str); - let import_map_url = - Url::from_file_path(import_map_path).map_err(|_| { - anyhow!("Bad file path for import map: {}", import_map_str) - })?; - Some(import_map_url) - } else { - return Err(anyhow!( - "The path to the import map (\"{}\") is not resolvable.", - import_map_str - )); - } - } else if let Some(config_file) = self.config.maybe_config_file() { - if config_file.is_an_import_map() { - lsp_log!( - "Setting import map defined in configuration file: \"{}\"", - config_file.specifier - ); - let import_map_url = config_file.specifier.clone(); - Some(import_map_url) - } else if let Some(import_map_path) = config_file.to_import_map_path() { - lsp_log!( - "Setting import map from configuration file: \"{}\"", - import_map_path - ); - let specifier = if let Ok(config_file_path) = - config_file.specifier.to_file_path() - { - let import_map_file_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() - } else { - deno_core::resolve_import( - &import_map_path, - config_file.specifier.as_str(), - )? - }; - Some(specifier) - } else { - None - } - } else { - None - }, - ) + let Some(import_map_str) = self + .config + .workspace_settings() + .import_map + .clone() + .and_then(|s| if s.is_empty() { None } else { Some(s) }) + else { + return Ok(None); + }; + lsp_log!( + "Setting import map from workspace settings: \"{}\"", + import_map_str + ); + if let Some(config_file) = self.config.maybe_config_file() { + if let Some(import_map_path) = &config_file.json.import_map { + lsp_log!("Warning: Import map \"{}\" configured in \"{}\" being ignored due to an import map being explicitly configured in workspace settings.", import_map_path, config_file.specifier); + } + } + if let Ok(url) = Url::parse(&import_map_str) { + Ok(Some(url)) + } else if let Some(root_uri) = self.config.root_uri() { + let root_path = specifier_to_file_path(root_uri)?; + let import_map_path = root_path.join(&import_map_str); + let import_map_url = + Url::from_file_path(import_map_path).map_err(|_| { + anyhow!("Bad file path for import map: {}", import_map_str) + })?; + Ok(Some(import_map_url)) + } else { + Err(anyhow!( + "The path to the import map (\"{}\") is not resolvable.", + import_map_str + )) + } } pub fn update_debug_flag(&self) { @@ -1671,9 +1625,9 @@ impl Inner { // if the current import map, or config file has changed, we need to // reload the import map let import_map_changed = self - .maybe_import_map_uri + .maybe_import_map .as_ref() - .map(|uri| changes.contains(uri)) + .map(|import_map| changes.contains(import_map.base_url())) .unwrap_or(false); if touched || import_map_changed { if let Err(err) = self.update_import_map().await { @@ -3696,7 +3650,9 @@ impl Inner { self.config.maybe_lockfile().cloned(), self.maybe_package_json.clone(), )?; - cli_options.set_import_map_specifier(self.maybe_import_map_uri.clone()); + cli_options.set_import_map_specifier( + self.maybe_import_map.as_ref().map(|m| m.base_url().clone()), + ); let open_docs = self.documents.documents(DocumentsFilter::OpenDiagnosable); Ok(Some(PrepareCacheResult { diff --git a/cli/tools/bundle.rs b/cli/tools/bundle.rs index c0b1ce31bc..0a992fcb04 100644 --- a/cli/tools/bundle.rs +++ b/cli/tools/bundle.rs @@ -82,7 +82,7 @@ async fn bundle_action( .collect(); if let Ok(Some(import_map_path)) = cli_options - .resolve_import_map_specifier() + .resolve_specified_import_map_specifier() .map(|ms| ms.and_then(|ref s| s.to_file_path().ok())) { paths_to_watch.push(import_map_path); diff --git a/cli/tools/vendor/mod.rs b/cli/tools/vendor/mod.rs index f168f84a2e..975cf08f12 100644 --- a/cli/tools/vendor/mod.rs +++ b/cli/tools/vendor/mod.rs @@ -173,9 +173,24 @@ fn validate_options( options: &mut CliOptions, output_dir: &Path, ) -> Result<(), AnyError> { + let import_map_specifier = options + .resolve_specified_import_map_specifier()? + .or_else(|| { + let config_file = options.maybe_config_file().as_ref()?; + config_file + .to_import_map_specifier() + .ok() + .flatten() + .or_else(|| { + if config_file.is_an_import_map() { + Some(config_file.specifier.clone()) + } else { + None + } + }) + }); // check the import map - if let Some(import_map_path) = options - .resolve_import_map_specifier()? + if let Some(import_map_path) = import_map_specifier .and_then(|p| specifier_to_file_path(&p).ok()) .and_then(|p| canonicalize_path(&p).ok()) { diff --git a/cli/util/import_map.rs b/cli/util/import_map.rs index 9adc41af07..a075443bad 100644 --- a/cli/util/import_map.rs +++ b/cli/util/import_map.rs @@ -17,16 +17,6 @@ use deno_semver::npm::NpmPackageReqReference; use crate::resolver::MappedSpecifierResolver; -pub fn import_map_deps(value: &serde_json::Value) -> HashSet { - let Some(obj) = value.as_object() else { - return Default::default(); - }; - let values = imports_values(obj.get("imports")) - .into_iter() - .chain(scope_values(obj.get("scopes"))); - values_to_set(values) -} - pub fn deno_json_deps( config: &deno_config::ConfigFile, ) -> HashSet { @@ -335,7 +325,7 @@ mod tests { } }); let ImportMapWithDiagnostics { import_map, .. } = - import_map::parse_from_value(&deno_json_url, value).unwrap(); + import_map::parse_from_value(deno_json_url, value).unwrap(); let mapped_resolved = MappedSpecifierResolver::new( Some(Arc::new(import_map)), Arc::new(PackageJsonDepsProvider::new(None)), diff --git a/tests/testdata/run/workspaces/basic/main.out b/tests/testdata/run/workspaces/basic/main.out index 77e0de4d16..a955ac54cc 100644 --- a/tests/testdata/run/workspaces/basic/main.out +++ b/tests/testdata/run/workspaces/basic/main.out @@ -1,6 +1,7 @@ [WILDCARD]Workspace config generated this import map { "imports": { - "chalk": "npm:chalk" + "chalk": "npm:chalk", + "chalk/": "npm:/chalk/" }, "scopes": { "./foo/": {