fix(config): match trailing-slash route sources#2255
Conversation
|
/bigbonk review for issues |
commit: |
|
|
/bigbonk review for issues |
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: trailing-slash config route source matching
I verified the core behavioral change against the authoritative Next.js implementation and ran the matchers across a broad parity matrix. The fix is correct and matches Next.js behavior. No actionable issues found.
Verification against Next.js
Next.js compiles config route sources (rewrites/redirects/headers) with path-to-regexp in non-strict mode (getPathMatch in shared/lib/router/utils/path-match.ts, default strict: false). I compiled the vendored Next.js path-to-regexp (.nextjs-ref/.../compiled/path-to-regexp) to derive the ground-truth match table:
| source | matches /x |
matches /x/ |
|---|---|---|
/x |
✅ | ✅ |
/x/ |
❌ | ✅ |
/:l(en|es) |
✅ | ✅ |
/:l(en|es)/ |
❌ | ✅ |
/api/:path*/ |
❌ (/api) |
✅ (/api/) |
Non-strict path-to-regexp appends an optional trailing-slash matcher, so a slash-ending source matches the slashed canonical form but not the slash-free form. The PR's new test assertions (matchConfigPattern("/en/", "/:lang(en|es)/") ⇒ match, "/en" ⇒ null; same for matchHeaders /docs/) line up exactly with this table.
I ran matchConfigPattern against a 13×13 pattern/pathname matrix (170 cases) cross-checked against the vendored path-to-regexp — all 170 matched Next.js exactly, including the catch-all edge cases (:path* zero-segment, :path+ minimum-segment) that the removed comment was guarding against. Param extraction values are also correct for slash-ending patterns.
Implementation notes (all sound)
matchConfigPattern: stripping the trailing slash from the pattern only when the pathname had one correctly reproduces non-strict optional-slash semantics without changing catch-all/root behavior. Root/is preserved (length > 1guard).matchRedirect: the locale-static fast path usesnormalizedPathnameforMap.getkeys (derived from slash-free sources), while the linear fallback passes the originalpathnameintomatchConfigPattern. Verified_LOCALE_STATIC_RErequires a non-empty suffix, so a slash-only source like/:lang(en|es)/falls to the linear path — consistent with the new test.matchHeaders: the compiled-regex cache key now matches the (possibly-stripped)sourceused to build the regex, so there is no cache poisoning. Cache growth is at most 2× a finite static rule set — negligible.- All callers (
matchRewrite,matchesRewriteSource, request pipelines) pass the unstripped pathname, which is what the fix expects.
Tests run (all green)
- New
tests/app-trailing-slash-isr.test.tsintegration test (rewrite + ISR/GSP end-to-end) — passes tests/shims.test.tsmatchConfigPattern / matchRedirect / matchHeaders suites — passtests/routing.test.ts,tests/route-sorting.test.ts,tests/isr-cache.test.ts— pass- 170-case + param-extraction parity checks vs vendored
path-to-regexp— pass
LGTM.
|
Review posted to PR #2255. SummaryI reviewed PR #2255 ( What the PR doesMakes Verification approachI cloned the Next.js reference repo and confirmed that Next.js compiles config sources with
The PR's new test assertions align exactly with verified Next.js semantics. LGTM. |
Summary
/Next.js parity
Targets two failures in the trailing-slash ISR/generateStaticParams deploy-suite cluster. The prerendered destination was correct; the canonical rewritten source failed before ISR assertions because its config source no longer matched.
Validation
vp run vinext#buildFull suites are deferred to CI.