diff --git a/README.md b/README.md index a45e58c..b287381 100644 --- a/README.md +++ b/README.md @@ -167,12 +167,12 @@ The options are aligned with [enhanced-resolve](https://github.com/webpack/enhan ### Other Options -| Field | Default | Description | -| ------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| tsconfig | None | TypeScript related config for resolver | -| tsconfig.configFile | | A relative path to the tsconfig file based on `cwd`, or an absolute path of tsconfig file. | -| tsconfig.references | `[]` | - 'auto': inherits from TypeScript config
- `string []`: relative path (based on directory of the referencing tsconfig file) or absolute path of referenced project's tsconfig | -| enablePnp | false | Enable Yarn Plug'n'Play support | +| Field | Default | Description | +| ------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| tsconfig | None | TypeScript related config for resolver | +| tsconfig.configFile | | A relative path to the tsconfig file based on `cwd`, or an absolute path of tsconfig file. | +| tsconfig.references | `[]` | - 'auto': inherits from TypeScript config
- `string []`: relative path (based on directory of the referencing tsconfig file) or absolute path of referenced project's tsconfig | +| enablePnp | false | Enable Yarn Plug'n'Play support | In the context of `@rspack/resolver`, the `tsconfig.references` option helps isolate the `paths` configurations of different TypeScript projects. This ensures that path aliases defined in one TypeScript project do not unintentionally affect the resolving behavior of another. diff --git a/src/cache.rs b/src/cache.rs index 8d54f36..06e693a 100644 --- a/src/cache.rs +++ b/src/cache.rs @@ -106,7 +106,7 @@ impl Cache { } } -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct CachedPath(Arc); impl Hash for CachedPath { @@ -158,6 +158,12 @@ pub struct CachedPathImpl { package_json: OnceLock>>, } +impl std::fmt::Debug for CachedPathImpl { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.path.fmt(f) + } +} + impl CachedPathImpl { fn new(hash: u64, path: Box, parent: Option) -> Self { Self { diff --git a/src/lib.rs b/src/lib.rs index dbbd3d3..8350eee 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1948,7 +1948,8 @@ impl ResolverGeneric { )); } // 2. For each item targetValue in target, do - for (i, target_value) in targets.iter().enumerate() { + let mut last_error = None; + for target_value in targets.iter() { // 1. Let resolved be the result of PACKAGE_TARGET_RESOLVE( packageURL, targetValue, patternMatch, isImports, conditions), continuing the loop on any Invalid Package Target error. let resolved = self .package_target_resolve( @@ -1962,18 +1963,24 @@ impl ResolverGeneric { ) .await; - if resolved.is_err() && i == targets.len() { - return resolved; - } - - // 2. If resolved is undefined, continue the loop. - if let Ok(Some(path)) = resolved { + match resolved { + // Only track InvalidPackageTarget for re-throwing after the loop. + Err(e @ ResolveError::InvalidPackageTarget(..)) => { + last_error = Some(e); + } + Err(_) => {} + // 2. If resolved is undefined, continue the loop. + Ok(None) => { + last_error = None; + } // 3. Return resolved. - return Ok(Some(path)); + Ok(Some(path)) => return Ok(Some(path)), } } // 3. Return or throw the last fallback resolution null return or error. - // Note: see `resolved.is_err() && i == targets.len()` + if let Some(e) = last_error { + return Err(e); + } } JSONValue::Static(_) => {} } diff --git a/src/tests/exports_field.rs b/src/tests/exports_field.rs index cfbbc8b..9e47397 100644 --- a/src/tests/exports_field.rs +++ b/src/tests/exports_field.rs @@ -2634,3 +2634,26 @@ async fn test_cases() { } } } + +/// When all targets in a fallback array are invalid, `package_target_resolve` should +/// propagate `InvalidPackageTarget` instead of swallowing it and returning `Ok(None)`. +/// Bug: `resolved.is_err() && i == targets.len()` is always false (off-by-one), +/// causing errors to be silently dropped. +#[tokio::test] +async fn array_fallback_all_invalid_targets_should_return_invalid_package_target() { + let resolved = Resolver::new(ResolveOptions::default()) + .package_exports_resolve( + Path::new(""), + ".", + &exports_field(json!({ + ".": ["invalid_target_1", "invalid_target_2"] + })), + &mut Ctx::default(), + ) + .await; + + assert!( + matches!(resolved, Err(ResolveError::InvalidPackageTarget(..))), + "expected InvalidPackageTarget, got {resolved:?}" + ); +}