summaryrefslogtreecommitdiff
path: root/cli/config_file.rs
diff options
context:
space:
mode:
Diffstat (limited to 'cli/config_file.rs')
-rw-r--r--cli/config_file.rs131
1 files changed, 131 insertions, 0 deletions
diff --git a/cli/config_file.rs b/cli/config_file.rs
index 3ca00b0f4..53fe2f35e 100644
--- a/cli/config_file.rs
+++ b/cli/config_file.rs
@@ -227,6 +227,56 @@ pub fn json_merge(a: &mut Value, b: &Value) {
}
}
+/// Based on an optional command line import map path and an optional
+/// configuration file, return a resolved module specifier to an import map.
+pub fn resolve_import_map_specifier(
+ maybe_import_map_path: Option<&str>,
+ maybe_config_file: Option<&ConfigFile>,
+) -> 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() {
+ log::warn!("{} the configuration file \"{}\" contains an entry for \"importMap\" that is being ignored.", crate::colors::yellow("Warning"), config_file.specifier);
+ }
+ }
+ let specifier = deno_core::resolve_url_or_path(import_map_path)
+ .context(format!("Bad URL (\"{}\") for import map.", import_map_path))?;
+ return Ok(Some(specifier));
+ } else if let Some(config_file) = &maybe_config_file {
+ // 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() {
+ 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 = 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())
+ .context(format!(
+ "Bad URL (\"{}\") for import map.",
+ import_map_path
+ ))?
+ };
+ return Ok(Some(specifier));
+ }
+ }
+ Ok(None)
+}
+
fn parse_compiler_options(
compiler_options: &HashMap<String, Value>,
maybe_specifier: Option<ModuleSpecifier>,
@@ -476,6 +526,7 @@ pub struct FmtConfig {
#[serde(rename_all = "camelCase")]
pub struct ConfigFileJson {
pub compiler_options: Option<Value>,
+ pub import_map: Option<String>,
pub lint: Option<Value>,
pub fmt: Option<Value>,
}
@@ -583,6 +634,10 @@ impl ConfigFile {
}
}
+ pub fn to_import_map_path(&self) -> Option<String> {
+ self.json.import_map.clone()
+ }
+
pub fn to_lint_config(&self) -> Result<Option<LintConfig>, AnyError> {
if let Some(config) = self.json.lint.clone() {
let lint_config: SerializedLintConfig = serde_json::from_value(config)
@@ -914,4 +969,80 @@ mod tests {
let err = discover_from(&d, &mut checked).unwrap_err();
assert!(err.to_string().contains("Unable to parse config file"));
}
+
+ #[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));
+ 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_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));
+ 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#"{
+ "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(Some("import-map.json"), Some(&config_file));
+ let import_map_path =
+ std::env::current_dir().unwrap().join("import-map.json");
+ let expected_specifier =
+ ModuleSpecifier::from_file_path(&import_map_path).unwrap();
+ assert!(actual.is_ok());
+ let actual = actual.unwrap();
+ assert_eq!(actual, Some(expected_specifier));
+ }
+
+ #[test]
+ fn resolve_import_map_none() {
+ let config_text = r#"{}"#;
+ 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, None);
+ }
+
+ #[test]
+ fn resolve_import_map_no_config() {
+ let actual = resolve_import_map_specifier(None, None);
+ assert!(actual.is_ok());
+ let actual = actual.unwrap();
+ assert_eq!(actual, None);
+ }
}