[PR #783] [MERGED] fix: extract actual URL params for intercepting route source routes #841

Closed
opened 2026-05-06 13:10:25 +02:00 by BreizhHardware · 0 comments

📋 Pull Request Information

Original PR: https://github.com/cloudflare/vinext/pull/783
Author: @NathanDrake2406
Created: 4/4/2026
Status: Merged
Merged: 4/5/2026
Merged by: @james-elicx

Base: mainHead: fix/intercept-route-source-params


📝 Commits (4)

  • 534d977 fix: deduplicate inherited intercept target patterns in validation
  • 34362a6 fix: extract actual URL params for intercepting route source routes
  • b9c0ab4 fix: handle optional catch-all in source route param extraction
  • 13d02ec test: update entry template snapshots for catch-all reorder

📊 Changes

9 files changed (+279 additions, -12 deletions)

View changed files

📝 packages/vinext/src/entries/app-rsc-entry.ts (+25 -1)
📝 packages/vinext/src/routing/app-router.ts (+11 -5)
📝 tests/__snapshots__/entry-templates.test.ts.snap (+150 -6)
📝 tests/app-router.test.ts (+48 -0)
tests/fixtures/app-basic/app/team/[teamId]/members/@modal/(..)settings/page.tsx (+11 -0)
tests/fixtures/app-basic/app/team/[teamId]/members/@modal/default.tsx (+3 -0)
tests/fixtures/app-basic/app/team/[teamId]/members/layout.tsx (+14 -0)
tests/fixtures/app-basic/app/team/[teamId]/members/page.tsx (+8 -0)
tests/fixtures/app-basic/app/team/[teamId]/settings/page.tsx (+9 -0)

📄 Description

Summary

  • Fixes matchSourceRouteParams in the generated RSC entry to extract actual URL param values instead of literal pattern strings (e.g. {teamId: "42"} instead of {teamId: ":teamId"}).
  • Fixes false-positive route validation error when child routes inherit parent parallel slots with intercepting routes (duplicate intercept target patterns).

The bug

When an intercepting route's source route has dynamic segments (e.g. /team/[teamId]/members with @modal/(..)settings), the generated matchSourceRouteParams callback called matchRoute(pattern) — feeding the source route's pattern string through the URL trie. The trie's dynamic child matched the literal :teamId as a segment value, producing {teamId: ":teamId"} instead of extracting the real value from the request URL.

This only triggers on cross-route interception (source route != current route) with dynamic source routes. The existing tests used a static source route (/feed) so the bug was not caught.

The fix

Replace matchRoute(pattern) with a prefix-match param extractor that walks the source pattern and request URL (cleanPathname) in parallel:

  • Dynamic segments (:param) extract the corresponding URL segment value
  • Static segments verify alignment and stop on mismatch
  • Catch-all segments (+/*) consume remaining URL parts

This correctly handles all interception conventions: (.) same-level, (..) one-level-up, and (...) root.

Additionally, the intercept target pattern validation now deduplicates patterns via Set before checking for duplicates, since child routes inherit parent slots (and their intercepts), causing false duplicate detection.

Test plan

  • New integration test: RSC request to /team/42/settings with dynamic source route /team/:teamId/members verifies params contain "42" not ":teamId"
  • New SSR tests: direct navigation to /team/42/members and /team/42/settings render correctly
  • Existing intercept tests pass (static source route /feed with (...)photos/[id])
  • Existing intercept middleware header test passes
  • Routing tests pass (routing.test.ts, route-sorting.test.ts)
  • Entry template snapshots updated
  • app-page-request.test.ts unit tests pass

🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/cloudflare/vinext/pull/783 **Author:** [@NathanDrake2406](https://github.com/NathanDrake2406) **Created:** 4/4/2026 **Status:** ✅ Merged **Merged:** 4/5/2026 **Merged by:** [@james-elicx](https://github.com/james-elicx) **Base:** `main` ← **Head:** `fix/intercept-route-source-params` --- ### 📝 Commits (4) - [`534d977`](https://github.com/cloudflare/vinext/commit/534d977b212a8eed1a89c6fd822618e8cf7fc832) fix: deduplicate inherited intercept target patterns in validation - [`34362a6`](https://github.com/cloudflare/vinext/commit/34362a667dc824ba46d805df35e028901af77f27) fix: extract actual URL params for intercepting route source routes - [`b9c0ab4`](https://github.com/cloudflare/vinext/commit/b9c0ab4f000be60eafa1d6ecd3b303231f238dd5) fix: handle optional catch-all in source route param extraction - [`13d02ec`](https://github.com/cloudflare/vinext/commit/13d02ec03b46bf47279bc90b69b9a120d21a1c98) test: update entry template snapshots for catch-all reorder ### 📊 Changes **9 files changed** (+279 additions, -12 deletions) <details> <summary>View changed files</summary> 📝 `packages/vinext/src/entries/app-rsc-entry.ts` (+25 -1) 📝 `packages/vinext/src/routing/app-router.ts` (+11 -5) 📝 `tests/__snapshots__/entry-templates.test.ts.snap` (+150 -6) 📝 `tests/app-router.test.ts` (+48 -0) ➕ `tests/fixtures/app-basic/app/team/[teamId]/members/@modal/(..)settings/page.tsx` (+11 -0) ➕ `tests/fixtures/app-basic/app/team/[teamId]/members/@modal/default.tsx` (+3 -0) ➕ `tests/fixtures/app-basic/app/team/[teamId]/members/layout.tsx` (+14 -0) ➕ `tests/fixtures/app-basic/app/team/[teamId]/members/page.tsx` (+8 -0) ➕ `tests/fixtures/app-basic/app/team/[teamId]/settings/page.tsx` (+9 -0) </details> ### 📄 Description ## Summary - **Fixes** `matchSourceRouteParams` in the generated RSC entry to extract actual URL param values instead of literal pattern strings (e.g. `{teamId: "42"}` instead of `{teamId: ":teamId"}`). - **Fixes** false-positive route validation error when child routes inherit parent parallel slots with intercepting routes (duplicate intercept target patterns). ## The bug When an intercepting route's source route has dynamic segments (e.g. `/team/[teamId]/members` with `@modal/(..)settings`), the generated `matchSourceRouteParams` callback called `matchRoute(pattern)` — feeding the source route's pattern string through the URL trie. The trie's dynamic child matched the literal `:teamId` as a segment value, producing `{teamId: ":teamId"}` instead of extracting the real value from the request URL. This only triggers on cross-route interception (source route != current route) with dynamic source routes. The existing tests used a static source route (`/feed`) so the bug was not caught. ## The fix Replace `matchRoute(pattern)` with a prefix-match param extractor that walks the source pattern and request URL (`cleanPathname`) in parallel: - Dynamic segments (`:param`) extract the corresponding URL segment value - Static segments verify alignment and stop on mismatch - Catch-all segments (`+`/`*`) consume remaining URL parts This correctly handles all interception conventions: `(.)` same-level, `(..)` one-level-up, and `(...)` root. Additionally, the intercept target pattern validation now deduplicates patterns via `Set` before checking for duplicates, since child routes inherit parent slots (and their intercepts), causing false duplicate detection. ## Test plan - [x] New integration test: RSC request to `/team/42/settings` with dynamic source route `/team/:teamId/members` verifies params contain `"42"` not `":teamId"` - [x] New SSR tests: direct navigation to `/team/42/members` and `/team/42/settings` render correctly - [x] Existing intercept tests pass (static source route `/feed` with `(...)photos/[id]`) - [x] Existing intercept middleware header test passes - [x] Routing tests pass (`routing.test.ts`, `route-sorting.test.ts`) - [x] Entry template snapshots updated - [x] `app-page-request.test.ts` unit tests pass --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
BreizhHardware 2026-05-06 13:10:25 +02:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/vinext#841
No description provided.