Skip to content

fix(routing): match inherited slot params against routePath, not request.url#1040

Merged
james-elicx merged 1 commit intomainfrom
fix/inherited-slot-basepath-and-memoize
May 3, 2026
Merged

fix(routing): match inherited slot params against routePath, not request.url#1040
james-elicx merged 1 commit intomainfrom
fix/inherited-slot-basepath-and-memoize

Conversation

@james-elicx
Copy link
Copy Markdown
Collaborator

Follow-up to #1039, addressing two items I missed during review.

Summary

  • Bug under basePath: buildSlotOverrides was deriving the URL parts to match against slot.slotPatternParts from request.url. The original request URL still carries the configured basePath, but slotPatternParts is built from app-relative segments only — so for any basePath-configured app, the pattern match silently failed and slot params fell back to the route's matched params (the exact bug fix(routing): mirror inherited parallel slots to descendant sub-pages #1039 was supposed to fix). Switched to the already-normalized routePath parameter (basePath stripped, RSC suffix removed) that buildPageElements already receives.
  • Memoize findSlotSubPages: cached per-build via a WeakMap keyed by the matcher. Inherited slots get scanned once per descendant route, so without memoization a route N segments deep paid O(N) full subtree walks for every shared ancestor slot. Cache lifetime tracks the matcher (one per build), so a long-lived dev server doesn't accumulate state across builds.
  • Dedupe: findMirroredSlotPage was calling convertSegmentsToRouteParts(segmentsBelow) twice when the literal-tier match missed. Hoisted it out so both tiers reuse the conversion.

Test plan

  • New unit test in tests/app-page-element-builder.test.ts exercises the basePath regression: request URL is http://localhost/base/distinct/alice while routePath is /distinct/alice, slot has slotPatternParts: ["distinct", ":name"]. Without the fix, urlParts would be ["base","distinct","alice"], the match would fail, and the slot's resolved params would silently lose the name value. With the fix, slotParams resolves to { name: "alice" }.
  • pnpm exec vp test run --project unit — 3524 tests pass.
  • pnpm check — only pre-existing failure (benchmarks/vinext/vite.config.ts cannot resolve vinext until the package is built; same on main).

🤖 Generated with Claude Code

…est.url

Follow-up to #1039. Two related polish items in the inherited-slot
mirror runtime:

1. Bug under `basePath`: `buildSlotOverrides` was parsing `request.url`
   to derive the URL parts to match against `slot.slotPatternParts`. The
   original request URL still carries the configured `basePath`, but
   `slotPatternParts` is built from app-relative segments only — so for
   every basePath-configured app, the pattern match silently failed and
   slot params fell back to the route's matched params. Use the already
   normalized `routePath` parameter (basePath stripped, RSC suffix
   removed) instead. Added a unit test that exercises the regression.

2. Memoize `findSlotSubPages` per build via a `WeakMap` keyed by the
   matcher. Inherited slots get scanned once per descendant route, so
   without memoization a route N segments deep paid O(N) full subtree
   walks for every shared ancestor slot. Cache lifetime tracks the
   matcher's, so a long-lived dev server doesn't accumulate state across
   builds.

Also dedupes a redundant `convertSegmentsToRouteParts` call inside
`findMirroredSlotPage` (both tiers needed the same conversion).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented May 3, 2026

Open in StackBlitz

npm i https://pkg.pr.new/vinext@1040

commit: f35c27b

@james-elicx
Copy link
Copy Markdown
Collaborator Author

/bigbonk review

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 3, 2026

Example Preview Production Original
app-router-cloudflare preview production
pages-router-cloudflare preview production
app-router-playground preview production original
realworld-api-rest preview production
nextra-docs-template preview production
benchmarks preview production
hackernews preview production original

Copy link
Copy Markdown
Contributor

@ask-bonk ask-bonk Bot left a comment

Choose a reason for hiding this comment

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

Clean follow-up. All three changes are correct and well-tested.

basePath fix — The root cause was straightforward: buildSlotOverrides was parsing request.url to get URL parts for matching against slotPatternParts, but slotPatternParts are app-relative (basePath already stripped during graph build). The routePath parameter is already the normalized form (basePath stripped, RSC suffix removed), so switching to it is the right fix. The new test directly exercises this by setting request.url to http://localhost/base/distinct/alice while routePath is /distinct/alice, proving the old code would have produced ["base","distinct","alice"] and silently failed the match.

MemoizationfindSlotSubPages does synchronous fs.readdirSync + fs.existsSync walks, so caching per (matcher, slotDir) is a clear win for routes with deep inherited slot chains. The WeakMap<ValidFileMatcher, ...> keying is appropriate since ValidFileMatcher is an object created once per build (via createValidFileMatcher), and the cache naturally dies when the matcher is GC'd. Both call sites (line 332 in discoverSlotSubRoutes and line 907 in findMirroredSlotPage) only iterate the returned array without mutating it, so sharing the cached reference is safe.

Dedupe — Hoisting convertSegmentsToRouteParts above both tiers in findMirroredSlotPage is a trivial but correct cleanup. The conversion only depends on segmentsBelow which doesn't change between tiers.

CI is all green. Ship it.

@ask-bonk
Copy link
Copy Markdown
Contributor

ask-bonk Bot commented May 3, 2026

Approved PR #1040. All three changes are correct:

  1. basePath fixbuildSlotOverrides now uses the already-normalized routePath instead of re-parsing request.url, which would re-introduce the basePath prefix and silently break slot param matching.

  2. MemoizationfindSlotSubPages caches results in a WeakMap keyed by matcher, avoiding redundant filesystem walks for deep inherited slot chains. Both callers only iterate the returned array (no mutation), so sharing is safe.

  3. DedupeconvertSegmentsToRouteParts hoisted above both tiers in findMirroredSlotPage to avoid the redundant second call.

CI is all green across all 30 checks.

github run

@james-elicx james-elicx enabled auto-merge (squash) May 3, 2026 17:14
@james-elicx james-elicx merged commit 3cd2bfc into main May 3, 2026
33 checks passed
@james-elicx james-elicx deleted the fix/inherited-slot-basepath-and-memoize branch May 3, 2026 17:15
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