summaryrefslogtreecommitdiff
path: root/cli/lsp/analysis.rs
diff options
context:
space:
mode:
authorDavid Sherret <dsherret@users.noreply.github.com>2024-01-21 17:19:10 -0500
committerGitHub <noreply@github.com>2024-01-21 17:19:10 -0500
commitfbfeedb68b593d0dbf3f0bfb0061939756da20b7 (patch)
treedc58c302dcb1ac9cdc11c0cdd14fea39b6d960d1 /cli/lsp/analysis.rs
parente58462dbb9b8b611628c5d8da7e523e48a58242c (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.rs49
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 {