summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock18
-rw-r--r--cli/Cargo.toml2
-rw-r--r--cli/args/config_file.rs17
-rw-r--r--cli/args/import_map.rs35
-rw-r--r--cli/args/mod.rs80
-rw-r--r--cli/lsp/language_server.rs177
-rw-r--r--cli/proc_state.rs47
-rw-r--r--cli/schemas/config-file.v1.json24
-rw-r--r--cli/tests/integration/lsp_tests.rs87
-rw-r--r--cli/tests/integration/run_tests.rs11
-rw-r--r--cli/tests/testdata/import_maps/config.json15
-rw-r--r--cli/tests/testdata/import_maps/import_map_invalid.json7
-rw-r--r--cli/tests/testdata/lsp/deno.embedded_import_map.jsonc5
-rw-r--r--cli/tests/testdata/run/033_import_map_in_config_file.out8
-rw-r--r--cli/tests/testdata/run/033_import_map_in_flag_has_precedence.out1
-rw-r--r--cli/tools/standalone.rs23
16 files changed, 418 insertions, 139 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 2832b4abc..dd9ae0d32 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -792,7 +792,7 @@ dependencies = [
"fwdansi",
"glibc_version",
"http",
- "import_map",
+ "import_map 0.15.0",
"indexmap",
"jsonc-parser",
"junction",
@@ -980,7 +980,7 @@ dependencies = [
"deno_ast",
"deno_graph",
"futures",
- "import_map",
+ "import_map 0.13.0",
"lazy_static",
"regex",
"serde",
@@ -2309,6 +2309,20 @@ dependencies = [
]
[[package]]
+name = "import_map"
+version = "0.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "632089ec08bd62e807311104122fb26d5c911ab172e2b9864be154a575979e29"
+dependencies = [
+ "cfg-if",
+ "indexmap",
+ "log",
+ "serde",
+ "serde_json",
+ "url",
+]
+
+[[package]]
name = "indexmap"
version = "1.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/cli/Cargo.toml b/cli/Cargo.toml
index 9ef5e93dd..8e89c7dc4 100644
--- a/cli/Cargo.toml
+++ b/cli/Cargo.toml
@@ -73,7 +73,7 @@ eszip = "=0.33.0"
fancy-regex = "=0.10.0"
flate2.workspace = true
http.workspace = true
-import_map = "=0.13.0"
+import_map = "=0.15.0"
indexmap = "=1.9.2"
jsonc-parser = { version = "=0.21.0", features = ["serde"] }
libc.workspace = true
diff --git a/cli/args/config_file.rs b/cli/args/config_file.rs
index c31f09960..82ae7e5d7 100644
--- a/cli/args/config_file.rs
+++ b/cli/args/config_file.rs
@@ -465,6 +465,8 @@ pub enum LockConfig {
pub struct ConfigFileJson {
pub compiler_options: Option<Value>,
pub import_map: Option<String>,
+ pub imports: Option<Value>,
+ pub scopes: Option<Value>,
pub lint: Option<Value>,
pub fmt: Option<Value>,
pub tasks: Option<Value>,
@@ -667,6 +669,21 @@ impl ConfigFile {
self.json.import_map.clone()
}
+ pub fn to_import_map_value(&self) -> Value {
+ let mut value = serde_json::Map::with_capacity(2);
+ if let Some(imports) = &self.json.imports {
+ value.insert("imports".to_string(), imports.clone());
+ }
+ if let Some(scopes) = &self.json.scopes {
+ value.insert("scopes".to_string(), scopes.clone());
+ }
+ value.into()
+ }
+
+ pub fn is_an_import_map(&self) -> bool {
+ self.json.imports.is_some() || self.json.scopes.is_some()
+ }
+
pub fn to_fmt_config(&self) -> Result<Option<FmtConfig>, AnyError> {
if let Some(config) = self.json.fmt.clone() {
let fmt_config: SerializedFmtConfig = serde_json::from_value(config)
diff --git a/cli/args/import_map.rs b/cli/args/import_map.rs
new file mode 100644
index 000000000..200336c43
--- /dev/null
+++ b/cli/args/import_map.rs
@@ -0,0 +1,35 @@
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+
+use deno_core::error::AnyError;
+use deno_core::serde_json;
+use deno_core::url::Url;
+use import_map::ImportMap;
+use import_map::ImportMapDiagnostic;
+use log::warn;
+
+pub fn import_map_from_value(
+ specifier: &Url,
+ json_value: serde_json::Value,
+) -> Result<ImportMap, AnyError> {
+ debug_assert!(
+ !specifier.as_str().contains("../"),
+ "Import map specifier incorrectly contained ../: {}",
+ specifier.as_str()
+ );
+ let result = import_map::parse_from_value(specifier, json_value)?;
+ print_import_map_diagnostics(&result.diagnostics);
+ Ok(result.import_map)
+}
+
+fn print_import_map_diagnostics(diagnostics: &[ImportMapDiagnostic]) {
+ if !diagnostics.is_empty() {
+ warn!(
+ "Import map diagnostics:\n{}",
+ diagnostics
+ .iter()
+ .map(|d| format!(" - {}", d))
+ .collect::<Vec<_>>()
+ .join("\n")
+ );
+ }
+}
diff --git a/cli/args/mod.rs b/cli/args/mod.rs
index 4cb960238..1bf294018 100644
--- a/cli/args/mod.rs
+++ b/cli/args/mod.rs
@@ -2,10 +2,12 @@
mod config_file;
mod flags;
-mod lockfile;
-
mod flags_allow_net;
+mod import_map;
+mod lockfile;
+pub use self::import_map::import_map_from_value;
+use ::import_map::ImportMap;
pub use config_file::BenchConfig;
pub use config_file::CompilerOptions;
pub use config_file::ConfigFile;
@@ -19,6 +21,8 @@ pub use config_file::TsConfig;
pub use config_file::TsConfigForEmit;
pub use config_file::TsConfigType;
pub use config_file::TsTypeLib;
+use deno_core::serde_json;
+use deno_runtime::permissions::PermissionsContainer;
pub use flags::*;
pub use lockfile::Lockfile;
pub use lockfile::LockfileError;
@@ -49,6 +53,7 @@ use std::path::PathBuf;
use std::sync::Arc;
use crate::cache::DenoDir;
+use crate::file_fetcher::FileFetcher;
use crate::util::fs::canonicalize_path_maybe_not_exists;
use crate::version;
@@ -547,12 +552,13 @@ 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.
+ /// 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(
&self,
) -> Result<Option<ModuleSpecifier>, AnyError> {
match self.overrides.import_map_specifier.clone() {
- Some(path) => Ok(path),
+ Some(maybe_path) => Ok(maybe_path),
None => resolve_import_map_specifier(
self.flags.import_map_path.as_deref(),
self.maybe_config_file.as_ref(),
@@ -560,6 +566,45 @@ impl CliOptions {
}
}
+ pub async fn resolve_import_map(
+ &self,
+ file_fetcher: &FileFetcher,
+ ) -> Result<Option<ImportMap>, AnyError> {
+ let import_map_specifier = match self.resolve_import_map_specifier()? {
+ Some(specifier) => specifier,
+ None => return Ok(None),
+ };
+ self
+ .resolve_import_map_from_specifier(&import_map_specifier, file_fetcher)
+ .await
+ .context(format!(
+ "Unable to load '{}' import map",
+ import_map_specifier
+ ))
+ .map(Some)
+ }
+
+ async fn resolve_import_map_from_specifier(
+ &self,
+ import_map_specifier: &ModuleSpecifier,
+ file_fetcher: &FileFetcher,
+ ) -> Result<ImportMap, AnyError> {
+ let import_map_config = self
+ .get_maybe_config_file()
+ .as_ref()
+ .filter(|c| c.specifier == *import_map_specifier);
+ let value: serde_json::Value = match import_map_config {
+ Some(config) => config.to_import_map_value(),
+ None => {
+ let file = file_fetcher
+ .fetch(import_map_specifier, PermissionsContainer::allow_all())
+ .await?;
+ serde_json::from_str(&file.source)?
+ }
+ };
+ import_map_from_value(import_map_specifier, value)
+ }
+
/// Overrides the import map specifier to use.
pub fn set_import_map_specifier(&mut self, path: Option<ModuleSpecifier>) {
self.overrides.import_map_specifier = Some(path);
@@ -907,6 +952,16 @@ fn resolve_import_map_specifier(
.context(format!("Bad URL (\"{}\") for import map.", import_map_path))?;
return 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,
@@ -990,7 +1045,7 @@ mod test {
let actual = actual.unwrap();
assert_eq!(
actual,
- Some(ModuleSpecifier::parse("file:///deno/import_map.json").unwrap())
+ Some(ModuleSpecifier::parse("file:///deno/import_map.json").unwrap(),)
);
}
@@ -1052,6 +1107,21 @@ mod test {
}
#[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).unwrap();
+ let actual = resolve_import_map_specifier(None, Some(&config_file));
+ assert!(actual.is_ok());
+ let actual = actual.unwrap();
+ assert_eq!(actual, Some(config_specifier));
+ }
+
+ #[test]
fn resolve_import_map_none() {
let config_text = r#"{}"#;
let config_specifier =
diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs
index 0941fe0fb..7f53b17f3 100644
--- a/cli/lsp/language_server.rs
+++ b/cli/lsp/language_server.rs
@@ -57,6 +57,7 @@ use super::tsc::AssetsSnapshot;
use super::tsc::TsServer;
use super::urls;
use crate::args::get_root_cert_store;
+use crate::args::import_map_from_value;
use crate::args::CaData;
use crate::args::CacheSetting;
use crate::args::CliOptions;
@@ -72,7 +73,6 @@ use crate::http_util::HttpClient;
use crate::npm::NpmCache;
use crate::npm::NpmPackageResolver;
use crate::npm::RealNpmRegistryApi;
-use crate::proc_state::import_map_from_text;
use crate::proc_state::ProcState;
use crate::tools::fmt::format_file;
use crate::tools::fmt::format_parsed_source;
@@ -601,47 +601,84 @@ impl Inner {
pub async fn update_import_map(&mut self) -> Result<(), AnyError> {
let mark = self.performance.mark("update_import_map", None::<()>);
- let maybe_import_map_url = if let Some(import_map_str) = self
- .config
- .get_workspace_settings()
- .import_map
- .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.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::from_file_path(&import_map_str) {
- Some(url)
- } else if import_map_str.starts_with("data:") {
- Some(Url::parse(&import_map_str).map_err(|_| {
- anyhow!("Bad data url for import map: {}", import_map_str)
- })?)
- } 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);
- Some(Url::from_file_path(import_map_path).map_err(|_| {
- anyhow!("Bad file path for import map: {}", import_map_str)
- })?)
- } else {
- return Err(anyhow!(
- "The path to the import map (\"{}\") is not resolvable.",
- import_map_str
- ));
- }
- } else if let Some(config_file) = &self.maybe_config_file {
- if let Some(import_map_path) = config_file.to_import_map_path() {
+ let maybe_import_map_url = self.resolve_import_map_specifier()?;
+ if let Some(import_map_url) = maybe_import_map_url {
+ let import_map = self
+ .resolve_import_map_from_specifier(&import_map_url)
+ .await
+ .map_err(|err| {
+ anyhow!(
+ "Failed to load the import map at: {}. {:#}",
+ import_map_url,
+ err
+ )
+ })?;
+ 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);
+ Ok(())
+ }
+
+ fn resolve_import_map_specifier(
+ &self,
+ ) -> Result<Option<ModuleSpecifier>, AnyError> {
+ Ok(
+ if let Some(import_map_str) = self
+ .config
+ .get_workspace_settings()
+ .import_map
+ .and_then(|s| if s.is_empty() { None } else { Some(s) })
+ {
lsp_log!(
- "Setting import map from configuration file: \"{}\"",
- import_map_path
+ "Setting import map from workspace settings: \"{}\"",
+ import_map_str
);
- let specifier =
- if let Ok(config_file_path) = config_file.specifier.to_file_path() {
+ if let Some(config_file) = &self.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::from_file_path(&import_map_str) {
+ Some(url)
+ } else if import_map_str.starts_with("data:") {
+ let import_map_url = Url::parse(&import_map_str).map_err(|_| {
+ anyhow!("Bad data url for import map: {}", import_map_str)
+ })?;
+ Some(import_map_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.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(|| {
@@ -655,39 +692,41 @@ impl Inner {
config_file.specifier.as_str(),
)?
};
- Some(specifier)
+ Some(specifier)
+ } else {
+ None
+ }
} else {
None
- }
+ },
+ )
+ }
+
+ async fn resolve_import_map_from_specifier(
+ &self,
+ import_map_url: &ModuleSpecifier,
+ ) -> Result<ImportMap, AnyError> {
+ let import_map_json: Value = if import_map_url.scheme() == "data" {
+ serde_json::from_str(&get_source_from_data_url(import_map_url)?.0)?
} else {
- None
+ let import_map_path = specifier_to_file_path(import_map_url)?;
+ lsp_log!(
+ " Resolved import map: \"{}\"",
+ import_map_path.to_string_lossy()
+ );
+ let import_map_config_file = self
+ .maybe_config_file
+ .as_ref()
+ .filter(|c| c.specifier == *import_map_url);
+ match import_map_config_file {
+ Some(c) => c.to_import_map_value(),
+ None => {
+ serde_json::from_str(&fs::read_to_string(import_map_path).await?)?
+ }
+ }
};
- if let Some(import_map_url) = maybe_import_map_url {
- let import_map_json = if import_map_url.scheme() == "data" {
- get_source_from_data_url(&import_map_url)?.0
- } else {
- let import_map_path = specifier_to_file_path(&import_map_url)?;
- lsp_log!(
- " Resolved import map: \"{}\"",
- import_map_path.to_string_lossy()
- );
- fs::read_to_string(import_map_path).await.map_err(|err| {
- anyhow!(
- "Failed to load the import map at: {}. [{}]",
- import_map_url,
- err
- )
- })?
- };
- let import_map = import_map_from_text(&import_map_url, &import_map_json)?;
- 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);
- Ok(())
+
+ import_map_from_value(import_map_url, import_map_json)
}
pub fn update_debug_flag(&self) {
diff --git a/cli/proc_state.rs b/cli/proc_state.rs
index fd5ad4840..e979ffd6b 100644
--- a/cli/proc_state.rs
+++ b/cli/proc_state.rs
@@ -43,7 +43,6 @@ use deno_core::futures;
use deno_core::parking_lot::Mutex;
use deno_core::parking_lot::RwLock;
use deno_core::resolve_url_or_path;
-use deno_core::url::Url;
use deno_core::CompiledWasmModuleStore;
use deno_core::ModuleSpecifier;
use deno_core::SharedArrayBufferStore;
@@ -206,25 +205,11 @@ impl ProcState {
)?;
let lockfile = cli_options.maybe_lock_file();
- let maybe_import_map_specifier =
- cli_options.resolve_import_map_specifier()?;
-
- let maybe_import_map =
- if let Some(import_map_specifier) = maybe_import_map_specifier {
- let file = file_fetcher
- .fetch(&import_map_specifier, PermissionsContainer::allow_all())
- .await
- .context(format!(
- "Unable to load '{}' import map",
- import_map_specifier
- ))?;
- let import_map =
- import_map_from_text(&import_map_specifier, &file.source)?;
- Some(Arc::new(import_map))
- } else {
- None
- };
+ let maybe_import_map = cli_options
+ .resolve_import_map(&file_fetcher)
+ .await?
+ .map(Arc::new);
let maybe_inspector_server =
cli_options.resolve_inspector_server().map(Arc::new);
@@ -765,30 +750,6 @@ impl ProcState {
}
}
-pub fn import_map_from_text(
- specifier: &Url,
- json_text: &str,
-) -> Result<ImportMap, AnyError> {
- debug_assert!(
- !specifier.as_str().contains("../"),
- "Import map specifier incorrectly contained ../: {}",
- specifier.as_str()
- );
- let result = import_map::parse_from_json(specifier, json_text)?;
- if !result.diagnostics.is_empty() {
- warn!(
- "Import map diagnostics:\n{}",
- result
- .diagnostics
- .into_iter()
- .map(|d| format!(" - {}", d))
- .collect::<Vec<_>>()
- .join("\n")
- );
- }
- Ok(result.import_map)
-}
-
#[derive(Clone, Debug)]
struct FileWatcherReporter {
sender: tokio::sync::mpsc::UnboundedSender<Vec<PathBuf>>,
diff --git a/cli/schemas/config-file.v1.json b/cli/schemas/config-file.v1.json
index 675a763a4..f0b496720 100644
--- a/cli/schemas/config-file.v1.json
+++ b/cli/schemas/config-file.v1.json
@@ -198,9 +198,31 @@
}
},
"importMap": {
- "description": "The location of an import map to be used when resolving modules. If an import map is explicitly specified, it will override this value.",
+ "description": "The location of an import map to be used when resolving modules. If an import map is specified as an `--importmap` flag or using \"imports\" and \"scopes\" properties, they will override this value.",
"type": "string"
},
+ "imports": {
+ "description": "A map of specifiers to their remapped specifiers.",
+ "type": "object",
+ "additionalProperties": {
+ "description": "The key is the specifier or partial specifier to match, with a value that represents the target specifier.",
+ "type": "string"
+ }
+ },
+ "scopes": {
+ "default": {},
+ "description": "Define a scope which remaps a specifier in only a specified scope",
+ "type": "object",
+ "properties": {},
+ "additionalProperties": {
+ "description": "A definition of a scoped remapping.",
+ "type": "object",
+ "additionalProperties": {
+ "description": "The key is the specifier or partial specifier to match within the referring scope, with a value that represents the target specifier.",
+ "type": "string"
+ }
+ }
+ },
"lint": {
"description": "Configuration for linter",
"type": "object",
diff --git a/cli/tests/integration/lsp_tests.rs b/cli/tests/integration/lsp_tests.rs
index f7c0e6137..be280bfa7 100644
--- a/cli/tests/integration/lsp_tests.rs
+++ b/cli/tests/integration/lsp_tests.rs
@@ -583,6 +583,93 @@ fn lsp_import_map_config_file() {
}
#[test]
+fn lsp_import_map_embedded_in_config_file() {
+ let temp_dir = TempDir::new();
+ let mut params: lsp::InitializeParams =
+ serde_json::from_value(load_fixture("initialize_params.json")).unwrap();
+
+ let deno_import_map_jsonc = serde_json::to_string_pretty(&load_fixture(
+ "deno.embedded_import_map.jsonc",
+ ))
+ .unwrap();
+ temp_dir.write("deno.embedded_import_map.jsonc", deno_import_map_jsonc);
+
+ params.root_uri = Some(Url::from_file_path(temp_dir.path()).unwrap());
+ if let Some(Value::Object(mut map)) = params.initialization_options {
+ map.insert(
+ "config".to_string(),
+ json!("./deno.embedded_import_map.jsonc"),
+ );
+ params.initialization_options = Some(Value::Object(map));
+ }
+ fs::create_dir(temp_dir.path().join("lib")).unwrap();
+ temp_dir.write("lib/b.ts", r#"export const b = "b";"#);
+
+ let deno_exe = deno_exe_path();
+ let mut client = LspClient::new(&deno_exe, false).unwrap();
+ client
+ .write_request::<_, _, Value>("initialize", params)
+ .unwrap();
+
+ client.write_notification("initialized", json!({})).unwrap();
+ let uri = Url::from_file_path(temp_dir.path().join("a.ts")).unwrap();
+
+ let diagnostics = did_open(
+ &mut client,
+ json!({
+ "textDocument": {
+ "uri": uri,
+ "languageId": "typescript",
+ "version": 1,
+ "text": "import { b } from \"/~/b.ts\";\n\nconsole.log(b);\n"
+ }
+ }),
+ );
+
+ let diagnostics = diagnostics.into_iter().flat_map(|x| x.diagnostics);
+ assert_eq!(diagnostics.count(), 0);
+
+ let (maybe_res, maybe_err) = client
+ .write_request::<_, _, Value>(
+ "textDocument/hover",
+ json!({
+ "textDocument": {
+ "uri": uri
+ },
+ "position": {
+ "line": 2,
+ "character": 12
+ }
+ }),
+ )
+ .unwrap();
+ assert!(maybe_err.is_none());
+ assert_eq!(
+ maybe_res,
+ Some(json!({
+ "contents": [
+ {
+ "language": "typescript",
+ "value":"(alias) const b: \"b\"\nimport b"
+ },
+ ""
+ ],
+ "range": {
+ "start": {
+ "line": 2,
+ "character": 12
+ },
+ "end": {
+ "line": 2,
+ "character": 13
+ }
+ }
+ }))
+ );
+ shutdown(&mut client);
+}
+
+#[test]
fn lsp_deno_task() {
let temp_dir = TempDir::new();
let workspace_root = temp_dir.path().canonicalize().unwrap();
diff --git a/cli/tests/integration/run_tests.rs b/cli/tests/integration/run_tests.rs
index fd6e6588c..62bfe136f 100644
--- a/cli/tests/integration/run_tests.rs
+++ b/cli/tests/integration/run_tests.rs
@@ -164,6 +164,17 @@ itest!(_033_import_map {
output: "run/033_import_map.out",
});
+itest!(_033_import_map_in_config_file {
+ args: "run --reload --config=import_maps/config.json import_maps/test.ts",
+ output: "run/033_import_map_in_config_file.out",
+});
+
+itest!(_033_import_map_in_flag_has_precedence {
+ args: "run --quiet --reload --import-map=import_maps/import_map_invalid.json --config=import_maps/config.json import_maps/test.ts",
+ output: "run/033_import_map_in_flag_has_precedence.out",
+ exit_code: 1,
+});
+
itest!(_033_import_map_remote {
args:
"run --quiet --reload --import-map=http://127.0.0.1:4545/import_maps/import_map_remote.json --unstable import_maps/test_remote.ts",
diff --git a/cli/tests/testdata/import_maps/config.json b/cli/tests/testdata/import_maps/config.json
new file mode 100644
index 000000000..b296a63c7
--- /dev/null
+++ b/cli/tests/testdata/import_maps/config.json
@@ -0,0 +1,15 @@
+{
+ "importMap": "./import_map.json",
+ "imports": {
+ "moment": "./moment/moment.ts",
+ "moment/": "./moment/",
+ "lodash": "./lodash/lodash.ts",
+ "lodash/": "./lodash/",
+ "https://www.unpkg.com/vue/dist/vue.runtime.esm.js": "./vue.ts"
+ },
+ "scopes": {
+ "scope/": {
+ "moment": "./scoped_moment.ts"
+ }
+ }
+}
diff --git a/cli/tests/testdata/import_maps/import_map_invalid.json b/cli/tests/testdata/import_maps/import_map_invalid.json
new file mode 100644
index 000000000..a09d280c5
--- /dev/null
+++ b/cli/tests/testdata/import_maps/import_map_invalid.json
@@ -0,0 +1,7 @@
+{
+ "imports": {
+ "https://www.unpkg.com/vue/dist/vue.runtime.esm.js": "./vue.ts"
+ },
+ "scopes": {
+ }
+}
diff --git a/cli/tests/testdata/lsp/deno.embedded_import_map.jsonc b/cli/tests/testdata/lsp/deno.embedded_import_map.jsonc
new file mode 100644
index 000000000..75d5d0849
--- /dev/null
+++ b/cli/tests/testdata/lsp/deno.embedded_import_map.jsonc
@@ -0,0 +1,5 @@
+{
+ "imports": {
+ "/~/": "./lib/"
+ }
+}
diff --git a/cli/tests/testdata/run/033_import_map_in_config_file.out b/cli/tests/testdata/run/033_import_map_in_config_file.out
new file mode 100644
index 000000000..72df124a2
--- /dev/null
+++ b/cli/tests/testdata/run/033_import_map_in_config_file.out
@@ -0,0 +1,8 @@
+Warning "importMap" setting is ignored when "imports" or "scopes" are specified in the config file.
+Hello from remapped moment!
+Hello from remapped moment dir!
+Hello from remapped lodash!
+Hello from remapped lodash dir!
+Hello from remapped Vue!
+Hello from scoped moment!
+Hello from scoped!
diff --git a/cli/tests/testdata/run/033_import_map_in_flag_has_precedence.out b/cli/tests/testdata/run/033_import_map_in_flag_has_precedence.out
new file mode 100644
index 000000000..e9b183ee6
--- /dev/null
+++ b/cli/tests/testdata/run/033_import_map_in_flag_has_precedence.out
@@ -0,0 +1 @@
+error: Relative import path [WILDCARD] not prefixed with / or ./ or ../ and not in import map [WILDCARD]
diff --git a/cli/tools/standalone.rs b/cli/tools/standalone.rs
index 8b99b8874..60d77d586 100644
--- a/cli/tools/standalone.rs
+++ b/cli/tools/standalone.rs
@@ -19,10 +19,8 @@ use deno_core::error::generic_error;
use deno_core::error::AnyError;
use deno_core::resolve_url_or_path;
use deno_core::serde_json;
-use deno_core::url::Url;
use deno_graph::ModuleSpecifier;
use deno_runtime::colors;
-use deno_runtime::permissions::PermissionsContainer;
use std::env;
use std::fs;
use std::fs::File;
@@ -166,22 +164,11 @@ async fn create_standalone_binary(
Some(CaData::Bytes(bytes)) => Some(bytes.clone()),
None => None,
};
- let maybe_import_map: Option<(Url, String)> =
- match ps.options.resolve_import_map_specifier()? {
- None => None,
- Some(import_map_specifier) => {
- let file = ps
- .file_fetcher
- .fetch(&import_map_specifier, PermissionsContainer::allow_all())
- .await
- .context(format!(
- "Unable to load '{}' import map",
- import_map_specifier
- ))?;
-
- Some((import_map_specifier, file.source.to_string()))
- }
- };
+ let maybe_import_map = ps
+ .options
+ .resolve_import_map(&ps.file_fetcher)
+ .await?
+ .map(|import_map| (import_map.base_url().clone(), import_map.to_json()));
let metadata = Metadata {
argv: compile_flags.args.clone(),
unstable: ps.options.unstable(),