1
- import type { LoadComponentsReturnType } from '../../../server/load-components'
2
1
import 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'
11
4
import {
12
5
type AppSegmentConfig ,
13
6
parseAppSegmentConfig ,
@@ -25,6 +18,8 @@ import {
25
18
type LoaderTree ,
26
19
} from '../../../server/lib/app-dir-module'
27
20
import { PAGE_SEGMENT_KEY } from '../../../shared/lib/segment'
21
+ import type { FallbackRouteParam } from '../../static-paths/types'
22
+ import { createFallbackRouteParam } from '../../static-paths/utils'
28
23
29
24
type GenerateStaticParams = ( options : { params ?: Params } ) => Promise < Params [ ] >
30
25
@@ -63,11 +58,17 @@ function attach(segment: AppSegment, userland: unknown, route: string) {
63
58
64
59
export type AppSegment = {
65
60
name : string
66
- param : string | undefined
61
+ paramName : string | undefined
67
62
filePath : string | undefined
68
63
config : AppSegmentConfig | undefined
69
64
isDynamicSegment : boolean
70
65
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
71
72
}
72
73
73
74
/**
@@ -81,27 +82,32 @@ async function collectAppPageSegments(routeModule: AppPageRouteModule) {
81
82
// to see the same segment multiple times.
82
83
const uniqueSegments = new Map < string , AppSegment > ( )
83
84
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 ] ]
87
92
88
93
while ( queue . length > 0 ) {
89
- const [ loaderTree , currentSegments ] = queue . shift ( ) !
94
+ const [ loaderTree , currentSegments , isParallelRouteSegment ] = queue . shift ( ) !
90
95
const [ name , parallelRoutes ] = loaderTree
91
96
92
97
// Process current node
93
98
const { mod : userland , filePath } = await getLayoutOrPageModule ( loaderTree )
94
99
const isClientComponent = userland && isClientReference ( userland )
95
100
96
- const param = getSegmentParam ( name ) ?. param
101
+ const paramName = getSegmentParam ( name ) ?. param
97
102
98
103
const segment : AppSegment = {
99
104
name,
100
- param ,
105
+ paramName ,
101
106
filePath,
102
107
config : undefined ,
103
- isDynamicSegment : ! ! param ,
108
+ isDynamicSegment : ! ! paramName ,
104
109
generateStaticParams : undefined ,
110
+ isParallelRouteSegment,
105
111
}
106
112
107
113
// Only server components can have app segment configurations
@@ -129,15 +135,21 @@ async function collectAppPageSegments(routeModule: AppPageRouteModule) {
129
135
// Add all parallel routes to the queue
130
136
for ( const parallelRouteKey in parallelRoutes ) {
131
137
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
+ ] )
133
145
}
134
146
}
135
147
136
148
return Array . from ( uniqueSegments . values ( ) )
137
149
}
138
150
139
151
function getSegmentKey ( segment : AppSegment ) {
140
- return `${ segment . name } -${ segment . filePath ?? '' } -${ segment . param ?? '' } `
152
+ return `${ segment . name } -${ segment . filePath ?? '' } -${ segment . paramName ?? '' } `
141
153
}
142
154
143
155
/**
@@ -157,16 +169,17 @@ function collectAppRouteSegments(
157
169
158
170
// Generate all the segments.
159
171
const segments : AppSegment [ ] = parts . map ( ( name ) => {
160
- const param = getSegmentParam ( name ) ?. param
172
+ const paramName = getSegmentParam ( name ) ?. param
161
173
162
174
return {
163
175
name,
164
- param ,
176
+ paramName ,
165
177
filePath : undefined ,
166
- isDynamicSegment : ! ! param ,
178
+ isDynamicSegment : ! ! paramName ,
167
179
config : undefined ,
168
180
generateStaticParams : undefined ,
169
- }
181
+ isParallelRouteSegment : undefined ,
182
+ } satisfies AppSegment
170
183
} )
171
184
172
185
// We know we have at least one, we verified this above. We should get the
@@ -187,11 +200,9 @@ function collectAppRouteSegments(
187
200
* @param components the loaded components
188
201
* @returns the segments for the route module
189
202
*/
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 [ ] {
195
206
if ( isAppRouteRouteModule ( routeModule ) ) {
196
207
return collectAppRouteSegments ( routeModule )
197
208
}
@@ -204,3 +215,51 @@ export function collectSegments({
204
215
'Expected a route module to be one of app route or page'
205
216
)
206
217
}
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