summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Sherret <dsherret@users.noreply.github.com>2024-07-25 19:08:14 -0400
committerGitHub <noreply@github.com>2024-07-25 19:08:14 -0400
commit3bf147fe287ac779b20d318daba56b336f356adf (patch)
tree3b5bfe2a1ad918b275a2cd08f7dcc05f90a180ab
parent0cf7f268a7df7711ac6ab8c2c67b4d7abf454fcd (diff)
refactor: decouple node resolution from deno_core (#24724)
-rw-r--r--Cargo.lock22
-rw-r--r--Cargo.toml2
-rw-r--r--cli/Cargo.toml1
-rw-r--r--cli/args/mod.rs4
-rw-r--r--cli/factory.rs7
-rw-r--r--cli/lsp/analysis.rs2
-rw-r--r--cli/lsp/resolver.rs10
-rw-r--r--cli/module_loader.rs2
-rw-r--r--cli/node.rs12
-rw-r--r--cli/npm/byonm.rs71
-rw-r--r--cli/npm/managed/mod.rs37
-rw-r--r--cli/npm/managed/resolvers/common.rs2
-rw-r--r--cli/npm/managed/resolvers/global.rs6
-rw-r--r--cli/npm/managed/resolvers/local.rs8
-rw-r--r--cli/npm/mod.rs8
-rw-r--r--cli/resolver.rs24
-rw-r--r--cli/standalone/mod.rs65
-rw-r--r--cli/tools/registry/pm.rs2
-rw-r--r--cli/tsc/mod.rs10
-rw-r--r--cli/worker.rs23
-rw-r--r--ext/fs/clippy.toml38
-rw-r--r--ext/fs/sync.rs62
-rw-r--r--ext/node/Cargo.toml3
-rw-r--r--ext/node/global.rs2
-rw-r--r--ext/node/lib.rs161
-rw-r--r--ext/node/ops/require.rs17
-rw-r--r--ext/node/ops/worker_threads.rs14
-rw-r--r--ext/node/path.rs50
-rw-r--r--ext/node/polyfill.rs14
-rw-r--r--ext/node_resolver/Cargo.toml32
-rw-r--r--ext/node_resolver/README.md6
-rw-r--r--ext/node_resolver/analyze.rs (renamed from ext/node/analyze.rs)63
-rw-r--r--ext/node_resolver/clippy.toml48
-rw-r--r--ext/node_resolver/env.rs39
-rw-r--r--ext/node_resolver/errors.rs (renamed from ext/node/errors.rs)32
-rw-r--r--ext/node_resolver/lib.rs26
-rw-r--r--ext/node_resolver/npm.rs41
-rw-r--r--ext/node_resolver/package_json.rs (renamed from ext/node/package_json.rs)25
-rw-r--r--ext/node_resolver/path.rs142
-rw-r--r--ext/node_resolver/resolution.rs (renamed from ext/node/resolution.rs)207
-rw-r--r--ext/node_resolver/sync.rs86
-rw-r--r--runtime/Cargo.toml1
-rw-r--r--runtime/snapshot.rs2
-rw-r--r--runtime/web_worker.rs8
-rw-r--r--runtime/worker.rs10
45 files changed, 934 insertions, 513 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 019166214..4ff45842d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1167,6 +1167,7 @@ dependencies = [
"monch",
"napi_sym",
"nix 0.26.2",
+ "node_resolver",
"notify",
"once_cell",
"open",
@@ -1767,6 +1768,7 @@ dependencies = [
"libz-sys",
"md-5",
"md4",
+ "node_resolver",
"num-bigint",
"num-bigint-dig",
"num-integer",
@@ -1908,6 +1910,7 @@ dependencies = [
"log",
"netif",
"nix 0.26.2",
+ "node_resolver",
"notify",
"ntapi",
"once_cell",
@@ -4342,6 +4345,25 @@ dependencies = [
]
[[package]]
+name = "node_resolver"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "async-trait",
+ "deno_media_type",
+ "deno_package_json",
+ "futures",
+ "lazy-regex",
+ "once_cell",
+ "path-clean",
+ "regex",
+ "serde_json",
+ "thiserror",
+ "tokio",
+ "url",
+]
+
+[[package]]
name = "nom"
version = "5.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index 1e9f53e46..3902a028a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -21,6 +21,7 @@ members = [
"ext/napi",
"ext/net",
"ext/node",
+ "ext/node_resolver",
"ext/url",
"ext/web",
"ext/webgpu",
@@ -83,6 +84,7 @@ deno_webgpu = { version = "0.129.0", path = "./ext/webgpu" }
deno_webidl = { version = "0.162.0", path = "./ext/webidl" }
deno_websocket = { version = "0.167.0", path = "./ext/websocket" }
deno_webstorage = { version = "0.157.0", path = "./ext/webstorage" }
+node_resolver = { version = "0.1.0", path = "./ext/node_resolver" }
aes = "=0.8.3"
anyhow = "1.0.57"
diff --git a/cli/Cargo.toml b/cli/Cargo.toml
index a19dcbe3d..cda410c63 100644
--- a/cli/Cargo.toml
+++ b/cli/Cargo.toml
@@ -80,6 +80,7 @@ deno_task_shell = "=0.17.0"
deno_terminal.workspace = true
eszip = "=0.72.2"
napi_sym.workspace = true
+node_resolver.workspace = true
async-trait.workspace = true
base32.workspace = true
diff --git a/cli/args/mod.rs b/cli/args/mod.rs
index aea6ed8a8..ea79aaa46 100644
--- a/cli/args/mod.rs
+++ b/cli/args/mod.rs
@@ -820,9 +820,7 @@ impl CliOptions {
WorkspaceDiscoverOptions {
fs: Default::default(), // use real fs
deno_json_cache: None,
- pkg_json_cache: Some(
- &deno_runtime::deno_node::PackageJsonThreadLocalCache,
- ),
+ pkg_json_cache: Some(&node_resolver::PackageJsonThreadLocalCache),
workspace_cache: None,
config_parse_options,
additional_config_file_names,
diff --git a/cli/factory.rs b/cli/factory.rs
index aeab3cbc4..3e618e239 100644
--- a/cli/factory.rs
+++ b/cli/factory.rs
@@ -62,13 +62,14 @@ use deno_core::futures::FutureExt;
use deno_core::FeatureChecker;
use deno_runtime::deno_fs;
-use deno_runtime::deno_node::analyze::NodeCodeTranslator;
+use deno_runtime::deno_node::DenoFsNodeResolverEnv;
use deno_runtime::deno_node::NodeResolver;
use deno_runtime::deno_tls::rustls::RootCertStore;
use deno_runtime::deno_tls::RootCertStoreProvider;
use deno_runtime::deno_web::BlobStore;
use deno_runtime::inspector_server::InspectorServer;
use log::warn;
+use node_resolver::analyze::NodeCodeTranslator;
use once_cell::sync::OnceCell;
use std::future::Future;
use std::sync::Arc;
@@ -553,7 +554,7 @@ impl CliFactory {
.get_or_try_init_async(
async {
Ok(Arc::new(NodeResolver::new(
- self.fs().clone(),
+ DenoFsNodeResolverEnv::new(self.fs().clone()),
self.npm_resolver().await?.clone().into_npm_resolver(),
)))
}
@@ -577,7 +578,7 @@ impl CliFactory {
Ok(Arc::new(NodeCodeTranslator::new(
cjs_esm_analyzer,
- self.fs().clone(),
+ DenoFsNodeResolverEnv::new(self.fs().clone()),
self.node_resolver().await?.clone(),
self.npm_resolver().await?.clone().into_npm_resolver(),
)))
diff --git a/cli/lsp/analysis.rs b/cli/lsp/analysis.rs
index 97730ac7e..ec8bd4a28 100644
--- a/cli/lsp/analysis.rs
+++ b/cli/lsp/analysis.rs
@@ -23,7 +23,6 @@ use deno_core::serde::Serialize;
use deno_core::serde_json;
use deno_core::serde_json::json;
use deno_core::ModuleSpecifier;
-use deno_runtime::deno_node::NpmResolver;
use deno_runtime::deno_node::PathClean;
use deno_semver::jsr::JsrPackageNvReference;
use deno_semver::jsr::JsrPackageReqReference;
@@ -34,6 +33,7 @@ use deno_semver::package::PackageReq;
use deno_semver::package::PackageReqReference;
use deno_semver::Version;
use import_map::ImportMap;
+use node_resolver::NpmResolver;
use once_cell::sync::Lazy;
use regex::Regex;
use std::cmp::Ordering;
diff --git a/cli/lsp/resolver.rs b/cli/lsp/resolver.rs
index bdfd5fd3e..d6fc3096c 100644
--- a/cli/lsp/resolver.rs
+++ b/cli/lsp/resolver.rs
@@ -35,11 +35,7 @@ use deno_graph::GraphImport;
use deno_graph::ModuleSpecifier;
use deno_npm::NpmSystemInfo;
use deno_runtime::deno_fs;
-use deno_runtime::deno_node::errors::ClosestPkgJsonError;
-use deno_runtime::deno_node::NodeResolution;
-use deno_runtime::deno_node::NodeResolutionMode;
use deno_runtime::deno_node::NodeResolver;
-use deno_runtime::deno_node::NpmResolver;
use deno_runtime::deno_node::PackageJson;
use deno_runtime::fs_util::specifier_to_file_path;
use deno_semver::jsr::JsrPackageReqReference;
@@ -47,6 +43,10 @@ use deno_semver::npm::NpmPackageReqReference;
use deno_semver::package::PackageNv;
use deno_semver::package::PackageReq;
use indexmap::IndexMap;
+use node_resolver::errors::ClosestPkgJsonError;
+use node_resolver::NodeResolution;
+use node_resolver::NodeResolutionMode;
+use node_resolver::NpmResolver;
use std::borrow::Cow;
use std::collections::BTreeMap;
use std::collections::BTreeSet;
@@ -496,7 +496,7 @@ fn create_node_resolver(
let npm_resolver = npm_resolver?;
let fs = Arc::new(deno_fs::RealFs);
let node_resolver_inner = Arc::new(NodeResolver::new(
- fs.clone(),
+ deno_runtime::deno_node::DenoFsNodeResolverEnv::new(fs.clone()),
npm_resolver.clone().into_npm_resolver(),
));
Some(Arc::new(CliNodeResolver::new(
diff --git a/cli/module_loader.rs b/cli/module_loader.rs
index 2e047d36d..bda4e58d8 100644
--- a/cli/module_loader.rs
+++ b/cli/module_loader.rs
@@ -64,9 +64,9 @@ use deno_graph::Module;
use deno_graph::ModuleGraph;
use deno_graph::Resolution;
use deno_runtime::code_cache;
-use deno_runtime::deno_node::NodeResolutionMode;
use deno_runtime::deno_permissions::PermissionsContainer;
use deno_semver::npm::NpmPackageReqReference;
+use node_resolver::NodeResolutionMode;
pub async fn load_top_level_deps(factory: &CliFactory) -> Result<(), AnyError> {
let npm_resolver = factory.npm_resolver().await?;
diff --git a/cli/node.rs b/cli/node.rs
index 5ecbacdc7..0fd18e299 100644
--- a/cli/node.rs
+++ b/cli/node.rs
@@ -6,10 +6,11 @@ use deno_ast::MediaType;
use deno_ast::ModuleSpecifier;
use deno_core::error::AnyError;
use deno_runtime::deno_fs;
-use deno_runtime::deno_node::analyze::CjsAnalysis as ExtNodeCjsAnalysis;
-use deno_runtime::deno_node::analyze::CjsAnalysisExports;
-use deno_runtime::deno_node::analyze::CjsCodeAnalyzer;
-use deno_runtime::deno_node::analyze::NodeCodeTranslator;
+use deno_runtime::deno_node::DenoFsNodeResolverEnv;
+use node_resolver::analyze::CjsAnalysis as ExtNodeCjsAnalysis;
+use node_resolver::analyze::CjsAnalysisExports;
+use node_resolver::analyze::CjsCodeAnalyzer;
+use node_resolver::analyze::NodeCodeTranslator;
use serde::Deserialize;
use serde::Serialize;
@@ -17,7 +18,8 @@ use crate::cache::CacheDBHash;
use crate::cache::NodeAnalysisCache;
use crate::util::fs::canonicalize_path_maybe_not_exists;
-pub type CliNodeCodeTranslator = NodeCodeTranslator<CliCjsCodeAnalyzer>;
+pub type CliNodeCodeTranslator =
+ NodeCodeTranslator<CliCjsCodeAnalyzer, DenoFsNodeResolverEnv>;
/// Resolves a specifier that is pointing into a node_modules folder.
///
diff --git a/cli/npm/byonm.rs b/cli/npm/byonm.rs
index 86c9badac..a0f23fc66 100644
--- a/cli/npm/byonm.rs
+++ b/cli/npm/byonm.rs
@@ -11,14 +11,18 @@ use deno_core::error::AnyError;
use deno_core::serde_json;
use deno_package_json::PackageJsonDepValue;
use deno_runtime::deno_fs::FileSystem;
-use deno_runtime::deno_node::errors::PackageFolderResolveError;
-use deno_runtime::deno_node::errors::PackageFolderResolveIoError;
-use deno_runtime::deno_node::errors::PackageNotFoundError;
-use deno_runtime::deno_node::load_pkg_json;
+use deno_runtime::deno_node::DenoPkgJsonFsAdapter;
use deno_runtime::deno_node::NodePermissions;
-use deno_runtime::deno_node::NpmResolver;
+use deno_runtime::deno_node::NodeRequireResolver;
+use deno_runtime::deno_node::NpmProcessStateProvider;
use deno_runtime::deno_node::PackageJson;
use deno_semver::package::PackageReq;
+use node_resolver::errors::PackageFolderResolveError;
+use node_resolver::errors::PackageFolderResolveIoError;
+use node_resolver::errors::PackageJsonLoadError;
+use node_resolver::errors::PackageNotFoundError;
+use node_resolver::load_pkg_json;
+use node_resolver::NpmResolver;
use crate::args::NpmProcessState;
use crate::args::NpmProcessStateKind;
@@ -50,6 +54,15 @@ pub struct ByonmCliNpmResolver {
}
impl ByonmCliNpmResolver {
+ fn load_pkg_json(
+ &self,
+ path: &Path,
+ ) -> Result<Option<Arc<PackageJson>>, PackageJsonLoadError> {
+ load_pkg_json(&DenoPkgJsonFsAdapter(self.fs.as_ref()), path)
+ }
+}
+
+impl ByonmCliNpmResolver {
/// Finds the ancestor package.json that contains the specified dependency.
pub fn find_ancestor_package_json_with_dep(
&self,
@@ -60,9 +73,7 @@ impl ByonmCliNpmResolver {
let mut current_folder = referrer_path.parent()?;
loop {
let pkg_json_path = current_folder.join("package.json");
- if let Ok(Some(pkg_json)) =
- load_pkg_json(self.fs.as_ref(), &pkg_json_path)
- {
+ if let Ok(Some(pkg_json)) = self.load_pkg_json(&pkg_json_path) {
if let Some(deps) = &pkg_json.dependencies {
if deps.contains_key(dep_name) {
return Some(pkg_json);
@@ -119,9 +130,7 @@ impl ByonmCliNpmResolver {
let mut current_path = file_path.as_path();
while let Some(dir_path) = current_path.parent() {
let package_json_path = dir_path.join("package.json");
- if let Some(pkg_json) =
- load_pkg_json(self.fs.as_ref(), &package_json_path)?
- {
+ if let Some(pkg_json) = self.load_pkg_json(&package_json_path)? {
if let Some(alias) =
resolve_alias_from_pkg_json(req, pkg_json.as_ref())
{
@@ -136,9 +145,7 @@ impl ByonmCliNpmResolver {
if let Some(root_node_modules_dir) = &self.root_node_modules_dir {
let root_pkg_json_path =
root_node_modules_dir.parent().unwrap().join("package.json");
- if let Some(pkg_json) =
- load_pkg_json(self.fs.as_ref(), &root_pkg_json_path)?
- {
+ if let Some(pkg_json) = self.load_pkg_json(&root_pkg_json_path)? {
if let Some(alias) = resolve_alias_from_pkg_json(req, pkg_json.as_ref())
{
return Ok((pkg_json, alias));
@@ -158,17 +165,6 @@ impl ByonmCliNpmResolver {
}
impl NpmResolver for ByonmCliNpmResolver {
- fn get_npm_process_state(&self) -> String {
- serde_json::to_string(&NpmProcessState {
- kind: NpmProcessStateKind::Byonm,
- local_node_modules_path: self
- .root_node_modules_dir
- .as_ref()
- .map(|p| p.to_string_lossy().to_string()),
- })
- .unwrap()
- }
-
fn resolve_package_folder_from_package(
&self,
name: &str,
@@ -226,7 +222,9 @@ impl NpmResolver for ByonmCliNpmResolver {
.to_ascii_lowercase()
.contains("/node_modules/")
}
+}
+impl NodeRequireResolver for ByonmCliNpmResolver {
fn ensure_read_permission(
&self,
permissions: &mut dyn NodePermissions,
@@ -242,11 +240,34 @@ impl NpmResolver for ByonmCliNpmResolver {
}
}
+impl NpmProcessStateProvider for ByonmCliNpmResolver {
+ fn get_npm_process_state(&self) -> String {
+ serde_json::to_string(&NpmProcessState {
+ kind: NpmProcessStateKind::Byonm,
+ local_node_modules_path: self
+ .root_node_modules_dir
+ .as_ref()
+ .map(|p| p.to_string_lossy().to_string()),
+ })
+ .unwrap()
+ }
+}
+
impl CliNpmResolver for ByonmCliNpmResolver {
fn into_npm_resolver(self: Arc<Self>) -> Arc<dyn NpmResolver> {
self
}
+ fn into_require_resolver(self: Arc<Self>) -> Arc<dyn NodeRequireResolver> {
+ self
+ }
+
+ fn into_process_state_provider(
+ self: Arc<Self>,
+ ) -> Arc<dyn NpmProcessStateProvider> {
+ self
+ }
+
fn clone_snapshotted(&self) -> Arc<dyn CliNpmResolver> {
Arc::new(Self {
fs: self.fs.clone(),
diff --git a/cli/npm/managed/mod.rs b/cli/npm/managed/mod.rs
index 602733cab..1561d3969 100644
--- a/cli/npm/managed/mod.rs
+++ b/cli/npm/managed/mod.rs
@@ -20,12 +20,14 @@ use deno_npm::NpmPackageId;
use deno_npm::NpmResolutionPackage;
use deno_npm::NpmSystemInfo;
use deno_runtime::deno_fs::FileSystem;
-use deno_runtime::deno_node::errors::PackageFolderResolveError;
-use deno_runtime::deno_node::errors::PackageFolderResolveIoError;
use deno_runtime::deno_node::NodePermissions;
-use deno_runtime::deno_node::NpmResolver;
+use deno_runtime::deno_node::NodeRequireResolver;
+use deno_runtime::deno_node::NpmProcessStateProvider;
use deno_semver::package::PackageNv;
use deno_semver::package::PackageReq;
+use node_resolver::errors::PackageFolderResolveError;
+use node_resolver::errors::PackageFolderResolveIoError;
+use node_resolver::NpmResolver;
use resolution::AddPkgReqsResult;
use crate::args::CliLockfile;
@@ -531,14 +533,6 @@ fn npm_process_state(
}
impl NpmResolver for ManagedCliNpmResolver {
- /// Gets the state of npm for the process.
- fn get_npm_process_state(&self) -> String {
- npm_process_state(
- self.resolution.serialized_valid_snapshot(),
- self.fs_resolver.node_modules_path().map(|p| p.as_path()),
- )
- }
-
fn resolve_package_folder_from_package(
&self,
name: &str,
@@ -563,7 +557,9 @@ impl NpmResolver for ManagedCliNpmResolver {
debug_assert!(root_dir_url.as_str().ends_with('/'));
specifier.as_ref().starts_with(root_dir_url.as_str())
}
+}
+impl NodeRequireResolver for ManagedCliNpmResolver {
fn ensure_read_permission(
&self,
permissions: &mut dyn NodePermissions,
@@ -573,11 +569,30 @@ impl NpmResolver for ManagedCliNpmResolver {
}
}
+impl NpmProcessStateProvider for ManagedCliNpmResolver {
+ fn get_npm_process_state(&self) -> String {
+ npm_process_state(
+ self.resolution.serialized_valid_snapshot(),
+ self.fs_resolver.node_modules_path().map(|p| p.as_path()),
+ )
+ }
+}
+
impl CliNpmResolver for ManagedCliNpmResolver {
fn into_npm_resolver(self: Arc<Self>) -> Arc<dyn NpmResolver> {
self
}
+ fn into_require_resolver(self: Arc<Self>) -> Arc<dyn NodeRequireResolver> {
+ self
+ }
+
+ fn into_process_state_provider(
+ self: Arc<Self>,
+ ) -> Arc<dyn NpmProcessStateProvider> {
+ self
+ }
+
fn clone_snapshotted(&self) -> Arc<dyn CliNpmResolver> {
// create a new snapshotted npm resolution and resolver
let npm_resolution = Arc::new(NpmResolution::new(
diff --git a/cli/npm/managed/resolvers/common.rs b/cli/npm/managed/resolvers/common.rs
index dffa1b75c..170dc2ae6 100644
--- a/cli/npm/managed/resolvers/common.rs
+++ b/cli/npm/managed/resolvers/common.rs
@@ -18,8 +18,8 @@ use deno_npm::NpmPackageCacheFolderId;
use deno_npm::NpmPackageId;
use deno_npm::NpmResolutionPackage;
use deno_runtime::deno_fs::FileSystem;
-use deno_runtime::deno_node::errors::PackageFolderResolveError;
use deno_runtime::deno_node::NodePermissions;
+use node_resolver::errors::PackageFolderResolveError;
use crate::npm::managed::cache::TarballCache;
diff --git a/cli/npm/managed/resolvers/global.rs b/cli/npm/managed/resolvers/global.rs
index e7a57fc23..7f8f285f3 100644
--- a/cli/npm/managed/resolvers/global.rs
+++ b/cli/npm/managed/resolvers/global.rs
@@ -14,10 +14,10 @@ use deno_npm::NpmPackageCacheFolderId;
use deno_npm::NpmPackageId;
use deno_npm::NpmSystemInfo;
use deno_runtime::deno_fs::FileSystem;
-use deno_runtime::deno_node::errors::PackageFolderResolveError;
-use deno_runtime::deno_node::errors::PackageNotFoundError;
-use deno_runtime::deno_node::errors::ReferrerNotFoundError;
use deno_runtime::deno_node::NodePermissions;
+use node_resolver::errors::PackageFolderResolveError;
+use node_resolver::errors::PackageNotFoundError;
+use node_resolver::errors::ReferrerNotFoundError;
use super::super::cache::NpmCache;
use super::super::cache::TarballCache;
diff --git a/cli/npm/managed/resolvers/local.rs b/cli/npm/managed/resolvers/local.rs
index cda78548b..b741fd15d 100644
--- a/cli/npm/managed/resolvers/local.rs
+++ b/cli/npm/managed/resolvers/local.rs
@@ -32,12 +32,12 @@ use deno_npm::NpmPackageId;
use deno_npm::NpmResolutionPackage;
use deno_npm::NpmSystemInfo;
use deno_runtime::deno_fs;
-use deno_runtime::deno_node::errors::PackageFolderResolveError;
-use deno_runtime::deno_node::errors::PackageFolderResolveIoError;
-use deno_runtime::deno_node::errors::PackageNotFoundError;
-use deno_runtime::deno_node::errors::ReferrerNotFoundError;
use deno_runtime::deno_node::NodePermissions;
use deno_semver::package::PackageNv;
+use node_resolver::errors::PackageFolderResolveError;
+use node_resolver::errors::PackageFolderResolveIoError;
+use node_resolver::errors::PackageNotFoundError;
+use node_resolver::errors::ReferrerNotFoundError;
use serde::Deserialize;
use serde::Serialize;
diff --git a/cli/npm/mod.rs b/cli/npm/mod.rs
index 8ae81de24..f883883aa 100644
--- a/cli/npm/mod.rs
+++ b/cli/npm/mod.rs
@@ -13,10 +13,12 @@ use deno_ast::ModuleSpecifier;
use deno_core::error::AnyError;
use deno_core::serde_json;
use deno_npm::registry::NpmPackageInfo;
-use deno_runtime::deno_node::NpmResolver;
+use deno_runtime::deno_node::NodeRequireResolver;
+use deno_runtime::deno_node::NpmProcessStateProvider;
use deno_runtime::deno_permissions::PermissionsContainer;
use deno_semver::package::PackageNv;
use deno_semver::package::PackageReq;
+use node_resolver::NpmResolver;
use crate::args::npm_registry_url;
use crate::file_fetcher::FileFetcher;
@@ -63,6 +65,10 @@ pub enum InnerCliNpmResolverRef<'a> {
pub trait CliNpmResolver: NpmResolver {
fn into_npm_resolver(self: Arc<Self>) -> Arc<dyn NpmResolver>;
+ fn into_require_resolver(self: Arc<Self>) -> Arc<dyn NodeRequireResolver>;
+ fn into_process_state_provider(
+ self: Arc<Self>,
+ ) -> Arc<dyn NpmProcessStateProvider>;
fn clone_snapshotted(&self) -> Arc<dyn CliNpmResolver>;
diff --git a/cli/resolver.rs b/cli/resolver.rs
index 5296b42b8..18804c025 100644
--- a/cli/resolver.rs
+++ b/cli/resolver.rs
@@ -23,23 +23,23 @@ use deno_npm::resolution::NpmResolutionError;
use deno_package_json::PackageJsonDepValue;
use deno_runtime::deno_fs;
use deno_runtime::deno_fs::FileSystem;
-use deno_runtime::deno_node::errors::ClosestPkgJsonError;
-use deno_runtime::deno_node::errors::NodeResolveError;
-use deno_runtime::deno_node::errors::NodeResolveErrorKind;
-use deno_runtime::deno_node::errors::PackageFolderResolveErrorKind;
-use deno_runtime::deno_node::errors::PackageFolderResolveIoError;
-use deno_runtime::deno_node::errors::PackageNotFoundError;
-use deno_runtime::deno_node::errors::PackageResolveErrorKind;
-use deno_runtime::deno_node::errors::UrlToNodeResolutionError;
use deno_runtime::deno_node::is_builtin_node_module;
-use deno_runtime::deno_node::NodeModuleKind;
-use deno_runtime::deno_node::NodeResolution;
-use deno_runtime::deno_node::NodeResolutionMode;
use deno_runtime::deno_node::NodeResolver;
-use deno_runtime::deno_node::PackageJson;
use deno_runtime::fs_util::specifier_to_file_path;
use deno_semver::npm::NpmPackageReqReference;
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::UrlToNodeResolutionError;
+use node_resolver::NodeModuleKind;
+use node_resolver::NodeResolution;
+use node_resolver::NodeResolutionMode;
+use node_resolver::PackageJson;
use std::borrow::Cow;
use std::path::Path;
use std::path::PathBuf;
diff --git a/cli/standalone/mod.rs b/cli/standalone/mod.rs
index c91f3bec9..1538807f1 100644
--- a/cli/standalone/mod.rs
+++ b/cli/standalone/mod.rs
@@ -5,34 +5,6 @@
#![allow(dead_code)]
#![allow(unused_imports)]
-use crate::args::create_default_npmrc;
-use crate::args::get_root_cert_store;
-use crate::args::npm_pkg_req_ref_to_binary_command;
-use crate::args::CaData;
-use crate::args::CacheSetting;
-use crate::args::PackageJsonInstallDepsProvider;
-use crate::args::StorageKeyResolver;
-use crate::cache::Caches;
-use crate::cache::DenoDirProvider;
-use crate::cache::NodeAnalysisCache;
-use crate::http_util::HttpClientProvider;
-use crate::node::CliCjsCodeAnalyzer;
-use crate::npm::create_cli_npm_resolver;
-use crate::npm::CliNpmResolverByonmCreateOptions;
-use crate::npm::CliNpmResolverCreateOptions;
-use crate::npm::CliNpmResolverManagedCreateOptions;
-use crate::npm::CliNpmResolverManagedSnapshotOption;
-use crate::npm::NpmCacheDir;
-use crate::resolver::CjsResolutionStore;
-use crate::resolver::CliNodeResolver;
-use crate::resolver::NpmModuleLoader;
-use crate::util::progress_bar::ProgressBar;
-use crate::util::progress_bar::ProgressBarStyle;
-use crate::util::v8::construct_v8_flags;
-use crate::worker::CliMainWorkerFactory;
-use crate::worker::CliMainWorkerOptions;
-use crate::worker::ModuleLoaderAndSourceMapGetter;
-use crate::worker::ModuleLoaderFactory;
use deno_ast::MediaType;
use deno_config::workspace::MappedResolution;
use deno_config::workspace::MappedResolutionError;
@@ -53,8 +25,6 @@ use deno_core::ResolutionKind;
use deno_npm::npm_rc::ResolvedNpmRc;
use deno_package_json::PackageJsonDepValue;
use deno_runtime::deno_fs;
-use deno_runtime::deno_node::analyze::NodeCodeTranslator;
-use deno_runtime::deno_node::NodeResolutionMode;
use deno_runtime::deno_node::NodeResolver;
use deno_runtime::deno_permissions::Permissions;
use deno_runtime::deno_permissions::PermissionsContainer;
@@ -65,10 +35,41 @@ use deno_runtime::WorkerLogLevel;
use deno_semver::npm::NpmPackageReqReference;
use eszip::EszipRelativeFileBaseUrl;
use import_map::parse_from_json;
+use node_resolver::analyze::NodeCodeTranslator;
+use node_resolver::NodeResolutionMode;
use std::borrow::Cow;
use std::rc::Rc;
use std::sync::Arc;
+use crate::args::create_default_npmrc;
+use crate::args::get_root_cert_store;
+use crate::args::npm_pkg_req_ref_to_binary_command;
+use crate::args::CaData;
+use crate::args::CacheSetting;
+use crate::args::PackageJsonInstallDepsProvider;
+use crate::args::StorageKeyResolver;
+use crate::cache::Caches;
+use crate::cache::DenoDirProvider;
+use crate::cache::NodeAnalysisCache;
+use crate::http_util::HttpClientProvider;
+use crate::node::CliCjsCodeAnalyzer;
+use crate::npm::create_cli_npm_resolver;
+use crate::npm::CliNpmResolverByonmCreateOptions;
+use crate::npm::CliNpmResolverCreateOptions;
+use crate::npm::CliNpmResolverManagedCreateOptions;
+use crate::npm::CliNpmResolverManagedSnapshotOption;
+use crate::npm::NpmCacheDir;
+use crate::resolver::CjsResolutionStore;
+use crate::resolver::CliNodeResolver;
+use crate::resolver::NpmModuleLoader;
+use crate::util::progress_bar::ProgressBar;
+use crate::util::progress_bar::ProgressBarStyle;
+use crate::util::v8::construct_v8_flags;
+use crate::worker::CliMainWorkerFactory;
+use crate::worker::CliMainWorkerOptions;
+use crate::worker::ModuleLoaderAndSourceMapGetter;
+use crate::worker::ModuleLoaderFactory;
+
pub mod binary;
mod file_system;
mod virtual_fs;
@@ -549,7 +550,7 @@ pub async fn run(
let has_node_modules_dir = npm_resolver.root_node_modules_path().is_some();
let node_resolver = Arc::new(NodeResolver::new(
- fs.clone(),
+ deno_runtime::deno_node::DenoFsNodeResolverEnv::new(fs.clone()),
npm_resolver.clone().into_npm_resolver(),
));
let cjs_resolutions = Arc::new(CjsResolutionStore::default());
@@ -559,7 +560,7 @@ pub async fn run(
CliCjsCodeAnalyzer::new(node_analysis_cache, fs.clone());
let node_code_translator = Arc::new(NodeCodeTranslator::new(
cjs_esm_code_analyzer,
- fs.clone(),
+ deno_runtime::deno_node::DenoFsNodeResolverEnv::new(fs.clone()),
node_resolver.clone(),
npm_resolver.clone().into_npm_resolver(),
));
diff --git a/cli/tools/registry/pm.rs b/cli/tools/registry/pm.rs
index 233e68240..2986c1c2a 100644
--- a/cli/tools/registry/pm.rs
+++ b/cli/tools/registry/pm.rs
@@ -308,7 +308,7 @@ pub async fn add(
.context("Failed to update configuration file")?;
// clear the previously cached package.json from memory before reloading it
- deno_node::PackageJsonThreadLocalCache::clear();
+ node_resolver::PackageJsonThreadLocalCache::clear();
// make a new CliFactory to pick up the updated config file
let cli_factory = CliFactory::from_flags(flags);
// cache deps
diff --git a/cli/tsc/mod.rs b/cli/tsc/mod.rs
index 424b5c3d3..ac7fc48e3 100644
--- a/cli/tsc/mod.rs
+++ b/cli/tsc/mod.rs
@@ -30,14 +30,14 @@ use deno_graph::GraphKind;
use deno_graph::Module;
use deno_graph::ModuleGraph;
use deno_graph::ResolutionResolved;
-use deno_runtime::deno_node::errors::NodeJsErrorCode;
-use deno_runtime::deno_node::errors::NodeJsErrorCoded;
-use deno_runtime::deno_node::NodeModuleKind;
-use deno_runtime::deno_node::NodeResolution;
-use deno_runtime::deno_node::NodeResolutionMode;
use deno_runtime::deno_node::NodeResolver;
use deno_semver::npm::NpmPackageReqReference;
use lsp_types::Url;
+use node_resolver::errors::NodeJsErrorCode;
+use node_resolver::errors::NodeJsErrorCoded;
+use node_resolver::NodeModuleKind;
+use node_resolver::NodeResolution;
+use node_resolver::NodeResolutionMode;
use once_cell::sync::Lazy;
use std::borrow::Cow;
use std::collections::HashMap;
diff --git a/cli/worker.rs b/cli/worker.rs
index 0d7e61c50..8673804ab 100644
--- a/cli/worker.rs
+++ b/cli/worker.rs
@@ -22,8 +22,7 @@ use deno_runtime::code_cache;
use deno_runtime::deno_broadcast_channel::InMemoryBroadcastChannel;
use deno_runtime::deno_fs;
use deno_runtime::deno_node;
-use deno_runtime::deno_node::NodeResolution;
-use deno_runtime::deno_node::NodeResolutionMode;
+use deno_runtime::deno_node::NodeExtInitServices;
use deno_runtime::deno_node::NodeResolver;
use deno_runtime::deno_permissions::PermissionsContainer;
use deno_runtime::deno_tls::RootCertStoreProvider;
@@ -40,6 +39,8 @@ use deno_runtime::WorkerExecutionMode;
use deno_runtime::WorkerLogLevel;
use deno_semver::npm::NpmPackageReqReference;
use deno_terminal::colors;
+use node_resolver::NodeResolution;
+use node_resolver::NodeResolutionMode;
use tokio::select;
use crate::args::CliLockfile;
@@ -144,7 +145,17 @@ struct SharedWorkerState {
}
impl SharedWorkerState {
- // Currently empty
+ pub fn create_node_init_services(&self) -> NodeExtInitServices {
+ NodeExtInitServices {
+ node_require_resolver: self.npm_resolver.clone().into_require_resolver(),
+ node_resolver: self.node_resolver.clone(),
+ npm_process_state_provider: self
+ .npm_resolver
+ .clone()
+ .into_process_state_provider(),
+ npm_resolver: self.npm_resolver.clone().into_npm_resolver(),
+ }
+ }
}
pub struct CliMainWorker {
@@ -599,8 +610,7 @@ impl CliMainWorkerFactory {
strace_ops: shared.options.strace_ops.clone(),
module_loader,
fs: shared.fs.clone(),
- node_resolver: Some(shared.node_resolver.clone()),
- npm_resolver: Some(shared.npm_resolver.clone().into_npm_resolver()),
+ node_services: Some(shared.create_node_init_services()),
get_error_class_fn: Some(&errors::get_error_class_name),
cache_storage_dir,
origin_storage_dir,
@@ -793,8 +803,7 @@ fn create_web_worker_callback(
format_js_error_fn: Some(Arc::new(format_js_error)),
module_loader,
fs: shared.fs.clone(),
- node_resolver: Some(shared.node_resolver.clone()),
- npm_resolver: Some(shared.npm_resolver.clone().into_npm_resolver()),
+ node_services: Some(shared.create_node_init_services()),
worker_type: args.worker_type,
maybe_inspector_server,
get_error_class_fn: Some(&errors::get_error_class_name),
diff --git a/ext/fs/clippy.toml b/ext/fs/clippy.toml
index 023769214..943d28c6d 100644
--- a/ext/fs/clippy.toml
+++ b/ext/fs/clippy.toml
@@ -1,24 +1,24 @@
disallowed-methods = [
{ path = "std::env::current_dir", reason = "File system operations should be done using FileSystem trait" },
- { path = "std::path::Path::canonicalize", reason = "File system operations should be done using NodeFs trait" },
- { path = "std::path::Path::is_dir", reason = "File system operations should be done using NodeFs trait" },
- { path = "std::path::Path::is_file", reason = "File system operations should be done using NodeFs trait" },
- { path = "std::path::Path::is_symlink", reason = "File system operations should be done using NodeFs trait" },
- { path = "std::path::Path::metadata", reason = "File system operations should be done using NodeFs trait" },
- { path = "std::path::Path::read_dir", reason = "File system operations should be done using NodeFs trait" },
- { path = "std::path::Path::read_link", reason = "File system operations should be done using NodeFs trait" },
- { path = "std::path::Path::symlink_metadata", reason = "File system operations should be done using NodeFs trait" },
- { path = "std::path::Path::try_exists", reason = "File system operations should be done using NodeFs trait" },
- { path = "std::path::PathBuf::exists", reason = "File system operations should be done using NodeFs trait" },
- { path = "std::path::PathBuf::canonicalize", reason = "File system operations should be done using NodeFs trait" },
- { path = "std::path::PathBuf::is_dir", reason = "File system operations should be done using NodeFs trait" },
- { path = "std::path::PathBuf::is_file", reason = "File system operations should be done using NodeFs trait" },
- { path = "std::path::PathBuf::is_symlink", reason = "File system operations should be done using NodeFs trait" },
- { path = "std::path::PathBuf::metadata", reason = "File system operations should be done using NodeFs trait" },
- { path = "std::path::PathBuf::read_dir", reason = "File system operations should be done using NodeFs trait" },
- { path = "std::path::PathBuf::read_link", reason = "File system operations should be done using NodeFs trait" },
- { path = "std::path::PathBuf::symlink_metadata", reason = "File system operations should be done using NodeFs trait" },
- { path = "std::path::PathBuf::try_exists", reason = "File system operations should be done using NodeFs trait" },
+ { path = "std::path::Path::canonicalize", reason = "File system operations should be done using FileSystem trait" },
+ { path = "std::path::Path::is_dir", reason = "File system operations should be done using FileSystem trait" },
+ { path = "std::path::Path::is_file", reason = "File system operations should be done using FileSystem trait" },
+ { path = "std::path::Path::is_symlink", reason = "File system operations should be done using FileSystem trait" },
+ { path = "std::path::Path::metadata", reason = "File system operations should be done using FileSystem trait" },
+ { path = "std::path::Path::read_dir", reason = "File system operations should be done using FileSystem trait" },
+ { path = "std::path::Path::read_link", reason = "File system operations should be done using FileSystem trait" },
+ { path = "std::path::Path::symlink_metadata", reason = "File system operations should be done using FileSystem trait" },
+ { path = "std::path::Path::try_exists", reason = "File system operations should be done using FileSystem trait" },
+ { path = "std::path::PathBuf::exists", reason = "File system operations should be done using FileSystem trait" },
+ { path = "std::path::PathBuf::canonicalize", reason = "File system operations should be done using FileSystem trait" },
+ { path = "std::path::PathBuf::is_dir", reason = "File system operations should be done using FileSystem trait" },
+ { path = "std::path::PathBuf::is_file", reason = "File system operations should be done using FileSystem trait" },
+ { path = "std::path::PathBuf::is_symlink", reason = "File system operations should be done using FileSystem trait" },
+ { path = "std::path::PathBuf::metadata", reason = "File system operations should be done using FileSystem trait" },
+ { path = "std::path::PathBuf::read_dir", reason = "File system operations should be done using FileSystem trait" },
+ { path = "std::path::PathBuf::read_link", reason = "File system operations should be done using FileSystem trait" },
+ { path = "std::path::PathBuf::symlink_metadata", reason = "File system operations should be done using FileSystem trait" },
+ { path = "std::path::PathBuf::try_exists", reason = "File system operations should be done using FileSystem trait" },
{ path = "std::env::set_current_dir", reason = "File system operations should be done using FileSystem trait" },
{ path = "std::env::temp_dir", reason = "File system operations should be done using FileSystem trait" },
{ path = "std::fs::canonicalize", reason = "File system operations should be done using FileSystem trait" },
diff --git a/ext/fs/sync.rs b/ext/fs/sync.rs
index 83f1f8bc3..6a913f658 100644
--- a/ext/fs/sync.rs
+++ b/ext/fs/sync.rs
@@ -6,80 +6,18 @@ pub use inner::*;
mod inner {
#![allow(clippy::disallowed_types)]
- use std::ops::Deref;
- use std::ops::DerefMut;
pub use std::sync::Arc as MaybeArc;
pub use core::marker::Send as MaybeSend;
pub use core::marker::Sync as MaybeSync;
-
- pub struct MaybeArcMutexGuard<'lock, T>(std::sync::MutexGuard<'lock, T>);
-
- impl<'lock, T> Deref for MaybeArcMutexGuard<'lock, T> {
- type Target = std::sync::MutexGuard<'lock, T>;
- fn deref(&self) -> &std::sync::MutexGuard<'lock, T> {
- &self.0
- }
- }
-
- impl<'lock, T> DerefMut for MaybeArcMutexGuard<'lock, T> {
- fn deref_mut(&mut self) -> &mut std::sync::MutexGuard<'lock, T> {
- &mut self.0
- }
- }
-
- #[derive(Debug)]
- pub struct MaybeArcMutex<T>(std::sync::Arc<std::sync::Mutex<T>>);
- impl<T> MaybeArcMutex<T> {
- pub fn new(val: T) -> Self {
- Self(std::sync::Arc::new(std::sync::Mutex::new(val)))
- }
- }
-
- impl<'lock, T> MaybeArcMutex<T> {
- pub fn lock(&'lock self) -> MaybeArcMutexGuard<'lock, T> {
- MaybeArcMutexGuard(self.0.lock().unwrap())
- }
- }
}
#[cfg(not(feature = "sync_fs"))]
mod inner {
- use std::ops::Deref;
- use std::ops::DerefMut;
pub use std::rc::Rc as MaybeArc;
pub trait MaybeSync {}
impl<T> MaybeSync for T where T: ?Sized {}
pub trait MaybeSend {}
impl<T> MaybeSend for T where T: ?Sized {}
-
- pub struct MaybeArcMutexGuard<'lock, T>(std::cell::RefMut<'lock, T>);
-
- impl<'lock, T> Deref for MaybeArcMutexGuard<'lock, T> {
- type Target = std::cell::RefMut<'lock, T>;
- fn deref(&self) -> &std::cell::RefMut<'lock, T> {
- &self.0
- }
- }
-
- impl<'lock, T> DerefMut for MaybeArcMutexGuard<'lock, T> {
- fn deref_mut(&mut self) -> &mut std::cell::RefMut<'lock, T> {
- &mut self.0
- }
- }
-
- #[derive(Debug)]
- pub struct MaybeArcMutex<T>(std::rc::Rc<std::cell::RefCell<T>>);
- impl<T> MaybeArcMutex<T> {
- pub fn new(val: T) -> Self {
- Self(std::rc::Rc::new(std::cell::RefCell::new(val)))
- }
- }
-
- impl<'lock, T> MaybeArcMutex<T> {
- pub fn lock(&'lock self) -> MaybeArcMutexGuard<'lock, T> {
- MaybeArcMutexGuard(self.0.borrow_mut())
- }
- }
}
diff --git a/ext/node/Cargo.toml b/ext/node/Cargo.toml
index ed168eace..00afb64eb 100644
--- a/ext/node/Cargo.toml
+++ b/ext/node/Cargo.toml
@@ -14,7 +14,7 @@ description = "Node compatibility for Deno"
path = "lib.rs"
[features]
-sync_fs = ["deno_package_json/sync"]
+sync_fs = ["deno_package_json/sync", "node_resolver/sync"]
[dependencies]
aead-gcm-stream = "0.1"
@@ -55,6 +55,7 @@ libc.workspace = true
libz-sys.workspace = true
md-5 = { version = "0.10.5", features = ["oid"] }
md4 = "0.10.2"
+node_resolver.workspace = true
num-bigint.workspace = true
num-bigint-dig = "0.8.2"
num-integer = "0.1.45"
diff --git a/ext/node/global.rs b/ext/node/global.rs
index 7f901fd03..618e68494 100644
--- a/ext/node/global.rs
+++ b/ext/node/global.rs
@@ -6,7 +6,7 @@ use deno_core::v8;
use deno_core::v8::GetPropertyNamesArgs;
use deno_core::v8::MapFnTo;
-use crate::resolution::NodeResolverRc;
+use crate::NodeResolverRc;
// NOTE(bartlomieju): somehow calling `.map_fn_to()` multiple times on a function
// returns two different pointers. That shouldn't be the case as `.map_fn_to()`
diff --git a/ext/node/lib.rs b/ext/node/lib.rs
index 21af5a094..2c8650577 100644
--- a/ext/node/lib.rs
+++ b/ext/node/lib.rs
@@ -5,7 +5,6 @@
use std::collections::HashSet;
use std::path::Path;
-use std::path::PathBuf;
use deno_core::error::AnyError;
use deno_core::located_script_name;
@@ -15,24 +14,20 @@ use deno_core::url::Url;
use deno_core::v8;
use deno_core::v8::ExternalReference;
use deno_core::JsRuntime;
-use deno_core::ModuleSpecifier;
use deno_core::OpState;
use deno_fs::sync::MaybeSend;
use deno_fs::sync::MaybeSync;
+use node_resolver::NpmResolverRc;
use once_cell::sync::Lazy;
extern crate libz_sys as zlib;
-pub mod analyze;
-pub mod errors;
mod global;
mod ops;
-mod package_json;
-mod path;
mod polyfill;
-mod resolution;
pub use deno_package_json::PackageJson;
+pub use node_resolver::PathClean;
pub use ops::ipc::ChildPipeFd;
pub use ops::ipc::IpcJsonStreamResource;
use ops::vm;
@@ -40,17 +35,9 @@ pub use ops::vm::create_v8_context;
pub use ops::vm::init_global_template;
pub use ops::vm::ContextInitMode;
pub use ops::vm::VM_CONTEXT_INDEX;
-pub use package_json::load_pkg_json;
-pub use package_json::PackageJsonThreadLocalCache;
-pub use path::PathClean;
pub use polyfill::is_builtin_node_module;
pub use polyfill::SUPPORTED_BUILTIN_NODE_MODULES;
pub use polyfill::SUPPORTED_BUILTIN_NODE_MODULES_WITH_PREFIX;
-pub use resolution::NodeModuleKind;
-pub use resolution::NodeResolution;
-pub use resolution::NodeResolutionMode;
-pub use resolution::NodeResolver;
-use resolution::NodeResolverRc;
use crate::global::global_object_middleware;
use crate::global::global_template_middleware;
@@ -149,9 +136,12 @@ impl NodePermissions for deno_permissions::PermissionsContainer {
}
#[allow(clippy::disallowed_types)]
-pub type NpmResolverRc = deno_fs::sync::MaybeArc<dyn NpmResolver>;
+pub type NpmProcessStateProviderRc =
+ deno_fs::sync::MaybeArc<dyn NpmProcessStateProvider>;
-pub trait NpmResolver: std::fmt::Debug + MaybeSend + MaybeSync {
+pub trait NpmProcessStateProvider:
+ std::fmt::Debug + MaybeSend + MaybeSync
+{
/// Gets a string containing the serialized npm state of the process.
///
/// This will be set on the `DENO_DONT_USE_INTERNAL_NODE_COMPAT_STATE` environment
@@ -161,34 +151,13 @@ pub trait NpmResolver: std::fmt::Debug + MaybeSend + MaybeSync {
// This method is only used in the CLI.
String::new()
}
+}
- /// Resolves an npm package folder path from an npm package referrer.
- fn resolve_package_folder_from_package(
- &self,
- specifier: &str,
- referrer: &ModuleSpecifier,
- ) -> Result<PathBuf, errors::PackageFolderResolveError>;
-
- fn in_npm_package(&self, specifier: &ModuleSpecifier) -> bool;
-
- fn in_npm_package_at_dir_path(&self, path: &Path) -> bool {
- let specifier =
- match ModuleSpecifier::from_directory_path(path.to_path_buf().clean()) {
- Ok(p) => p,
- Err(_) => return false,
- };
- self.in_npm_package(&specifier)
- }
-
- fn in_npm_package_at_file_path(&self, path: &Path) -> bool {
- let specifier =
- match ModuleSpecifier::from_file_path(path.to_path_buf().clean()) {
- Ok(p) => p,
- Err(_) => return false,
- };
- self.in_npm_package(&specifier)
- }
+#[allow(clippy::disallowed_types)]
+pub type NodeRequireResolverRc =
+ deno_fs::sync::MaybeArc<dyn NodeRequireResolver>;
+pub trait NodeRequireResolver: std::fmt::Debug + MaybeSend + MaybeSync {
fn ensure_read_permission(
&self,
permissions: &mut dyn NodePermissions,
@@ -223,10 +192,17 @@ fn op_node_is_promise_rejected(value: v8::Local<v8::Value>) -> bool {
#[op2]
#[string]
fn op_npm_process_state(state: &mut OpState) -> Result<String, AnyError> {
- let npm_resolver = state.borrow_mut::<NpmResolverRc>();
+ let npm_resolver = state.borrow_mut::<NpmProcessStateProviderRc>();
Ok(npm_resolver.get_npm_process_state())
}
+pub struct NodeExtInitServices {
+ pub node_require_resolver: NodeRequireResolverRc,
+ pub node_resolver: NodeResolverRc,
+ pub npm_process_state_provider: NpmProcessStateProviderRc,
+ pub npm_resolver: NpmResolverRc,
+}
+
deno_core::extension!(deno_node,
deps = [ deno_io, deno_fs ],
parameters = [P: NodePermissions],
@@ -643,21 +619,17 @@ deno_core::extension!(deno_node,
"node:zlib" = "zlib.ts",
],
options = {
- maybe_node_resolver: Option<NodeResolverRc>,
- maybe_npm_resolver: Option<NpmResolverRc>,
+ maybe_init: Option<NodeExtInitServices>,
fs: deno_fs::FileSystemRc,
},
state = |state, options| {
- // you should provide both of these or neither
- debug_assert_eq!(options.maybe_node_resolver.is_some(), options.maybe_npm_resolver.is_some());
-
state.put(options.fs.clone());
- if let Some(node_resolver) = &options.maybe_node_resolver {
- state.put(node_resolver.clone());
- }
- if let Some(npm_resolver) = &options.maybe_npm_resolver {
- state.put(npm_resolver.clone());
+ if let Some(init) = &options.maybe_init {
+ state.put(init.node_require_resolver.clone());
+ state.put(init.node_resolver.clone());
+ state.put(init.npm_resolver.clone());
+ state.put(init.npm_process_state_provider.clone());
}
},
global_template_middleware = global_template_middleware,
@@ -783,3 +755,84 @@ pub fn load_cjs_module(
js_runtime.execute_script(located_script_name!(), source_code)?;
Ok(())
}
+
+pub type NodeResolver = node_resolver::NodeResolver<DenoFsNodeResolverEnv>;
+#[allow(clippy::disallowed_types)]
+pub type NodeResolverRc =
+ deno_fs::sync::MaybeArc<node_resolver::NodeResolver<DenoFsNodeResolverEnv>>;
+
+#[derive(Debug)]
+pub struct DenoFsNodeResolverEnv {
+ fs: deno_fs::FileSystemRc,
+}
+
+impl DenoFsNodeResolverEnv {
+ pub fn new(fs: deno_fs::FileSystemRc) -> Self {
+ Self { fs }
+ }
+}
+
+impl node_resolver::env::NodeResolverEnv for DenoFsNodeResolverEnv {
+ fn is_builtin_node_module(&self, specifier: &str) -> bool {
+ is_builtin_node_module(specifier)
+ }
+
+ fn realpath_sync(
+ &self,
+ path: &std::path::Path,
+ ) -> std::io::Result<std::path::PathBuf> {
+ self
+ .fs
+ .realpath_sync(path)
+ .map_err(|err| err.into_io_error())
+ }
+
+ fn stat_sync(
+ &self,
+ path: &std::path::Path,
+ ) -> std::io::Result<node_resolver::env::NodeResolverFsStat> {
+ self
+ .fs
+ .stat_sync(path)
+ .map(|stat| node_resolver::env::NodeResolverFsStat {
+ is_file: stat.is_file,
+ is_dir: stat.is_directory,
+ is_symlink: stat.is_symlink,
+ })
+ .map_err(|err| err.into_io_error())
+ }
+
+ fn exists_sync(&self, path: &std::path::Path) -> bool {
+ self.fs.exists_sync(path)
+ }
+
+ fn pkg_json_fs(&self) -> &dyn deno_package_json::fs::DenoPkgJsonFs {
+ self
+ }
+}
+
+impl deno_package_json::fs::DenoPkgJsonFs for DenoFsNodeResolverEnv {
+ fn read_to_string_lossy(
+ &self,
+ path: &std::path::Path,
+ ) -> Result<String, std::io::Error> {
+ self
+ .fs
+ .read_text_file_lossy_sync(path, None)
+ .map_err(|err| err.into_io_error())
+ }
+}
+
+pub struct DenoPkgJsonFsAdapter<'a>(pub &'a dyn deno_fs::FileSystem);
+
+impl<'a> deno_package_json::fs::DenoPkgJsonFs for DenoPkgJsonFsAdapter<'a> {
+ fn read_to_string_lossy(
+ &self,
+ path: &Path,
+ ) -> Result<String, std::io::Error> {
+ self
+ .0
+ .read_text_file_lossy_sync(path, None)
+ .map_err(|err| err.into_io_error())
+ }
+}
diff --git a/ext/node/ops/require.rs b/ext/node/ops/require.rs
index d03b3dd9c..d074234c3 100644
--- a/ext/node/ops/require.rs
+++ b/ext/node/ops/require.rs
@@ -10,16 +10,17 @@ use deno_core::JsRuntimeInspector;
use deno_core::ModuleSpecifier;
use deno_core::OpState;
use deno_fs::FileSystemRc;
+use node_resolver::NodeModuleKind;
+use node_resolver::NodeResolutionMode;
+use node_resolver::REQUIRE_CONDITIONS;
use std::cell::RefCell;
use std::path::Path;
use std::path::PathBuf;
use std::rc::Rc;
-use crate::resolution;
-use crate::resolution::NodeResolverRc;
-use crate::NodeModuleKind;
use crate::NodePermissions;
-use crate::NodeResolutionMode;
+use crate::NodeRequireResolverRc;
+use crate::NodeResolverRc;
use crate::NpmResolverRc;
use crate::PackageJson;
@@ -30,7 +31,7 @@ fn ensure_read_permission<P>(
where
P: NodePermissions + 'static,
{
- let resolver = state.borrow::<NpmResolverRc>().clone();
+ let resolver = state.borrow::<NodeRequireResolverRc>().clone();
let permissions = state.borrow_mut::<P>();
resolver.ensure_read_permission(permissions, file_path)
}
@@ -423,7 +424,7 @@ where
exports,
Some(&referrer),
NodeModuleKind::Cjs,
- resolution::REQUIRE_CONDITIONS,
+ REQUIRE_CONDITIONS,
NodeResolutionMode::Execution,
)?;
Ok(Some(if r.scheme() == "file" {
@@ -511,7 +512,7 @@ where
exports,
Some(&referrer),
NodeModuleKind::Cjs,
- resolution::REQUIRE_CONDITIONS,
+ REQUIRE_CONDITIONS,
NodeResolutionMode::Execution,
)?;
Ok(Some(if r.scheme() == "file" {
@@ -590,7 +591,7 @@ where
Some(&referrer_url),
NodeModuleKind::Cjs,
Some(&pkg),
- resolution::REQUIRE_CONDITIONS,
+ REQUIRE_CONDITIONS,
NodeResolutionMode::Execution,
)?;
Ok(Some(url_to_file_path_string(&url)?))
diff --git a/ext/node/ops/worker_threads.rs b/ext/node/ops/worker_threads.rs
index 182ba0118..c7ea4c52c 100644
--- a/ext/node/ops/worker_threads.rs
+++ b/ext/node/ops/worker_threads.rs
@@ -6,13 +6,13 @@ use deno_core::op2;
use deno_core::url::Url;
use deno_core::OpState;
use deno_fs::FileSystemRc;
+use node_resolver::NodeResolution;
use std::path::Path;
use std::path::PathBuf;
-use crate::resolution;
-use crate::resolution::NodeResolverRc;
use crate::NodePermissions;
-use crate::NpmResolverRc;
+use crate::NodeRequireResolverRc;
+use crate::NodeResolverRc;
fn ensure_read_permission<P>(
state: &mut OpState,
@@ -21,7 +21,7 @@ fn ensure_read_permission<P>(
where
P: NodePermissions + 'static,
{
- let resolver = state.borrow::<NpmResolverRc>().clone();
+ let resolver = state.borrow::<NodeRequireResolverRc>().clone();
let permissions = state.borrow_mut::<P>();
resolver.ensure_read_permission(permissions, file_path)
}
@@ -64,9 +64,9 @@ where
}
let node_resolver = state.borrow::<NodeResolverRc>();
match node_resolver.url_to_node_resolution(url)? {
- resolution::NodeResolution::Esm(u) => Ok(u.to_string()),
- resolution::NodeResolution::CommonJs(u) => wrap_cjs(u),
- _ => Err(generic_error("Neither ESM nor CJS")),
+ NodeResolution::Esm(u) => Ok(u.to_string()),
+ NodeResolution::CommonJs(u) => wrap_cjs(u),
+ NodeResolution::BuiltIn(_) => Err(generic_error("Neither ESM nor CJS")),
}
}
diff --git a/ext/node/path.rs b/ext/node/path.rs
deleted file mode 100644
index 0f151edaf..000000000
--- a/ext/node/path.rs
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
-
-use std::path::Component;
-use std::path::Path;
-use std::path::PathBuf;
-
-use deno_core::ModuleSpecifier;
-
-/// Extension to path_clean::PathClean
-pub trait PathClean<T> {
- fn clean(&self) -> T;
-}
-
-impl PathClean<PathBuf> for PathBuf {
- fn clean(&self) -> PathBuf {
- let path = path_clean::PathClean::clean(self);
- if cfg!(windows) && path.to_string_lossy().contains("..\\") {
- // temporary workaround because path_clean::PathClean::clean is
- // not good enough on windows
- let mut components = Vec::new();
-
- for component in path.components() {
- match component {
- Component::CurDir => {
- // skip
- }
- Component::ParentDir => {
- let maybe_last_component = components.pop();
- if !matches!(maybe_last_component, Some(Component::Normal(_))) {
- panic!("Error normalizing: {}", path.display());
- }
- }
- Component::Normal(_) | Component::RootDir | Component::Prefix(_) => {
- components.push(component);
- }
- }
- }
- components.into_iter().collect::<PathBuf>()
- } else {
- path
- }
- }
-}
-
-pub(crate) fn to_file_specifier(path: &Path) -> ModuleSpecifier {
- match ModuleSpecifier::from_file_path(path) {
- Ok(url) => url,
- Err(_) => panic!("Invalid path: {}", path.display()),
- }
-}
diff --git a/ext/node/polyfill.rs b/ext/node/polyfill.rs
index 5847acc42..b4030a491 100644
--- a/ext/node/polyfill.rs
+++ b/ext/node/polyfill.rs
@@ -1,7 +1,5 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
-use deno_core::ModuleSpecifier;
-
/// e.g. `is_builtin_node_module("assert")`
pub fn is_builtin_node_module(module_name: &str) -> bool {
SUPPORTED_BUILTIN_NODE_MODULES
@@ -9,18 +7,6 @@ pub fn is_builtin_node_module(module_name: &str) -> bool {
.any(|m| *m == module_name)
}
-/// Ex. returns `fs` for `node:fs`
-pub fn get_module_name_from_builtin_node_module_specifier(
- specifier: &ModuleSpecifier,
-) -> Option<&str> {
- if specifier.scheme() != "node" {
- return None;
- }
-
- let (_, specifier) = specifier.as_str().split_once(':')?;
- Some(specifier)
-}
-
macro_rules! generate_builtin_node_module_lists {
($( $module_name:literal ,)+) => {
pub static SUPPORTED_BUILTIN_NODE_MODULES: &[&str] = &[
diff --git a/ext/node_resolver/Cargo.toml b/ext/node_resolver/Cargo.toml
new file mode 100644
index 000000000..a636eaf9f
--- /dev/null
+++ b/ext/node_resolver/Cargo.toml
@@ -0,0 +1,32 @@
+# Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
+
+[package]
+name = "node_resolver"
+version = "0.1.0"
+authors.workspace = true
+edition.workspace = true
+license.workspace = true
+readme = "README.md"
+repository.workspace = true
+description = "Node.js module resolution algorithm used in Deno"
+
+[lib]
+path = "lib.rs"
+
+[features]
+sync = ["deno_package_json/sync"]
+
+[dependencies]
+anyhow.workspace = true
+async-trait.workspace = true
+deno_media_type.workspace = true
+deno_package_json.workspace = true
+futures.workspace = true
+lazy-regex.workspace = true
+once_cell.workspace = true
+path-clean = "=0.1.0"
+regex.workspace = true
+serde_json.workspace = true
+thiserror.workspace = true
+tokio.workspace = true
+url.workspace = true
diff --git a/ext/node_resolver/README.md b/ext/node_resolver/README.md
new file mode 100644
index 000000000..8f2f63ca1
--- /dev/null
+++ b/ext/node_resolver/README.md
@@ -0,0 +1,6 @@
+# Node Resolver
+
+[![crates](https://img.shields.io/crates/v/node_resolver.svg)](https://crates.io/crates/node_resolver)
+[![docs](https://docs.rs/node_resolver/badge.svg)](https://docs.rs/node_resolver)
+
+Provides Node.js compatible resolution for the Deno project.
diff --git a/ext/node/analyze.rs b/ext/node_resolver/analyze.rs
index 3513a8105..8d6a73424 100644
--- a/ext/node/analyze.rs
+++ b/ext/node_resolver/analyze.rs
@@ -5,17 +5,17 @@ use std::collections::HashSet;
use std::path::Path;
use std::path::PathBuf;
-use deno_core::anyhow;
-use deno_core::anyhow::Context;
-use deno_core::futures::future::LocalBoxFuture;
-use deno_core::futures::stream::FuturesUnordered;
-use deno_core::futures::FutureExt;
-use deno_core::futures::StreamExt;
-use deno_core::ModuleSpecifier;
+use futures::future::LocalBoxFuture;
+use futures::stream::FuturesUnordered;
+use futures::FutureExt;
+use futures::StreamExt;
use once_cell::sync::Lazy;
-use deno_core::error::AnyError;
+use anyhow::Context;
+use anyhow::Error as AnyError;
+use url::Url;
+use crate::env::NodeResolverEnv;
use crate::package_json::load_pkg_json;
use crate::path::to_file_specifier;
use crate::resolution::NodeResolverRc;
@@ -50,28 +50,33 @@ pub trait CjsCodeAnalyzer {
/// necessary.
async fn analyze_cjs(
&self,
- specifier: &ModuleSpecifier,
+ specifier: &Url,
maybe_source: Option<String>,
) -> Result<CjsAnalysis, AnyError>;
}
-pub struct NodeCodeTranslator<TCjsCodeAnalyzer: CjsCodeAnalyzer> {
+pub struct NodeCodeTranslator<
+ TCjsCodeAnalyzer: CjsCodeAnalyzer,
+ TNodeResolverEnv: NodeResolverEnv,
+> {
cjs_code_analyzer: TCjsCodeAnalyzer,
- fs: deno_fs::FileSystemRc,
- node_resolver: NodeResolverRc,
+ env: TNodeResolverEnv,
+ node_resolver: NodeResolverRc<TNodeResolverEnv>,
npm_resolver: NpmResolverRc,
}
-impl<TCjsCodeAnalyzer: CjsCodeAnalyzer> NodeCodeTranslator<TCjsCodeAnalyzer> {
+impl<TCjsCodeAnalyzer: CjsCodeAnalyzer, TNodeResolverEnv: NodeResolverEnv>
+ NodeCodeTranslator<TCjsCodeAnalyzer, TNodeResolverEnv>
+{
pub fn new(
cjs_code_analyzer: TCjsCodeAnalyzer,
- fs: deno_fs::FileSystemRc,
- node_resolver: NodeResolverRc,
+ env: TNodeResolverEnv,
+ node_resolver: NodeResolverRc<TNodeResolverEnv>,
npm_resolver: NpmResolverRc,
) -> Self {
Self {
cjs_code_analyzer,
- fs,
+ env,
node_resolver,
npm_resolver,
}
@@ -85,7 +90,7 @@ impl<TCjsCodeAnalyzer: CjsCodeAnalyzer> NodeCodeTranslator<TCjsCodeAnalyzer> {
/// If successful a source code for equivalent ES module is returned.
pub async fn translate_cjs_to_esm(
&self,
- entry_specifier: &ModuleSpecifier,
+ entry_specifier: &Url,
source: Option<String>,
) -> Result<String, AnyError> {
let mut temp_var_count = 0;
@@ -173,7 +178,7 @@ impl<TCjsCodeAnalyzer: CjsCodeAnalyzer> NodeCodeTranslator<TCjsCodeAnalyzer> {
type AnalysisFuture<'a> = LocalBoxFuture<'a, Result<Analysis, AnyError>>;
- let mut handled_reexports: HashSet<ModuleSpecifier> = HashSet::default();
+ let mut handled_reexports: HashSet<Url> = HashSet::default();
handled_reexports.insert(entry_specifier.clone());
let mut analyze_futures: FuturesUnordered<AnalysisFuture<'a>> =
FuturesUnordered::new();
@@ -282,10 +287,10 @@ impl<TCjsCodeAnalyzer: CjsCodeAnalyzer> NodeCodeTranslator<TCjsCodeAnalyzer> {
fn resolve(
&self,
specifier: &str,
- referrer: &ModuleSpecifier,
+ referrer: &Url,
conditions: &[&str],
mode: NodeResolutionMode,
- ) -> Result<ModuleSpecifier, AnyError> {
+ ) -> Result<Url, AnyError> {
if specifier.starts_with('/') {
todo!();
}
@@ -305,14 +310,14 @@ impl<TCjsCodeAnalyzer: CjsCodeAnalyzer> NodeCodeTranslator<TCjsCodeAnalyzer> {
let (package_specifier, package_subpath) =
parse_specifier(specifier).unwrap();
- // todo(dsherret): use not_found error on not found here
let module_dir = self.npm_resolver.resolve_package_folder_from_package(
package_specifier.as_str(),
referrer,
)?;
let package_json_path = module_dir.join("package.json");
- let maybe_package_json = load_pkg_json(&*self.fs, &package_json_path)?;
+ let maybe_package_json =
+ load_pkg_json(self.env.pkg_json_fs(), &package_json_path)?;
if let Some(package_json) = maybe_package_json {
if let Some(exports) = &package_json.exports {
return self
@@ -332,11 +337,11 @@ impl<TCjsCodeAnalyzer: CjsCodeAnalyzer> NodeCodeTranslator<TCjsCodeAnalyzer> {
// old school
if package_subpath != "." {
let d = module_dir.join(package_subpath);
- if self.fs.is_dir_sync(&d) {
+ if self.env.is_dir_sync(&d) {
// subdir might have a package.json that specifies the entrypoint
let package_json_path = d.join("package.json");
let maybe_package_json =
- load_pkg_json(&*self.fs, &package_json_path)?;
+ load_pkg_json(self.env.pkg_json_fs(), &package_json_path)?;
if let Some(package_json) = maybe_package_json {
if let Some(main) = package_json.main(NodeModuleKind::Cjs) {
return Ok(to_file_specifier(&d.join(main).clean()));
@@ -381,13 +386,13 @@ impl<TCjsCodeAnalyzer: CjsCodeAnalyzer> NodeCodeTranslator<TCjsCodeAnalyzer> {
referrer: &Path,
) -> Result<PathBuf, AnyError> {
let p = p.clean();
- if self.fs.exists_sync(&p) {
+ if self.env.exists_sync(&p) {
let file_name = p.file_name().unwrap();
let p_js =
p.with_file_name(format!("{}.js", file_name.to_str().unwrap()));
- if self.fs.is_file_sync(&p_js) {
+ if self.env.is_file_sync(&p_js) {
return Ok(p_js);
- } else if self.fs.is_dir_sync(&p) {
+ } else if self.env.is_dir_sync(&p) {
return Ok(p.join("index.js"));
} else {
return Ok(p);
@@ -396,14 +401,14 @@ impl<TCjsCodeAnalyzer: CjsCodeAnalyzer> NodeCodeTranslator<TCjsCodeAnalyzer> {
{
let p_js =
p.with_file_name(format!("{}.js", file_name.to_str().unwrap()));
- if self.fs.is_file_sync(&p_js) {
+ if self.env.is_file_sync(&p_js) {
return Ok(p_js);
}
}
{
let p_json =
p.with_file_name(format!("{}.json", file_name.to_str().unwrap()));
- if self.fs.is_file_sync(&p_json) {
+ if self.env.is_file_sync(&p_json) {
return Ok(p_json);
}
}
diff --git a/ext/node_resolver/clippy.toml b/ext/node_resolver/clippy.toml
new file mode 100644
index 000000000..86150781b
--- /dev/null
+++ b/ext/node_resolver/clippy.toml
@@ -0,0 +1,48 @@
+disallowed-methods = [
+ { path = "std::env::current_dir", reason = "File system operations should be done using NodeResolverFs trait" },
+ { path = "std::path::Path::canonicalize", reason = "File system operations should be done using NodeResolverFs trait" },
+ { path = "std::path::Path::is_dir", reason = "File system operations should be done using NodeResolverFs trait" },
+ { path = "std::path::Path::is_file", reason = "File system operations should be done using NodeResolverFs trait" },
+ { path = "std::path::Path::is_symlink", reason = "File system operations should be done using NodeResolverFs trait" },
+ { path = "std::path::Path::metadata", reason = "File system operations should be done using NodeResolverFs trait" },
+ { path = "std::path::Path::read_dir", reason = "File system operations should be done using NodeResolverFs trait" },
+ { path = "std::path::Path::read_link", reason = "File system operations should be done using NodeResolverFs trait" },
+ { path = "std::path::Path::symlink_metadata", reason = "File system operations should be done using NodeResolverFs trait" },
+ { path = "std::path::Path::try_exists", reason = "File system operations should be done using NodeResolverFs trait" },
+ { path = "std::path::PathBuf::exists", reason = "File system operations should be done using NodeResolverFs trait" },
+ { path = "std::path::PathBuf::canonicalize", reason = "File system operations should be done using NodeResolverFs trait" },
+ { path = "std::path::PathBuf::is_dir", reason = "File system operations should be done using NodeResolverFs trait" },
+ { path = "std::path::PathBuf::is_file", reason = "File system operations should be done using NodeResolverFs trait" },
+ { path = "std::path::PathBuf::is_symlink", reason = "File system operations should be done using NodeResolverFs trait" },
+ { path = "std::path::PathBuf::metadata", reason = "File system operations should be done using NodeResolverFs trait" },
+ { path = "std::path::PathBuf::read_dir", reason = "File system operations should be done using NodeResolverFs trait" },
+ { path = "std::path::PathBuf::read_link", reason = "File system operations should be done using NodeResolverFs trait" },
+ { path = "std::path::PathBuf::symlink_metadata", reason = "File system operations should be done using NodeResolverFs trait" },
+ { path = "std::path::PathBuf::try_exists", reason = "File system operations should be done using NodeResolverFs trait" },
+ { path = "std::env::set_current_dir", reason = "File system operations should be done using NodeResolverFs trait" },
+ { path = "std::env::temp_dir", reason = "File system operations should be done using NodeResolverFs trait" },
+ { path = "std::fs::canonicalize", reason = "File system operations should be done using NodeResolverFs trait" },
+ { path = "std::fs::copy", reason = "File system operations should be done using NodeResolverFs trait" },
+ { path = "std::fs::create_dir_all", reason = "File system operations should be done using NodeResolverFs trait" },
+ { path = "std::fs::create_dir", reason = "File system operations should be done using NodeResolverFs trait" },
+ { path = "std::fs::DirBuilder::new", reason = "File system operations should be done using NodeResolverFs trait" },
+ { path = "std::fs::hard_link", reason = "File system operations should be done using NodeResolverFs trait" },
+ { path = "std::fs::metadata", reason = "File system operations should be done using NodeResolverFs trait" },
+ { path = "std::fs::OpenOptions::new", reason = "File system operations should be done using NodeResolverFs trait" },
+ { path = "std::fs::read_dir", reason = "File system operations should be done using NodeResolverFs trait" },
+ { path = "std::fs::read_link", reason = "File system operations should be done using NodeResolverFs trait" },
+ { path = "std::fs::read_to_string", reason = "File system operations should be done using NodeResolverFs trait" },
+ { path = "std::fs::read", reason = "File system operations should be done using NodeResolverFs trait" },
+ { path = "std::fs::remove_dir_all", reason = "File system operations should be done using NodeResolverFs trait" },
+ { path = "std::fs::remove_dir", reason = "File system operations should be done using NodeResolverFs trait" },
+ { path = "std::fs::remove_file", reason = "File system operations should be done using NodeResolverFs trait" },
+ { path = "std::fs::rename", reason = "File system operations should be done using NodeResolverFs trait" },
+ { path = "std::fs::set_permissions", reason = "File system operations should be done using NodeResolverFs trait" },
+ { path = "std::fs::symlink_metadata", reason = "File system operations should be done using NodeResolverFs trait" },
+ { path = "std::fs::write", reason = "File system operations should be done using NodeResolverFs trait" },
+ { path = "std::path::Path::canonicalize", reason = "File system operations should be done using NodeResolverFs trait" },
+ { path = "std::path::Path::exists", reason = "File system operations should be done using NodeResolverFs trait" },
+]
+disallowed-types = [
+ { path = "std::sync::Arc", reason = "use crate::sync::MaybeArc instead" },
+]
diff --git a/ext/node_resolver/env.rs b/ext/node_resolver/env.rs
new file mode 100644
index 000000000..b520ece0f
--- /dev/null
+++ b/ext/node_resolver/env.rs
@@ -0,0 +1,39 @@
+// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
+
+use std::path::Path;
+use std::path::PathBuf;
+
+use crate::sync::MaybeSend;
+use crate::sync::MaybeSync;
+
+pub struct NodeResolverFsStat {
+ pub is_file: bool,
+ pub is_dir: bool,
+ pub is_symlink: bool,
+}
+
+pub trait NodeResolverEnv: std::fmt::Debug + MaybeSend + MaybeSync {
+ fn is_builtin_node_module(&self, specifier: &str) -> bool;
+
+ fn realpath_sync(&self, path: &Path) -> std::io::Result<PathBuf>;
+
+ fn stat_sync(&self, path: &Path) -> std::io::Result<NodeResolverFsStat>;
+
+ fn exists_sync(&self, path: &Path) -> bool;
+
+ fn is_file_sync(&self, path: &Path) -> bool {
+ self
+ .stat_sync(path)
+ .map(|stat| stat.is_file)
+ .unwrap_or(false)
+ }
+
+ fn is_dir_sync(&self, path: &Path) -> bool {
+ self
+ .stat_sync(path)
+ .map(|stat| stat.is_dir)
+ .unwrap_or(false)
+ }
+
+ fn pkg_json_fs(&self) -> &dyn deno_package_json::fs::DenoPkgJsonFs;
+}
diff --git a/ext/node/errors.rs b/ext/node_resolver/errors.rs
index 64625d32f..4ba829eda 100644
--- a/ext/node/errors.rs
+++ b/ext/node_resolver/errors.rs
@@ -4,8 +4,8 @@ use std::borrow::Cow;
use std::fmt::Write;
use std::path::PathBuf;
-use deno_core::ModuleSpecifier;
use thiserror::Error;
+use url::Url;
use crate::NodeModuleKind;
use crate::NodeResolutionMode;
@@ -155,7 +155,7 @@ kinded_err!(PackageFolderResolveError, PackageFolderResolveErrorKind);
)]
pub struct PackageNotFoundError {
pub package_name: String,
- pub referrer: ModuleSpecifier,
+ pub referrer: Url,
/// Extra information about the referrer.
pub referrer_extra: Option<String>,
}
@@ -173,7 +173,7 @@ impl NodeJsErrorCoded for PackageNotFoundError {
referrer_extra.as_ref().map(|r| format!(" ({})", r)).unwrap_or_default()
)]
pub struct ReferrerNotFoundError {
- pub referrer: ModuleSpecifier,
+ pub referrer: Url,
/// Extra information about the referrer.
pub referrer_extra: Option<String>,
}
@@ -188,7 +188,7 @@ impl NodeJsErrorCoded for ReferrerNotFoundError {
#[error("Failed resolving '{package_name}' from referrer '{referrer}'.")]
pub struct PackageFolderResolveIoError {
pub package_name: String,
- pub referrer: ModuleSpecifier,
+ pub referrer: Url,
#[source]
pub source: std::io::Error,
}
@@ -264,7 +264,7 @@ pub enum PackageSubpathResolveErrorKind {
pub struct PackageTargetNotFoundError {
pub pkg_json_path: PathBuf,
pub target: String,
- pub maybe_referrer: Option<ModuleSpecifier>,
+ pub maybe_referrer: Option<Url>,
pub referrer_kind: NodeModuleKind,
pub mode: NodeResolutionMode,
}
@@ -333,8 +333,8 @@ pub struct TypesNotFoundError(pub Box<TypesNotFoundErrorData>);
#[derive(Debug)]
pub struct TypesNotFoundErrorData {
- pub code_specifier: ModuleSpecifier,
- pub maybe_referrer: Option<ModuleSpecifier>,
+ pub code_specifier: Url,
+ pub maybe_referrer: Option<Url>,
}
impl NodeJsErrorCoded for TypesNotFoundError {
@@ -397,7 +397,7 @@ impl NodeJsErrorCoded for CanonicalizingPkgJsonDirError {
#[derive(Debug, Error)]
#[error("TypeScript files are not supported in npm packages: {specifier}")]
pub struct TypeScriptNotSupportedInNpmError {
- pub specifier: ModuleSpecifier,
+ pub specifier: Url,
}
impl NodeJsErrorCoded for TypeScriptNotSupportedInNpmError {
@@ -437,7 +437,7 @@ pub enum UrlToNodeResolutionErrorKind {
pub struct PackageImportNotDefinedError {
pub name: String,
pub package_json_path: Option<PathBuf>,
- pub maybe_referrer: Option<ModuleSpecifier>,
+ pub maybe_referrer: Option<Url>,
}
impl NodeJsErrorCoded for PackageImportNotDefinedError {
@@ -503,7 +503,7 @@ pub enum PackageResolveErrorKind {
#[error("Failed joining '{path}' from '{base}'.")]
pub struct NodeResolveRelativeJoinError {
pub path: String,
- pub base: ModuleSpecifier,
+ pub base: Url,
#[source]
pub source: url::ParseError,
}
@@ -568,8 +568,8 @@ impl NodeJsErrorCoded for FinalizeResolutionError {
maybe_referrer.as_ref().map(|referrer| format!(" imported from '{}'", referrer)).unwrap_or_default()
)]
pub struct ModuleNotFoundError {
- pub specifier: ModuleSpecifier,
- pub maybe_referrer: Option<ModuleSpecifier>,
+ pub specifier: Url,
+ pub maybe_referrer: Option<Url>,
pub typ: &'static str,
}
@@ -587,8 +587,8 @@ impl NodeJsErrorCoded for ModuleNotFoundError {
maybe_referrer.as_ref().map(|referrer| format!(" imported from '{}'", referrer)).unwrap_or_default(),
)]
pub struct UnsupportedDirImportError {
- pub dir_url: ModuleSpecifier,
- pub maybe_referrer: Option<ModuleSpecifier>,
+ pub dir_url: Url,
+ pub maybe_referrer: Option<Url>,
}
impl NodeJsErrorCoded for UnsupportedDirImportError {
@@ -603,7 +603,7 @@ pub struct InvalidPackageTargetError {
pub sub_path: String,
pub target: String,
pub is_import: bool,
- pub maybe_referrer: Option<ModuleSpecifier>,
+ pub maybe_referrer: Option<Url>,
}
impl std::error::Error for InvalidPackageTargetError {}
@@ -657,7 +657,7 @@ impl NodeJsErrorCoded for InvalidPackageTargetError {
pub struct PackagePathNotExportedError {
pub pkg_json_path: PathBuf,
pub subpath: String,
- pub maybe_referrer: Option<ModuleSpecifier>,
+ pub maybe_referrer: Option<Url>,
pub mode: NodeResolutionMode,
}
diff --git a/ext/node_resolver/lib.rs b/ext/node_resolver/lib.rs
new file mode 100644
index 000000000..1ab972ccf
--- /dev/null
+++ b/ext/node_resolver/lib.rs
@@ -0,0 +1,26 @@
+// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
+
+#![deny(clippy::print_stderr)]
+#![deny(clippy::print_stdout)]
+
+pub mod analyze;
+pub mod env;
+pub mod errors;
+mod npm;
+mod package_json;
+mod path;
+mod resolution;
+mod sync;
+
+pub use deno_package_json::PackageJson;
+pub use npm::NpmResolver;
+pub use npm::NpmResolverRc;
+pub use package_json::load_pkg_json;
+pub use package_json::PackageJsonThreadLocalCache;
+pub use path::PathClean;
+pub use resolution::NodeModuleKind;
+pub use resolution::NodeResolution;
+pub use resolution::NodeResolutionMode;
+pub use resolution::NodeResolver;
+pub use resolution::DEFAULT_CONDITIONS;
+pub use resolution::REQUIRE_CONDITIONS;
diff --git a/ext/node_resolver/npm.rs b/ext/node_resolver/npm.rs
new file mode 100644
index 000000000..77df57c48
--- /dev/null
+++ b/ext/node_resolver/npm.rs
@@ -0,0 +1,41 @@
+// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
+
+use std::path::Path;
+use std::path::PathBuf;
+
+use url::Url;
+
+use crate::errors;
+use crate::path::PathClean;
+use crate::sync::MaybeSend;
+use crate::sync::MaybeSync;
+
+#[allow(clippy::disallowed_types)]
+pub type NpmResolverRc = crate::sync::MaybeArc<dyn NpmResolver>;
+
+pub trait NpmResolver: std::fmt::Debug + MaybeSend + MaybeSync {
+ /// Resolves an npm package folder path from an npm package referrer.
+ fn resolve_package_folder_from_package(
+ &self,
+ specifier: &str,
+ referrer: &Url,
+ ) -> Result<PathBuf, errors::PackageFolderResolveError>;
+
+ fn in_npm_package(&self, specifier: &Url) -> bool;
+
+ fn in_npm_package_at_dir_path(&self, path: &Path) -> bool {
+ let specifier = match Url::from_directory_path(path.to_path_buf().clean()) {
+ Ok(p) => p,
+ Err(_) => return false,
+ };
+ self.in_npm_package(&specifier)
+ }
+
+ fn in_npm_package_at_file_path(&self, path: &Path) -> bool {
+ let specifier = match Url::from_file_path(path.to_path_buf().clean()) {
+ Ok(p) => p,
+ Err(_) => return false,
+ };
+ self.in_npm_package(&specifier)
+ }
+}
diff --git a/ext/node/package_json.rs b/ext/node_resolver/package_json.rs
index 877acfc7a..de750f1d7 100644
--- a/ext/node/package_json.rs
+++ b/ext/node_resolver/package_json.rs
@@ -33,31 +33,14 @@ impl deno_package_json::PackageJsonCache for PackageJsonThreadLocalCache {
}
}
-pub struct DenoPkgJsonFsAdapter<'a>(pub &'a dyn deno_fs::FileSystem);
-
-impl<'a> deno_package_json::fs::DenoPkgJsonFs for DenoPkgJsonFsAdapter<'a> {
- fn read_to_string_lossy(
- &self,
- path: &Path,
- ) -> Result<String, std::io::Error> {
- self
- .0
- .read_text_file_lossy_sync(path, None)
- .map_err(|err| err.into_io_error())
- }
-}
-
/// Helper to load a package.json file using the thread local cache
-/// in deno_node.
+/// in node_resolver.
pub fn load_pkg_json(
- fs: &dyn deno_fs::FileSystem,
+ fs: &dyn deno_package_json::fs::DenoPkgJsonFs,
path: &Path,
) -> Result<Option<PackageJsonRc>, PackageJsonLoadError> {
- let result = PackageJson::load_from_path(
- path,
- &DenoPkgJsonFsAdapter(fs),
- Some(&PackageJsonThreadLocalCache),
- );
+ let result =
+ PackageJson::load_from_path(path, fs, Some(&PackageJsonThreadLocalCache));
match result {
Ok(pkg_json) => Ok(Some(pkg_json)),
Err(deno_package_json::PackageJsonLoadError::Io { source, .. })
diff --git a/ext/node_resolver/path.rs b/ext/node_resolver/path.rs
new file mode 100644
index 000000000..8c33285db
--- /dev/null
+++ b/ext/node_resolver/path.rs
@@ -0,0 +1,142 @@
+// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
+
+use std::path::Component;
+use std::path::Path;
+use std::path::PathBuf;
+
+use url::Url;
+
+/// Extension to path_clean::PathClean
+pub trait PathClean<T> {
+ fn clean(&self) -> T;
+}
+
+impl PathClean<PathBuf> for PathBuf {
+ fn clean(&self) -> PathBuf {
+ let path = path_clean::PathClean::clean(self);
+ if cfg!(windows) && path.to_string_lossy().contains("..\\") {
+ // temporary workaround because path_clean::PathClean::clean is
+ // not good enough on windows
+ let mut components = Vec::new();
+
+ for component in path.components() {
+ match component {
+ Component::CurDir => {
+ // skip
+ }
+ Component::ParentDir => {
+ let maybe_last_component = components.pop();
+ if !matches!(maybe_last_component, Some(Component::Normal(_))) {
+ panic!("Error normalizing: {}", path.display());
+ }
+ }
+ Component::Normal(_) | Component::RootDir | Component::Prefix(_) => {
+ components.push(component);
+ }
+ }
+ }
+ components.into_iter().collect::<PathBuf>()
+ } else {
+ path
+ }
+ }
+}
+
+pub(crate) fn to_file_specifier(path: &Path) -> Url {
+ match Url::from_file_path(path) {
+ Ok(url) => url,
+ Err(_) => panic!("Invalid path: {}", path.display()),
+ }
+}
+
+// todo(dsherret): we have the below code also in deno_core and it
+// would be good to somehow re-use it in both places (we don't want
+// to create a dependency on deno_core here)
+
+#[cfg(not(windows))]
+#[inline]
+pub fn strip_unc_prefix(path: PathBuf) -> PathBuf {
+ path
+}
+
+/// Strips the unc prefix (ex. \\?\) from Windows paths.
+#[cfg(windows)]
+pub fn strip_unc_prefix(path: PathBuf) -> PathBuf {
+ use std::path::Component;
+ use std::path::Prefix;
+
+ let mut components = path.components();
+ match components.next() {
+ Some(Component::Prefix(prefix)) => {
+ match prefix.kind() {
+ // \\?\device
+ Prefix::Verbatim(device) => {
+ let mut path = PathBuf::new();
+ path.push(format!(r"\\{}\", device.to_string_lossy()));
+ path.extend(components.filter(|c| !matches!(c, Component::RootDir)));
+ path
+ }
+ // \\?\c:\path
+ Prefix::VerbatimDisk(_) => {
+ let mut path = PathBuf::new();
+ path.push(prefix.as_os_str().to_string_lossy().replace(r"\\?\", ""));
+ path.extend(components);
+ path
+ }
+ // \\?\UNC\hostname\share_name\path
+ Prefix::VerbatimUNC(hostname, share_name) => {
+ let mut path = PathBuf::new();
+ path.push(format!(
+ r"\\{}\{}\",
+ hostname.to_string_lossy(),
+ share_name.to_string_lossy()
+ ));
+ path.extend(components.filter(|c| !matches!(c, Component::RootDir)));
+ path
+ }
+ _ => path,
+ }
+ }
+ _ => path,
+ }
+}
+
+#[cfg(test)]
+mod test {
+ #[cfg(windows)]
+ #[test]
+ fn test_strip_unc_prefix() {
+ use std::path::PathBuf;
+
+ run_test(r"C:\", r"C:\");
+ run_test(r"C:\test\file.txt", r"C:\test\file.txt");
+
+ run_test(r"\\?\C:\", r"C:\");
+ run_test(r"\\?\C:\test\file.txt", r"C:\test\file.txt");
+
+ run_test(r"\\.\C:\", r"\\.\C:\");
+ run_test(r"\\.\C:\Test\file.txt", r"\\.\C:\Test\file.txt");
+
+ run_test(r"\\?\UNC\localhost\", r"\\localhost");
+ run_test(r"\\?\UNC\localhost\c$\", r"\\localhost\c$");
+ run_test(
+ r"\\?\UNC\localhost\c$\Windows\file.txt",
+ r"\\localhost\c$\Windows\file.txt",
+ );
+ run_test(r"\\?\UNC\wsl$\deno.json", r"\\wsl$\deno.json");
+
+ run_test(r"\\?\server1", r"\\server1");
+ run_test(r"\\?\server1\e$\", r"\\server1\e$\");
+ run_test(
+ r"\\?\server1\e$\test\file.txt",
+ r"\\server1\e$\test\file.txt",
+ );
+
+ fn run_test(input: &str, expected: &str) {
+ assert_eq!(
+ super::strip_unc_prefix(PathBuf::from(input)),
+ PathBuf::from(expected)
+ );
+ }
+ }
+}
diff --git a/ext/node/resolution.rs b/ext/node_resolver/resolution.rs
index 6417835a2..d7918c75c 100644
--- a/ext/node/resolution.rs
+++ b/ext/node_resolver/resolution.rs
@@ -5,16 +5,15 @@ use std::collections::HashMap;
use std::path::Path;
use std::path::PathBuf;
-use deno_core::anyhow::bail;
-use deno_core::error::AnyError;
-use deno_core::serde_json::Map;
-use deno_core::serde_json::Value;
-use deno_core::url::Url;
-use deno_core::ModuleSpecifier;
-use deno_fs::FileSystemRc;
+use anyhow::bail;
+use anyhow::Error as AnyError;
use deno_media_type::MediaType;
use deno_package_json::PackageJsonRc;
+use serde_json::Map;
+use serde_json::Value;
+use url::Url;
+use crate::env::NodeResolverEnv;
use crate::errors;
use crate::errors::CanonicalizingPkgJsonDirError;
use crate::errors::ClosestPkgJsonError;
@@ -49,12 +48,11 @@ use crate::errors::TypesNotFoundErrorData;
use crate::errors::UnsupportedDirImportError;
use crate::errors::UnsupportedEsmUrlSchemeError;
use crate::errors::UrlToNodeResolutionError;
-use crate::is_builtin_node_module;
+use crate::path::strip_unc_prefix;
use crate::path::to_file_specifier;
-use crate::polyfill::get_module_name_from_builtin_node_module_specifier;
use crate::NpmResolverRc;
-use crate::PackageJson;
use crate::PathClean;
+use deno_package_json::PackageJson;
pub static DEFAULT_CONDITIONS: &[&str] = &["deno", "node", "import"];
pub static REQUIRE_CONDITIONS: &[&str] = &["require", "node"];
@@ -76,21 +74,21 @@ impl NodeResolutionMode {
#[derive(Debug)]
pub enum NodeResolution {
- Esm(ModuleSpecifier),
- CommonJs(ModuleSpecifier),
+ Esm(Url),
+ CommonJs(Url),
BuiltIn(String),
}
impl NodeResolution {
- pub fn into_url(self) -> ModuleSpecifier {
+ pub fn into_url(self) -> Url {
match self {
Self::Esm(u) => u,
Self::CommonJs(u) => u,
Self::BuiltIn(specifier) => {
if specifier.starts_with("node:") {
- ModuleSpecifier::parse(&specifier).unwrap()
+ Url::parse(&specifier).unwrap()
} else {
- ModuleSpecifier::parse(&format!("node:{specifier}")).unwrap()
+ Url::parse(&format!("node:{specifier}")).unwrap()
}
}
}
@@ -98,7 +96,7 @@ impl NodeResolution {
pub fn into_specifier_and_media_type(
resolution: Option<Self>,
- ) -> (ModuleSpecifier, MediaType) {
+ ) -> (Url, MediaType) {
match resolution {
Some(NodeResolution::CommonJs(specifier)) => {
let media_type = MediaType::from_specifier(&specifier);
@@ -126,7 +124,7 @@ impl NodeResolution {
}
Some(resolution) => (resolution.into_url(), MediaType::Dts),
None => (
- ModuleSpecifier::parse("internal:///missing_dependency.d.ts").unwrap(),
+ Url::parse("internal:///missing_dependency.d.ts").unwrap(),
MediaType::Dts,
),
}
@@ -134,25 +132,25 @@ impl NodeResolution {
}
#[allow(clippy::disallowed_types)]
-pub type NodeResolverRc = deno_fs::sync::MaybeArc<NodeResolver>;
+pub type NodeResolverRc<TEnv> = crate::sync::MaybeArc<NodeResolver<TEnv>>;
#[derive(Debug)]
-pub struct NodeResolver {
- fs: FileSystemRc,
+pub struct NodeResolver<TEnv: NodeResolverEnv> {
+ env: TEnv,
npm_resolver: NpmResolverRc,
- in_npm_package_cache: deno_fs::sync::MaybeArcMutex<HashMap<String, bool>>,
+ in_npm_package_cache: crate::sync::MaybeArcMutex<HashMap<String, bool>>,
}
-impl NodeResolver {
- pub fn new(fs: FileSystemRc, npm_resolver: NpmResolverRc) -> Self {
+impl<TEnv: NodeResolverEnv> NodeResolver<TEnv> {
+ pub fn new(env: TEnv, npm_resolver: NpmResolverRc) -> Self {
Self {
- fs,
+ env,
npm_resolver,
- in_npm_package_cache: deno_fs::sync::MaybeArcMutex::new(HashMap::new()),
+ in_npm_package_cache: crate::sync::MaybeArcMutex::new(HashMap::new()),
}
}
- pub fn in_npm_package(&self, specifier: &ModuleSpecifier) -> bool {
+ pub fn in_npm_package(&self, specifier: &Url) -> bool {
self.npm_resolver.in_npm_package(specifier)
}
@@ -163,12 +161,11 @@ impl NodeResolver {
return *result;
}
- let result =
- if let Ok(specifier) = deno_core::ModuleSpecifier::parse(&specifier) {
- self.npm_resolver.in_npm_package(&specifier)
- } else {
- false
- };
+ let result = if let Ok(specifier) = Url::parse(&specifier) {
+ self.npm_resolver.in_npm_package(&specifier)
+ } else {
+ false
+ };
cache.insert(specifier.into_owned(), result);
result
}
@@ -178,14 +175,14 @@ impl NodeResolver {
pub fn resolve(
&self,
specifier: &str,
- referrer: &ModuleSpecifier,
+ referrer: &Url,
referrer_kind: NodeModuleKind,
mode: NodeResolutionMode,
) -> Result<NodeResolution, NodeResolveError> {
// Note: if we are here, then the referrer is an esm module
// TODO(bartlomieju): skipped "policy" part as we don't plan to support it
- if crate::is_builtin_node_module(specifier) {
+ if self.env.is_builtin_node_module(specifier) {
return Ok(NodeResolution::BuiltIn(specifier.to_string()));
}
@@ -248,11 +245,11 @@ impl NodeResolver {
fn module_resolve(
&self,
specifier: &str,
- referrer: &ModuleSpecifier,
+ referrer: &Url,
referrer_kind: NodeModuleKind,
conditions: &[&str],
mode: NodeResolutionMode,
- ) -> Result<ModuleSpecifier, NodeResolveError> {
+ ) -> Result<Url, NodeResolveError> {
if should_be_treated_as_relative_or_absolute_path(specifier) {
Ok(referrer.join(specifier).map_err(|err| {
NodeResolveRelativeJoinError {
@@ -289,9 +286,9 @@ impl NodeResolver {
fn finalize_resolution(
&self,
- resolved: ModuleSpecifier,
- maybe_referrer: Option<&ModuleSpecifier>,
- ) -> Result<ModuleSpecifier, FinalizeResolutionError> {
+ resolved: Url,
+ maybe_referrer: Option<&Url>,
+ ) -> Result<Url, FinalizeResolutionError> {
let encoded_sep_re = lazy_regex::regex!(r"%2F|%2C");
if encoded_sep_re.is_match(resolved.path()) {
@@ -325,9 +322,9 @@ impl NodeResolver {
p_str.to_string()
};
- let (is_dir, is_file) = if let Ok(stats) = self.fs.stat_sync(Path::new(&p))
+ let (is_dir, is_file) = if let Ok(stats) = self.env.stat_sync(Path::new(&p))
{
- (stats.is_directory, stats.is_file)
+ (stats.is_dir, stats.is_file)
} else {
(false, false)
};
@@ -357,7 +354,7 @@ impl NodeResolver {
&self,
package_dir: &Path,
package_subpath: Option<&str>,
- maybe_referrer: Option<&ModuleSpecifier>,
+ maybe_referrer: Option<&Url>,
mode: NodeResolutionMode,
) -> Result<NodeResolution, ResolvePkgSubpathFromDenoModuleError> {
let node_module_kind = NodeModuleKind::Esm;
@@ -430,7 +427,7 @@ impl NodeResolver {
pub fn url_to_node_resolution(
&self,
- url: ModuleSpecifier,
+ url: Url,
) -> Result<NodeResolution, UrlToNodeResolutionError> {
let url_str = url.as_str().to_lowercase();
if url_str.starts_with("http") || url_str.ends_with(".json") {
@@ -459,11 +456,11 @@ impl NodeResolver {
fn path_to_declaration_url(
&self,
path: &Path,
- maybe_referrer: Option<&ModuleSpecifier>,
+ maybe_referrer: Option<&Url>,
referrer_kind: NodeModuleKind,
- ) -> Result<ModuleSpecifier, TypesNotFoundError> {
- fn probe_extensions(
- fs: &dyn deno_fs::FileSystem,
+ ) -> Result<Url, TypesNotFoundError> {
+ fn probe_extensions<TEnv: NodeResolverEnv>(
+ fs: &TEnv,
path: &Path,
lowercase_path: &str,
referrer_kind: NodeModuleKind,
@@ -514,11 +511,11 @@ impl NodeResolver {
return Ok(to_file_specifier(path));
}
if let Some(path) =
- probe_extensions(&*self.fs, path, &lowercase_path, referrer_kind)
+ probe_extensions(&self.env, path, &lowercase_path, referrer_kind)
{
return Ok(to_file_specifier(&path));
}
- if self.fs.is_dir_sync(path) {
+ if self.env.is_dir_sync(path) {
let resolution_result = self.resolve_package_dir_subpath(
path,
/* sub path */ ".",
@@ -535,7 +532,7 @@ impl NodeResolver {
}
let index_path = path.join("index.js");
if let Some(path) = probe_extensions(
- &*self.fs,
+ &self.env,
&index_path,
&index_path.to_string_lossy().to_lowercase(),
referrer_kind,
@@ -554,15 +551,15 @@ impl NodeResolver {
}
#[allow(clippy::too_many_arguments)]
- pub(super) fn package_imports_resolve(
+ pub fn package_imports_resolve(
&self,
name: &str,
- maybe_referrer: Option<&ModuleSpecifier>,
+ maybe_referrer: Option<&Url>,
referrer_kind: NodeModuleKind,
referrer_pkg_json: Option<&PackageJson>,
conditions: &[&str],
mode: NodeResolutionMode,
- ) -> Result<ModuleSpecifier, PackageImportsResolveError> {
+ ) -> Result<Url, PackageImportsResolveError> {
if name == "#" || name.starts_with("#/") || name.ends_with('/') {
let reason = "is not a valid internal imports specifier name";
return Err(
@@ -659,13 +656,13 @@ impl NodeResolver {
subpath: &str,
match_: &str,
package_json_path: &Path,
- maybe_referrer: Option<&ModuleSpecifier>,
+ maybe_referrer: Option<&Url>,
referrer_kind: NodeModuleKind,
pattern: bool,
internal: bool,
conditions: &[&str],
mode: NodeResolutionMode,
- ) -> Result<ModuleSpecifier, PackageTargetResolveError> {
+ ) -> Result<Url, PackageTargetResolveError> {
if !subpath.is_empty() && !pattern && !target.ends_with('/') {
return Err(
InvalidPackageTargetError {
@@ -739,11 +736,8 @@ impl NodeResolver {
return match result {
Ok(url) => Ok(url),
Err(err) => {
- if is_builtin_node_module(target) {
- Ok(
- ModuleSpecifier::parse(&format!("node:{}", target))
- .unwrap(),
- )
+ if self.env.is_builtin_node_module(target) {
+ Ok(Url::parse(&format!("node:{}", target)).unwrap())
} else {
Err(err)
}
@@ -824,13 +818,13 @@ impl NodeResolver {
target: &Value,
subpath: &str,
package_subpath: &str,
- maybe_referrer: Option<&ModuleSpecifier>,
+ maybe_referrer: Option<&Url>,
referrer_kind: NodeModuleKind,
pattern: bool,
internal: bool,
conditions: &[&str],
mode: NodeResolutionMode,
- ) -> Result<Option<ModuleSpecifier>, PackageTargetResolveError> {
+ ) -> Result<Option<Url>, PackageTargetResolveError> {
let result = self.resolve_package_target_inner(
package_json_path,
target,
@@ -880,13 +874,13 @@ impl NodeResolver {
target: &Value,
subpath: &str,
package_subpath: &str,
- maybe_referrer: Option<&ModuleSpecifier>,
+ maybe_referrer: Option<&Url>,
referrer_kind: NodeModuleKind,
pattern: bool,
internal: bool,
conditions: &[&str],
mode: NodeResolutionMode,
- ) -> Result<Option<ModuleSpecifier>, PackageTargetResolveError> {
+ ) -> Result<Option<Url>, PackageTargetResolveError> {
if let Some(target) = target.as_str() {
let url = self.resolve_package_target_string(
target,
@@ -1007,11 +1001,11 @@ impl NodeResolver {
package_json_path: &Path,
package_subpath: &str,
package_exports: &Map<String, Value>,
- maybe_referrer: Option<&ModuleSpecifier>,
+ maybe_referrer: Option<&Url>,
referrer_kind: NodeModuleKind,
conditions: &[&str],
mode: NodeResolutionMode,
- ) -> Result<ModuleSpecifier, PackageExportsResolveError> {
+ ) -> Result<Url, PackageExportsResolveError> {
if package_exports.contains_key(package_subpath)
&& package_subpath.find('*').is_none()
&& !package_subpath.ends_with('/')
@@ -1120,11 +1114,11 @@ impl NodeResolver {
pub(super) fn package_resolve(
&self,
specifier: &str,
- referrer: &ModuleSpecifier,
+ referrer: &Url,
referrer_kind: NodeModuleKind,
conditions: &[&str],
mode: NodeResolutionMode,
- ) -> Result<ModuleSpecifier, PackageResolveError> {
+ ) -> Result<Url, PackageResolveError> {
let (package_name, package_subpath, _is_scoped) =
parse_npm_pkg_name(specifier, referrer)?;
@@ -1162,11 +1156,11 @@ impl NodeResolver {
&self,
package_name: &str,
package_subpath: &str,
- referrer: &ModuleSpecifier,
+ referrer: &Url,
referrer_kind: NodeModuleKind,
conditions: &[&str],
mode: NodeResolutionMode,
- ) -> Result<ModuleSpecifier, PackageResolveError> {
+ ) -> Result<Url, PackageResolveError> {
let result = self.resolve_package_subpath_for_package_inner(
package_name,
package_subpath,
@@ -1175,7 +1169,7 @@ impl NodeResolver {
conditions,
mode,
);
- if mode.is_types() && !matches!(result, Ok(ModuleSpecifier { .. })) {
+ if mode.is_types() && !matches!(result, Ok(Url { .. })) {
// try to resolve with the @types package
let package_name = types_package_name(package_name);
if let Ok(result) = self.resolve_package_subpath_for_package_inner(
@@ -1197,11 +1191,11 @@ impl NodeResolver {
&self,
package_name: &str,
package_subpath: &str,
- referrer: &ModuleSpecifier,
+ referrer: &Url,
referrer_kind: NodeModuleKind,
conditions: &[&str],
mode: NodeResolutionMode,
- ) -> Result<ModuleSpecifier, PackageResolveError> {
+ ) -> Result<Url, PackageResolveError> {
let package_dir_path = self
.npm_resolver
.resolve_package_folder_from_package(package_name, referrer)?;
@@ -1237,11 +1231,11 @@ impl NodeResolver {
&self,
package_dir_path: &Path,
package_subpath: &str,
- maybe_referrer: Option<&ModuleSpecifier>,
+ maybe_referrer: Option<&Url>,
referrer_kind: NodeModuleKind,
conditions: &[&str],
mode: NodeResolutionMode,
- ) -> Result<ModuleSpecifier, PackageSubpathResolveError> {
+ ) -> Result<Url, PackageSubpathResolveError> {
let package_json_path = package_dir_path.join("package.json");
match self.load_package_json(&package_json_path)? {
Some(pkg_json) => self.resolve_package_subpath(
@@ -1271,11 +1265,11 @@ impl NodeResolver {
&self,
package_json: &PackageJson,
package_subpath: &str,
- referrer: Option<&ModuleSpecifier>,
+ referrer: Option<&Url>,
referrer_kind: NodeModuleKind,
conditions: &[&str],
mode: NodeResolutionMode,
- ) -> Result<ModuleSpecifier, PackageSubpathResolveError> {
+ ) -> Result<Url, PackageSubpathResolveError> {
if let Some(exports) = &package_json.exports {
let result = self.package_exports_resolve(
&package_json.path,
@@ -1328,10 +1322,10 @@ impl NodeResolver {
&self,
directory: &Path,
package_subpath: &str,
- referrer: Option<&ModuleSpecifier>,
+ referrer: Option<&Url>,
referrer_kind: NodeModuleKind,
mode: NodeResolutionMode,
- ) -> Result<ModuleSpecifier, TypesNotFoundError> {
+ ) -> Result<Url, TypesNotFoundError> {
assert_ne!(package_subpath, ".");
let file_path = directory.join(package_subpath);
if mode.is_types() {
@@ -1345,10 +1339,10 @@ impl NodeResolver {
&self,
directory: &Path,
package_subpath: &str,
- maybe_referrer: Option<&ModuleSpecifier>,
+ maybe_referrer: Option<&Url>,
referrer_kind: NodeModuleKind,
mode: NodeResolutionMode,
- ) -> Result<ModuleSpecifier, LegacyResolveError> {
+ ) -> Result<Url, LegacyResolveError> {
if package_subpath == "." {
self.legacy_index_resolve(directory, maybe_referrer, referrer_kind, mode)
} else {
@@ -1366,7 +1360,7 @@ impl NodeResolver {
pub fn get_closest_package_json(
&self,
- url: &ModuleSpecifier,
+ url: &Url,
) -> Result<Option<PackageJsonRc>, ClosestPkgJsonError> {
let Ok(file_path) = url.to_file_path() else {
return Ok(None);
@@ -1380,10 +1374,10 @@ impl NodeResolver {
) -> Result<Option<PackageJsonRc>, ClosestPkgJsonError> {
let parent_dir = file_path.parent().unwrap();
let current_dir =
- deno_core::strip_unc_prefix(self.fs.realpath_sync(parent_dir).map_err(
+ strip_unc_prefix(self.env.realpath_sync(parent_dir).map_err(
|source| CanonicalizingPkgJsonDirError {
dir_path: parent_dir.to_path_buf(),
- source: source.into_io_error(),
+ source,
},
)?);
for current_dir in current_dir.ancestors() {
@@ -1396,20 +1390,23 @@ impl NodeResolver {
Ok(None)
}
- pub(super) fn load_package_json(
+ pub fn load_package_json(
&self,
package_json_path: &Path,
) -> Result<Option<PackageJsonRc>, PackageJsonLoadError> {
- crate::package_json::load_pkg_json(&*self.fs, package_json_path)
+ crate::package_json::load_pkg_json(
+ self.env.pkg_json_fs(),
+ package_json_path,
+ )
}
pub(super) fn legacy_main_resolve(
&self,
package_json: &PackageJson,
- maybe_referrer: Option<&ModuleSpecifier>,
+ maybe_referrer: Option<&Url>,
referrer_kind: NodeModuleKind,
mode: NodeResolutionMode,
- ) -> Result<ModuleSpecifier, LegacyResolveError> {
+ ) -> Result<Url, LegacyResolveError> {
let maybe_main = if mode.is_types() {
match package_json.types.as_ref() {
Some(types) => Some(types.as_str()),
@@ -1437,7 +1434,7 @@ impl NodeResolver {
if let Some(main) = maybe_main {
let guess = package_json.path.parent().unwrap().join(main).clean();
- if self.fs.is_file_sync(&guess) {
+ if self.env.is_file_sync(&guess) {
return Ok(to_file_specifier(&guess));
}
@@ -1466,7 +1463,7 @@ impl NodeResolver {
.unwrap()
.join(format!("{main}{ending}"))
.clean();
- if self.fs.is_file_sync(&guess) {
+ if self.env.is_file_sync(&guess) {
// TODO(bartlomieju): emitLegacyIndexDeprecation()
return Ok(to_file_specifier(&guess));
}
@@ -1484,10 +1481,10 @@ impl NodeResolver {
fn legacy_index_resolve(
&self,
directory: &Path,
- maybe_referrer: Option<&ModuleSpecifier>,
+ maybe_referrer: Option<&Url>,
referrer_kind: NodeModuleKind,
mode: NodeResolutionMode,
- ) -> Result<ModuleSpecifier, LegacyResolveError> {
+ ) -> Result<Url, LegacyResolveError> {
let index_file_names = if mode.is_types() {
// todo(dsherret): investigate exactly how typescript does this
match referrer_kind {
@@ -1499,7 +1496,7 @@ impl NodeResolver {
};
for index_file_name in index_file_names {
let guess = directory.join(index_file_name).clean();
- if self.fs.is_file_sync(&guess) {
+ if self.env.is_file_sync(&guess) {
// TODO(bartlomieju): emitLegacyIndexDeprecation()
return Ok(to_file_specifier(&guess));
}
@@ -1615,13 +1612,13 @@ fn resolve_bin_entry_value<'a>(
}
}
-fn to_file_path(url: &ModuleSpecifier) -> PathBuf {
+fn to_file_path(url: &Url) -> PathBuf {
url
.to_file_path()
.unwrap_or_else(|_| panic!("Provided URL was not file:// URL: {url}"))
}
-fn to_file_path_string(url: &ModuleSpecifier) -> String {
+fn to_file_path_string(url: &Url) -> String {
to_file_path(url).display().to_string()
}
@@ -1696,7 +1693,7 @@ fn with_known_extension(path: &Path, ext: &str) -> PathBuf {
path.with_file_name(format!("{file_name}.{ext}"))
}
-fn to_specifier_display_string(url: &ModuleSpecifier) -> String {
+fn to_specifier_display_string(url: &Url) -> String {
if let Ok(path) = url.to_file_path() {
path.display().to_string()
} else {
@@ -1708,7 +1705,7 @@ fn throw_invalid_subpath(
subpath: String,
package_json_path: &Path,
internal: bool,
- maybe_referrer: Option<&ModuleSpecifier>,
+ maybe_referrer: Option<&Url>,
) -> InvalidModuleSpecifierError {
let ie = if internal { "imports" } else { "exports" };
let reason = format!(
@@ -1725,7 +1722,7 @@ fn throw_invalid_subpath(
pub fn parse_npm_pkg_name(
specifier: &str,
- referrer: &ModuleSpecifier,
+ referrer: &Url,
) -> Result<(String, String, bool), InvalidModuleSpecifierError> {
let mut separator_index = specifier.find('/');
let mut valid_package_name = true;
@@ -1824,9 +1821,21 @@ fn types_package_name(package_name: &str) -> String {
format!("@types/{}", package_name.replace('/', "__"))
}
+/// Ex. returns `fs` for `node:fs`
+fn get_module_name_from_builtin_node_module_specifier(
+ specifier: &Url,
+) -> Option<&str> {
+ if specifier.scheme() != "node" {
+ return None;
+ }
+
+ let (_, specifier) = specifier.as_str().split_once(':')?;
+ Some(specifier)
+}
+
#[cfg(test)]
mod tests {
- use deno_core::serde_json::json;
+ use serde_json::json;
use super::*;
diff --git a/ext/node_resolver/sync.rs b/ext/node_resolver/sync.rs
new file mode 100644
index 000000000..f6689a56a
--- /dev/null
+++ b/ext/node_resolver/sync.rs
@@ -0,0 +1,86 @@
+// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
+
+pub use inner::*;
+
+#[cfg(feature = "sync")]
+mod inner {
+ #![allow(clippy::disallowed_types)]
+
+ use std::ops::Deref;
+ use std::ops::DerefMut;
+ pub use std::sync::Arc as MaybeArc;
+
+ pub struct MaybeArcMutexGuard<'lock, T>(std::sync::MutexGuard<'lock, T>);
+
+ impl<'lock, T> Deref for MaybeArcMutexGuard<'lock, T> {
+ type Target = std::sync::MutexGuard<'lock, T>;
+ fn deref(&self) -> &std::sync::MutexGuard<'lock, T> {
+ &self.0
+ }
+ }
+
+ impl<'lock, T> DerefMut for MaybeArcMutexGuard<'lock, T> {
+ fn deref_mut(&mut self) -> &mut std::sync::MutexGuard<'lock, T> {
+ &mut self.0
+ }
+ }
+
+ #[derive(Debug)]
+ pub struct MaybeArcMutex<T>(std::sync::Arc<std::sync::Mutex<T>>);
+ impl<T> MaybeArcMutex<T> {
+ pub fn new(val: T) -> Self {
+ Self(std::sync::Arc::new(std::sync::Mutex::new(val)))
+ }
+ }
+
+ impl<'lock, T> MaybeArcMutex<T> {
+ pub fn lock(&'lock self) -> MaybeArcMutexGuard<'lock, T> {
+ MaybeArcMutexGuard(self.0.lock().unwrap())
+ }
+ }
+
+ pub use core::marker::Send as MaybeSend;
+ pub use core::marker::Sync as MaybeSync;
+}
+
+#[cfg(not(feature = "sync"))]
+mod inner {
+ use std::ops::Deref;
+ use std::ops::DerefMut;
+
+ pub use std::rc::Rc as MaybeArc;
+
+ pub struct MaybeArcMutexGuard<'lock, T>(std::cell::RefMut<'lock, T>);
+
+ impl<'lock, T> Deref for MaybeArcMutexGuard<'lock, T> {
+ type Target = std::cell::RefMut<'lock, T>;
+ fn deref(&self) -> &std::cell::RefMut<'lock, T> {
+ &self.0
+ }
+ }
+
+ impl<'lock, T> DerefMut for MaybeArcMutexGuard<'lock, T> {
+ fn deref_mut(&mut self) -> &mut std::cell::RefMut<'lock, T> {
+ &mut self.0
+ }
+ }
+
+ #[derive(Debug)]
+ pub struct MaybeArcMutex<T>(std::rc::Rc<std::cell::RefCell<T>>);
+ impl<T> MaybeArcMutex<T> {
+ pub fn new(val: T) -> Self {
+ Self(std::rc::Rc::new(std::cell::RefCell::new(val)))
+ }
+ }
+
+ impl<'lock, T> MaybeArcMutex<T> {
+ pub fn lock(&'lock self) -> MaybeArcMutexGuard<'lock, T> {
+ MaybeArcMutexGuard(self.0.borrow_mut())
+ }
+ }
+
+ pub trait MaybeSync {}
+ impl<T> MaybeSync for T where T: ?Sized {}
+ pub trait MaybeSend {}
+ impl<T> MaybeSend for T where T: ?Sized {}
+}
diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml
index 7772b017b..9980df294 100644
--- a/runtime/Cargo.toml
+++ b/runtime/Cargo.toml
@@ -94,6 +94,7 @@ deno_webgpu.workspace = true
deno_webidl.workspace = true
deno_websocket.workspace = true
deno_webstorage.workspace = true
+node_resolver = { workspace = true, features = ["sync"] }
dlopen2.workspace = true
encoding_rs.workspace = true
diff --git a/runtime/snapshot.rs b/runtime/snapshot.rs
index 2144ff07a..da66bff5e 100644
--- a/runtime/snapshot.rs
+++ b/runtime/snapshot.rs
@@ -254,7 +254,7 @@ pub fn create_runtime_snapshot(
deno_http::deno_http::init_ops_and_esm::<DefaultHttpPropertyExtractor>(),
deno_io::deno_io::init_ops_and_esm(Default::default()),
deno_fs::deno_fs::init_ops_and_esm::<Permissions>(fs.clone()),
- deno_node::deno_node::init_ops_and_esm::<Permissions>(None, None, fs),
+ deno_node::deno_node::init_ops_and_esm::<Permissions>(None, fs),
runtime::init_ops_and_esm(),
ops::runtime::deno_runtime::init_ops("deno:runtime".parse().unwrap()),
ops::worker_host::deno_worker_host::init_ops(
diff --git a/runtime/web_worker.rs b/runtime/web_worker.rs
index cf0384196..2611b6f34 100644
--- a/runtime/web_worker.rs
+++ b/runtime/web_worker.rs
@@ -43,7 +43,7 @@ use deno_fs::FileSystem;
use deno_http::DefaultHttpPropertyExtractor;
use deno_io::Stdio;
use deno_kv::dynamic::MultiBackendDbHandler;
-use deno_node::NodeResolver;
+use deno_node::NodeExtInitServices;
use deno_permissions::PermissionsContainer;
use deno_terminal::colors;
use deno_tls::RootCertStoreProvider;
@@ -364,8 +364,7 @@ pub struct WebWorkerOptions {
pub seed: Option<u64>,
pub fs: Arc<dyn FileSystem>,
pub module_loader: Rc<dyn ModuleLoader>,
- pub node_resolver: Option<Arc<NodeResolver>>,
- pub npm_resolver: Option<Arc<dyn deno_node::NpmResolver>>,
+ pub node_services: Option<NodeExtInitServices>,
pub create_web_worker_cb: Arc<ops::worker_host::CreateWebWorkerCb>,
pub format_js_error_fn: Option<Arc<FormatJsErrorFn>>,
pub worker_type: WebWorkerType,
@@ -490,8 +489,7 @@ impl WebWorker {
options.fs.clone(),
),
deno_node::deno_node::init_ops_and_esm::<PermissionsContainer>(
- options.node_resolver,
- options.npm_resolver,
+ options.node_services,
options.fs,
),
// Runtime ops that are always initialized for WebWorkers
diff --git a/runtime/worker.rs b/runtime/worker.rs
index fc11be582..bd67c8706 100644
--- a/runtime/worker.rs
+++ b/runtime/worker.rs
@@ -38,6 +38,7 @@ use deno_fs::FileSystem;
use deno_http::DefaultHttpPropertyExtractor;
use deno_io::Stdio;
use deno_kv::dynamic::MultiBackendDbHandler;
+use deno_node::NodeExtInitServices;
use deno_permissions::PermissionsContainer;
use deno_tls::RootCertStoreProvider;
use deno_tls::TlsKeys;
@@ -155,8 +156,7 @@ pub struct WorkerOptions {
/// If not provided runtime will error if code being
/// executed tries to load modules.
pub module_loader: Rc<dyn ModuleLoader>,
- pub node_resolver: Option<Arc<deno_node::NodeResolver>>,
- pub npm_resolver: Option<Arc<dyn deno_node::NpmResolver>>,
+ pub node_services: Option<NodeExtInitServices>,
// Callbacks invoked when creating new instance of WebWorker
pub create_web_worker_cb: Arc<ops::worker_host::CreateWebWorkerCb>,
pub format_js_error_fn: Option<Arc<FormatJsErrorFn>>,
@@ -224,8 +224,7 @@ impl Default for WorkerOptions {
cache_storage_dir: Default::default(),
broadcast_channel: Default::default(),
root_cert_store_provider: Default::default(),
- node_resolver: Default::default(),
- npm_resolver: Default::default(),
+ node_services: Default::default(),
blob_store: Default::default(),
extensions: Default::default(),
startup_snapshot: Default::default(),
@@ -414,8 +413,7 @@ impl MainWorker {
options.fs.clone(),
),
deno_node::deno_node::init_ops_and_esm::<PermissionsContainer>(
- options.node_resolver,
- options.npm_resolver,
+ options.node_services,
options.fs,
),
// Ops from this crate