summaryrefslogtreecommitdiff
path: root/ext/node/ops/require.rs
diff options
context:
space:
mode:
authorhaturau <135221985+haturatu@users.noreply.github.com>2024-11-20 01:20:47 +0900
committerGitHub <noreply@github.com>2024-11-20 01:20:47 +0900
commit85719a67e59c7aa45bead26e4942d7df8b1b42d4 (patch)
treeface0aecaac53e93ce2f23b53c48859bcf1a36ec /ext/node/ops/require.rs
parent67697bc2e4a62a9670699fd18ad0dd8efc5bd955 (diff)
parent186b52731c6bb326c4d32905c5e732d082e83465 (diff)
Merge branch 'denoland:main' into main
Diffstat (limited to 'ext/node/ops/require.rs')
-rw-r--r--ext/node/ops/require.rs200
1 files changed, 119 insertions, 81 deletions
diff --git a/ext/node/ops/require.rs b/ext/node/ops/require.rs
index 547336981..06c034fd5 100644
--- a/ext/node/ops/require.rs
+++ b/ext/node/ops/require.rs
@@ -1,18 +1,19 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
-use deno_core::anyhow::Context;
-use deno_core::error::generic_error;
+use boxed_error::Boxed;
use deno_core::error::AnyError;
use deno_core::op2;
use deno_core::url::Url;
use deno_core::v8;
use deno_core::JsRuntimeInspector;
-use deno_core::ModuleSpecifier;
use deno_core::OpState;
use deno_fs::FileSystemRc;
+use deno_package_json::NodeModuleKind;
use deno_package_json::PackageJsonRc;
use deno_path_util::normalize_path;
-use node_resolver::NodeModuleKind;
+use deno_path_util::url_from_file_path;
+use deno_path_util::url_to_file_path;
+use node_resolver::errors::ClosestPkgJsonError;
use node_resolver::NodeResolutionMode;
use node_resolver::REQUIRE_CONDITIONS;
use std::borrow::Cow;
@@ -22,21 +23,55 @@ use std::path::PathBuf;
use std::rc::Rc;
use crate::NodePermissions;
-use crate::NodeRequireResolverRc;
+use crate::NodeRequireLoaderRc;
use crate::NodeResolverRc;
-use crate::NpmResolverRc;
+use crate::NpmPackageFolderResolverRc;
+use crate::PackageJsonResolverRc;
#[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
fn ensure_read_permission<'a, P>(
state: &mut OpState,
file_path: &'a Path,
-) -> Result<Cow<'a, Path>, AnyError>
+) -> Result<Cow<'a, Path>, deno_core::error::AnyError>
where
P: NodePermissions + 'static,
{
- let resolver = state.borrow::<NodeRequireResolverRc>().clone();
+ let loader = state.borrow::<NodeRequireLoaderRc>().clone();
let permissions = state.borrow_mut::<P>();
- resolver.ensure_read_permission(permissions, file_path)
+ loader.ensure_read_permission(permissions, file_path)
+}
+
+#[derive(Debug, Boxed)]
+pub struct RequireError(pub Box<RequireErrorKind>);
+
+#[derive(Debug, thiserror::Error)]
+pub enum RequireErrorKind {
+ #[error(transparent)]
+ UrlParse(#[from] url::ParseError),
+ #[error(transparent)]
+ Permission(deno_core::error::AnyError),
+ #[error(transparent)]
+ PackageExportsResolve(
+ #[from] node_resolver::errors::PackageExportsResolveError,
+ ),
+ #[error(transparent)]
+ PackageJsonLoad(#[from] node_resolver::errors::PackageJsonLoadError),
+ #[error(transparent)]
+ ClosestPkgJson(#[from] node_resolver::errors::ClosestPkgJsonError),
+ #[error(transparent)]
+ PackageImportsResolve(
+ #[from] node_resolver::errors::PackageImportsResolveError,
+ ),
+ #[error(transparent)]
+ FilePathConversion(#[from] deno_path_util::UrlToFilePathError),
+ #[error(transparent)]
+ UrlConversion(#[from] deno_path_util::PathToUrlError),
+ #[error(transparent)]
+ Fs(#[from] deno_io::fs::FsError),
+ #[error(transparent)]
+ ReadModule(deno_core::error::AnyError),
+ #[error("Unable to get CWD: {0}")]
+ UnableToGetCwd(deno_io::fs::FsError),
}
#[op2]
@@ -95,7 +130,7 @@ pub fn op_require_init_paths() -> Vec<String> {
pub fn op_require_node_module_paths<P>(
state: &mut OpState,
#[string] from: String,
-) -> Result<Vec<String>, AnyError>
+) -> Result<Vec<String>, RequireError>
where
P: NodePermissions + 'static,
{
@@ -104,13 +139,10 @@ where
let from = if from.starts_with("file:///") {
url_to_file_path(&Url::parse(&from)?)?
} else {
- let current_dir =
- &(fs.cwd().map_err(AnyError::from)).context("Unable to get CWD")?;
- deno_path_util::normalize_path(current_dir.join(from))
+ let current_dir = &fs.cwd().map_err(RequireErrorKind::UnableToGetCwd)?;
+ normalize_path(current_dir.join(from))
};
- let from = ensure_read_permission::<P>(state, &from)?;
-
if cfg!(windows) {
// return root node_modules when path is 'D:\\'.
let from_str = from.to_str().unwrap();
@@ -131,7 +163,7 @@ where
}
let mut paths = Vec::with_capacity(from.components().count());
- let mut current_path = from.as_ref();
+ let mut current_path = from.as_path();
let mut maybe_parent = Some(current_path);
while let Some(parent) = maybe_parent {
if !parent.ends_with("node_modules") {
@@ -191,17 +223,17 @@ pub fn op_require_resolve_deno_dir(
state: &mut OpState,
#[string] request: String,
#[string] parent_filename: String,
-) -> Option<String> {
- let resolver = state.borrow::<NpmResolverRc>();
- resolver
- .resolve_package_folder_from_package(
- &request,
- &ModuleSpecifier::from_file_path(&parent_filename).unwrap_or_else(|_| {
- panic!("Url::from_file_path: [{:?}]", parent_filename)
- }),
- )
- .ok()
- .map(|p| p.to_string_lossy().into_owned())
+) -> Result<Option<String>, AnyError> {
+ let resolver = state.borrow::<NpmPackageFolderResolverRc>();
+ Ok(
+ resolver
+ .resolve_package_folder_from_package(
+ &request,
+ &url_from_file_path(&PathBuf::from(parent_filename))?,
+ )
+ .ok()
+ .map(|p| p.to_string_lossy().into_owned()),
+ )
}
#[op2(fast)]
@@ -209,8 +241,11 @@ pub fn op_require_is_deno_dir_package(
state: &mut OpState,
#[string] path: String,
) -> bool {
- let resolver = state.borrow::<NpmResolverRc>();
- resolver.in_npm_package_at_file_path(&PathBuf::from(path))
+ let resolver = state.borrow::<NodeResolverRc>();
+ match deno_path_util::url_from_file_path(&PathBuf::from(path)) {
+ Ok(specifier) => resolver.in_npm_package(&specifier),
+ Err(_) => false,
+ }
}
#[op2]
@@ -264,7 +299,7 @@ pub fn op_require_path_is_absolute(#[string] p: String) -> bool {
pub fn op_require_stat<P>(
state: &mut OpState,
#[string] path: String,
-) -> Result<i32, AnyError>
+) -> Result<i32, deno_core::error::AnyError>
where
P: NodePermissions + 'static,
{
@@ -287,15 +322,16 @@ where
pub fn op_require_real_path<P>(
state: &mut OpState,
#[string] request: String,
-) -> Result<String, AnyError>
+) -> Result<String, RequireError>
where
P: NodePermissions + 'static,
{
let path = PathBuf::from(request);
- let path = ensure_read_permission::<P>(state, &path)?;
+ let path = ensure_read_permission::<P>(state, &path)
+ .map_err(RequireErrorKind::Permission)?;
let fs = state.borrow::<FileSystemRc>();
let canonicalized_path =
- deno_core::strip_unc_prefix(fs.realpath_sync(&path)?);
+ deno_path_util::strip_unc_prefix(fs.realpath_sync(&path)?);
Ok(canonicalized_path.to_string_lossy().into_owned())
}
@@ -319,12 +355,14 @@ pub fn op_require_path_resolve(#[serde] parts: Vec<String>) -> String {
#[string]
pub fn op_require_path_dirname(
#[string] request: String,
-) -> Result<String, AnyError> {
+) -> Result<String, deno_core::error::AnyError> {
let p = PathBuf::from(request);
if let Some(parent) = p.parent() {
Ok(parent.to_string_lossy().into_owned())
} else {
- Err(generic_error("Path doesn't have a parent"))
+ Err(deno_core::error::generic_error(
+ "Path doesn't have a parent",
+ ))
}
}
@@ -332,12 +370,14 @@ pub fn op_require_path_dirname(
#[string]
pub fn op_require_path_basename(
#[string] request: String,
-) -> Result<String, AnyError> {
+) -> Result<String, deno_core::error::AnyError> {
let p = PathBuf::from(request);
if let Some(path) = p.file_name() {
Ok(path.to_string_lossy().into_owned())
} else {
- Err(generic_error("Path doesn't have a file name"))
+ Err(deno_core::error::generic_error(
+ "Path doesn't have a file name",
+ ))
}
}
@@ -348,7 +388,7 @@ pub fn op_require_try_self_parent_path<P>(
has_parent: bool,
#[string] maybe_parent_filename: Option<String>,
#[string] maybe_parent_id: Option<String>,
-) -> Result<Option<String>, AnyError>
+) -> Result<Option<String>, deno_core::error::AnyError>
where
P: NodePermissions + 'static,
{
@@ -378,7 +418,7 @@ pub fn op_require_try_self<P>(
state: &mut OpState,
#[string] parent_path: Option<String>,
#[string] request: String,
-) -> Result<Option<String>, AnyError>
+) -> Result<Option<String>, RequireError>
where
P: NodePermissions + 'static,
{
@@ -386,8 +426,8 @@ where
return Ok(None);
}
- let node_resolver = state.borrow::<NodeResolverRc>();
- let pkg = node_resolver
+ let pkg_json_resolver = state.borrow::<PackageJsonResolverRc>();
+ let pkg = pkg_json_resolver
.get_closest_package_json_from_path(&PathBuf::from(parent_path.unwrap()))
.ok()
.flatten();
@@ -416,6 +456,7 @@ where
let referrer = deno_core::url::Url::from_file_path(&pkg.path).unwrap();
if let Some(exports) = &pkg.exports {
+ let node_resolver = state.borrow::<NodeResolverRc>();
let r = node_resolver.package_exports_resolve(
&pkg.path,
&expansion,
@@ -440,14 +481,18 @@ where
pub fn op_require_read_file<P>(
state: &mut OpState,
#[string] file_path: String,
-) -> Result<String, AnyError>
+) -> Result<String, RequireError>
where
P: NodePermissions + 'static,
{
let file_path = PathBuf::from(file_path);
- let file_path = ensure_read_permission::<P>(state, &file_path)?;
- let fs = state.borrow::<FileSystemRc>();
- Ok(fs.read_text_file_lossy_sync(&file_path, None)?)
+ // todo(dsherret): there's multiple borrows to NodeRequireLoaderRc here
+ let file_path = ensure_read_permission::<P>(state, &file_path)
+ .map_err(RequireErrorKind::Permission)?;
+ let loader = state.borrow::<NodeRequireLoaderRc>();
+ loader
+ .load_text_file_lossy(&file_path)
+ .map_err(|e| RequireErrorKind::ReadModule(e).into_box())
}
#[op2]
@@ -472,16 +517,17 @@ pub fn op_require_resolve_exports<P>(
#[string] name: String,
#[string] expansion: String,
#[string] parent_path: String,
-) -> Result<Option<String>, AnyError>
+) -> Result<Option<String>, RequireError>
where
P: NodePermissions + 'static,
{
let fs = state.borrow::<FileSystemRc>();
- let npm_resolver = state.borrow::<NpmResolverRc>();
let node_resolver = state.borrow::<NodeResolverRc>();
+ let pkg_json_resolver = state.borrow::<PackageJsonResolverRc>();
let modules_path = PathBuf::from(&modules_path_str);
- let pkg_path = if npm_resolver.in_npm_package_at_file_path(&modules_path)
+ let modules_specifier = deno_path_util::url_from_file_path(&modules_path)?;
+ let pkg_path = if node_resolver.in_npm_package(&modules_specifier)
&& !uses_local_node_modules_dir
{
modules_path
@@ -495,7 +541,7 @@ where
}
};
let Some(pkg) =
- node_resolver.load_package_json(&pkg_path.join("package.json"))?
+ pkg_json_resolver.load_package_json(&pkg_path.join("package.json"))?
else {
return Ok(None);
};
@@ -503,12 +549,16 @@ where
return Ok(None);
};
- let referrer = Url::from_file_path(parent_path).unwrap();
+ let referrer = if parent_path.is_empty() {
+ None
+ } else {
+ Some(Url::from_file_path(parent_path).unwrap())
+ };
let r = node_resolver.package_exports_resolve(
&pkg.path,
&format!(".{expansion}"),
exports,
- Some(&referrer),
+ referrer.as_ref(),
NodeModuleKind::Cjs,
REQUIRE_CONDITIONS,
NodeResolutionMode::Execution,
@@ -520,21 +570,17 @@ where
}))
}
-#[op2]
-#[serde]
-pub fn op_require_read_closest_package_json<P>(
+#[op2(fast)]
+pub fn op_require_is_maybe_cjs(
state: &mut OpState,
#[string] filename: String,
-) -> Result<Option<PackageJsonRc>, AnyError>
-where
- P: NodePermissions + 'static,
-{
+) -> Result<bool, ClosestPkgJsonError> {
let filename = PathBuf::from(filename);
- // permissions: allow reading the closest package.json files
- let node_resolver = state.borrow::<NodeResolverRc>().clone();
- node_resolver
- .get_closest_package_json_from_path(&filename)
- .map_err(AnyError::from)
+ let Ok(url) = url_from_file_path(&filename) else {
+ return Ok(false);
+ };
+ let loader = state.borrow::<NodeRequireLoaderRc>();
+ loader.is_maybe_cjs(&url)
}
#[op2]
@@ -546,13 +592,13 @@ pub fn op_require_read_package_scope<P>(
where
P: NodePermissions + 'static,
{
- let node_resolver = state.borrow::<NodeResolverRc>().clone();
+ let pkg_json_resolver = state.borrow::<PackageJsonResolverRc>();
let package_json_path = PathBuf::from(package_json_path);
if package_json_path.file_name() != Some("package.json".as_ref()) {
// permissions: do not allow reading a non-package.json file
return None;
}
- node_resolver
+ pkg_json_resolver
.load_package_json(&package_json_path)
.ok()
.flatten()
@@ -564,22 +610,23 @@ pub fn op_require_package_imports_resolve<P>(
state: &mut OpState,
#[string] referrer_filename: String,
#[string] request: String,
-) -> Result<Option<String>, AnyError>
+) -> Result<Option<String>, RequireError>
where
P: NodePermissions + 'static,
{
let referrer_path = PathBuf::from(&referrer_filename);
- let referrer_path = ensure_read_permission::<P>(state, &referrer_path)?;
- let node_resolver = state.borrow::<NodeResolverRc>();
+ let referrer_path = ensure_read_permission::<P>(state, &referrer_path)
+ .map_err(RequireErrorKind::Permission)?;
+ let pkg_json_resolver = state.borrow::<PackageJsonResolverRc>();
let Some(pkg) =
- node_resolver.get_closest_package_json_from_path(&referrer_path)?
+ pkg_json_resolver.get_closest_package_json_from_path(&referrer_path)?
else {
return Ok(None);
};
if pkg.imports.is_some() {
- let referrer_url =
- deno_core::url::Url::from_file_path(&referrer_filename).unwrap();
+ let node_resolver = state.borrow::<NodeResolverRc>();
+ let referrer_url = Url::from_file_path(&referrer_filename).unwrap();
let url = node_resolver.package_imports_resolve(
&request,
Some(&referrer_url),
@@ -604,20 +651,11 @@ pub fn op_require_break_on_next_statement(state: Rc<RefCell<OpState>>) {
inspector.wait_for_session_and_break_on_next_statement()
}
-fn url_to_file_path_string(url: &Url) -> Result<String, AnyError> {
+fn url_to_file_path_string(url: &Url) -> Result<String, RequireError> {
let file_path = url_to_file_path(url)?;
Ok(file_path.to_string_lossy().into_owned())
}
-fn url_to_file_path(url: &Url) -> Result<PathBuf, AnyError> {
- match url.to_file_path() {
- Ok(file_path) => Ok(file_path),
- Err(()) => {
- deno_core::anyhow::bail!("failed to convert '{}' to file path", url)
- }
- }
-}
-
#[op2(fast)]
pub fn op_require_can_parse_as_esm(
scope: &mut v8::HandleScope,