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.rs142
1 files changed, 133 insertions, 9 deletions
diff --git a/cli/util/import_map.rs b/cli/util/import_map.rs
index ac5ff24a4..b4689064d 100644
--- a/cli/util/import_map.rs
+++ b/cli/util/import_map.rs
@@ -4,10 +4,14 @@ use deno_ast::ParsedSource;
use deno_core::error::AnyError;
use deno_core::ModuleSpecifier;
use deno_graph::DefaultModuleAnalyzer;
+use deno_graph::DependencyDescriptor;
+use deno_graph::DynamicTemplatePart;
use deno_graph::MediaType;
use deno_graph::TypeScriptReference;
use import_map::ImportMap;
+use crate::graph_util::format_range_with_colors;
+
pub struct ImportMapUnfurler<'a> {
import_map: &'a ImportMap,
}
@@ -59,23 +63,49 @@ impl<'a> ImportMapUnfurler<'a> {
})?;
let mut text_changes = Vec::new();
let module_info = DefaultModuleAnalyzer::module_info(&parsed_source);
- let mut analyze_specifier =
- |specifier: &str, range: &deno_graph::PositionRange| {
+ 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 {
- let new_text = if resolved.scheme() == "file" {
- format!("./{}", url.make_relative(&resolved).unwrap())
- } else {
- resolved.to_string()
- };
text_changes.push(deno_ast::TextChange {
range: to_range(&parsed_source, range),
- new_text,
+ new_text: make_relative_to(url, &resolved),
});
}
};
for dep in &module_info.dependencies {
- analyze_specifier(&dep.specifier, &dep.specifier_range);
+ 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 {
+ log::warn!(
+ "{} Dynamic import was not analyzable and won't use the import map once published.\n at {}",
+ crate::colors::yellow("Warning"),
+ format_range_with_colors(&deno_graph::Range {
+ specifier: url.clone(),
+ start: dep.range.start.clone(),
+ end: dep.range.end.clone(),
+ })
+ );
+ }
+ }
+ }
}
for ts_ref in &module_info.ts_references {
let specifier_with_range = match ts_ref {
@@ -85,18 +115,21 @@ impl<'a> ImportMapUnfurler<'a> {
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,
);
}
Ok(
@@ -120,6 +153,81 @@ impl<'a> ImportMapUnfurler<'a> {
}
}
+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(
+ import_map: &ImportMap,
+ 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 = import_map.resolve(value, module_url);
+ let Ok(resolved) = resolved 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) = import_map.resolve(value, module_url) 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,
@@ -166,6 +274,14 @@ mod tests {
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 unfurled_source = unfurler
@@ -175,6 +291,14 @@ import fizz from "fizz";
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);
}