summaryrefslogtreecommitdiff
path: root/ext/node/errors.rs
diff options
context:
space:
mode:
Diffstat (limited to 'ext/node/errors.rs')
-rw-r--r--ext/node/errors.rs562
1 files changed, 426 insertions, 136 deletions
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'")
);
}