fix(router): re-enter filesystem routes after rewrites - #2032
Conversation
commit: |
|
|
/bigbonk review for issues |
There was a problem hiding this comment.
Review: re-enter filesystem routes after rewrites
I reviewed the full diff, traced all three runtime adapters (dev index.ts, Node prod-server.ts, Worker deploy.ts), and ran the relevant suites locally.
Verification:
tests/pages-i18n-public-rewrite.test.ts— 13/13 pass (dev + prod)tests/pages-request-pipeline.test.ts+tests/deploy.test.ts— 336/336 passtests/pages-router.test.ts+tests/routing.test.ts— 405/405 pass (no regressions)vp checkon all changed source files — clean (format/lint/types)
Assessment: The core design is sound. Hoisting the filesystem/API checkpoint into a serveFilesystemRoute(pathname, phase) helper and re-running it after beforeFiles/afterFiles/fallback rewrites is the right shape, and the three adapters stay consistent:
- Node writes directly to
resand returnstrue→{ type: "handled" } - Dev/Workers return a
Response→ merged with staged middleware headers directphase is correctly skipped on Workers (assets served by the binding before the worker runs) and on dev (Vite serves directly), while Node keeps direct public-file serving but excludes_next/staticonly in thedirectphase
Page-vs-rewrite precedence is preserved correctly: the afterFiles loop is gated on !pageMatch || pageMatch.route.isDynamic, so a real page (/after-control) wins over an afterFiles rewrite — confirmed by the fixture test.
The dev replay of viteServePublicMiddleware/viteServeStaticMiddleware is careful: it snapshots req.url/status/headers, settles on finish/close/next(err) with a settled guard, removes listeners on settle, and restores state on fall-through. HEAD/Range/ETag/304/MIME parity is exercised by the fixture.
Non-blocking observations
-
Worker re-entry status override (
pages-request-pipeline.ts:312):mergeHeaders(served, middlewareHeaders, middlewareStatus)appliesmiddlewareStatusas a status override to the asset response. If middleware staged a status (e.g.200) and the asset returns206/304, the override would replace the asset's status. This matches how render/api results already mergemiddlewareStatus, so it is consistent with existing behavior rather than a new regression — but worth a comment, since a static-asset Range/conditional response is a new caller of this path. Low impact (requires middleware setting a status alongside a rewrite to a static file). -
Dev gating recomputes per request (
index.ts:3739-3765): theHeaders/Request/requestContextFromRequest/matchRewritework runs for every dev request reaching that block, even non-dotted paths, before theif. Dev-only and cheap, but it could be deferred behind the existingpathname.includes(".")check to avoid the allocation on the common path. -
Hardcoded
hadBasePath: truein the dev gatingmatchRewritecall (index.ts:3763): for a basePath app, a dotted path outside the basePath could spuriously match a rewrite source and enter the pipeline, which then falls through harmlessly. Matches the documentedmiddleware.ts:160convention, so this is intentional, but a one-line note would help future readers.
None of these block merge. Nice work on the cross-runtime consistency and the thorough fixture coverage.
|
Review posted to PR #2032. SummaryI reviewed PR #2032 ( Verification (all green):
Assessment: The design is sound. The Non-blocking observations I flagged:
No blockers found. |
Summary
beforeFiles,afterFiles, andfallbackrewriteslocale: falserewrites to public files and/_next/staticassetsNext.js parity
Fixes
test/e2e/i18n-ignore-rewrite-source-locale/rewrites.test.tsand closes broader filesystem rewrite parity across all rewrite phases.Validation
vp check, Cloudflare build, and vinext build passedCache Components, PPR, and resume behavior are out of scope.