diff options
Diffstat (limited to 'ext/node')
-rw-r--r-- | ext/node/errors.rs | 210 | ||||
-rw-r--r-- | ext/node/lib.rs | 1 | ||||
-rw-r--r-- | ext/node/resolution.rs | 387 |
3 files changed, 370 insertions, 228 deletions
diff --git a/ext/node/errors.rs b/ext/node/errors.rs index 11bb011f8..64625d32f 100644 --- a/ext/node/errors.rs +++ b/ext/node/errors.rs @@ -49,6 +49,8 @@ pub enum NodeJsErrorCode { ERR_UNKNOWN_FILE_EXTENSION, ERR_UNSUPPORTED_DIR_IMPORT, ERR_UNSUPPORTED_ESM_URL_SCHEME, + /// Deno specific since Node doesn't support TypeScript. + ERR_TYPES_NOT_FOUND, } impl std::fmt::Display for NodeJsErrorCode { @@ -70,6 +72,7 @@ impl 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", + ERR_TYPES_NOT_FOUND => "ERR_TYPES_NOT_FOUND", } } } @@ -122,65 +125,98 @@ impl NodeJsErrorCoded for InvalidModuleSpecifierError { } } +kinded_err!(LegacyResolveError, LegacyResolveErrorKind); + #[derive(Debug, Error)] -pub enum LegacyMainResolveError { +pub enum LegacyResolveErrorKind { + #[error(transparent)] + TypesNotFound(#[from] TypesNotFoundError), #[error(transparent)] - PathToDeclarationUrl(PathToDeclarationUrlError), + ModuleNotFound(#[from] ModuleNotFoundError), } -impl NodeJsErrorCoded for LegacyMainResolveError { +impl NodeJsErrorCoded for LegacyResolveError { fn code(&self) -> NodeJsErrorCode { - match self { - Self::PathToDeclarationUrl(e) => e.code(), + match self.as_kind() { + LegacyResolveErrorKind::TypesNotFound(e) => e.code(), + LegacyResolveErrorKind::ModuleNotFound(e) => e.code(), } } } 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 { fn code(&self) -> NodeJsErrorCode { match self.as_kind() { - PackageFolderResolveErrorKind::NotFoundPackage { .. } - | PackageFolderResolveErrorKind::NotFoundReferrer { .. } - | PackageFolderResolveErrorKind::Io { .. } => { - NodeJsErrorCode::ERR_MODULE_NOT_FOUND - } + PackageFolderResolveErrorKind::PackageNotFound(e) => e.code(), + PackageFolderResolveErrorKind::ReferrerNotFound(e) => e.code(), + PackageFolderResolveErrorKind::Io(e) => e.code(), } } } #[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, - }, + #[error(transparent)] + PackageNotFound(#[from] PackageNotFoundError), + #[error(transparent)] + ReferrerNotFound(#[from] ReferrerNotFoundError), + #[error(transparent)] + Io(#[from] PackageFolderResolveIoError), } kinded_err!(PackageSubpathResolveError, PackageSubpathResolveErrorKind); @@ -189,10 +225,8 @@ impl NodeJsErrorCoded for PackageSubpathResolveError { fn code(&self) -> NodeJsErrorCode { match self.as_kind() { PackageSubpathResolveErrorKind::PkgJsonLoad(e) => e.code(), - PackageSubpathResolveErrorKind::PackageFolderResolve(e) => e.code(), PackageSubpathResolveErrorKind::Exports(e) => e.code(), - PackageSubpathResolveErrorKind::LegacyMain(e) => e.code(), - PackageSubpathResolveErrorKind::LegacyExact(e) => e.code(), + PackageSubpathResolveErrorKind::LegacyResolve(e) => e.code(), } } } @@ -202,13 +236,9 @@ pub enum PackageSubpathResolveErrorKind { #[error(transparent)] PkgJsonLoad(#[from] PackageJsonLoadError), #[error(transparent)] - PackageFolderResolve(#[from] PackageFolderResolveError), - #[error(transparent)] Exports(PackageExportsResolveError), #[error(transparent)] - LegacyMain(LegacyMainResolveError), - #[error(transparent)] - LegacyExact(PathToDeclarationUrlError), + LegacyResolve(LegacyResolveError), } #[derive(Debug, Error)] @@ -254,7 +284,7 @@ impl NodeJsErrorCoded for PackageTargetResolveError { PackageTargetResolveErrorKind::InvalidPackageTarget(e) => e.code(), PackageTargetResolveErrorKind::InvalidModuleSpecifier(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)] PackageResolve(#[from] PackageResolveError), #[error(transparent)] - PathToDeclarationUrl(#[from] PathToDeclarationUrlError), + TypesNotFound(#[from] TypesNotFoundError), } kinded_err!(PackageExportsResolveError, PackageExportsResolveErrorKind); @@ -293,16 +323,23 @@ pub enum PackageExportsResolveErrorKind { } #[derive(Debug, Error)] -pub enum PathToDeclarationUrlError { - #[error(transparent)] - SubPath(#[from] PackageSubpathResolveError), +#[error( + "[{}] Could not find types for '{}'{}", + 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 { - match self { - PathToDeclarationUrlError::SubPath(e) => e.code(), - } + NodeJsErrorCode::ERR_TYPES_NOT_FOUND } } @@ -441,6 +478,7 @@ impl NodeJsErrorCoded for PackageResolveError { match self.as_kind() { PackageResolveErrorKind::ClosestPkgJson(e) => e.code(), PackageResolveErrorKind::InvalidModuleSpecifier(e) => e.code(), + PackageResolveErrorKind::PackageFolderResolve(e) => e.code(), PackageResolveErrorKind::ExportsResolve(e) => e.code(), PackageResolveErrorKind::SubpathResolve(e) => e.code(), } @@ -454,37 +492,49 @@ pub enum PackageResolveErrorKind { #[error(transparent)] InvalidModuleSpecifier(#[from] InvalidModuleSpecifierError), #[error(transparent)] + PackageFolderResolve(#[from] PackageFolderResolveError), + #[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("Failed joining '{path}' from '{base}'.")] +pub struct NodeResolveRelativeJoinError { + pub path: String, + pub base: ModuleSpecifier, + #[source] + pub 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)] PackageImportsResolve(#[from] PackageImportsResolveError), #[error(transparent)] UnsupportedEsmUrlScheme(#[from] UnsupportedEsmUrlSchemeError), - #[error("Failed resolving specifier from data url referrer.")] - DataUrlReferrerFailed { - #[source] - source: url::ParseError, - }, + #[error(transparent)] + DataUrlReferrer(#[from] DataUrlReferrerError), #[error(transparent)] PackageResolve(#[from] PackageResolveError), #[error(transparent)] - PathToDeclarationUrl(#[from] PathToDeclarationUrlError), - #[error(transparent)] - UrlToNodeResolution(#[from] UrlToNodeResolutionError), + TypesNotFound(#[from] TypesNotFoundError), #[error(transparent)] FinalizeResolution(#[from] FinalizeResolutionError), + #[error(transparent)] + UrlToNodeResolution(#[from] UrlToNodeResolutionError), } kinded_err!(FinalizeResolutionError, FinalizeResolutionErrorKind); @@ -499,6 +549,16 @@ pub enum FinalizeResolutionErrorKind { 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)] #[error( "[{}] Cannot find {} '{}'{}", @@ -513,9 +573,9 @@ pub struct ModuleNotFoundError { pub typ: &'static str, } -impl ModuleNotFoundError { - pub fn code(&self) -> &'static str { - "ERR_MODULE_NOT_FOUND" +impl NodeJsErrorCoded for ModuleNotFoundError { + fn code(&self) -> NodeJsErrorCode { + NodeJsErrorCode::ERR_MODULE_NOT_FOUND } } diff --git a/ext/node/lib.rs b/ext/node/lib.rs index 6f7185148..21af5a094 100644 --- a/ext/node/lib.rs +++ b/ext/node/lib.rs @@ -46,7 +46,6 @@ pub use path::PathClean; pub use polyfill::is_builtin_node_module; pub use polyfill::SUPPORTED_BUILTIN_NODE_MODULES; pub use polyfill::SUPPORTED_BUILTIN_NODE_MODULES_WITH_PREFIX; -pub use resolution::parse_npm_pkg_name; pub use resolution::NodeModuleKind; pub use resolution::NodeResolution; pub use resolution::NodeResolutionMode; diff --git a/ext/node/resolution.rs b/ext/node/resolution.rs index 476b4f59c..6417835a2 100644 --- a/ext/node/resolution.rs +++ b/ext/node/resolution.rs @@ -18,14 +18,16 @@ use deno_package_json::PackageJsonRc; use crate::errors; use crate::errors::CanonicalizingPkgJsonDirError; use crate::errors::ClosestPkgJsonError; +use crate::errors::DataUrlReferrerError; use crate::errors::FinalizeResolutionError; use crate::errors::InvalidModuleSpecifierError; use crate::errors::InvalidPackageTargetError; -use crate::errors::LegacyMainResolveError; +use crate::errors::LegacyResolveError; use crate::errors::ModuleNotFoundError; use crate::errors::NodeJsErrorCode; use crate::errors::NodeJsErrorCoded; use crate::errors::NodeResolveError; +use crate::errors::NodeResolveRelativeJoinError; use crate::errors::PackageExportsResolveError; use crate::errors::PackageImportNotDefinedError; use crate::errors::PackageImportsResolveError; @@ -38,11 +40,12 @@ 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::TypesNotFoundError; +use crate::errors::TypesNotFoundErrorData; use crate::errors::UnsupportedDirImportError; use crate::errors::UnsupportedEsmUrlSchemeError; use crate::errors::UrlToNodeResolutionError; @@ -55,6 +58,7 @@ use crate::PathClean; pub static DEFAULT_CONDITIONS: &[&str] = &["deno", "node", "import"]; pub static REQUIRE_CONDITIONS: &[&str] = &["require", "node"]; +static TYPES_ONLY_CONDITIONS: &[&str] = &["types"]; pub type NodeModuleKind = deno_package_json::NodeModuleKind; @@ -177,23 +181,23 @@ impl NodeResolver { referrer: &ModuleSpecifier, referrer_kind: NodeModuleKind, mode: NodeResolutionMode, - ) -> Result<Option<NodeResolution>, NodeResolveError> { + ) -> Result<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 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 url.scheme() == "data" { - return Ok(Some(NodeResolution::Esm(url))); + return Ok(NodeResolution::Esm(url)); } if let Some(module_name) = 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(); @@ -209,14 +213,14 @@ impl NodeResolver { // todo(dsherret): this seems wrong if referrer.scheme() == "data" { - let url = referrer.join(specifier).map_err(|source| { - NodeResolveError::DataUrlReferrerFailed { source } - })?; - return Ok(Some(NodeResolution::Esm(url))); + let url = referrer + .join(specifier) + .map_err(|source| DataUrlReferrerError { source })?; + return Ok(NodeResolution::Esm(url)); } } - let maybe_url = self.module_resolve( + let url = self.module_resolve( specifier, referrer, referrer_kind, @@ -226,15 +230,19 @@ impl NodeResolver { }, mode, )?; - let url = match maybe_url { - Some(url) => url, - None => return Ok(None), + + let url = if mode.is_types() { + 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)?; // TODO(bartlomieju): skipped checking errors for commonJS resolution and // "preserveSymlinksMain"/"preserveSymlinks" options. - Ok(Some(resolve_response)) + Ok(resolve_response) } fn module_resolve( @@ -244,12 +252,10 @@ impl NodeResolver { referrer_kind: NodeModuleKind, conditions: &[&str], mode: NodeResolutionMode, - ) -> Result<Option<ModuleSpecifier>, NodeResolveError> { - // note: if we're here, the referrer is an esm module - let maybe_url = if should_be_treated_as_relative_or_absolute_path(specifier) - { - Some(referrer.join(specifier).map_err(|err| { - NodeResolveError::RelativeJoinError { + ) -> Result<ModuleSpecifier, NodeResolveError> { + if should_be_treated_as_relative_or_absolute_path(specifier) { + Ok(referrer.join(specifier).map_err(|err| { + NodeResolveRelativeJoinError { path: specifier.to_string(), base: referrer.clone(), source: err, @@ -260,7 +266,7 @@ impl NodeResolver { .get_closest_package_json(referrer) .map_err(PackageImportsResolveErrorKind::ClosestPkgJson) .map_err(|err| PackageImportsResolveError(Box::new(err)))?; - Some(self.package_imports_resolve( + Ok(self.package_imports_resolve( specifier, Some(referrer), referrer_kind, @@ -269,32 +275,16 @@ impl NodeResolver { mode, )?) } else if let Ok(resolved) = Url::parse(specifier) { - Some(resolved) + Ok(resolved) } else { - self.package_resolve( + Ok(self.package_resolve( specifier, referrer, referrer_kind, conditions, 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( @@ -369,12 +359,12 @@ impl NodeResolver { package_subpath: Option<&str>, maybe_referrer: Option<&ModuleSpecifier>, mode: NodeResolutionMode, - ) -> Result<Option<NodeResolution>, ResolvePkgSubpathFromDenoModuleError> { + ) -> Result<NodeResolution, ResolvePkgSubpathFromDenoModuleError> { let node_module_kind = NodeModuleKind::Esm; let package_subpath = package_subpath .map(|s| format!("./{s}")) .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_subpath, maybe_referrer, @@ -382,14 +372,10 @@ impl NodeResolver { DEFAULT_CONDITIONS, 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)?; // TODO(bartlomieju): skipped checking errors for commonJS resolution and // "preserveSymlinksMain"/"preserveSymlinks" options. - Ok(Some(resolve_response)) + Ok(resolve_response) } pub fn resolve_binary_commands( @@ -475,7 +461,7 @@ impl NodeResolver { path: &Path, maybe_referrer: Option<&ModuleSpecifier>, referrer_kind: NodeModuleKind, - ) -> Result<Option<ModuleSpecifier>, PathToDeclarationUrlError> { + ) -> Result<ModuleSpecifier, TypesNotFoundError> { fn probe_extensions( fs: &dyn deno_fs::FileSystem, path: &Path, @@ -525,15 +511,15 @@ impl NodeResolver { || lowercase_path.ends_with(".d.cts") || lowercase_path.ends_with(".d.mts") { - return Ok(Some(to_file_specifier(path))); + return Ok(to_file_specifier(path)); } if let Some(path) = 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) { - let maybe_resolution = self.resolve_package_dir_subpath( + let resolution_result = self.resolve_package_dir_subpath( path, /* sub path */ ".", maybe_referrer, @@ -543,9 +529,9 @@ impl NodeResolver { NodeModuleKind::Cjs => REQUIRE_CONDITIONS, }, NodeResolutionMode::Types, - )?; - if let Some(resolution) = maybe_resolution { - return Ok(Some(resolution)); + ); + if let Ok(resolution) = resolution_result { + return Ok(resolution); } let index_path = path.join("index.js"); if let Some(path) = probe_extensions( @@ -554,14 +540,17 @@ impl NodeResolver { &index_path.to_string_lossy().to_lowercase(), referrer_kind, ) { - return Ok(Some(to_file_specifier(&path))); + return Ok(to_file_specifier(&path)); } } // allow resolving .css files for types resolution 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)] @@ -719,22 +708,32 @@ impl NodeResolver { conditions, mode, ) { - Ok(Some(url)) => Ok(url), - 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()) - } + Ok(url) => Ok(url), + Err(err) => match err.code() { + NodeJsErrorCode::ERR_INVALID_MODULE_SPECIFIER + | NodeJsErrorCode::ERR_INVALID_PACKAGE_CONFIG + | NodeJsErrorCode::ERR_INVALID_PACKAGE_TARGET + | NodeJsErrorCode::ERR_PACKAGE_IMPORT_NOT_DEFINED + | NodeJsErrorCode::ERR_PACKAGE_PATH_NOT_EXPORTED + | NodeJsErrorCode::ERR_UNKNOWN_FILE_EXTENSION + | NodeJsErrorCode::ERR_UNSUPPORTED_DIR_IMPORT + | NodeJsErrorCode::ERR_UNSUPPORTED_ESM_URL_SCHEME + | NodeJsErrorCode::ERR_TYPES_NOT_FOUND => { + Err(PackageTargetResolveErrorKind::PackageResolve(err).into()) + } + NodeJsErrorCode::ERR_MODULE_NOT_FOUND => 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(), + ), + }, }; return match result { @@ -832,6 +831,62 @@ impl NodeResolver { conditions: &[&str], 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> { if let Some(target) = target.as_str() { let url = self.resolve_package_target_string( target, @@ -847,11 +902,11 @@ impl NodeResolver { )?; if mode.is_types() && url.scheme() == "file" { let path = url.to_file_path().unwrap(); - return Ok(self.path_to_declaration_url( + return Ok(Some(self.path_to_declaration_url( &path, maybe_referrer, referrer_kind, - )?); + )?)); } else { return Ok(Some(url)); } @@ -1069,41 +1124,37 @@ impl NodeResolver { referrer_kind: NodeModuleKind, conditions: &[&str], mode: NodeResolutionMode, - ) -> Result<Option<ModuleSpecifier>, PackageResolveError> { + ) -> Result<ModuleSpecifier, PackageResolveError> { let (package_name, package_subpath, _is_scoped) = parse_npm_pkg_name(specifier, referrer)?; - let Some(package_config) = self.get_closest_package_json(referrer)? else { - return Ok(None); - }; - // ResolveSelf - if package_config.name.as_ref() == Some(&package_name) { - if let Some(exports) = &package_config.exports { - return self - .package_exports_resolve( - &package_config.path, - &package_subpath, - exports, - Some(referrer), - referrer_kind, - conditions, - mode, - ) - .map(Some) - .map_err(|err| err.into()); + if let Some(package_config) = self.get_closest_package_json(referrer)? { + // ResolveSelf + if package_config.name.as_ref() == Some(&package_name) { + if let Some(exports) = &package_config.exports { + return self + .package_exports_resolve( + &package_config.path, + &package_subpath, + exports, + Some(referrer), + referrer_kind, + conditions, + mode, + ) + .map_err(|err| err.into()); + } } } - self - .resolve_package_subpath_for_package( - &package_name, - &package_subpath, - referrer, - referrer_kind, - conditions, - mode, - ) - .map_err(|err| err.into()) + self.resolve_package_subpath_for_package( + &package_name, + &package_subpath, + referrer, + referrer_kind, + conditions, + mode, + ) } #[allow(clippy::too_many_arguments)] @@ -1115,7 +1166,7 @@ impl NodeResolver { referrer_kind: NodeModuleKind, conditions: &[&str], mode: NodeResolutionMode, - ) -> Result<Option<ModuleSpecifier>, PackageSubpathResolveError> { + ) -> Result<ModuleSpecifier, PackageResolveError> { let result = self.resolve_package_subpath_for_package_inner( package_name, package_subpath, @@ -1124,10 +1175,10 @@ impl NodeResolver { conditions, mode, ); - if mode.is_types() && !matches!(result, Ok(Some(_))) { + if mode.is_types() && !matches!(result, Ok(ModuleSpecifier { .. })) { // try to resolve with the @types package 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_subpath, referrer, @@ -1135,7 +1186,7 @@ impl NodeResolver { conditions, mode, ) { - return Ok(Some(result)); + return Ok(result); } } result @@ -1150,7 +1201,7 @@ impl NodeResolver { referrer_kind: NodeModuleKind, conditions: &[&str], mode: NodeResolutionMode, - ) -> Result<Option<ModuleSpecifier>, PackageSubpathResolveError> { + ) -> Result<ModuleSpecifier, PackageResolveError> { let package_dir_path = self .npm_resolver .resolve_package_folder_from_package(package_name, referrer)?; @@ -1169,14 +1220,16 @@ impl NodeResolver { // )) // Package match. - self.resolve_package_dir_subpath( - &package_dir_path, - package_subpath, - Some(referrer), - referrer_kind, - conditions, - mode, - ) + self + .resolve_package_dir_subpath( + &package_dir_path, + package_subpath, + Some(referrer), + referrer_kind, + conditions, + mode, + ) + .map_err(|err| err.into()) } #[allow(clippy::too_many_arguments)] @@ -1188,7 +1241,7 @@ impl NodeResolver { referrer_kind: NodeModuleKind, conditions: &[&str], mode: NodeResolutionMode, - ) -> Result<Option<ModuleSpecifier>, PackageSubpathResolveError> { + ) -> Result<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( @@ -1207,7 +1260,9 @@ impl NodeResolver { referrer_kind, mode, ) - .map_err(|err| PackageSubpathResolveErrorKind::LegacyExact(err).into()), + .map_err(|err| { + PackageSubpathResolveErrorKind::LegacyResolve(err).into() + }), } } @@ -1220,7 +1275,7 @@ impl NodeResolver { referrer_kind: NodeModuleKind, conditions: &[&str], mode: NodeResolutionMode, - ) -> Result<Option<ModuleSpecifier>, PackageSubpathResolveError> { + ) -> Result<ModuleSpecifier, PackageSubpathResolveError> { if let Some(exports) = &package_json.exports { let result = self.package_exports_resolve( &package_json.path, @@ -1232,13 +1287,13 @@ impl NodeResolver { mode, ); match result { - Ok(found) => return Ok(Some(found)), + Ok(found) => return Ok(found), Err(exports_err) => { if mode.is_types() && package_subpath == "." { return self .legacy_main_resolve(package_json, referrer, referrer_kind, mode) .map_err(|err| { - PackageSubpathResolveErrorKind::LegacyMain(err).into() + PackageSubpathResolveErrorKind::LegacyResolve(err).into() }); } return Err( @@ -1251,7 +1306,9 @@ impl NodeResolver { if package_subpath == "." { return self .legacy_main_resolve(package_json, referrer, referrer_kind, mode) - .map_err(|err| PackageSubpathResolveErrorKind::LegacyMain(err).into()); + .map_err(|err| { + PackageSubpathResolveErrorKind::LegacyResolve(err).into() + }); } self @@ -1262,7 +1319,9 @@ impl NodeResolver { referrer_kind, mode, ) - .map_err(|err| PackageSubpathResolveErrorKind::LegacyExact(err).into()) + .map_err(|err| { + PackageSubpathResolveErrorKind::LegacyResolve(err.into()).into() + }) } fn resolve_subpath_exact( @@ -1272,13 +1331,13 @@ impl NodeResolver { referrer: Option<&ModuleSpecifier>, referrer_kind: NodeModuleKind, mode: NodeResolutionMode, - ) -> Result<Option<ModuleSpecifier>, PathToDeclarationUrlError> { + ) -> Result<ModuleSpecifier, TypesNotFoundError> { assert_ne!(package_subpath, "."); let file_path = directory.join(package_subpath); if mode.is_types() { Ok(self.path_to_declaration_url(&file_path, referrer, referrer_kind)?) } else { - Ok(Some(to_file_specifier(&file_path))) + Ok(to_file_specifier(&file_path)) } } @@ -1286,20 +1345,22 @@ impl NodeResolver { &self, directory: &Path, package_subpath: &str, - referrer: Option<&ModuleSpecifier>, + maybe_referrer: Option<&ModuleSpecifier>, referrer_kind: NodeModuleKind, mode: NodeResolutionMode, - ) -> Result<Option<ModuleSpecifier>, PathToDeclarationUrlError> { + ) -> Result<ModuleSpecifier, LegacyResolveError> { if package_subpath == "." { - Ok(self.legacy_index_resolve(directory, referrer_kind, mode)) + self.legacy_index_resolve(directory, maybe_referrer, referrer_kind, mode) } else { - self.resolve_subpath_exact( - directory, - package_subpath, - referrer, - referrer_kind, - mode, - ) + self + .resolve_subpath_exact( + directory, + package_subpath, + maybe_referrer, + referrer_kind, + mode, + ) + .map_err(|err| err.into()) } } @@ -1348,7 +1409,7 @@ impl NodeResolver { maybe_referrer: Option<&ModuleSpecifier>, referrer_kind: NodeModuleKind, mode: NodeResolutionMode, - ) -> Result<Option<ModuleSpecifier>, LegacyMainResolveError> { + ) -> Result<ModuleSpecifier, LegacyResolveError> { let maybe_main = if mode.is_types() { match package_json.types.as_ref() { Some(types) => Some(types.as_str()), @@ -1357,11 +1418,14 @@ 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, maybe_referrer, referrer_kind) - .map_err(LegacyMainResolveError::PathToDeclarationUrl)?; - if let Some(path) = maybe_decl_url { - return Ok(Some(path)); + let decl_url_result = self.path_to_declaration_url( + &main, + maybe_referrer, + referrer_kind, + ); + // don't surface errors, fallback to checking the index now + if let Ok(url) = decl_url_result { + return Ok(url); } } None @@ -1374,7 +1438,7 @@ impl NodeResolver { if let Some(main) = maybe_main { let guess = package_json.path.parent().unwrap().join(main).clean(); 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 @@ -1404,24 +1468,26 @@ impl NodeResolver { .clean(); if self.fs.is_file_sync(&guess) { // 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(), + maybe_referrer, referrer_kind, mode, - )) + ) } fn legacy_index_resolve( &self, directory: &Path, + maybe_referrer: Option<&ModuleSpecifier>, referrer_kind: NodeModuleKind, mode: NodeResolutionMode, - ) -> Option<ModuleSpecifier> { + ) -> Result<ModuleSpecifier, LegacyResolveError> { let index_file_names = if mode.is_types() { // todo(dsherret): investigate exactly how typescript does this match referrer_kind { @@ -1435,11 +1501,28 @@ impl NodeResolver { let guess = directory.join(index_file_name).clean(); if self.fs.is_file_sync(&guess) { // 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. fn is_relative_specifier(specifier: &str) -> bool { 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_len == 1 || specifier_chars[1] == '/' { |