fix: relative route entry names, stable dev manifest version, CI stabilization - #116
Conversation
Route tables built with `relative()` resolve route files to absolute paths. React Router relativizes `file` but keeps the derived `id` absolute, and the plugin used that id as the rspack entry name for split route module chunks. Chunks were emitted under a directory tree mirroring the developer's checkout and the browser manifest published those paths, leaking the local path into production assets and making builds unreproducible across machines. Derive the route entry and its chunk entry names from the app-relative route file instead, sanitized so an entry can never escape the JS output directory or carry a drive prefix. Route ids are untouched; they remain the runtime contract behind `useRouteLoaderData(id)` and `matches[].id`. Ports the fix from #112. Co-Authored-By: Igor Sant'Ana <igor@alvadorn.com.br> Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The rstackjs organization does not allow GitHub Actions to create pull requests, so the changesets action could not open its "Version Packages" PR and the first release run today failed at that step. Apply pending changesets directly on the dispatched branch instead: bump the version, write the changelog, commit, push, and then publish from a tree with no pending changesets. Runs without pending changesets go straight to publish, matching the previous behavior after the version PR merged. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
React Router's stale-client detection compares the manifest version the browser loaded with the version pinned to the server build and answers a mismatch on route discovery with a document reload. In development the browser manifest asset is served from the latest web compilation while the server build stays pinned to the compilation it was committed with, so a random per-compilation version made the two disagree whenever a web compilation completed without changing the manifest, for example the hot data revalidation recompile that follows a server change. The reload could land mid-navigation and surfaced in the framework corpus as a hydration mismatch on a route without a loader. Use the content hash that production builds already use, so equal manifests share a version and a real change still busts the browser cache through the `?v=` query on the development manifest URL. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Three tests failed the fail-fast ecosystem run on main, each for a different reason: - fog-of-war "skips prefetching if the URL gets too large" asserted an exact manifest snapshot right after navigating to /a, but /a links to /a/b and fog-of-war discovery for that link runs on its own debounce, so `routes/a.b` was sometimes already patched in. Assert on the routes the navigation had to load and on the absence of the dummy links. - route-exports-modified-offscreen slept 200ms after the edit; on a loaded runner the rebuild outlasted that and the next navigation was resynced with a document reload. Poll the dev server for the updated route instead. - the RSC external redirect tests navigated to the real example.com and waited on the first navigation, which aborts when React Router's error handler starts the redirect more than once. Serve a local stand-in for example.com and poll for the final URL like the sibling server action tests already do. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
commit: |
Benchmark results
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8747f7c021
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| .replace(/^[A-Za-z]:/, '') | ||
| .split('/') | ||
| .filter(segment => segment !== '' && segment !== '.') | ||
| .map(segment => (segment === '..' ? '__' : segment)) |
There was a problem hiding this comment.
Preserve distinct entry names when sanitizing
..
When an app contains a legitimate route such as app/__/shared/x.tsx and another route references ../shared/x.tsx, this replacement maps both files to the same entry name, __/shared/x. createClassicWebRouteEntries then overwrites one entry in its accumulator, and both manifest records resolve to whichever module was retained, so one route serves the wrong code. Encode parent traversal with a representation that cannot collide with valid in-app path segments.
Useful? React with 👍 / 👎.
Merging this PR will not alter performance
Comparing Footnotes
|
Summary
Four changes that close out the open items on the repo after 0.6.0:
relative()keep an absolute routeid, and the plugin used that id as the rspack entry name for split route module chunks, so chunks landed understatic/js/<checkout path>/...and the browser manifest published those paths. Entry and chunk names are now derived from the app-relative route file. Route ids are untouched.versionper web compilation while the server build stayed pinned to the compilation it was committed with. A web compilation that left the manifest unchanged (the HDR recompile after a server change) made React Router's stale-client check return 204 and reload the document mid-navigation. This is what produced the hydration mismatch failures inroute-exports-modified-offscreenon main. The dev version now uses the same content hash as production.Verification
pnpm typecheck,pnpm exec rstest run(all files pass)route-exports-modified-offscreenunder single-core stress (taskset -c 0, 24 repeats × 6 workers): 5 failures before the manifest version fix, 0 afterhmr-hdr-test,fog-of-war-test,route-exports-modified-offscreen-test,route-entry-names-test,split-route-modules-testlocally: 38 passedCloses #112.
🤖 Generated with Claude Code