fix(exports): propagate the last error in array target resolution#1267
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #1267 +/- ##
==========================================
+ Coverage 93.85% 93.88% +0.02%
==========================================
Files 21 21
Lines 4313 4315 +2
==========================================
+ Hits 4048 4051 +3
+ Misses 265 264 -1 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Merging this PR will not alter performance
Warning Please fix the performance issues or acknowledge them on CodSpeed. Performance Changes
Tip Investigate this regression by commenting Comparing Footnotes
|
91c9aa0 to
86dc2f5
Compare
The "return the last array entry's error" branch was dead: `i` comes from `enumerate()`, so `i == targets.len()` could never be true, and errors from array entries were always swallowed - an invalid target in the last fallback entry surfaced as a generic PackagePathNotExported instead of the specific error. Fix the off-by-one (`i + 1 == targets.len()`) so the last entry's error propagates, implementing the documented "Return or throw the last fallback resolution null return or error" step. `NotFound` is exempt: it comes from re-resolving a bare `imports` target through the module system, which enhanced-resolve lets fall through to a soft failure rather than a hard error.
86dc2f5 to
a867cbe
Compare
## 🤖 New release * `oxc_resolver`: 11.22.0 -> 11.23.0 * `oxc_resolver_napi`: 11.22.0 -> 11.23.0 <details><summary><i><b>Changelog</b></i></summary><p> ## `oxc_resolver` <blockquote> ## [11.23.0](v11.22.0...v11.23.0) - 2026-07-02 ### <!-- 0 -->🚀 Features - *(tsconfig)* expose outDir, declarationDir, resolveJsonModule, checkJs ([#1257](#1257)) (by @Boshen) ### <!-- 1 -->🐛 Bug Fixes - *(exports)* propagate the last error in array target resolution ([#1267](#1267)) (by @Boshen) ### <!-- 2 -->🚜 Refactor - deduplicate cross-file helpers, drop stale lint allows ([#1269](#1269)) (by @Boshen) - *(dts)* deduplicate package entry selection and remove dead code ([#1266](#1266)) (by @Boshen) - *(tsconfig)* skip TsConfig deep clone for non-root configs ([#1265](#1265)) (by @Boshen) - *(alias)* compile fallback aliases once at construction ([#1264](#1264)) (by @Boshen) ### <!-- 4 -->⚡ Performance - *(alias)* scan a packed first-byte array instead of striding over alias entries ([#1260](#1260)) (by @Boshen) ### Contributors * @Boshen </blockquote> </p></details> --- This PR was generated with [release-plz](https://github.com/release-plz/release-plz/). Co-authored-by: oxc-guard[bot] <276638029+oxc-guard[bot]@users.noreply.github.com>
The "return the last array entry's error" branch in
package_target_resolvewas dead code:icomes fromenumerate(), soi == targets.len()could never be true. Errors from array entries were always swallowed likeundefined, so an invalid target in the last fallback entry surfaced as a genericPackagePathNotExportedinstead of the specific error — despite the comment right below claiming "Return or throw the last fallback resolution null return or error".This fixes the off-by-one (
i + 1 == targets.len()) so the last entry's error now propagates, matching the documented intent and Node'slastExceptionbehavior for arrays.One deliberate exemption:
ResolveError::NotFoundstill falls through. It only arises from re-resolving a bareimportstarget (e.g.\"#a\": [{\"import\": [\"#b/import.js\"]}]) through the module system, and enhanced-resolve treats an unresolvable candidate there as a soft failure, not a hard error — the ported "Direct and conditional mapping #4" test pins that behavior. Node would actually throw immediately on any non-ERR_INVALID_PACKAGE_TARGETerror inside the array loop, which is stricter than enhanced-resolve; the exemption keeps us compatible with the latter.Adds two imports-field tests: an invalid target in the last array entry now errors (previously
Ok(None)), and an invalid target in a non-last entry still falls through to the next entry.Split out of #1263.