Fix prerendering of interception routes with generateStaticParams (#85835)
## What
Fixes a bug where interception routes in parallel slots could not be
prerendered using `generateStaticParams`, causing 404 responses when
these routes were accessed directly.
## Why
**The Problem:**
Interception routes like `app/@modal/(.)photo/[id]/page.tsx` could not
be prerendered even when they exported `generateStaticParams`. This was
because the static path generation code only examined "children"
segments in the loader tree, completely missing segments from parallel
routes (like `@modal`) that actually contribute to the pathname.
**Root Cause:**
The previous implementation used `childrenRouteParamSegments` which only
traversed the `children` branch of the loader tree:
```typescript
// OLD: Only looked at children
const childrenRouteParamSegments = [...segments from children only...]
// This missed parallel routes like @modal that have dynamic segments
```
For a route structure like:
```
app/
[username]/
page.tsx
@modal/
(.)[username]/
[id]/
page.tsx // ← This route's segments were MISSED
```
The build system couldn't discover the `[id]` parameter in the parallel
route because it never traversed that branch of the tree.
## How
**Solution:**
Introduces `extractPathnameSegments()` which properly traverses the
ENTIRE loader tree (not just children) to find ALL segments that
contribute to the pathname:
1. **BFS Traversal**: Explores both `children` AND all parallel route
slots (e.g., `@modal`, `@sidebar`)
2. **Depth Tracking**: Correctly tracks URL depth by:
- Skipping route groups `(marketing)` - not in URL
- Skipping parallel markers `@modal` - not in URL
- Including interception markers `(.)photo` - ARE in URL
3. **Prefix Validation**: Ensures static segments match the target
pathname before including dynamic segments
4. **Complete Parameter Discovery**: Returns all segments that
contribute to pathname construction, regardless of which tree branch
they're in
**Example:**
For `app/@modal/(.)photo/[id]/page.tsx`:
- Old: Missed the `[id]` parameter entirely
- New: Discovers `[id]` and enables prerendering with
`generateStaticParams`
## Changes
**New Module**: `extract-pathname-segments.ts` (192 lines)
- Core algorithm for traversing loader tree and extracting pathname
segments
- Handles complex cases: parallel routes, interception routes, route
groups
- Well-documented with examples and algorithm explanation
**Comprehensive Tests**: `extract-pathname-segments.test.ts` (897 lines)
- Tests for simple cases, nested structures, parallel routes
- Interception route handling in various configurations
- Route group behavior and edge cases
- Depth tracking validation
**Integration**: `static-paths/app.ts`
- Replaced `childrenRouteParamSegments` with `extractPathnameSegments()`
- Updated pathname construction to use segments from parallel routes
- Maintained backward compatibility
**E2E Test**: Added test validating prerendering works for intercepted
routes
## Test Plan
The new E2E test verifies:
```typescript
it('should prerender a dynamic intercepted route', async () => {
// Verifies build output contains the prerendered interception route
expect(next.cliOutput).toContain('/(.)john/1')
// Verifies it doesn't generate the non-intercepted path
expect(next.cliOutput).not.toContain('/john/1')
})
```
## Impact
**Before**: Interception routes with dynamic segments returned 404 when
accessed directly, even with `generateStaticParams`
**After**: These routes are properly prerendered at build time and
return correct responses