summaryrefslogtreecommitdiff
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
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).
-rw-r--r--Cargo.lock1
-rw-r--r--cli/lsp/resolver.rs4
-rw-r--r--cli/module_loader.rs2
-rw-r--r--cli/npm/byonm.rs60
-rw-r--r--cli/npm/managed/mod.rs11
-rw-r--r--cli/npm/managed/resolvers/common.rs14
-rw-r--r--cli/npm/managed/resolvers/global.rs65
-rw-r--r--cli/npm/managed/resolvers/local.rs90
-rw-r--r--cli/resolver.rs38
-rw-r--r--cli/standalone/mod.rs2
-rw-r--r--cli/tsc/mod.rs4
-rw-r--r--cli/worker.rs7
-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
-rw-r--r--tests/testdata/npm/deno_run_no_bin_entrypoint.out2
-rw-r--r--tests/testdata/npm/deno_run_no_bin_entrypoint_non_existent_subpath.out2
-rw-r--r--tests/testdata/npm/imports_package_json/import_not_defined.out2
-rw-r--r--tests/testdata/npm/imports_package_json/sub_path_import_not_defined.out2
23 files changed, 1007 insertions, 585 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 43e2fd0c6..d7d8c7496 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1771,6 +1771,7 @@ dependencies = [
"simd-json",
"sm3",
"spki",
+ "thiserror",
"tokio",
"url",
"winapi",
diff --git a/cli/lsp/resolver.rs b/cli/lsp/resolver.rs
index 18d22afad..29f986ce3 100644
--- a/cli/lsp/resolver.rs
+++ b/cli/lsp/resolver.rs
@@ -28,13 +28,13 @@ use deno_ast::MediaType;
use deno_cache_dir::HttpCache;
use deno_config::workspace::PackageJsonDepResolution;
use deno_config::workspace::WorkspaceResolver;
-use deno_core::error::AnyError;
use deno_core::url::Url;
use deno_graph::source::Resolver;
use deno_graph::GraphImport;
use deno_graph::ModuleSpecifier;
use deno_npm::NpmSystemInfo;
use deno_runtime::deno_fs;
+use deno_runtime::deno_node::errors::ClosestPkgJsonError;
use deno_runtime::deno_node::NodeResolution;
use deno_runtime::deno_node::NodeResolutionMode;
use deno_runtime::deno_node::NodeResolver;
@@ -365,7 +365,7 @@ impl LspResolver {
pub fn get_closest_package_json(
&self,
referrer: &ModuleSpecifier,
- ) -> Result<Option<Arc<PackageJson>>, AnyError> {
+ ) -> Result<Option<Arc<PackageJson>>, ClosestPkgJsonError> {
let resolver = self.get_scope_resolver(Some(referrer));
let Some(node_resolver) = resolver.node_resolver.as_ref() else {
return Ok(None);
diff --git a/cli/module_loader.rs b/cli/module_loader.rs
index 4786b742f..5156e98e3 100644
--- a/cli/module_loader.rs
+++ b/cli/module_loader.rs
@@ -517,7 +517,7 @@ impl<TGraphContainer: ModuleGraphContainer>
.resolve_package_sub_path_from_deno_module(
&package_folder,
module.nv_reference.sub_path(),
- referrer,
+ Some(referrer),
NodeResolutionMode::Execution,
)
.with_context(|| {
diff --git a/cli/npm/byonm.rs b/cli/npm/byonm.rs
index bbd5da8ec..4abb6b3b2 100644
--- a/cli/npm/byonm.rs
+++ b/cli/npm/byonm.rs
@@ -11,6 +11,8 @@ use deno_core::anyhow::bail;
use deno_core::error::AnyError;
use deno_core::serde_json;
use deno_runtime::deno_fs::FileSystem;
+use deno_runtime::deno_node::errors::PackageFolderResolveError;
+use deno_runtime::deno_node::errors::PackageFolderResolveErrorKind;
use deno_runtime::deno_node::load_pkg_json;
use deno_runtime::deno_node::NodePermissions;
use deno_runtime::deno_node::NpmResolver;
@@ -168,42 +170,50 @@ impl NpmResolver for ByonmCliNpmResolver {
&self,
name: &str,
referrer: &ModuleSpecifier,
- ) -> Result<PathBuf, AnyError> {
+ ) -> Result<PathBuf, PackageFolderResolveError> {
fn inner(
fs: &dyn FileSystem,
name: &str,
referrer: &ModuleSpecifier,
- ) -> Result<PathBuf, AnyError> {
- let referrer_file = specifier_to_file_path(referrer)?;
- let mut current_folder = referrer_file.parent().unwrap();
- loop {
- let node_modules_folder = if current_folder.ends_with("node_modules") {
- Cow::Borrowed(current_folder)
- } else {
- Cow::Owned(current_folder.join("node_modules"))
- };
-
- let sub_dir = join_package_name(&node_modules_folder, name);
- if fs.is_dir_sync(&sub_dir) {
- return Ok(sub_dir);
- }
+ ) -> Result<PathBuf, PackageFolderResolveError> {
+ let maybe_referrer_file = specifier_to_file_path(referrer).ok();
+ let maybe_start_folder =
+ maybe_referrer_file.as_ref().and_then(|f| f.parent());
+ if let Some(start_folder) = maybe_start_folder {
+ for current_folder in start_folder.ancestors() {
+ let node_modules_folder = if current_folder.ends_with("node_modules")
+ {
+ Cow::Borrowed(current_folder)
+ } else {
+ Cow::Owned(current_folder.join("node_modules"))
+ };
- if let Some(parent) = current_folder.parent() {
- current_folder = parent;
- } else {
- break;
+ let sub_dir = join_package_name(&node_modules_folder, name);
+ if fs.is_dir_sync(&sub_dir) {
+ return Ok(sub_dir);
+ }
}
}
- bail!(
- "could not find package '{}' from referrer '{}'.",
- name,
- referrer
- );
+ Err(
+ PackageFolderResolveErrorKind::NotFoundPackage {
+ package_name: name.to_string(),
+ referrer: referrer.clone(),
+ referrer_extra: None,
+ }
+ .into(),
+ )
}
let path = inner(&*self.fs, name, referrer)?;
- Ok(self.fs.realpath_sync(&path)?)
+ self.fs.realpath_sync(&path).map_err(|err| {
+ PackageFolderResolveErrorKind::Io {
+ package_name: name.to_string(),
+ referrer: referrer.clone(),
+ source: err.into_io_error(),
+ }
+ .into()
+ })
}
fn in_npm_package(&self, specifier: &ModuleSpecifier) -> bool {
diff --git a/cli/npm/managed/mod.rs b/cli/npm/managed/mod.rs
index 467703b05..6022396d6 100644
--- a/cli/npm/managed/mod.rs
+++ b/cli/npm/managed/mod.rs
@@ -20,6 +20,8 @@ use deno_npm::NpmPackageId;
use deno_npm::NpmResolutionPackage;
use deno_npm::NpmSystemInfo;
use deno_runtime::deno_fs::FileSystem;
+use deno_runtime::deno_node::errors::PackageFolderResolveError;
+use deno_runtime::deno_node::errors::PackageFolderResolveErrorKind;
use deno_runtime::deno_node::NodePermissions;
use deno_runtime::deno_node::NpmResolver;
use deno_semver::package::PackageNv;
@@ -522,12 +524,17 @@ impl NpmResolver for ManagedCliNpmResolver {
&self,
name: &str,
referrer: &ModuleSpecifier,
- ) -> Result<PathBuf, AnyError> {
+ ) -> Result<PathBuf, PackageFolderResolveError> {
let path = self
.fs_resolver
.resolve_package_folder_from_package(name, referrer)?;
let path =
- canonicalize_path_maybe_not_exists_with_fs(&path, self.fs.as_ref())?;
+ canonicalize_path_maybe_not_exists_with_fs(&path, self.fs.as_ref())
+ .map_err(|err| PackageFolderResolveErrorKind::Io {
+ package_name: name.to_string(),
+ referrer: referrer.clone(),
+ source: err,
+ })?;
log::debug!("Resolved {} from {} to {}", name, referrer, path.display());
Ok(path)
}
diff --git a/cli/npm/managed/resolvers/common.rs b/cli/npm/managed/resolvers/common.rs
index 35f368cb5..dffa1b75c 100644
--- a/cli/npm/managed/resolvers/common.rs
+++ b/cli/npm/managed/resolvers/common.rs
@@ -18,6 +18,7 @@ use deno_npm::NpmPackageCacheFolderId;
use deno_npm::NpmPackageId;
use deno_npm::NpmResolutionPackage;
use deno_runtime::deno_fs::FileSystem;
+use deno_runtime::deno_node::errors::PackageFolderResolveError;
use deno_runtime::deno_node::NodePermissions;
use crate::npm::managed::cache::TarballCache;
@@ -31,16 +32,25 @@ pub trait NpmPackageFsResolver: Send + Sync {
/// The local node_modules folder if it is applicable to the implementation.
fn node_modules_path(&self) -> Option<&PathBuf>;
+ fn maybe_package_folder(&self, package_id: &NpmPackageId) -> Option<PathBuf>;
+
fn package_folder(
&self,
package_id: &NpmPackageId,
- ) -> Result<PathBuf, AnyError>;
+ ) -> Result<PathBuf, AnyError> {
+ self.maybe_package_folder(package_id).ok_or_else(|| {
+ deno_core::anyhow::anyhow!(
+ "Package folder not found for '{}'",
+ package_id.as_serialized()
+ )
+ })
+ }
fn resolve_package_folder_from_package(
&self,
name: &str,
referrer: &ModuleSpecifier,
- ) -> Result<PathBuf, AnyError>;
+ ) -> Result<PathBuf, PackageFolderResolveError>;
fn resolve_package_cache_folder_id_from_specifier(
&self,
diff --git a/cli/npm/managed/resolvers/global.rs b/cli/npm/managed/resolvers/global.rs
index d10b33e7d..d16fe7cd0 100644
--- a/cli/npm/managed/resolvers/global.rs
+++ b/cli/npm/managed/resolvers/global.rs
@@ -8,13 +8,14 @@ use std::sync::Arc;
use async_trait::async_trait;
use deno_ast::ModuleSpecifier;
-use deno_core::anyhow::bail;
use deno_core::error::AnyError;
use deno_core::url::Url;
use deno_npm::NpmPackageCacheFolderId;
use deno_npm::NpmPackageId;
use deno_npm::NpmSystemInfo;
use deno_runtime::deno_fs::FileSystem;
+use deno_runtime::deno_node::errors::PackageFolderResolveError;
+use deno_runtime::deno_node::errors::PackageFolderResolveErrorKind;
use deno_runtime::deno_node::NodePermissions;
use super::super::cache::NpmCache;
@@ -65,29 +66,71 @@ impl NpmPackageFsResolver for GlobalNpmPackageResolver {
None
}
- fn package_folder(&self, id: &NpmPackageId) -> Result<PathBuf, AnyError> {
+ fn maybe_package_folder(&self, id: &NpmPackageId) -> Option<PathBuf> {
let folder_id = self
.resolution
- .resolve_pkg_cache_folder_id_from_pkg_id(id)
- .unwrap();
- Ok(self.cache.package_folder_for_id(&folder_id))
+ .resolve_pkg_cache_folder_id_from_pkg_id(id)?;
+ Some(self.cache.package_folder_for_id(&folder_id))
}
fn resolve_package_folder_from_package(
&self,
name: &str,
referrer: &ModuleSpecifier,
- ) -> Result<PathBuf, AnyError> {
- let Some(referrer_pkg_id) = self
+ ) -> Result<PathBuf, PackageFolderResolveError> {
+ use deno_npm::resolution::PackageNotFoundFromReferrerError;
+ let Some(referrer_cache_folder_id) = self
.cache
.resolve_package_folder_id_from_specifier(referrer)
else {
- bail!("could not find npm package for '{}'", referrer);
+ return Err(
+ PackageFolderResolveErrorKind::NotFoundReferrer {
+ referrer: referrer.clone(),
+ referrer_extra: None,
+ }
+ .into(),
+ );
};
- let pkg = self
+ let resolve_result = self
.resolution
- .resolve_package_from_package(name, &referrer_pkg_id)?;
- self.package_folder(&pkg.id)
+ .resolve_package_from_package(name, &referrer_cache_folder_id);
+ match resolve_result {
+ Ok(pkg) => match self.maybe_package_folder(&pkg.id) {
+ Some(folder) => Ok(folder),
+ None => Err(
+ PackageFolderResolveErrorKind::NotFoundPackage {
+ package_name: name.to_string(),
+ referrer: referrer.clone(),
+ referrer_extra: Some(format!(
+ "{} -> {}",
+ referrer_cache_folder_id,
+ pkg.id.as_serialized()
+ )),
+ }
+ .into(),
+ ),
+ },
+ Err(err) => match *err {
+ PackageNotFoundFromReferrerError::Referrer(cache_folder_id) => Err(
+ PackageFolderResolveErrorKind::NotFoundReferrer {
+ referrer: referrer.clone(),
+ referrer_extra: Some(cache_folder_id.to_string()),
+ }
+ .into(),
+ ),
+ PackageNotFoundFromReferrerError::Package {
+ name,
+ referrer: cache_folder_id_referrer,
+ } => Err(
+ PackageFolderResolveErrorKind::NotFoundPackage {
+ package_name: name,
+ referrer: referrer.clone(),
+ referrer_extra: Some(cache_folder_id_referrer.to_string()),
+ }
+ .into(),
+ ),
+ },
+ }
}
fn resolve_package_cache_folder_id_from_specifier(
diff --git a/cli/npm/managed/resolvers/local.rs b/cli/npm/managed/resolvers/local.rs
index e8fffa0cd..ed36162c0 100644
--- a/cli/npm/managed/resolvers/local.rs
+++ b/cli/npm/managed/resolvers/local.rs
@@ -15,19 +15,8 @@ use std::path::PathBuf;
use std::rc::Rc;
use std::sync::Arc;
-use crate::args::PackageJsonInstallDepsProvider;
-use crate::cache::CACHE_PERM;
-use crate::npm::cache_dir::mixed_case_package_name_decode;
-use crate::util::fs::atomic_write_file_with_retries;
-use crate::util::fs::canonicalize_path_maybe_not_exists_with_fs;
-use crate::util::fs::clone_dir_recursive;
-use crate::util::fs::symlink_dir;
-use crate::util::fs::LaxSingleProcessFsFlag;
-use crate::util::progress_bar::ProgressBar;
-use crate::util::progress_bar::ProgressMessagePrompt;
use async_trait::async_trait;
use deno_ast::ModuleSpecifier;
-use deno_core::anyhow::bail;
use deno_core::anyhow::Context;
use deno_core::error::AnyError;
use deno_core::futures::stream::FuturesUnordered;
@@ -39,12 +28,24 @@ use deno_npm::NpmPackageId;
use deno_npm::NpmResolutionPackage;
use deno_npm::NpmSystemInfo;
use deno_runtime::deno_fs;
+use deno_runtime::deno_node::errors::PackageFolderResolveError;
+use deno_runtime::deno_node::errors::PackageFolderResolveErrorKind;
use deno_runtime::deno_node::NodePermissions;
use deno_semver::package::PackageNv;
use serde::Deserialize;
use serde::Serialize;
+use crate::args::PackageJsonInstallDepsProvider;
+use crate::cache::CACHE_PERM;
+use crate::npm::cache_dir::mixed_case_package_name_decode;
use crate::npm::cache_dir::mixed_case_package_name_encode;
+use crate::util::fs::atomic_write_file_with_retries;
+use crate::util::fs::canonicalize_path_maybe_not_exists_with_fs;
+use crate::util::fs::clone_dir_recursive;
+use crate::util::fs::symlink_dir;
+use crate::util::fs::LaxSingleProcessFsFlag;
+use crate::util::progress_bar::ProgressBar;
+use crate::util::progress_bar::ProgressMessagePrompt;
use super::super::cache::NpmCache;
use super::super::cache::TarballCache;
@@ -113,7 +114,7 @@ impl LocalNpmPackageResolver {
fn resolve_folder_for_specifier(
&self,
specifier: &ModuleSpecifier,
- ) -> Result<Option<PathBuf>, AnyError> {
+ ) -> Result<Option<PathBuf>, std::io::Error> {
let Some(relative_url) =
self.root_node_modules_url.make_relative(specifier)
else {
@@ -130,7 +131,6 @@ impl LocalNpmPackageResolver {
// in `node_modules` directory of the referrer.
canonicalize_path_maybe_not_exists_with_fs(&path, self.fs.as_ref())
.map(Some)
- .map_err(|err| err.into())
}
fn resolve_package_folder_from_specifier(
@@ -155,32 +155,42 @@ impl NpmPackageFsResolver for LocalNpmPackageResolver {
Some(&self.root_node_modules_path)
}
- fn package_folder(&self, id: &NpmPackageId) -> Result<PathBuf, AnyError> {
- match self.resolution.resolve_pkg_cache_folder_id_from_pkg_id(id) {
- // package is stored at:
- // node_modules/.deno/<package_cache_folder_id_folder_name>/node_modules/<package_name>
- Some(cache_folder_id) => Ok(
- self
- .root_node_modules_path
- .join(".deno")
- .join(get_package_folder_id_folder_name(&cache_folder_id))
- .join("node_modules")
- .join(&cache_folder_id.nv.name),
- ),
- None => bail!(
- "Could not find package information for '{}'",
- id.as_serialized()
- ),
- }
+ fn maybe_package_folder(&self, id: &NpmPackageId) -> Option<PathBuf> {
+ let cache_folder_id = self
+ .resolution
+ .resolve_pkg_cache_folder_id_from_pkg_id(id)?;
+ // package is stored at:
+ // node_modules/.deno/<package_cache_folder_id_folder_name>/node_modules/<package_name>
+ Some(
+ self
+ .root_node_modules_path
+ .join(".deno")
+ .join(get_package_folder_id_folder_name(&cache_folder_id))
+ .join("node_modules")
+ .join(&cache_folder_id.nv.name),
+ )
}
fn resolve_package_folder_from_package(
&self,
name: &str,
referrer: &ModuleSpecifier,
- ) -> Result<PathBuf, AnyError> {
- let Some(local_path) = self.resolve_folder_for_specifier(referrer)? else {
- bail!("could not find npm package for '{}'", referrer);
+ ) -> Result<PathBuf, PackageFolderResolveError> {
+ let maybe_local_path = self
+ .resolve_folder_for_specifier(referrer)
+ .map_err(|err| PackageFolderResolveErrorKind::Io {
+ package_name: name.to_string(),
+ referrer: referrer.clone(),
+ source: err,
+ })?;
+ let Some(local_path) = maybe_local_path else {
+ return Err(
+ PackageFolderResolveErrorKind::NotFoundReferrer {
+ referrer: referrer.clone(),
+ referrer_extra: None,
+ }
+ .into(),
+ );
};
let package_root_path = self.resolve_package_root(&local_path);
let mut current_folder = package_root_path.as_path();
@@ -202,11 +212,14 @@ impl NpmPackageFsResolver for LocalNpmPackageResolver {
}
}
- bail!(
- "could not find package '{}' from referrer '{}'.",
- name,
- referrer
- );
+ Err(
+ PackageFolderResolveErrorKind::NotFoundPackage {
+ package_name: name.to_string(),
+ referrer: referrer.clone(),
+ referrer_extra: None,
+ }
+ .into(),
+ )
}
fn resolve_package_cache_folder_id_from_specifier(
@@ -696,6 +709,7 @@ fn junction_or_symlink_dir(
old_path: &Path,
new_path: &Path,
) -> Result<(), AnyError> {
+ use deno_core::anyhow::bail;
// Use junctions because they're supported on ntfs file systems without
// needing to elevate privileges on Windows
diff --git a/cli/resolver.rs b/cli/resolver.rs
index d1e5d91e7..e035a313c 100644
--- a/cli/resolver.rs
+++ b/cli/resolver.rs
@@ -23,6 +23,8 @@ use deno_graph::NpmResolvePkgReqsResult;
use deno_npm::resolution::NpmResolutionError;
use deno_runtime::deno_fs;
use deno_runtime::deno_fs::FileSystem;
+use deno_runtime::deno_node::errors::ClosestPkgJsonError;
+use deno_runtime::deno_node::errors::UrlToNodeResolutionError;
use deno_runtime::deno_node::is_builtin_node_module;
use deno_runtime::deno_node::parse_npm_pkg_name;
use deno_runtime::deno_node::NodeResolution;
@@ -94,7 +96,7 @@ impl CliNodeResolver {
pub fn get_closest_package_json(
&self,
referrer: &ModuleSpecifier,
- ) -> Result<Option<Arc<PackageJson>>, AnyError> {
+ ) -> Result<Option<Arc<PackageJson>>, ClosestPkgJsonError> {
self.node_resolver.get_closest_package_json(referrer)
}
@@ -119,7 +121,10 @@ impl CliNodeResolver {
mode: NodeResolutionMode,
) -> Result<Option<NodeResolution>, AnyError> {
self.handle_node_resolve_result(
- self.node_resolver.resolve(specifier, referrer, mode),
+ self
+ .node_resolver
+ .resolve(specifier, referrer, mode)
+ .map_err(AnyError::from),
)
}
@@ -151,7 +156,7 @@ impl CliNodeResolver {
.maybe_resolve_package_sub_path_from_deno_module(
&package_folder,
sub_path,
- referrer,
+ Some(referrer),
mode,
)?;
match maybe_resolution {
@@ -180,14 +185,14 @@ impl CliNodeResolver {
&self,
package_folder: &Path,
sub_path: Option<&str>,
- referrer: &ModuleSpecifier,
+ maybe_referrer: Option<&ModuleSpecifier>,
mode: NodeResolutionMode,
) -> Result<NodeResolution, AnyError> {
self
.maybe_resolve_package_sub_path_from_deno_module(
package_folder,
sub_path,
- referrer,
+ maybe_referrer,
mode,
)?
.ok_or_else(|| {
@@ -205,16 +210,19 @@ impl CliNodeResolver {
&self,
package_folder: &Path,
sub_path: Option<&str>,
- referrer: &ModuleSpecifier,
+ maybe_referrer: Option<&ModuleSpecifier>,
mode: NodeResolutionMode,
) -> Result<Option<NodeResolution>, AnyError> {
self.handle_node_resolve_result(
- self.node_resolver.resolve_package_subpath_from_deno_module(
- package_folder,
- sub_path,
- referrer,
- mode,
- ),
+ self
+ .node_resolver
+ .resolve_package_subpath_from_deno_module(
+ package_folder,
+ sub_path,
+ maybe_referrer,
+ mode,
+ )
+ .map_err(AnyError::from),
)
}
@@ -252,7 +260,7 @@ impl CliNodeResolver {
pub fn url_to_node_resolution(
&self,
specifier: ModuleSpecifier,
- ) -> Result<NodeResolution, AnyError> {
+ ) -> Result<NodeResolution, UrlToNodeResolutionError> {
self.node_resolver.url_to_node_resolution(specifier)
}
@@ -574,7 +582,7 @@ impl Resolver for CliGraphResolver {
.resolve_package_sub_path_from_deno_module(
pkg_folder,
sub_path.as_deref(),
- referrer,
+ Some(referrer),
to_node_mode(mode),
)?
.into_url(),
@@ -599,7 +607,7 @@ impl Resolver for CliGraphResolver {
.resolve_package_sub_path_from_deno_module(
pkg_folder,
req_ref.sub_path(),
- referrer,
+ Some(referrer),
to_node_mode(mode),
)?
.into_url(),
diff --git a/cli/standalone/mod.rs b/cli/standalone/mod.rs
index cbd14db4f..106b7b7e7 100644
--- a/cli/standalone/mod.rs
+++ b/cli/standalone/mod.rs
@@ -197,7 +197,7 @@ impl ModuleLoader for EmbeddedModuleLoader {
.resolve_package_sub_path_from_deno_module(
pkg_folder,
sub_path.as_deref(),
- &referrer,
+ Some(&referrer),
NodeResolutionMode::Execution,
)?
.into_url(),
diff --git a/cli/tsc/mod.rs b/cli/tsc/mod.rs
index de4c05e27..6306c9975 100644
--- a/cli/tsc/mod.rs
+++ b/cli/tsc/mod.rs
@@ -739,7 +739,7 @@ fn resolve_graph_specifier_types(
npm.node_resolver.resolve_package_subpath_from_deno_module(
&package_folder,
module.nv_reference.sub_path(),
- referrer,
+ Some(referrer),
NodeResolutionMode::Types,
)?;
Ok(Some(NodeResolution::into_specifier_and_media_type(
@@ -793,7 +793,7 @@ fn resolve_non_graph_specifier_types(
.resolve_package_subpath_from_deno_module(
&package_folder,
npm_req_ref.sub_path(),
- referrer,
+ Some(referrer),
NodeResolutionMode::Types,
)?;
Ok(Some(NodeResolution::into_specifier_and_media_type(
diff --git a/cli/worker.rs b/cli/worker.rs
index 987d65192..9125f28be 100644
--- a/cli/worker.rs
+++ b/cli/worker.rs
@@ -669,7 +669,7 @@ impl CliMainWorkerFactory {
self.resolve_binary_entrypoint_fallback(package_folder, sub_path);
match result {
Ok(Some(resolution)) => Ok(resolution),
- Ok(None) => Err(original_err),
+ Ok(None) => Err(original_err.into()),
Err(fallback_err) => {
bail!("{:#}\n\nFallback failed: {:#}", original_err, fallback_err)
}
@@ -692,16 +692,13 @@ impl CliMainWorkerFactory {
return Ok(None);
}
- // use a fake referrer since a real one doesn't exist
- let referrer =
- ModuleSpecifier::from_directory_path(package_folder).unwrap();
let Some(resolution) = self
.shared
.node_resolver
.resolve_package_subpath_from_deno_module(
package_folder,
sub_path,
- &referrer,
+ /* referrer */ None,
NodeResolutionMode::Execution,
)?
else {
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 {
diff --git a/tests/testdata/npm/deno_run_no_bin_entrypoint.out b/tests/testdata/npm/deno_run_no_bin_entrypoint.out
index 73bddfecf..2b885ed59 100644
--- a/tests/testdata/npm/deno_run_no_bin_entrypoint.out
+++ b/tests/testdata/npm/deno_run_no_bin_entrypoint.out
@@ -1 +1 @@
-error: '[WILDCARD]@denotest[WILDCARD]esm-basic[WILDCARD]package.json' did not have a bin property
+error: Failed resolving binary export. '[WILDCARD]@denotest[WILDCARD]esm-basic[WILDCARD]package.json' did not have a bin property
diff --git a/tests/testdata/npm/deno_run_no_bin_entrypoint_non_existent_subpath.out b/tests/testdata/npm/deno_run_no_bin_entrypoint_non_existent_subpath.out
index ea5ee3d35..525fe4b4f 100644
--- a/tests/testdata/npm/deno_run_no_bin_entrypoint_non_existent_subpath.out
+++ b/tests/testdata/npm/deno_run_no_bin_entrypoint_non_existent_subpath.out
@@ -1,3 +1,3 @@
-error: '[WILDCARD]@denotest[WILDCARD]esm-basic[WILDCARD]package.json' did not have a bin property
+error: Failed resolving binary export. '[WILDCARD]@denotest[WILDCARD]esm-basic[WILDCARD]package.json' did not have a bin property
Fallback failed: Cannot find module 'file:///[WILDCARD]/non-existent.js'
diff --git a/tests/testdata/npm/imports_package_json/import_not_defined.out b/tests/testdata/npm/imports_package_json/import_not_defined.out
index 8baf5bb18..70d1145a3 100644
--- a/tests/testdata/npm/imports_package_json/import_not_defined.out
+++ b/tests/testdata/npm/imports_package_json/import_not_defined.out
@@ -1,3 +1,3 @@
Download http://localhost:4260/@denotest/imports-package-json
Download http://localhost:4260/@denotest/imports-package-json/1.0.0.tgz
-error: [ERR_PACKAGE_IMPORT_NOT_DEFINED] Package import specifier "#not-defined" is not defined in package [WILDCARD]package.json imported from [WILDCARD]import_not_defined.js
+error: [ERR_PACKAGE_IMPORT_NOT_DEFINED] Package import specifier "#not-defined" is not defined in package [WILDCARD]package.json imported from '[WILDCARD]import_not_defined.js'
diff --git a/tests/testdata/npm/imports_package_json/sub_path_import_not_defined.out b/tests/testdata/npm/imports_package_json/sub_path_import_not_defined.out
index 98ca8bde6..7c803f2bf 100644
--- a/tests/testdata/npm/imports_package_json/sub_path_import_not_defined.out
+++ b/tests/testdata/npm/imports_package_json/sub_path_import_not_defined.out
@@ -1,3 +1,3 @@
Download http://localhost:4260/@denotest/imports-package-json
Download http://localhost:4260/@denotest/imports-package-json/1.0.0.tgz
-error: [ERR_PACKAGE_IMPORT_NOT_DEFINED] Package import specifier "#hi" is not defined in package [WILDCARD]sub_path[WILDCARD]package.json imported from [WILDCARD]import_not_defined.js
+error: [ERR_PACKAGE_IMPORT_NOT_DEFINED] Package import specifier "#hi" is not defined in package [WILDCARD]sub_path[WILDCARD]package.json imported from '[WILDCARD]import_not_defined.js'