diff options
Diffstat (limited to 'cli/lsp/documents.rs')
-rw-r--r-- | cli/lsp/documents.rs | 178 |
1 files changed, 124 insertions, 54 deletions
diff --git a/cli/lsp/documents.rs b/cli/lsp/documents.rs index 61db899e5..57216eecb 100644 --- a/cli/lsp/documents.rs +++ b/cli/lsp/documents.rs @@ -12,6 +12,13 @@ use crate::file_fetcher::SUPPORTED_SCHEMES; use crate::fs_util::specifier_to_file_path; use crate::http_cache; use crate::http_cache::HttpCache; +use crate::node; +use crate::node::node_resolve_npm_reference; +use crate::node::NodeResolution; +use crate::node::NodeResolutionMode; +use crate::npm::NpmPackageReference; +use crate::npm::NpmPackageReq; +use crate::npm::NpmPackageResolver; use crate::resolver::ImportMapResolver; use crate::resolver::JsxResolver; use crate::text_encoding; @@ -209,6 +216,29 @@ impl AssetOrDocument { } } +#[derive(Debug, Default)] +struct DocumentDependencies { + deps: BTreeMap<String, deno_graph::Dependency>, + maybe_types_dependency: Option<(String, Resolved)>, +} + +impl DocumentDependencies { + pub fn from_maybe_module(maybe_module: &MaybeModuleResult) -> Self { + if let Some(Ok(module)) = &maybe_module { + Self::from_module(module) + } else { + Self::default() + } + } + + pub fn from_module(module: &deno_graph::Module) -> Self { + Self { + deps: module.dependencies.clone(), + maybe_types_dependency: module.maybe_types_dependency.clone(), + } + } +} + type MaybeModuleResult = Option<Result<deno_graph::Module, deno_graph::ModuleGraphError>>; type MaybeParsedSourceResult = @@ -217,7 +247,7 @@ type MaybeParsedSourceResult = #[derive(Debug, Clone)] struct DocumentInner { /// contains the last-known-good set of dependencies from parsing the module - dependencies: Arc<BTreeMap<String, deno_graph::Dependency>>, + dependencies: Arc<DocumentDependencies>, fs_version: String, line_index: Arc<LineIndex>, maybe_language_id: Option<LanguageId>, @@ -249,12 +279,9 @@ impl Document { maybe_headers, maybe_resolver, ); - let dependencies = if let Some(Ok(module)) = &maybe_module { - Arc::new(module.dependencies.clone()) - } else { - Arc::new(BTreeMap::new()) - }; - // todo(dsherret): retrieve this from the parsed source if it + let dependencies = + Arc::new(DocumentDependencies::from_maybe_module(&maybe_module)); + // todo(dsherret): retrieve this from the parsed source if it exists let text_info = SourceTextInfo::new(content); let line_index = Arc::new(LineIndex::new(text_info.text_str())); Self(Arc::new(DocumentInner { @@ -289,11 +316,8 @@ impl Document { } else { (None, None) }; - let dependencies = if let Some(Ok(module)) = &maybe_module { - Arc::new(module.dependencies.clone()) - } else { - Arc::new(BTreeMap::new()) - }; + let dependencies = + Arc::new(DocumentDependencies::from_maybe_module(&maybe_module)); let source = SourceTextInfo::new(content); let line_index = Arc::new(LineIndex::new(source.text_str())); Self(Arc::new(DocumentInner { @@ -355,9 +379,9 @@ impl Document { (None, None) }; let dependencies = if let Some(Ok(module)) = &maybe_module { - Arc::new(module.dependencies.clone()) + Arc::new(DocumentDependencies::from_module(module)) } else { - self.0.dependencies.clone() + self.0.dependencies.clone() // use the last known good }; let text_info = SourceTextInfo::new(content); let line_index = if index_valid == IndexValid::All { @@ -435,15 +459,9 @@ impl Document { } pub fn maybe_types_dependency(&self) -> deno_graph::Resolved { - let module_result = match self.0.maybe_module.as_ref() { - Some(module_result) => module_result, - _ => return deno_graph::Resolved::None, - }; - let module = match module_result.as_ref() { - Ok(module) => module, - Err(_) => return deno_graph::Resolved::None, - }; - if let Some((_, maybe_dep)) = module.maybe_types_dependency.as_ref() { + if let Some((_, maybe_dep)) = + self.0.dependencies.maybe_types_dependency.as_ref() + { maybe_dep.clone() } else { deno_graph::Resolved::None @@ -479,13 +497,8 @@ impl Document { self.0.maybe_navigation_tree.clone() } - pub fn dependencies(&self) -> Vec<(String, deno_graph::Dependency)> { - self - .0 - .dependencies - .iter() - .map(|(s, d)| (s.clone(), d.clone())) - .collect() + pub fn dependencies(&self) -> &BTreeMap<String, deno_graph::Dependency> { + &self.0.dependencies.deps } /// If the supplied position is within a dependency range, return the resolved @@ -698,6 +711,8 @@ pub struct Documents { maybe_import_map: Option<ImportMapResolver>, /// The optional JSX resolver, which is used when JSX imports are configured. maybe_jsx_resolver: Option<JsxResolver>, + /// The npm package requirements. + npm_reqs: HashSet<NpmPackageReq>, /// Resolves a specifier to its final redirected to specifier. specifier_resolver: Arc<SpecifierResolver>, } @@ -713,6 +728,7 @@ impl Documents { imports: Default::default(), maybe_import_map: None, maybe_jsx_resolver: None, + npm_reqs: HashSet::new(), specifier_resolver: Arc::new(SpecifierResolver::new(location)), } } @@ -847,6 +863,12 @@ impl Documents { } } + /// Returns a collection of npm package requirements. + pub fn npm_package_reqs(&mut self) -> HashSet<NpmPackageReq> { + self.calculate_dependents_if_dirty(); + self.npm_reqs.clone() + } + /// Return a document for the specifier. pub fn get(&self, original_specifier: &ModuleSpecifier) -> Option<Document> { let specifier = self.specifier_resolver.resolve(original_specifier)?; @@ -921,10 +943,28 @@ impl Documents { &self, specifiers: Vec<String>, referrer: &ModuleSpecifier, + maybe_npm_resolver: Option<&NpmPackageResolver>, ) -> Option<Vec<Option<(ModuleSpecifier, MediaType)>>> { let dependencies = self.get(referrer)?.0.dependencies.clone(); let mut results = Vec::new(); for specifier in specifiers { + if let Some(npm_resolver) = maybe_npm_resolver { + if npm_resolver.in_npm_package(referrer) { + // we're in an npm package, so use node resolution + results.push(Some(NodeResolution::into_specifier_and_media_type( + node::node_resolve( + &specifier, + referrer, + node::NodeResolutionMode::Types, + npm_resolver, + ) + .ok() + .flatten(), + ))); + continue; + } + } + // handle npm:<package> urls if specifier.starts_with("asset:") { if let Ok(specifier) = ModuleSpecifier::parse(&specifier) { let media_type = MediaType::from(&specifier); @@ -932,11 +972,11 @@ impl Documents { } else { results.push(None); } - } else if let Some(dep) = dependencies.get(&specifier) { + } else if let Some(dep) = dependencies.deps.get(&specifier) { if let Resolved::Ok { specifier, .. } = &dep.maybe_type { - results.push(self.resolve_dependency(specifier)); + results.push(self.resolve_dependency(specifier, maybe_npm_resolver)); } else if let Resolved::Ok { specifier, .. } = &dep.maybe_code { - results.push(self.resolve_dependency(specifier)); + results.push(self.resolve_dependency(specifier, maybe_npm_resolver)); } else { results.push(None); } @@ -945,7 +985,19 @@ impl Documents { { // clone here to avoid double borrow of self let specifier = specifier.clone(); - results.push(self.resolve_dependency(&specifier)); + results.push(self.resolve_dependency(&specifier, maybe_npm_resolver)); + } else if let Ok(npm_ref) = NpmPackageReference::from_str(&specifier) { + results.push(maybe_npm_resolver.map(|npm_resolver| { + NodeResolution::into_specifier_and_media_type( + node_resolve_npm_reference( + &npm_ref, + NodeResolutionMode::Types, + npm_resolver, + ) + .ok() + .flatten(), + ) + })); } else { results.push(None); } @@ -1038,32 +1090,36 @@ impl Documents { // favour documents that are open in case a document exists in both collections let documents = file_system_docs.docs.iter().chain(self.open_docs.iter()); for (specifier, doc) in documents { - if let Some(Ok(module)) = doc.maybe_module() { - for dependency in module.dependencies.values() { - if let Some(dep) = dependency.get_code() { - dependents_map - .entry(dep.clone()) - .or_default() - .insert(specifier.clone()); - } - if let Some(dep) = dependency.get_type() { - dependents_map - .entry(dep.clone()) - .or_default() - .insert(specifier.clone()); - } + for dependency in doc.dependencies().values() { + if let Some(dep) = dependency.get_code() { + dependents_map + .entry(dep.clone()) + .or_default() + .insert(specifier.clone()); } - if let Some((_, Resolved::Ok { specifier: dep, .. })) = - &module.maybe_types_dependency - { + if let Some(dep) = dependency.get_type() { dependents_map .entry(dep.clone()) .or_default() .insert(specifier.clone()); } } + if let Resolved::Ok { specifier: dep, .. } = doc.maybe_types_dependency() + { + dependents_map + .entry(dep.clone()) + .or_default() + .insert(specifier.clone()); + } + } + let mut npm_reqs = HashSet::new(); + for specifier in dependents_map.keys() { + if let Ok(reference) = NpmPackageReference::from_specifier(specifier) { + npm_reqs.insert(reference.req); + } } self.dependents_map = Arc::new(dependents_map); + self.npm_reqs = npm_reqs; self.dirty = false; file_system_docs.dirty = false; } @@ -1079,7 +1135,21 @@ impl Documents { fn resolve_dependency( &self, specifier: &ModuleSpecifier, + maybe_npm_resolver: Option<&NpmPackageResolver>, ) -> Option<(ModuleSpecifier, MediaType)> { + if let Ok(npm_ref) = NpmPackageReference::from_specifier(specifier) { + return maybe_npm_resolver.map(|npm_resolver| { + NodeResolution::into_specifier_and_media_type( + node_resolve_npm_reference( + &npm_ref, + NodeResolutionMode::Types, + npm_resolver, + ) + .ok() + .flatten(), + ) + }); + } let doc = self.get(specifier)?; let maybe_module = doc.maybe_module().and_then(|r| r.as_ref().ok()); let maybe_types_dependency = maybe_module.and_then(|m| { @@ -1088,7 +1158,7 @@ impl Documents { .map(|(_, resolved)| resolved.clone()) }); if let Some(Resolved::Ok { specifier, .. }) = maybe_types_dependency { - self.resolve_dependency(&specifier) + self.resolve_dependency(&specifier, maybe_npm_resolver) } else { let media_type = doc.media_type(); Some((specifier.clone(), media_type)) @@ -1113,12 +1183,12 @@ impl Documents { } /// Loader that will look at the open documents. -pub struct DocumentsDenoGraphLoader<'a> { +pub struct OpenDocumentsGraphLoader<'a> { pub inner_loader: &'a mut dyn deno_graph::source::Loader, pub open_docs: &'a HashMap<ModuleSpecifier, Document>, } -impl<'a> deno_graph::source::Loader for DocumentsDenoGraphLoader<'a> { +impl<'a> deno_graph::source::Loader for OpenDocumentsGraphLoader<'a> { fn load( &mut self, specifier: &ModuleSpecifier, |