1- import type { LoadComponentsReturnType } from '../../../server/load-components'
21import type { Params } from '../../../server/request/params'
3- import type {
4- AppPageRouteModule ,
5- AppPageModule ,
6- } from '../../../server/route-modules/app-page/module.compiled'
7- import type {
8- AppRouteRouteModule ,
9- AppRouteModule ,
10- } from '../../../server/route-modules/app-route/module.compiled'
2+ import type { AppPageRouteModule } from '../../../server/route-modules/app-page/module.compiled'
3+ import type { AppRouteRouteModule } from '../../../server/route-modules/app-route/module.compiled'
114import {
125 type AppSegmentConfig ,
136 parseAppSegmentConfig ,
@@ -25,6 +18,8 @@ import {
2518 type LoaderTree ,
2619} from '../../../server/lib/app-dir-module'
2720import { PAGE_SEGMENT_KEY } from '../../../shared/lib/segment'
21+ import type { FallbackRouteParam } from '../../static-paths/types'
22+ import { createFallbackRouteParam } from '../../static-paths/utils'
2823
2924type GenerateStaticParams = ( options : { params ?: Params } ) => Promise < Params [ ] >
3025
@@ -63,11 +58,17 @@ function attach(segment: AppSegment, userland: unknown, route: string) {
6358
6459export type AppSegment = {
6560 name : string
66- param : string | undefined
61+ paramName : string | undefined
6762 filePath : string | undefined
6863 config : AppSegmentConfig | undefined
6964 isDynamicSegment : boolean
7065 generateStaticParams : GenerateStaticParams | undefined
66+
67+ /**
68+ * Whether this segment is a parallel route segment or descends from a
69+ * parallel route segment.
70+ */
71+ isParallelRouteSegment : boolean | undefined
7172}
7273
7374/**
@@ -81,27 +82,32 @@ async function collectAppPageSegments(routeModule: AppPageRouteModule) {
8182 // to see the same segment multiple times.
8283 const uniqueSegments = new Map < string , AppSegment > ( )
8384
84- // Queue will store tuples of [loaderTree, currentSegments]
85- type QueueItem = [ LoaderTree , AppSegment [ ] ]
86- const queue : QueueItem [ ] = [ [ routeModule . userland . loaderTree , [ ] ] ]
85+ // Queue will store tuples of [loaderTree, currentSegments, isParallelRouteSegment]
86+ type QueueItem = [
87+ loaderTree : LoaderTree ,
88+ currentSegments : AppSegment [ ] ,
89+ isParallelRouteSegment : boolean ,
90+ ]
91+ const queue : QueueItem [ ] = [ [ routeModule . userland . loaderTree , [ ] , false ] ]
8792
8893 while ( queue . length > 0 ) {
89- const [ loaderTree , currentSegments ] = queue . shift ( ) !
94+ const [ loaderTree , currentSegments , isParallelRouteSegment ] = queue . shift ( ) !
9095 const [ name , parallelRoutes ] = loaderTree
9196
9297 // Process current node
9398 const { mod : userland , filePath } = await getLayoutOrPageModule ( loaderTree )
9499 const isClientComponent = userland && isClientReference ( userland )
95100
96- const param = getSegmentParam ( name ) ?. param
101+ const paramName = getSegmentParam ( name ) ?. param
97102
98103 const segment : AppSegment = {
99104 name,
100- param ,
105+ paramName ,
101106 filePath,
102107 config : undefined ,
103- isDynamicSegment : ! ! param ,
108+ isDynamicSegment : ! ! paramName ,
104109 generateStaticParams : undefined ,
110+ isParallelRouteSegment,
105111 }
106112
107113 // Only server components can have app segment configurations
@@ -129,15 +135,21 @@ async function collectAppPageSegments(routeModule: AppPageRouteModule) {
129135 // Add all parallel routes to the queue
130136 for ( const parallelRouteKey in parallelRoutes ) {
131137 const parallelRoute = parallelRoutes [ parallelRouteKey ]
132- queue . push ( [ parallelRoute , updatedSegments ] )
138+ queue . push ( [
139+ parallelRoute ,
140+ updatedSegments ,
141+ // A parallel route segment is one that descends from a segment that is
142+ // not children or descends from a parallel route segment.
143+ isParallelRouteSegment || parallelRouteKey !== 'children' ,
144+ ] )
133145 }
134146 }
135147
136148 return Array . from ( uniqueSegments . values ( ) )
137149}
138150
139151function getSegmentKey ( segment : AppSegment ) {
140- return `${ segment . name } -${ segment . filePath ?? '' } -${ segment . param ?? '' } `
152+ return `${ segment . name } -${ segment . filePath ?? '' } -${ segment . paramName ?? '' } `
141153}
142154
143155/**
@@ -157,16 +169,17 @@ function collectAppRouteSegments(
157169
158170 // Generate all the segments.
159171 const segments : AppSegment [ ] = parts . map ( ( name ) => {
160- const param = getSegmentParam ( name ) ?. param
172+ const paramName = getSegmentParam ( name ) ?. param
161173
162174 return {
163175 name,
164- param ,
176+ paramName ,
165177 filePath : undefined ,
166- isDynamicSegment : ! ! param ,
178+ isDynamicSegment : ! ! paramName ,
167179 config : undefined ,
168180 generateStaticParams : undefined ,
169- }
181+ isParallelRouteSegment : undefined ,
182+ } satisfies AppSegment
170183 } )
171184
172185 // We know we have at least one, we verified this above. We should get the
@@ -187,11 +200,9 @@ function collectAppRouteSegments(
187200 * @param components the loaded components
188201 * @returns the segments for the route module
189202 */
190- export function collectSegments ( {
191- routeModule,
192- } : LoadComponentsReturnType < AppPageModule | AppRouteModule > ) :
193- | Promise < AppSegment [ ] >
194- | AppSegment [ ] {
203+ export function collectSegments (
204+ routeModule : AppRouteRouteModule | AppPageRouteModule
205+ ) : Promise < AppSegment [ ] > | AppSegment [ ] {
195206 if ( isAppRouteRouteModule ( routeModule ) ) {
196207 return collectAppRouteSegments ( routeModule )
197208 }
@@ -204,3 +215,51 @@ export function collectSegments({
204215 'Expected a route module to be one of app route or page'
205216 )
206217}
218+
219+ /**
220+ * Collects the fallback route params for a given app page route module. This is
221+ * a variant of the `collectSegments` function that only collects the fallback
222+ * route params without importing anything.
223+ *
224+ * @param routeModule the app page route module
225+ * @returns the fallback route params for the app page route module
226+ */
227+ export function collectFallbackRouteParams (
228+ routeModule : AppPageRouteModule
229+ ) : readonly FallbackRouteParam [ ] {
230+ const uniqueSegments = new Map < string , FallbackRouteParam > ( )
231+
232+ // Queue will store tuples of [loaderTree, isParallelRouteSegment]
233+ type QueueItem = [ loaderTree : LoaderTree , isParallelRouteSegment : boolean ]
234+ const queue : QueueItem [ ] = [ [ routeModule . userland . loaderTree , false ] ]
235+
236+ while ( queue . length > 0 ) {
237+ const [ loaderTree , isParallelRouteSegment ] = queue . shift ( ) !
238+ const [ name , parallelRoutes ] = loaderTree
239+
240+ // Handle this segment (if it's a dynamic segment param).
241+ const segmentParam = getSegmentParam ( name )
242+ if ( segmentParam ) {
243+ const key = `${ name } -${ segmentParam . param } `
244+ if ( ! uniqueSegments . has ( key ) ) {
245+ uniqueSegments . set (
246+ key ,
247+ createFallbackRouteParam ( segmentParam . param , isParallelRouteSegment )
248+ )
249+ }
250+ }
251+
252+ // Add all of this segment's parallel routes to the queue.
253+ for ( const parallelRouteKey in parallelRoutes ) {
254+ const parallelRoute = parallelRoutes [ parallelRouteKey ]
255+ queue . push ( [
256+ parallelRoute ,
257+ // A parallel route segment is one that descends from a segment that is
258+ // not children or descends from a parallel route segment.
259+ isParallelRouteSegment || parallelRouteKey !== 'children' ,
260+ ] )
261+ }
262+ }
263+
264+ return Array . from ( uniqueSegments . values ( ) )
265+ }
0 commit comments