1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-24 15:19:26 -05:00

fix(node): better detection for when to surface node resolution errors (#24653)

This commit is contained in:
David Sherret 2024-07-23 20:22:24 -04:00 committed by GitHub
parent 445e05a39d
commit 52ababc4bf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
35 changed files with 678 additions and 519 deletions

View file

@ -446,15 +446,14 @@ impl<TGraphContainer: ModuleGraphContainer>
specifier: &str, specifier: &str,
referrer: &ModuleSpecifier, referrer: &ModuleSpecifier,
) -> Result<ModuleSpecifier, AnyError> { ) -> Result<ModuleSpecifier, AnyError> {
if let Some(result) = self.shared.node_resolver.resolve_if_in_npm_package( if self.shared.node_resolver.in_npm_package(referrer) {
specifier, return Ok(
referrer, self
NodeResolutionMode::Execution, .shared
) { .node_resolver
return match result? { .resolve(specifier, referrer, NodeResolutionMode::Execution)?
Some(res) => Ok(res.into_url()), .into_url(),
None => Err(generic_error("not found")), );
};
} }
let graph = self.graph_container.graph(); let graph = self.graph_container.graph();

View file

@ -12,7 +12,8 @@ use deno_core::serde_json;
use deno_package_json::PackageJsonDepValue; use deno_package_json::PackageJsonDepValue;
use deno_runtime::deno_fs::FileSystem; use deno_runtime::deno_fs::FileSystem;
use deno_runtime::deno_node::errors::PackageFolderResolveError; use deno_runtime::deno_node::errors::PackageFolderResolveError;
use deno_runtime::deno_node::errors::PackageFolderResolveErrorKind; use deno_runtime::deno_node::errors::PackageFolderResolveIoError;
use deno_runtime::deno_node::errors::PackageNotFoundError;
use deno_runtime::deno_node::load_pkg_json; use deno_runtime::deno_node::load_pkg_json;
use deno_runtime::deno_node::NodePermissions; use deno_runtime::deno_node::NodePermissions;
use deno_runtime::deno_node::NpmResolver; use deno_runtime::deno_node::NpmResolver;
@ -198,7 +199,7 @@ impl NpmResolver for ByonmCliNpmResolver {
} }
Err( Err(
PackageFolderResolveErrorKind::NotFoundPackage { PackageNotFoundError {
package_name: name.to_string(), package_name: name.to_string(),
referrer: referrer.clone(), referrer: referrer.clone(),
referrer_extra: None, referrer_extra: None,
@ -209,7 +210,7 @@ impl NpmResolver for ByonmCliNpmResolver {
let path = inner(&*self.fs, name, referrer)?; let path = inner(&*self.fs, name, referrer)?;
self.fs.realpath_sync(&path).map_err(|err| { self.fs.realpath_sync(&path).map_err(|err| {
PackageFolderResolveErrorKind::Io { PackageFolderResolveIoError {
package_name: name.to_string(), package_name: name.to_string(),
referrer: referrer.clone(), referrer: referrer.clone(),
source: err.into_io_error(), source: err.into_io_error(),

View file

@ -21,7 +21,7 @@ use deno_npm::NpmResolutionPackage;
use deno_npm::NpmSystemInfo; use deno_npm::NpmSystemInfo;
use deno_runtime::deno_fs::FileSystem; use deno_runtime::deno_fs::FileSystem;
use deno_runtime::deno_node::errors::PackageFolderResolveError; use deno_runtime::deno_node::errors::PackageFolderResolveError;
use deno_runtime::deno_node::errors::PackageFolderResolveErrorKind; use deno_runtime::deno_node::errors::PackageFolderResolveIoError;
use deno_runtime::deno_node::NodePermissions; use deno_runtime::deno_node::NodePermissions;
use deno_runtime::deno_node::NpmResolver; use deno_runtime::deno_node::NpmResolver;
use deno_semver::package::PackageNv; use deno_semver::package::PackageNv;
@ -549,7 +549,7 @@ impl NpmResolver for ManagedCliNpmResolver {
.resolve_package_folder_from_package(name, referrer)?; .resolve_package_folder_from_package(name, referrer)?;
let path = 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 { .map_err(|err| PackageFolderResolveIoError {
package_name: name.to_string(), package_name: name.to_string(),
referrer: referrer.clone(), referrer: referrer.clone(),
source: err, source: err,

View file

@ -15,7 +15,8 @@ use deno_npm::NpmPackageId;
use deno_npm::NpmSystemInfo; use deno_npm::NpmSystemInfo;
use deno_runtime::deno_fs::FileSystem; use deno_runtime::deno_fs::FileSystem;
use deno_runtime::deno_node::errors::PackageFolderResolveError; use deno_runtime::deno_node::errors::PackageFolderResolveError;
use deno_runtime::deno_node::errors::PackageFolderResolveErrorKind; use deno_runtime::deno_node::errors::PackageNotFoundError;
use deno_runtime::deno_node::errors::ReferrerNotFoundError;
use deno_runtime::deno_node::NodePermissions; use deno_runtime::deno_node::NodePermissions;
use super::super::cache::NpmCache; use super::super::cache::NpmCache;
@ -84,7 +85,7 @@ impl NpmPackageFsResolver for GlobalNpmPackageResolver {
.resolve_package_folder_id_from_specifier(referrer) .resolve_package_folder_id_from_specifier(referrer)
else { else {
return Err( return Err(
PackageFolderResolveErrorKind::NotFoundReferrer { ReferrerNotFoundError {
referrer: referrer.clone(), referrer: referrer.clone(),
referrer_extra: None, referrer_extra: None,
} }
@ -98,7 +99,7 @@ impl NpmPackageFsResolver for GlobalNpmPackageResolver {
Ok(pkg) => match self.maybe_package_folder(&pkg.id) { Ok(pkg) => match self.maybe_package_folder(&pkg.id) {
Some(folder) => Ok(folder), Some(folder) => Ok(folder),
None => Err( None => Err(
PackageFolderResolveErrorKind::NotFoundPackage { PackageNotFoundError {
package_name: name.to_string(), package_name: name.to_string(),
referrer: referrer.clone(), referrer: referrer.clone(),
referrer_extra: Some(format!( referrer_extra: Some(format!(
@ -112,7 +113,7 @@ impl NpmPackageFsResolver for GlobalNpmPackageResolver {
}, },
Err(err) => match *err { Err(err) => match *err {
PackageNotFoundFromReferrerError::Referrer(cache_folder_id) => Err( PackageNotFoundFromReferrerError::Referrer(cache_folder_id) => Err(
PackageFolderResolveErrorKind::NotFoundReferrer { ReferrerNotFoundError {
referrer: referrer.clone(), referrer: referrer.clone(),
referrer_extra: Some(cache_folder_id.to_string()), referrer_extra: Some(cache_folder_id.to_string()),
} }
@ -122,7 +123,7 @@ impl NpmPackageFsResolver for GlobalNpmPackageResolver {
name, name,
referrer: cache_folder_id_referrer, referrer: cache_folder_id_referrer,
} => Err( } => Err(
PackageFolderResolveErrorKind::NotFoundPackage { PackageNotFoundError {
package_name: name, package_name: name,
referrer: referrer.clone(), referrer: referrer.clone(),
referrer_extra: Some(cache_folder_id_referrer.to_string()), referrer_extra: Some(cache_folder_id_referrer.to_string()),

View file

@ -33,7 +33,9 @@ use deno_npm::NpmResolutionPackage;
use deno_npm::NpmSystemInfo; use deno_npm::NpmSystemInfo;
use deno_runtime::deno_fs; use deno_runtime::deno_fs;
use deno_runtime::deno_node::errors::PackageFolderResolveError; use deno_runtime::deno_node::errors::PackageFolderResolveError;
use deno_runtime::deno_node::errors::PackageFolderResolveErrorKind; use deno_runtime::deno_node::errors::PackageFolderResolveIoError;
use deno_runtime::deno_node::errors::PackageNotFoundError;
use deno_runtime::deno_node::errors::ReferrerNotFoundError;
use deno_runtime::deno_node::NodePermissions; use deno_runtime::deno_node::NodePermissions;
use deno_semver::package::PackageNv; use deno_semver::package::PackageNv;
use serde::Deserialize; use serde::Deserialize;
@ -185,14 +187,14 @@ impl NpmPackageFsResolver for LocalNpmPackageResolver {
) -> Result<PathBuf, PackageFolderResolveError> { ) -> Result<PathBuf, PackageFolderResolveError> {
let maybe_local_path = self let maybe_local_path = self
.resolve_folder_for_specifier(referrer) .resolve_folder_for_specifier(referrer)
.map_err(|err| PackageFolderResolveErrorKind::Io { .map_err(|err| PackageFolderResolveIoError {
package_name: name.to_string(), package_name: name.to_string(),
referrer: referrer.clone(), referrer: referrer.clone(),
source: err, source: err,
})?; })?;
let Some(local_path) = maybe_local_path else { let Some(local_path) = maybe_local_path else {
return Err( return Err(
PackageFolderResolveErrorKind::NotFoundReferrer { ReferrerNotFoundError {
referrer: referrer.clone(), referrer: referrer.clone(),
referrer_extra: None, referrer_extra: None,
} }
@ -220,7 +222,7 @@ impl NpmPackageFsResolver for LocalNpmPackageResolver {
} }
Err( Err(
PackageFolderResolveErrorKind::NotFoundPackage { PackageNotFoundError {
package_name: name.to_string(), package_name: name.to_string(),
referrer: referrer.clone(), referrer: referrer.clone(),
referrer_extra: None, referrer_extra: None,

View file

@ -25,15 +25,17 @@ use deno_runtime::deno_fs;
use deno_runtime::deno_fs::FileSystem; use deno_runtime::deno_fs::FileSystem;
use deno_runtime::deno_node::errors::ClosestPkgJsonError; use deno_runtime::deno_node::errors::ClosestPkgJsonError;
use deno_runtime::deno_node::errors::NodeResolveError; use deno_runtime::deno_node::errors::NodeResolveError;
use deno_runtime::deno_node::errors::ResolvePkgSubpathFromDenoModuleError; use deno_runtime::deno_node::errors::NodeResolveErrorKind;
use deno_runtime::deno_node::errors::PackageFolderResolveErrorKind;
use deno_runtime::deno_node::errors::PackageFolderResolveIoError;
use deno_runtime::deno_node::errors::PackageNotFoundError;
use deno_runtime::deno_node::errors::PackageResolveErrorKind;
use deno_runtime::deno_node::errors::UrlToNodeResolutionError; use deno_runtime::deno_node::errors::UrlToNodeResolutionError;
use deno_runtime::deno_node::is_builtin_node_module; use deno_runtime::deno_node::is_builtin_node_module;
use deno_runtime::deno_node::parse_npm_pkg_name;
use deno_runtime::deno_node::NodeModuleKind; use deno_runtime::deno_node::NodeModuleKind;
use deno_runtime::deno_node::NodeResolution; use deno_runtime::deno_node::NodeResolution;
use deno_runtime::deno_node::NodeResolutionMode; use deno_runtime::deno_node::NodeResolutionMode;
use deno_runtime::deno_node::NodeResolver; use deno_runtime::deno_node::NodeResolver;
use deno_runtime::deno_node::NpmResolver as DenoNodeNpmResolver;
use deno_runtime::deno_node::PackageJson; use deno_runtime::deno_node::PackageJson;
use deno_runtime::fs_util::specifier_to_file_path; use deno_runtime::fs_util::specifier_to_file_path;
use deno_semver::npm::NpmPackageReqReference; use deno_semver::npm::NpmPackageReqReference;
@ -47,7 +49,6 @@ use crate::args::JsxImportSourceConfig;
use crate::args::DENO_DISABLE_PEDANTIC_NODE_WARNINGS; use crate::args::DENO_DISABLE_PEDANTIC_NODE_WARNINGS;
use crate::colors; use crate::colors;
use crate::node::CliNodeCodeTranslator; use crate::node::CliNodeCodeTranslator;
use crate::npm::ByonmCliNpmResolver;
use crate::npm::CliNpmResolver; use crate::npm::CliNpmResolver;
use crate::npm::InnerCliNpmResolverRef; use crate::npm::InnerCliNpmResolverRef;
use crate::util::sync::AtomicFlag; use crate::util::sync::AtomicFlag;
@ -102,17 +103,77 @@ impl CliNodeResolver {
self.node_resolver.get_closest_package_json(referrer) self.node_resolver.get_closest_package_json(referrer)
} }
pub fn resolve_if_in_npm_package( pub fn resolve_if_for_npm_pkg(
&self, &self,
specifier: &str, specifier: &str,
referrer: &ModuleSpecifier, referrer: &ModuleSpecifier,
mode: NodeResolutionMode, mode: NodeResolutionMode,
) -> Option<Result<Option<NodeResolution>, NodeResolveError>> { ) -> Result<Option<NodeResolution>, AnyError> {
if self.in_npm_package(referrer) { let resolution_result = self.resolve(specifier, referrer, mode);
// we're in an npm package, so use node resolution match resolution_result {
Some(self.resolve(specifier, referrer, mode)) Ok(res) => Ok(Some(res)),
} else { Err(err) => {
None let err = err.into_kind();
match err {
NodeResolveErrorKind::RelativeJoin(_)
| NodeResolveErrorKind::PackageImportsResolve(_)
| NodeResolveErrorKind::UnsupportedEsmUrlScheme(_)
| NodeResolveErrorKind::DataUrlReferrer(_)
| NodeResolveErrorKind::TypesNotFound(_)
| NodeResolveErrorKind::FinalizeResolution(_)
| NodeResolveErrorKind::UrlToNodeResolution(_) => Err(err.into()),
NodeResolveErrorKind::PackageResolve(err) => {
let err = err.into_kind();
match err {
PackageResolveErrorKind::ClosestPkgJson(_)
| PackageResolveErrorKind::InvalidModuleSpecifier(_)
| PackageResolveErrorKind::ExportsResolve(_)
| PackageResolveErrorKind::SubpathResolve(_) => Err(err.into()),
PackageResolveErrorKind::PackageFolderResolve(err) => {
match err.as_kind() {
PackageFolderResolveErrorKind::Io(
PackageFolderResolveIoError { package_name, .. },
)
| PackageFolderResolveErrorKind::PackageNotFound(
PackageNotFoundError { package_name, .. },
) => {
if self.in_npm_package(referrer) {
return Err(err.into());
}
if let Some(byonm_npm_resolver) =
self.npm_resolver.as_byonm()
{
if byonm_npm_resolver
.find_ancestor_package_json_with_dep(
package_name,
referrer,
)
.is_some()
{
return Err(anyhow!(
concat!(
"Could not resolve \"{}\", but found it in a package.json. ",
"Deno expects the node_modules/ directory to be up to date. ",
"Did you forget to run `npm install`?"
),
specifier
));
}
}
Ok(None)
}
PackageFolderResolveErrorKind::ReferrerNotFound(_) => {
if self.in_npm_package(referrer) {
return Err(err.into());
}
Ok(None)
}
}
}
}
}
}
}
} }
} }
@ -121,18 +182,18 @@ impl CliNodeResolver {
specifier: &str, specifier: &str,
referrer: &ModuleSpecifier, referrer: &ModuleSpecifier,
mode: NodeResolutionMode, mode: NodeResolutionMode,
) -> Result<Option<NodeResolution>, NodeResolveError> { ) -> Result<NodeResolution, NodeResolveError> {
let referrer_kind = if self.cjs_resolutions.contains(referrer) { let referrer_kind = if self.cjs_resolutions.contains(referrer) {
NodeModuleKind::Cjs NodeModuleKind::Cjs
} else { } else {
NodeModuleKind::Esm NodeModuleKind::Esm
}; };
let maybe_res = let res =
self self
.node_resolver .node_resolver
.resolve(specifier, referrer, referrer_kind, mode)?; .resolve(specifier, referrer, referrer_kind, mode)?;
Ok(self.handle_node_resolution(maybe_res)) Ok(self.handle_node_resolution(res))
} }
pub fn resolve_req_reference( pub fn resolve_req_reference(
@ -159,16 +220,15 @@ impl CliNodeResolver {
let package_folder = self let package_folder = self
.npm_resolver .npm_resolver
.resolve_pkg_folder_from_deno_module_req(req, referrer)?; .resolve_pkg_folder_from_deno_module_req(req, referrer)?;
let maybe_resolution = self let resolution_result = self.resolve_package_sub_path_from_deno_module(
.maybe_resolve_package_sub_path_from_deno_module( &package_folder,
&package_folder, sub_path,
sub_path, Some(referrer),
Some(referrer), mode,
mode, );
)?; match resolution_result {
match maybe_resolution { Ok(resolution) => Ok(resolution),
Some(resolution) => Ok(resolution), Err(err) => {
None => {
if self.npm_resolver.as_byonm().is_some() { if self.npm_resolver.as_byonm().is_some() {
let package_json_path = package_folder.join("package.json"); let package_json_path = package_folder.join("package.json");
if !self.fs.exists_sync(&package_json_path) { if !self.fs.exists_sync(&package_json_path) {
@ -178,12 +238,7 @@ impl CliNodeResolver {
)); ));
} }
} }
Err(anyhow!( Err(err)
"Failed resolving '{}{}' in '{}'.",
req,
sub_path.map(|s| format!("/{}", s)).unwrap_or_default(),
package_folder.display()
))
} }
} }
} }
@ -195,32 +250,7 @@ impl CliNodeResolver {
maybe_referrer: Option<&ModuleSpecifier>, maybe_referrer: Option<&ModuleSpecifier>,
mode: NodeResolutionMode, mode: NodeResolutionMode,
) -> Result<NodeResolution, AnyError> { ) -> Result<NodeResolution, AnyError> {
self let res = self
.maybe_resolve_package_sub_path_from_deno_module(
package_folder,
sub_path,
maybe_referrer,
mode,
)?
.ok_or_else(|| {
anyhow!(
"Failed resolving '{}' in '{}'.",
sub_path
.map(|s| format!("/{}", s))
.unwrap_or_else(|| ".".to_string()),
package_folder.display(),
)
})
}
pub fn maybe_resolve_package_sub_path_from_deno_module(
&self,
package_folder: &Path,
sub_path: Option<&str>,
maybe_referrer: Option<&ModuleSpecifier>,
mode: NodeResolutionMode,
) -> Result<Option<NodeResolution>, ResolvePkgSubpathFromDenoModuleError> {
let maybe_res = self
.node_resolver .node_resolver
.resolve_package_subpath_from_deno_module( .resolve_package_subpath_from_deno_module(
package_folder, package_folder,
@ -228,7 +258,7 @@ impl CliNodeResolver {
maybe_referrer, maybe_referrer,
mode, mode,
)?; )?;
Ok(self.handle_node_resolution(maybe_res)) Ok(self.handle_node_resolution(res))
} }
pub fn handle_if_in_node_modules( pub fn handle_if_in_node_modules(
@ -267,13 +297,13 @@ impl CliNodeResolver {
fn handle_node_resolution( fn handle_node_resolution(
&self, &self,
maybe_resolution: Option<NodeResolution>, resolution: NodeResolution,
) -> Option<NodeResolution> { ) -> NodeResolution {
if let Some(NodeResolution::CommonJs(specifier)) = &maybe_resolution { if let NodeResolution::CommonJs(specifier) = &resolution {
// remember that this was a common js resolution // remember that this was a common js resolution
self.cjs_resolutions.insert(specifier.clone()); self.cjs_resolutions.insert(specifier.clone());
} }
maybe_resolution resolution
} }
} }
@ -458,39 +488,6 @@ impl CliGraphResolver {
bare_node_builtins_enabled: self.bare_node_builtins_enabled, bare_node_builtins_enabled: self.bare_node_builtins_enabled,
} }
} }
// todo(dsherret): update this and the surrounding code to handle the structured errors from NodeResolver
fn check_surface_byonm_node_error(
&self,
specifier: &str,
referrer: &ModuleSpecifier,
original_err: AnyError,
resolver: &ByonmCliNpmResolver,
) -> Result<(), AnyError> {
if let Ok((pkg_name, _, _)) = parse_npm_pkg_name(specifier, referrer) {
match resolver.resolve_package_folder_from_package(&pkg_name, referrer) {
Ok(_) => {
return Err(original_err);
}
Err(_) => {
if resolver
.find_ancestor_package_json_with_dep(&pkg_name, referrer)
.is_some()
{
return Err(anyhow!(
concat!(
"Could not resolve \"{}\", but found it in a package.json. ",
"Deno expects the node_modules/ directory to be up to date. ",
"Did you forget to run `npm install`?"
),
specifier
));
}
}
}
}
Ok(())
}
} }
impl Resolver for CliGraphResolver { impl Resolver for CliGraphResolver {
@ -523,6 +520,18 @@ impl Resolver for CliGraphResolver {
} }
let referrer = &referrer_range.specifier; let referrer = &referrer_range.specifier;
// Use node resolution if we're in an npm package
if let Some(node_resolver) = self.node_resolver.as_ref() {
if referrer.scheme() == "file" && node_resolver.in_npm_package(referrer) {
return node_resolver
.resolve(specifier, referrer, to_node_mode(mode))
.map(|res| res.into_url())
.map_err(|e| ResolveError::Other(e.into()));
}
}
// Attempt to resolve with the workspace resolver
let result: Result<_, ResolveError> = self let result: Result<_, ResolveError> = self
.workspace_resolver .workspace_resolver
.resolve(specifier, referrer) .resolve(specifier, referrer)
@ -535,7 +544,19 @@ impl Resolver for CliGraphResolver {
let result = match result { let result = match result {
Ok(resolution) => match resolution { Ok(resolution) => match resolution {
MappedResolution::Normal(specifier) MappedResolution::Normal(specifier)
| MappedResolution::ImportMap(specifier) => Ok(specifier), | MappedResolution::ImportMap(specifier) => {
// do sloppy imports resolution if enabled
if let Some(sloppy_imports_resolver) = &self.sloppy_imports_resolver {
Ok(sloppy_imports_resolve(
sloppy_imports_resolver,
specifier,
referrer_range,
mode,
))
} else {
Ok(specifier)
}
}
MappedResolution::WorkspaceNpmPackage { MappedResolution::WorkspaceNpmPackage {
target_pkg_json: pkg_json, target_pkg_json: pkg_json,
sub_path, sub_path,
@ -552,8 +573,6 @@ impl Resolver for CliGraphResolver {
) )
.map_err(ResolveError::Other) .map_err(ResolveError::Other)
.map(|res| res.into_url()), .map(|res| res.into_url()),
// todo(dsherret): for byonm it should do resolution solely based on
// the referrer and not the package.json
MappedResolution::PackageJson { MappedResolution::PackageJson {
dep_result, dep_result,
alias, alias,
@ -604,44 +623,6 @@ impl Resolver for CliGraphResolver {
Err(err) => Err(err), Err(err) => Err(err),
}; };
// check if it's an npm specifier that resolves to a workspace member
if let Some(node_resolver) = &self.node_resolver {
if let Ok(specifier) = &result {
if let Ok(req_ref) = NpmPackageReqReference::from_specifier(specifier) {
if let Some(pkg_folder) = self
.workspace_resolver
.resolve_workspace_pkg_json_folder_for_npm_specifier(req_ref.req())
{
return Ok(
node_resolver
.resolve_package_sub_path_from_deno_module(
pkg_folder,
req_ref.sub_path(),
Some(referrer),
to_node_mode(mode),
)?
.into_url(),
);
}
}
}
}
// do sloppy imports resolution if enabled
let result =
if let Some(sloppy_imports_resolver) = &self.sloppy_imports_resolver {
result.map(|specifier| {
sloppy_imports_resolve(
sloppy_imports_resolver,
specifier,
referrer_range,
mode,
)
})
} else {
result
};
// When the user is vendoring, don't allow them to import directly from the vendor/ directory // When the user is vendoring, don't allow them to import directly from the vendor/ directory
// as it might cause them confusion or duplicate dependencies. Additionally, this folder has // as it might cause them confusion or duplicate dependencies. Additionally, this folder has
// special treatment in the language server so it will definitely cause issues/confusion there // special treatment in the language server so it will definitely cause issues/confusion there
@ -654,77 +635,63 @@ impl Resolver for CliGraphResolver {
} }
} }
if let Some(resolver) = let Some(node_resolver) = &self.node_resolver else {
self.npm_resolver.as_ref().and_then(|r| r.as_byonm()) return result;
{ };
match &result {
Ok(specifier) => { let is_byonm = self
if let Ok(npm_req_ref) = .npm_resolver
NpmPackageReqReference::from_specifier(specifier) .as_ref()
.is_some_and(|r| r.as_byonm().is_some());
match result {
Ok(specifier) => {
if let Ok(npm_req_ref) =
NpmPackageReqReference::from_specifier(&specifier)
{
// check if the npm specifier resolves to a workspace member
if let Some(pkg_folder) = self
.workspace_resolver
.resolve_workspace_pkg_json_folder_for_npm_specifier(
npm_req_ref.req(),
)
{ {
let node_resolver = self.node_resolver.as_ref().unwrap(); return Ok(
node_resolver
.resolve_package_sub_path_from_deno_module(
pkg_folder,
npm_req_ref.sub_path(),
Some(referrer),
to_node_mode(mode),
)?
.into_url(),
);
}
// do npm resolution for byonm
if is_byonm {
return node_resolver return node_resolver
.resolve_req_reference(&npm_req_ref, referrer, to_node_mode(mode)) .resolve_req_reference(&npm_req_ref, referrer, to_node_mode(mode))
.map(|res| res.into_url()) .map(|res| res.into_url())
.map_err(|err| err.into()); .map_err(|err| err.into());
} }
} }
Err(_) => {
if referrer.scheme() == "file" { Ok(node_resolver.handle_if_in_node_modules(specifier)?)
if let Some(node_resolver) = &self.node_resolver { }
let node_result = Err(err) => {
node_resolver.resolve(specifier, referrer, to_node_mode(mode)); // If byonm, check if the bare specifier resolves to an npm package
match node_result { if is_byonm && referrer.scheme() == "file" {
Ok(Some(res)) => { let maybe_resolution = node_resolver
return Ok(res.into_url()); .resolve_if_for_npm_pkg(specifier, referrer, to_node_mode(mode))
} .map_err(ResolveError::Other)?;
Ok(None) => { if let Some(res) = maybe_resolution {
self return Ok(res.into_url());
.check_surface_byonm_node_error(
specifier,
referrer,
anyhow!("Cannot find \"{}\"", specifier),
resolver,
)
.map_err(ResolveError::Other)?;
}
Err(err) => {
self
.check_surface_byonm_node_error(
specifier,
referrer,
err.into(),
resolver,
)
.map_err(ResolveError::Other)?;
}
}
}
} }
} }
}
}
if referrer.scheme() == "file" { Err(err)
if let Some(node_resolver) = &self.node_resolver {
let node_result = node_resolver.resolve_if_in_npm_package(
specifier,
referrer,
to_node_mode(mode),
);
if let Some(Ok(Some(res))) = node_result {
return Ok(res.into_url());
}
} }
} }
let specifier = result?;
match &self.node_resolver {
Some(node_resolver) => node_resolver
.handle_if_in_node_modules(specifier)
.map_err(|e| e.into()),
None => Ok(specifier),
}
} }
} }

View file

@ -151,15 +151,14 @@ impl ModuleLoader for EmbeddedModuleLoader {
})? })?
}; };
if let Some(result) = self.shared.node_resolver.resolve_if_in_npm_package( if self.shared.node_resolver.in_npm_package(&referrer) {
specifier, return Ok(
&referrer, self
NodeResolutionMode::Execution, .shared
) { .node_resolver
return match result? { .resolve(specifier, &referrer, NodeResolutionMode::Execution)?
Some(res) => Ok(res.into_url()), .into_url(),
None => Err(generic_error("not found")), );
};
} }
let mapped_resolution = let mapped_resolution =
@ -250,14 +249,12 @@ impl ModuleLoader for EmbeddedModuleLoader {
Err(err) Err(err)
if err.is_unmapped_bare_specifier() && referrer.scheme() == "file" => if err.is_unmapped_bare_specifier() && referrer.scheme() == "file" =>
{ {
// todo(dsherret): return a better error from node resolution so that let maybe_res = self.shared.node_resolver.resolve_if_for_npm_pkg(
// we can more easily tell whether to surface it or not
let node_result = self.shared.node_resolver.resolve(
specifier, specifier,
&referrer, &referrer,
NodeResolutionMode::Execution, NodeResolutionMode::Execution,
); )?;
if let Ok(Some(res)) = node_result { if let Some(res) = maybe_res {
return Ok(res.into_url()); return Ok(res.into_url());
} }
Err(err.into()) Err(err.into())

View file

@ -30,6 +30,8 @@ use deno_graph::GraphKind;
use deno_graph::Module; use deno_graph::Module;
use deno_graph::ModuleGraph; use deno_graph::ModuleGraph;
use deno_graph::ResolutionResolved; use deno_graph::ResolutionResolved;
use deno_runtime::deno_node::errors::NodeJsErrorCode;
use deno_runtime::deno_node::errors::NodeJsErrorCoded;
use deno_runtime::deno_node::NodeModuleKind; use deno_runtime::deno_node::NodeModuleKind;
use deno_runtime::deno_node::NodeResolution; use deno_runtime::deno_node::NodeResolution;
use deno_runtime::deno_node::NodeResolutionMode; use deno_runtime::deno_node::NodeResolutionMode;
@ -756,13 +758,21 @@ fn resolve_graph_specifier_types(
.as_managed() .as_managed()
.unwrap() // should never be byonm because it won't create Module::Npm .unwrap() // should never be byonm because it won't create Module::Npm
.resolve_pkg_folder_from_deno_module(module.nv_reference.nv())?; .resolve_pkg_folder_from_deno_module(module.nv_reference.nv())?;
let maybe_resolution = let res_result =
npm.node_resolver.resolve_package_subpath_from_deno_module( npm.node_resolver.resolve_package_subpath_from_deno_module(
&package_folder, &package_folder,
module.nv_reference.sub_path(), module.nv_reference.sub_path(),
Some(referrer), Some(referrer),
NodeResolutionMode::Types, NodeResolutionMode::Types,
)?; );
let maybe_resolution = match res_result {
Ok(res) => Some(res),
Err(err) => match err.code() {
NodeJsErrorCode::ERR_TYPES_NOT_FOUND
| NodeJsErrorCode::ERR_MODULE_NOT_FOUND => None,
_ => return Err(err.into()),
},
};
Ok(Some(NodeResolution::into_specifier_and_media_type( Ok(Some(NodeResolution::into_specifier_and_media_type(
maybe_resolution, maybe_resolution,
))) )))
@ -805,8 +815,7 @@ fn resolve_non_graph_specifier_types(
referrer_kind, referrer_kind,
NodeResolutionMode::Types, NodeResolutionMode::Types,
) )
.ok() .ok(),
.flatten(),
))) )))
} else if let Ok(npm_req_ref) = NpmPackageReqReference::from_str(specifier) { } else if let Ok(npm_req_ref) = NpmPackageReqReference::from_str(specifier) {
debug_assert_eq!(referrer_kind, NodeModuleKind::Esm); debug_assert_eq!(referrer_kind, NodeModuleKind::Esm);
@ -817,13 +826,20 @@ fn resolve_non_graph_specifier_types(
let package_folder = npm let package_folder = npm
.npm_resolver .npm_resolver
.resolve_pkg_folder_from_deno_module_req(npm_req_ref.req(), referrer)?; .resolve_pkg_folder_from_deno_module_req(npm_req_ref.req(), referrer)?;
let maybe_resolution = node_resolver let res_result = node_resolver.resolve_package_subpath_from_deno_module(
.resolve_package_subpath_from_deno_module( &package_folder,
&package_folder, npm_req_ref.sub_path(),
npm_req_ref.sub_path(), Some(referrer),
Some(referrer), NodeResolutionMode::Types,
NodeResolutionMode::Types, );
)?; let maybe_resolution = match res_result {
Ok(res) => Some(res),
Err(err) => match err.code() {
NodeJsErrorCode::ERR_TYPES_NOT_FOUND
| NodeJsErrorCode::ERR_MODULE_NOT_FOUND => None,
_ => return Err(err.into()),
},
};
Ok(Some(NodeResolution::into_specifier_and_media_type( Ok(Some(NodeResolution::into_specifier_and_media_type(
maybe_resolution, maybe_resolution,
))) )))

View file

@ -687,7 +687,7 @@ impl CliMainWorkerFactory {
return Ok(None); return Ok(None);
} }
let Some(resolution) = self let resolution = self
.shared .shared
.node_resolver .node_resolver
.resolve_package_subpath_from_deno_module( .resolve_package_subpath_from_deno_module(
@ -695,10 +695,7 @@ impl CliMainWorkerFactory {
sub_path, sub_path,
/* referrer */ None, /* referrer */ None,
NodeResolutionMode::Execution, NodeResolutionMode::Execution,
)? )?;
else {
return Ok(None);
};
match &resolution { match &resolution {
NodeResolution::BuiltIn(_) => Ok(None), NodeResolution::BuiltIn(_) => Ok(None),
NodeResolution::CommonJs(specifier) | NodeResolution::Esm(specifier) => { NodeResolution::CommonJs(specifier) | NodeResolution::Esm(specifier) => {

View file

@ -49,6 +49,8 @@ pub enum NodeJsErrorCode {
ERR_UNKNOWN_FILE_EXTENSION, ERR_UNKNOWN_FILE_EXTENSION,
ERR_UNSUPPORTED_DIR_IMPORT, ERR_UNSUPPORTED_DIR_IMPORT,
ERR_UNSUPPORTED_ESM_URL_SCHEME, ERR_UNSUPPORTED_ESM_URL_SCHEME,
/// Deno specific since Node doesn't support TypeScript.
ERR_TYPES_NOT_FOUND,
} }
impl std::fmt::Display for NodeJsErrorCode { impl std::fmt::Display for NodeJsErrorCode {
@ -70,6 +72,7 @@ impl NodeJsErrorCode {
ERR_UNKNOWN_FILE_EXTENSION => "ERR_UNKNOWN_FILE_EXTENSION", ERR_UNKNOWN_FILE_EXTENSION => "ERR_UNKNOWN_FILE_EXTENSION",
ERR_UNSUPPORTED_DIR_IMPORT => "ERR_UNSUPPORTED_DIR_IMPORT", ERR_UNSUPPORTED_DIR_IMPORT => "ERR_UNSUPPORTED_DIR_IMPORT",
ERR_UNSUPPORTED_ESM_URL_SCHEME => "ERR_UNSUPPORTED_ESM_URL_SCHEME", ERR_UNSUPPORTED_ESM_URL_SCHEME => "ERR_UNSUPPORTED_ESM_URL_SCHEME",
ERR_TYPES_NOT_FOUND => "ERR_TYPES_NOT_FOUND",
} }
} }
} }
@ -122,65 +125,98 @@ impl NodeJsErrorCoded for InvalidModuleSpecifierError {
} }
} }
kinded_err!(LegacyResolveError, LegacyResolveErrorKind);
#[derive(Debug, Error)] #[derive(Debug, Error)]
pub enum LegacyMainResolveError { pub enum LegacyResolveErrorKind {
#[error(transparent)] #[error(transparent)]
PathToDeclarationUrl(PathToDeclarationUrlError), TypesNotFound(#[from] TypesNotFoundError),
#[error(transparent)]
ModuleNotFound(#[from] ModuleNotFoundError),
} }
impl NodeJsErrorCoded for LegacyMainResolveError { impl NodeJsErrorCoded for LegacyResolveError {
fn code(&self) -> NodeJsErrorCode { fn code(&self) -> NodeJsErrorCode {
match self { match self.as_kind() {
Self::PathToDeclarationUrl(e) => e.code(), LegacyResolveErrorKind::TypesNotFound(e) => e.code(),
LegacyResolveErrorKind::ModuleNotFound(e) => e.code(),
} }
} }
} }
kinded_err!(PackageFolderResolveError, PackageFolderResolveErrorKind); kinded_err!(PackageFolderResolveError, PackageFolderResolveErrorKind);
#[derive(Debug, Error)]
#[error(
"Could not find package '{}' from referrer '{}'{}.",
package_name,
referrer,
referrer_extra.as_ref().map(|r| format!(" ({})", r)).unwrap_or_default()
)]
pub struct PackageNotFoundError {
pub package_name: String,
pub referrer: ModuleSpecifier,
/// Extra information about the referrer.
pub referrer_extra: Option<String>,
}
impl NodeJsErrorCoded for PackageNotFoundError {
fn code(&self) -> NodeJsErrorCode {
NodeJsErrorCode::ERR_MODULE_NOT_FOUND
}
}
#[derive(Debug, Error)]
#[error(
"Could not find referrer npm package '{}'{}.",
referrer,
referrer_extra.as_ref().map(|r| format!(" ({})", r)).unwrap_or_default()
)]
pub struct ReferrerNotFoundError {
pub referrer: ModuleSpecifier,
/// Extra information about the referrer.
pub referrer_extra: Option<String>,
}
impl NodeJsErrorCoded for ReferrerNotFoundError {
fn code(&self) -> NodeJsErrorCode {
NodeJsErrorCode::ERR_MODULE_NOT_FOUND
}
}
#[derive(Debug, Error)]
#[error("Failed resolving '{package_name}' from referrer '{referrer}'.")]
pub struct PackageFolderResolveIoError {
pub package_name: String,
pub referrer: ModuleSpecifier,
#[source]
pub source: std::io::Error,
}
impl NodeJsErrorCoded for PackageFolderResolveIoError {
fn code(&self) -> NodeJsErrorCode {
NodeJsErrorCode::ERR_MODULE_NOT_FOUND
}
}
impl NodeJsErrorCoded for PackageFolderResolveError { impl NodeJsErrorCoded for PackageFolderResolveError {
fn code(&self) -> NodeJsErrorCode { fn code(&self) -> NodeJsErrorCode {
match self.as_kind() { match self.as_kind() {
PackageFolderResolveErrorKind::NotFoundPackage { .. } PackageFolderResolveErrorKind::PackageNotFound(e) => e.code(),
| PackageFolderResolveErrorKind::NotFoundReferrer { .. } PackageFolderResolveErrorKind::ReferrerNotFound(e) => e.code(),
| PackageFolderResolveErrorKind::Io { .. } => { PackageFolderResolveErrorKind::Io(e) => e.code(),
NodeJsErrorCode::ERR_MODULE_NOT_FOUND
}
} }
} }
} }
#[derive(Debug, Error)] #[derive(Debug, Error)]
pub enum PackageFolderResolveErrorKind { pub enum PackageFolderResolveErrorKind {
#[error( #[error(transparent)]
"Could not find package '{}' from referrer '{}'{}.", PackageNotFound(#[from] PackageNotFoundError),
package_name, #[error(transparent)]
referrer, ReferrerNotFound(#[from] ReferrerNotFoundError),
referrer_extra.as_ref().map(|r| format!(" ({})", r)).unwrap_or_default() #[error(transparent)]
)] Io(#[from] PackageFolderResolveIoError),
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,
},
} }
kinded_err!(PackageSubpathResolveError, PackageSubpathResolveErrorKind); kinded_err!(PackageSubpathResolveError, PackageSubpathResolveErrorKind);
@ -189,10 +225,8 @@ impl NodeJsErrorCoded for PackageSubpathResolveError {
fn code(&self) -> NodeJsErrorCode { fn code(&self) -> NodeJsErrorCode {
match self.as_kind() { match self.as_kind() {
PackageSubpathResolveErrorKind::PkgJsonLoad(e) => e.code(), PackageSubpathResolveErrorKind::PkgJsonLoad(e) => e.code(),
PackageSubpathResolveErrorKind::PackageFolderResolve(e) => e.code(),
PackageSubpathResolveErrorKind::Exports(e) => e.code(), PackageSubpathResolveErrorKind::Exports(e) => e.code(),
PackageSubpathResolveErrorKind::LegacyMain(e) => e.code(), PackageSubpathResolveErrorKind::LegacyResolve(e) => e.code(),
PackageSubpathResolveErrorKind::LegacyExact(e) => e.code(),
} }
} }
} }
@ -202,13 +236,9 @@ pub enum PackageSubpathResolveErrorKind {
#[error(transparent)] #[error(transparent)]
PkgJsonLoad(#[from] PackageJsonLoadError), PkgJsonLoad(#[from] PackageJsonLoadError),
#[error(transparent)] #[error(transparent)]
PackageFolderResolve(#[from] PackageFolderResolveError),
#[error(transparent)]
Exports(PackageExportsResolveError), Exports(PackageExportsResolveError),
#[error(transparent)] #[error(transparent)]
LegacyMain(LegacyMainResolveError), LegacyResolve(LegacyResolveError),
#[error(transparent)]
LegacyExact(PathToDeclarationUrlError),
} }
#[derive(Debug, Error)] #[derive(Debug, Error)]
@ -254,7 +284,7 @@ impl NodeJsErrorCoded for PackageTargetResolveError {
PackageTargetResolveErrorKind::InvalidPackageTarget(e) => e.code(), PackageTargetResolveErrorKind::InvalidPackageTarget(e) => e.code(),
PackageTargetResolveErrorKind::InvalidModuleSpecifier(e) => e.code(), PackageTargetResolveErrorKind::InvalidModuleSpecifier(e) => e.code(),
PackageTargetResolveErrorKind::PackageResolve(e) => e.code(), PackageTargetResolveErrorKind::PackageResolve(e) => e.code(),
PackageTargetResolveErrorKind::PathToDeclarationUrl(e) => e.code(), PackageTargetResolveErrorKind::TypesNotFound(e) => e.code(),
} }
} }
} }
@ -270,7 +300,7 @@ pub enum PackageTargetResolveErrorKind {
#[error(transparent)] #[error(transparent)]
PackageResolve(#[from] PackageResolveError), PackageResolve(#[from] PackageResolveError),
#[error(transparent)] #[error(transparent)]
PathToDeclarationUrl(#[from] PathToDeclarationUrlError), TypesNotFound(#[from] TypesNotFoundError),
} }
kinded_err!(PackageExportsResolveError, PackageExportsResolveErrorKind); kinded_err!(PackageExportsResolveError, PackageExportsResolveErrorKind);
@ -293,16 +323,23 @@ pub enum PackageExportsResolveErrorKind {
} }
#[derive(Debug, Error)] #[derive(Debug, Error)]
pub enum PathToDeclarationUrlError { #[error(
#[error(transparent)] "[{}] Could not find types for '{}'{}",
SubPath(#[from] PackageSubpathResolveError), self.code(),
self.0.code_specifier,
self.0.maybe_referrer.as_ref().map(|r| format!(" imported from '{}'", r)).unwrap_or_default(),
)]
pub struct TypesNotFoundError(pub Box<TypesNotFoundErrorData>);
#[derive(Debug)]
pub struct TypesNotFoundErrorData {
pub code_specifier: ModuleSpecifier,
pub maybe_referrer: Option<ModuleSpecifier>,
} }
impl NodeJsErrorCoded for PathToDeclarationUrlError { impl NodeJsErrorCoded for TypesNotFoundError {
fn code(&self) -> NodeJsErrorCode { fn code(&self) -> NodeJsErrorCode {
match self { NodeJsErrorCode::ERR_TYPES_NOT_FOUND
PathToDeclarationUrlError::SubPath(e) => e.code(),
}
} }
} }
@ -441,6 +478,7 @@ impl NodeJsErrorCoded for PackageResolveError {
match self.as_kind() { match self.as_kind() {
PackageResolveErrorKind::ClosestPkgJson(e) => e.code(), PackageResolveErrorKind::ClosestPkgJson(e) => e.code(),
PackageResolveErrorKind::InvalidModuleSpecifier(e) => e.code(), PackageResolveErrorKind::InvalidModuleSpecifier(e) => e.code(),
PackageResolveErrorKind::PackageFolderResolve(e) => e.code(),
PackageResolveErrorKind::ExportsResolve(e) => e.code(), PackageResolveErrorKind::ExportsResolve(e) => e.code(),
PackageResolveErrorKind::SubpathResolve(e) => e.code(), PackageResolveErrorKind::SubpathResolve(e) => e.code(),
} }
@ -454,37 +492,49 @@ pub enum PackageResolveErrorKind {
#[error(transparent)] #[error(transparent)]
InvalidModuleSpecifier(#[from] InvalidModuleSpecifierError), InvalidModuleSpecifier(#[from] InvalidModuleSpecifierError),
#[error(transparent)] #[error(transparent)]
PackageFolderResolve(#[from] PackageFolderResolveError),
#[error(transparent)]
ExportsResolve(#[from] PackageExportsResolveError), ExportsResolve(#[from] PackageExportsResolveError),
#[error(transparent)] #[error(transparent)]
SubpathResolve(#[from] PackageSubpathResolveError), SubpathResolve(#[from] PackageSubpathResolveError),
} }
#[derive(Debug, Error)] #[derive(Debug, Error)]
pub enum NodeResolveError { #[error("Failed joining '{path}' from '{base}'.")]
#[error("Failed joining '{path}' from '{base}'.")] pub struct NodeResolveRelativeJoinError {
RelativeJoinError { pub path: String,
path: String, pub base: ModuleSpecifier,
base: ModuleSpecifier, #[source]
#[source] pub source: url::ParseError,
source: url::ParseError, }
},
#[derive(Debug, Error)]
#[error("Failed resolving specifier from data url referrer.")]
pub struct DataUrlReferrerError {
#[source]
pub source: url::ParseError,
}
kinded_err!(NodeResolveError, NodeResolveErrorKind);
#[derive(Debug, Error)]
pub enum NodeResolveErrorKind {
#[error(transparent)]
RelativeJoin(#[from] NodeResolveRelativeJoinError),
#[error(transparent)] #[error(transparent)]
PackageImportsResolve(#[from] PackageImportsResolveError), PackageImportsResolve(#[from] PackageImportsResolveError),
#[error(transparent)] #[error(transparent)]
UnsupportedEsmUrlScheme(#[from] UnsupportedEsmUrlSchemeError), UnsupportedEsmUrlScheme(#[from] UnsupportedEsmUrlSchemeError),
#[error("Failed resolving specifier from data url referrer.")] #[error(transparent)]
DataUrlReferrerFailed { DataUrlReferrer(#[from] DataUrlReferrerError),
#[source]
source: url::ParseError,
},
#[error(transparent)] #[error(transparent)]
PackageResolve(#[from] PackageResolveError), PackageResolve(#[from] PackageResolveError),
#[error(transparent)] #[error(transparent)]
PathToDeclarationUrl(#[from] PathToDeclarationUrlError), TypesNotFound(#[from] TypesNotFoundError),
#[error(transparent)]
UrlToNodeResolution(#[from] UrlToNodeResolutionError),
#[error(transparent)] #[error(transparent)]
FinalizeResolution(#[from] FinalizeResolutionError), FinalizeResolution(#[from] FinalizeResolutionError),
#[error(transparent)]
UrlToNodeResolution(#[from] UrlToNodeResolutionError),
} }
kinded_err!(FinalizeResolutionError, FinalizeResolutionErrorKind); kinded_err!(FinalizeResolutionError, FinalizeResolutionErrorKind);
@ -499,6 +549,16 @@ pub enum FinalizeResolutionErrorKind {
UnsupportedDirImport(#[from] UnsupportedDirImportError), UnsupportedDirImport(#[from] UnsupportedDirImportError),
} }
impl NodeJsErrorCoded for FinalizeResolutionError {
fn code(&self) -> NodeJsErrorCode {
match self.as_kind() {
FinalizeResolutionErrorKind::InvalidModuleSpecifierError(e) => e.code(),
FinalizeResolutionErrorKind::ModuleNotFound(e) => e.code(),
FinalizeResolutionErrorKind::UnsupportedDirImport(e) => e.code(),
}
}
}
#[derive(Debug, Error)] #[derive(Debug, Error)]
#[error( #[error(
"[{}] Cannot find {} '{}'{}", "[{}] Cannot find {} '{}'{}",
@ -513,9 +573,9 @@ pub struct ModuleNotFoundError {
pub typ: &'static str, pub typ: &'static str,
} }
impl ModuleNotFoundError { impl NodeJsErrorCoded for ModuleNotFoundError {
pub fn code(&self) -> &'static str { fn code(&self) -> NodeJsErrorCode {
"ERR_MODULE_NOT_FOUND" NodeJsErrorCode::ERR_MODULE_NOT_FOUND
} }
} }

View file

@ -46,7 +46,6 @@ pub use path::PathClean;
pub use polyfill::is_builtin_node_module; pub use polyfill::is_builtin_node_module;
pub use polyfill::SUPPORTED_BUILTIN_NODE_MODULES; pub use polyfill::SUPPORTED_BUILTIN_NODE_MODULES;
pub use polyfill::SUPPORTED_BUILTIN_NODE_MODULES_WITH_PREFIX; pub use polyfill::SUPPORTED_BUILTIN_NODE_MODULES_WITH_PREFIX;
pub use resolution::parse_npm_pkg_name;
pub use resolution::NodeModuleKind; pub use resolution::NodeModuleKind;
pub use resolution::NodeResolution; pub use resolution::NodeResolution;
pub use resolution::NodeResolutionMode; pub use resolution::NodeResolutionMode;

View file

@ -18,14 +18,16 @@ use deno_package_json::PackageJsonRc;
use crate::errors; use crate::errors;
use crate::errors::CanonicalizingPkgJsonDirError; use crate::errors::CanonicalizingPkgJsonDirError;
use crate::errors::ClosestPkgJsonError; use crate::errors::ClosestPkgJsonError;
use crate::errors::DataUrlReferrerError;
use crate::errors::FinalizeResolutionError; use crate::errors::FinalizeResolutionError;
use crate::errors::InvalidModuleSpecifierError; use crate::errors::InvalidModuleSpecifierError;
use crate::errors::InvalidPackageTargetError; use crate::errors::InvalidPackageTargetError;
use crate::errors::LegacyMainResolveError; use crate::errors::LegacyResolveError;
use crate::errors::ModuleNotFoundError; use crate::errors::ModuleNotFoundError;
use crate::errors::NodeJsErrorCode; use crate::errors::NodeJsErrorCode;
use crate::errors::NodeJsErrorCoded; use crate::errors::NodeJsErrorCoded;
use crate::errors::NodeResolveError; use crate::errors::NodeResolveError;
use crate::errors::NodeResolveRelativeJoinError;
use crate::errors::PackageExportsResolveError; use crate::errors::PackageExportsResolveError;
use crate::errors::PackageImportNotDefinedError; use crate::errors::PackageImportNotDefinedError;
use crate::errors::PackageImportsResolveError; use crate::errors::PackageImportsResolveError;
@ -38,11 +40,12 @@ use crate::errors::PackageSubpathResolveErrorKind;
use crate::errors::PackageTargetNotFoundError; use crate::errors::PackageTargetNotFoundError;
use crate::errors::PackageTargetResolveError; use crate::errors::PackageTargetResolveError;
use crate::errors::PackageTargetResolveErrorKind; use crate::errors::PackageTargetResolveErrorKind;
use crate::errors::PathToDeclarationUrlError;
use crate::errors::ResolveBinaryCommandsError; use crate::errors::ResolveBinaryCommandsError;
use crate::errors::ResolvePkgJsonBinExportError; use crate::errors::ResolvePkgJsonBinExportError;
use crate::errors::ResolvePkgSubpathFromDenoModuleError; use crate::errors::ResolvePkgSubpathFromDenoModuleError;
use crate::errors::TypeScriptNotSupportedInNpmError; use crate::errors::TypeScriptNotSupportedInNpmError;
use crate::errors::TypesNotFoundError;
use crate::errors::TypesNotFoundErrorData;
use crate::errors::UnsupportedDirImportError; use crate::errors::UnsupportedDirImportError;
use crate::errors::UnsupportedEsmUrlSchemeError; use crate::errors::UnsupportedEsmUrlSchemeError;
use crate::errors::UrlToNodeResolutionError; use crate::errors::UrlToNodeResolutionError;
@ -55,6 +58,7 @@ use crate::PathClean;
pub static DEFAULT_CONDITIONS: &[&str] = &["deno", "node", "import"]; pub static DEFAULT_CONDITIONS: &[&str] = &["deno", "node", "import"];
pub static REQUIRE_CONDITIONS: &[&str] = &["require", "node"]; pub static REQUIRE_CONDITIONS: &[&str] = &["require", "node"];
static TYPES_ONLY_CONDITIONS: &[&str] = &["types"];
pub type NodeModuleKind = deno_package_json::NodeModuleKind; pub type NodeModuleKind = deno_package_json::NodeModuleKind;
@ -177,23 +181,23 @@ impl NodeResolver {
referrer: &ModuleSpecifier, referrer: &ModuleSpecifier,
referrer_kind: NodeModuleKind, referrer_kind: NodeModuleKind,
mode: NodeResolutionMode, mode: NodeResolutionMode,
) -> Result<Option<NodeResolution>, NodeResolveError> { ) -> Result<NodeResolution, NodeResolveError> {
// Note: if we are here, then the referrer is an esm module // 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 // TODO(bartlomieju): skipped "policy" part as we don't plan to support it
if crate::is_builtin_node_module(specifier) { if crate::is_builtin_node_module(specifier) {
return Ok(Some(NodeResolution::BuiltIn(specifier.to_string()))); return Ok(NodeResolution::BuiltIn(specifier.to_string()));
} }
if let Ok(url) = Url::parse(specifier) { if let Ok(url) = Url::parse(specifier) {
if url.scheme() == "data" { if url.scheme() == "data" {
return Ok(Some(NodeResolution::Esm(url))); return Ok(NodeResolution::Esm(url));
} }
if let Some(module_name) = if let Some(module_name) =
get_module_name_from_builtin_node_module_specifier(&url) get_module_name_from_builtin_node_module_specifier(&url)
{ {
return Ok(Some(NodeResolution::BuiltIn(module_name.to_string()))); return Ok(NodeResolution::BuiltIn(module_name.to_string()));
} }
let protocol = url.scheme(); let protocol = url.scheme();
@ -209,14 +213,14 @@ impl NodeResolver {
// todo(dsherret): this seems wrong // todo(dsherret): this seems wrong
if referrer.scheme() == "data" { if referrer.scheme() == "data" {
let url = referrer.join(specifier).map_err(|source| { let url = referrer
NodeResolveError::DataUrlReferrerFailed { source } .join(specifier)
})?; .map_err(|source| DataUrlReferrerError { source })?;
return Ok(Some(NodeResolution::Esm(url))); return Ok(NodeResolution::Esm(url));
} }
} }
let maybe_url = self.module_resolve( let url = self.module_resolve(
specifier, specifier,
referrer, referrer,
referrer_kind, referrer_kind,
@ -226,15 +230,19 @@ impl NodeResolver {
}, },
mode, mode,
)?; )?;
let url = match maybe_url {
Some(url) => url, let url = if mode.is_types() {
None => return Ok(None), let file_path = to_file_path(&url);
self.path_to_declaration_url(&file_path, Some(referrer), referrer_kind)?
} else {
url
}; };
let url = self.finalize_resolution(url, Some(referrer))?;
let resolve_response = self.url_to_node_resolution(url)?; let resolve_response = self.url_to_node_resolution(url)?;
// TODO(bartlomieju): skipped checking errors for commonJS resolution and // TODO(bartlomieju): skipped checking errors for commonJS resolution and
// "preserveSymlinksMain"/"preserveSymlinks" options. // "preserveSymlinksMain"/"preserveSymlinks" options.
Ok(Some(resolve_response)) Ok(resolve_response)
} }
fn module_resolve( fn module_resolve(
@ -244,12 +252,10 @@ impl NodeResolver {
referrer_kind: NodeModuleKind, referrer_kind: NodeModuleKind,
conditions: &[&str], conditions: &[&str],
mode: NodeResolutionMode, mode: NodeResolutionMode,
) -> Result<Option<ModuleSpecifier>, NodeResolveError> { ) -> Result<ModuleSpecifier, NodeResolveError> {
// note: if we're here, the referrer is an esm module if should_be_treated_as_relative_or_absolute_path(specifier) {
let maybe_url = if should_be_treated_as_relative_or_absolute_path(specifier) Ok(referrer.join(specifier).map_err(|err| {
{ NodeResolveRelativeJoinError {
Some(referrer.join(specifier).map_err(|err| {
NodeResolveError::RelativeJoinError {
path: specifier.to_string(), path: specifier.to_string(),
base: referrer.clone(), base: referrer.clone(),
source: err, source: err,
@ -260,7 +266,7 @@ impl NodeResolver {
.get_closest_package_json(referrer) .get_closest_package_json(referrer)
.map_err(PackageImportsResolveErrorKind::ClosestPkgJson) .map_err(PackageImportsResolveErrorKind::ClosestPkgJson)
.map_err(|err| PackageImportsResolveError(Box::new(err)))?; .map_err(|err| PackageImportsResolveError(Box::new(err)))?;
Some(self.package_imports_resolve( Ok(self.package_imports_resolve(
specifier, specifier,
Some(referrer), Some(referrer),
referrer_kind, referrer_kind,
@ -269,32 +275,16 @@ impl NodeResolver {
mode, mode,
)?) )?)
} else if let Ok(resolved) = Url::parse(specifier) { } else if let Ok(resolved) = Url::parse(specifier) {
Some(resolved) Ok(resolved)
} else { } else {
self.package_resolve( Ok(self.package_resolve(
specifier, specifier,
referrer, referrer,
referrer_kind, referrer_kind,
conditions, conditions,
mode, mode,
)? )?)
}; }
let Some(url) = maybe_url else {
return Ok(None);
};
let maybe_url = if mode.is_types() {
let file_path = to_file_path(&url);
self.path_to_declaration_url(&file_path, Some(referrer), referrer_kind)?
} else {
Some(url)
};
Ok(match maybe_url {
Some(url) => Some(self.finalize_resolution(url, Some(referrer))?),
None => None,
})
} }
fn finalize_resolution( fn finalize_resolution(
@ -369,12 +359,12 @@ impl NodeResolver {
package_subpath: Option<&str>, package_subpath: Option<&str>,
maybe_referrer: Option<&ModuleSpecifier>, maybe_referrer: Option<&ModuleSpecifier>,
mode: NodeResolutionMode, mode: NodeResolutionMode,
) -> Result<Option<NodeResolution>, ResolvePkgSubpathFromDenoModuleError> { ) -> Result<NodeResolution, ResolvePkgSubpathFromDenoModuleError> {
let node_module_kind = NodeModuleKind::Esm; let node_module_kind = NodeModuleKind::Esm;
let package_subpath = package_subpath let package_subpath = package_subpath
.map(|s| format!("./{s}")) .map(|s| format!("./{s}"))
.unwrap_or_else(|| ".".to_string()); .unwrap_or_else(|| ".".to_string());
let maybe_resolved_url = self.resolve_package_dir_subpath( let resolved_url = self.resolve_package_dir_subpath(
package_dir, package_dir,
&package_subpath, &package_subpath,
maybe_referrer, maybe_referrer,
@ -382,14 +372,10 @@ impl NodeResolver {
DEFAULT_CONDITIONS, DEFAULT_CONDITIONS,
mode, mode,
)?; )?;
let resolved_url = match maybe_resolved_url {
Some(resolved_url) => resolved_url,
None => return Ok(None),
};
let resolve_response = self.url_to_node_resolution(resolved_url)?; let resolve_response = self.url_to_node_resolution(resolved_url)?;
// TODO(bartlomieju): skipped checking errors for commonJS resolution and // TODO(bartlomieju): skipped checking errors for commonJS resolution and
// "preserveSymlinksMain"/"preserveSymlinks" options. // "preserveSymlinksMain"/"preserveSymlinks" options.
Ok(Some(resolve_response)) Ok(resolve_response)
} }
pub fn resolve_binary_commands( pub fn resolve_binary_commands(
@ -475,7 +461,7 @@ impl NodeResolver {
path: &Path, path: &Path,
maybe_referrer: Option<&ModuleSpecifier>, maybe_referrer: Option<&ModuleSpecifier>,
referrer_kind: NodeModuleKind, referrer_kind: NodeModuleKind,
) -> Result<Option<ModuleSpecifier>, PathToDeclarationUrlError> { ) -> Result<ModuleSpecifier, TypesNotFoundError> {
fn probe_extensions( fn probe_extensions(
fs: &dyn deno_fs::FileSystem, fs: &dyn deno_fs::FileSystem,
path: &Path, path: &Path,
@ -525,15 +511,15 @@ impl NodeResolver {
|| lowercase_path.ends_with(".d.cts") || lowercase_path.ends_with(".d.cts")
|| lowercase_path.ends_with(".d.mts") || lowercase_path.ends_with(".d.mts")
{ {
return Ok(Some(to_file_specifier(path))); return Ok(to_file_specifier(path));
} }
if let Some(path) = if let Some(path) =
probe_extensions(&*self.fs, path, &lowercase_path, referrer_kind) probe_extensions(&*self.fs, path, &lowercase_path, referrer_kind)
{ {
return Ok(Some(to_file_specifier(&path))); return Ok(to_file_specifier(&path));
} }
if self.fs.is_dir_sync(path) { if self.fs.is_dir_sync(path) {
let maybe_resolution = self.resolve_package_dir_subpath( let resolution_result = self.resolve_package_dir_subpath(
path, path,
/* sub path */ ".", /* sub path */ ".",
maybe_referrer, maybe_referrer,
@ -543,9 +529,9 @@ impl NodeResolver {
NodeModuleKind::Cjs => REQUIRE_CONDITIONS, NodeModuleKind::Cjs => REQUIRE_CONDITIONS,
}, },
NodeResolutionMode::Types, NodeResolutionMode::Types,
)?; );
if let Some(resolution) = maybe_resolution { if let Ok(resolution) = resolution_result {
return Ok(Some(resolution)); return Ok(resolution);
} }
let index_path = path.join("index.js"); let index_path = path.join("index.js");
if let Some(path) = probe_extensions( if let Some(path) = probe_extensions(
@ -554,14 +540,17 @@ impl NodeResolver {
&index_path.to_string_lossy().to_lowercase(), &index_path.to_string_lossy().to_lowercase(),
referrer_kind, referrer_kind,
) { ) {
return Ok(Some(to_file_specifier(&path))); return Ok(to_file_specifier(&path));
} }
} }
// allow resolving .css files for types resolution // allow resolving .css files for types resolution
if lowercase_path.ends_with(".css") { if lowercase_path.ends_with(".css") {
return Ok(Some(to_file_specifier(path))); return Ok(to_file_specifier(path));
} }
Ok(None) Err(TypesNotFoundError(Box::new(TypesNotFoundErrorData {
code_specifier: to_file_specifier(path),
maybe_referrer: maybe_referrer.cloned(),
})))
} }
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
@ -719,22 +708,32 @@ impl NodeResolver {
conditions, conditions,
mode, mode,
) { ) {
Ok(Some(url)) => Ok(url), Ok(url) => Ok(url),
Ok(None) => Err( Err(err) => match err.code() {
PackageTargetResolveErrorKind::NotFound( NodeJsErrorCode::ERR_INVALID_MODULE_SPECIFIER
PackageTargetNotFoundError { | NodeJsErrorCode::ERR_INVALID_PACKAGE_CONFIG
pkg_json_path: package_json_path.to_path_buf(), | NodeJsErrorCode::ERR_INVALID_PACKAGE_TARGET
target: export_target.to_string(), | NodeJsErrorCode::ERR_PACKAGE_IMPORT_NOT_DEFINED
maybe_referrer: maybe_referrer.map(ToOwned::to_owned), | NodeJsErrorCode::ERR_PACKAGE_PATH_NOT_EXPORTED
referrer_kind, | NodeJsErrorCode::ERR_UNKNOWN_FILE_EXTENSION
mode, | NodeJsErrorCode::ERR_UNSUPPORTED_DIR_IMPORT
}, | NodeJsErrorCode::ERR_UNSUPPORTED_ESM_URL_SCHEME
) | NodeJsErrorCode::ERR_TYPES_NOT_FOUND => {
.into(), Err(PackageTargetResolveErrorKind::PackageResolve(err).into())
), }
Err(err) => { NodeJsErrorCode::ERR_MODULE_NOT_FOUND => Err(
Err(PackageTargetResolveErrorKind::PackageResolve(err).into()) 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(),
),
},
}; };
return match result { return match result {
@ -831,6 +830,62 @@ impl NodeResolver {
internal: bool, internal: bool,
conditions: &[&str], conditions: &[&str],
mode: NodeResolutionMode, mode: NodeResolutionMode,
) -> Result<Option<ModuleSpecifier>, PackageTargetResolveError> {
let result = self.resolve_package_target_inner(
package_json_path,
target,
subpath,
package_subpath,
maybe_referrer,
referrer_kind,
pattern,
internal,
conditions,
mode,
);
match result {
Ok(maybe_resolved) => Ok(maybe_resolved),
Err(err) => {
if mode.is_types()
&& err.code() == NodeJsErrorCode::ERR_TYPES_NOT_FOUND
&& conditions != TYPES_ONLY_CONDITIONS
{
// try resolving with just "types" conditions for when someone misconfigures
// and puts the "types" condition in the wrong place
if let Ok(Some(resolved)) = self.resolve_package_target_inner(
package_json_path,
target,
subpath,
package_subpath,
maybe_referrer,
referrer_kind,
pattern,
internal,
TYPES_ONLY_CONDITIONS,
mode,
) {
return Ok(Some(resolved));
}
}
Err(err)
}
}
}
#[allow(clippy::too_many_arguments)]
fn resolve_package_target_inner(
&self,
package_json_path: &Path,
target: &Value,
subpath: &str,
package_subpath: &str,
maybe_referrer: Option<&ModuleSpecifier>,
referrer_kind: NodeModuleKind,
pattern: bool,
internal: bool,
conditions: &[&str],
mode: NodeResolutionMode,
) -> Result<Option<ModuleSpecifier>, PackageTargetResolveError> { ) -> Result<Option<ModuleSpecifier>, PackageTargetResolveError> {
if let Some(target) = target.as_str() { if let Some(target) = target.as_str() {
let url = self.resolve_package_target_string( let url = self.resolve_package_target_string(
@ -847,11 +902,11 @@ impl NodeResolver {
)?; )?;
if mode.is_types() && url.scheme() == "file" { if mode.is_types() && url.scheme() == "file" {
let path = url.to_file_path().unwrap(); let path = url.to_file_path().unwrap();
return Ok(self.path_to_declaration_url( return Ok(Some(self.path_to_declaration_url(
&path, &path,
maybe_referrer, maybe_referrer,
referrer_kind, referrer_kind,
)?); )?));
} else { } else {
return Ok(Some(url)); return Ok(Some(url));
} }
@ -1069,41 +1124,37 @@ impl NodeResolver {
referrer_kind: NodeModuleKind, referrer_kind: NodeModuleKind,
conditions: &[&str], conditions: &[&str],
mode: NodeResolutionMode, mode: NodeResolutionMode,
) -> Result<Option<ModuleSpecifier>, PackageResolveError> { ) -> Result<ModuleSpecifier, PackageResolveError> {
let (package_name, package_subpath, _is_scoped) = let (package_name, package_subpath, _is_scoped) =
parse_npm_pkg_name(specifier, referrer)?; parse_npm_pkg_name(specifier, referrer)?;
let Some(package_config) = self.get_closest_package_json(referrer)? else { if let Some(package_config) = self.get_closest_package_json(referrer)? {
return Ok(None); // ResolveSelf
}; if package_config.name.as_ref() == Some(&package_name) {
// ResolveSelf if let Some(exports) = &package_config.exports {
if package_config.name.as_ref() == Some(&package_name) { return self
if let Some(exports) = &package_config.exports { .package_exports_resolve(
return self &package_config.path,
.package_exports_resolve( &package_subpath,
&package_config.path, exports,
&package_subpath, Some(referrer),
exports, referrer_kind,
Some(referrer), conditions,
referrer_kind, mode,
conditions, )
mode, .map_err(|err| err.into());
) }
.map(Some)
.map_err(|err| err.into());
} }
} }
self self.resolve_package_subpath_for_package(
.resolve_package_subpath_for_package( &package_name,
&package_name, &package_subpath,
&package_subpath, referrer,
referrer, referrer_kind,
referrer_kind, conditions,
conditions, mode,
mode, )
)
.map_err(|err| err.into())
} }
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
@ -1115,7 +1166,7 @@ impl NodeResolver {
referrer_kind: NodeModuleKind, referrer_kind: NodeModuleKind,
conditions: &[&str], conditions: &[&str],
mode: NodeResolutionMode, mode: NodeResolutionMode,
) -> Result<Option<ModuleSpecifier>, PackageSubpathResolveError> { ) -> Result<ModuleSpecifier, PackageResolveError> {
let result = self.resolve_package_subpath_for_package_inner( let result = self.resolve_package_subpath_for_package_inner(
package_name, package_name,
package_subpath, package_subpath,
@ -1124,10 +1175,10 @@ impl NodeResolver {
conditions, conditions,
mode, mode,
); );
if mode.is_types() && !matches!(result, Ok(Some(_))) { if mode.is_types() && !matches!(result, Ok(ModuleSpecifier { .. })) {
// try to resolve with the @types package // try to resolve with the @types package
let package_name = types_package_name(package_name); let package_name = types_package_name(package_name);
if let Ok(Some(result)) = self.resolve_package_subpath_for_package_inner( if let Ok(result) = self.resolve_package_subpath_for_package_inner(
&package_name, &package_name,
package_subpath, package_subpath,
referrer, referrer,
@ -1135,7 +1186,7 @@ impl NodeResolver {
conditions, conditions,
mode, mode,
) { ) {
return Ok(Some(result)); return Ok(result);
} }
} }
result result
@ -1150,7 +1201,7 @@ impl NodeResolver {
referrer_kind: NodeModuleKind, referrer_kind: NodeModuleKind,
conditions: &[&str], conditions: &[&str],
mode: NodeResolutionMode, mode: NodeResolutionMode,
) -> Result<Option<ModuleSpecifier>, PackageSubpathResolveError> { ) -> Result<ModuleSpecifier, PackageResolveError> {
let package_dir_path = self let package_dir_path = self
.npm_resolver .npm_resolver
.resolve_package_folder_from_package(package_name, referrer)?; .resolve_package_folder_from_package(package_name, referrer)?;
@ -1169,14 +1220,16 @@ impl NodeResolver {
// )) // ))
// Package match. // Package match.
self.resolve_package_dir_subpath( self
&package_dir_path, .resolve_package_dir_subpath(
package_subpath, &package_dir_path,
Some(referrer), package_subpath,
referrer_kind, Some(referrer),
conditions, referrer_kind,
mode, conditions,
) mode,
)
.map_err(|err| err.into())
} }
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
@ -1188,7 +1241,7 @@ impl NodeResolver {
referrer_kind: NodeModuleKind, referrer_kind: NodeModuleKind,
conditions: &[&str], conditions: &[&str],
mode: NodeResolutionMode, mode: NodeResolutionMode,
) -> Result<Option<ModuleSpecifier>, PackageSubpathResolveError> { ) -> Result<ModuleSpecifier, PackageSubpathResolveError> {
let package_json_path = package_dir_path.join("package.json"); let package_json_path = package_dir_path.join("package.json");
match self.load_package_json(&package_json_path)? { match self.load_package_json(&package_json_path)? {
Some(pkg_json) => self.resolve_package_subpath( Some(pkg_json) => self.resolve_package_subpath(
@ -1207,7 +1260,9 @@ impl NodeResolver {
referrer_kind, referrer_kind,
mode, mode,
) )
.map_err(|err| PackageSubpathResolveErrorKind::LegacyExact(err).into()), .map_err(|err| {
PackageSubpathResolveErrorKind::LegacyResolve(err).into()
}),
} }
} }
@ -1220,7 +1275,7 @@ impl NodeResolver {
referrer_kind: NodeModuleKind, referrer_kind: NodeModuleKind,
conditions: &[&str], conditions: &[&str],
mode: NodeResolutionMode, mode: NodeResolutionMode,
) -> Result<Option<ModuleSpecifier>, PackageSubpathResolveError> { ) -> Result<ModuleSpecifier, PackageSubpathResolveError> {
if let Some(exports) = &package_json.exports { if let Some(exports) = &package_json.exports {
let result = self.package_exports_resolve( let result = self.package_exports_resolve(
&package_json.path, &package_json.path,
@ -1232,13 +1287,13 @@ impl NodeResolver {
mode, mode,
); );
match result { match result {
Ok(found) => return Ok(Some(found)), Ok(found) => return Ok(found),
Err(exports_err) => { Err(exports_err) => {
if mode.is_types() && package_subpath == "." { if mode.is_types() && package_subpath == "." {
return self return self
.legacy_main_resolve(package_json, referrer, referrer_kind, mode) .legacy_main_resolve(package_json, referrer, referrer_kind, mode)
.map_err(|err| { .map_err(|err| {
PackageSubpathResolveErrorKind::LegacyMain(err).into() PackageSubpathResolveErrorKind::LegacyResolve(err).into()
}); });
} }
return Err( return Err(
@ -1251,7 +1306,9 @@ impl NodeResolver {
if package_subpath == "." { if package_subpath == "." {
return self return self
.legacy_main_resolve(package_json, referrer, referrer_kind, mode) .legacy_main_resolve(package_json, referrer, referrer_kind, mode)
.map_err(|err| PackageSubpathResolveErrorKind::LegacyMain(err).into()); .map_err(|err| {
PackageSubpathResolveErrorKind::LegacyResolve(err).into()
});
} }
self self
@ -1262,7 +1319,9 @@ impl NodeResolver {
referrer_kind, referrer_kind,
mode, mode,
) )
.map_err(|err| PackageSubpathResolveErrorKind::LegacyExact(err).into()) .map_err(|err| {
PackageSubpathResolveErrorKind::LegacyResolve(err.into()).into()
})
} }
fn resolve_subpath_exact( fn resolve_subpath_exact(
@ -1272,13 +1331,13 @@ impl NodeResolver {
referrer: Option<&ModuleSpecifier>, referrer: Option<&ModuleSpecifier>,
referrer_kind: NodeModuleKind, referrer_kind: NodeModuleKind,
mode: NodeResolutionMode, mode: NodeResolutionMode,
) -> Result<Option<ModuleSpecifier>, PathToDeclarationUrlError> { ) -> Result<ModuleSpecifier, TypesNotFoundError> {
assert_ne!(package_subpath, "."); assert_ne!(package_subpath, ".");
let file_path = directory.join(package_subpath); let file_path = directory.join(package_subpath);
if mode.is_types() { if mode.is_types() {
Ok(self.path_to_declaration_url(&file_path, referrer, referrer_kind)?) Ok(self.path_to_declaration_url(&file_path, referrer, referrer_kind)?)
} else { } else {
Ok(Some(to_file_specifier(&file_path))) Ok(to_file_specifier(&file_path))
} }
} }
@ -1286,20 +1345,22 @@ impl NodeResolver {
&self, &self,
directory: &Path, directory: &Path,
package_subpath: &str, package_subpath: &str,
referrer: Option<&ModuleSpecifier>, maybe_referrer: Option<&ModuleSpecifier>,
referrer_kind: NodeModuleKind, referrer_kind: NodeModuleKind,
mode: NodeResolutionMode, mode: NodeResolutionMode,
) -> Result<Option<ModuleSpecifier>, PathToDeclarationUrlError> { ) -> Result<ModuleSpecifier, LegacyResolveError> {
if package_subpath == "." { if package_subpath == "." {
Ok(self.legacy_index_resolve(directory, referrer_kind, mode)) self.legacy_index_resolve(directory, maybe_referrer, referrer_kind, mode)
} else { } else {
self.resolve_subpath_exact( self
directory, .resolve_subpath_exact(
package_subpath, directory,
referrer, package_subpath,
referrer_kind, maybe_referrer,
mode, referrer_kind,
) mode,
)
.map_err(|err| err.into())
} }
} }
@ -1348,7 +1409,7 @@ impl NodeResolver {
maybe_referrer: Option<&ModuleSpecifier>, maybe_referrer: Option<&ModuleSpecifier>,
referrer_kind: NodeModuleKind, referrer_kind: NodeModuleKind,
mode: NodeResolutionMode, mode: NodeResolutionMode,
) -> Result<Option<ModuleSpecifier>, LegacyMainResolveError> { ) -> Result<ModuleSpecifier, LegacyResolveError> {
let maybe_main = if mode.is_types() { let maybe_main = if mode.is_types() {
match package_json.types.as_ref() { match package_json.types.as_ref() {
Some(types) => Some(types.as_str()), Some(types) => Some(types.as_str()),
@ -1357,11 +1418,14 @@ impl NodeResolver {
// a corresponding declaration file // a corresponding declaration file
if let Some(main) = package_json.main(referrer_kind) { if let Some(main) = package_json.main(referrer_kind) {
let main = package_json.path.parent().unwrap().join(main).clean(); let main = package_json.path.parent().unwrap().join(main).clean();
let maybe_decl_url = self let decl_url_result = self.path_to_declaration_url(
.path_to_declaration_url(&main, maybe_referrer, referrer_kind) &main,
.map_err(LegacyMainResolveError::PathToDeclarationUrl)?; maybe_referrer,
if let Some(path) = maybe_decl_url { referrer_kind,
return Ok(Some(path)); );
// don't surface errors, fallback to checking the index now
if let Ok(url) = decl_url_result {
return Ok(url);
} }
} }
None None
@ -1374,7 +1438,7 @@ impl NodeResolver {
if let Some(main) = maybe_main { if let Some(main) = maybe_main {
let guess = package_json.path.parent().unwrap().join(main).clean(); let guess = package_json.path.parent().unwrap().join(main).clean();
if self.fs.is_file_sync(&guess) { if self.fs.is_file_sync(&guess) {
return Ok(Some(to_file_specifier(&guess))); return Ok(to_file_specifier(&guess));
} }
// todo(dsherret): investigate exactly how node and typescript handles this // todo(dsherret): investigate exactly how node and typescript handles this
@ -1404,24 +1468,26 @@ impl NodeResolver {
.clean(); .clean();
if self.fs.is_file_sync(&guess) { if self.fs.is_file_sync(&guess) {
// TODO(bartlomieju): emitLegacyIndexDeprecation() // TODO(bartlomieju): emitLegacyIndexDeprecation()
return Ok(Some(to_file_specifier(&guess))); return Ok(to_file_specifier(&guess));
} }
} }
} }
Ok(self.legacy_index_resolve( self.legacy_index_resolve(
package_json.path.parent().unwrap(), package_json.path.parent().unwrap(),
maybe_referrer,
referrer_kind, referrer_kind,
mode, mode,
)) )
} }
fn legacy_index_resolve( fn legacy_index_resolve(
&self, &self,
directory: &Path, directory: &Path,
maybe_referrer: Option<&ModuleSpecifier>,
referrer_kind: NodeModuleKind, referrer_kind: NodeModuleKind,
mode: NodeResolutionMode, mode: NodeResolutionMode,
) -> Option<ModuleSpecifier> { ) -> Result<ModuleSpecifier, LegacyResolveError> {
let index_file_names = if mode.is_types() { let index_file_names = if mode.is_types() {
// todo(dsherret): investigate exactly how typescript does this // todo(dsherret): investigate exactly how typescript does this
match referrer_kind { match referrer_kind {
@ -1435,11 +1501,28 @@ impl NodeResolver {
let guess = directory.join(index_file_name).clean(); let guess = directory.join(index_file_name).clean();
if self.fs.is_file_sync(&guess) { if self.fs.is_file_sync(&guess) {
// TODO(bartlomieju): emitLegacyIndexDeprecation() // TODO(bartlomieju): emitLegacyIndexDeprecation()
return Some(to_file_specifier(&guess)); return Ok(to_file_specifier(&guess));
} }
} }
None if mode.is_types() {
Err(
TypesNotFoundError(Box::new(TypesNotFoundErrorData {
code_specifier: to_file_specifier(&directory.join("index.js")),
maybe_referrer: maybe_referrer.cloned(),
}))
.into(),
)
} else {
Err(
ModuleNotFoundError {
specifier: to_file_specifier(&directory.join("index.js")),
typ: "module",
maybe_referrer: maybe_referrer.cloned(),
}
.into(),
)
}
} }
} }
@ -1557,7 +1640,7 @@ fn should_be_treated_as_relative_or_absolute_path(specifier: &str) -> bool {
// TODO(ry) We very likely have this utility function elsewhere in Deno. // TODO(ry) We very likely have this utility function elsewhere in Deno.
fn is_relative_specifier(specifier: &str) -> bool { fn is_relative_specifier(specifier: &str) -> bool {
let specifier_len = specifier.len(); let specifier_len = specifier.len();
let specifier_chars: Vec<_> = specifier.chars().collect(); let specifier_chars: Vec<_> = specifier.chars().take(3).collect();
if !specifier_chars.is_empty() && specifier_chars[0] == '.' { if !specifier_chars.is_empty() && specifier_chars[0] == '.' {
if specifier_len == 1 || specifier_chars[1] == '/' { if specifier_len == 1 || specifier_chars[1] == '/' {

View file

@ -15,13 +15,6 @@ use util::TestContextBuilder;
// NOTE: See how to make test npm packages at ./testdata/npm/README.md // NOTE: See how to make test npm packages at ./testdata/npm/README.md
itest!(esm_import_cjs_default {
args: "run --allow-read --allow-env --quiet --check=all npm/esm_import_cjs_default/main.ts",
output: "npm/esm_import_cjs_default/main.out",
envs: env_vars_for_npm_tests(),
http_server: true,
});
itest!(cjs_with_deps { itest!(cjs_with_deps {
args: "run --allow-read --allow-env npm/cjs_with_deps/main.js", args: "run --allow-read --allow-env npm/cjs_with_deps/main.js",
output: "npm/cjs_with_deps/main.out", output: "npm/cjs_with_deps/main.out",
@ -324,14 +317,6 @@ itest!(check_local {
exit_code: 1, exit_code: 1,
}); });
itest!(types_general {
args: "check --quiet npm/types/main.ts",
output: "npm/types/main.out",
envs: env_vars_for_npm_tests(),
http_server: true,
exit_code: 1,
});
itest!(types_ambient_module { itest!(types_ambient_module {
args: "check --quiet npm/types_ambient_module/main.ts", args: "check --quiet npm/types_ambient_module/main.ts",
output: "npm/types_ambient_module/main.out", output: "npm/types_ambient_module/main.out",
@ -341,27 +326,11 @@ itest!(types_ambient_module {
}); });
itest!(types_ambient_module_import_map { itest!(types_ambient_module_import_map {
args: "check --quiet --import-map=npm/types_ambient_module/import_map.json npm/types_ambient_module/main_import_map.ts", args: "check --quiet --import-map=npm/types_ambient_module/import_map.json npm/types_ambient_module/main_import_map.ts",
output: "npm/types_ambient_module/main_import_map.out", output: "npm/types_ambient_module/main_import_map.out",
envs: env_vars_for_npm_tests(),
http_server: true,
exit_code: 1,
});
itest!(no_types_cjs {
args: "check --quiet npm/no_types_cjs/main.ts",
output_str: Some(""),
exit_code: 0,
envs: env_vars_for_npm_tests(),
http_server: true,
});
itest!(no_types_in_conditional_exports {
args: "run --check npm/no_types_in_conditional_exports/main.ts",
output: "npm/no_types_in_conditional_exports/main.out",
exit_code: 0,
envs: env_vars_for_npm_tests(), envs: env_vars_for_npm_tests(),
http_server: true, http_server: true,
exit_code: 1,
}); });
itest!(types_entry_value_not_exists { itest!(types_entry_value_not_exists {

View file

@ -0,0 +1,13 @@
{
"tests": {
"bad_import": {
"args": "run bad_import.ts",
"output": "bad_import.out",
"exitCode": 1
},
"good_import": {
"args": "run good_import.ts",
"output": "good_import.out"
}
}
}

View file

@ -0,0 +1,2 @@
error: [ERR_MODULE_NOT_FOUND] Cannot find module 'file:///[WILDLINE]/node_modules/package/index.js' imported from 'file:///[WILDLINE]/bad_import.ts'
at file:///[WILDLINE]/bad_import.ts:1:16

View file

@ -0,0 +1,3 @@
import hi from "package";
hi();

View file

@ -0,0 +1,3 @@
{
"unstable": ["byonm"]
}

View file

@ -0,0 +1 @@
hi

View file

@ -0,0 +1,3 @@
import hi from "package/main.js";
hi();

View file

@ -0,0 +1 @@
module.exports = () => console.log('hi');

View file

@ -0,0 +1,4 @@
{
"name": "package",
"version": "1.0.0"
}

View file

@ -0,0 +1,2 @@
{
}

View file

@ -0,0 +1,4 @@
{
"args": "run --allow-read --allow-env --check=all main.ts",
"output": "main.out"
}

View file

@ -1,3 +1,10 @@
[UNORDERED_START]
Download http://localhost:4260/@denotest/esm-import-cjs-default
Download http://localhost:4260/@denotest/cjs-default-export
Download http://localhost:4260/@denotest/cjs-default-export/1.0.0.tgz
Download http://localhost:4260/@denotest/esm-import-cjs-default/1.0.0.tgz
[UNORDERED_END]
Check file:///[WILDLINE]/main.ts
Node esm importing node cjs Node esm importing node cjs
=========================== ===========================
{ {

View file

@ -0,0 +1,4 @@
{
"args": "check main.ts",
"output": "main.out"
}

View file

@ -0,0 +1,3 @@
Download http://localhost:4260/@denotest/no-types-cjs
Download http://localhost:4260/@denotest/no-types-cjs/1.0.0.tgz
Check file:///[WILDLINE]/no_types_cjs/main.ts

View file

@ -0,0 +1,4 @@
{
"args": "run --check main.ts",
"output": "main.out"
}

View file

@ -0,0 +1,5 @@
{
"args": "check main.ts",
"output": "main.out",
"exitCode": 1
}

View file

@ -1,7 +1,16 @@
[UNORDERED_START]
Download http://localhost:4260/@denotest/types
Download http://localhost:4260/@denotest/types_imported
Download http://localhost:4260/@denotest/types-exports-subpaths
Download http://localhost:4260/@denotest/types_imported/1.0.0.tgz
Download http://localhost:4260/@denotest/types-exports-subpaths/1.0.0.tgz
Download http://localhost:4260/@denotest/types/1.0.0.tgz
[UNORDERED_END]
Check file:///[WILDLINE]/main.ts
error: TS2322 [ERROR]: Type 'number' is not assignable to type 'string'. error: TS2322 [ERROR]: Type 'number' is not assignable to type 'string'.
bar: 1, bar: 1,
~~~ ~~~
at [WILDCARD]/npm/types/main.ts:[WILDCARD] at [WILDCARD]/main.ts:[WILDCARD]
The expected type comes from property 'bar' which is declared here on type 'Foobar' The expected type comes from property 'bar' which is declared here on type 'Foobar'
bar: string; bar: string;
@ -11,7 +20,7 @@ error: TS2322 [ERROR]: Type 'number' is not assignable to type 'string'.
TS2322 [ERROR]: Type 'number' is not assignable to type 'string'. TS2322 [ERROR]: Type 'number' is not assignable to type 'string'.
prop: 1, prop: 1,
~~~~ ~~~~
at [WILDCARD]/npm/types/main.ts:[WILDCARD] at [WILDCARD]/main.ts:[WILDCARD]
The expected type comes from property 'prop' which is declared here on type 'SomeInterface' The expected type comes from property 'prop' which is declared here on type 'SomeInterface'
prop: string; prop: string;
@ -21,7 +30,7 @@ TS2322 [ERROR]: Type 'number' is not assignable to type 'string'.
TS2322 [ERROR]: Type 'string' is not assignable to type 'number'. TS2322 [ERROR]: Type 'string' is not assignable to type 'number'.
prop2: "asdf", prop2: "asdf",
~~~~~ ~~~~~
at [WILDCARD]/npm/types/main.ts:[WILDCARD] at [WILDCARD]/main.ts:[WILDCARD]
The expected type comes from property 'prop2' which is declared here on type 'SomeInterface' The expected type comes from property 'prop2' which is declared here on type 'SomeInterface'
prop2: number; prop2: number;
@ -31,7 +40,7 @@ TS2322 [ERROR]: Type 'string' is not assignable to type 'number'.
TS2322 [ERROR]: Type 'number' is not assignable to type 'string'. TS2322 [ERROR]: Type 'number' is not assignable to type 'string'.
fizz: 1, fizz: 1,
~~~~ ~~~~
at [WILDCARD]/npm/types/main.ts:[WILDCARD] at [WILDCARD]/main.ts:[WILDCARD]
The expected type comes from property 'fizz' which is declared here on type 'Fizzbuzz' The expected type comes from property 'fizz' which is declared here on type 'Fizzbuzz'
fizz: string; fizz: string;
@ -41,7 +50,7 @@ TS2322 [ERROR]: Type 'number' is not assignable to type 'string'.
TS2322 [ERROR]: Type 'number' is not assignable to type 'string'. TS2322 [ERROR]: Type 'number' is not assignable to type 'string'.
buzz: 2, buzz: 2,
~~~~ ~~~~
at [WILDCARD]/npm/types/main.ts:[WILDCARD] at [WILDCARD]/main.ts:[WILDCARD]
The expected type comes from property 'buzz' which is declared here on type 'Fizzbuzz' The expected type comes from property 'buzz' which is declared here on type 'Fizzbuzz'
buzz: string; buzz: string;
@ -51,21 +60,21 @@ TS2322 [ERROR]: Type 'number' is not assignable to type 'string'.
TS2322 [ERROR]: Type '5' is not assignable to type '"test1"'. TS2322 [ERROR]: Type '5' is not assignable to type '"test1"'.
const valueA: "test1" = getClient(); const valueA: "test1" = getClient();
~~~~~~ ~~~~~~
at [WILDCARD]/npm/types/main.ts:[WILDCARD] at [WILDCARD]/main.ts:[WILDCARD]
TS2322 [ERROR]: Type '"import"' is not assignable to type '"test2"'. TS2322 [ERROR]: Type '"import"' is not assignable to type '"test2"'.
const valueB: "test2" = entryImport(); const valueB: "test2" = entryImport();
~~~~~~ ~~~~~~
at [WILDCARD]/types/main.ts:[WILDCARD] at [WILDCARD]/main.ts:[WILDCARD]
TS2322 [ERROR]: Type '12' is not assignable to type '"test3"'. TS2322 [ERROR]: Type '12' is not assignable to type '"test3"'.
const valueC: "test3" = entryA(); const valueC: "test3" = entryA();
~~~~~~ ~~~~~~
at [WILDCARD]/types/main.ts:[WILDCARD] at [WILDCARD]/main.ts:[WILDCARD]
TS2322 [ERROR]: Type '"types"' is not assignable to type '"test4"'. TS2322 [ERROR]: Type '"types"' is not assignable to type '"test4"'.
const valueD: "test4" = entryTypes(); const valueD: "test4" = entryTypes();
~~~~~~ ~~~~~~
at file:///[WILDCARD]/types/main.ts:[WILDCARD] at file:///[WILDCARD]/main.ts:[WILDCARD]
Found 9 errors. Found 9 errors.

View file

@ -218,7 +218,7 @@ async function ensureNoNewITests() {
"lsp_tests.rs": 0, "lsp_tests.rs": 0,
"node_compat_tests.rs": 4, "node_compat_tests.rs": 4,
"node_unit_tests.rs": 2, "node_unit_tests.rs": 2,
"npm_tests.rs": 97, "npm_tests.rs": 93,
"pm_tests.rs": 0, "pm_tests.rs": 0,
"publish_tests.rs": 0, "publish_tests.rs": 0,
"repl_tests.rs": 0, "repl_tests.rs": 0,