next.js
73e654f6 - fix(turbopack): preserve resolveExtensions priority in read_matches fast path (#91856)

Commit
12 days ago
fix(turbopack): preserve resolveExtensions priority in read_matches fast path (#91856) ### What? Fix a bug where Turbopack ignored the priority order of `resolveExtensions` when both a platform-specific variant (e.g. `Component.web.tsx`) and a default variant (`Component.tsx`) exist for the same module path. The first-listed extension should always win, but it often didn't. ### Why? Turbopack's `read_matches` function iterates over constant alternatives extracted from a resolve pattern (one per extension in `resolveExtensions`). It uses `enumerate()` to assign a priority `index` to each alternative — lower index = higher priority — so the correct file is picked after sorting. The `until_end=true` branch (direct file existence check) correctly threaded `index` through. But the `until_end=false` branch (subdirectory descent, the path taken when the pattern contains a `/` before the dynamic suffix) had a **hardcoded `0`** instead of `index`: ```rust // before — all alternatives get the same priority nested.push((0, read_matches(fs_path.clone(), ...))); // after — priority matches the extension's position in resolveExtensions nested.push((index, read_matches(fs_path.clone(), ...))); ``` With all alternatives at priority 0, the tie-breaking sort was effectively arbitrary. In practice `Component.tsx` frequently beat `Component.web.tsx` even when `.web.tsx` was listed first, breaking platform-specific module overrides — a common React Native Web / Expo Web pattern. ### How? **One-line Rust fix** in `turbopack/crates/turbopack-core/src/resolve/pattern.rs`: replace the hardcoded `0` with the loop variable `index` in the `until_end=false` branch of the fast path. **Turbopack unit tests** (`turbopack-core`): - `test_custom_extensions_web_before_default` — `.web.tsx` wins when listed first in `resolveExtensions` - `test_custom_extensions_fallback_when_web_missing` — correctly falls back to `.tsx` when `.web.tsx` is absent - Extended `test_read_matches` with `extension_ordering` and `subpath_ordering` cases covering both the `until_end=true` and `until_end=false` branches **Next.js e2e test** (extended `test/e2e/app-dir/resolve-extensions/`): - Added `PlatformComponent.web.tsx` (renders `"hello web platform"`) and `PlatformComponent.tsx` (renders `"hello default platform"`) - `.web.tsx` is placed before `.tsx` in `resolveExtensions` in `next.config.js` (for both Turbopack and webpack) - New assertion: SSR'd HTML must contain `"hello web platform"` and must **not** contain `"hello default platform"` Fixes #91117 - [x] Tests added. See: https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs --------- Co-authored-by: Tobias Koppers <sokra@users.noreply.github.com> Co-authored-by: Claude <noreply@anthropic.com>
Author
Parents
Loading