summaryrefslogtreecommitdiff
path: root/ext
diff options
context:
space:
mode:
authorDavid Sherret <dsherret@users.noreply.github.com>2024-07-09 12:15:03 -0400
committerGitHub <noreply@github.com>2024-07-09 12:15:03 -0400
commit839caf6fafdf9ca1cdec6cd9cef38296be41145f (patch)
tree691dba21b45e9c5640275304308aa5d8a5d4a7ba /ext
parent07613a6bf26d9112d47fda9e502425395bd78105 (diff)
refactor: use concrete error types for node resolution (#24470)
This will help clean up some of the code in the CLI because we'll be able to tell how the resolution failed (not part of this PR).
Diffstat (limited to 'ext')
-rw-r--r--ext/fs/interface.rs22
-rw-r--r--ext/node/Cargo.toml1
-rw-r--r--ext/node/analyze.rs22
-rw-r--r--ext/node/errors.rs562
-rw-r--r--ext/node/lib.rs2
-rw-r--r--ext/node/ops/require.rs7
-rw-r--r--ext/node/resolution.rs670
7 files changed, 809 insertions, 477 deletions
diff --git a/ext/fs/interface.rs b/ext/fs/interface.rs
index 8f791f4c2..2fdfa2186 100644
--- a/ext/fs/interface.rs
+++ b/ext/fs/interface.rs
@@ -350,7 +350,7 @@ impl<'a> deno_config::fs::DenoConfigFs for DenoConfigFsAdapter<'a> {
self
.0
.read_text_file_lossy_sync(path, None)
- .map_err(map_deno_fs_to_config_err)
+ .map_err(|err| err.into_io_error())
}
fn stat_sync(
@@ -365,7 +365,7 @@ impl<'a> deno_config::fs::DenoConfigFs for DenoConfigFsAdapter<'a> {
is_directory: stat.is_directory,
is_symlink: stat.is_symlink,
})
- .map_err(map_deno_fs_to_config_err)
+ .map_err(|err| err.into_io_error())
}
fn read_dir(
@@ -375,7 +375,7 @@ impl<'a> deno_config::fs::DenoConfigFs for DenoConfigFsAdapter<'a> {
self
.0
.read_dir_sync(path)
- .map_err(map_deno_fs_to_config_err)
+ .map_err(|err| err.into_io_error())
.map(|entries| {
entries
.into_iter()
@@ -392,22 +392,6 @@ impl<'a> deno_config::fs::DenoConfigFs for DenoConfigFsAdapter<'a> {
}
}
-fn map_deno_fs_to_config_err(fs_err: deno_io::fs::FsError) -> std::io::Error {
- use deno_io::fs::FsError;
- use std::io::ErrorKind;
- match fs_err {
- FsError::Io(io) => io,
- FsError::FileBusy => std::io::Error::new(ErrorKind::Other, "file busy"),
- FsError::NotSupported => {
- std::io::Error::new(ErrorKind::Other, "not supported")
- }
- FsError::PermissionDenied(name) => std::io::Error::new(
- ErrorKind::PermissionDenied,
- format!("requires {}", name),
- ),
- }
-}
-
// Like String::from_utf8_lossy but operates on owned values
#[inline(always)]
fn string_from_utf8_lossy(buf: Vec<u8>) -> String {
diff --git a/ext/node/Cargo.toml b/ext/node/Cargo.toml
index b9228dfdd..fcbd07903 100644
--- a/ext/node/Cargo.toml
+++ b/ext/node/Cargo.toml
@@ -78,6 +78,7 @@ signature.workspace = true
simd-json = "0.13.4"
sm3 = "0.4.2"
spki.workspace = true
+thiserror.workspace = true
tokio.workspace = true
url.workspace = true
winapi.workspace = true
diff --git a/ext/node/analyze.rs b/ext/node/analyze.rs
index 0a4ff8dac..3513a8105 100644
--- a/ext/node/analyze.rs
+++ b/ext/node/analyze.rs
@@ -278,6 +278,7 @@ impl<TCjsCodeAnalyzer: CjsCodeAnalyzer> NodeCodeTranslator<TCjsCodeAnalyzer> {
}
}
+ // todo(dsherret): what is going on here? Isn't this a bunch of duplicate code?
fn resolve(
&self,
specifier: &str,
@@ -314,15 +315,18 @@ impl<TCjsCodeAnalyzer: CjsCodeAnalyzer> NodeCodeTranslator<TCjsCodeAnalyzer> {
let maybe_package_json = load_pkg_json(&*self.fs, &package_json_path)?;
if let Some(package_json) = maybe_package_json {
if let Some(exports) = &package_json.exports {
- return self.node_resolver.package_exports_resolve(
- &package_json_path,
- &package_subpath,
- exports,
- referrer,
- NodeModuleKind::Esm,
- conditions,
- mode,
- );
+ return self
+ .node_resolver
+ .package_exports_resolve(
+ &package_json_path,
+ &package_subpath,
+ exports,
+ Some(referrer),
+ NodeModuleKind::Esm,
+ conditions,
+ mode,
+ )
+ .map_err(AnyError::from);
}
// old school
diff --git a/ext/node/errors.rs b/ext/node/errors.rs
index 45ae0b778..560336d68 100644
--- a/ext/node/errors.rs
+++ b/ext/node/errors.rs
@@ -1,170 +1,462 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
+use std::borrow::Cow;
use std::path::PathBuf;
use deno_core::error::generic_error;
-use deno_core::error::type_error;
use deno_core::error::AnyError;
-use deno_core::url::Url;
+use deno_core::ModuleSpecifier;
+use thiserror::Error;
+use crate::NodeModuleKind;
use crate::NodeResolutionMode;
-pub fn err_invalid_module_specifier(
- request: &str,
- reason: &str,
- maybe_base: Option<String>,
-) -> AnyError {
- let mut msg = format!(
- "[ERR_INVALID_MODULE_SPECIFIER] Invalid module \"{request}\" {reason}"
- );
+macro_rules! kinded_err {
+ ($name:ident, $kind_name:ident) => {
+ #[derive(Error, Debug)]
+ #[error(transparent)]
+ pub struct $name(pub Box<$kind_name>);
- if let Some(base) = maybe_base {
- msg = format!("{msg} imported from {base}");
- }
+ impl $name {
+ pub fn as_kind(&self) -> &$kind_name {
+ &self.0
+ }
- type_error(msg)
+ pub fn into_kind(self) -> $kind_name {
+ *self.0
+ }
+ }
+
+ impl<E> From<E> for $name
+ where
+ $kind_name: From<E>,
+ {
+ fn from(err: E) -> Self {
+ $name(Box::new($kind_name::from(err)))
+ }
+ }
+ };
}
-#[allow(unused)]
-pub fn err_invalid_package_config(
- path: &str,
- maybe_base: Option<String>,
- maybe_message: Option<String>,
-) -> AnyError {
- let mut msg =
- format!("[ERR_INVALID_PACKAGE_CONFIG] Invalid package config {path}");
+kinded_err!(
+ ResolvePkgSubpathFromDenoModuleError,
+ ResolvePkgSubpathFromDenoModuleErrorKind
+);
- if let Some(base) = maybe_base {
- msg = format!("{msg} while importing {base}");
- }
+#[derive(Debug, Error)]
+pub enum ResolvePkgSubpathFromDenoModuleErrorKind {
+ #[error(transparent)]
+ PackageSubpathResolve(#[from] PackageSubpathResolveError),
+ #[error(transparent)]
+ UrlToNodeResolution(#[from] UrlToNodeResolutionError),
+}
- if let Some(message) = maybe_message {
- msg = format!("{msg}. {message}");
- }
+// todo(https://github.com/denoland/deno_core/issues/810): make this a TypeError
+#[derive(Debug, Clone, Error)]
+#[error(
+ "[ERR_INVALID_MODULE_SPECIFIER] Invalid module '{}' {}{}",
+ request,
+ reason,
+ maybe_referrer.as_ref().map(|referrer| format!(" imported from '{}'", referrer)).unwrap_or_default()
+)]
+pub struct InvalidModuleSpecifierError {
+ pub request: String,
+ pub reason: Cow<'static, str>,
+ pub maybe_referrer: Option<String>,
+}
- generic_error(msg)
+#[derive(Debug, Error)]
+pub enum LegacyMainResolveError {
+ #[error(transparent)]
+ PathToDeclarationUrl(PathToDeclarationUrlError),
}
-pub fn err_module_not_found(path: &str, base: &str, typ: &str) -> AnyError {
- generic_error(format!(
- "[ERR_MODULE_NOT_FOUND] Cannot find {typ} \"{path}\" imported from \"{base}\""
- ))
+kinded_err!(PackageFolderResolveError, PackageFolderResolveErrorKind);
+
+#[derive(Debug, Error)]
+pub enum PackageFolderResolveErrorKind {
+ #[error(
+ "Could not find package '{}' from referrer '{}'{}.",
+ package_name,
+ referrer,
+ referrer_extra.as_ref().map(|r| format!(" ({})", r)).unwrap_or_default()
+ )]
+ NotFoundPackage {
+ package_name: String,
+ referrer: ModuleSpecifier,
+ /// Extra information about the referrer.
+ referrer_extra: Option<String>,
+ },
+ #[error(
+ "Could not find referrer npm package '{}'{}.",
+ referrer,
+ referrer_extra.as_ref().map(|r| format!(" ({})", r)).unwrap_or_default()
+ )]
+ NotFoundReferrer {
+ referrer: ModuleSpecifier,
+ /// Extra information about the referrer.
+ referrer_extra: Option<String>,
+ },
+ #[error("Failed resolving '{package_name}' from referrer '{referrer}'.")]
+ Io {
+ package_name: String,
+ referrer: ModuleSpecifier,
+ #[source]
+ source: std::io::Error,
+ },
}
-pub fn err_invalid_package_target(
- pkg_path: &str,
- key: &str,
- target: &str,
- is_import: bool,
- maybe_referrer: Option<String>,
-) -> AnyError {
- let rel_error = !is_import && !target.is_empty() && !target.starts_with("./");
- let mut msg = "[ERR_INVALID_PACKAGE_TARGET]".to_string();
- let pkg_json_path = PathBuf::from(pkg_path).join("package.json");
-
- if key == "." {
- assert!(!is_import);
- msg = format!(
- "{} Invalid \"exports\" main target {} defined in the package config {}",
- msg,
- target,
- pkg_json_path.display()
- )
- } else {
- let ie = if is_import { "imports" } else { "exports" };
- msg = format!(
- "{} Invalid \"{}\" target {} defined for '{}' in the package config {}",
- msg,
- ie,
- target,
- key,
- pkg_json_path.display()
- )
- };
+kinded_err!(PackageSubpathResolveError, PackageSubpathResolveErrorKind);
- if let Some(base) = maybe_referrer {
- msg = format!("{msg} imported from {base}");
- };
- if rel_error {
- msg = format!("{msg}; target must start with \"./\"");
+#[derive(Debug, Error)]
+pub enum PackageSubpathResolveErrorKind {
+ #[error(transparent)]
+ PkgJsonLoad(#[from] deno_config::package_json::PackageJsonLoadError),
+ #[error(transparent)]
+ PackageFolderResolve(#[from] PackageFolderResolveError),
+ #[error(transparent)]
+ DirNotFound(AnyError),
+ #[error(transparent)]
+ Exports(PackageExportsResolveError),
+ #[error(transparent)]
+ LegacyMain(LegacyMainResolveError),
+ #[error(transparent)]
+ LegacyExact(PathToDeclarationUrlError),
+}
+
+#[derive(Debug, Error)]
+#[error(
+ "Target '{}' not found from '{}'{}{}.",
+ target,
+ pkg_json_path.display(),
+ maybe_referrer.as_ref().map(|r|
+ format!(
+ " from{} referrer {}",
+ match referrer_kind {
+ NodeModuleKind::Esm => "",
+ NodeModuleKind::Cjs => " cjs",
+ },
+ r
+ )
+ ).unwrap_or_default(),
+ match mode {
+ NodeResolutionMode::Execution => "",
+ NodeResolutionMode::Types => " for types",
}
+)]
+pub struct PackageTargetNotFoundError {
+ pub pkg_json_path: PathBuf,
+ pub target: String,
+ pub maybe_referrer: Option<ModuleSpecifier>,
+ pub referrer_kind: NodeModuleKind,
+ pub mode: NodeResolutionMode,
+}
- generic_error(msg)
+kinded_err!(PackageTargetResolveError, PackageTargetResolveErrorKind);
+
+#[derive(Debug, Error)]
+pub enum PackageTargetResolveErrorKind {
+ #[error(transparent)]
+ NotFound(#[from] PackageTargetNotFoundError),
+ #[error(transparent)]
+ InvalidPackageTarget(#[from] InvalidPackageTargetError),
+ #[error(transparent)]
+ InvalidModuleSpecifier(#[from] InvalidModuleSpecifierError),
+ #[error(transparent)]
+ PackageResolve(#[from] PackageResolveError),
+ #[error(transparent)]
+ PathToDeclarationUrl(#[from] PathToDeclarationUrlError),
}
-pub fn err_package_path_not_exported(
- mut pkg_path: String,
- subpath: &str,
- maybe_referrer: Option<String>,
- mode: NodeResolutionMode,
-) -> AnyError {
- let mut msg = "[ERR_PACKAGE_PATH_NOT_EXPORTED]".to_string();
+kinded_err!(PackageExportsResolveError, PackageExportsResolveErrorKind);
+
+#[derive(Debug, Error)]
+pub enum PackageExportsResolveErrorKind {
+ #[error(transparent)]
+ PackagePathNotExported(#[from] PackagePathNotExportedError),
+ #[error(transparent)]
+ PackageTargetResolve(#[from] PackageTargetResolveError),
+}
+
+#[derive(Debug, Error)]
+pub enum PathToDeclarationUrlError {
+ #[error(transparent)]
+ SubPath(#[from] PackageSubpathResolveError),
+}
+
+kinded_err!(ClosestPkgJsonError, ClosestPkgJsonErrorKind);
- #[cfg(windows)]
- {
- if !pkg_path.ends_with('\\') {
- pkg_path.push('\\');
+#[derive(Debug, Error)]
+pub enum ClosestPkgJsonErrorKind {
+ #[error("Failed canonicalizing package.json directory '{dir_path}'.")]
+ CanonicalizingDir {
+ dir_path: PathBuf,
+ #[source]
+ source: std::io::Error,
+ },
+ #[error(transparent)]
+ Load(#[from] deno_config::package_json::PackageJsonLoadError),
+}
+
+#[derive(Debug, Error)]
+#[error("TypeScript files are not supported in npm packages: {specifier}")]
+pub struct TypeScriptNotSupportedInNpmError {
+ pub specifier: ModuleSpecifier,
+}
+
+kinded_err!(UrlToNodeResolutionError, UrlToNodeResolutionErrorKind);
+
+#[derive(Debug, Error)]
+pub enum UrlToNodeResolutionErrorKind {
+ #[error(transparent)]
+ TypeScriptNotSupported(#[from] TypeScriptNotSupportedInNpmError),
+ #[error(transparent)]
+ ClosestPkgJson(#[from] ClosestPkgJsonError),
+}
+
+// todo(https://github.com/denoland/deno_core/issues/810): make this a TypeError
+#[derive(Debug, Error)]
+#[error(
+ "[ERR_PACKAGE_IMPORT_NOT_DEFINED] Package import specifier \"{}\" is not defined{}{}",
+ name,
+ package_json_path.as_ref().map(|p| format!(" in package {}", p.display())).unwrap_or_default(),
+ maybe_referrer.as_ref().map(|r| format!(" imported from '{}'", r)).unwrap_or_default(),
+)]
+pub struct PackageImportNotDefinedError {
+ pub name: String,
+ pub package_json_path: Option<PathBuf>,
+ pub maybe_referrer: Option<ModuleSpecifier>,
+}
+
+kinded_err!(PackageImportsResolveError, PackageImportsResolveErrorKind);
+
+#[derive(Debug, Error)]
+pub enum PackageImportsResolveErrorKind {
+ #[error(transparent)]
+ ClosestPkgJson(ClosestPkgJsonError),
+ #[error(transparent)]
+ InvalidModuleSpecifier(#[from] InvalidModuleSpecifierError),
+ #[error(transparent)]
+ NotDefined(#[from] PackageImportNotDefinedError),
+ #[error(transparent)]
+ Target(#[from] PackageTargetResolveError),
+}
+
+kinded_err!(PackageResolveError, PackageResolveErrorKind);
+
+#[derive(Debug, Error)]
+pub enum PackageResolveErrorKind {
+ #[error(transparent)]
+ ClosestPkgJson(#[from] ClosestPkgJsonError),
+ #[error(transparent)]
+ InvalidModuleSpecifier(#[from] InvalidModuleSpecifierError),
+ #[error(transparent)]
+ ExportsResolve(#[from] PackageExportsResolveError),
+ #[error(transparent)]
+ SubpathResolve(#[from] PackageSubpathResolveError),
+}
+
+#[derive(Debug, Error)]
+pub enum NodeResolveError {
+ #[error("Failed joining '{path}' from '{base}'.")]
+ RelativeJoinError {
+ path: String,
+ base: ModuleSpecifier,
+ #[source]
+ source: url::ParseError,
+ },
+ #[error(transparent)]
+ PackageImportsResolve(#[from] PackageImportsResolveError),
+ #[error(transparent)]
+ UnsupportedEsmUrlScheme(#[from] UnsupportedEsmUrlSchemeError),
+ #[error("Failed resolving specifier from data url referrer.")]
+ DataUrlReferrerFailed {
+ #[source]
+ source: url::ParseError,
+ },
+ #[error(transparent)]
+ PackageResolve(#[from] PackageResolveError),
+ #[error(transparent)]
+ PathToDeclarationUrl(#[from] PathToDeclarationUrlError),
+ #[error(transparent)]
+ UrlToNodeResolution(#[from] UrlToNodeResolutionError),
+ #[error(transparent)]
+ FinalizeResolution(#[from] FinalizeResolutionError),
+}
+
+kinded_err!(FinalizeResolutionError, FinalizeResolutionErrorKind);
+
+#[derive(Debug, Error)]
+pub enum FinalizeResolutionErrorKind {
+ #[error(transparent)]
+ InvalidModuleSpecifierError(#[from] InvalidModuleSpecifierError),
+ #[error(transparent)]
+ ModuleNotFound(#[from] ModuleNotFoundError),
+ #[error(transparent)]
+ UnsupportedDirImport(#[from] UnsupportedDirImportError),
+}
+
+#[derive(Debug, Error)]
+#[error(
+ "[ERR_MODULE_NOT_FOUND] Cannot find {} '{}'{}",
+ typ,
+ specifier,
+ 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 typ: &'static str,
+}
+
+#[derive(Debug, Error)]
+#[error(
+ "[ERR_UNSUPPORTED_DIR_IMPORT] Directory import '{}' is not supported resolving ES modules{}",
+ dir_url,
+ 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>,
+}
+
+#[derive(Debug)]
+pub struct InvalidPackageTargetError {
+ pub pkg_json_path: PathBuf,
+ pub sub_path: String,
+ pub target: String,
+ pub is_import: bool,
+ pub maybe_referrer: Option<ModuleSpecifier>,
+}
+
+impl std::error::Error for InvalidPackageTargetError {}
+
+impl std::fmt::Display for InvalidPackageTargetError {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ let rel_error = !self.is_import
+ && !self.target.is_empty()
+ && !self.target.starts_with("./");
+ f.write_str("[ERR_INVALID_PACKAGE_TARGET]")?;
+
+ if self.sub_path == "." {
+ assert!(!self.is_import);
+ write!(
+ f,
+ " Invalid \"exports\" main target {} defined in the package config {}",
+ self.target,
+ self.pkg_json_path.display()
+ )?;
+ } else {
+ let ie = if self.is_import { "imports" } else { "exports" };
+ write!(
+ f,
+ " Invalid \"{}\" target {} defined for '{}' in the package config {}",
+ ie,
+ self.target,
+ self.sub_path,
+ self.pkg_json_path.display()
+ )?;
+ };
+
+ if let Some(referrer) = &self.maybe_referrer {
+ write!(f, " imported from '{}'", referrer)?;
}
- }
- #[cfg(not(windows))]
- {
- if !pkg_path.ends_with('/') {
- pkg_path.push('/');
+ if rel_error {
+ write!(f, "; target must start with \"./\"")?;
}
+ Ok(())
}
+}
- let types_msg = match mode {
- NodeResolutionMode::Execution => String::new(),
- NodeResolutionMode::Types => " for types".to_string(),
- };
- if subpath == "." {
- msg =
- format!("{msg} No \"exports\" main defined{types_msg} in '{pkg_path}package.json'");
- } else {
- msg = format!("{msg} Package subpath '{subpath}' is not defined{types_msg} by \"exports\" in '{pkg_path}package.json'");
- };
+#[derive(Debug)]
+pub struct PackagePathNotExportedError {
+ pub pkg_json_path: PathBuf,
+ pub subpath: String,
+ pub maybe_referrer: Option<ModuleSpecifier>,
+ pub mode: NodeResolutionMode,
+}
- if let Some(referrer) = maybe_referrer {
- msg = format!("{msg} imported from '{referrer}'");
- }
+impl std::error::Error for PackagePathNotExportedError {}
- generic_error(msg)
-}
+impl std::fmt::Display for PackagePathNotExportedError {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.write_str("[ERR_PACKAGE_PATH_NOT_EXPORTED]")?;
-pub fn err_package_import_not_defined(
- specifier: &str,
- package_path: Option<String>,
- base: &str,
-) -> AnyError {
- let mut msg = format!(
- "[ERR_PACKAGE_IMPORT_NOT_DEFINED] Package import specifier \"{specifier}\" is not defined"
- );
+ let types_msg = match self.mode {
+ NodeResolutionMode::Execution => String::new(),
+ NodeResolutionMode::Types => " for types".to_string(),
+ };
+ if self.subpath == "." {
+ write!(
+ f,
+ " No \"exports\" main defined{} in '{}'",
+ types_msg,
+ self.pkg_json_path.display()
+ )?;
+ } else {
+ write!(
+ f,
+ " Package subpath '{}' is not defined{} by \"exports\" in '{}'",
+ self.subpath,
+ types_msg,
+ self.pkg_json_path.display()
+ )?;
+ };
- if let Some(package_path) = package_path {
- let pkg_json_path = PathBuf::from(package_path).join("package.json");
- msg = format!("{} in package {}", msg, pkg_json_path.display());
+ if let Some(referrer) = &self.maybe_referrer {
+ write!(f, " imported from '{}'", referrer)?;
+ }
+ Ok(())
}
+}
- msg = format!("{msg} imported from {base}");
+#[derive(Debug, Clone, Error)]
+#[error(
+ "[ERR_UNSUPPORTED_ESM_URL_SCHEME] Only file and data URLS are supported by the default ESM loader.{} Received protocol '{}'",
+ if cfg!(windows) && url_scheme.len() == 2 { " On Windows, absolute path must be valid file:// URLS."} else { "" },
+ url_scheme
+)]
+pub struct UnsupportedEsmUrlSchemeError {
+ pub url_scheme: String,
+}
- type_error(msg)
+#[derive(Debug, Error)]
+pub enum ResolvePkgJsonBinExportError {
+ #[error(transparent)]
+ PkgJsonLoad(#[from] deno_config::package_json::PackageJsonLoadError),
+ #[error("Failed resolving binary export. '{}' did not exist", pkg_json_path.display())]
+ MissingPkgJson { pkg_json_path: PathBuf },
+ #[error("Failed resolving binary export. {message}")]
+ InvalidBinProperty { message: String },
+ #[error(transparent)]
+ UrlToNodeResolution(#[from] UrlToNodeResolutionError),
}
-pub fn err_unsupported_dir_import(path: &str, base: &str) -> AnyError {
- generic_error(format!("[ERR_UNSUPPORTED_DIR_IMPORT] Directory import '{path}' is not supported resolving ES modules imported from {base}"))
+#[derive(Debug, Error)]
+pub enum ResolveBinaryCommandsError {
+ #[error(transparent)]
+ PkgJsonLoad(#[from] deno_config::package_json::PackageJsonLoadError),
+ #[error("'{}' did not have a name", pkg_json_path.display())]
+ MissingPkgJsonName { pkg_json_path: PathBuf },
}
-pub fn err_unsupported_esm_url_scheme(url: &Url) -> AnyError {
+#[allow(unused)]
+pub fn err_invalid_package_config(
+ path: &str,
+ maybe_base: Option<String>,
+ maybe_message: Option<String>,
+) -> AnyError {
let mut msg =
- "[ERR_UNSUPPORTED_ESM_URL_SCHEME] Only file and data URLS are supported by the default ESM loader"
- .to_string();
+ format!("[ERR_INVALID_PACKAGE_CONFIG] Invalid package config {path}");
- if cfg!(window) && url.scheme().len() == 2 {
- msg =
- format!("{msg}. On Windows, absolute path must be valid file:// URLs");
+ if let Some(base) = maybe_base {
+ msg = format!("{msg} while importing {base}");
+ }
+
+ if let Some(message) = maybe_message {
+ msg = format!("{msg}. {message}");
}
- msg = format!("{}. Received protocol '{}'", msg, url.scheme());
generic_error(msg)
}
@@ -176,23 +468,21 @@ mod test {
fn types_resolution_package_path_not_exported() {
let separator_char = if cfg!(windows) { '\\' } else { '/' };
assert_eq!(
- err_package_path_not_exported(
- "test_path".to_string(),
- "./jsx-runtime",
- None,
- NodeResolutionMode::Types,
- )
- .to_string(),
+ PackagePathNotExportedError {
+ pkg_json_path: PathBuf::from("test_path").join("package.json"),
+ subpath: "./jsx-runtime".to_string(),
+ maybe_referrer: None,
+ mode: NodeResolutionMode::Types
+ }.to_string(),
format!("[ERR_PACKAGE_PATH_NOT_EXPORTED] Package subpath './jsx-runtime' is not defined for types by \"exports\" in 'test_path{separator_char}package.json'")
);
assert_eq!(
- err_package_path_not_exported(
- "test_path".to_string(),
- ".",
- None,
- NodeResolutionMode::Types,
- )
- .to_string(),
+ PackagePathNotExportedError {
+ pkg_json_path: PathBuf::from("test_path").join("package.json"),
+ subpath: ".".to_string(),
+ maybe_referrer: None,
+ mode: NodeResolutionMode::Types
+ }.to_string(),
format!("[ERR_PACKAGE_PATH_NOT_EXPORTED] No \"exports\" main defined for types in 'test_path{separator_char}package.json'")
);
}
diff --git a/ext/node/lib.rs b/ext/node/lib.rs
index e86413346..13f9abc60 100644
--- a/ext/node/lib.rs
+++ b/ext/node/lib.rs
@@ -168,7 +168,7 @@ pub trait NpmResolver: std::fmt::Debug + MaybeSend + MaybeSync {
&self,
specifier: &str,
referrer: &ModuleSpecifier,
- ) -> Result<PathBuf, AnyError>;
+ ) -> Result<PathBuf, errors::PackageFolderResolveError>;
fn in_npm_package(&self, specifier: &ModuleSpecifier) -> bool;
diff --git a/ext/node/ops/require.rs b/ext/node/ops/require.rs
index 3702a9047..d03b3dd9c 100644
--- a/ext/node/ops/require.rs
+++ b/ext/node/ops/require.rs
@@ -421,7 +421,7 @@ where
&pkg.path,
&expansion,
exports,
- &referrer,
+ Some(&referrer),
NodeModuleKind::Cjs,
resolution::REQUIRE_CONDITIONS,
NodeResolutionMode::Execution,
@@ -509,7 +509,7 @@ where
&pkg.path,
&format!(".{expansion}"),
exports,
- &referrer,
+ Some(&referrer),
NodeModuleKind::Cjs,
resolution::REQUIRE_CONDITIONS,
NodeResolutionMode::Execution,
@@ -538,6 +538,7 @@ where
node_resolver
.get_closest_package_json(&Url::from_file_path(filename).unwrap())
.map(|maybe_pkg| maybe_pkg.map(|pkg| (*pkg).clone()))
+ .map_err(AnyError::from)
}
#[op2]
@@ -586,7 +587,7 @@ where
deno_core::url::Url::from_file_path(&referrer_filename).unwrap();
let url = node_resolver.package_imports_resolve(
&request,
- &referrer_url,
+ Some(&referrer_url),
NodeModuleKind::Cjs,
Some(&pkg),
resolution::REQUIRE_CONDITIONS,
diff --git a/ext/node/resolution.rs b/ext/node/resolution.rs
index 1b9a20012..5b61c1642 100644
--- a/ext/node/resolution.rs
+++ b/ext/node/resolution.rs
@@ -7,7 +7,6 @@ use std::path::PathBuf;
use deno_config::package_json::PackageJsonRc;
use deno_core::anyhow::bail;
-use deno_core::error::generic_error;
use deno_core::error::AnyError;
use deno_core::serde_json::Map;
use deno_core::serde_json::Value;
@@ -17,6 +16,33 @@ use deno_fs::FileSystemRc;
use deno_media_type::MediaType;
use crate::errors;
+use crate::errors::ClosestPkgJsonError;
+use crate::errors::ClosestPkgJsonErrorKind;
+use crate::errors::FinalizeResolutionError;
+use crate::errors::InvalidModuleSpecifierError;
+use crate::errors::InvalidPackageTargetError;
+use crate::errors::LegacyMainResolveError;
+use crate::errors::ModuleNotFoundError;
+use crate::errors::NodeResolveError;
+use crate::errors::PackageExportsResolveError;
+use crate::errors::PackageImportNotDefinedError;
+use crate::errors::PackageImportsResolveError;
+use crate::errors::PackageImportsResolveErrorKind;
+use crate::errors::PackagePathNotExportedError;
+use crate::errors::PackageResolveError;
+use crate::errors::PackageSubpathResolveError;
+use crate::errors::PackageSubpathResolveErrorKind;
+use crate::errors::PackageTargetNotFoundError;
+use crate::errors::PackageTargetResolveError;
+use crate::errors::PackageTargetResolveErrorKind;
+use crate::errors::PathToDeclarationUrlError;
+use crate::errors::ResolveBinaryCommandsError;
+use crate::errors::ResolvePkgJsonBinExportError;
+use crate::errors::ResolvePkgSubpathFromDenoModuleError;
+use crate::errors::TypeScriptNotSupportedInNpmError;
+use crate::errors::UnsupportedDirImportError;
+use crate::errors::UnsupportedEsmUrlSchemeError;
+use crate::errors::UrlToNodeResolutionError;
use crate::is_builtin_node_module;
use crate::path::to_file_specifier;
use crate::polyfill::get_module_name_from_builtin_node_module_specifier;
@@ -147,7 +173,7 @@ impl NodeResolver {
specifier: &str,
referrer: &ModuleSpecifier,
mode: NodeResolutionMode,
- ) -> Result<Option<NodeResolution>, AnyError> {
+ ) -> Result<Option<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
@@ -169,36 +195,29 @@ impl NodeResolver {
let protocol = url.scheme();
if protocol != "file" && protocol != "data" {
- return Err(errors::err_unsupported_esm_url_scheme(&url));
+ return Err(
+ UnsupportedEsmUrlSchemeError {
+ url_scheme: protocol.to_string(),
+ }
+ .into(),
+ );
}
// todo(dsherret): this seems wrong
if referrer.scheme() == "data" {
- let url = referrer.join(specifier).map_err(AnyError::from)?;
+ let url = referrer.join(specifier).map_err(|source| {
+ NodeResolveError::DataUrlReferrerFailed { source }
+ })?;
return Ok(Some(NodeResolution::Esm(url)));
}
}
- let url =
+ let maybe_url =
self.module_resolve(specifier, referrer, DEFAULT_CONDITIONS, mode)?;
- let url = match url {
+ let url = match maybe_url {
Some(url) => url,
None => return Ok(None),
};
- let url = match mode {
- NodeResolutionMode::Execution => url,
- NodeResolutionMode::Types => {
- let path = url.to_file_path().unwrap();
- // todo(16370): the module kind is not correct here. I think we need
- // typescript to tell us if the referrer is esm or cjs
- let maybe_decl_url =
- self.path_to_declaration_url(path, referrer, NodeModuleKind::Esm)?;
- match maybe_decl_url {
- Some(url) => url,
- None => return Ok(None),
- }
- }
- };
let resolve_response = self.url_to_node_resolution(url)?;
// TODO(bartlomieju): skipped checking errors for commonJS resolution and
@@ -212,27 +231,25 @@ impl NodeResolver {
referrer: &ModuleSpecifier,
conditions: &[&str],
mode: NodeResolutionMode,
- ) -> Result<Option<ModuleSpecifier>, AnyError> {
+ ) -> Result<Option<ModuleSpecifier>, NodeResolveError> {
// note: if we're here, the referrer is an esm module
- let url = if should_be_treated_as_relative_or_absolute_path(specifier) {
- let resolved_specifier = referrer.join(specifier)?;
- if mode.is_types() {
- let file_path = to_file_path(&resolved_specifier);
- // todo(dsherret): the node module kind is not correct and we
- // should use the value provided by typescript instead
- self.path_to_declaration_url(
- file_path,
- referrer,
- NodeModuleKind::Esm,
- )?
- } else {
- Some(resolved_specifier)
- }
+ let maybe_url = if should_be_treated_as_relative_or_absolute_path(specifier)
+ {
+ Some(referrer.join(specifier).map_err(|err| {
+ NodeResolveError::RelativeJoinError {
+ path: specifier.to_string(),
+ base: referrer.clone(),
+ source: err,
+ }
+ })?)
} else if specifier.starts_with('#') {
- let pkg_config = self.get_closest_package_json(referrer)?;
+ let pkg_config = self
+ .get_closest_package_json(referrer)
+ .map_err(PackageImportsResolveErrorKind::ClosestPkgJson)
+ .map_err(|err| PackageImportsResolveError(Box::new(err)))?;
Some(self.package_imports_resolve(
specifier,
- referrer,
+ Some(referrer),
NodeModuleKind::Esm,
pkg_config.as_deref(),
conditions,
@@ -249,8 +266,26 @@ impl NodeResolver {
mode,
)?
};
- Ok(match url {
- Some(url) => Some(self.finalize_resolution(url, referrer)?),
+
+ let Some(url) = maybe_url else {
+ return Ok(None);
+ };
+
+ let maybe_url = if mode.is_types() {
+ let file_path = to_file_path(&url);
+ // todo(16370): the referrer module kind is not correct here. I think we need
+ // typescript to tell us if the referrer is esm or cjs
+ self.path_to_declaration_url(
+ file_path,
+ Some(referrer),
+ NodeModuleKind::Esm,
+ )?
+ } else {
+ Some(url)
+ };
+
+ Ok(match maybe_url {
+ Some(url) => Some(self.finalize_resolution(url, Some(referrer))?),
None => None,
})
}
@@ -258,16 +293,21 @@ impl NodeResolver {
fn finalize_resolution(
&self,
resolved: ModuleSpecifier,
- base: &ModuleSpecifier,
- ) -> Result<ModuleSpecifier, AnyError> {
+ maybe_referrer: Option<&ModuleSpecifier>,
+ ) -> Result<ModuleSpecifier, FinalizeResolutionError> {
let encoded_sep_re = lazy_regex::regex!(r"%2F|%2C");
if encoded_sep_re.is_match(resolved.path()) {
- return Err(errors::err_invalid_module_specifier(
- resolved.path(),
- "must not include encoded \"/\" or \"\\\\\" characters",
- Some(to_file_path_string(base)),
- ));
+ return Err(
+ errors::InvalidModuleSpecifierError {
+ request: resolved.to_string(),
+ reason: Cow::Borrowed(
+ "must not include encoded \"/\" or \"\\\\\" characters",
+ ),
+ maybe_referrer: maybe_referrer.map(to_file_path_string),
+ }
+ .into(),
+ );
}
if resolved.scheme() == "node" {
@@ -295,16 +335,22 @@ impl NodeResolver {
(false, false)
};
if is_dir {
- return Err(errors::err_unsupported_dir_import(
- resolved.as_str(),
- base.as_str(),
- ));
+ return Err(
+ UnsupportedDirImportError {
+ dir_url: resolved.clone(),
+ maybe_referrer: maybe_referrer.map(ToOwned::to_owned),
+ }
+ .into(),
+ );
} else if !is_file {
- return Err(errors::err_module_not_found(
- resolved.as_str(),
- base.as_str(),
- "module",
- ));
+ return Err(
+ ModuleNotFoundError {
+ specifier: resolved,
+ maybe_referrer: maybe_referrer.map(ToOwned::to_owned),
+ typ: "module",
+ }
+ .into(),
+ );
}
Ok(resolved)
@@ -314,9 +360,9 @@ impl NodeResolver {
&self,
package_dir: &Path,
package_subpath: Option<&str>,
- referrer: &ModuleSpecifier,
+ maybe_referrer: Option<&ModuleSpecifier>,
mode: NodeResolutionMode,
- ) -> Result<Option<NodeResolution>, AnyError> {
+ ) -> Result<Option<NodeResolution>, ResolvePkgSubpathFromDenoModuleError> {
let node_module_kind = NodeModuleKind::Esm;
let package_subpath = package_subpath
.map(|s| format!("./{s}"))
@@ -324,33 +370,15 @@ impl NodeResolver {
let maybe_resolved_url = self.resolve_package_dir_subpath(
package_dir,
&package_subpath,
- referrer,
+ maybe_referrer,
node_module_kind,
DEFAULT_CONDITIONS,
mode,
)?;
let resolved_url = match maybe_resolved_url {
- Some(resolved_path) => resolved_path,
+ Some(resolved_url) => resolved_url,
None => return Ok(None),
};
- let resolved_url = match mode {
- NodeResolutionMode::Execution => resolved_url,
- NodeResolutionMode::Types => {
- if resolved_url.scheme() == "file" {
- let path = resolved_url.to_file_path().unwrap();
- match self.path_to_declaration_url(
- path,
- referrer,
- node_module_kind,
- )? {
- Some(url) => url,
- None => return Ok(None),
- }
- } else {
- resolved_url
- }
- }
- };
let resolve_response = self.url_to_node_resolution(resolved_url)?;
// TODO(bartlomieju): skipped checking errors for commonJS resolution and
// "preserveSymlinksMain"/"preserveSymlinks" options.
@@ -360,16 +388,18 @@ impl NodeResolver {
pub fn resolve_binary_commands(
&self,
package_folder: &Path,
- ) -> Result<Vec<String>, AnyError> {
- let package_json_path = package_folder.join("package.json");
- let Some(package_json) = self.load_package_json(&package_json_path)? else {
+ ) -> Result<Vec<String>, ResolveBinaryCommandsError> {
+ let pkg_json_path = package_folder.join("package.json");
+ let Some(package_json) = self.load_package_json(&pkg_json_path)? else {
return Ok(Vec::new());
};
Ok(match &package_json.bin {
Some(Value::String(_)) => {
let Some(name) = &package_json.name else {
- bail!("'{}' did not have a name", package_json_path.display());
+ return Err(ResolveBinaryCommandsError::MissingPkgJsonName {
+ pkg_json_path,
+ });
};
vec![name.to_string()]
}
@@ -384,15 +414,19 @@ impl NodeResolver {
&self,
package_folder: &Path,
sub_path: Option<&str>,
- ) -> Result<NodeResolution, AnyError> {
- let package_json_path = package_folder.join("package.json");
- let Some(package_json) = self.load_package_json(&package_json_path)? else {
- bail!(
- "Failed resolving binary export. '{}' did not exist",
- package_json_path.display(),
- )
+ ) -> Result<NodeResolution, ResolvePkgJsonBinExportError> {
+ let pkg_json_path = package_folder.join("package.json");
+ let Some(package_json) = self.load_package_json(&pkg_json_path)? else {
+ return Err(ResolvePkgJsonBinExportError::MissingPkgJson {
+ pkg_json_path,
+ });
};
- let bin_entry = resolve_bin_entry_value(&package_json, sub_path)?;
+ let bin_entry =
+ resolve_bin_entry_value(&package_json, sub_path).map_err(|err| {
+ ResolvePkgJsonBinExportError::InvalidBinProperty {
+ message: err.to_string(),
+ }
+ })?;
let url = to_file_specifier(&package_folder.join(bin_entry));
let resolve_response = self.url_to_node_resolution(url)?;
@@ -404,7 +438,7 @@ impl NodeResolver {
pub fn url_to_node_resolution(
&self,
url: ModuleSpecifier,
- ) -> Result<NodeResolution, AnyError> {
+ ) -> Result<NodeResolution, UrlToNodeResolutionError> {
let url_str = url.as_str().to_lowercase();
if url_str.starts_with("http") || url_str.ends_with(".json") {
Ok(NodeResolution::Esm(url))
@@ -419,9 +453,7 @@ impl NodeResolver {
Ok(NodeResolution::Esm(url))
} else if url_str.ends_with(".ts") || url_str.ends_with(".mts") {
if self.in_npm_package(&url) {
- Err(generic_error(format!(
- "TypeScript files are not supported in npm packages: {url}"
- )))
+ Err(TypeScriptNotSupportedInNpmError { specifier: url }.into())
} else {
Ok(NodeResolution::Esm(url))
}
@@ -434,9 +466,9 @@ impl NodeResolver {
fn path_to_declaration_url(
&self,
path: PathBuf,
- referrer: &ModuleSpecifier,
+ maybe_referrer: Option<&ModuleSpecifier>,
referrer_kind: NodeModuleKind,
- ) -> Result<Option<ModuleSpecifier>, AnyError> {
+ ) -> Result<Option<ModuleSpecifier>, PathToDeclarationUrlError> {
fn probe_extensions(
fs: &dyn deno_fs::FileSystem,
path: &Path,
@@ -497,7 +529,7 @@ impl NodeResolver {
let maybe_resolution = self.resolve_package_dir_subpath(
&path,
/* sub path */ ".",
- referrer,
+ maybe_referrer,
referrer_kind,
match referrer_kind {
NodeModuleKind::Esm => DEFAULT_CONDITIONS,
@@ -529,19 +561,22 @@ impl NodeResolver {
pub(super) fn package_imports_resolve(
&self,
name: &str,
- referrer: &ModuleSpecifier,
+ maybe_referrer: Option<&ModuleSpecifier>,
referrer_kind: NodeModuleKind,
referrer_pkg_json: Option<&PackageJson>,
conditions: &[&str],
mode: NodeResolutionMode,
- ) -> Result<ModuleSpecifier, AnyError> {
+ ) -> Result<ModuleSpecifier, PackageImportsResolveError> {
if name == "#" || name.starts_with("#/") || name.ends_with('/') {
let reason = "is not a valid internal imports specifier name";
- return Err(errors::err_invalid_module_specifier(
- name,
- reason,
- Some(to_specifier_display_string(referrer)),
- ));
+ return Err(
+ errors::InvalidModuleSpecifierError {
+ request: name.to_string(),
+ reason: Cow::Borrowed(reason),
+ maybe_referrer: maybe_referrer.map(to_specifier_display_string),
+ }
+ .into(),
+ );
}
let mut package_json_path = None;
@@ -555,7 +590,7 @@ impl NodeResolver {
target,
"",
name,
- referrer,
+ maybe_referrer,
referrer_kind,
false,
true,
@@ -596,7 +631,7 @@ impl NodeResolver {
target,
&best_match_subpath.unwrap(),
best_match,
- referrer,
+ maybe_referrer,
referrer_kind,
true,
true,
@@ -611,11 +646,14 @@ impl NodeResolver {
}
}
- Err(throw_import_not_defined(
- name,
- package_json_path.as_deref(),
- referrer,
- ))
+ Err(
+ PackageImportNotDefinedError {
+ name: name.to_string(),
+ package_json_path,
+ maybe_referrer: maybe_referrer.map(ToOwned::to_owned),
+ }
+ .into(),
+ )
}
#[allow(clippy::too_many_arguments)]
@@ -625,21 +663,24 @@ impl NodeResolver {
subpath: &str,
match_: &str,
package_json_path: &Path,
- referrer: &ModuleSpecifier,
+ maybe_referrer: Option<&ModuleSpecifier>,
referrer_kind: NodeModuleKind,
pattern: bool,
internal: bool,
conditions: &[&str],
mode: NodeResolutionMode,
- ) -> Result<ModuleSpecifier, AnyError> {
+ ) -> Result<ModuleSpecifier, PackageTargetResolveError> {
if !subpath.is_empty() && !pattern && !target.ends_with('/') {
- return Err(throw_invalid_package_target(
- match_,
- target,
- package_json_path,
- internal,
- referrer,
- ));
+ return Err(
+ InvalidPackageTargetError {
+ pkg_json_path: package_json_path.to_path_buf(),
+ sub_path: match_.to_string(),
+ target: target.to_string(),
+ is_import: internal,
+ maybe_referrer: maybe_referrer.map(ToOwned::to_owned),
+ }
+ .into(),
+ );
}
let invalid_segment_re =
lazy_regex::regex!(r"(^|\\|/)(\.\.?|node_modules)(\\|/|$)");
@@ -672,8 +713,21 @@ impl NodeResolver {
mode,
) {
Ok(Some(url)) => Ok(url),
- Ok(None) => Err(generic_error("not found")),
- Err(err) => Err(err),
+ Ok(None) => Err(
+ PackageTargetResolveErrorKind::NotFound(
+ PackageTargetNotFoundError {
+ pkg_json_path: package_json_path.to_path_buf(),
+ target: export_target.to_string(),
+ maybe_referrer: maybe_referrer.map(ToOwned::to_owned),
+ referrer_kind,
+ mode,
+ },
+ )
+ .into(),
+ ),
+ Err(err) => {
+ Err(PackageTargetResolveErrorKind::PackageResolve(err).into())
+ }
};
return match result {
@@ -692,33 +746,42 @@ impl NodeResolver {
}
}
}
- return Err(throw_invalid_package_target(
- match_,
- target,
- package_json_path,
- internal,
- referrer,
- ));
+ return Err(
+ InvalidPackageTargetError {
+ pkg_json_path: package_json_path.to_path_buf(),
+ sub_path: match_.to_string(),
+ target: target.to_string(),
+ is_import: internal,
+ maybe_referrer: maybe_referrer.map(ToOwned::to_owned),
+ }
+ .into(),
+ );
}
if invalid_segment_re.is_match(&target[2..]) {
- return Err(throw_invalid_package_target(
- match_,
- target,
- package_json_path,
- internal,
- referrer,
- ));
+ return Err(
+ InvalidPackageTargetError {
+ pkg_json_path: package_json_path.to_path_buf(),
+ sub_path: match_.to_string(),
+ target: target.to_string(),
+ is_import: internal,
+ maybe_referrer: maybe_referrer.map(ToOwned::to_owned),
+ }
+ .into(),
+ );
}
let package_path = package_json_path.parent().unwrap();
let resolved_path = package_path.join(target).clean();
if !resolved_path.starts_with(package_path) {
- return Err(throw_invalid_package_target(
- match_,
- target,
- package_json_path,
- internal,
- referrer,
- ));
+ return Err(
+ InvalidPackageTargetError {
+ pkg_json_path: package_json_path.to_path_buf(),
+ sub_path: match_.to_string(),
+ target: target.to_string(),
+ is_import: internal,
+ maybe_referrer: maybe_referrer.map(ToOwned::to_owned),
+ }
+ .into(),
+ );
}
if subpath.is_empty() {
return Ok(to_file_specifier(&resolved_path));
@@ -729,12 +792,15 @@ impl NodeResolver {
} else {
format!("{match_}{subpath}")
};
- return Err(throw_invalid_subpath(
- request,
- package_json_path,
- internal,
- referrer,
- ));
+ return Err(
+ throw_invalid_subpath(
+ request,
+ package_json_path,
+ internal,
+ maybe_referrer,
+ )
+ .into(),
+ );
}
if pattern {
let resolved_path_str = resolved_path.to_string_lossy();
@@ -752,20 +818,20 @@ impl NodeResolver {
target: &Value,
subpath: &str,
package_subpath: &str,
- referrer: &ModuleSpecifier,
+ maybe_referrer: Option<&ModuleSpecifier>,
referrer_kind: NodeModuleKind,
pattern: bool,
internal: bool,
conditions: &[&str],
mode: NodeResolutionMode,
- ) -> Result<Option<ModuleSpecifier>, AnyError> {
+ ) -> Result<Option<ModuleSpecifier>, PackageTargetResolveError> {
if let Some(target) = target.as_str() {
let url = self.resolve_package_target_string(
target,
subpath,
package_subpath,
package_json_path,
- referrer,
+ maybe_referrer,
referrer_kind,
pattern,
internal,
@@ -774,7 +840,11 @@ impl NodeResolver {
)?;
if mode.is_types() && url.scheme() == "file" {
let path = url.to_file_path().unwrap();
- return self.path_to_declaration_url(path, referrer, referrer_kind);
+ return Ok(self.path_to_declaration_url(
+ path,
+ maybe_referrer,
+ referrer_kind,
+ )?);
} else {
return Ok(Some(url));
}
@@ -790,7 +860,7 @@ impl NodeResolver {
target_item,
subpath,
package_subpath,
- referrer,
+ maybe_referrer,
referrer_kind,
pattern,
internal,
@@ -804,14 +874,15 @@ impl NodeResolver {
last_error = None;
continue;
}
- Err(e) => {
- let err_string = e.to_string();
- last_error = Some(e);
- if err_string.starts_with("[ERR_INVALID_PACKAGE_TARGET]") {
+ Err(e) => match e.as_kind() {
+ PackageTargetResolveErrorKind::InvalidPackageTarget(_) => {
+ last_error = Some(e);
continue;
}
- return Err(last_error.unwrap());
- }
+ _ => {
+ return Err(e);
+ }
+ },
}
}
if last_error.is_none() {
@@ -838,7 +909,7 @@ impl NodeResolver {
condition_target,
subpath,
package_subpath,
- referrer,
+ maybe_referrer,
referrer_kind,
pattern,
internal,
@@ -857,13 +928,16 @@ impl NodeResolver {
return Ok(None);
}
- Err(throw_invalid_package_target(
- package_subpath,
- &target.to_string(),
- package_json_path,
- internal,
- referrer,
- ))
+ Err(
+ InvalidPackageTargetError {
+ pkg_json_path: package_json_path.to_path_buf(),
+ sub_path: package_subpath.to_string(),
+ target: target.to_string(),
+ is_import: internal,
+ maybe_referrer: maybe_referrer.map(ToOwned::to_owned),
+ }
+ .into(),
+ )
}
#[allow(clippy::too_many_arguments)]
@@ -872,11 +946,11 @@ impl NodeResolver {
package_json_path: &Path,
package_subpath: &str,
package_exports: &Map<String, Value>,
- referrer: &ModuleSpecifier,
+ maybe_referrer: Option<&ModuleSpecifier>,
referrer_kind: NodeModuleKind,
conditions: &[&str],
mode: NodeResolutionMode,
- ) -> Result<ModuleSpecifier, AnyError> {
+ ) -> Result<ModuleSpecifier, PackageExportsResolveError> {
if package_exports.contains_key(package_subpath)
&& package_subpath.find('*').is_none()
&& !package_subpath.ends_with('/')
@@ -887,7 +961,7 @@ impl NodeResolver {
target,
"",
package_subpath,
- referrer,
+ maybe_referrer,
referrer_kind,
false,
false,
@@ -896,12 +970,15 @@ impl NodeResolver {
)?;
return match resolved {
Some(resolved) => Ok(resolved),
- None => Err(throw_exports_not_found(
- package_subpath,
- package_json_path,
- referrer,
- mode,
- )),
+ None => Err(
+ PackagePathNotExportedError {
+ pkg_json_path: package_json_path.to_path_buf(),
+ subpath: package_subpath.to_string(),
+ maybe_referrer: maybe_referrer.map(ToOwned::to_owned),
+ mode,
+ }
+ .into(),
+ ),
};
}
@@ -946,7 +1023,7 @@ impl NodeResolver {
target,
&best_match_subpath.unwrap(),
best_match,
- referrer,
+ maybe_referrer,
referrer_kind,
true,
false,
@@ -956,21 +1033,27 @@ impl NodeResolver {
if let Some(resolved) = maybe_resolved {
return Ok(resolved);
} else {
- return Err(throw_exports_not_found(
- package_subpath,
- package_json_path,
- referrer,
- mode,
- ));
+ return Err(
+ PackagePathNotExportedError {
+ pkg_json_path: package_json_path.to_path_buf(),
+ subpath: package_subpath.to_string(),
+ maybe_referrer: maybe_referrer.map(ToOwned::to_owned),
+ mode,
+ }
+ .into(),
+ );
}
}
- Err(throw_exports_not_found(
- package_subpath,
- package_json_path,
- referrer,
- mode,
- ))
+ Err(
+ PackagePathNotExportedError {
+ pkg_json_path: package_json_path.to_path_buf(),
+ subpath: package_subpath.to_string(),
+ maybe_referrer: maybe_referrer.map(ToOwned::to_owned),
+ mode,
+ }
+ .into(),
+ )
}
pub(super) fn package_resolve(
@@ -980,7 +1063,7 @@ impl NodeResolver {
referrer_kind: NodeModuleKind,
conditions: &[&str],
mode: NodeResolutionMode,
- ) -> Result<Option<ModuleSpecifier>, AnyError> {
+ ) -> Result<Option<ModuleSpecifier>, PackageResolveError> {
let (package_name, package_subpath, _is_scoped) =
parse_npm_pkg_name(specifier, referrer)?;
@@ -995,23 +1078,26 @@ impl NodeResolver {
&package_config.path,
&package_subpath,
exports,
- referrer,
+ Some(referrer),
referrer_kind,
conditions,
mode,
)
- .map(Some);
+ .map(Some)
+ .map_err(|err| err.into());
}
}
- self.resolve_package_subpath_for_package(
- &package_name,
- &package_subpath,
- referrer,
- referrer_kind,
- conditions,
- mode,
- )
+ self
+ .resolve_package_subpath_for_package(
+ &package_name,
+ &package_subpath,
+ referrer,
+ referrer_kind,
+ conditions,
+ mode,
+ )
+ .map_err(|err| err.into())
}
#[allow(clippy::too_many_arguments)]
@@ -1023,7 +1109,7 @@ impl NodeResolver {
referrer_kind: NodeModuleKind,
conditions: &[&str],
mode: NodeResolutionMode,
- ) -> Result<Option<ModuleSpecifier>, AnyError> {
+ ) -> Result<Option<ModuleSpecifier>, PackageSubpathResolveError> {
let result = self.resolve_package_subpath_for_package_inner(
package_name,
package_subpath,
@@ -1058,7 +1144,7 @@ impl NodeResolver {
referrer_kind: NodeModuleKind,
conditions: &[&str],
mode: NodeResolutionMode,
- ) -> Result<Option<ModuleSpecifier>, AnyError> {
+ ) -> Result<Option<ModuleSpecifier>, PackageSubpathResolveError> {
let package_dir_path = self
.npm_resolver
.resolve_package_folder_from_package(package_name, referrer)?;
@@ -1080,7 +1166,7 @@ impl NodeResolver {
self.resolve_package_dir_subpath(
&package_dir_path,
package_subpath,
- referrer,
+ Some(referrer),
referrer_kind,
conditions,
mode,
@@ -1092,28 +1178,30 @@ impl NodeResolver {
&self,
package_dir_path: &Path,
package_subpath: &str,
- referrer: &ModuleSpecifier,
+ maybe_referrer: Option<&ModuleSpecifier>,
referrer_kind: NodeModuleKind,
conditions: &[&str],
mode: NodeResolutionMode,
- ) -> Result<Option<ModuleSpecifier>, AnyError> {
+ ) -> Result<Option<ModuleSpecifier>, 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(
&pkg_json,
package_subpath,
- referrer,
+ maybe_referrer,
referrer_kind,
conditions,
mode,
),
- None => self.resolve_package_subpath_no_pkg_json(
- package_dir_path,
- package_subpath,
- referrer,
- referrer_kind,
- mode,
- ),
+ None => self
+ .resolve_package_subpath_no_pkg_json(
+ package_dir_path,
+ package_subpath,
+ maybe_referrer,
+ referrer_kind,
+ mode,
+ )
+ .map_err(|err| PackageSubpathResolveErrorKind::LegacyExact(err).into()),
}
}
@@ -1122,11 +1210,11 @@ impl NodeResolver {
&self,
package_json: &PackageJson,
package_subpath: &str,
- referrer: &ModuleSpecifier,
+ referrer: Option<&ModuleSpecifier>,
referrer_kind: NodeModuleKind,
conditions: &[&str],
mode: NodeResolutionMode,
- ) -> Result<Option<ModuleSpecifier>, AnyError> {
+ ) -> Result<Option<ModuleSpecifier>, PackageSubpathResolveError> {
if let Some(exports) = &package_json.exports {
let result = self.package_exports_resolve(
&package_json.path,
@@ -1141,48 +1229,48 @@ impl NodeResolver {
Ok(found) => return Ok(Some(found)),
Err(exports_err) => {
if mode.is_types() && package_subpath == "." {
- return self.legacy_main_resolve(
- package_json,
- referrer,
- referrer_kind,
- mode,
- );
+ return self
+ .legacy_main_resolve(package_json, referrer, referrer_kind, mode)
+ .map_err(|err| {
+ PackageSubpathResolveErrorKind::LegacyMain(err).into()
+ });
}
- return Err(exports_err);
+ return Err(
+ PackageSubpathResolveErrorKind::Exports(exports_err).into(),
+ );
}
}
}
if package_subpath == "." {
- return self.legacy_main_resolve(
- package_json,
+ return self
+ .legacy_main_resolve(package_json, referrer, referrer_kind, mode)
+ .map_err(|err| PackageSubpathResolveErrorKind::LegacyMain(err).into());
+ }
+
+ self
+ .resolve_subpath_exact(
+ package_json.path.parent().unwrap(),
+ package_subpath,
referrer,
referrer_kind,
mode,
- );
- }
-
- self.resolve_subpath_exact(
- package_json.path.parent().unwrap(),
- package_subpath,
- referrer,
- referrer_kind,
- mode,
- )
+ )
+ .map_err(|err| PackageSubpathResolveErrorKind::LegacyExact(err).into())
}
fn resolve_subpath_exact(
&self,
directory: &Path,
package_subpath: &str,
- referrer: &ModuleSpecifier,
+ referrer: Option<&ModuleSpecifier>,
referrer_kind: NodeModuleKind,
mode: NodeResolutionMode,
- ) -> Result<Option<ModuleSpecifier>, AnyError> {
+ ) -> Result<Option<ModuleSpecifier>, PathToDeclarationUrlError> {
assert_ne!(package_subpath, ".");
let file_path = directory.join(package_subpath);
if mode.is_types() {
- self.path_to_declaration_url(file_path, referrer, referrer_kind)
+ Ok(self.path_to_declaration_url(file_path, referrer, referrer_kind)?)
} else {
Ok(Some(to_file_specifier(&file_path)))
}
@@ -1192,12 +1280,12 @@ impl NodeResolver {
&self,
directory: &Path,
package_subpath: &str,
- referrer: &ModuleSpecifier,
+ referrer: Option<&ModuleSpecifier>,
referrer_kind: NodeModuleKind,
mode: NodeResolutionMode,
- ) -> Result<Option<ModuleSpecifier>, AnyError> {
+ ) -> Result<Option<ModuleSpecifier>, PathToDeclarationUrlError> {
if package_subpath == "." {
- self.legacy_index_resolve(directory, referrer_kind, mode)
+ Ok(self.legacy_index_resolve(directory, referrer_kind, mode))
} else {
self.resolve_subpath_exact(
directory,
@@ -1212,7 +1300,7 @@ impl NodeResolver {
pub fn get_closest_package_json(
&self,
url: &ModuleSpecifier,
- ) -> Result<Option<PackageJsonRc>, AnyError> {
+ ) -> Result<Option<PackageJsonRc>, ClosestPkgJsonError> {
let Ok(file_path) = url.to_file_path() else {
return Ok(None);
};
@@ -1222,10 +1310,15 @@ impl NodeResolver {
pub fn get_closest_package_json_from_path(
&self,
file_path: &Path,
- ) -> Result<Option<PackageJsonRc>, AnyError> {
- let current_dir = deno_core::strip_unc_prefix(
- self.fs.realpath_sync(file_path.parent().unwrap())?,
- );
+ ) -> 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(
+ |source| ClosestPkgJsonErrorKind::CanonicalizingDir {
+ dir_path: parent_dir.to_path_buf(),
+ source: source.into_io_error(),
+ },
+ )?);
let mut current_dir = current_dir.as_path();
let package_json_path = current_dir.join("package.json");
if let Some(pkg_json) = self.load_package_json(&package_json_path)? {
@@ -1255,10 +1348,10 @@ impl NodeResolver {
pub(super) fn legacy_main_resolve(
&self,
package_json: &PackageJson,
- referrer: &ModuleSpecifier,
+ maybe_referrer: Option<&ModuleSpecifier>,
referrer_kind: NodeModuleKind,
mode: NodeResolutionMode,
- ) -> Result<Option<ModuleSpecifier>, AnyError> {
+ ) -> Result<Option<ModuleSpecifier>, LegacyMainResolveError> {
let maybe_main = if mode.is_types() {
match package_json.types.as_ref() {
Some(types) => Some(types.as_str()),
@@ -1267,8 +1360,9 @@ impl NodeResolver {
// a corresponding declaration file
if let Some(main) = package_json.main(referrer_kind) {
let main = package_json.path.parent().unwrap().join(main).clean();
- let maybe_decl_url =
- self.path_to_declaration_url(main, referrer, referrer_kind)?;
+ let maybe_decl_url = self
+ .path_to_declaration_url(main, maybe_referrer, referrer_kind)
+ .map_err(LegacyMainResolveError::PathToDeclarationUrl)?;
if let Some(path) = maybe_decl_url {
return Ok(Some(path));
}
@@ -1318,11 +1412,11 @@ impl NodeResolver {
}
}
- self.legacy_index_resolve(
+ Ok(self.legacy_index_resolve(
package_json.path.parent().unwrap(),
referrer_kind,
mode,
- )
+ ))
}
fn legacy_index_resolve(
@@ -1330,7 +1424,7 @@ impl NodeResolver {
directory: &Path,
referrer_kind: NodeModuleKind,
mode: NodeResolutionMode,
- ) -> Result<Option<ModuleSpecifier>, AnyError> {
+ ) -> Option<ModuleSpecifier> {
let index_file_names = if mode.is_types() {
// todo(dsherret): investigate exactly how typescript does this
match referrer_kind {
@@ -1344,11 +1438,11 @@ impl NodeResolver {
let guess = directory.join(index_file_name).clean();
if self.fs.is_file_sync(&guess) {
// TODO(bartlomieju): emitLegacyIndexDeprecation()
- return Ok(Some(to_file_specifier(&guess)));
+ return Some(to_file_specifier(&guess));
}
}
- Ok(None)
+ None
}
}
@@ -1524,71 +1618,29 @@ fn to_specifier_display_string(url: &ModuleSpecifier) -> String {
}
}
-fn throw_import_not_defined(
- specifier: &str,
- package_json_path: Option<&Path>,
- base: &ModuleSpecifier,
-) -> AnyError {
- errors::err_package_import_not_defined(
- specifier,
- package_json_path.map(|p| p.parent().unwrap().display().to_string()),
- &to_specifier_display_string(base),
- )
-}
-
-fn throw_invalid_package_target(
- subpath: &str,
- target: &str,
- package_json_path: &Path,
- internal: bool,
- referrer: &ModuleSpecifier,
-) -> AnyError {
- errors::err_invalid_package_target(
- &package_json_path.parent().unwrap().display().to_string(),
- subpath,
- target,
- internal,
- Some(referrer.to_string()),
- )
-}
-
fn throw_invalid_subpath(
subpath: String,
package_json_path: &Path,
internal: bool,
- referrer: &ModuleSpecifier,
-) -> AnyError {
+ maybe_referrer: Option<&ModuleSpecifier>,
+) -> InvalidModuleSpecifierError {
let ie = if internal { "imports" } else { "exports" };
let reason = format!(
"request is not a valid subpath for the \"{}\" resolution of {}",
ie,
package_json_path.display(),
);
- errors::err_invalid_module_specifier(
- &subpath,
- &reason,
- Some(to_specifier_display_string(referrer)),
- )
-}
-
-fn throw_exports_not_found(
- subpath: &str,
- package_json_path: &Path,
- referrer: &ModuleSpecifier,
- mode: NodeResolutionMode,
-) -> AnyError {
- errors::err_package_path_not_exported(
- package_json_path.parent().unwrap().display().to_string(),
- subpath,
- Some(to_specifier_display_string(referrer)),
- mode,
- )
+ InvalidModuleSpecifierError {
+ request: subpath,
+ reason: Cow::Owned(reason),
+ maybe_referrer: maybe_referrer.map(to_specifier_display_string),
+ }
}
pub fn parse_npm_pkg_name(
specifier: &str,
referrer: &ModuleSpecifier,
-) -> Result<(String, String, bool), AnyError> {
+) -> Result<(String, String, bool), InvalidModuleSpecifierError> {
let mut separator_index = specifier.find('/');
let mut valid_package_name = true;
let mut is_scoped = false;
@@ -1620,11 +1672,11 @@ pub fn parse_npm_pkg_name(
}
if !valid_package_name {
- return Err(errors::err_invalid_module_specifier(
- specifier,
- "is not a valid package name",
- Some(to_specifier_display_string(referrer)),
- ));
+ return Err(errors::InvalidModuleSpecifierError {
+ request: specifier.to_string(),
+ reason: Cow::Borrowed("is not a valid package name"),
+ maybe_referrer: Some(to_specifier_display_string(referrer)),
+ });
}
let package_subpath = if let Some(index) = separator_index {