summaryrefslogtreecommitdiff
path: root/cli/util/import_map.rs
diff options
context:
space:
mode:
Diffstat (limited to 'cli/util/import_map.rs')
-rw-r--r--cli/util/import_map.rs388
1 files changed, 0 insertions, 388 deletions
diff --git a/cli/util/import_map.rs b/cli/util/import_map.rs
deleted file mode 100644
index a075443ba..000000000
--- a/cli/util/import_map.rs
+++ /dev/null
@@ -1,388 +0,0 @@
-// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
-
-use std::collections::HashSet;
-
-use deno_ast::ParsedSource;
-use deno_ast::SourceRange;
-use deno_ast::SourceTextInfo;
-use deno_core::serde_json;
-use deno_core::ModuleSpecifier;
-use deno_graph::DefaultModuleAnalyzer;
-use deno_graph::DependencyDescriptor;
-use deno_graph::DynamicTemplatePart;
-use deno_graph::TypeScriptReference;
-use deno_semver::jsr::JsrDepPackageReq;
-use deno_semver::jsr::JsrPackageReqReference;
-use deno_semver::npm::NpmPackageReqReference;
-
-use crate::resolver::MappedSpecifierResolver;
-
-pub fn deno_json_deps(
- config: &deno_config::ConfigFile,
-) -> HashSet<JsrDepPackageReq> {
- let values = imports_values(config.json.imports.as_ref())
- .into_iter()
- .chain(scope_values(config.json.scopes.as_ref()));
- values_to_set(values)
-}
-
-fn imports_values(value: Option<&serde_json::Value>) -> Vec<&String> {
- let Some(obj) = value.and_then(|v| v.as_object()) else {
- return Vec::new();
- };
- let mut items = Vec::with_capacity(obj.len());
- for value in obj.values() {
- if let serde_json::Value::String(value) = value {
- items.push(value);
- }
- }
- items
-}
-
-fn scope_values(value: Option<&serde_json::Value>) -> Vec<&String> {
- let Some(obj) = value.and_then(|v| v.as_object()) else {
- return Vec::new();
- };
- obj.values().flat_map(|v| imports_values(Some(v))).collect()
-}
-
-fn values_to_set<'a>(
- values: impl Iterator<Item = &'a String>,
-) -> HashSet<JsrDepPackageReq> {
- let mut entries = HashSet::new();
- for value in values {
- if let Ok(req_ref) = JsrPackageReqReference::from_str(value) {
- entries.insert(JsrDepPackageReq::jsr(req_ref.into_inner().req));
- } else if let Ok(req_ref) = NpmPackageReqReference::from_str(value) {
- entries.insert(JsrDepPackageReq::npm(req_ref.into_inner().req));
- }
- }
- entries
-}
-
-#[derive(Debug, Clone)]
-pub enum ImportMapUnfurlDiagnostic {
- UnanalyzableDynamicImport {
- specifier: ModuleSpecifier,
- text_info: SourceTextInfo,
- range: SourceRange,
- },
-}
-
-impl ImportMapUnfurlDiagnostic {
- pub fn code(&self) -> &'static str {
- match self {
- Self::UnanalyzableDynamicImport { .. } => "unanalyzable-dynamic-import",
- }
- }
-
- pub fn message(&self) -> &'static str {
- match self {
- Self::UnanalyzableDynamicImport { .. } => {
- "unable to analyze dynamic import"
- }
- }
- }
-}
-
-pub struct ImportMapUnfurler<'a> {
- import_map: &'a MappedSpecifierResolver,
-}
-
-impl<'a> ImportMapUnfurler<'a> {
- pub fn new(import_map: &'a MappedSpecifierResolver) -> Self {
- Self { import_map }
- }
-
- pub fn unfurl(
- &self,
- url: &ModuleSpecifier,
- parsed_source: &ParsedSource,
- diagnostic_reporter: &mut dyn FnMut(ImportMapUnfurlDiagnostic),
- ) -> String {
- let mut text_changes = Vec::new();
- let module_info = DefaultModuleAnalyzer::module_info(parsed_source);
- let analyze_specifier =
- |specifier: &str,
- range: &deno_graph::PositionRange,
- text_changes: &mut Vec<deno_ast::TextChange>| {
- let resolved = self.import_map.resolve(specifier, url);
- if let Ok(resolved) = resolved {
- if let Some(resolved) = resolved.into_specifier() {
- text_changes.push(deno_ast::TextChange {
- range: to_range(parsed_source, range),
- new_text: make_relative_to(url, &resolved),
- });
- }
- }
- };
- for dep in &module_info.dependencies {
- match dep {
- DependencyDescriptor::Static(dep) => {
- analyze_specifier(
- &dep.specifier,
- &dep.specifier_range,
- &mut text_changes,
- );
- }
- DependencyDescriptor::Dynamic(dep) => {
- let success = try_unfurl_dynamic_dep(
- self.import_map,
- url,
- parsed_source,
- dep,
- &mut text_changes,
- );
-
- if !success {
- let start_pos = parsed_source
- .text_info()
- .line_start(dep.argument_range.start.line)
- + dep.argument_range.start.character;
- let end_pos = parsed_source
- .text_info()
- .line_start(dep.argument_range.end.line)
- + dep.argument_range.end.character;
- diagnostic_reporter(
- ImportMapUnfurlDiagnostic::UnanalyzableDynamicImport {
- specifier: url.to_owned(),
- range: SourceRange::new(start_pos, end_pos),
- text_info: parsed_source.text_info().clone(),
- },
- );
- }
- }
- }
- }
- for ts_ref in &module_info.ts_references {
- let specifier_with_range = match ts_ref {
- TypeScriptReference::Path(range) => range,
- TypeScriptReference::Types(range) => range,
- };
- analyze_specifier(
- &specifier_with_range.text,
- &specifier_with_range.range,
- &mut text_changes,
- );
- }
- for specifier_with_range in &module_info.jsdoc_imports {
- analyze_specifier(
- &specifier_with_range.text,
- &specifier_with_range.range,
- &mut text_changes,
- );
- }
- if let Some(specifier_with_range) = &module_info.jsx_import_source {
- analyze_specifier(
- &specifier_with_range.text,
- &specifier_with_range.range,
- &mut text_changes,
- );
- }
-
- let rewritten_text = deno_ast::apply_text_changes(
- parsed_source.text_info().text_str(),
- text_changes,
- );
- rewritten_text
- }
-}
-
-fn make_relative_to(from: &ModuleSpecifier, to: &ModuleSpecifier) -> String {
- if to.scheme() == "file" {
- format!("./{}", from.make_relative(to).unwrap())
- } else {
- to.to_string()
- }
-}
-
-/// Attempts to unfurl the dynamic dependency returning `true` on success
-/// or `false` when the import was not analyzable.
-fn try_unfurl_dynamic_dep(
- mapped_resolver: &MappedSpecifierResolver,
- module_url: &lsp_types::Url,
- parsed_source: &ParsedSource,
- dep: &deno_graph::DynamicDependencyDescriptor,
- text_changes: &mut Vec<deno_ast::TextChange>,
-) -> bool {
- match &dep.argument {
- deno_graph::DynamicArgument::String(value) => {
- let range = to_range(parsed_source, &dep.argument_range);
- let maybe_relative_index =
- parsed_source.text_info().text_str()[range.start..].find(value);
- let Some(relative_index) = maybe_relative_index else {
- return false;
- };
- let resolved = mapped_resolver.resolve(value, module_url);
- let Ok(resolved) = resolved else {
- return false;
- };
- let Some(resolved) = resolved.into_specifier() else {
- return false;
- };
- let start = range.start + relative_index;
- text_changes.push(deno_ast::TextChange {
- range: start..start + value.len(),
- new_text: make_relative_to(module_url, &resolved),
- });
- true
- }
- deno_graph::DynamicArgument::Template(parts) => match parts.first() {
- Some(DynamicTemplatePart::String { value }) => {
- // relative doesn't need to be modified
- let is_relative = value.starts_with("./") || value.starts_with("../");
- if is_relative {
- return true;
- }
- if !value.ends_with('/') {
- return false;
- }
- let Ok(resolved) = mapped_resolver.resolve(value, module_url) else {
- return false;
- };
- let Some(resolved) = resolved.into_specifier() else {
- return false;
- };
- let range = to_range(parsed_source, &dep.argument_range);
- let maybe_relative_index =
- parsed_source.text_info().text_str()[range.start..].find(value);
- let Some(relative_index) = maybe_relative_index else {
- return false;
- };
- let start = range.start + relative_index;
- text_changes.push(deno_ast::TextChange {
- range: start..start + value.len(),
- new_text: make_relative_to(module_url, &resolved),
- });
- true
- }
- Some(DynamicTemplatePart::Expr) => {
- false // failed analyzing
- }
- None => {
- true // ignore
- }
- },
- deno_graph::DynamicArgument::Expr => {
- false // failed analyzing
- }
- }
-}
-
-fn to_range(
- parsed_source: &ParsedSource,
- range: &deno_graph::PositionRange,
-) -> std::ops::Range<usize> {
- let mut range = range
- .as_source_range(parsed_source.text_info())
- .as_byte_range(parsed_source.text_info().range().start);
- let text = &parsed_source.text_info().text_str()[range.clone()];
- if text.starts_with('"') || text.starts_with('\'') {
- range.start += 1;
- }
- if text.ends_with('"') || text.ends_with('\'') {
- range.end -= 1;
- }
- range
-}
-
-#[cfg(test)]
-mod tests {
- use std::sync::Arc;
-
- use crate::args::PackageJsonDepsProvider;
-
- use super::*;
- use deno_ast::MediaType;
- use deno_ast::ModuleSpecifier;
- use deno_core::serde_json::json;
- use deno_core::url::Url;
- use import_map::ImportMapWithDiagnostics;
- use pretty_assertions::assert_eq;
-
- fn parse_ast(specifier: &Url, source_code: &str) -> ParsedSource {
- let media_type = MediaType::from_specifier(specifier);
- deno_ast::parse_module(deno_ast::ParseParams {
- specifier: specifier.clone(),
- media_type,
- capture_tokens: false,
- maybe_syntax: None,
- scope_analysis: false,
- text_info: deno_ast::SourceTextInfo::new(source_code.into()),
- })
- .unwrap()
- }
-
- #[test]
- fn test_unfurling() {
- let deno_json_url =
- ModuleSpecifier::parse("file:///dev/deno.json").unwrap();
- let value = json!({
- "imports": {
- "express": "npm:express@5",
- "lib/": "./lib/",
- "fizz": "./fizz/mod.ts"
- }
- });
- let ImportMapWithDiagnostics { import_map, .. } =
- 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)),
- );
- let unfurler = ImportMapUnfurler::new(&mapped_resolved);
-
- // Unfurling TS file should apply changes.
- {
- let source_code = r#"import express from "express";"
-import foo from "lib/foo.ts";
-import bar from "lib/bar.ts";
-import fizz from "fizz";
-
-const test1 = await import("lib/foo.ts");
-const test2 = await import(`lib/foo.ts`);
-const test3 = await import(`lib/${expr}`);
-const test4 = await import(`./lib/${expr}`);
-// will warn
-const test5 = await import(`lib${expr}`);
-const test6 = await import(`${expr}`);
-"#;
- let specifier = ModuleSpecifier::parse("file:///dev/mod.ts").unwrap();
- let source = parse_ast(&specifier, source_code);
- let mut d = Vec::new();
- let mut reporter = |diagnostic| d.push(diagnostic);
- let unfurled_source = unfurler.unfurl(&specifier, &source, &mut reporter);
- assert_eq!(d.len(), 2);
- assert!(
- matches!(
- d[0],
- ImportMapUnfurlDiagnostic::UnanalyzableDynamicImport { .. }
- ),
- "{:?}",
- d[0]
- );
- assert!(
- matches!(
- d[1],
- ImportMapUnfurlDiagnostic::UnanalyzableDynamicImport { .. }
- ),
- "{:?}",
- d[1]
- );
- let expected_source = r#"import express from "npm:express@5";"
-import foo from "./lib/foo.ts";
-import bar from "./lib/bar.ts";
-import fizz from "./fizz/mod.ts";
-
-const test1 = await import("./lib/foo.ts");
-const test2 = await import(`./lib/foo.ts`);
-const test3 = await import(`./lib/${expr}`);
-const test4 = await import(`./lib/${expr}`);
-// will warn
-const test5 = await import(`lib${expr}`);
-const test6 = await import(`${expr}`);
-"#;
- assert_eq!(unfurled_source, expected_source);
- }
- }
-}