mirror of
https://github.com/denoland/deno.git
synced 2024-11-21 15:04:11 -05:00
fix(lsp): import map expansion (#22553)
This commit is contained in:
parent
76e8e02bba
commit
6567dc94a9
12 changed files with 200 additions and 370 deletions
26
Cargo.lock
generated
26
Cargo.lock
generated
|
@ -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",
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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!(
|
||||
|
|
269
cli/args/mod.rs
269
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<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<_>>()
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
);
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
19
cli/tools/vendor/mod.rs
vendored
19
cli/tools/vendor/mod.rs
vendored
|
@ -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())
|
||||
{
|
||||
|
|
|
@ -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)),
|
||||
|
|
3
tests/testdata/run/workspaces/basic/main.out
vendored
3
tests/testdata/run/workspaces/basic/main.out
vendored
|
@ -1,6 +1,7 @@
|
|||
[WILDCARD]Workspace config generated this import map {
|
||||
"imports": {
|
||||
"chalk": "npm:chalk"
|
||||
"chalk": "npm:chalk",
|
||||
"chalk/": "npm:/chalk/"
|
||||
},
|
||||
"scopes": {
|
||||
"./foo/": {
|
||||
|
|
Loading…
Reference in a new issue