diff options
author | David Sherret <dsherret@users.noreply.github.com> | 2024-01-21 17:19:10 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-01-21 17:19:10 -0500 |
commit | fbfeedb68b593d0dbf3f0bfb0061939756da20b7 (patch) | |
tree | dc58c302dcb1ac9cdc11c0cdd14fea39b6d960d1 /cli/lsp/analysis.rs | |
parent | e58462dbb9b8b611628c5d8da7e523e48a58242c (diff) |
fix(lsp): improved npm specifier to import map entry mapping (#22016)
Upgrades to the latest deno_semver
Diffstat (limited to 'cli/lsp/analysis.rs')
-rw-r--r-- | cli/lsp/analysis.rs | 49 |
1 files changed, 39 insertions, 10 deletions
diff --git a/cli/lsp/analysis.rs b/cli/lsp/analysis.rs index 5301fbeea..6c6d7cab4 100644 --- a/cli/lsp/analysis.rs +++ b/cli/lsp/analysis.rs @@ -25,12 +25,14 @@ use deno_runtime::deno_node::NodeResolver; use deno_runtime::deno_node::NpmResolver; use deno_runtime::deno_node::PathClean; use deno_runtime::permissions::PermissionsContainer; +use deno_semver::npm::NpmPackageReqReference; use deno_semver::package::PackageReq; use import_map::ImportMap; use once_cell::sync::Lazy; use regex::Regex; use std::cmp::Ordering; use std::collections::HashMap; +use std::collections::HashSet; use std::path::Path; use tower_lsp::lsp_types as lsp; use tower_lsp::lsp_types::Position; @@ -211,20 +213,31 @@ impl<'a> TsResponseImportMapper<'a> { if !pkg_reqs.is_empty() { let sub_path = self.resolve_package_path(specifier); if let Some(import_map) = self.maybe_import_map { - for pkg_req in &pkg_reqs { - let paths = vec![ - concat_npm_specifier("npm:", pkg_req, sub_path.as_deref()), - concat_npm_specifier("npm:/", pkg_req, sub_path.as_deref()), - ]; - for path in paths { - if let Some(mapped_path) = ModuleSpecifier::parse(&path) - .ok() - .and_then(|s| import_map.lookup(&s, referrer)) + let pkg_reqs = pkg_reqs.iter().collect::<HashSet<_>>(); + let mut matches = Vec::new(); + for entry in import_map.entries_for_referrer(referrer) { + if let Some(value) = entry.raw_value { + if let Ok(package_ref) = + NpmPackageReqReference::from_str(value) { - return Some(mapped_path); + if pkg_reqs.contains(package_ref.req()) { + let sub_path = sub_path.as_deref().unwrap_or(""); + let value_sub_path = package_ref.sub_path().unwrap_or(""); + if let Some(key_sub_path) = + sub_path.strip_prefix(value_sub_path) + { + matches + .push(format!("{}{}", entry.raw_key, key_sub_path)); + } + } } } } + // select the shortest match + matches.sort_by_key(|a| a.len()); + if let Some(matched) = matches.first() { + return Some(matched.to_string()); + } } // if not found in the import map, return the first pkg req @@ -267,6 +280,22 @@ impl<'a> TsResponseImportMapper<'a> { // a search for the .d.ts file instead if specifier_path.extension().and_then(|e| e.to_str()) == Some("js") { search_paths.insert(0, specifier_path.with_extension("d.ts")); + } else if let Some(file_name) = + specifier_path.file_name().and_then(|f| f.to_str()) + { + // In some other cases, typescript will provide the .d.ts extension, but the + // export might not have a .d.ts defined. In that case, look for the corresponding + // JavaScript file after not being able to find the .d.ts file. + if let Some(file_stem) = file_name.strip_suffix(".d.ts") { + search_paths + .push(specifier_path.with_file_name(format!("{}.js", file_stem))); + } else if let Some(file_stem) = file_name.strip_suffix(".d.cts") { + search_paths + .push(specifier_path.with_file_name(format!("{}.cjs", file_stem))); + } else if let Some(file_stem) = file_name.strip_suffix(".d.mts") { + search_paths + .push(specifier_path.with_file_name(format!("{}.mjs", file_stem))); + } } for search_path in search_paths { |