mirror of
https://github.com/cloudflare/vinext.git
synced 2026-09-14 19:04:59 +08:00
codex/cacheability-platform-io
1 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
68c90808e0 |
fix(routing): discover nested parallel slot sub-routes from layout-only parents (#1051)
* fix(routing): discover nested parallel slot sub-routes from layout-only parents Nested parallel routes failed when the parent segment had a layout but no page.tsx. discoverSlotSubRoutes skipped any parent route without pagePath, so slots like @parallelB/nested/page.tsx under home/layout.tsx (but no home/page.tsx) were never discovered. Next.js explicitly tests this shape in parallel-routes-and-interception: app/parallel-nested/home/layout.tsx + @parallelB/default.tsx + @parallelB/nested/page.tsx creates a valid /parallel-nested/home/nested route. The fix makes three targeted changes to discoverSlotSubRoutes: 1. Remove the hard pagePath requirement. Layout-only routes with parallel slots can still own nested slot sub-routes. 2. Compute parentPageDir from the innermost layout when pagePath is null, so slot ownership checks still work for layout-only routes. 3. Relax the children default.tsx requirement, but only for layout-only parent routes. When a parent HAS a children page, default.tsx is still required as a fallback for the synthetic sub-route. Layout-only parents do not need this fallback because the children slot was never occupied. 4. Add structural-conflict detection before creating synthetic routes. This prevents layout-only root routes from generating synthetic routes that collide with existing page routes differing only by param name (e.g. /shop/:id vs /shop/:name), which validateRoutePatterns rejects. Tests ported from the Next.js parallel-routes-and-interception suite cover both route-discovery and end-to-end rendering. Refs: https://github.com/vercel/next.js/blob/canary/test/e2e/app-dir/parallel-routes-and-interception/parallel-routes-and-interception.test.ts * fix(routing): correct patternsStructurallyEquivalent tree-node comparison The initial implementation of patternsStructurallyEquivalent used a naive dynamic-segment check (startsWith(':')) that produced two categories of false positives: 1. Literal vs dynamic segments (e.g. 'about' vs ':id') were considered equivalent, incorrectly suppressing valid synthetic routes. 2. Single dynamic vs catch-all segments (e.g. ':id' vs ':slug+') were considered equivalent, even though they map to different validator tree nodes ([] vs [...]). Replace the ad-hoc boolean logic with segmentTreeNodeType, which classifies each segment by its Next.js validator tree node (literal, dynamic, catchAll, optionalCatchAll). Two patterns are structurally equivalent only when every segment maps to the same tree node type. Also add a clarifying comment to the parentPageDir computation so the next developer understands why the innermost layout proxies the route directory for layout-only routes. * fix(routing): scan synthetic routes in structural conflict check Copilot review identified that patternsStructurallyEquivalent only scanned the original routes array, missing conflicts between synthetic routes created earlier in the same loop. Fixed by scanning routesByPattern.values() which includes both original and synthetic routes. Also adds a TODO comment documenting the known prerender limitation for layout-only synthetic routes (skipped at build/prerender.ts:849). These routes work in dev/SSR but won't be statically exported — this is pre-existing behaviour for all layout-only routes. * test(routing): cover structural conflict skip for synthetic parallel routes Adds a unit test proving that discoverSlotSubRoutes skips synthetic routes when an existing page route has the same structural shape but a different param name (e.g. /shop/:id page + @feed/[name]/page.tsx slot). Without this coverage the structural-conflict safeguard is easy to regress. * fix(routing): guard discoverSlotSubRoutes to layout-only UI routes Review identified that removing the old !parentRoute.pagePath guard also let route-handler-only AppRoutes enter discoverSlotSubRoutes. Route handlers have pagePath: null but are not layout-only UI routes. A shape like app/api/route.ts + app/@feed/foo/page.tsx could make the /api route handler scan the ancestor slot and materialise a synthetic UI route under /api/foo. Add an explicit isLayoutOnlyUiRoute check: !parentRoute.pagePath && !parentRoute.routePath && parentRoute.layouts.length > 0 Only page-bearing routes or layout-only UI routes can own nested parallel- slot sub-routes. Route handlers and layout-less routes are skipped. Also adds a regression test: a route.ts handler with an ancestor parallel slot must not emit synthetic routes under the handler pattern. * fix(routing): scan synthetic routes in structural conflict check + regression test The structural-conflict guard had regressed back to scanning the original routes array, missing conflicts against synthetic routes created earlier in the same discoverSlotSubRoutes pass. Fixed by scanning Array.from(routesByPattern.values()) instead. Also adds a regression test proving that two slot sub-pages with different param names under the same parent do not both materialise (e.g. @a/[id] and @b/[name] under /shop — only one of /shop/:id or /shop/:name is emitted). |