Skip to content

fix: relative route entry names, stable dev manifest version, CI stabilization - #116

Merged
ScriptedAlchemy merged 4 commits into
mainfrom
fix/route-entry-names-and-ci
Sep 3, 2026
Merged

fix: relative route entry names, stable dev manifest version, CI stabilization#116
ScriptedAlchemy merged 4 commits into
mainfrom
fix/route-entry-names-and-ci

Conversation

@ScriptedAlchemy

Copy link
Copy Markdown
Collaborator

Summary

Four changes that close out the open items on the repo after 0.6.0:

  • Relative route entry names (ports fix: ensure route chunk names are relative instead of absolute paths #112 by @alvadorn, fixes the case fix: stabilize route client entry requests #114 left open). Route tables built with relative() keep an absolute route id, and the plugin used that id as the rspack entry name for split route module chunks, so chunks landed under static/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.
  • Content-derived dev manifest version. The dev browser manifest used a random version per 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 in route-exports-modified-offscreen on main. The dev version now uses the same content hash as production.
  • Framework corpus stabilization for the three tests that failed the fail-fast ecosystem step on main since 2026-08-29: fog-of-war manifest snapshot race, the offscreen test's fixed 200ms sleep, and the RSC external redirect tests that navigated to the real example.com and latched onto the first of several navigations.
  • Release workflow. The rstackjs organization does not allow GitHub Actions to create pull requests, so the changesets "Version Packages" PR step fails (see the first Release run on 2026-09-03). The workflow now applies pending changesets directly on the dispatched branch, commits, pushes, and publishes.

Verification

  • pnpm typecheck, pnpm exec rstest run (all files pass)
  • route-exports-modified-offscreen under single-core stress (taskset -c 0, 24 repeats × 6 workers): 5 failures before the manifest version fix, 0 after
  • fog-of-war "URL gets too large" × 8 and the six external redirect tests × 3 under the same stress: all pass
  • hmr-hdr-test, fog-of-war-test, route-exports-modified-offscreen-test, route-entry-names-test, split-route-modules-test locally: 38 passed

Closes #112.

🤖 Generated with Claude Code

ScriptedAlchemy and others added 4 commits September 3, 2026 21:49
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>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 3, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-03T22:04:09.891148Z 8747f7c PR opened
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@pkg-pr-new

pkg-pr-new Bot commented Sep 3, 2026

Copy link
Copy Markdown

Open in StackBlitz

pnpm add https://pkg.pr.new/rsbuild-plugin-react-router@8747f7c

commit: 8747f7c

@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Benchmark results

Case Base Head Delta
build-256-ssr 1431.2 ms 1441.2 ms +0.7%
dev-48-ssr 617.8 ms 713.2 ms +15.4%

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread src/route-chunks.ts
.replace(/^[A-Za-z]:/, '')
.split('/')
.filter(segment => segment !== '' && segment !== '.')
.map(segment => (segment === '..' ? '__' : segment))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

@codspeed-hq

codspeed-hq Bot commented Sep 3, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 1 untouched benchmark
⏩ 2 skipped benchmarks1


Comparing fix/route-entry-names-and-ci (8747f7c) with main (bf05985)

Open in CodSpeed

Footnotes

  1. 2 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@ScriptedAlchemy
ScriptedAlchemy merged commit cabf4b9 into main Sep 3, 2026
9 checks passed
@ScriptedAlchemy
ScriptedAlchemy deleted the fix/route-entry-names-and-ci branch September 3, 2026 22:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant