diff options
Diffstat (limited to 'cli/util/import_map.rs')
| -rw-r--r-- | cli/util/import_map.rs | 142 |
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); } |
