fix(lsp): import map expansion (#22553)

This commit is contained in:
David Sherret 2024-02-24 00:21:09 -05:00 committed by GitHub
parent 76e8e02bba
commit 6567dc94a9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 200 additions and 370 deletions

26
Cargo.lock generated
View File

@ -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",

View File

@ -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

View File

@ -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<Option<ImportMap>, 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<ImportMap, AnyError> {
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<ImportMap, AnyError> {
debug_assert!(

View File

@ -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<Option<ModuleSpecifier>, AnyError> {
match self.overrides.import_map_specifier.clone() {
@ -896,94 +895,55 @@ impl CliOptions {
file_fetcher: &FileFetcher,
) -> Result<Option<ImportMap>, 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<i64> {
@ -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<Option<PathBuf>, 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<PathBuf> {
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<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() {
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<Option<String>>);
@ -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::<Vec<_>>()
);
}

View File

@ -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())

View File

@ -1238,12 +1238,12 @@ fn resolve_node_modules_dir(config_file: &ConfigFile) -> Option<PathBuf> {
// `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;
}

View File

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

View File

@ -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<PathBuf>,
/// An optional import map which is used to resolve modules.
maybe_import_map: Option<Arc<ImportMap>>,
/// The URL for the import map which is used to determine relative imports.
maybe_import_map_uri: Option<Url>,
/// An optional package.json configuration file.
maybe_package_json: Option<PackageJson>,
/// 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<ImportMap, AnyError> {
resolve_import_map_from_specifier(
) -> Result<Option<ImportMap>, 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<Option<ModuleSpecifier>, 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 {

View File

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

View File

@ -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())
{

View File

@ -17,16 +17,6 @@ use deno_semver::npm::NpmPackageReqReference;
use crate::resolver::MappedSpecifierResolver;
pub fn import_map_deps(value: &serde_json::Value) -> HashSet<JsrDepPackageReq> {
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<JsrDepPackageReq> {
@ -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)),

View File

@ -1,6 +1,7 @@
[WILDCARD]Workspace config generated this import map {
"imports": {
"chalk": "npm:chalk"
"chalk": "npm:chalk",
"chalk/": "npm:/chalk/"
},
"scopes": {
"./foo/": {