deno/cli/args/import_map.rs
Bartek Iwańczuk 35220f0069
fix(workspace): provide workspace members as 'imports' in import map (#23492)
This commit changes the workspace support to provide all workspace
members to be available as imports based on their names and versions.

Closes https://github.com/denoland/deno/issues/23343
2024-04-23 17:21:06 +02:00

128 lines
3.6 KiB
Rust

// 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;
use deno_runtime::permissions::PermissionsContainer;
use import_map::ImportMap;
use import_map::ImportMapDiagnostic;
use log::warn;
use super::ConfigFile;
use crate::file_fetcher::FileFetcher;
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()?;
serde_json::from_str(&data_url_text)?
} else {
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,
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")
);
}
}
pub fn enhance_import_map_value_with_workspace_members(
mut import_map_value: serde_json::Value,
workspace_members: &[deno_config::WorkspaceMemberConfig],
) -> serde_json::Value {
let mut imports =
if let Some(imports) = import_map_value.get("imports").as_ref() {
imports.as_object().unwrap().clone()
} else {
serde_json::Map::new()
};
for workspace_member in workspace_members {
let name = &workspace_member.package_name;
let version = &workspace_member.package_version;
// Don't override existings, explicit imports
if imports.contains_key(name) {
continue;
}
imports.insert(
name.to_string(),
serde_json::Value::String(format!("jsr:{}@^{}", name, version)),
);
}
import_map_value["imports"] = serde_json::Value::Object(imports);
::import_map::ext::expand_import_map_value(import_map_value)
}