summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock2
-rw-r--r--Cargo.toml1
-rw-r--r--cli/Cargo.toml4
-rw-r--r--cli/factory.rs94
-rw-r--r--cli/lsp/analysis.rs5
-rw-r--r--cli/lsp/resolver.rs427
-rw-r--r--cli/main.rs2
-rw-r--r--cli/module_loader.rs36
-rw-r--r--cli/node.rs13
-rw-r--r--cli/npm/byonm.rs30
-rw-r--r--cli/npm/managed/mod.rs41
-rw-r--r--cli/npm/mod.rs32
-rw-r--r--cli/resolver.rs769
-rw-r--r--cli/standalone/mod.rs47
-rw-r--r--cli/tsc/mod.rs2
-rw-r--r--cli/worker.rs2
-rw-r--r--ext/node/lib.rs4
-rw-r--r--ext/node/ops/require.rs4
-rw-r--r--resolvers/deno/Cargo.toml2
-rw-r--r--resolvers/deno/cjs.rs272
-rw-r--r--resolvers/deno/fs.rs1
-rw-r--r--resolvers/deno/lib.rs434
-rw-r--r--resolvers/deno/npm/byonm.rs23
-rw-r--r--resolvers/deno/npm/mod.rs250
-rw-r--r--resolvers/node/analyze.rs6
-rw-r--r--resolvers/node/lib.rs5
-rw-r--r--resolvers/node/npm.rs9
-rw-r--r--resolvers/node/resolution.rs51
28 files changed, 1520 insertions, 1048 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 2c25c0de0..87265c02d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2007,6 +2007,8 @@ version = "0.9.0"
dependencies = [
"anyhow",
"base32",
+ "dashmap",
+ "deno_config",
"deno_media_type",
"deno_package_json",
"deno_path_util",
diff --git a/Cargo.toml b/Cargo.toml
index f384b9255..4a78e7e46 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -49,6 +49,7 @@ deno_ast = { version = "=0.43.3", features = ["transpiling"] }
deno_core = { version = "0.319.0" }
deno_bench_util = { version = "0.171.0", path = "./bench_util" }
+deno_config = { version = "=0.38.2", features = ["workspace", "sync"] }
deno_lockfile = "=0.23.1"
deno_media_type = { version = "0.2.0", features = ["module_specifier"] }
deno_npm = "=0.25.4"
diff --git a/cli/Cargo.toml b/cli/Cargo.toml
index 928259a83..374f3dae5 100644
--- a/cli/Cargo.toml
+++ b/cli/Cargo.toml
@@ -69,8 +69,8 @@ winres.workspace = true
[dependencies]
deno_ast = { workspace = true, features = ["bundler", "cjs", "codegen", "proposal", "react", "sourcemap", "transforms", "typescript", "view", "visit"] }
-deno_cache_dir = { workspace = true }
-deno_config = { version = "=0.38.2", features = ["workspace", "sync"] }
+deno_cache_dir.workspace = true
+deno_config.workspace = true
deno_core = { workspace = true, features = ["include_js_files_for_snapshotting"] }
deno_doc = { version = "0.156.0", default-features = false, features = ["rust", "html", "syntect"] }
deno_graph = { version = "=0.84.1" }
diff --git a/cli/factory.rs b/cli/factory.rs
index 5cb2dd7b3..7949a83a5 100644
--- a/cli/factory.rs
+++ b/cli/factory.rs
@@ -42,8 +42,9 @@ use crate::npm::CliNpmResolverCreateOptions;
use crate::npm::CliNpmResolverManagedSnapshotOption;
use crate::npm::CreateInNpmPkgCheckerOptions;
use crate::resolver::CjsTracker;
+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::CliSloppyImportsResolver;
@@ -71,6 +72,9 @@ use deno_core::error::AnyError;
use deno_core::futures::FutureExt;
use deno_core::FeatureChecker;
+use deno_resolver::npm::NpmReqResolverOptions;
+use deno_resolver::DenoResolverOptions;
+use deno_resolver::NodeAndNpmReqResolver;
use deno_runtime::deno_fs;
use deno_runtime::deno_node::DenoFsNodeResolverEnv;
use deno_runtime::deno_node::NodeResolver;
@@ -126,7 +130,7 @@ impl RootCertStoreProvider for CliRootCertStoreProvider {
}
}
-struct Deferred<T>(once_cell::unsync::OnceCell<T>);
+pub struct Deferred<T>(once_cell::unsync::OnceCell<T>);
impl<T> Default for Deferred<T> {
fn default() -> Self {
@@ -175,9 +179,9 @@ struct CliFactoryServices {
blob_store: Deferred<Arc<BlobStore>>,
caches: Deferred<Arc<Caches>>,
cjs_tracker: Deferred<Arc<CjsTracker>>,
- cli_node_resolver: Deferred<Arc<CliNodeResolver>>,
cli_options: Deferred<Arc<CliOptions>>,
code_cache: Deferred<Arc<CodeCache>>,
+ deno_resolver: Deferred<Arc<CliDenoResolver>>,
emit_cache: Deferred<Arc<EmitCache>>,
emitter: Deferred<Arc<Emitter>>,
feature_checker: Deferred<Arc<FeatureChecker>>,
@@ -197,6 +201,7 @@ struct CliFactoryServices {
node_code_translator: Deferred<Arc<CliNodeCodeTranslator>>,
node_resolver: Deferred<Arc<NodeResolver>>,
npm_cache_dir: Deferred<Arc<NpmCacheDir>>,
+ npm_req_resolver: Deferred<Arc<CliNpmReqResolver>>,
npm_resolver: Deferred<Arc<dyn CliNpmResolver>>,
parsed_source_cache: Deferred<Arc<ParsedSourceCache>>,
permission_desc_parser: Deferred<Arc<RuntimePermissionDescriptorParser>>,
@@ -523,6 +528,31 @@ impl CliFactory {
.await
}
+ pub async fn deno_resolver(&self) -> Result<&Arc<CliDenoResolver>, AnyError> {
+ self
+ .services
+ .deno_resolver
+ .get_or_try_init_async(async {
+ let cli_options = self.cli_options()?;
+ Ok(Arc::new(CliDenoResolver::new(DenoResolverOptions {
+ in_npm_pkg_checker: self.in_npm_pkg_checker()?.clone(),
+ node_and_req_resolver: if cli_options.no_npm() {
+ None
+ } else {
+ Some(NodeAndNpmReqResolver {
+ node_resolver: self.node_resolver().await?.clone(),
+ npm_req_resolver: self.npm_req_resolver().await?.clone(),
+ })
+ },
+ sloppy_imports_resolver: self.sloppy_imports_resolver()?.cloned(),
+ workspace_resolver: self.workspace_resolver().await?.clone(),
+ is_byonm: cli_options.use_byonm(),
+ maybe_vendor_dir: cli_options.vendor_dir_path(),
+ })))
+ })
+ .await
+ }
+
pub async fn resolver(&self) -> Result<&Arc<CliResolver>, AnyError> {
self
.services
@@ -531,17 +561,14 @@ impl CliFactory {
async {
let cli_options = self.cli_options()?;
Ok(Arc::new(CliResolver::new(CliResolverOptions {
- sloppy_imports_resolver: self.sloppy_imports_resolver()?.cloned(),
- node_resolver: Some(self.cli_node_resolver().await?.clone()),
npm_resolver: if cli_options.no_npm() {
None
} else {
Some(self.npm_resolver().await?.clone())
},
- workspace_resolver: self.workspace_resolver().await?.clone(),
bare_node_builtins_enabled: cli_options
.unstable_bare_node_builtins(),
- maybe_vendor_dir: cli_options.vendor_dir_path(),
+ deno_resolver: self.deno_resolver().await?.clone(),
})))
}
.boxed_local(),
@@ -624,7 +651,11 @@ impl CliFactory {
Ok(Arc::new(NodeResolver::new(
DenoFsNodeResolverEnv::new(self.fs().clone()),
self.in_npm_pkg_checker()?.clone(),
- self.npm_resolver().await?.clone().into_npm_resolver(),
+ self
+ .npm_resolver()
+ .await?
+ .clone()
+ .into_npm_pkg_folder_resolver(),
self.pkg_json_resolver().clone(),
)))
}
@@ -656,13 +687,36 @@ impl CliFactory {
DenoFsNodeResolverEnv::new(self.fs().clone()),
self.in_npm_pkg_checker()?.clone(),
node_resolver,
- self.npm_resolver().await?.clone().into_npm_resolver(),
+ self
+ .npm_resolver()
+ .await?
+ .clone()
+ .into_npm_pkg_folder_resolver(),
self.pkg_json_resolver().clone(),
)))
})
.await
}
+ pub async fn npm_req_resolver(
+ &self,
+ ) -> Result<&Arc<CliNpmReqResolver>, AnyError> {
+ self
+ .services
+ .npm_req_resolver
+ .get_or_try_init_async(async {
+ let npm_resolver = self.npm_resolver().await?;
+ Ok(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: self.node_resolver().await?.clone(),
+ npm_req_resolver: npm_resolver.clone().into_npm_req_resolver(),
+ })))
+ })
+ .await
+ }
+
pub fn pkg_json_resolver(&self) -> &Arc<PackageJsonResolver> {
self.services.pkg_json_resolver.get_or_init(|| {
Arc::new(PackageJsonResolver::new(DenoFsNodeResolverEnv::new(
@@ -799,23 +853,6 @@ impl CliFactory {
})
}
- pub async fn cli_node_resolver(
- &self,
- ) -> Result<&Arc<CliNodeResolver>, AnyError> {
- self
- .services
- .cli_node_resolver
- .get_or_try_init_async(async {
- Ok(Arc::new(CliNodeResolver::new(
- self.fs().clone(),
- self.in_npm_pkg_checker()?.clone(),
- self.node_resolver().await?.clone(),
- self.npm_resolver().await?.clone(),
- )))
- })
- .await
- }
-
pub fn permission_desc_parser(
&self,
) -> Result<&Arc<RuntimePermissionDescriptorParser>, AnyError> {
@@ -880,7 +917,6 @@ impl CliFactory {
let fs = self.fs();
let node_resolver = self.node_resolver().await?;
let npm_resolver = self.npm_resolver().await?;
- let cli_node_resolver = self.cli_node_resolver().await?;
let cli_npm_resolver = self.npm_resolver().await?.clone();
let in_npm_pkg_checker = self.in_npm_pkg_checker()?;
let maybe_file_watcher_communicator = if cli_options.has_hmr() {
@@ -891,6 +927,7 @@ impl CliFactory {
let node_code_translator = self.node_code_translator().await?;
let cjs_tracker = self.cjs_tracker()?.clone();
let pkg_json_resolver = self.pkg_json_resolver().clone();
+ let npm_req_resolver = self.npm_req_resolver().await?;
Ok(CliMainWorkerFactory::new(
self.blob_store().clone(),
@@ -918,7 +955,8 @@ impl CliFactory {
self.main_module_graph_container().await?.clone(),
self.module_load_preparer().await?.clone(),
node_code_translator.clone(),
- cli_node_resolver.clone(),
+ node_resolver.clone(),
+ npm_req_resolver.clone(),
cli_npm_resolver.clone(),
NpmModuleLoader::new(
self.cjs_tracker()?.clone(),
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)]
diff --git a/cli/main.rs b/cli/main.rs
index 20d2cb6bf..7d3ef0e6a 100644
--- a/cli/main.rs
+++ b/cli/main.rs
@@ -37,6 +37,7 @@ use crate::util::v8::init_v8_flags;
use args::TaskFlags;
use deno_resolver::npm::ByonmResolvePkgFolderFromDenoReqError;
+use deno_resolver::npm::ResolvePkgFolderFromDenoReqError;
use deno_runtime::WorkerExecutionMode;
pub use deno_runtime::UNSTABLE_GRANULAR_FLAGS;
@@ -50,7 +51,6 @@ use deno_runtime::fmt_errors::format_js_error;
use deno_runtime::tokio_util::create_and_run_current_thread_with_maybe_metrics;
use deno_terminal::colors;
use factory::CliFactory;
-use npm::ResolvePkgFolderFromDenoReqError;
use standalone::MODULE_NOT_FOUND;
use standalone::UNSUPPORTED_SCHEME;
use std::env;
diff --git a/cli/module_loader.rs b/cli/module_loader.rs
index f9c974d77..b9adfe642 100644
--- a/cli/module_loader.rs
+++ b/cli/module_loader.rs
@@ -27,7 +27,7 @@ use crate::node;
use crate::node::CliNodeCodeTranslator;
use crate::npm::CliNpmResolver;
use crate::resolver::CjsTracker;
-use crate::resolver::CliNodeResolver;
+use crate::resolver::CliNpmReqResolver;
use crate::resolver::CliResolver;
use crate::resolver::ModuleCodeStringSource;
use crate::resolver::NotSupportedKindInNpmError;
@@ -70,6 +70,7 @@ use deno_runtime::code_cache;
use deno_runtime::deno_fs::FileSystem;
use deno_runtime::deno_node::create_host_defined_options;
use deno_runtime::deno_node::NodeRequireLoader;
+use deno_runtime::deno_node::NodeResolver;
use deno_runtime::deno_permissions::PermissionsContainer;
use deno_semver::npm::NpmPackageReqReference;
use node_resolver::errors::ClosestPkgJsonError;
@@ -215,7 +216,8 @@ struct SharedCliModuleLoaderState {
main_module_graph_container: Arc<MainModuleGraphContainer>,
module_load_preparer: Arc<ModuleLoadPreparer>,
node_code_translator: Arc<CliNodeCodeTranslator>,
- node_resolver: Arc<CliNodeResolver>,
+ node_resolver: Arc<NodeResolver>,
+ npm_req_resolver: Arc<CliNpmReqResolver>,
npm_resolver: Arc<dyn CliNpmResolver>,
npm_module_loader: NpmModuleLoader,
parsed_source_cache: Arc<ParsedSourceCache>,
@@ -238,7 +240,8 @@ impl CliModuleLoaderFactory {
main_module_graph_container: Arc<MainModuleGraphContainer>,
module_load_preparer: Arc<ModuleLoadPreparer>,
node_code_translator: Arc<CliNodeCodeTranslator>,
- node_resolver: Arc<CliNodeResolver>,
+ node_resolver: Arc<NodeResolver>,
+ npm_req_resolver: Arc<CliNpmReqResolver>,
npm_resolver: Arc<dyn CliNpmResolver>,
npm_module_loader: NpmModuleLoader,
parsed_source_cache: Arc<ParsedSourceCache>,
@@ -264,6 +267,7 @@ impl CliModuleLoaderFactory {
module_load_preparer,
node_code_translator,
node_resolver,
+ npm_req_resolver,
npm_resolver,
npm_module_loader,
parsed_source_cache,
@@ -425,7 +429,7 @@ impl<TGraphContainer: ModuleGraphContainer>
if let Some(code_source) = self.load_prepared_module(specifier).await? {
return Ok(code_source);
}
- if self.shared.node_resolver.in_npm_package(specifier) {
+ if self.shared.in_npm_pkg_checker.in_npm_package(specifier) {
return self
.shared
.npm_module_loader
@@ -470,7 +474,7 @@ impl<TGraphContainer: ModuleGraphContainer>
raw_specifier: &str,
referrer: &ModuleSpecifier,
) -> Result<ModuleSpecifier, AnyError> {
- if self.shared.node_resolver.in_npm_package(referrer) {
+ if self.shared.in_npm_pkg_checker.in_npm_package(referrer) {
return Ok(
self
.shared
@@ -518,12 +522,16 @@ impl<TGraphContainer: ModuleGraphContainer>
if self.shared.is_repl {
if let Ok(reference) = NpmPackageReqReference::from_specifier(&specifier)
{
- return self.shared.node_resolver.resolve_req_reference(
- &reference,
- referrer,
- self.shared.cjs_tracker.get_referrer_kind(referrer),
- NodeResolutionMode::Execution,
- );
+ return self
+ .shared
+ .npm_req_resolver
+ .resolve_req_reference(
+ &reference,
+ referrer,
+ self.shared.cjs_tracker.get_referrer_kind(referrer),
+ NodeResolutionMode::Execution,
+ )
+ .map_err(AnyError::from);
}
}
@@ -538,7 +546,7 @@ impl<TGraphContainer: ModuleGraphContainer>
self
.shared
.node_resolver
- .resolve_package_sub_path_from_deno_module(
+ .resolve_package_subpath_from_deno_module(
&package_folder,
module.nv_reference.sub_path(),
Some(referrer),
@@ -828,7 +836,7 @@ impl<TGraphContainer: ModuleGraphContainer> ModuleLoader
name: &str,
) -> Option<deno_core::v8::Local<'s, deno_core::v8::Data>> {
let name = deno_core::ModuleSpecifier::parse(name).ok()?;
- if self.0.shared.node_resolver.in_npm_package(&name) {
+ if self.0.shared.in_npm_pkg_checker.in_npm_package(&name) {
Some(create_host_defined_options(scope))
} else {
None
@@ -865,7 +873,7 @@ impl<TGraphContainer: ModuleGraphContainer> ModuleLoader
_maybe_referrer: Option<String>,
is_dynamic: bool,
) -> Pin<Box<dyn Future<Output = Result<(), AnyError>>>> {
- if self.0.shared.node_resolver.in_npm_package(specifier) {
+ if self.0.shared.in_npm_pkg_checker.in_npm_package(specifier) {
return Box::pin(deno_core::futures::future::ready(Ok(())));
}
diff --git a/cli/node.rs b/cli/node.rs
index 8235745a9..bc39cdbde 100644
--- a/cli/node.rs
+++ b/cli/node.rs
@@ -7,8 +7,6 @@ use deno_ast::MediaType;
use deno_ast::ModuleSpecifier;
use deno_core::error::AnyError;
use deno_graph::ParsedSourceStore;
-use deno_path_util::url_from_file_path;
-use deno_path_util::url_to_file_path;
use deno_runtime::deno_fs;
use deno_runtime::deno_node::DenoFsNodeResolverEnv;
use node_resolver::analyze::CjsAnalysis as ExtNodeCjsAnalysis;
@@ -22,7 +20,6 @@ use crate::cache::CacheDBHash;
use crate::cache::NodeAnalysisCache;
use crate::cache::ParsedSourceCache;
use crate::resolver::CjsTracker;
-use crate::util::fs::canonicalize_path_maybe_not_exists_with_fs;
pub type CliNodeCodeTranslator =
NodeCodeTranslator<CliCjsCodeAnalyzer, DenoFsNodeResolverEnv>;
@@ -37,13 +34,9 @@ pub fn resolve_specifier_into_node_modules(
specifier: &ModuleSpecifier,
fs: &dyn deno_fs::FileSystem,
) -> ModuleSpecifier {
- url_to_file_path(specifier)
- .ok()
- // this path might not exist at the time the graph is being created
- // because the node_modules folder might not yet exist
- .and_then(|path| canonicalize_path_maybe_not_exists_with_fs(&path, fs).ok())
- .and_then(|path| url_from_file_path(&path).ok())
- .unwrap_or_else(|| specifier.clone())
+ node_resolver::resolve_specifier_into_node_modules(specifier, &|path| {
+ fs.realpath_sync(path).map_err(|err| err.into_io_error())
+ })
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
diff --git a/cli/npm/byonm.rs b/cli/npm/byonm.rs
index 45fa4cfd1..eca399251 100644
--- a/cli/npm/byonm.rs
+++ b/cli/npm/byonm.rs
@@ -2,19 +2,17 @@
use std::borrow::Cow;
use std::path::Path;
-use std::path::PathBuf;
use std::sync::Arc;
use deno_core::error::AnyError;
use deno_core::serde_json;
-use deno_core::url::Url;
use deno_resolver::npm::ByonmNpmResolver;
use deno_resolver::npm::ByonmNpmResolverCreateOptions;
+use deno_resolver::npm::CliNpmReqResolver;
use deno_runtime::deno_node::DenoFsNodeResolverEnv;
use deno_runtime::deno_node::NodePermissions;
use deno_runtime::ops::process::NpmProcessStateProvider;
-use deno_semver::package::PackageReq;
-use node_resolver::NpmResolver;
+use node_resolver::NpmPackageFolderResolver;
use crate::args::NpmProcessState;
use crate::args::NpmProcessStateKind;
@@ -22,7 +20,6 @@ use crate::resolver::CliDenoResolverFs;
use super::CliNpmResolver;
use super::InnerCliNpmResolverRef;
-use super::ResolvePkgFolderFromDenoReqError;
pub type CliByonmNpmResolverCreateOptions =
ByonmNpmResolverCreateOptions<CliDenoResolverFs, DenoFsNodeResolverEnv>;
@@ -47,7 +44,13 @@ impl NpmProcessStateProvider for CliByonmWrapper {
}
impl CliNpmResolver for CliByonmNpmResolver {
- fn into_npm_resolver(self: Arc<Self>) -> Arc<dyn NpmResolver> {
+ fn into_npm_pkg_folder_resolver(
+ self: Arc<Self>,
+ ) -> Arc<dyn NpmPackageFolderResolver> {
+ self
+ }
+
+ fn into_npm_req_resolver(self: Arc<Self>) -> Arc<dyn CliNpmReqResolver> {
self
}
@@ -57,6 +60,10 @@ impl CliNpmResolver for CliByonmNpmResolver {
Arc::new(CliByonmWrapper(self))
}
+ fn into_maybe_byonm(self: Arc<Self>) -> Option<Arc<CliByonmNpmResolver>> {
+ Some(self)
+ }
+
fn clone_snapshotted(&self) -> Arc<dyn CliNpmResolver> {
Arc::new(self.clone())
}
@@ -69,17 +76,6 @@ impl CliNpmResolver for CliByonmNpmResolver {
self.root_node_modules_dir()
}
- fn resolve_pkg_folder_from_deno_module_req(
- &self,
- req: &PackageReq,
- referrer: &Url,
- ) -> Result<PathBuf, ResolvePkgFolderFromDenoReqError> {
- ByonmNpmResolver::resolve_pkg_folder_from_deno_module_req(
- self, req, referrer,
- )
- .map_err(ResolvePkgFolderFromDenoReqError::Byonm)
- }
-
fn ensure_read_permission<'a>(
&self,
permissions: &mut dyn NodePermissions,
diff --git a/cli/npm/managed/mod.rs b/cli/npm/managed/mod.rs
index 4a91bc347..2e64f5f18 100644
--- a/cli/npm/managed/mod.rs
+++ b/cli/npm/managed/mod.rs
@@ -22,6 +22,7 @@ use deno_npm::resolution::ValidSerializedNpmResolutionSnapshot;
use deno_npm::NpmPackageId;
use deno_npm::NpmResolutionPackage;
use deno_npm::NpmSystemInfo;
+use deno_resolver::npm::CliNpmReqResolver;
use deno_runtime::colors;
use deno_runtime::deno_fs::FileSystem;
use deno_runtime::deno_node::NodePermissions;
@@ -31,7 +32,7 @@ use deno_semver::package::PackageReq;
use node_resolver::errors::PackageFolderResolveError;
use node_resolver::errors::PackageFolderResolveIoError;
use node_resolver::InNpmPackageChecker;
-use node_resolver::NpmResolver;
+use node_resolver::NpmPackageFolderResolver;
use resolution::AddPkgReqsResult;
use crate::args::CliLockfile;
@@ -605,7 +606,7 @@ fn npm_process_state(
.unwrap()
}
-impl NpmResolver for ManagedCliNpmResolver {
+impl NpmPackageFolderResolver for ManagedCliNpmResolver {
fn resolve_package_folder_from_package(
&self,
name: &str,
@@ -635,8 +636,29 @@ impl NpmProcessStateProvider for ManagedCliNpmResolver {
}
}
+impl CliNpmReqResolver for ManagedCliNpmResolver {
+ fn resolve_pkg_folder_from_deno_module_req(
+ &self,
+ req: &PackageReq,
+ _referrer: &ModuleSpecifier,
+ ) -> Result<PathBuf, ResolvePkgFolderFromDenoReqError> {
+ let pkg_id = self
+ .resolve_pkg_id_from_pkg_req(req)
+ .map_err(|err| ResolvePkgFolderFromDenoReqError::Managed(err.into()))?;
+ self
+ .resolve_pkg_folder_from_pkg_id(&pkg_id)
+ .map_err(ResolvePkgFolderFromDenoReqError::Managed)
+ }
+}
+
impl CliNpmResolver for ManagedCliNpmResolver {
- fn into_npm_resolver(self: Arc<Self>) -> Arc<dyn NpmResolver> {
+ fn into_npm_pkg_folder_resolver(
+ self: Arc<Self>,
+ ) -> Arc<dyn NpmPackageFolderResolver> {
+ self
+ }
+
+ fn into_npm_req_resolver(self: Arc<Self>) -> Arc<dyn CliNpmReqResolver> {
self
}
@@ -687,19 +709,6 @@ impl CliNpmResolver for ManagedCliNpmResolver {
self.fs_resolver.node_modules_path()
}
- fn resolve_pkg_folder_from_deno_module_req(
- &self,
- req: &PackageReq,
- _referrer: &ModuleSpecifier,
- ) -> Result<PathBuf, ResolvePkgFolderFromDenoReqError> {
- let pkg_id = self
- .resolve_pkg_id_from_pkg_req(req)
- .map_err(|err| ResolvePkgFolderFromDenoReqError::Managed(err.into()))?;
- self
- .resolve_pkg_folder_from_pkg_id(&pkg_id)
- .map_err(ResolvePkgFolderFromDenoReqError::Managed)
- }
-
fn ensure_read_permission<'a>(
&self,
permissions: &mut dyn NodePermissions,
diff --git a/cli/npm/mod.rs b/cli/npm/mod.rs
index 0d434ca27..0e955ac5b 100644
--- a/cli/npm/mod.rs
+++ b/cli/npm/mod.rs
@@ -6,19 +6,18 @@ mod managed;
use std::borrow::Cow;
use std::path::Path;
-use std::path::PathBuf;
use std::sync::Arc;
use common::maybe_auth_header_for_npm_registry;
use dashmap::DashMap;
-use deno_ast::ModuleSpecifier;
use deno_core::error::AnyError;
use deno_core::serde_json;
use deno_npm::npm_rc::ResolvedNpmRc;
use deno_npm::registry::NpmPackageInfo;
use deno_resolver::npm::ByonmInNpmPackageChecker;
use deno_resolver::npm::ByonmNpmResolver;
-use deno_resolver::npm::ByonmResolvePkgFolderFromDenoReqError;
+use deno_resolver::npm::CliNpmReqResolver;
+use deno_resolver::npm::ResolvePkgFolderFromDenoReqError;
use deno_runtime::deno_node::NodePermissions;
use deno_runtime::ops::process::NpmProcessStateProvider;
use deno_semver::package::PackageNv;
@@ -26,8 +25,7 @@ use deno_semver::package::PackageReq;
use managed::cache::registry_info::get_package_url;
use managed::create_managed_in_npm_pkg_checker;
use node_resolver::InNpmPackageChecker;
-use node_resolver::NpmResolver;
-use thiserror::Error;
+use node_resolver::NpmPackageFolderResolver;
use crate::file_fetcher::FileFetcher;
@@ -38,14 +36,6 @@ pub use self::managed::CliManagedNpmResolverCreateOptions;
pub use self::managed::CliNpmResolverManagedSnapshotOption;
pub use self::managed::ManagedCliNpmResolver;
-#[derive(Debug, Error)]
-pub enum ResolvePkgFolderFromDenoReqError {
- #[error(transparent)]
- Managed(deno_core::error::AnyError),
- #[error(transparent)]
- Byonm(#[from] ByonmResolvePkgFolderFromDenoReqError),
-}
-
pub enum CliNpmResolverCreateOptions {
Managed(CliManagedNpmResolverCreateOptions),
Byonm(CliByonmNpmResolverCreateOptions),
@@ -95,11 +85,17 @@ pub enum InnerCliNpmResolverRef<'a> {
Byonm(&'a CliByonmNpmResolver),
}
-pub trait CliNpmResolver: NpmResolver {
- fn into_npm_resolver(self: Arc<Self>) -> Arc<dyn NpmResolver>;
+pub trait CliNpmResolver: NpmPackageFolderResolver + CliNpmReqResolver {
+ fn into_npm_pkg_folder_resolver(
+ self: Arc<Self>,
+ ) -> Arc<dyn NpmPackageFolderResolver>;
+ fn into_npm_req_resolver(self: Arc<Self>) -> Arc<dyn CliNpmReqResolver>;
fn into_process_state_provider(
self: Arc<Self>,
) -> Arc<dyn NpmProcessStateProvider>;
+ fn into_maybe_byonm(self: Arc<Self>) -> Option<Arc<CliByonmNpmResolver>> {
+ None
+ }
fn clone_snapshotted(&self) -> Arc<dyn CliNpmResolver>;
@@ -121,12 +117,6 @@ pub trait CliNpmResolver: NpmResolver {
fn root_node_modules_path(&self) -> Option<&Path>;
- fn resolve_pkg_folder_from_deno_module_req(
- &self,
- req: &PackageReq,
- referrer: &ModuleSpecifier,
- ) -> Result<PathBuf, ResolvePkgFolderFromDenoReqError>;
-
fn ensure_read_permission<'a>(
&self,
permissions: &mut dyn NodePermissions,
diff --git a/cli/resolver.rs b/cli/resolver.rs
index 786e5d0db..a2dd47430 100644
--- a/cli/resolver.rs
+++ b/cli/resolver.rs
@@ -4,10 +4,8 @@ use async_trait::async_trait;
use dashmap::DashMap;
use dashmap::DashSet;
use deno_ast::MediaType;
-use deno_config::workspace::MappedResolution;
use deno_config::workspace::MappedResolutionDiagnostic;
use deno_config::workspace::MappedResolutionError;
-use deno_config::workspace::WorkspaceResolver;
use deno_core::anyhow::anyhow;
use deno_core::anyhow::Context;
use deno_core::error::AnyError;
@@ -20,28 +18,14 @@ use deno_graph::source::UnknownBuiltInNodeModuleError;
use deno_graph::NpmLoadError;
use deno_graph::NpmResolvePkgReqsResult;
use deno_npm::resolution::NpmResolutionError;
-use deno_package_json::PackageJsonDepValue;
-use deno_resolver::sloppy_imports::SloppyImportsResolutionMode;
use deno_resolver::sloppy_imports::SloppyImportsResolver;
use deno_runtime::colors;
use deno_runtime::deno_fs;
use deno_runtime::deno_fs::FileSystem;
use deno_runtime::deno_node::is_builtin_node_module;
-use deno_runtime::deno_node::NodeResolver;
-use deno_runtime::deno_node::PackageJsonResolver;
-use deno_semver::npm::NpmPackageReqReference;
+use deno_runtime::deno_node::DenoFsNodeResolverEnv;
use deno_semver::package::PackageReq;
-use node_resolver::errors::ClosestPkgJsonError;
-use node_resolver::errors::NodeResolveError;
-use node_resolver::errors::NodeResolveErrorKind;
-use node_resolver::errors::PackageFolderResolveErrorKind;
-use node_resolver::errors::PackageFolderResolveIoError;
-use node_resolver::errors::PackageNotFoundError;
-use node_resolver::errors::PackageResolveErrorKind;
-use node_resolver::errors::PackageSubpathResolveError;
-use node_resolver::InNpmPackageChecker;
use node_resolver::NodeModuleKind;
-use node_resolver::NodeResolution;
use node_resolver::NodeResolutionMode;
use std::borrow::Cow;
use std::path::Path;
@@ -56,6 +40,20 @@ use crate::npm::InnerCliNpmResolverRef;
use crate::util::sync::AtomicFlag;
use crate::util::text_encoding::from_utf8_lossy_owned;
+pub type CjsTracker = deno_resolver::cjs::CjsTracker<DenoFsNodeResolverEnv>;
+pub type IsCjsResolver =
+ deno_resolver::cjs::IsCjsResolver<DenoFsNodeResolverEnv>;
+pub type IsCjsResolverOptions = deno_resolver::cjs::IsCjsResolverOptions;
+pub type CliSloppyImportsResolver =
+ SloppyImportsResolver<SloppyImportsCachedFs>;
+pub type CliDenoResolver = deno_resolver::DenoResolver<
+ CliDenoResolverFs,
+ DenoFsNodeResolverEnv,
+ SloppyImportsCachedFs,
+>;
+pub type CliNpmReqResolver =
+ deno_resolver::npm::NpmReqResolver<CliDenoResolverFs, DenoFsNodeResolverEnv>;
+
pub struct ModuleCodeStringSource {
pub code: ModuleSourceCode,
pub found_url: ModuleSpecifier,
@@ -77,6 +75,10 @@ impl deno_resolver::fs::DenoResolverFs for CliDenoResolverFs {
self.0.realpath_sync(path).map_err(|e| e.into_io_error())
}
+ fn exists_sync(&self, path: &Path) -> bool {
+ self.0.exists_sync(path)
+ }
+
fn is_dir_sync(&self, path: &Path) -> bool {
self.0.is_dir_sync(path)
}
@@ -102,211 +104,6 @@ impl deno_resolver::fs::DenoResolverFs for CliDenoResolverFs {
}
}
-#[derive(Debug)]
-pub struct CliNodeResolver {
- fs: Arc<dyn deno_fs::FileSystem>,
- in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
- node_resolver: Arc<NodeResolver>,
- npm_resolver: Arc<dyn CliNpmResolver>,
-}
-
-impl CliNodeResolver {
- pub fn new(
- fs: Arc<dyn deno_fs::FileSystem>,
- in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
- node_resolver: Arc<NodeResolver>,
- npm_resolver: Arc<dyn CliNpmResolver>,
- ) -> Self {
- Self {
- fs,
- in_npm_pkg_checker,
- node_resolver,
- npm_resolver,
- }
- }
-
- pub fn in_npm_package(&self, specifier: &ModuleSpecifier) -> bool {
- self.in_npm_pkg_checker.in_npm_package(specifier)
- }
-
- pub fn resolve_if_for_npm_pkg(
- &self,
- specifier: &str,
- referrer: &ModuleSpecifier,
- referrer_kind: NodeModuleKind,
- mode: NodeResolutionMode,
- ) -> Result<Option<NodeResolution>, AnyError> {
- let resolution_result =
- self.resolve(specifier, referrer, referrer_kind, mode);
- match resolution_result {
- Ok(res) => Ok(Some(res)),
- Err(err) => {
- let err = err.into_kind();
- match err {
- NodeResolveErrorKind::RelativeJoin(_)
- | NodeResolveErrorKind::PackageImportsResolve(_)
- | NodeResolveErrorKind::UnsupportedEsmUrlScheme(_)
- | NodeResolveErrorKind::DataUrlReferrer(_)
- | NodeResolveErrorKind::TypesNotFound(_)
- | NodeResolveErrorKind::FinalizeResolution(_) => Err(err.into()),
- NodeResolveErrorKind::PackageResolve(err) => {
- let err = err.into_kind();
- match err {
- PackageResolveErrorKind::ClosestPkgJson(_)
- | PackageResolveErrorKind::InvalidModuleSpecifier(_)
- | PackageResolveErrorKind::ExportsResolve(_)
- | PackageResolveErrorKind::SubpathResolve(_) => Err(err.into()),
- PackageResolveErrorKind::PackageFolderResolve(err) => {
- match err.as_kind() {
- PackageFolderResolveErrorKind::Io(
- PackageFolderResolveIoError { package_name, .. },
- )
- | PackageFolderResolveErrorKind::PackageNotFound(
- PackageNotFoundError { package_name, .. },
- ) => {
- if self.in_npm_package(referrer) {
- return Err(err.into());
- }
- if let Some(byonm_npm_resolver) =
- self.npm_resolver.as_byonm()
- {
- if byonm_npm_resolver
- .find_ancestor_package_json_with_dep(
- package_name,
- referrer,
- )
- .is_some()
- {
- return Err(anyhow!(
- concat!(
- "Could not resolve \"{}\", but found it in a package.json. ",
- "Deno expects the node_modules/ directory to be up to date. ",
- "Did you forget to run `deno install`?"
- ),
- specifier
- ));
- }
- }
- Ok(None)
- }
- PackageFolderResolveErrorKind::ReferrerNotFound(_) => {
- if self.in_npm_package(referrer) {
- return Err(err.into());
- }
- Ok(None)
- }
- }
- }
- }
- }
- }
- }
- }
- }
-
- pub fn resolve(
- &self,
- specifier: &str,
- referrer: &ModuleSpecifier,
- referrer_kind: NodeModuleKind,
- mode: NodeResolutionMode,
- ) -> Result<NodeResolution, NodeResolveError> {
- self
- .node_resolver
- .resolve(specifier, referrer, referrer_kind, mode)
- }
-
- pub fn resolve_req_reference(
- &self,
- req_ref: &NpmPackageReqReference,
- referrer: &ModuleSpecifier,
- referrer_kind: NodeModuleKind,
- mode: NodeResolutionMode,
- ) -> Result<ModuleSpecifier, AnyError> {
- self.resolve_req_with_sub_path(
- req_ref.req(),
- req_ref.sub_path(),
- referrer,
- referrer_kind,
- mode,
- )
- }
-
- pub fn resolve_req_with_sub_path(
- &self,
- req: &PackageReq,
- sub_path: Option<&str>,
- referrer: &ModuleSpecifier,
- referrer_kind: NodeModuleKind,
- mode: NodeResolutionMode,
- ) -> Result<ModuleSpecifier, AnyError> {
- let package_folder = self
- .npm_resolver
- .resolve_pkg_folder_from_deno_module_req(req, referrer)?;
- let resolution_result = self.resolve_package_sub_path_from_deno_module(
- &package_folder,
- sub_path,
- Some(referrer),
- referrer_kind,
- mode,
- );
- match resolution_result {
- Ok(url) => Ok(url),
- Err(err) => {
- if self.npm_resolver.as_byonm().is_some() {
- let package_json_path = package_folder.join("package.json");
- if !self.fs.exists_sync(&package_json_path) {
- return Err(anyhow!(
- "Could not find '{}'. Deno expects the node_modules/ directory to be up to date. Did you forget to run `deno install`?",
- package_json_path.display(),
- ));
- }
- }
- Err(err.into())
- }
- }
- }
-
- pub fn resolve_package_sub_path_from_deno_module(
- &self,
- package_folder: &Path,
- sub_path: Option<&str>,
- maybe_referrer: Option<&ModuleSpecifier>,
- referrer_kind: NodeModuleKind,
- mode: NodeResolutionMode,
- ) -> Result<ModuleSpecifier, PackageSubpathResolveError> {
- self.node_resolver.resolve_package_subpath_from_deno_module(
- package_folder,
- sub_path,
- maybe_referrer,
- referrer_kind,
- mode,
- )
- }
-
- pub fn handle_if_in_node_modules(
- &self,
- specifier: &ModuleSpecifier,
- ) -> Result<Option<ModuleSpecifier>, AnyError> {
- // skip canonicalizing if we definitely know it's unnecessary
- if specifier.scheme() == "file"
- && specifier.path().contains("/node_modules/")
- {
- // Specifiers in the node_modules directory are canonicalized
- // so canoncalize then check if it's in the node_modules directory.
- // If so, check if we need to store this specifier as being a CJS
- // resolution.
- let specifier = crate::node::resolve_specifier_into_node_modules(
- specifier,
- self.fs.as_ref(),
- );
- return Ok(Some(specifier));
- }
-
- Ok(None)
- }
-}
-
#[derive(Debug, Error)]
#[error("{media_type} files are not supported in npm packages: {specifier}")]
pub struct NotSupportedKindInNpmError {
@@ -409,305 +206,36 @@ impl NpmModuleLoader {
}
}
-/// Keeps track of what module specifiers were resolved as CJS.
-///
-/// Modules that are `.js` or `.ts` are only known to be CJS or
-/// ESM after they're loaded based on their contents. So these files
-/// will be "maybe CJS" until they're loaded.
-#[derive(Debug)]
-pub struct CjsTracker {
- is_cjs_resolver: IsCjsResolver,
- known: DashMap<ModuleSpecifier, NodeModuleKind>,
-}
-
-impl CjsTracker {
- pub fn new(
- in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
- pkg_json_resolver: Arc<PackageJsonResolver>,
- options: IsCjsResolverOptions,
- ) -> Self {
- Self {
- is_cjs_resolver: IsCjsResolver::new(
- in_npm_pkg_checker,
- pkg_json_resolver,
- options,
- ),
- known: Default::default(),
- }
- }
-
- /// Checks whether the file might be treated as CJS, but it's not for sure
- /// yet because the source hasn't been loaded to see whether it contains
- /// imports or exports.
- pub fn is_maybe_cjs(
- &self,
- specifier: &ModuleSpecifier,
- media_type: MediaType,
- ) -> Result<bool, ClosestPkgJsonError> {
- self.treat_as_cjs_with_is_script(specifier, media_type, None)
- }
-
- /// Gets whether the file is CJS. If true, this is for sure
- /// cjs because `is_script` is provided.
- ///
- /// `is_script` should be `true` when the contents of the file at the
- /// provided specifier are known to be a script and not an ES module.
- pub fn is_cjs_with_known_is_script(
- &self,
- specifier: &ModuleSpecifier,
- media_type: MediaType,
- is_script: bool,
- ) -> Result<bool, ClosestPkgJsonError> {
- self.treat_as_cjs_with_is_script(specifier, media_type, Some(is_script))
- }
-
- fn treat_as_cjs_with_is_script(
- &self,
- specifier: &ModuleSpecifier,
- media_type: MediaType,
- is_script: Option<bool>,
- ) -> Result<bool, ClosestPkgJsonError> {
- let kind = match self
- .get_known_kind_with_is_script(specifier, media_type, is_script)
- {
- Some(kind) => kind,
- None => self.is_cjs_resolver.check_based_on_pkg_json(specifier)?,
- };
- Ok(kind == NodeModuleKind::Cjs)
- }
-
- pub fn get_known_kind(
- &self,
- specifier: &ModuleSpecifier,
- media_type: MediaType,
- ) -> Option<NodeModuleKind> {
- self.get_known_kind_with_is_script(specifier, media_type, None)
- }
-
- pub fn get_referrer_kind(
- &self,
- specifier: &ModuleSpecifier,
- ) -> NodeModuleKind {
- if specifier.scheme() != "file" {
- return NodeModuleKind::Esm;
- }
- self
- .get_known_kind(specifier, MediaType::from_specifier(specifier))
- .unwrap_or(NodeModuleKind::Esm)
- }
-
- fn get_known_kind_with_is_script(
- &self,
- specifier: &ModuleSpecifier,
- media_type: MediaType,
- is_script: Option<bool>,
- ) -> Option<NodeModuleKind> {
- self.is_cjs_resolver.get_known_kind_with_is_script(
- specifier,
- media_type,
- is_script,
- &self.known,
- )
- }
-}
-
-#[derive(Debug)]
-pub struct IsCjsResolverOptions {
- pub detect_cjs: bool,
- pub is_node_main: bool,
-}
-
-#[derive(Debug)]
-pub struct IsCjsResolver {
- in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
- pkg_json_resolver: Arc<PackageJsonResolver>,
- options: IsCjsResolverOptions,
-}
-
-impl IsCjsResolver {
- pub fn new(
- in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
- pkg_json_resolver: Arc<PackageJsonResolver>,
- options: IsCjsResolverOptions,
- ) -> Self {
- Self {
- in_npm_pkg_checker,
- pkg_json_resolver,
- options,
- }
- }
-
- pub fn get_lsp_referrer_kind(
- &self,
- specifier: &ModuleSpecifier,
- is_script: Option<bool>,
- ) -> NodeModuleKind {
- if specifier.scheme() != "file" {
- return NodeModuleKind::Esm;
- }
- match MediaType::from_specifier(specifier) {
- MediaType::Mts | MediaType::Mjs | MediaType::Dmts => NodeModuleKind::Esm,
- MediaType::Cjs | MediaType::Cts | MediaType::Dcts => NodeModuleKind::Cjs,
- MediaType::Dts => {
- // dts files are always determined based on the package.json because
- // they contain imports/exports even when considered CJS
- self.check_based_on_pkg_json(specifier).unwrap_or(NodeModuleKind::Esm)
- }
- MediaType::Wasm |
- MediaType::Json => NodeModuleKind::Esm,
- MediaType::JavaScript
- | MediaType::Jsx
- | MediaType::TypeScript
- | MediaType::Tsx
- // treat these as unknown
- | MediaType::Css
- | MediaType::SourceMap
- | MediaType::Unknown => {
- match is_script {
- Some(true) => self.check_based_on_pkg_json(specifier).unwrap_or(NodeModuleKind::Esm),
- Some(false) | None => NodeModuleKind::Esm,
- }
- }
- }
- }
-
- fn get_known_kind_with_is_script(
- &self,
- specifier: &ModuleSpecifier,
- media_type: MediaType,
- is_script: Option<bool>,
- known_cache: &DashMap<ModuleSpecifier, NodeModuleKind>,
- ) -> Option<NodeModuleKind> {
- if specifier.scheme() != "file" {
- return Some(NodeModuleKind::Esm);
- }
-
- match media_type {
- MediaType::Mts | MediaType::Mjs | MediaType::Dmts => Some(NodeModuleKind::Esm),
- MediaType::Cjs | MediaType::Cts | MediaType::Dcts => Some(NodeModuleKind::Cjs),
- MediaType::Dts => {
- // dts files are always determined based on the package.json because
- // they contain imports/exports even when considered CJS
- if let Some(value) = known_cache.get(specifier).map(|v| *v) {
- Some(value)
- } else {
- let value = self.check_based_on_pkg_json(specifier).ok();
- if let Some(value) = value {
- known_cache.insert(specifier.clone(), value);
- }
- Some(value.unwrap_or(NodeModuleKind::Esm))
- }
- }
- MediaType::Wasm |
- MediaType::Json => Some(NodeModuleKind::Esm),
- MediaType::JavaScript
- | MediaType::Jsx
- | MediaType::TypeScript
- | MediaType::Tsx
- // treat these as unknown
- | MediaType::Css
- | MediaType::SourceMap
- | MediaType::Unknown => {
- if let Some(value) = known_cache.get(specifier).map(|v| *v) {
- if value == NodeModuleKind::Cjs && is_script == Some(false) {
- // we now know this is actually esm
- known_cache.insert(specifier.clone(), NodeModuleKind::Esm);
- Some(NodeModuleKind::Esm)
- } else {
- Some(value)
- }
- } else if is_script == Some(false) {
- // we know this is esm
- known_cache.insert(specifier.clone(), NodeModuleKind::Esm);
- Some(NodeModuleKind::Esm)
- } else {
- None
- }
- }
- }
- }
-
- fn check_based_on_pkg_json(
- &self,
- specifier: &ModuleSpecifier,
- ) -> Result<NodeModuleKind, ClosestPkgJsonError> {
- if self.in_npm_pkg_checker.in_npm_package(specifier) {
- if let Some(pkg_json) =
- self.pkg_json_resolver.get_closest_package_json(specifier)?
- {
- let is_file_location_cjs = pkg_json.typ != "module";
- Ok(if is_file_location_cjs {
- NodeModuleKind::Cjs
- } else {
- NodeModuleKind::Esm
- })
- } else {
- Ok(NodeModuleKind::Cjs)
- }
- } else if self.options.detect_cjs || self.options.is_node_main {
- if let Some(pkg_json) =
- self.pkg_json_resolver.get_closest_package_json(specifier)?
- {
- let is_cjs_type = pkg_json.typ == "commonjs"
- || self.options.is_node_main && pkg_json.typ == "none";
- Ok(if is_cjs_type {
- NodeModuleKind::Cjs
- } else {
- NodeModuleKind::Esm
- })
- } else if self.options.is_node_main {
- Ok(NodeModuleKind::Cjs)
- } else {
- Ok(NodeModuleKind::Esm)
- }
- } else {
- Ok(NodeModuleKind::Esm)
- }
- }
+pub struct CliResolverOptions {
+ pub deno_resolver: Arc<CliDenoResolver>,
+ pub npm_resolver: Option<Arc<dyn CliNpmResolver>>,
+ pub bare_node_builtins_enabled: bool,
}
-pub type CliSloppyImportsResolver =
- SloppyImportsResolver<SloppyImportsCachedFs>;
-
/// A resolver that takes care of resolution, taking into account loaded
/// import map, JSX settings.
#[derive(Debug)]
pub struct CliResolver {
- node_resolver: Option<Arc<CliNodeResolver>>,
+ deno_resolver: Arc<CliDenoResolver>,
npm_resolver: Option<Arc<dyn CliNpmResolver>>,
- sloppy_imports_resolver: Option<Arc<CliSloppyImportsResolver>>,
- workspace_resolver: Arc<WorkspaceResolver>,
- maybe_vendor_specifier: Option<ModuleSpecifier>,
found_package_json_dep_flag: AtomicFlag,
bare_node_builtins_enabled: bool,
warned_pkgs: DashSet<PackageReq>,
}
-pub struct CliResolverOptions<'a> {
- pub node_resolver: Option<Arc<CliNodeResolver>>,
- pub npm_resolver: Option<Arc<dyn CliNpmResolver>>,
- pub sloppy_imports_resolver: Option<Arc<CliSloppyImportsResolver>>,
- pub workspace_resolver: Arc<WorkspaceResolver>,
- pub bare_node_builtins_enabled: bool,
- pub maybe_vendor_dir: Option<&'a PathBuf>,
-}
-
impl CliResolver {
pub fn new(options: CliResolverOptions) -> Self {
Self {
- node_resolver: options.node_resolver,
+ deno_resolver: options.deno_resolver,
npm_resolver: options.npm_resolver,
- sloppy_imports_resolver: options.sloppy_imports_resolver,
- workspace_resolver: options.workspace_resolver,
- maybe_vendor_specifier: options
- .maybe_vendor_dir
- .and_then(|v| ModuleSpecifier::from_directory_path(v).ok()),
found_package_json_dep_flag: Default::default(),
bare_node_builtins_enabled: options.bare_node_builtins_enabled,
warned_pkgs: Default::default(),
}
}
+ // todo(dsherret): move this off CliResolver as CliResolver is acting
+ // like a factory by doing this (it's beyond its responsibility)
pub fn create_graph_npm_resolver(&self) -> WorkerCliNpmGraphResolver {
WorkerCliNpmGraphResolver {
npm_resolver: self.npm_resolver.as_ref(),
@@ -730,223 +258,50 @@ impl CliResolver {
}
}
- let referrer = &referrer_range.specifier;
+ let resolution = self
+ .deno_resolver
+ .resolve(
+ raw_specifier,
+ &referrer_range.specifier,
+ referrer_kind,
+ to_node_mode(mode),
+ )
+ .map_err(|err| match err.into_kind() {
+ deno_resolver::DenoResolveErrorKind::MappedResolution(
+ mapped_resolution_error,
+ ) => match mapped_resolution_error {
+ MappedResolutionError::Specifier(e) => ResolveError::Specifier(e),
+ // deno_graph checks specifically for an ImportMapError
+ MappedResolutionError::ImportMap(e) => ResolveError::Other(e.into()),
+ err => ResolveError::Other(err.into()),
+ },
+ err => ResolveError::Other(err.into()),
+ })?;
- // Use node resolution if we're in an npm package
- if let Some(node_resolver) = self.node_resolver.as_ref() {
- if referrer.scheme() == "file" && node_resolver.in_npm_package(referrer) {
- return node_resolver
- .resolve(raw_specifier, referrer, referrer_kind, to_node_mode(mode))
- .map(|res| res.into_url())
- .map_err(|e| ResolveError::Other(e.into()));
- }
+ if resolution.found_package_json_dep {
+ // mark that we need to do an "npm install" later
+ self.found_package_json_dep_flag.raise();
}
- // Attempt to resolve with the workspace resolver
- let result: Result<_, ResolveError> = self
- .workspace_resolver
- .resolve(raw_specifier, referrer)
- .map_err(|err| match err {
- MappedResolutionError::Specifier(err) => ResolveError::Specifier(err),
- MappedResolutionError::ImportMap(err) => {
- ResolveError::Other(err.into())
- }
- MappedResolutionError::Workspace(err) => {
- ResolveError::Other(err.into())
- }
- });
- let result = match result {
- Ok(resolution) => match resolution {
- MappedResolution::Normal {
- specifier,
- maybe_diagnostic,
- }
- | MappedResolution::ImportMap {
- specifier,
- maybe_diagnostic,
- } => {
- if let Some(diagnostic) = maybe_diagnostic {
- match &*diagnostic {
- MappedResolutionDiagnostic::ConstraintNotMatchedLocalVersion { reference, .. } => {
- if self.warned_pkgs.insert(reference.req().clone()) {
- log::warn!("{} {}\n at {}", colors::yellow("Warning"), diagnostic, referrer_range);
- }
- }
- }
- }
- // do sloppy imports resolution if enabled
- if let Some(sloppy_imports_resolver) = &self.sloppy_imports_resolver {
- Ok(
- sloppy_imports_resolver
- .resolve(
- &specifier,
- match mode {
- ResolutionMode::Execution => {
- SloppyImportsResolutionMode::Execution
- }
- ResolutionMode::Types => SloppyImportsResolutionMode::Types,
- },
- )
- .map(|s| s.into_specifier())
- .unwrap_or(specifier),
- )
- } else {
- Ok(specifier)
- }
- }
- MappedResolution::WorkspaceJsrPackage { specifier, .. } => {
- Ok(specifier)
- }
- MappedResolution::WorkspaceNpmPackage {
- target_pkg_json: pkg_json,
- sub_path,
- ..
- } => self
- .node_resolver
- .as_ref()
- .unwrap()
- .resolve_package_sub_path_from_deno_module(
- pkg_json.dir_path(),
- sub_path.as_deref(),
- Some(referrer),
- referrer_kind,
- to_node_mode(mode),
- )
- .map_err(|e| ResolveError::Other(e.into())),
- MappedResolution::PackageJson {
- dep_result,
- alias,
- sub_path,
+ if let Some(diagnostic) = resolution.maybe_diagnostic {
+ match &*diagnostic {
+ MappedResolutionDiagnostic::ConstraintNotMatchedLocalVersion {
+ reference,
..
} => {
- // found a specifier in the package.json, so mark that
- // we need to do an "npm install" later
- self.found_package_json_dep_flag.raise();
-
- dep_result
- .as_ref()
- .map_err(|e| ResolveError::Other(e.clone().into()))
- .and_then(|dep| match dep {
- PackageJsonDepValue::Req(req) => {
- ModuleSpecifier::parse(&format!(
- "npm:{}{}",
- req,
- sub_path.map(|s| format!("/{}", s)).unwrap_or_default()
- ))
- .map_err(|e| ResolveError::Other(e.into()))
- }
- PackageJsonDepValue::Workspace(version_req) => self
- .workspace_resolver
- .resolve_workspace_pkg_json_folder_for_pkg_json_dep(
- alias,
- version_req,
- )
- .map_err(|e| ResolveError::Other(e.into()))
- .and_then(|pkg_folder| {
- self
- .node_resolver
- .as_ref()
- .unwrap()
- .resolve_package_sub_path_from_deno_module(
- pkg_folder,
- sub_path.as_deref(),
- Some(referrer),
- referrer_kind,
- to_node_mode(mode),
- )
- .map_err(|e| ResolveError::Other(e.into()))
- }),
- })
- }
- },
- Err(err) => Err(err),
- };
-
- // When the user is vendoring, don't allow them to import directly from the vendor/ directory
- // as it might cause them confusion or duplicate dependencies. Additionally, this folder has
- // special treatment in the language server so it will definitely cause issues/confusion there
- // if they do this.
- if let Some(vendor_specifier) = &self.maybe_vendor_specifier {
- if let Ok(specifier) = &result {
- if specifier.as_str().starts_with(vendor_specifier.as_str()) {
- return Err(ResolveError::Other(anyhow!("Importing from the vendor directory is not permitted. Use a remote specifier instead or disable vendoring.")));
- }
- }
- }
-
- let Some(node_resolver) = &self.node_resolver else {
- return result;
- };
-
- let is_byonm = self
- .npm_resolver
- .as_ref()
- .is_some_and(|r| r.as_byonm().is_some());
- match result {
- Ok(specifier) => {
- if let Ok(npm_req_ref) =
- NpmPackageReqReference::from_specifier(&specifier)
- {
- // check if the npm specifier resolves to a workspace member
- if let Some(pkg_folder) = self
- .workspace_resolver
- .resolve_workspace_pkg_json_folder_for_npm_specifier(
- npm_req_ref.req(),
- )
- {
- return node_resolver
- .resolve_package_sub_path_from_deno_module(
- pkg_folder,
- npm_req_ref.sub_path(),
- Some(referrer),
- referrer_kind,
- to_node_mode(mode),
- )
- .map_err(|e| ResolveError::Other(e.into()));
- }
-
- // do npm resolution for byonm
- if is_byonm {
- return node_resolver
- .resolve_req_reference(
- &npm_req_ref,
- referrer,
- referrer_kind,
- to_node_mode(mode),
- )
- .map_err(|err| err.into());
+ if self.warned_pkgs.insert(reference.req().clone()) {
+ log::warn!(
+ "{} {}\n at {}",
+ colors::yellow("Warning"),
+ diagnostic,
+ referrer_range
+ );
}
}
-
- Ok(match node_resolver.handle_if_in_node_modules(&specifier)? {
- Some(specifier) => specifier,
- None => specifier,
- })
- }
- Err(err) => {
- // If byonm, check if the bare specifier resolves to an npm package
- if is_byonm && referrer.scheme() == "file" {
- let maybe_resolution = node_resolver
- .resolve_if_for_npm_pkg(
- raw_specifier,
- referrer,
- referrer_kind,
- to_node_mode(mode),
- )
- .map_err(ResolveError::Other)?;
- if let Some(res) = maybe_resolution {
- match res {
- NodeResolution::Module(url) => return Ok(url),
- NodeResolution::BuiltIn(_) => {
- // don't resolve bare specifiers for built-in modules via node resolution
- }
- }
- }
- }
-
- Err(err)
}
}
+
+ Ok(resolution.url)
}
}
diff --git a/cli/standalone/mod.rs b/cli/standalone/mod.rs
index 15937c7ae..e3449c152 100644
--- a/cli/standalone/mod.rs
+++ b/cli/standalone/mod.rs
@@ -29,6 +29,7 @@ use deno_core::RequestedModuleType;
use deno_core::ResolutionKind;
use deno_npm::npm_rc::ResolvedNpmRc;
use deno_package_json::PackageJsonDepValue;
+use deno_resolver::npm::NpmReqResolverOptions;
use deno_runtime::deno_fs;
use deno_runtime::deno_node::create_host_defined_options;
use deno_runtime::deno_node::NodeRequireLoader;
@@ -79,7 +80,7 @@ use crate::npm::CliNpmResolverManagedSnapshotOption;
use crate::npm::CreateInNpmPkgCheckerOptions;
use crate::resolver::CjsTracker;
use crate::resolver::CliDenoResolverFs;
-use crate::resolver::CliNodeResolver;
+use crate::resolver::CliNpmReqResolver;
use crate::resolver::IsCjsResolverOptions;
use crate::resolver::NpmModuleLoader;
use crate::util::progress_bar::ProgressBar;
@@ -107,8 +108,9 @@ struct SharedModuleLoaderState {
fs: Arc<dyn deno_fs::FileSystem>,
modules: StandaloneModules,
node_code_translator: Arc<CliNodeCodeTranslator>,
- node_resolver: Arc<CliNodeResolver>,
+ node_resolver: Arc<NodeResolver>,
npm_module_loader: Arc<NpmModuleLoader>,
+ npm_req_resolver: Arc<CliNpmReqResolver>,
npm_resolver: Arc<dyn CliNpmResolver>,
workspace_resolver: WorkspaceResolver,
}
@@ -190,7 +192,7 @@ impl ModuleLoader for EmbeddedModuleLoader {
self
.shared
.node_resolver
- .resolve_package_sub_path_from_deno_module(
+ .resolve_package_subpath_from_deno_module(
pkg_json.dir_path(),
sub_path.as_deref(),
Some(&referrer),
@@ -204,15 +206,17 @@ impl ModuleLoader for EmbeddedModuleLoader {
alias,
..
}) => match dep_result.as_ref().map_err(|e| AnyError::from(e.clone()))? {
- PackageJsonDepValue::Req(req) => {
- self.shared.node_resolver.resolve_req_with_sub_path(
+ PackageJsonDepValue::Req(req) => self
+ .shared
+ .npm_req_resolver
+ .resolve_req_with_sub_path(
req,
sub_path.as_deref(),
&referrer,
referrer_kind,
NodeResolutionMode::Execution,
)
- }
+ .map_err(AnyError::from),
PackageJsonDepValue::Workspace(version_req) => {
let pkg_folder = self
.shared
@@ -225,7 +229,7 @@ impl ModuleLoader for EmbeddedModuleLoader {
self
.shared
.node_resolver
- .resolve_package_sub_path_from_deno_module(
+ .resolve_package_subpath_from_deno_module(
pkg_folder,
sub_path.as_deref(),
Some(&referrer),
@@ -240,12 +244,12 @@ impl ModuleLoader for EmbeddedModuleLoader {
if let Ok(reference) =
NpmPackageReqReference::from_specifier(&specifier)
{
- return self.shared.node_resolver.resolve_req_reference(
+ return Ok(self.shared.npm_req_resolver.resolve_req_reference(
&reference,
&referrer,
referrer_kind,
NodeResolutionMode::Execution,
- );
+ )?);
}
if specifier.scheme() == "jsr" {
@@ -260,14 +264,14 @@ impl ModuleLoader for EmbeddedModuleLoader {
self
.shared
.node_resolver
- .handle_if_in_node_modules(&specifier)?
+ .handle_if_in_node_modules(&specifier)
.unwrap_or(specifier),
)
}
Err(err)
if err.is_unmapped_bare_specifier() && referrer.scheme() == "file" =>
{
- let maybe_res = self.shared.node_resolver.resolve_if_for_npm_pkg(
+ let maybe_res = self.shared.npm_req_resolver.resolve_if_for_npm_pkg(
raw_specifier,
&referrer,
referrer_kind,
@@ -651,7 +655,7 @@ pub async fn run(data: StandaloneData) -> Result<i32, AnyError> {
let node_resolver = Arc::new(NodeResolver::new(
deno_runtime::deno_node::DenoFsNodeResolverEnv::new(fs.clone()),
in_npm_pkg_checker.clone(),
- npm_resolver.clone().into_npm_resolver(),
+ npm_resolver.clone().into_npm_pkg_folder_resolver(),
pkg_json_resolver.clone(),
));
let cjs_tracker = Arc::new(CjsTracker::new(
@@ -664,12 +668,14 @@ pub async fn run(data: StandaloneData) -> Result<i32, AnyError> {
));
let cache_db = Caches::new(deno_dir_provider.clone());
let node_analysis_cache = NodeAnalysisCache::new(cache_db.node_analysis_db());
- let cli_node_resolver = Arc::new(CliNodeResolver::new(
- fs.clone(),
- in_npm_pkg_checker.clone(),
- node_resolver.clone(),
- npm_resolver.clone(),
- ));
+ let npm_req_resolver =
+ Arc::new(CliNpmReqResolver::new(NpmReqResolverOptions {
+ byonm_resolver: (npm_resolver.clone()).into_maybe_byonm(),
+ fs: CliDenoResolverFs(fs.clone()),
+ in_npm_pkg_checker: in_npm_pkg_checker.clone(),
+ node_resolver: node_resolver.clone(),
+ npm_req_resolver: npm_resolver.clone().into_npm_req_resolver(),
+ }));
let cjs_esm_code_analyzer = CliCjsCodeAnalyzer::new(
node_analysis_cache,
cjs_tracker.clone(),
@@ -681,7 +687,7 @@ pub async fn run(data: StandaloneData) -> Result<i32, AnyError> {
deno_runtime::deno_node::DenoFsNodeResolverEnv::new(fs.clone()),
in_npm_pkg_checker,
node_resolver.clone(),
- npm_resolver.clone().into_npm_resolver(),
+ npm_resolver.clone().into_npm_pkg_folder_resolver(),
pkg_json_resolver.clone(),
));
let workspace_resolver = {
@@ -739,7 +745,7 @@ pub async fn run(data: StandaloneData) -> Result<i32, AnyError> {
fs: fs.clone(),
modules,
node_code_translator: node_code_translator.clone(),
- node_resolver: cli_node_resolver.clone(),
+ node_resolver: node_resolver.clone(),
npm_module_loader: Arc::new(NpmModuleLoader::new(
cjs_tracker.clone(),
fs.clone(),
@@ -747,6 +753,7 @@ pub async fn run(data: StandaloneData) -> Result<i32, AnyError> {
)),
npm_resolver: npm_resolver.clone(),
workspace_resolver,
+ npm_req_resolver,
}),
};
diff --git a/cli/tsc/mod.rs b/cli/tsc/mod.rs
index a56906162..452d5c165 100644
--- a/cli/tsc/mod.rs
+++ b/cli/tsc/mod.rs
@@ -6,7 +6,6 @@ use crate::cache::FastInsecureHasher;
use crate::cache::ModuleInfoCache;
use crate::node;
use crate::npm::CliNpmResolver;
-use crate::npm::ResolvePkgFolderFromDenoReqError;
use crate::resolver::CjsTracker;
use crate::util::checksum;
use crate::util::path::mapped_specifier_for_tsc;
@@ -34,6 +33,7 @@ use deno_graph::GraphKind;
use deno_graph::Module;
use deno_graph::ModuleGraph;
use deno_graph::ResolutionResolved;
+use deno_resolver::npm::ResolvePkgFolderFromDenoReqError;
use deno_runtime::deno_fs;
use deno_runtime::deno_node::NodeResolver;
use deno_semver::npm::NpmPackageReqReference;
diff --git a/cli/worker.rs b/cli/worker.rs
index 83e36b36c..1afe37e34 100644
--- a/cli/worker.rs
+++ b/cli/worker.rs
@@ -155,7 +155,7 @@ impl SharedWorkerState {
NodeExtInitServices {
node_require_loader,
node_resolver: self.node_resolver.clone(),
- npm_resolver: self.npm_resolver.clone().into_npm_resolver(),
+ npm_resolver: self.npm_resolver.clone().into_npm_pkg_folder_resolver(),
pkg_json_resolver: self.pkg_json_resolver.clone(),
}
}
diff --git a/ext/node/lib.rs b/ext/node/lib.rs
index 702c919f4..84f39552c 100644
--- a/ext/node/lib.rs
+++ b/ext/node/lib.rs
@@ -15,7 +15,7 @@ use deno_core::url::Url;
use deno_core::v8;
use deno_core::v8::ExternalReference;
use node_resolver::errors::ClosestPkgJsonError;
-use node_resolver::NpmResolverRc;
+use node_resolver::NpmPackageFolderResolverRc;
use once_cell::sync::Lazy;
extern crate libz_sys as zlib;
@@ -183,7 +183,7 @@ fn op_node_build_os() -> String {
pub struct NodeExtInitServices {
pub node_require_loader: NodeRequireLoaderRc,
pub node_resolver: NodeResolverRc,
- pub npm_resolver: NpmResolverRc,
+ pub npm_resolver: NpmPackageFolderResolverRc,
pub pkg_json_resolver: PackageJsonResolverRc,
}
diff --git a/ext/node/ops/require.rs b/ext/node/ops/require.rs
index b7fa8feb2..e381ee91d 100644
--- a/ext/node/ops/require.rs
+++ b/ext/node/ops/require.rs
@@ -24,7 +24,7 @@ use std::rc::Rc;
use crate::NodePermissions;
use crate::NodeRequireLoaderRc;
use crate::NodeResolverRc;
-use crate::NpmResolverRc;
+use crate::NpmPackageFolderResolverRc;
use crate::PackageJsonResolverRc;
#[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
@@ -220,7 +220,7 @@ pub fn op_require_resolve_deno_dir(
#[string] request: String,
#[string] parent_filename: String,
) -> Result<Option<String>, AnyError> {
- let resolver = state.borrow::<NpmResolverRc>();
+ let resolver = state.borrow::<NpmPackageFolderResolverRc>();
Ok(
resolver
.resolve_package_folder_from_package(
diff --git a/resolvers/deno/Cargo.toml b/resolvers/deno/Cargo.toml
index 24d50587b..89c0232dc 100644
--- a/resolvers/deno/Cargo.toml
+++ b/resolvers/deno/Cargo.toml
@@ -16,6 +16,8 @@ path = "lib.rs"
[dependencies]
anyhow.workspace = true
base32.workspace = true
+dashmap.workspace = true
+deno_config.workspace = true
deno_media_type.workspace = true
deno_package_json.workspace = true
deno_package_json.features = ["sync"]
diff --git a/resolvers/deno/cjs.rs b/resolvers/deno/cjs.rs
new file mode 100644
index 000000000..dbcbd8b6b
--- /dev/null
+++ b/resolvers/deno/cjs.rs
@@ -0,0 +1,272 @@
+// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
+
+use std::sync::Arc;
+
+use dashmap::DashMap;
+use deno_media_type::MediaType;
+use node_resolver::env::NodeResolverEnv;
+use node_resolver::errors::ClosestPkgJsonError;
+use node_resolver::InNpmPackageChecker;
+use node_resolver::NodeModuleKind;
+use node_resolver::PackageJsonResolver;
+use url::Url;
+
+/// Keeps track of what module specifiers were resolved as CJS.
+///
+/// Modules that are `.js`, `.ts`, `.jsx`, and `tsx` are only known to
+/// be CJS or ESM after they're loaded based on their contents. So these
+/// files will be "maybe CJS" until they're loaded.
+#[derive(Debug)]
+pub struct CjsTracker<TEnv: NodeResolverEnv> {
+ is_cjs_resolver: IsCjsResolver<TEnv>,
+ known: DashMap<Url, NodeModuleKind>,
+}
+
+impl<TEnv: NodeResolverEnv> CjsTracker<TEnv> {
+ pub fn new(
+ in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
+ pkg_json_resolver: Arc<PackageJsonResolver<TEnv>>,
+ options: IsCjsResolverOptions,
+ ) -> Self {
+ Self {
+ is_cjs_resolver: IsCjsResolver::new(
+ in_npm_pkg_checker,
+ pkg_json_resolver,
+ options,
+ ),
+ known: Default::default(),
+ }
+ }
+
+ /// Checks whether the file might be treated as CJS, but it's not for sure
+ /// yet because the source hasn't been loaded to see whether it contains
+ /// imports or exports.
+ pub fn is_maybe_cjs(
+ &self,
+ specifier: &Url,
+ media_type: MediaType,
+ ) -> Result<bool, ClosestPkgJsonError> {
+ self.treat_as_cjs_with_is_script(specifier, media_type, None)
+ }
+
+ /// Gets whether the file is CJS. If true, this is for sure
+ /// cjs because `is_script` is provided.
+ ///
+ /// `is_script` should be `true` when the contents of the file at the
+ /// provided specifier are known to be a script and not an ES module.
+ pub fn is_cjs_with_known_is_script(
+ &self,
+ specifier: &Url,
+ media_type: MediaType,
+ is_script: bool,
+ ) -> Result<bool, ClosestPkgJsonError> {
+ self.treat_as_cjs_with_is_script(specifier, media_type, Some(is_script))
+ }
+
+ fn treat_as_cjs_with_is_script(
+ &self,
+ specifier: &Url,
+ media_type: MediaType,
+ is_script: Option<bool>,
+ ) -> Result<bool, ClosestPkgJsonError> {
+ let kind = match self
+ .get_known_kind_with_is_script(specifier, media_type, is_script)
+ {
+ Some(kind) => kind,
+ None => self.is_cjs_resolver.check_based_on_pkg_json(specifier)?,
+ };
+ Ok(kind == NodeModuleKind::Cjs)
+ }
+
+ /// Gets the referrer for the specified module specifier.
+ ///
+ /// Generally the referrer should already be tracked by calling
+ /// `is_cjs_with_known_is_script` before calling this method.
+ pub fn get_referrer_kind(&self, specifier: &Url) -> NodeModuleKind {
+ if specifier.scheme() != "file" {
+ return NodeModuleKind::Esm;
+ }
+ self
+ .get_known_kind(specifier, MediaType::from_specifier(specifier))
+ .unwrap_or(NodeModuleKind::Esm)
+ }
+
+ fn get_known_kind(
+ &self,
+ specifier: &Url,
+ media_type: MediaType,
+ ) -> Option<NodeModuleKind> {
+ self.get_known_kind_with_is_script(specifier, media_type, None)
+ }
+
+ fn get_known_kind_with_is_script(
+ &self,
+ specifier: &Url,
+ media_type: MediaType,
+ is_script: Option<bool>,
+ ) -> Option<NodeModuleKind> {
+ self.is_cjs_resolver.get_known_kind_with_is_script(
+ specifier,
+ media_type,
+ is_script,
+ &self.known,
+ )
+ }
+}
+
+#[derive(Debug)]
+pub struct IsCjsResolverOptions {
+ pub detect_cjs: bool,
+ pub is_node_main: bool,
+}
+
+/// Resolves whether a module is CJS or ESM.
+#[derive(Debug)]
+pub struct IsCjsResolver<TEnv: NodeResolverEnv> {
+ in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
+ pkg_json_resolver: Arc<PackageJsonResolver<TEnv>>,
+ options: IsCjsResolverOptions,
+}
+
+impl<TEnv: NodeResolverEnv> IsCjsResolver<TEnv> {
+ pub fn new(
+ in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
+ pkg_json_resolver: Arc<PackageJsonResolver<TEnv>>,
+ options: IsCjsResolverOptions,
+ ) -> Self {
+ Self {
+ in_npm_pkg_checker,
+ pkg_json_resolver,
+ options,
+ }
+ }
+
+ /// Gets the referrer kind for a script in the LSP.
+ pub fn get_lsp_referrer_kind(
+ &self,
+ specifier: &Url,
+ is_script: Option<bool>,
+ ) -> NodeModuleKind {
+ if specifier.scheme() != "file" {
+ return NodeModuleKind::Esm;
+ }
+ match MediaType::from_specifier(specifier) {
+ MediaType::Mts | MediaType::Mjs | MediaType::Dmts => NodeModuleKind::Esm,
+ MediaType::Cjs | MediaType::Cts | MediaType::Dcts => NodeModuleKind::Cjs,
+ MediaType::Dts => {
+ // dts files are always determined based on the package.json because
+ // they contain imports/exports even when considered CJS
+ self.check_based_on_pkg_json(specifier).unwrap_or(NodeModuleKind::Esm)
+ }
+ MediaType::Wasm |
+ MediaType::Json => NodeModuleKind::Esm,
+ MediaType::JavaScript
+ | MediaType::Jsx
+ | MediaType::TypeScript
+ | MediaType::Tsx
+ // treat these as unknown
+ | MediaType::Css
+ | MediaType::SourceMap
+ | MediaType::Unknown => {
+ match is_script {
+ Some(true) => self.check_based_on_pkg_json(specifier).unwrap_or(NodeModuleKind::Esm),
+ Some(false) | None => NodeModuleKind::Esm,
+ }
+ }
+ }
+ }
+
+ fn get_known_kind_with_is_script(
+ &self,
+ specifier: &Url,
+ media_type: MediaType,
+ is_script: Option<bool>,
+ known_cache: &DashMap<Url, NodeModuleKind>,
+ ) -> Option<NodeModuleKind> {
+ if specifier.scheme() != "file" {
+ return Some(NodeModuleKind::Esm);
+ }
+
+ match media_type {
+ MediaType::Mts | MediaType::Mjs | MediaType::Dmts => Some(NodeModuleKind::Esm),
+ MediaType::Cjs | MediaType::Cts | MediaType::Dcts => Some(NodeModuleKind::Cjs),
+ MediaType::Dts => {
+ // dts files are always determined based on the package.json because
+ // they contain imports/exports even when considered CJS
+ if let Some(value) = known_cache.get(specifier).map(|v| *v) {
+ Some(value)
+ } else {
+ let value = self.check_based_on_pkg_json(specifier).ok();
+ if let Some(value) = value {
+ known_cache.insert(specifier.clone(), value);
+ }
+ Some(value.unwrap_or(NodeModuleKind::Esm))
+ }
+ }
+ MediaType::Wasm |
+ MediaType::Json => Some(NodeModuleKind::Esm),
+ MediaType::JavaScript
+ | MediaType::Jsx
+ | MediaType::TypeScript
+ | MediaType::Tsx
+ // treat these as unknown
+ | MediaType::Css
+ | MediaType::SourceMap
+ | MediaType::Unknown => {
+ if let Some(value) = known_cache.get(specifier).map(|v| *v) {
+ if value == NodeModuleKind::Cjs && is_script == Some(false) {
+ // we now know this is actually esm
+ known_cache.insert(specifier.clone(), NodeModuleKind::Esm);
+ Some(NodeModuleKind::Esm)
+ } else {
+ Some(value)
+ }
+ } else if is_script == Some(false) {
+ // we know this is esm
+ known_cache.insert(specifier.clone(), NodeModuleKind::Esm);
+ Some(NodeModuleKind::Esm)
+ } else {
+ None
+ }
+ }
+ }
+ }
+
+ fn check_based_on_pkg_json(
+ &self,
+ specifier: &Url,
+ ) -> Result<NodeModuleKind, ClosestPkgJsonError> {
+ if self.in_npm_pkg_checker.in_npm_package(specifier) {
+ if let Some(pkg_json) =
+ self.pkg_json_resolver.get_closest_package_json(specifier)?
+ {
+ let is_file_location_cjs = pkg_json.typ != "module";
+ Ok(if is_file_location_cjs {
+ NodeModuleKind::Cjs
+ } else {
+ NodeModuleKind::Esm
+ })
+ } else {
+ Ok(NodeModuleKind::Cjs)
+ }
+ } else if self.options.detect_cjs || self.options.is_node_main {
+ if let Some(pkg_json) =
+ self.pkg_json_resolver.get_closest_package_json(specifier)?
+ {
+ let is_cjs_type = pkg_json.typ == "commonjs"
+ || self.options.is_node_main && pkg_json.typ == "none";
+ Ok(if is_cjs_type {
+ NodeModuleKind::Cjs
+ } else {
+ NodeModuleKind::Esm
+ })
+ } else if self.options.is_node_main {
+ Ok(NodeModuleKind::Cjs)
+ } else {
+ Ok(NodeModuleKind::Esm)
+ }
+ } else {
+ Ok(NodeModuleKind::Esm)
+ }
+ }
+}
diff --git a/resolvers/deno/fs.rs b/resolvers/deno/fs.rs
index 44495fa7c..4929f4508 100644
--- a/resolvers/deno/fs.rs
+++ b/resolvers/deno/fs.rs
@@ -12,6 +12,7 @@ pub struct DirEntry {
pub trait DenoResolverFs {
fn read_to_string_lossy(&self, path: &Path) -> std::io::Result<String>;
fn realpath_sync(&self, path: &Path) -> std::io::Result<PathBuf>;
+ fn exists_sync(&self, path: &Path) -> bool;
fn is_dir_sync(&self, path: &Path) -> bool;
fn read_dir_sync(&self, dir_path: &Path) -> std::io::Result<Vec<DirEntry>>;
}
diff --git a/resolvers/deno/lib.rs b/resolvers/deno/lib.rs
index 57fa67512..a2b6b642f 100644
--- a/resolvers/deno/lib.rs
+++ b/resolvers/deno/lib.rs
@@ -1,5 +1,439 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
+#![deny(clippy::print_stderr)]
+#![deny(clippy::print_stdout)]
+
+use std::path::PathBuf;
+use std::sync::Arc;
+
+use deno_config::workspace::MappedResolution;
+use deno_config::workspace::MappedResolutionDiagnostic;
+use deno_config::workspace::MappedResolutionError;
+use deno_config::workspace::WorkspaceResolvePkgJsonFolderError;
+use deno_config::workspace::WorkspaceResolver;
+use deno_package_json::PackageJsonDepValue;
+use deno_package_json::PackageJsonDepValueParseError;
+use deno_semver::npm::NpmPackageReqReference;
+use fs::DenoResolverFs;
+use node_resolver::env::NodeResolverEnv;
+use node_resolver::errors::NodeResolveError;
+use node_resolver::errors::PackageSubpathResolveError;
+use node_resolver::InNpmPackageChecker;
+use node_resolver::NodeModuleKind;
+use node_resolver::NodeResolution;
+use node_resolver::NodeResolutionMode;
+use node_resolver::NodeResolver;
+use npm::MissingPackageNodeModulesFolderError;
+use npm::NodeModulesOutOfDateError;
+use npm::NpmReqResolver;
+use npm::ResolveIfForNpmPackageError;
+use npm::ResolvePkgFolderFromDenoReqError;
+use npm::ResolveReqWithSubPathError;
+use sloppy_imports::SloppyImportResolverFs;
+use sloppy_imports::SloppyImportsResolutionMode;
+use sloppy_imports::SloppyImportsResolver;
+use thiserror::Error;
+use url::Url;
+
+pub mod cjs;
pub mod fs;
pub mod npm;
pub mod sloppy_imports;
+
+#[derive(Debug, Clone)]
+pub struct DenoResolution {
+ pub url: Url,
+ pub maybe_diagnostic: Option<Box<MappedResolutionDiagnostic>>,
+ pub found_package_json_dep: bool,
+}
+
+#[derive(Debug, Error)]
+pub enum DenoResolveErrorKind {
+ #[error("Importing from the vendor directory is not permitted. Use a remote specifier instead or disable vendoring.")]
+ InvalidVendorFolderImport,
+ #[error(transparent)]
+ MappedResolution(#[from] MappedResolutionError),
+ #[error(transparent)]
+ MissingPackageNodeModulesFolder(#[from] MissingPackageNodeModulesFolderError),
+ #[error(transparent)]
+ Node(#[from] NodeResolveError),
+ #[error(transparent)]
+ NodeModulesOutOfDate(#[from] NodeModulesOutOfDateError),
+ #[error(transparent)]
+ PackageJsonDepValueParse(#[from] PackageJsonDepValueParseError),
+ #[error(transparent)]
+ PackageJsonDepValueUrlParse(url::ParseError),
+ #[error(transparent)]
+ PackageSubpathResolve(#[from] PackageSubpathResolveError),
+ #[error(transparent)]
+ ResolvePkgFolderFromDenoReq(#[from] ResolvePkgFolderFromDenoReqError),
+ #[error(transparent)]
+ WorkspaceResolvePkgJsonFolder(#[from] WorkspaceResolvePkgJsonFolderError),
+}
+
+impl DenoResolveErrorKind {
+ pub fn into_box(self) -> DenoResolveError {
+ DenoResolveError(Box::new(self))
+ }
+}
+
+#[derive(Error, Debug)]
+#[error(transparent)]
+pub struct DenoResolveError(pub Box<DenoResolveErrorKind>);
+
+impl DenoResolveError {
+ pub fn as_kind(&self) -> &DenoResolveErrorKind {
+ &self.0
+ }
+
+ pub fn into_kind(self) -> DenoResolveErrorKind {
+ *self.0
+ }
+}
+
+impl<E> From<E> for DenoResolveError
+where
+ DenoResolveErrorKind: From<E>,
+{
+ fn from(err: E) -> Self {
+ DenoResolveError(Box::new(DenoResolveErrorKind::from(err)))
+ }
+}
+
+#[derive(Debug)]
+pub struct NodeAndNpmReqResolver<
+ Fs: DenoResolverFs,
+ TNodeResolverEnv: NodeResolverEnv,
+> {
+ pub node_resolver: Arc<NodeResolver<TNodeResolverEnv>>,
+ pub npm_req_resolver: Arc<NpmReqResolver<Fs, TNodeResolverEnv>>,
+}
+
+pub struct DenoResolverOptions<
+ 'a,
+ Fs: DenoResolverFs,
+ TNodeResolverEnv: NodeResolverEnv,
+ TSloppyImportResolverFs: SloppyImportResolverFs,
+> {
+ pub in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
+ pub node_and_req_resolver:
+ Option<NodeAndNpmReqResolver<Fs, TNodeResolverEnv>>,
+ pub sloppy_imports_resolver:
+ Option<Arc<SloppyImportsResolver<TSloppyImportResolverFs>>>,
+ pub workspace_resolver: Arc<WorkspaceResolver>,
+ /// Whether "bring your own node_modules" is enabled where Deno does not
+ /// setup the node_modules directories automatically, but instead uses
+ /// what already exists on the file system.
+ pub is_byonm: bool,
+ pub maybe_vendor_dir: Option<&'a PathBuf>,
+}
+
+/// A resolver that takes care of resolution, taking into account loaded
+/// import map, JSX settings.
+#[derive(Debug)]
+pub struct DenoResolver<
+ Fs: DenoResolverFs,
+ TNodeResolverEnv: NodeResolverEnv,
+ TSloppyImportResolverFs: SloppyImportResolverFs,
+> {
+ in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
+ node_and_npm_resolver: Option<NodeAndNpmReqResolver<Fs, TNodeResolverEnv>>,
+ sloppy_imports_resolver:
+ Option<Arc<SloppyImportsResolver<TSloppyImportResolverFs>>>,
+ workspace_resolver: Arc<WorkspaceResolver>,
+ is_byonm: bool,
+ maybe_vendor_specifier: Option<Url>,
+}
+
+impl<
+ Fs: DenoResolverFs,
+ TNodeResolverEnv: NodeResolverEnv,
+ TSloppyImportResolverFs: SloppyImportResolverFs,
+ > DenoResolver<Fs, TNodeResolverEnv, TSloppyImportResolverFs>
+{
+ pub fn new(
+ options: DenoResolverOptions<Fs, TNodeResolverEnv, TSloppyImportResolverFs>,
+ ) -> Self {
+ Self {
+ in_npm_pkg_checker: options.in_npm_pkg_checker,
+ node_and_npm_resolver: options.node_and_req_resolver,
+ sloppy_imports_resolver: options.sloppy_imports_resolver,
+ workspace_resolver: options.workspace_resolver,
+ is_byonm: options.is_byonm,
+ maybe_vendor_specifier: options
+ .maybe_vendor_dir
+ .and_then(|v| deno_path_util::url_from_directory_path(v).ok()),
+ }
+ }
+
+ pub fn resolve(
+ &self,
+ raw_specifier: &str,
+ referrer: &Url,
+ referrer_kind: NodeModuleKind,
+ mode: NodeResolutionMode,
+ ) -> Result<DenoResolution, DenoResolveError> {
+ let mut found_package_json_dep = false;
+ let mut maybe_diagnostic = None;
+ // Use node resolution if we're in an npm package
+ if let Some(node_and_npm_resolver) = self.node_and_npm_resolver.as_ref() {
+ let node_resolver = &node_and_npm_resolver.node_resolver;
+ if referrer.scheme() == "file"
+ && self.in_npm_pkg_checker.in_npm_package(referrer)
+ {
+ return node_resolver
+ .resolve(raw_specifier, referrer, referrer_kind, mode)
+ .map(|res| DenoResolution {
+ url: res.into_url(),
+ found_package_json_dep,
+ maybe_diagnostic,
+ })
+ .map_err(|e| e.into());
+ }
+ }
+
+ // Attempt to resolve with the workspace resolver
+ let result: Result<_, DenoResolveError> = self
+ .workspace_resolver
+ .resolve(raw_specifier, referrer)
+ .map_err(|err| err.into());
+ let result = match result {
+ Ok(resolution) => match resolution {
+ MappedResolution::Normal {
+ specifier,
+ maybe_diagnostic: current_diagnostic,
+ }
+ | MappedResolution::ImportMap {
+ specifier,
+ maybe_diagnostic: current_diagnostic,
+ } => {
+ maybe_diagnostic = current_diagnostic;
+ // do sloppy imports resolution if enabled
+ if let Some(sloppy_imports_resolver) = &self.sloppy_imports_resolver {
+ Ok(
+ sloppy_imports_resolver
+ .resolve(
+ &specifier,
+ match mode {
+ NodeResolutionMode::Execution => {
+ SloppyImportsResolutionMode::Execution
+ }
+ NodeResolutionMode::Types => {
+ SloppyImportsResolutionMode::Types
+ }
+ },
+ )
+ .map(|s| s.into_specifier())
+ .unwrap_or(specifier),
+ )
+ } else {
+ Ok(specifier)
+ }
+ }
+ MappedResolution::WorkspaceJsrPackage { specifier, .. } => {
+ Ok(specifier)
+ }
+ MappedResolution::WorkspaceNpmPackage {
+ target_pkg_json: pkg_json,
+ sub_path,
+ ..
+ } => self
+ .node_and_npm_resolver
+ .as_ref()
+ .unwrap()
+ .node_resolver
+ .resolve_package_subpath_from_deno_module(
+ pkg_json.dir_path(),
+ sub_path.as_deref(),
+ Some(referrer),
+ referrer_kind,
+ mode,
+ )
+ .map_err(|e| e.into()),
+ MappedResolution::PackageJson {
+ dep_result,
+ alias,
+ sub_path,
+ ..
+ } => {
+ // found a specifier in the package.json, so mark that
+ // we need to do an "npm install" later
+ found_package_json_dep = true;
+
+ dep_result
+ .as_ref()
+ .map_err(|e| {
+ DenoResolveErrorKind::PackageJsonDepValueParse(e.clone())
+ .into_box()
+ })
+ .and_then(|dep| match dep {
+ // todo(dsherret): it seems bad that we're converting this
+ // to a url because the req might not be a valid url.
+ PackageJsonDepValue::Req(req) => Url::parse(&format!(
+ "npm:{}{}",
+ req,
+ sub_path.map(|s| format!("/{}", s)).unwrap_or_default()
+ ))
+ .map_err(|e| {
+ DenoResolveErrorKind::PackageJsonDepValueUrlParse(e).into_box()
+ }),
+ PackageJsonDepValue::Workspace(version_req) => self
+ .workspace_resolver
+ .resolve_workspace_pkg_json_folder_for_pkg_json_dep(
+ alias,
+ version_req,
+ )
+ .map_err(|e| {
+ DenoResolveErrorKind::WorkspaceResolvePkgJsonFolder(e)
+ .into_box()
+ })
+ .and_then(|pkg_folder| {
+ self
+ .node_and_npm_resolver
+ .as_ref()
+ .unwrap()
+ .node_resolver
+ .resolve_package_subpath_from_deno_module(
+ pkg_folder,
+ sub_path.as_deref(),
+ Some(referrer),
+ referrer_kind,
+ mode,
+ )
+ .map_err(|e| {
+ DenoResolveErrorKind::PackageSubpathResolve(e).into_box()
+ })
+ }),
+ })
+ }
+ },
+ Err(err) => Err(err),
+ };
+
+ // When the user is vendoring, don't allow them to import directly from the vendor/ directory
+ // as it might cause them confusion or duplicate dependencies. Additionally, this folder has
+ // special treatment in the language server so it will definitely cause issues/confusion there
+ // if they do this.
+ if let Some(vendor_specifier) = &self.maybe_vendor_specifier {
+ if let Ok(specifier) = &result {
+ if specifier.as_str().starts_with(vendor_specifier.as_str()) {
+ return Err(
+ DenoResolveErrorKind::InvalidVendorFolderImport.into_box(),
+ );
+ }
+ }
+ }
+
+ let Some(NodeAndNpmReqResolver {
+ node_resolver,
+ npm_req_resolver,
+ }) = &self.node_and_npm_resolver
+ else {
+ return Ok(DenoResolution {
+ url: result?,
+ maybe_diagnostic,
+ found_package_json_dep,
+ });
+ };
+
+ match result {
+ Ok(specifier) => {
+ if let Ok(npm_req_ref) =
+ NpmPackageReqReference::from_specifier(&specifier)
+ {
+ // check if the npm specifier resolves to a workspace member
+ if let Some(pkg_folder) = self
+ .workspace_resolver
+ .resolve_workspace_pkg_json_folder_for_npm_specifier(
+ npm_req_ref.req(),
+ )
+ {
+ return node_resolver
+ .resolve_package_subpath_from_deno_module(
+ pkg_folder,
+ npm_req_ref.sub_path(),
+ Some(referrer),
+ referrer_kind,
+ mode,
+ )
+ .map(|url| DenoResolution {
+ url,
+ maybe_diagnostic,
+ found_package_json_dep,
+ })
+ .map_err(|e| e.into());
+ }
+
+ // do npm resolution for byonm
+ if self.is_byonm {
+ return npm_req_resolver
+ .resolve_req_reference(
+ &npm_req_ref,
+ referrer,
+ referrer_kind,
+ mode,
+ )
+ .map(|url| DenoResolution {
+ url,
+ maybe_diagnostic,
+ found_package_json_dep,
+ })
+ .map_err(|err| match err {
+ ResolveReqWithSubPathError::MissingPackageNodeModulesFolder(
+ err,
+ ) => err.into(),
+ ResolveReqWithSubPathError::ResolvePkgFolderFromDenoReq(
+ err,
+ ) => err.into(),
+ ResolveReqWithSubPathError::PackageSubpathResolve(err) => {
+ err.into()
+ }
+ });
+ }
+ }
+
+ Ok(DenoResolution {
+ url: node_resolver
+ .handle_if_in_node_modules(&specifier)
+ .unwrap_or(specifier),
+ maybe_diagnostic,
+ found_package_json_dep,
+ })
+ }
+ Err(err) => {
+ // If byonm, check if the bare specifier resolves to an npm package
+ if self.is_byonm && referrer.scheme() == "file" {
+ let maybe_resolution = npm_req_resolver
+ .resolve_if_for_npm_pkg(
+ raw_specifier,
+ referrer,
+ referrer_kind,
+ mode,
+ )
+ .map_err(|e| match e {
+ ResolveIfForNpmPackageError::NodeResolve(e) => {
+ DenoResolveErrorKind::Node(e).into_box()
+ }
+ ResolveIfForNpmPackageError::NodeModulesOutOfDate(e) => e.into(),
+ })?;
+ if let Some(res) = maybe_resolution {
+ match res {
+ NodeResolution::Module(url) => {
+ return Ok(DenoResolution {
+ url,
+ maybe_diagnostic,
+ found_package_json_dep,
+ })
+ }
+ NodeResolution::BuiltIn(_) => {
+ // don't resolve bare specifiers for built-in modules via node resolution
+ }
+ }
+ }
+ }
+
+ Err(err)
+ }
+ }
+ }
+}
diff --git a/resolvers/deno/npm/byonm.rs b/resolvers/deno/npm/byonm.rs
index b85117052..e9182d47a 100644
--- a/resolvers/deno/npm/byonm.rs
+++ b/resolvers/deno/npm/byonm.rs
@@ -16,7 +16,7 @@ use node_resolver::errors::PackageFolderResolveIoError;
use node_resolver::errors::PackageJsonLoadError;
use node_resolver::errors::PackageNotFoundError;
use node_resolver::InNpmPackageChecker;
-use node_resolver::NpmResolver;
+use node_resolver::NpmPackageFolderResolver;
use node_resolver::PackageJsonResolverRc;
use thiserror::Error;
use url::Url;
@@ -24,6 +24,8 @@ use url::Url;
use crate::fs::DenoResolverFs;
use super::local::normalize_pkg_name_for_node_modules_deno_folder;
+use super::CliNpmReqResolver;
+use super::ResolvePkgFolderFromDenoReqError;
#[derive(Debug, Error)]
pub enum ByonmResolvePkgFolderFromDenoReqError {
@@ -303,7 +305,24 @@ impl<Fs: DenoResolverFs, TEnv: NodeResolverEnv> ByonmNpmResolver<Fs, TEnv> {
impl<
Fs: DenoResolverFs + Send + Sync + std::fmt::Debug,
TEnv: NodeResolverEnv,
- > NpmResolver for ByonmNpmResolver<Fs, TEnv>
+ > CliNpmReqResolver for ByonmNpmResolver<Fs, TEnv>
+{
+ fn resolve_pkg_folder_from_deno_module_req(
+ &self,
+ req: &PackageReq,
+ referrer: &Url,
+ ) -> Result<PathBuf, ResolvePkgFolderFromDenoReqError> {
+ ByonmNpmResolver::resolve_pkg_folder_from_deno_module_req(
+ self, req, referrer,
+ )
+ .map_err(ResolvePkgFolderFromDenoReqError::Byonm)
+ }
+}
+
+impl<
+ Fs: DenoResolverFs + Send + Sync + std::fmt::Debug,
+ TEnv: NodeResolverEnv,
+ > NpmPackageFolderResolver for ByonmNpmResolver<Fs, TEnv>
{
fn resolve_package_folder_from_package(
&self,
diff --git a/resolvers/deno/npm/mod.rs b/resolvers/deno/npm/mod.rs
index 45e2341c7..b0aec71b0 100644
--- a/resolvers/deno/npm/mod.rs
+++ b/resolvers/deno/npm/mod.rs
@@ -1,10 +1,256 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
-mod byonm;
-mod local;
+use std::fmt::Debug;
+use std::path::PathBuf;
+use std::sync::Arc;
+
+use deno_semver::npm::NpmPackageReqReference;
+use deno_semver::package::PackageReq;
+use node_resolver::env::NodeResolverEnv;
+use node_resolver::errors::NodeResolveError;
+use node_resolver::errors::NodeResolveErrorKind;
+use node_resolver::errors::PackageFolderResolveErrorKind;
+use node_resolver::errors::PackageFolderResolveIoError;
+use node_resolver::errors::PackageNotFoundError;
+use node_resolver::errors::PackageResolveErrorKind;
+use node_resolver::errors::PackageSubpathResolveError;
+use node_resolver::InNpmPackageChecker;
+use node_resolver::NodeModuleKind;
+use node_resolver::NodeResolution;
+use node_resolver::NodeResolutionMode;
+use node_resolver::NodeResolver;
+use thiserror::Error;
+use url::Url;
+
+use crate::fs::DenoResolverFs;
pub use byonm::ByonmInNpmPackageChecker;
pub use byonm::ByonmNpmResolver;
pub use byonm::ByonmNpmResolverCreateOptions;
pub use byonm::ByonmResolvePkgFolderFromDenoReqError;
pub use local::normalize_pkg_name_for_node_modules_deno_folder;
+
+mod byonm;
+mod local;
+
+#[derive(Debug, Error)]
+#[error("Could not resolve \"{}\", but found it in a package.json. Deno expects the node_modules/ directory to be up to date. Did you forget to run `deno install`?", specifier)]
+pub struct NodeModulesOutOfDateError {
+ pub specifier: String,
+}
+
+#[derive(Debug, Error)]
+#[error("Could not find '{}'. Deno expects the node_modules/ directory to be up to date. Did you forget to run `deno install`?", package_json_path.display())]
+pub struct MissingPackageNodeModulesFolderError {
+ pub package_json_path: PathBuf,
+}
+
+#[derive(Debug, Error)]
+pub enum ResolveIfForNpmPackageError {
+ #[error(transparent)]
+ NodeResolve(#[from] NodeResolveError),
+ #[error(transparent)]
+ NodeModulesOutOfDate(#[from] NodeModulesOutOfDateError),
+}
+
+#[derive(Debug, Error)]
+pub enum ResolveReqWithSubPathError {
+ #[error(transparent)]
+ MissingPackageNodeModulesFolder(#[from] MissingPackageNodeModulesFolderError),
+ #[error(transparent)]
+ ResolvePkgFolderFromDenoReq(#[from] ResolvePkgFolderFromDenoReqError),
+ #[error(transparent)]
+ PackageSubpathResolve(#[from] PackageSubpathResolveError),
+}
+
+#[derive(Debug, Error)]
+pub enum ResolvePkgFolderFromDenoReqError {
+ // todo(dsherret): don't use anyhow here
+ #[error(transparent)]
+ Managed(anyhow::Error),
+ #[error(transparent)]
+ Byonm(#[from] ByonmResolvePkgFolderFromDenoReqError),
+}
+
+// todo(dsherret): a temporary trait until we extract
+// out the CLI npm resolver into here
+pub trait CliNpmReqResolver: Debug + Send + Sync {
+ fn resolve_pkg_folder_from_deno_module_req(
+ &self,
+ req: &PackageReq,
+ referrer: &Url,
+ ) -> Result<PathBuf, ResolvePkgFolderFromDenoReqError>;
+}
+
+pub struct NpmReqResolverOptions<
+ Fs: DenoResolverFs,
+ TNodeResolverEnv: NodeResolverEnv,
+> {
+ /// The resolver when "bring your own node_modules" is enabled where Deno
+ /// does not setup the node_modules directories automatically, but instead
+ /// uses what already exists on the file system.
+ pub byonm_resolver: Option<Arc<ByonmNpmResolver<Fs, TNodeResolverEnv>>>,
+ pub fs: Fs,
+ pub in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
+ pub node_resolver: Arc<NodeResolver<TNodeResolverEnv>>,
+ pub npm_req_resolver: Arc<dyn CliNpmReqResolver>,
+}
+
+#[derive(Debug)]
+pub struct NpmReqResolver<Fs: DenoResolverFs, TNodeResolverEnv: NodeResolverEnv>
+{
+ byonm_resolver: Option<Arc<ByonmNpmResolver<Fs, TNodeResolverEnv>>>,
+ fs: Fs,
+ in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
+ node_resolver: Arc<NodeResolver<TNodeResolverEnv>>,
+ npm_resolver: Arc<dyn CliNpmReqResolver>,
+}
+
+impl<Fs: DenoResolverFs, TNodeResolverEnv: NodeResolverEnv>
+ NpmReqResolver<Fs, TNodeResolverEnv>
+{
+ pub fn new(options: NpmReqResolverOptions<Fs, TNodeResolverEnv>) -> Self {
+ Self {
+ byonm_resolver: options.byonm_resolver,
+ fs: options.fs,
+ in_npm_pkg_checker: options.in_npm_pkg_checker,
+ node_resolver: options.node_resolver,
+ npm_resolver: options.npm_req_resolver,
+ }
+ }
+
+ pub fn resolve_req_reference(
+ &self,
+ req_ref: &NpmPackageReqReference,
+ referrer: &Url,
+ referrer_kind: NodeModuleKind,
+ mode: NodeResolutionMode,
+ ) -> Result<Url, ResolveReqWithSubPathError> {
+ self.resolve_req_with_sub_path(
+ req_ref.req(),
+ req_ref.sub_path(),
+ referrer,
+ referrer_kind,
+ mode,
+ )
+ }
+
+ pub fn resolve_req_with_sub_path(
+ &self,
+ req: &PackageReq,
+ sub_path: Option<&str>,
+ referrer: &Url,
+ referrer_kind: NodeModuleKind,
+ mode: NodeResolutionMode,
+ ) -> Result<Url, ResolveReqWithSubPathError> {
+ let package_folder = self
+ .npm_resolver
+ .resolve_pkg_folder_from_deno_module_req(req, referrer)?;
+ let resolution_result =
+ self.node_resolver.resolve_package_subpath_from_deno_module(
+ &package_folder,
+ sub_path,
+ Some(referrer),
+ referrer_kind,
+ mode,
+ );
+ match resolution_result {
+ Ok(url) => Ok(url),
+ Err(err) => {
+ if self.byonm_resolver.is_some() {
+ let package_json_path = package_folder.join("package.json");
+ if !self.fs.exists_sync(&package_json_path) {
+ return Err(
+ MissingPackageNodeModulesFolderError { package_json_path }.into(),
+ );
+ }
+ }
+ Err(err.into())
+ }
+ }
+ }
+
+ pub fn resolve_if_for_npm_pkg(
+ &self,
+ specifier: &str,
+ referrer: &Url,
+ referrer_kind: NodeModuleKind,
+ mode: NodeResolutionMode,
+ ) -> Result<Option<NodeResolution>, ResolveIfForNpmPackageError> {
+ let resolution_result =
+ self
+ .node_resolver
+ .resolve(specifier, referrer, referrer_kind, mode);
+ match resolution_result {
+ Ok(res) => Ok(Some(res)),
+ Err(err) => {
+ let err = err.into_kind();
+ match err {
+ NodeResolveErrorKind::RelativeJoin(_)
+ | NodeResolveErrorKind::PackageImportsResolve(_)
+ | NodeResolveErrorKind::UnsupportedEsmUrlScheme(_)
+ | NodeResolveErrorKind::DataUrlReferrer(_)
+ | NodeResolveErrorKind::TypesNotFound(_)
+ | NodeResolveErrorKind::FinalizeResolution(_) => {
+ Err(ResolveIfForNpmPackageError::NodeResolve(err.into()))
+ }
+ NodeResolveErrorKind::PackageResolve(err) => {
+ let err = err.into_kind();
+ match err {
+ PackageResolveErrorKind::ClosestPkgJson(_)
+ | PackageResolveErrorKind::InvalidModuleSpecifier(_)
+ | PackageResolveErrorKind::ExportsResolve(_)
+ | PackageResolveErrorKind::SubpathResolve(_) => {
+ Err(ResolveIfForNpmPackageError::NodeResolve(
+ NodeResolveErrorKind::PackageResolve(err.into()).into(),
+ ))
+ }
+ PackageResolveErrorKind::PackageFolderResolve(err) => {
+ match err.as_kind() {
+ PackageFolderResolveErrorKind::Io(
+ PackageFolderResolveIoError { package_name, .. },
+ )
+ | PackageFolderResolveErrorKind::PackageNotFound(
+ PackageNotFoundError { package_name, .. },
+ ) => {
+ if self.in_npm_pkg_checker.in_npm_package(referrer) {
+ return Err(ResolveIfForNpmPackageError::NodeResolve(
+ NodeResolveErrorKind::PackageResolve(err.into()).into(),
+ ));
+ }
+ if let Some(byonm_npm_resolver) = &self.byonm_resolver {
+ if byonm_npm_resolver
+ .find_ancestor_package_json_with_dep(
+ package_name,
+ referrer,
+ )
+ .is_some()
+ {
+ return Err(
+ ResolveIfForNpmPackageError::NodeModulesOutOfDate(
+ NodeModulesOutOfDateError {
+ specifier: specifier.to_string(),
+ },
+ ),
+ );
+ }
+ }
+ Ok(None)
+ }
+ PackageFolderResolveErrorKind::ReferrerNotFound(_) => {
+ if self.in_npm_pkg_checker.in_npm_package(referrer) {
+ return Err(ResolveIfForNpmPackageError::NodeResolve(
+ NodeResolveErrorKind::PackageResolve(err.into()).into(),
+ ));
+ }
+ Ok(None)
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/resolvers/node/analyze.rs b/resolvers/node/analyze.rs
index c7415933d..912689080 100644
--- a/resolvers/node/analyze.rs
+++ b/resolvers/node/analyze.rs
@@ -23,7 +23,7 @@ use crate::npm::InNpmPackageCheckerRc;
use crate::resolution::NodeResolverRc;
use crate::NodeModuleKind;
use crate::NodeResolutionMode;
-use crate::NpmResolverRc;
+use crate::NpmPackageFolderResolverRc;
use crate::PackageJsonResolverRc;
use crate::PathClean;
@@ -66,7 +66,7 @@ pub struct NodeCodeTranslator<
env: TNodeResolverEnv,
in_npm_pkg_checker: InNpmPackageCheckerRc,
node_resolver: NodeResolverRc<TNodeResolverEnv>,
- npm_resolver: NpmResolverRc,
+ npm_resolver: NpmPackageFolderResolverRc,
pkg_json_resolver: PackageJsonResolverRc<TNodeResolverEnv>,
}
@@ -78,7 +78,7 @@ impl<TCjsCodeAnalyzer: CjsCodeAnalyzer, TNodeResolverEnv: NodeResolverEnv>
env: TNodeResolverEnv,
in_npm_pkg_checker: InNpmPackageCheckerRc,
node_resolver: NodeResolverRc<TNodeResolverEnv>,
- npm_resolver: NpmResolverRc,
+ npm_resolver: NpmPackageFolderResolverRc,
pkg_json_resolver: PackageJsonResolverRc<TNodeResolverEnv>,
) -> Self {
Self {
diff --git a/resolvers/node/lib.rs b/resolvers/node/lib.rs
index 18b0a8536..87bd62994 100644
--- a/resolvers/node/lib.rs
+++ b/resolvers/node/lib.rs
@@ -15,13 +15,14 @@ mod sync;
pub use deno_package_json::PackageJson;
pub use npm::InNpmPackageChecker;
pub use npm::InNpmPackageCheckerRc;
-pub use npm::NpmResolver;
-pub use npm::NpmResolverRc;
+pub use npm::NpmPackageFolderResolver;
+pub use npm::NpmPackageFolderResolverRc;
pub use package_json::PackageJsonResolver;
pub use package_json::PackageJsonResolverRc;
pub use package_json::PackageJsonThreadLocalCache;
pub use path::PathClean;
pub use resolution::parse_npm_pkg_name;
+pub use resolution::resolve_specifier_into_node_modules;
pub use resolution::NodeModuleKind;
pub use resolution::NodeResolution;
pub use resolution::NodeResolutionMode;
diff --git a/resolvers/node/npm.rs b/resolvers/node/npm.rs
index 2132f0b54..ab3a17942 100644
--- a/resolvers/node/npm.rs
+++ b/resolvers/node/npm.rs
@@ -13,10 +13,13 @@ use crate::sync::MaybeSend;
use crate::sync::MaybeSync;
#[allow(clippy::disallowed_types)]
-pub type NpmResolverRc = crate::sync::MaybeArc<dyn NpmResolver>;
+pub type NpmPackageFolderResolverRc =
+ crate::sync::MaybeArc<dyn NpmPackageFolderResolver>;
-pub trait NpmResolver: std::fmt::Debug + MaybeSend + MaybeSync {
- /// Resolves an npm package folder path from an npm package referrer.
+pub trait NpmPackageFolderResolver:
+ std::fmt::Debug + MaybeSend + MaybeSync
+{
+ /// Resolves an npm package folder path from the specified referrer.
fn resolve_package_folder_from_package(
&self,
specifier: &str,
diff --git a/resolvers/node/resolution.rs b/resolvers/node/resolution.rs
index fcff29242..673a61abe 100644
--- a/resolvers/node/resolution.rs
+++ b/resolvers/node/resolution.rs
@@ -41,7 +41,7 @@ use crate::errors::TypesNotFoundErrorData;
use crate::errors::UnsupportedDirImportError;
use crate::errors::UnsupportedEsmUrlSchemeError;
use crate::npm::InNpmPackageCheckerRc;
-use crate::NpmResolverRc;
+use crate::NpmPackageFolderResolverRc;
use crate::PackageJsonResolverRc;
use crate::PathClean;
use deno_package_json::PackageJson;
@@ -101,7 +101,7 @@ pub type NodeResolverRc<TEnv> = crate::sync::MaybeArc<NodeResolver<TEnv>>;
pub struct NodeResolver<TEnv: NodeResolverEnv> {
env: TEnv,
in_npm_pkg_checker: InNpmPackageCheckerRc,
- npm_resolver: NpmResolverRc,
+ npm_pkg_folder_resolver: NpmPackageFolderResolverRc,
pkg_json_resolver: PackageJsonResolverRc<TEnv>,
}
@@ -109,13 +109,13 @@ impl<TEnv: NodeResolverEnv> NodeResolver<TEnv> {
pub fn new(
env: TEnv,
in_npm_pkg_checker: InNpmPackageCheckerRc,
- npm_resolver: NpmResolverRc,
+ npm_pkg_folder_resolver: NpmPackageFolderResolverRc,
pkg_json_resolver: PackageJsonResolverRc<TEnv>,
) -> Self {
Self {
env,
in_npm_pkg_checker,
- npm_resolver,
+ npm_pkg_folder_resolver,
pkg_json_resolver,
}
}
@@ -1126,7 +1126,7 @@ impl<TEnv: NodeResolverEnv> NodeResolver<TEnv> {
mode: NodeResolutionMode,
) -> Result<Url, PackageResolveError> {
let package_dir_path = self
- .npm_resolver
+ .npm_pkg_folder_resolver
.resolve_package_folder_from_package(package_name, referrer)?;
// todo: error with this instead when can't find package
@@ -1412,6 +1412,25 @@ impl<TEnv: NodeResolverEnv> NodeResolver<TEnv> {
)
}
}
+
+ /// Resolves a specifier that is pointing into a node_modules folder by canonicalizing it.
+ ///
+ /// Returns `None` when the specifier is not in a node_modules folder.
+ pub fn handle_if_in_node_modules(&self, specifier: &Url) -> Option<Url> {
+ // skip canonicalizing if we definitely know it's unnecessary
+ if specifier.scheme() == "file"
+ && specifier.path().contains("/node_modules/")
+ {
+ // Specifiers in the node_modules directory are canonicalized
+ // so canoncalize then check if it's in the node_modules directory.
+ let specifier = resolve_specifier_into_node_modules(specifier, &|path| {
+ self.env.realpath_sync(path)
+ });
+ return Some(specifier);
+ }
+
+ None
+ }
}
fn resolve_bin_entry_value<'a>(
@@ -1660,6 +1679,28 @@ pub fn parse_npm_pkg_name(
Ok((package_name, package_subpath, is_scoped))
}
+/// Resolves a specifier that is pointing into a node_modules folder.
+///
+/// Note: This should be called whenever getting the specifier from
+/// a Module::External(module) reference because that module might
+/// not be fully resolved at the time deno_graph is analyzing it
+/// because the node_modules folder might not exist at that time.
+pub fn resolve_specifier_into_node_modules(
+ specifier: &Url,
+ canonicalize: &impl Fn(&Path) -> std::io::Result<PathBuf>,
+) -> Url {
+ deno_path_util::url_to_file_path(specifier)
+ .ok()
+ // this path might not exist at the time the graph is being created
+ // because the node_modules folder might not yet exist
+ .and_then(|path| {
+ deno_path_util::canonicalize_path_maybe_not_exists(&path, canonicalize)
+ .ok()
+ })
+ .and_then(|path| deno_path_util::url_from_file_path(&path).ok())
+ .unwrap_or_else(|| specifier.clone())
+}
+
fn pattern_key_compare(a: &str, b: &str) -> i32 {
let a_pattern_index = a.find('*');
let b_pattern_index = b.find('*');