diff options
author | haturau <135221985+haturatu@users.noreply.github.com> | 2024-11-20 01:20:47 +0900 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-11-20 01:20:47 +0900 |
commit | 85719a67e59c7aa45bead26e4942d7df8b1b42d4 (patch) | |
tree | face0aecaac53e93ce2f23b53c48859bcf1a36ec /ext/node/ops/require.rs | |
parent | 67697bc2e4a62a9670699fd18ad0dd8efc5bd955 (diff) | |
parent | 186b52731c6bb326c4d32905c5e732d082e83465 (diff) |
Merge branch 'denoland:main' into main
Diffstat (limited to 'ext/node/ops/require.rs')
-rw-r--r-- | ext/node/ops/require.rs | 200 |
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, |