diff options
Diffstat (limited to 'cli/lsp/resolver.rs')
-rw-r--r-- | cli/lsp/resolver.rs | 254 |
1 files changed, 179 insertions, 75 deletions
diff --git a/cli/lsp/resolver.rs b/cli/lsp/resolver.rs index 9790dfed7..c4c66f114 100644 --- a/cli/lsp/resolver.rs +++ b/cli/lsp/resolver.rs @@ -7,6 +7,7 @@ use crate::graph_util::CliJsrUrlProvider; use crate::http_util::HttpClientProvider; use crate::lsp::config::Config; use crate::lsp::config::ConfigData; +use crate::lsp::logging::lsp_warn; use crate::npm::create_cli_npm_resolver_for_lsp; use crate::npm::CliNpmResolver; use crate::npm::CliNpmResolverByonmCreateOptions; @@ -54,17 +55,17 @@ use super::cache::LspCache; use super::jsr::JsrCacheResolver; #[derive(Debug, Clone)] -pub struct LspResolver { +struct LspScopeResolver { graph_resolver: Arc<CliGraphResolver>, jsr_resolver: Option<Arc<JsrCacheResolver>>, npm_resolver: Option<Arc<dyn CliNpmResolver>>, node_resolver: Option<Arc<CliNodeResolver>>, redirect_resolver: Option<Arc<RedirectResolver>>, graph_imports: Arc<IndexMap<ModuleSpecifier, GraphImport>>, - config: Arc<Config>, + config_data: Option<Arc<ConfigData>>, } -impl Default for LspResolver { +impl Default for LspScopeResolver { fn default() -> Self { Self { graph_resolver: create_graph_resolver(None, None, None), @@ -73,38 +74,41 @@ impl Default for LspResolver { node_resolver: None, redirect_resolver: None, graph_imports: Default::default(), - config: Default::default(), + config_data: None, } } } -impl LspResolver { - pub async fn from_config( +impl LspScopeResolver { + async fn from_config_data( + config_data: Option<&Arc<ConfigData>>, config: &Config, cache: &LspCache, http_client_provider: Option<&Arc<HttpClientProvider>>, ) -> Self { - let config_data = config.tree.root_data(); let mut npm_resolver = None; let mut node_resolver = None; - if let (Some(http_client), Some(config_data)) = - (http_client_provider, config_data) - { - npm_resolver = create_npm_resolver(config_data, cache, http_client).await; + if let Some(http_client) = http_client_provider { + npm_resolver = create_npm_resolver( + config_data.map(|d| d.as_ref()), + cache, + http_client, + ) + .await; node_resolver = create_node_resolver(npm_resolver.as_ref()); } let graph_resolver = create_graph_resolver( - config_data, + config_data.map(|d| d.as_ref()), npm_resolver.as_ref(), node_resolver.as_ref(), ); let jsr_resolver = Some(Arc::new(JsrCacheResolver::new( - cache.root_vendor_or_global(), - config_data, + cache.for_specifier(config_data.map(|d| &d.scope)), + config_data.map(|d| d.as_ref()), config, ))); let redirect_resolver = Some(Arc::new(RedirectResolver::new( - cache.root_vendor_or_global(), + cache.for_specifier(config_data.map(|d| &d.scope)), ))); let npm_graph_resolver = graph_resolver.create_graph_npm_resolver(); let graph_imports = config_data @@ -135,16 +139,16 @@ impl LspResolver { node_resolver, redirect_resolver, graph_imports, - config: Arc::new(config.clone()), + config_data: config_data.cloned(), } } - pub fn snapshot(&self) -> Arc<Self> { + fn snapshot(&self) -> Arc<Self> { let npm_resolver = self.npm_resolver.as_ref().map(|r| r.clone_snapshotted()); let node_resolver = create_node_resolver(npm_resolver.as_ref()); let graph_resolver = create_graph_resolver( - self.config.tree.root_data(), + self.config_data.as_deref(), npm_resolver.as_ref(), node_resolver.as_ref(), ); @@ -155,68 +159,133 @@ impl LspResolver { node_resolver, redirect_resolver: self.redirect_resolver.clone(), graph_imports: self.graph_imports.clone(), - config: self.config.clone(), + config_data: self.config_data.clone(), + }) + } +} + +#[derive(Debug, Default, Clone)] +pub struct LspResolver { + unscoped: Arc<LspScopeResolver>, + by_scope: BTreeMap<ModuleSpecifier, Arc<LspScopeResolver>>, +} + +impl LspResolver { + pub async fn from_config( + config: &Config, + cache: &LspCache, + http_client_provider: Option<&Arc<HttpClientProvider>>, + ) -> Self { + let mut by_scope = BTreeMap::new(); + for (scope, config_data) in config.tree.data_by_scope().as_ref() { + by_scope.insert( + scope.clone(), + Arc::new( + LspScopeResolver::from_config_data( + Some(config_data), + config, + cache, + http_client_provider, + ) + .await, + ), + ); + } + Self { + unscoped: Arc::new( + LspScopeResolver::from_config_data( + None, + config, + cache, + http_client_provider, + ) + .await, + ), + by_scope, + } + } + + pub fn snapshot(&self) -> Arc<Self> { + Arc::new(Self { + unscoped: self.unscoped.snapshot(), + by_scope: self + .by_scope + .iter() + .map(|(s, r)| (s.clone(), r.snapshot())) + .collect(), }) } pub fn did_cache(&self) { - self.jsr_resolver.as_ref().inspect(|r| r.did_cache()); + for resolver in + std::iter::once(&self.unscoped).chain(self.by_scope.values()) + { + resolver.jsr_resolver.as_ref().inspect(|r| r.did_cache()); + } } pub async fn set_npm_reqs( &self, reqs: &BTreeMap<Option<ModuleSpecifier>, BTreeSet<PackageReq>>, - ) -> Result<(), AnyError> { - let reqs = reqs - .values() - .flatten() - .collect::<BTreeSet<_>>() + ) { + for (scope, resolver) in [(None, &self.unscoped)] .into_iter() - .cloned() - .collect::<Vec<_>>(); - if let Some(npm_resolver) = self.npm_resolver.as_ref() { - if let Some(npm_resolver) = npm_resolver.as_managed() { - return npm_resolver.set_package_reqs(&reqs).await; + .chain(self.by_scope.iter().map(|(s, r)| (Some(s), r))) + { + if let Some(npm_resolver) = resolver.npm_resolver.as_ref() { + if let Some(npm_resolver) = npm_resolver.as_managed() { + let reqs = reqs + .get(&scope.cloned()) + .map(|reqs| reqs.iter().cloned().collect::<Vec<_>>()) + .unwrap_or_default(); + if let Err(err) = npm_resolver.set_package_reqs(&reqs).await { + lsp_warn!("Could not set npm package requirements: {:#}", err); + } + } } } - Ok(()) } pub fn as_graph_resolver( &self, - _file_referrer: Option<&ModuleSpecifier>, + file_referrer: Option<&ModuleSpecifier>, ) -> &dyn Resolver { - self.graph_resolver.as_ref() + let resolver = self.get_scope_resolver(file_referrer); + resolver.graph_resolver.as_ref() } pub fn create_graph_npm_resolver( &self, - _file_referrer: Option<&ModuleSpecifier>, + file_referrer: Option<&ModuleSpecifier>, ) -> WorkerCliNpmGraphResolver { - self.graph_resolver.create_graph_npm_resolver() + let resolver = self.get_scope_resolver(file_referrer); + resolver.graph_resolver.create_graph_npm_resolver() } pub fn maybe_managed_npm_resolver( &self, - _file_referrer: Option<&ModuleSpecifier>, + file_referrer: Option<&ModuleSpecifier>, ) -> Option<&ManagedCliNpmResolver> { - self.npm_resolver.as_ref().and_then(|r| r.as_managed()) + let resolver = self.get_scope_resolver(file_referrer); + resolver.npm_resolver.as_ref().and_then(|r| r.as_managed()) } pub fn graph_imports_by_referrer( &self, ) -> IndexMap<&ModuleSpecifier, Vec<&ModuleSpecifier>> { self - .graph_imports + .by_scope .iter() - .map(|(s, i)| { - ( - s, - i.dependencies - .values() - .flat_map(|d| d.get_type().or_else(|| d.get_code())) - .collect(), - ) + .flat_map(|(_, r)| { + r.graph_imports.iter().map(|(s, i)| { + ( + s, + i.dependencies + .values() + .flat_map(|d| d.get_type().or_else(|| d.get_code())) + .collect(), + ) + }) }) .collect() } @@ -224,35 +293,42 @@ impl LspResolver { pub fn jsr_to_resource_url( &self, req_ref: &JsrPackageReqReference, - _file_referrer: Option<&ModuleSpecifier>, + file_referrer: Option<&ModuleSpecifier>, ) -> Option<ModuleSpecifier> { - self.jsr_resolver.as_ref()?.jsr_to_resource_url(req_ref) + let resolver = self.get_scope_resolver(file_referrer); + resolver.jsr_resolver.as_ref()?.jsr_to_resource_url(req_ref) } pub fn jsr_lookup_export_for_path( &self, nv: &PackageNv, path: &str, - _file_referrer: Option<&ModuleSpecifier>, + file_referrer: Option<&ModuleSpecifier>, ) -> Option<String> { - self.jsr_resolver.as_ref()?.lookup_export_for_path(nv, path) + let resolver = self.get_scope_resolver(file_referrer); + resolver + .jsr_resolver + .as_ref()? + .lookup_export_for_path(nv, path) } pub fn jsr_lookup_req_for_nv( &self, nv: &PackageNv, - _file_referrer: Option<&ModuleSpecifier>, + file_referrer: Option<&ModuleSpecifier>, ) -> Option<PackageReq> { - self.jsr_resolver.as_ref()?.lookup_req_for_nv(nv) + let resolver = self.get_scope_resolver(file_referrer); + resolver.jsr_resolver.as_ref()?.lookup_req_for_nv(nv) } pub fn npm_to_file_url( &self, req_ref: &NpmPackageReqReference, referrer: &ModuleSpecifier, - _file_referrer: Option<&ModuleSpecifier>, + file_referrer: Option<&ModuleSpecifier>, ) -> Option<(ModuleSpecifier, MediaType)> { - let node_resolver = self.node_resolver.as_ref()?; + let resolver = self.get_scope_resolver(file_referrer); + let node_resolver = resolver.node_resolver.as_ref()?; Some(NodeResolution::into_specifier_and_media_type( node_resolver .resolve_req_reference(req_ref, referrer, NodeResolutionMode::Types) @@ -261,7 +337,8 @@ impl LspResolver { } pub fn in_node_modules(&self, specifier: &ModuleSpecifier) -> bool { - if let Some(npm_resolver) = &self.npm_resolver { + let resolver = self.get_scope_resolver(Some(specifier)); + if let Some(npm_resolver) = &resolver.npm_resolver { return npm_resolver.in_npm_package(specifier); } false @@ -271,7 +348,8 @@ impl LspResolver { &self, specifier: &ModuleSpecifier, ) -> Option<MediaType> { - let node_resolver = self.node_resolver.as_ref()?; + let resolver = self.get_scope_resolver(Some(specifier)); + let node_resolver = resolver.node_resolver.as_ref()?; let resolution = node_resolver .url_to_node_resolution(specifier.clone()) .ok()?; @@ -282,7 +360,8 @@ impl LspResolver { &self, referrer: &ModuleSpecifier, ) -> Result<Option<Rc<PackageJson>>, AnyError> { - let Some(node_resolver) = self.node_resolver.as_ref() else { + let resolver = self.get_scope_resolver(Some(referrer)); + let Some(node_resolver) = resolver.node_resolver.as_ref() else { return Ok(None); }; node_resolver.get_closest_package_json( @@ -294,9 +373,10 @@ impl LspResolver { pub fn resolve_redirects( &self, specifier: &ModuleSpecifier, - _file_referrer: Option<&ModuleSpecifier>, + file_referrer: Option<&ModuleSpecifier>, ) -> Option<ModuleSpecifier> { - let Some(redirect_resolver) = self.redirect_resolver.as_ref() else { + let resolver = self.get_scope_resolver(file_referrer); + let Some(redirect_resolver) = resolver.redirect_resolver.as_ref() else { return Some(specifier.clone()); }; redirect_resolver.resolve(specifier) @@ -305,9 +385,10 @@ impl LspResolver { pub fn redirect_chain_headers( &self, specifier: &ModuleSpecifier, - _file_referrer: Option<&ModuleSpecifier>, + file_referrer: Option<&ModuleSpecifier>, ) -> Vec<(ModuleSpecifier, Arc<HashMap<String, String>>)> { - let Some(redirect_resolver) = self.redirect_resolver.as_ref() else { + let resolver = self.get_scope_resolver(file_referrer); + let Some(redirect_resolver) = resolver.redirect_resolver.as_ref() else { return vec![]; }; redirect_resolver @@ -316,26 +397,47 @@ impl LspResolver { .map(|(s, e)| (s, e.headers.clone())) .collect() } + + fn get_scope_resolver( + &self, + file_referrer: Option<&ModuleSpecifier>, + ) -> &LspScopeResolver { + let Some(file_referrer) = file_referrer else { + return self.unscoped.as_ref(); + }; + self + .by_scope + .iter() + .rfind(|(s, _)| file_referrer.as_str().starts_with(s.as_str())) + .map(|(_, r)| r.as_ref()) + .unwrap_or(self.unscoped.as_ref()) + } } async fn create_npm_resolver( - config_data: &ConfigData, + config_data: Option<&ConfigData>, cache: &LspCache, http_client_provider: &Arc<HttpClientProvider>, ) -> Option<Arc<dyn CliNpmResolver>> { - let node_modules_dir = config_data - .node_modules_dir - .clone() - .or_else(|| specifier_to_file_path(&config_data.scope).ok())?; - let options = if config_data.byonm { + let mut byonm_dir = None; + if let Some(config_data) = config_data { + if config_data.byonm { + byonm_dir = Some(config_data.node_modules_dir.clone().or_else(|| { + specifier_to_file_path(&config_data.scope) + .ok() + .map(|p| p.join("node_modules/")) + })?) + } + } + let options = if let Some(byonm_dir) = byonm_dir { CliNpmResolverCreateOptions::Byonm(CliNpmResolverByonmCreateOptions { fs: Arc::new(deno_fs::RealFs), - root_node_modules_dir: node_modules_dir.clone(), + root_node_modules_dir: byonm_dir, }) } else { CliNpmResolverCreateOptions::Managed(CliNpmResolverManagedCreateOptions { http_client_provider: http_client_provider.clone(), - snapshot: match config_data.lockfile.as_ref() { + snapshot: match config_data.and_then(|d| d.lockfile.as_ref()) { Some(lockfile) => { CliNpmResolverManagedSnapshotOption::ResolveFromLockfile( lockfile.clone(), @@ -354,15 +456,17 @@ async fn create_npm_resolver( // the user is typing. cache_setting: CacheSetting::Only, text_only_progress_bar: ProgressBar::new(ProgressBarStyle::TextOnly), - maybe_node_modules_path: config_data.node_modules_dir.clone(), + maybe_node_modules_path: config_data + .and_then(|d| d.node_modules_dir.clone()), package_json_deps_provider: Arc::new(PackageJsonDepsProvider::new( - config_data.package_json.as_ref().map(|package_json| { - package_json::get_local_package_json_version_reqs(package_json) - }), + config_data + .and_then(|d| d.package_json.as_ref()) + .map(|package_json| { + package_json::get_local_package_json_version_reqs(package_json) + }), )), npmrc: config_data - .npmrc - .clone() + .and_then(|d| d.npmrc.clone()) .unwrap_or_else(create_default_npmrc), npm_system_info: NpmSystemInfo::default(), }) |