Repository navigation
fix(app-router): preserve interception across middleware rewrites - #2342
Conversation
|
/bigbonk review for issues |
commit: |
|
Performance benchmarksCompared 0 improved · 0 regressed · 6 within ±1.5%
View detailed results and traces 🟢 improvement · 🔴 regression · ⚫ change below 1.5% · paired base/head |
There was a problem hiding this comment.
Review: preserve interception across middleware rewrites
I reviewed the diff against main, traced the runtime call paths, and ran the targeted unit suites (app-browser-interception-context, app-page-render-identity, navigation-planner, app-rsc-route-matching, app-browser-history-controller, app-page-element-builder, app-elements — all green) plus a type check. This looks correct and well-scoped.
What the change does (and why it's sound)
-
app-page-render-identity.ts/app-page-element-builder.ts—targetMatchedPathnamenow derives fromroutePath(the concretely matched route) instead ofdisplayPathname(browser-visible URL), withdisplayPathnameas the fallback. SincedisplayPathnamealready defaults toroutePathinbuildPageElements, the non-rewrite case is unchanged; only middleware-rewrite navigations see the corrected target.createAppPageRenderIdentityhas a single production caller, andtargetMatchedPathnameis optional with a safe default, so the blast radius is contained. -
app-browser-interception-context.ts— the target-pattern check is now evaluated first, then the source match is tried against both the browser-visiblecurrentPathnameand the new middleware-matchedcurrentMatchedPathname. This is what lets/interception-mw(visible) resolve via its rewritten/interception-mw/enmatch. The new param is optional and null-guarded, so existing callers are unaffected. -
app-browser-state.ts—matchedUrlnow prefersinterception.targetMatchedUrlwith a??fallback to the snapshot pathname. Non-interception snapshots are unchanged. -
navigation-planner.ts—matchedUrlFromConcreteRouteIdextracts the concrete URL from aroute:/...id (correct:encodeRouteIdstores the concrete matched pathname, not the pattern). The new branch infindRouteManifestRouteByIdOrMatchedUrlis correctly gated onroute === undefined, so it does not weaken the stale-route-safety path (verified by the "does not let stale manifest route IDs override the matched URL" test, whererouteis defined-but-non-matching and the new branch is skipped).
Dev/prod parity
The fix lives in the shared buildPageElements helper that both the App Router dev RSC entry and prod delegate to, so parity is preserved without a separate edit. This is App Router interception logic, so the Pages Router prod server isn't in scope.
Performance
getCurrentMatchedRoutePathname() is evaluated lazily as a call argument inside the middleware-rewrite fallback branch only — after the previousNextUrl and manifest-interception branches fail — so it does not run on the common navigation path.
Tests
Good coverage: the unit tests assert the new middleware-matched source/target resolution and that displayPathname is unchanged; the planner tests cover both concrete-dynamic-URL matching and stale-route safety. The e2e fixture now starts from the browser-visible /interception-mw (which middleware rewrites to /interception-mw/en) rather than the pre-rewritten URL, which genuinely exercises the new path. The middleware fixture correctly handles the bare /interception-mw path (rewrites to /interception-mw/en).
Minor / non-blocking
- No issues that block merge. One small note: the PR body documents that the Next.js deploy-suite parity run was aborted before deployment (worktree symlink /
vp envissue) rather than a passing assertion. The ported e2e spec plus the unit coverage give reasonable confidence, and CI's clean install/build path should cover the rest — worth confirming the e2e job is green before merge.
LGTM pending green CI.
|
Review posted on PR #2342. SummaryI reviewed the interception-across-middleware-rewrites fix, traced the runtime call paths, and ran the targeted unit suites (all green) plus a type check. No blocking issues found — LGTM pending green CI. Key findingsThe change is correct and well-scoped:
Parity & performance verified:
Tests are solid:
One non-blocking note: the PR body documents that the Next.js deploy-suite parity run aborted before deployment (worktree/ |
Summary
Validation
vp test run tests/app-browser-interception-context.test.ts tests/app-page-render-identity.test.ts tests/navigation-planner.test.ts tests/app-rsc-route-matching.test.ts tests/app-browser-history-controller.test.ts— 95 passedCI=1 PLAYWRIGHT_PROJECT=app-router playwright test tests/e2e/app-router/interception-dynamic-segment-middleware.spec.ts --workers=1 --retries=0— 4 passedvp checkon all changed filesvp run vinext#buildThe exact Next.js deploy-suite attempt stopped before deployment because the disposable worktree reuses a dependency symlink and
vp envattempted to replace it non-interactively. No suite assertion was reached; CI should run the clean install/build path.