From 2f0c25d33fc0c8bb8e6e92826b2549f429d68d42 Mon Sep 17 00:00:00 2001 From: Nayeem Rahman Date: Thu, 31 Oct 2024 10:52:43 +0000 Subject: fix(lsp): include unstable features from editor settings (#26655) --- cli/lsp/resolver.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'cli/lsp/resolver.rs') diff --git a/cli/lsp/resolver.rs b/cli/lsp/resolver.rs index c89273147..00587f8f5 100644 --- a/cli/lsp/resolver.rs +++ b/cli/lsp/resolver.rs @@ -555,8 +555,8 @@ fn create_graph_resolver( workspace.to_maybe_jsx_import_source_config().ok().flatten() }), maybe_vendor_dir: config_data.and_then(|d| d.vendor_dir.as_ref()), - bare_node_builtins_enabled: workspace - .is_some_and(|workspace| workspace.has_unstable("bare-node-builtins")), + 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()), })) -- cgit v1.2.3 From 826e42a5b5880c974ae019a7a21aade6a718062c Mon Sep 17 00:00:00 2001 From: David Sherret Date: Fri, 1 Nov 2024 12:27:00 -0400 Subject: fix: improved support for cjs and cts modules (#26558) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * cts support * better cjs/cts type checking * deno compile cjs/cts support * More efficient detect cjs (going towards stabilization) * Determination of whether .js, .ts, .jsx, or .tsx is cjs or esm is only done after loading * Support `import x = require(...);` Co-authored-by: Bartek IwaƄczuk --- cli/lsp/resolver.rs | 228 +++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 181 insertions(+), 47 deletions(-) (limited to 'cli/lsp/resolver.rs') diff --git a/cli/lsp/resolver.rs b/cli/lsp/resolver.rs index 00587f8f5..9ce76005e 100644 --- a/cli/lsp/resolver.rs +++ b/cli/lsp/resolver.rs @@ -2,6 +2,8 @@ use dashmap::DashMap; use deno_ast::MediaType; +use deno_ast::ParsedSource; +use deno_cache_dir::npm::NpmCacheDir; use deno_cache_dir::HttpCache; use deno_config::workspace::PackageJsonDepResolution; use deno_config::workspace::WorkspaceResolver; @@ -14,15 +16,15 @@ use deno_path_util::url_to_file_path; use deno_runtime::deno_fs; use deno_runtime::deno_node::NodeResolver; use deno_runtime::deno_node::PackageJson; +use deno_runtime::deno_node::PackageJsonResolver; use deno_semver::jsr::JsrPackageReqReference; use deno_semver::npm::NpmPackageReqReference; use deno_semver::package::PackageNv; use deno_semver::package::PackageReq; use indexmap::IndexMap; use node_resolver::errors::ClosestPkgJsonError; -use node_resolver::NodeResolution; +use node_resolver::InNpmPackageChecker; use node_resolver::NodeResolutionMode; -use node_resolver::NpmResolver; use std::borrow::Cow; use std::collections::BTreeMap; use std::collections::BTreeSet; @@ -36,6 +38,7 @@ use crate::args::create_default_npmrc; use crate::args::CacheSetting; use crate::args::CliLockfile; use crate::args::NpmInstallDepsProvider; +use crate::cache::DenoCacheEnvFsAdapter; use crate::graph_util::CliJsrUrlProvider; use crate::http_util::HttpClientProvider; use crate::lsp::config::Config; @@ -43,26 +46,32 @@ use crate::lsp::config::ConfigData; use crate::lsp::logging::lsp_warn; use crate::npm::create_cli_npm_resolver_for_lsp; use crate::npm::CliByonmNpmResolverCreateOptions; +use crate::npm::CliManagedInNpmPkgCheckerCreateOptions; +use crate::npm::CliManagedNpmResolverCreateOptions; use crate::npm::CliNpmResolver; use crate::npm::CliNpmResolverCreateOptions; -use crate::npm::CliNpmResolverManagedCreateOptions; use crate::npm::CliNpmResolverManagedSnapshotOption; +use crate::npm::CreateInNpmPkgCheckerOptions; use crate::npm::ManagedCliNpmResolver; -use crate::resolver::CjsResolutionStore; +use crate::resolver::CjsTracker; +use crate::resolver::CjsTrackerOptions; use crate::resolver::CliDenoResolverFs; use crate::resolver::CliGraphResolver; use crate::resolver::CliGraphResolverOptions; use crate::resolver::CliNodeResolver; use crate::resolver::WorkerCliNpmGraphResolver; +use crate::tsc::into_specifier_and_media_type; use crate::util::progress_bar::ProgressBar; use crate::util::progress_bar::ProgressBarStyle; #[derive(Debug, Clone)] struct LspScopeResolver { + cjs_tracker: Option>, graph_resolver: Arc, jsr_resolver: Option>, npm_resolver: Option>, node_resolver: Option>, + pkg_json_resolver: Option>, redirect_resolver: Option>, graph_imports: Arc>, config_data: Option>, @@ -71,10 +80,12 @@ struct LspScopeResolver { impl Default for LspScopeResolver { fn default() -> Self { Self { + cjs_tracker: None, graph_resolver: create_graph_resolver(None, None, None), jsr_resolver: None, npm_resolver: None, node_resolver: None, + pkg_json_resolver: None, redirect_resolver: None, graph_imports: Default::default(), config_data: None, @@ -90,14 +101,35 @@ impl LspScopeResolver { ) -> Self { let mut npm_resolver = None; let mut node_resolver = None; + let mut lsp_cjs_tracker = 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; - node_resolver = create_node_resolver(npm_resolver.as_ref()); + if let Some(npm_resolver) = &npm_resolver { + let in_npm_pkg_checker = create_in_npm_pkg_checker(npm_resolver); + let cjs_tracker = create_cjs_tracker( + in_npm_pkg_checker.clone(), + pkg_json_resolver.clone(), + ); + lsp_cjs_tracker = + Some(Arc::new(LspCjsTracker::new(cjs_tracker.clone()))); + node_resolver = Some(create_node_resolver( + cjs_tracker, + fs.clone(), + in_npm_pkg_checker, + npm_resolver, + pkg_json_resolver.clone(), + )); + } } let graph_resolver = create_graph_resolver( config_data.map(|d| d.as_ref()), @@ -134,10 +166,12 @@ impl LspScopeResolver { }) .unwrap_or_default(); Self { + cjs_tracker: lsp_cjs_tracker, graph_resolver, jsr_resolver, npm_resolver, node_resolver, + pkg_json_resolver: Some(pkg_json_resolver), redirect_resolver, graph_imports, config_data: config_data.cloned(), @@ -147,18 +181,40 @@ impl LspScopeResolver { fn snapshot(&self) -> Arc { let npm_resolver = self.npm_resolver.as_ref().map(|r| r.clone_snapshotted()); - let node_resolver = create_node_resolver(npm_resolver.as_ref()); + 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; + let mut lsp_cjs_tracker = None; + if let Some(npm_resolver) = &npm_resolver { + let in_npm_pkg_checker = create_in_npm_pkg_checker(npm_resolver); + let cjs_tracker = create_cjs_tracker( + in_npm_pkg_checker.clone(), + pkg_json_resolver.clone(), + ); + lsp_cjs_tracker = Some(Arc::new(LspCjsTracker::new(cjs_tracker.clone()))); + node_resolver = Some(create_node_resolver( + cjs_tracker, + fs, + in_npm_pkg_checker, + npm_resolver, + pkg_json_resolver.clone(), + )); + } let graph_resolver = create_graph_resolver( self.config_data.as_deref(), npm_resolver.as_ref(), node_resolver.as_ref(), ); Arc::new(Self { + cjs_tracker: lsp_cjs_tracker, graph_resolver, jsr_resolver: self.jsr_resolver.clone(), npm_resolver, node_resolver, redirect_resolver: self.redirect_resolver.clone(), + pkg_json_resolver: Some(pkg_json_resolver), graph_imports: self.graph_imports.clone(), config_data: self.config_data.clone(), }) @@ -261,6 +317,22 @@ impl LspResolver { resolver.graph_resolver.create_graph_npm_resolver() } + pub fn maybe_cjs_tracker( + &self, + file_referrer: Option<&ModuleSpecifier>, + ) -> Option<&Arc> { + let resolver = self.get_scope_resolver(file_referrer); + resolver.cjs_tracker.as_ref() + } + + pub fn maybe_node_resolver( + &self, + file_referrer: Option<&ModuleSpecifier>, + ) -> Option<&Arc> { + let resolver = self.get_scope_resolver(file_referrer); + resolver.node_resolver.as_ref() + } + pub fn maybe_managed_npm_resolver( &self, file_referrer: Option<&ModuleSpecifier>, @@ -328,7 +400,7 @@ impl LspResolver { ) -> Option<(ModuleSpecifier, MediaType)> { let resolver = self.get_scope_resolver(file_referrer); let node_resolver = resolver.node_resolver.as_ref()?; - Some(NodeResolution::into_specifier_and_media_type(Some( + Some(into_specifier_and_media_type(Some( node_resolver .resolve_req_reference(req_ref, referrer, NodeResolutionMode::Types) .ok()?, @@ -346,14 +418,10 @@ impl LspResolver { .contains("/node_modules/") } - let global_npm_resolver = self - .get_scope_resolver(Some(specifier)) - .npm_resolver - .as_ref() - .and_then(|npm_resolver| npm_resolver.as_managed()) - .filter(|r| r.root_node_modules_path().is_none()); - if let Some(npm_resolver) = &global_npm_resolver { - if npm_resolver.in_npm_package(specifier) { + if let Some(node_resolver) = + &self.get_scope_resolver(Some(specifier)).node_resolver + { + if node_resolver.in_npm_package(specifier) { return true; } } @@ -361,18 +429,6 @@ impl LspResolver { has_node_modules_dir(specifier) } - pub fn node_media_type( - &self, - specifier: &ModuleSpecifier, - ) -> Option { - 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()?; - Some(NodeResolution::into_specifier_and_media_type(Some(resolution)).1) - } - pub fn is_bare_package_json_dep( &self, specifier_text: &str, @@ -398,10 +454,10 @@ impl LspResolver { referrer: &ModuleSpecifier, ) -> Result>, ClosestPkgJsonError> { let resolver = self.get_scope_resolver(Some(referrer)); - let Some(node_resolver) = resolver.node_resolver.as_ref() else { + let Some(pkg_json_resolver) = resolver.pkg_json_resolver.as_ref() else { return Ok(None); }; - node_resolver.get_closest_package_json(referrer) + pkg_json_resolver.get_closest_package_json(referrer) } pub fn resolve_redirects( @@ -457,11 +513,13 @@ async fn create_npm_resolver( config_data: Option<&ConfigData>, cache: &LspCache, http_client_provider: &Arc, + pkg_json_resolver: &Arc, ) -> Option> { 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) @@ -471,7 +529,15 @@ async fn create_npm_resolver( }), }) } else { - CliNpmResolverCreateOptions::Managed(CliNpmResolverManagedCreateOptions { + 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) => { @@ -485,7 +551,7 @@ async fn create_npm_resolver( // updating it. Only the cache request should update the lockfile. maybe_lockfile: None, fs: Arc::new(deno_fs::RealFs), - npm_global_cache_dir: cache.deno_dir().npm_folder_path(), + 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 @@ -496,9 +562,7 @@ async fn create_npm_resolver( .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: config_data - .and_then(|d| d.npmrc.clone()) - .unwrap_or_else(create_default_npmrc), + npmrc, npm_system_info: NpmSystemInfo::default(), lifecycle_scripts: Default::default(), }) @@ -506,28 +570,59 @@ async fn create_npm_resolver( Some(create_cli_npm_resolver_for_lsp(options).await) } -fn create_node_resolver( - npm_resolver: Option<&Arc>, -) -> Option> { - use once_cell::sync::Lazy; +fn create_cjs_tracker( + in_npm_pkg_checker: Arc, + pkg_json_resolver: Arc, +) -> Arc { + Arc::new(CjsTracker::new( + in_npm_pkg_checker, + pkg_json_resolver, + CjsTrackerOptions { + // todo(dsherret): support in the lsp by stabilizing the feature + // so that we don't have to pipe the config in here + unstable_detect_cjs: false, + }, + )) +} - // it's not ideal to share this across all scopes and to - // never clear it, but it's fine for the time being - static CJS_RESOLUTIONS: Lazy> = - Lazy::new(Default::default); +fn create_in_npm_pkg_checker( + npm_resolver: &Arc, +) -> Arc { + crate::npm::create_in_npm_pkg_checker(match npm_resolver.as_inner() { + crate::npm::InnerCliNpmResolverRef::Byonm(_) => { + CreateInNpmPkgCheckerOptions::Byonm + } + 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(), + }, + ) + } + }) +} - let npm_resolver = npm_resolver?; - let fs = Arc::new(deno_fs::RealFs); +fn create_node_resolver( + cjs_tracker: Arc, + fs: Arc, + in_npm_pkg_checker: Arc, + npm_resolver: &Arc, + pkg_json_resolver: Arc, +) -> Arc { 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(), )); - Some(Arc::new(CliNodeResolver::new( - CJS_RESOLUTIONS.clone(), + Arc::new(CliNodeResolver::new( + cjs_tracker.clone(), fs, + in_npm_pkg_checker, node_resolver_inner, npm_resolver.clone(), - ))) + )) } fn create_graph_resolver( @@ -702,6 +797,45 @@ impl RedirectResolver { } } +#[derive(Debug)] +pub struct LspCjsTracker { + cjs_tracker: Arc, +} + +impl LspCjsTracker { + pub fn new(cjs_tracker: Arc) -> Self { + Self { cjs_tracker } + } + + pub fn is_cjs( + &self, + specifier: &ModuleSpecifier, + media_type: MediaType, + maybe_parsed_source: Option<&ParsedSource>, + ) -> bool { + if let Some(module_kind) = + self.cjs_tracker.get_known_kind(specifier, media_type) + { + module_kind.is_cjs() + } else { + let maybe_is_script = maybe_parsed_source.map(|p| p.compute_is_script()); + maybe_is_script + .and_then(|is_script| { + self + .cjs_tracker + .is_cjs_with_known_is_script(specifier, media_type, is_script) + .ok() + }) + .unwrap_or_else(|| { + self + .cjs_tracker + .is_maybe_cjs(specifier, media_type) + .unwrap_or(false) + }) + } + } +} + #[cfg(test)] mod tests { use super::*; -- cgit v1.2.3 From 5088b25f2315fa45e912377356a89ba2a44dbcda Mon Sep 17 00:00:00 2001 From: Nayeem Rahman Date: Wed, 6 Nov 2024 06:26:46 +0000 Subject: feat(lsp): auto-import completions from byonm dependencies (#26680) --- cli/lsp/resolver.rs | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) (limited to 'cli/lsp/resolver.rs') diff --git a/cli/lsp/resolver.rs b/cli/lsp/resolver.rs index 9ce76005e..f5df24d57 100644 --- a/cli/lsp/resolver.rs +++ b/cli/lsp/resolver.rs @@ -74,6 +74,7 @@ struct LspScopeResolver { pkg_json_resolver: Option>, redirect_resolver: Option>, graph_imports: Arc>, + package_json_deps_by_resolution: Arc>, config_data: Option>, } @@ -88,6 +89,7 @@ impl Default for LspScopeResolver { pkg_json_resolver: None, redirect_resolver: None, graph_imports: Default::default(), + package_json_deps_by_resolution: Default::default(), config_data: None, } } @@ -165,6 +167,33 @@ impl LspScopeResolver { ) }) .unwrap_or_default(); + let package_json_deps_by_resolution = (|| { + let node_resolver = node_resolver.as_ref()?; + let package_json = config_data?.maybe_pkg_json()?; + let referrer = package_json.specifier(); + let dependencies = package_json.dependencies.as_ref()?; + let result = dependencies + .iter() + .flat_map(|(name, _)| { + let req_ref = + NpmPackageReqReference::from_str(&format!("npm:{name}")).ok()?; + let specifier = into_specifier_and_media_type(Some( + node_resolver + .resolve_req_reference( + &req_ref, + &referrer, + NodeResolutionMode::Types, + ) + .ok()?, + )) + .0; + Some((specifier, name.clone())) + }) + .collect(); + Some(result) + })(); + let package_json_deps_by_resolution = + Arc::new(package_json_deps_by_resolution.unwrap_or_default()); Self { cjs_tracker: lsp_cjs_tracker, graph_resolver, @@ -174,6 +203,7 @@ impl LspScopeResolver { pkg_json_resolver: Some(pkg_json_resolver), redirect_resolver, graph_imports, + package_json_deps_by_resolution, config_data: config_data.cloned(), } } @@ -216,6 +246,9 @@ impl LspScopeResolver { redirect_resolver: self.redirect_resolver.clone(), pkg_json_resolver: Some(pkg_json_resolver), graph_imports: self.graph_imports.clone(), + package_json_deps_by_resolution: self + .package_json_deps_by_resolution + .clone(), config_data: self.config_data.clone(), }) } @@ -407,6 +440,18 @@ impl LspResolver { ))) } + pub fn file_url_to_package_json_dep( + &self, + specifier: &ModuleSpecifier, + file_referrer: Option<&ModuleSpecifier>, + ) -> Option { + let resolver = self.get_scope_resolver(file_referrer); + resolver + .package_json_deps_by_resolution + .get(specifier) + .cloned() + } + pub fn in_node_modules(&self, specifier: &ModuleSpecifier) -> bool { fn has_node_modules_dir(specifier: &ModuleSpecifier) -> bool { // consider any /node_modules/ directory as being in the node_modules -- cgit v1.2.3 From f091d1ad69b4e5217ae3272b641171781a372c4f Mon Sep 17 00:00:00 2001 From: David Sherret Date: Wed, 13 Nov 2024 10:10:09 -0500 Subject: feat(node): stabilize detecting if CJS via `"type": "commonjs"` in a package.json (#26439) This will respect `"type": "commonjs"` in a package.json to determine if `.js`/`.jsx`/`.ts`/.tsx` files are CJS or ESM. If the file is found to be ESM it will be loaded as ESM though. --- cli/lsp/resolver.rs | 287 +++++++++++++++++++++++++++++++++------------------- 1 file changed, 183 insertions(+), 104 deletions(-) (limited to 'cli/lsp/resolver.rs') diff --git a/cli/lsp/resolver.rs b/cli/lsp/resolver.rs index f5df24d57..37f63b912 100644 --- a/cli/lsp/resolver.rs +++ b/cli/lsp/resolver.rs @@ -2,16 +2,18 @@ use dashmap::DashMap; use deno_ast::MediaType; -use deno_ast::ParsedSource; use deno_cache_dir::npm::NpmCacheDir; use deno_cache_dir::HttpCache; +use deno_config::deno_json::JsxImportSourceConfig; use deno_config::workspace::PackageJsonDepResolution; use deno_config::workspace::WorkspaceResolver; use deno_core::url::Url; -use deno_graph::source::Resolver; +use deno_graph::source::ResolutionMode; use deno_graph::GraphImport; use deno_graph::ModuleSpecifier; +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_runtime::deno_fs; use deno_runtime::deno_node::NodeResolver; @@ -24,6 +26,7 @@ use deno_semver::package::PackageReq; use indexmap::IndexMap; use node_resolver::errors::ClosestPkgJsonError; use node_resolver::InNpmPackageChecker; +use node_resolver::NodeModuleKind; use node_resolver::NodeResolutionMode; use std::borrow::Cow; use std::collections::BTreeMap; @@ -33,6 +36,7 @@ use std::collections::HashSet; use std::sync::Arc; use super::cache::LspCache; +use super::documents::Document; use super::jsr::JsrCacheResolver; use crate::args::create_default_npmrc; use crate::args::CacheSetting; @@ -53,21 +57,20 @@ use crate::npm::CliNpmResolverCreateOptions; use crate::npm::CliNpmResolverManagedSnapshotOption; use crate::npm::CreateInNpmPkgCheckerOptions; use crate::npm::ManagedCliNpmResolver; -use crate::resolver::CjsTracker; -use crate::resolver::CjsTrackerOptions; use crate::resolver::CliDenoResolverFs; -use crate::resolver::CliGraphResolver; -use crate::resolver::CliGraphResolverOptions; use crate::resolver::CliNodeResolver; +use crate::resolver::CliResolver; +use crate::resolver::CliResolverOptions; +use crate::resolver::IsCjsResolver; use crate::resolver::WorkerCliNpmGraphResolver; use crate::tsc::into_specifier_and_media_type; +use crate::util::fs::canonicalize_path_maybe_not_exists; use crate::util::progress_bar::ProgressBar; use crate::util::progress_bar::ProgressBarStyle; #[derive(Debug, Clone)] struct LspScopeResolver { - cjs_tracker: Option>, - graph_resolver: Arc, + resolver: Arc, jsr_resolver: Option>, npm_resolver: Option>, node_resolver: Option>, @@ -81,8 +84,7 @@ struct LspScopeResolver { impl Default for LspScopeResolver { fn default() -> Self { Self { - cjs_tracker: None, - graph_resolver: create_graph_resolver(None, None, None), + resolver: create_cli_resolver(None, None, None), jsr_resolver: None, npm_resolver: None, node_resolver: None, @@ -103,7 +105,6 @@ impl LspScopeResolver { ) -> Self { let mut npm_resolver = None; let mut node_resolver = None; - let mut lsp_cjs_tracker = None; let fs = Arc::new(deno_fs::RealFs); let pkg_json_resolver = Arc::new(PackageJsonResolver::new( deno_runtime::deno_node::DenoFsNodeResolverEnv::new(fs.clone()), @@ -118,14 +119,7 @@ impl LspScopeResolver { .await; if let Some(npm_resolver) = &npm_resolver { let in_npm_pkg_checker = create_in_npm_pkg_checker(npm_resolver); - let cjs_tracker = create_cjs_tracker( - in_npm_pkg_checker.clone(), - pkg_json_resolver.clone(), - ); - lsp_cjs_tracker = - Some(Arc::new(LspCjsTracker::new(cjs_tracker.clone()))); node_resolver = Some(create_node_resolver( - cjs_tracker, fs.clone(), in_npm_pkg_checker, npm_resolver, @@ -133,7 +127,7 @@ impl LspScopeResolver { )); } } - let graph_resolver = create_graph_resolver( + let cli_resolver = create_cli_resolver( config_data.map(|d| d.as_ref()), npm_resolver.as_ref(), node_resolver.as_ref(), @@ -146,7 +140,9 @@ impl LspScopeResolver { cache.for_specifier(config_data.map(|d| d.scope.as_ref())), config_data.and_then(|d| d.lockfile.clone()), ))); - let npm_graph_resolver = graph_resolver.create_graph_npm_resolver(); + let npm_graph_resolver = cli_resolver.create_graph_npm_resolver(); + let maybe_jsx_import_source_config = + config_data.and_then(|d| d.maybe_jsx_import_source_config()); let graph_imports = config_data .and_then(|d| d.member_dir.workspace.to_compiler_option_types().ok()) .map(|imports| { @@ -154,11 +150,18 @@ impl LspScopeResolver { imports .into_iter() .map(|(referrer, imports)| { + let resolver = SingleReferrerGraphResolver { + valid_referrer: &referrer, + referrer_kind: NodeModuleKind::Esm, + cli_resolver: &cli_resolver, + jsx_import_source_config: maybe_jsx_import_source_config + .as_ref(), + }; let graph_import = GraphImport::new( &referrer, imports, &CliJsrUrlProvider, - Some(graph_resolver.as_ref()), + Some(&resolver), Some(&npm_graph_resolver), ); (referrer, graph_import) @@ -182,6 +185,8 @@ impl LspScopeResolver { .resolve_req_reference( &req_ref, &referrer, + // todo(dsherret): this is wrong because it doesn't consider CJS referrers + NodeModuleKind::Esm, NodeResolutionMode::Types, ) .ok()?, @@ -195,8 +200,7 @@ impl LspScopeResolver { let package_json_deps_by_resolution = Arc::new(package_json_deps_by_resolution.unwrap_or_default()); Self { - cjs_tracker: lsp_cjs_tracker, - graph_resolver, + resolver: cli_resolver, jsr_resolver, npm_resolver, node_resolver, @@ -216,30 +220,22 @@ impl LspScopeResolver { deno_runtime::deno_node::DenoFsNodeResolverEnv::new(fs.clone()), )); let mut node_resolver = None; - let mut lsp_cjs_tracker = None; if let Some(npm_resolver) = &npm_resolver { let in_npm_pkg_checker = create_in_npm_pkg_checker(npm_resolver); - let cjs_tracker = create_cjs_tracker( - in_npm_pkg_checker.clone(), - pkg_json_resolver.clone(), - ); - lsp_cjs_tracker = Some(Arc::new(LspCjsTracker::new(cjs_tracker.clone()))); node_resolver = Some(create_node_resolver( - cjs_tracker, fs, in_npm_pkg_checker, npm_resolver, pkg_json_resolver.clone(), )); } - let graph_resolver = create_graph_resolver( + let graph_resolver = create_cli_resolver( self.config_data.as_deref(), npm_resolver.as_ref(), node_resolver.as_ref(), ); Arc::new(Self { - cjs_tracker: lsp_cjs_tracker, - graph_resolver, + resolver: graph_resolver, jsr_resolver: self.jsr_resolver.clone(), npm_resolver, node_resolver, @@ -334,12 +330,12 @@ impl LspResolver { } } - pub fn as_graph_resolver( + pub fn as_cli_resolver( &self, file_referrer: Option<&ModuleSpecifier>, - ) -> &dyn Resolver { + ) -> &CliResolver { let resolver = self.get_scope_resolver(file_referrer); - resolver.graph_resolver.as_ref() + resolver.resolver.as_ref() } pub fn create_graph_npm_resolver( @@ -347,15 +343,15 @@ impl LspResolver { file_referrer: Option<&ModuleSpecifier>, ) -> WorkerCliNpmGraphResolver { let resolver = self.get_scope_resolver(file_referrer); - resolver.graph_resolver.create_graph_npm_resolver() + resolver.resolver.create_graph_npm_resolver() } - pub fn maybe_cjs_tracker( + pub fn as_config_data( &self, file_referrer: Option<&ModuleSpecifier>, - ) -> Option<&Arc> { + ) -> Option<&Arc> { let resolver = self.get_scope_resolver(file_referrer); - resolver.cjs_tracker.as_ref() + resolver.config_data.as_ref() } pub fn maybe_node_resolver( @@ -429,13 +425,19 @@ impl LspResolver { &self, req_ref: &NpmPackageReqReference, referrer: &ModuleSpecifier, + referrer_kind: NodeModuleKind, file_referrer: Option<&ModuleSpecifier>, ) -> Option<(ModuleSpecifier, MediaType)> { let resolver = self.get_scope_resolver(file_referrer); let node_resolver = resolver.node_resolver.as_ref()?; Some(into_specifier_and_media_type(Some( node_resolver - .resolve_req_reference(req_ref, referrer, NodeResolutionMode::Types) + .resolve_req_reference( + req_ref, + referrer, + referrer_kind, + NodeResolutionMode::Types, + ) .ok()?, ))) } @@ -478,6 +480,7 @@ impl LspResolver { &self, specifier_text: &str, referrer: &ModuleSpecifier, + referrer_kind: NodeModuleKind, ) -> bool { let resolver = self.get_scope_resolver(Some(referrer)); let Some(node_resolver) = resolver.node_resolver.as_ref() else { @@ -487,6 +490,7 @@ impl LspResolver { .resolve_if_for_npm_pkg( specifier_text, referrer, + referrer_kind, NodeResolutionMode::Types, ) .ok() @@ -615,21 +619,6 @@ async fn create_npm_resolver( Some(create_cli_npm_resolver_for_lsp(options).await) } -fn create_cjs_tracker( - in_npm_pkg_checker: Arc, - pkg_json_resolver: Arc, -) -> Arc { - Arc::new(CjsTracker::new( - in_npm_pkg_checker, - pkg_json_resolver, - CjsTrackerOptions { - // todo(dsherret): support in the lsp by stabilizing the feature - // so that we don't have to pipe the config in here - unstable_detect_cjs: false, - }, - )) -} - fn create_in_npm_pkg_checker( npm_resolver: &Arc, ) -> Arc { @@ -649,7 +638,6 @@ fn create_in_npm_pkg_checker( } fn create_node_resolver( - cjs_tracker: Arc, fs: Arc, in_npm_pkg_checker: Arc, npm_resolver: &Arc, @@ -662,7 +650,6 @@ fn create_node_resolver( pkg_json_resolver.clone(), )); Arc::new(CliNodeResolver::new( - cjs_tracker.clone(), fs, in_npm_pkg_checker, node_resolver_inner, @@ -670,13 +657,12 @@ fn create_node_resolver( )) } -fn create_graph_resolver( +fn create_cli_resolver( config_data: Option<&ConfigData>, npm_resolver: Option<&Arc>, node_resolver: Option<&Arc>, -) -> Arc { - let workspace = config_data.map(|d| &d.member_dir.workspace); - Arc::new(CliGraphResolver::new(CliGraphResolverOptions { +) -> Arc { + 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( @@ -691,9 +677,6 @@ fn create_graph_resolver( )) }, ), - maybe_jsx_import_source_config: workspace.and_then(|workspace| { - workspace.to_maybe_jsx_import_source_config().ok().flatten() - }), 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")), @@ -726,6 +709,141 @@ impl std::fmt::Debug for RedirectResolver { } } +#[derive(Debug)] +pub struct LspIsCjsResolver { + inner: IsCjsResolver, +} + +impl Default for LspIsCjsResolver { + fn default() -> Self { + LspIsCjsResolver::new(&Default::default()) + } +} + +impl LspIsCjsResolver { + pub fn new(cache: &LspCache) -> Self { + #[derive(Debug)] + struct LspInNpmPackageChecker { + global_cache_dir: ModuleSpecifier, + } + + impl LspInNpmPackageChecker { + pub fn new(cache: &LspCache) -> Self { + let npm_folder_path = cache.deno_dir().npm_folder_path(); + Self { + global_cache_dir: url_from_directory_path( + &canonicalize_path_maybe_not_exists(&npm_folder_path) + .unwrap_or(npm_folder_path), + ) + .unwrap_or_else(|_| { + ModuleSpecifier::parse("file:///invalid/").unwrap() + }), + } + } + } + + impl InNpmPackageChecker for LspInNpmPackageChecker { + fn in_npm_package(&self, specifier: &ModuleSpecifier) -> bool { + if specifier.scheme() != "file" { + return false; + } + if specifier + .as_str() + .starts_with(self.global_cache_dir.as_str()) + { + return true; + } + specifier.as_str().contains("/node_modules/") + } + } + + let fs = Arc::new(deno_fs::RealFs); + let pkg_json_resolver = Arc::new(PackageJsonResolver::new( + deno_runtime::deno_node::DenoFsNodeResolverEnv::new(fs.clone()), + )); + + LspIsCjsResolver { + inner: IsCjsResolver::new( + Arc::new(LspInNpmPackageChecker::new(cache)), + pkg_json_resolver, + crate::resolver::IsCjsResolverOptions { + detect_cjs: true, + is_node_main: false, + }, + ), + } + } + + pub fn get_maybe_doc_module_kind( + &self, + specifier: &ModuleSpecifier, + maybe_document: Option<&Document>, + ) -> NodeModuleKind { + self.get_lsp_referrer_kind( + specifier, + maybe_document.and_then(|d| d.is_script()), + ) + } + + pub fn get_doc_module_kind(&self, document: &Document) -> NodeModuleKind { + self.get_lsp_referrer_kind(document.specifier(), document.is_script()) + } + + pub fn get_lsp_referrer_kind( + &self, + specifier: &ModuleSpecifier, + is_script: Option, + ) -> NodeModuleKind { + self.inner.get_lsp_referrer_kind(specifier, is_script) + } +} + +#[derive(Debug)] +pub struct SingleReferrerGraphResolver<'a> { + pub valid_referrer: &'a ModuleSpecifier, + pub referrer_kind: NodeModuleKind, + pub cli_resolver: &'a CliResolver, + pub jsx_import_source_config: Option<&'a JsxImportSourceConfig>, +} + +impl<'a> deno_graph::source::Resolver for SingleReferrerGraphResolver<'a> { + fn default_jsx_import_source(&self) -> Option { + self + .jsx_import_source_config + .and_then(|c| c.default_specifier.clone()) + } + + fn default_jsx_import_source_types(&self) -> Option { + self + .jsx_import_source_config + .and_then(|c| c.default_types_specifier.clone()) + } + + fn jsx_import_source_module(&self) -> &str { + self + .jsx_import_source_config + .map(|c| c.module.as_str()) + .unwrap_or(deno_graph::source::DEFAULT_JSX_IMPORT_SOURCE_MODULE) + } + + fn resolve( + &self, + specifier_text: &str, + referrer_range: &Range, + mode: ResolutionMode, + ) -> Result { + // this resolver assumes it will only be used with a single referrer + // with the provided referrer kind + debug_assert_eq!(referrer_range.specifier, *self.valid_referrer); + self.cli_resolver.resolve( + specifier_text, + referrer_range, + self.referrer_kind, + mode, + ) + } +} + impl RedirectResolver { fn new( cache: Arc, @@ -842,45 +960,6 @@ impl RedirectResolver { } } -#[derive(Debug)] -pub struct LspCjsTracker { - cjs_tracker: Arc, -} - -impl LspCjsTracker { - pub fn new(cjs_tracker: Arc) -> Self { - Self { cjs_tracker } - } - - pub fn is_cjs( - &self, - specifier: &ModuleSpecifier, - media_type: MediaType, - maybe_parsed_source: Option<&ParsedSource>, - ) -> bool { - if let Some(module_kind) = - self.cjs_tracker.get_known_kind(specifier, media_type) - { - module_kind.is_cjs() - } else { - let maybe_is_script = maybe_parsed_source.map(|p| p.compute_is_script()); - maybe_is_script - .and_then(|is_script| { - self - .cjs_tracker - .is_cjs_with_known_is_script(specifier, media_type, is_script) - .ok() - }) - .unwrap_or_else(|| { - self - .cjs_tracker - .is_maybe_cjs(specifier, media_type) - .unwrap_or(false) - }) - } - } -} - #[cfg(test)] mod tests { use super::*; -- cgit v1.2.3 From 617350e79c58b6e01984e3d7c7436d243d0e5cff Mon Sep 17 00:00:00 2001 From: David Sherret Date: Thu, 14 Nov 2024 15:24:25 -0500 Subject: 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. --- cli/lsp/resolver.rs | 427 +++++++++++++++++++++++++++++----------------------- 1 file changed, 241 insertions(+), 186 deletions(-) (limited to 'cli/lsp/resolver.rs') 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, + in_npm_pkg_checker: Arc, jsr_resolver: Option>, npm_resolver: Option>, - node_resolver: Option>, - pkg_json_resolver: Option>, + node_resolver: Option>, + npm_pkg_req_resolver: Option>, + pkg_json_resolver: Arc, redirect_resolver: Option>, graph_imports: Arc>, package_json_deps_by_resolution: Arc>, @@ -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>, ) -> 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 { + 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> { + ) -> &Arc { 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>, 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, - pkg_json_resolver: &Arc, -) -> Option> { - 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>, + in_npm_pkg_checker: Deferred>, + node_resolver: Deferred>>, + npm_pkg_req_resolver: Deferred>>, + npm_resolver: Option>, +} + +struct ResolverFactory<'a> { + config_data: Option<&'a Arc>, + fs: Arc, + pkg_json_resolver: Arc, + services: ResolverFactoryServices, } -fn create_in_npm_pkg_checker( - npm_resolver: &Arc, -) -> Arc { - 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>) -> 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, + 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) { + self.services.npm_resolver = Some(npm_resolver); + } + + pub fn npm_resolver(&self) -> Option<&Arc> { + self.services.npm_resolver.as_ref() + } + + pub fn cli_resolver(&self) -> &Arc { + 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 { + &self.pkg_json_resolver + } + + pub fn in_npm_pkg_checker(&self) -> &Arc { + 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, - in_npm_pkg_checker: Arc, - npm_resolver: &Arc, - pkg_json_resolver: Arc, -) -> Arc { - 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> { + 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>, - node_resolver: Option<&Arc>, -) -> Arc { - 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> { + 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)] -- cgit v1.2.3 From 3f26310728ef2d56ace7370a555eb9a862295983 Mon Sep 17 00:00:00 2001 From: Nayeem Rahman Date: Fri, 15 Nov 2024 14:40:32 +0000 Subject: feat(lsp): auto-imports with @deno-types directives (#26821) Co-authored-by: David Sherret --- cli/lsp/resolver.rs | 49 ++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 44 insertions(+), 5 deletions(-) (limited to 'cli/lsp/resolver.rs') diff --git a/cli/lsp/resolver.rs b/cli/lsp/resolver.rs index 399b89638..4ede79922 100644 --- a/cli/lsp/resolver.rs +++ b/cli/lsp/resolver.rs @@ -7,6 +7,7 @@ use deno_cache_dir::HttpCache; use deno_config::deno_json::JsxImportSourceConfig; use deno_config::workspace::PackageJsonDepResolution; use deno_config::workspace::WorkspaceResolver; +use deno_core::parking_lot::Mutex; use deno_core::url::Url; use deno_graph::source::ResolutionMode; use deno_graph::GraphImport; @@ -84,6 +85,7 @@ struct LspScopeResolver { pkg_json_resolver: Arc, redirect_resolver: Option>, graph_imports: Arc>, + dep_info: Arc>>, package_json_deps_by_resolution: Arc>, config_data: Option>, } @@ -101,6 +103,7 @@ impl Default for LspScopeResolver { pkg_json_resolver: factory.pkg_json_resolver().clone(), redirect_resolver: None, graph_imports: Default::default(), + dep_info: Default::default(), package_json_deps_by_resolution: Default::default(), config_data: None, } @@ -180,6 +183,15 @@ impl LspScopeResolver { NodeModuleKind::Esm, NodeResolutionMode::Types, ) + .or_else(|_| { + npm_pkg_req_resolver.resolve_req_reference( + &req_ref, + &referrer, + // todo(dsherret): this is wrong because it doesn't consider CJS referrers + NodeModuleKind::Esm, + NodeResolutionMode::Execution, + ) + }) .ok()?, )) .0; @@ -200,6 +212,7 @@ impl LspScopeResolver { pkg_json_resolver, redirect_resolver, graph_imports, + dep_info: Default::default(), package_json_deps_by_resolution, config_data: config_data.cloned(), } @@ -222,6 +235,7 @@ impl LspScopeResolver { redirect_resolver: self.redirect_resolver.clone(), pkg_json_resolver: factory.pkg_json_resolver().clone(), graph_imports: self.graph_imports.clone(), + dep_info: self.dep_info.clone(), package_json_deps_by_resolution: self .package_json_deps_by_resolution .clone(), @@ -288,19 +302,24 @@ impl LspResolver { } } - pub async fn set_npm_reqs( + pub async fn set_dep_info_by_scope( &self, - reqs: &BTreeMap, BTreeSet>, + dep_info_by_scope: &Arc< + BTreeMap, Arc>, + >, ) { for (scope, resolver) in [(None, &self.unscoped)] .into_iter() .chain(self.by_scope.iter().map(|(s, r)| (Some(s), r))) { + let dep_info = dep_info_by_scope.get(&scope.cloned()); + if let Some(dep_info) = dep_info { + *resolver.dep_info.lock() = dep_info.clone(); + } 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::>()) + let reqs = dep_info + .map(|i| i.npm_reqs.iter().cloned().collect::>()) .unwrap_or_default(); if let Err(err) = npm_resolver.set_package_reqs(&reqs).await { lsp_warn!("Could not set npm package requirements: {:#}", err); @@ -434,6 +453,19 @@ impl LspResolver { .cloned() } + pub fn deno_types_to_code_resolution( + &self, + specifier: &ModuleSpecifier, + file_referrer: Option<&ModuleSpecifier>, + ) -> Option { + let resolver = self.get_scope_resolver(file_referrer); + let dep_info = resolver.dep_info.lock().clone(); + dep_info + .deno_types_to_code_resolutions + .get(specifier) + .cloned() + } + pub fn in_node_modules(&self, specifier: &ModuleSpecifier) -> bool { fn has_node_modules_dir(specifier: &ModuleSpecifier) -> bool { // consider any /node_modules/ directory as being in the node_modules @@ -538,6 +570,13 @@ impl LspResolver { } } +#[derive(Debug, Default, Clone)] +pub struct ScopeDepInfo { + pub deno_types_to_code_resolutions: HashMap, + pub npm_reqs: BTreeSet, + pub has_node_specifier: bool, +} + #[derive(Default)] struct ResolverFactoryServices { cli_resolver: Deferred>, -- cgit v1.2.3