diff options
author | David Sherret <dsherret@users.noreply.github.com> | 2024-11-14 15:24:25 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-11-14 15:24:25 -0500 |
commit | 617350e79c58b6e01984e3d7c7436d243d0e5cff (patch) | |
tree | dadb3c7675d1d49952a30c525bbc6ee3086a78e3 /cli/lsp | |
parent | de34c7ed29bcce8b46a65f5effe45090b8493ba5 (diff) |
refactor(resolver): move more resolution code into deno_resolver (#26873)
Follow-up to cjs refactor.
This moves most of the resolution code into the deno_resolver crate.
Still pending is the npm resolution code.
Diffstat (limited to 'cli/lsp')
-rw-r--r-- | cli/lsp/analysis.rs | 5 | ||||
-rw-r--r-- | cli/lsp/resolver.rs | 427 |
2 files changed, 243 insertions, 189 deletions
diff --git a/cli/lsp/analysis.rs b/cli/lsp/analysis.rs index 9f26de70c..044b1573b 100644 --- a/cli/lsp/analysis.rs +++ b/cli/lsp/analysis.rs @@ -344,9 +344,8 @@ impl<'a> TsResponseImportMapper<'a> { { let in_npm_pkg = self .resolver - .maybe_node_resolver(Some(&self.file_referrer)) - .map(|n| n.in_npm_package(specifier)) - .unwrap_or(false); + .in_npm_pkg_checker(Some(&self.file_referrer)) + .in_npm_package(specifier); if in_npm_pkg { if let Ok(Some(pkg_id)) = npm_resolver.resolve_pkg_id_from_specifier(specifier) diff --git a/cli/lsp/resolver.rs b/cli/lsp/resolver.rs index 37f63b912..399b89638 100644 --- a/cli/lsp/resolver.rs +++ b/cli/lsp/resolver.rs @@ -15,6 +15,9 @@ use deno_graph::Range; use deno_npm::NpmSystemInfo; use deno_path_util::url_from_directory_path; use deno_path_util::url_to_file_path; +use deno_resolver::npm::NpmReqResolverOptions; +use deno_resolver::DenoResolverOptions; +use deno_resolver::NodeAndNpmReqResolver; use deno_runtime::deno_fs; use deno_runtime::deno_node::NodeResolver; use deno_runtime::deno_node::PackageJson; @@ -43,6 +46,7 @@ use crate::args::CacheSetting; use crate::args::CliLockfile; use crate::args::NpmInstallDepsProvider; use crate::cache::DenoCacheEnvFsAdapter; +use crate::factory::Deferred; use crate::graph_util::CliJsrUrlProvider; use crate::http_util::HttpClientProvider; use crate::lsp::config::Config; @@ -57,8 +61,9 @@ use crate::npm::CliNpmResolverCreateOptions; use crate::npm::CliNpmResolverManagedSnapshotOption; use crate::npm::CreateInNpmPkgCheckerOptions; use crate::npm::ManagedCliNpmResolver; +use crate::resolver::CliDenoResolver; use crate::resolver::CliDenoResolverFs; -use crate::resolver::CliNodeResolver; +use crate::resolver::CliNpmReqResolver; use crate::resolver::CliResolver; use crate::resolver::CliResolverOptions; use crate::resolver::IsCjsResolver; @@ -71,10 +76,12 @@ use crate::util::progress_bar::ProgressBarStyle; #[derive(Debug, Clone)] struct LspScopeResolver { resolver: Arc<CliResolver>, + in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>, jsr_resolver: Option<Arc<JsrCacheResolver>>, npm_resolver: Option<Arc<dyn CliNpmResolver>>, - node_resolver: Option<Arc<CliNodeResolver>>, - pkg_json_resolver: Option<Arc<PackageJsonResolver>>, + node_resolver: Option<Arc<NodeResolver>>, + npm_pkg_req_resolver: Option<Arc<CliNpmReqResolver>>, + pkg_json_resolver: Arc<PackageJsonResolver>, redirect_resolver: Option<Arc<RedirectResolver>>, graph_imports: Arc<IndexMap<ModuleSpecifier, GraphImport>>, package_json_deps_by_resolution: Arc<IndexMap<ModuleSpecifier, String>>, @@ -83,12 +90,15 @@ struct LspScopeResolver { impl Default for LspScopeResolver { fn default() -> Self { + let factory = ResolverFactory::new(None); Self { - resolver: create_cli_resolver(None, None, None), + resolver: factory.cli_resolver().clone(), + in_npm_pkg_checker: factory.in_npm_pkg_checker().clone(), jsr_resolver: None, npm_resolver: None, node_resolver: None, - pkg_json_resolver: None, + npm_pkg_req_resolver: None, + pkg_json_resolver: factory.pkg_json_resolver().clone(), redirect_resolver: None, graph_imports: Default::default(), package_json_deps_by_resolution: Default::default(), @@ -103,35 +113,16 @@ impl LspScopeResolver { cache: &LspCache, http_client_provider: Option<&Arc<HttpClientProvider>>, ) -> Self { - let mut npm_resolver = None; - let mut node_resolver = None; - let fs = Arc::new(deno_fs::RealFs); - let pkg_json_resolver = Arc::new(PackageJsonResolver::new( - deno_runtime::deno_node::DenoFsNodeResolverEnv::new(fs.clone()), - )); - if let Some(http_client) = http_client_provider { - npm_resolver = create_npm_resolver( - config_data.map(|d| d.as_ref()), - cache, - http_client, - &pkg_json_resolver, - ) - .await; - if let Some(npm_resolver) = &npm_resolver { - let in_npm_pkg_checker = create_in_npm_pkg_checker(npm_resolver); - node_resolver = Some(create_node_resolver( - fs.clone(), - in_npm_pkg_checker, - npm_resolver, - pkg_json_resolver.clone(), - )); - } + let mut factory = ResolverFactory::new(config_data); + if let Some(http_client_provider) = http_client_provider { + factory.init_npm_resolver(http_client_provider, cache).await; } - let cli_resolver = create_cli_resolver( - config_data.map(|d| d.as_ref()), - npm_resolver.as_ref(), - node_resolver.as_ref(), - ); + let in_npm_pkg_checker = factory.in_npm_pkg_checker().clone(); + let npm_resolver = factory.npm_resolver().cloned(); + let node_resolver = factory.node_resolver().cloned(); + let npm_pkg_req_resolver = factory.npm_pkg_req_resolver().cloned(); + let cli_resolver = factory.cli_resolver().clone(); + let pkg_json_resolver = factory.pkg_json_resolver().clone(); let jsr_resolver = Some(Arc::new(JsrCacheResolver::new( cache.for_specifier(config_data.map(|d| d.scope.as_ref())), config_data.map(|d| d.as_ref()), @@ -171,7 +162,7 @@ impl LspScopeResolver { }) .unwrap_or_default(); let package_json_deps_by_resolution = (|| { - let node_resolver = node_resolver.as_ref()?; + let npm_pkg_req_resolver = npm_pkg_req_resolver.as_ref()?; let package_json = config_data?.maybe_pkg_json()?; let referrer = package_json.specifier(); let dependencies = package_json.dependencies.as_ref()?; @@ -181,7 +172,7 @@ impl LspScopeResolver { let req_ref = NpmPackageReqReference::from_str(&format!("npm:{name}")).ok()?; let specifier = into_specifier_and_media_type(Some( - node_resolver + npm_pkg_req_resolver .resolve_req_reference( &req_ref, &referrer, @@ -201,10 +192,12 @@ impl LspScopeResolver { Arc::new(package_json_deps_by_resolution.unwrap_or_default()); Self { resolver: cli_resolver, + in_npm_pkg_checker, jsr_resolver, + npm_pkg_req_resolver, npm_resolver, node_resolver, - pkg_json_resolver: Some(pkg_json_resolver), + pkg_json_resolver, redirect_resolver, graph_imports, package_json_deps_by_resolution, @@ -213,34 +206,21 @@ impl LspScopeResolver { } fn snapshot(&self) -> Arc<Self> { + let mut factory = ResolverFactory::new(self.config_data.as_ref()); let npm_resolver = self.npm_resolver.as_ref().map(|r| r.clone_snapshotted()); - let fs = Arc::new(deno_fs::RealFs); - let pkg_json_resolver = Arc::new(PackageJsonResolver::new( - deno_runtime::deno_node::DenoFsNodeResolverEnv::new(fs.clone()), - )); - let mut node_resolver = None; if let Some(npm_resolver) = &npm_resolver { - let in_npm_pkg_checker = create_in_npm_pkg_checker(npm_resolver); - node_resolver = Some(create_node_resolver( - fs, - in_npm_pkg_checker, - npm_resolver, - pkg_json_resolver.clone(), - )); + factory.set_npm_resolver(npm_resolver.clone()); } - let graph_resolver = create_cli_resolver( - self.config_data.as_deref(), - npm_resolver.as_ref(), - node_resolver.as_ref(), - ); Arc::new(Self { - resolver: graph_resolver, + resolver: factory.cli_resolver().clone(), + in_npm_pkg_checker: factory.in_npm_pkg_checker().clone(), jsr_resolver: self.jsr_resolver.clone(), - npm_resolver, - node_resolver, + npm_pkg_req_resolver: factory.npm_pkg_req_resolver().cloned(), + npm_resolver: factory.npm_resolver().cloned(), + node_resolver: factory.node_resolver().cloned(), redirect_resolver: self.redirect_resolver.clone(), - pkg_json_resolver: Some(pkg_json_resolver), + pkg_json_resolver: factory.pkg_json_resolver().clone(), graph_imports: self.graph_imports.clone(), package_json_deps_by_resolution: self .package_json_deps_by_resolution @@ -354,12 +334,12 @@ impl LspResolver { resolver.config_data.as_ref() } - pub fn maybe_node_resolver( + pub fn in_npm_pkg_checker( &self, file_referrer: Option<&ModuleSpecifier>, - ) -> Option<&Arc<CliNodeResolver>> { + ) -> &Arc<dyn InNpmPackageChecker> { let resolver = self.get_scope_resolver(file_referrer); - resolver.node_resolver.as_ref() + &resolver.in_npm_pkg_checker } pub fn maybe_managed_npm_resolver( @@ -429,9 +409,9 @@ impl LspResolver { file_referrer: Option<&ModuleSpecifier>, ) -> Option<(ModuleSpecifier, MediaType)> { let resolver = self.get_scope_resolver(file_referrer); - let node_resolver = resolver.node_resolver.as_ref()?; + let npm_pkg_req_resolver = resolver.npm_pkg_req_resolver.as_ref()?; Some(into_specifier_and_media_type(Some( - node_resolver + npm_pkg_req_resolver .resolve_req_reference( req_ref, referrer, @@ -483,10 +463,11 @@ impl LspResolver { referrer_kind: NodeModuleKind, ) -> bool { let resolver = self.get_scope_resolver(Some(referrer)); - let Some(node_resolver) = resolver.node_resolver.as_ref() else { + let Some(npm_pkg_req_resolver) = resolver.npm_pkg_req_resolver.as_ref() + else { return false; }; - node_resolver + npm_pkg_req_resolver .resolve_if_for_npm_pkg( specifier_text, referrer, @@ -503,10 +484,9 @@ impl LspResolver { referrer: &ModuleSpecifier, ) -> Result<Option<Arc<PackageJson>>, ClosestPkgJsonError> { let resolver = self.get_scope_resolver(Some(referrer)); - let Some(pkg_json_resolver) = resolver.pkg_json_resolver.as_ref() else { - return Ok(None); - }; - pkg_json_resolver.get_closest_package_json(referrer) + resolver + .pkg_json_resolver + .get_closest_package_json(referrer) } pub fn resolve_redirects( @@ -558,131 +538,206 @@ impl LspResolver { } } -async fn create_npm_resolver( - config_data: Option<&ConfigData>, - cache: &LspCache, - http_client_provider: &Arc<HttpClientProvider>, - pkg_json_resolver: &Arc<PackageJsonResolver>, -) -> Option<Arc<dyn CliNpmResolver>> { - let enable_byonm = config_data.map(|d| d.byonm).unwrap_or(false); - let options = if enable_byonm { - CliNpmResolverCreateOptions::Byonm(CliByonmNpmResolverCreateOptions { - fs: CliDenoResolverFs(Arc::new(deno_fs::RealFs)), - pkg_json_resolver: pkg_json_resolver.clone(), - root_node_modules_dir: config_data.and_then(|config_data| { - config_data.node_modules_dir.clone().or_else(|| { - url_to_file_path(&config_data.scope) - .ok() - .map(|p| p.join("node_modules/")) - }) - }), - }) - } else { - let npmrc = config_data - .and_then(|d| d.npmrc.clone()) - .unwrap_or_else(create_default_npmrc); - let npm_cache_dir = Arc::new(NpmCacheDir::new( - &DenoCacheEnvFsAdapter(&deno_fs::RealFs), - cache.deno_dir().npm_folder_path(), - npmrc.get_all_known_registries_urls(), - )); - CliNpmResolverCreateOptions::Managed(CliManagedNpmResolverCreateOptions { - http_client_provider: http_client_provider.clone(), - snapshot: match config_data.and_then(|d| d.lockfile.as_ref()) { - Some(lockfile) => { - CliNpmResolverManagedSnapshotOption::ResolveFromLockfile( - lockfile.clone(), - ) - } - None => CliNpmResolverManagedSnapshotOption::Specified(None), - }, - // Don't provide the lockfile. We don't want these resolvers - // updating it. Only the cache request should update the lockfile. - maybe_lockfile: None, - fs: Arc::new(deno_fs::RealFs), - npm_cache_dir, - // Use an "only" cache setting in order to make the - // user do an explicit "cache" command and prevent - // the cache from being filled with lots of packages while - // the user is typing. - cache_setting: CacheSetting::Only, - text_only_progress_bar: ProgressBar::new(ProgressBarStyle::TextOnly), - maybe_node_modules_path: config_data - .and_then(|d| d.node_modules_dir.clone()), - // only used for top level install, so we can ignore this - npm_install_deps_provider: Arc::new(NpmInstallDepsProvider::empty()), - npmrc, - npm_system_info: NpmSystemInfo::default(), - lifecycle_scripts: Default::default(), - }) - }; - Some(create_cli_npm_resolver_for_lsp(options).await) +#[derive(Default)] +struct ResolverFactoryServices { + cli_resolver: Deferred<Arc<CliResolver>>, + in_npm_pkg_checker: Deferred<Arc<dyn InNpmPackageChecker>>, + node_resolver: Deferred<Option<Arc<NodeResolver>>>, + npm_pkg_req_resolver: Deferred<Option<Arc<CliNpmReqResolver>>>, + npm_resolver: Option<Arc<dyn CliNpmResolver>>, +} + +struct ResolverFactory<'a> { + config_data: Option<&'a Arc<ConfigData>>, + fs: Arc<dyn deno_fs::FileSystem>, + pkg_json_resolver: Arc<PackageJsonResolver>, + services: ResolverFactoryServices, } -fn create_in_npm_pkg_checker( - npm_resolver: &Arc<dyn CliNpmResolver>, -) -> Arc<dyn InNpmPackageChecker> { - crate::npm::create_in_npm_pkg_checker(match npm_resolver.as_inner() { - crate::npm::InnerCliNpmResolverRef::Byonm(_) => { - CreateInNpmPkgCheckerOptions::Byonm +impl<'a> ResolverFactory<'a> { + pub fn new(config_data: Option<&'a Arc<ConfigData>>) -> Self { + let fs = Arc::new(deno_fs::RealFs); + let pkg_json_resolver = Arc::new(PackageJsonResolver::new( + deno_runtime::deno_node::DenoFsNodeResolverEnv::new(fs.clone()), + )); + Self { + config_data, + fs, + pkg_json_resolver, + services: Default::default(), } - crate::npm::InnerCliNpmResolverRef::Managed(m) => { - CreateInNpmPkgCheckerOptions::Managed( - CliManagedInNpmPkgCheckerCreateOptions { - root_cache_dir_url: m.global_cache_root_url(), - maybe_node_modules_path: m.maybe_node_modules_path(), + } + + async fn init_npm_resolver( + &mut self, + http_client_provider: &Arc<HttpClientProvider>, + cache: &LspCache, + ) { + let enable_byonm = self.config_data.map(|d| d.byonm).unwrap_or(false); + let options = if enable_byonm { + CliNpmResolverCreateOptions::Byonm(CliByonmNpmResolverCreateOptions { + fs: CliDenoResolverFs(Arc::new(deno_fs::RealFs)), + pkg_json_resolver: self.pkg_json_resolver.clone(), + root_node_modules_dir: self.config_data.and_then(|config_data| { + config_data.node_modules_dir.clone().or_else(|| { + url_to_file_path(&config_data.scope) + .ok() + .map(|p| p.join("node_modules/")) + }) + }), + }) + } else { + let npmrc = self + .config_data + .and_then(|d| d.npmrc.clone()) + .unwrap_or_else(create_default_npmrc); + let npm_cache_dir = Arc::new(NpmCacheDir::new( + &DenoCacheEnvFsAdapter(self.fs.as_ref()), + cache.deno_dir().npm_folder_path(), + npmrc.get_all_known_registries_urls(), + )); + CliNpmResolverCreateOptions::Managed(CliManagedNpmResolverCreateOptions { + http_client_provider: http_client_provider.clone(), + snapshot: match self.config_data.and_then(|d| d.lockfile.as_ref()) { + Some(lockfile) => { + CliNpmResolverManagedSnapshotOption::ResolveFromLockfile( + lockfile.clone(), + ) + } + None => CliNpmResolverManagedSnapshotOption::Specified(None), + }, + // Don't provide the lockfile. We don't want these resolvers + // updating it. Only the cache request should update the lockfile. + maybe_lockfile: None, + fs: Arc::new(deno_fs::RealFs), + npm_cache_dir, + // Use an "only" cache setting in order to make the + // user do an explicit "cache" command and prevent + // the cache from being filled with lots of packages while + // the user is typing. + cache_setting: CacheSetting::Only, + text_only_progress_bar: ProgressBar::new(ProgressBarStyle::TextOnly), + maybe_node_modules_path: self + .config_data + .and_then(|d| d.node_modules_dir.clone()), + // only used for top level install, so we can ignore this + npm_install_deps_provider: Arc::new(NpmInstallDepsProvider::empty()), + npmrc, + npm_system_info: NpmSystemInfo::default(), + lifecycle_scripts: Default::default(), + }) + }; + self.set_npm_resolver(create_cli_npm_resolver_for_lsp(options).await); + } + + pub fn set_npm_resolver(&mut self, npm_resolver: Arc<dyn CliNpmResolver>) { + self.services.npm_resolver = Some(npm_resolver); + } + + pub fn npm_resolver(&self) -> Option<&Arc<dyn CliNpmResolver>> { + self.services.npm_resolver.as_ref() + } + + pub fn cli_resolver(&self) -> &Arc<CliResolver> { + self.services.cli_resolver.get_or_init(|| { + let npm_req_resolver = self.npm_pkg_req_resolver().cloned(); + let deno_resolver = Arc::new(CliDenoResolver::new(DenoResolverOptions { + in_npm_pkg_checker: self.in_npm_pkg_checker().clone(), + node_and_req_resolver: match (self.node_resolver(), npm_req_resolver) { + (Some(node_resolver), Some(npm_req_resolver)) => { + Some(NodeAndNpmReqResolver { + node_resolver: node_resolver.clone(), + npm_req_resolver, + }) + } + _ => None, + }, + sloppy_imports_resolver: self + .config_data + .and_then(|d| d.sloppy_imports_resolver.clone()), + workspace_resolver: self + .config_data + .map(|d| d.resolver.clone()) + .unwrap_or_else(|| { + Arc::new(WorkspaceResolver::new_raw( + // this is fine because this is only used before initialization + Arc::new(ModuleSpecifier::parse("file:///").unwrap()), + None, + Vec::new(), + Vec::new(), + PackageJsonDepResolution::Disabled, + )) + }), + is_byonm: self.config_data.map(|d| d.byonm).unwrap_or(false), + maybe_vendor_dir: self.config_data.and_then(|d| d.vendor_dir.as_ref()), + })); + Arc::new(CliResolver::new(CliResolverOptions { + deno_resolver, + npm_resolver: self.npm_resolver().cloned(), + bare_node_builtins_enabled: self + .config_data + .is_some_and(|d| d.unstable.contains("bare-node-builtins")), + })) + }) + } + + pub fn pkg_json_resolver(&self) -> &Arc<PackageJsonResolver> { + &self.pkg_json_resolver + } + + pub fn in_npm_pkg_checker(&self) -> &Arc<dyn InNpmPackageChecker> { + self.services.in_npm_pkg_checker.get_or_init(|| { + crate::npm::create_in_npm_pkg_checker( + match self.services.npm_resolver.as_ref().map(|r| r.as_inner()) { + Some(crate::npm::InnerCliNpmResolverRef::Byonm(_)) | None => { + CreateInNpmPkgCheckerOptions::Byonm + } + Some(crate::npm::InnerCliNpmResolverRef::Managed(m)) => { + CreateInNpmPkgCheckerOptions::Managed( + CliManagedInNpmPkgCheckerCreateOptions { + root_cache_dir_url: m.global_cache_root_url(), + maybe_node_modules_path: m.maybe_node_modules_path(), + }, + ) + } }, ) - } - }) -} + }) + } -fn create_node_resolver( - fs: Arc<dyn deno_fs::FileSystem>, - in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>, - npm_resolver: &Arc<dyn CliNpmResolver>, - pkg_json_resolver: Arc<PackageJsonResolver>, -) -> Arc<CliNodeResolver> { - let node_resolver_inner = Arc::new(NodeResolver::new( - deno_runtime::deno_node::DenoFsNodeResolverEnv::new(fs.clone()), - in_npm_pkg_checker.clone(), - npm_resolver.clone().into_npm_resolver(), - pkg_json_resolver.clone(), - )); - Arc::new(CliNodeResolver::new( - fs, - in_npm_pkg_checker, - node_resolver_inner, - npm_resolver.clone(), - )) -} + pub fn node_resolver(&self) -> Option<&Arc<NodeResolver>> { + self + .services + .node_resolver + .get_or_init(|| { + let npm_resolver = self.services.npm_resolver.as_ref()?; + Some(Arc::new(NodeResolver::new( + deno_runtime::deno_node::DenoFsNodeResolverEnv::new(self.fs.clone()), + self.in_npm_pkg_checker().clone(), + npm_resolver.clone().into_npm_pkg_folder_resolver(), + self.pkg_json_resolver.clone(), + ))) + }) + .as_ref() + } -fn create_cli_resolver( - config_data: Option<&ConfigData>, - npm_resolver: Option<&Arc<dyn CliNpmResolver>>, - node_resolver: Option<&Arc<CliNodeResolver>>, -) -> Arc<CliResolver> { - Arc::new(CliResolver::new(CliResolverOptions { - node_resolver: node_resolver.cloned(), - npm_resolver: npm_resolver.cloned(), - workspace_resolver: config_data.map(|d| d.resolver.clone()).unwrap_or_else( - || { - Arc::new(WorkspaceResolver::new_raw( - // this is fine because this is only used before initialization - Arc::new(ModuleSpecifier::parse("file:///").unwrap()), - None, - Vec::new(), - Vec::new(), - PackageJsonDepResolution::Disabled, - )) - }, - ), - maybe_vendor_dir: config_data.and_then(|d| d.vendor_dir.as_ref()), - bare_node_builtins_enabled: config_data - .is_some_and(|d| d.unstable.contains("bare-node-builtins")), - sloppy_imports_resolver: config_data - .and_then(|d| d.sloppy_imports_resolver.clone()), - })) + pub fn npm_pkg_req_resolver(&self) -> Option<&Arc<CliNpmReqResolver>> { + self + .services + .npm_pkg_req_resolver + .get_or_init(|| { + let node_resolver = self.node_resolver()?; + let npm_resolver = self.npm_resolver()?; + Some(Arc::new(CliNpmReqResolver::new(NpmReqResolverOptions { + byonm_resolver: (npm_resolver.clone()).into_maybe_byonm(), + fs: CliDenoResolverFs(self.fs.clone()), + in_npm_pkg_checker: self.in_npm_pkg_checker().clone(), + node_resolver: node_resolver.clone(), + npm_req_resolver: npm_resolver.clone().into_npm_req_resolver(), + }))) + }) + .as_ref() + } } #[derive(Debug, Eq, PartialEq)] |