@@ -161,57 +161,43 @@ describe('work in progress', () => {
161
161
` )
162
162
} )
163
163
164
- const parsedRoutes = result . flatRoutes . map ( ( route ) =>
165
- parsePathname ( route . fullPath ) ,
166
- )
167
-
168
- const logParsed = ( parsed : ReturnType < typeof parsePathname > ) =>
169
- '/' +
170
- parsed
171
- . slice ( 1 )
172
- . map ( ( s ) => s . value )
173
- . join ( '/' )
174
-
175
- const rebuildPath = ( leaf : ReturnType < typeof parsePathname > ) =>
176
- `/${ leaf
177
- . slice ( 1 )
178
- . map ( ( s ) =>
179
- s . value === '/'
180
- ? ''
181
- : `${ s . prefixSegment ?? '' } ${ s . prefixSegment || s . suffixSegment || s . type === SEGMENT_TYPE_OPTIONAL_PARAM ? '{' : '' } ${ s . type === SEGMENT_TYPE_OPTIONAL_PARAM ? '-' : '' } ${ s . value } ${ s . prefixSegment || s . suffixSegment || s . type === SEGMENT_TYPE_OPTIONAL_PARAM ? '}' : '' } ${ s . suffixSegment ?? '' } ` ,
182
- )
183
- . join ( '/' ) } `
164
+ const parsedRoutes = result . flatRoutes . map ( ( route ) => ( {
165
+ path : route . fullPath ,
166
+ segments : parsePathname ( route . fullPath ) ,
167
+ } ) )
184
168
185
169
const initialDepth = 0
186
170
let fn = 'const baseSegments = parsePathname(from);'
187
171
fn += '\nconst l = baseSegments.length;'
188
172
173
+ type ParsedRoute = { path : string , segments : ReturnType < typeof parsePathname > }
174
+
189
175
function recursiveStaticMatch (
190
- parsedRoutes : Array < ReturnType < typeof parsePathname > > ,
176
+ parsedRoutes : Array < ParsedRoute > ,
191
177
depth = initialDepth ,
192
178
indent = '' ,
193
179
) {
194
- const resolved = new Set < ReturnType < typeof parsePathname > > ( )
195
- for ( const routeSegments of parsedRoutes ) {
196
- if ( resolved . has ( routeSegments ) ) continue // already resolved
180
+ const resolved = new Set < ParsedRoute > ( )
181
+ for ( const route of parsedRoutes ) {
182
+ if ( resolved . has ( route ) ) continue // already resolved
197
183
console . log ( '\n' )
198
184
console . log (
199
185
'resolving: depth=' ,
200
186
depth ,
201
187
'parsed=' ,
202
- logParsed ( routeSegments ) ,
188
+ route . path ,
203
189
)
204
190
console . log ( '\u001b[34m' + fn + '\u001b[0m' )
205
- const currentSegment = routeSegments [ depth ]
191
+ const currentSegment = route . segments [ depth ]
206
192
if ( ! currentSegment ) {
207
193
throw new Error (
208
194
'Implementation error: this should not happen, depth=' +
209
195
depth +
210
- `, route=${ rebuildPath ( routeSegments ) } ` ,
196
+ `, route=${ route . path } ` ,
211
197
)
212
198
}
213
199
const candidates = parsedRoutes . filter ( ( r ) => {
214
- const rParsed = r [ depth ]
200
+ const rParsed = r . segments [ depth ]
215
201
if ( ! rParsed ) return false
216
202
217
203
// For SEGMENT_TYPE_PARAM (type 1), match only on type and prefix/suffix constraints
@@ -241,14 +227,14 @@ describe('work in progress', () => {
241
227
rParsed . suffixSegment === currentSegment . suffixSegment
242
228
)
243
229
} )
244
- console . log ( 'candidates:' , candidates . map ( logParsed ) )
230
+ console . log ( 'candidates:' , candidates . map ( r => r . path ) )
245
231
if ( candidates . length === 0 ) {
246
232
throw new Error ( 'Implementation error: this should not happen' )
247
233
}
248
234
if ( candidates . length > 1 ) {
249
- let skipDepth = routeSegments . slice ( depth + 1 ) . findIndex ( ( s , i ) =>
235
+ let skipDepth = route . segments . slice ( depth + 1 ) . findIndex ( ( s , i ) =>
250
236
candidates . some ( ( c ) => {
251
- const segment = c [ depth + 1 + i ]
237
+ const segment = c . segments [ depth + 1 + i ]
252
238
return (
253
239
! segment ||
254
240
segment . type !== s . type ||
@@ -259,12 +245,12 @@ describe('work in progress', () => {
259
245
)
260
246
} ) ,
261
247
)
262
- if ( skipDepth === - 1 ) skipDepth = routeSegments . length - depth - 1
248
+ if ( skipDepth === - 1 ) skipDepth = route . segments . length - depth - 1
263
249
const lCondition =
264
250
skipDepth || depth > initialDepth ? `l > ${ depth + skipDepth } ` : ''
265
251
const skipConditions =
266
252
Array . from ( { length : skipDepth + 1 } , ( _ , i ) => {
267
- const segment = candidates [ 0 ] ! [ depth + i ] !
253
+ const segment = candidates [ 0 ] ! . segments [ depth + i ] !
268
254
if ( segment . type === SEGMENT_TYPE_PARAM ) {
269
255
const conditions = [ ]
270
256
if ( segment . prefixSegment ) {
@@ -291,48 +277,56 @@ describe('work in progress', () => {
291
277
if ( hasCondition ) {
292
278
fn += `\n${ indent } if (${ lCondition } ${ lCondition && skipConditions ? ' && ' : '' } ${ skipConditions } ) {`
293
279
}
294
- const deeper = candidates . filter (
295
- ( c ) => c . length > depth + 1 + skipDepth ,
296
- )
297
- const leaves = candidates . filter (
298
- ( c ) => c . length <= depth + 1 + skipDepth ,
299
- )
300
- if ( deeper . length + leaves . length !== candidates . length ) {
301
- throw new Error ( 'Implementation error: this should not happen' )
280
+ const deeperBefore : Array < ParsedRoute > = [ ]
281
+ const deeperAfter : Array < ParsedRoute > = [ ]
282
+ let leaf : ParsedRoute | undefined
283
+ for ( const c of candidates ) {
284
+ const isLeaf = c . segments . length <= depth + 1 + skipDepth
285
+ if ( isLeaf && ! leaf ) {
286
+ leaf = c
287
+ continue
288
+ }
289
+ if ( isLeaf ) {
290
+ continue // ignore subsequent leaves, they can never be matched
291
+ }
292
+ if ( ! leaf ) {
293
+ deeperBefore . push ( c )
294
+ } else {
295
+ deeperAfter . push ( c )
296
+ }
302
297
}
303
- if ( deeper . length > 0 ) {
298
+ if ( deeperBefore . length > 0 ) {
304
299
recursiveStaticMatch (
305
- deeper ,
300
+ deeperBefore ,
306
301
depth + 1 + skipDepth ,
307
302
hasCondition ? indent + ' ' : indent ,
308
303
)
309
304
}
310
- if ( leaves . length > 1 ) {
311
- // WARN: we should probably support "multiple leaves"
312
- // 1. user error: it's possible that a user created both `/a/$id` and `/a/$foo`, they'd be both matched, just use the 1st one
313
- // 2. wildcards: if a user created both `/a/$` and `/a/b`, we could have 2 leaves. the order in `leaves` will be `[/a/b, /a/$]` which is correct, try to match `/a/b` first, then `/a/$`
314
- throw new Error (
315
- `Multiple candidates found for depth ${ depth } with type ${ routeSegments [ depth ] ! . type } and value ${ routeSegments [ depth ] ! . value } : ${ leaves . map ( logParsed ) . join ( ', ' ) } ` ,
316
- )
317
- } else if ( leaves . length === 1 ) {
318
- // WARN: is it ok that the leaf is matched last?
319
- fn += `\n${ indent } if (l === ${ leaves [ 0 ] ! . length } ) {`
320
- fn += `\n${ indent } return '${ rebuildPath ( leaves [ 0 ] ! ) } ';`
305
+ if ( leaf ) {
306
+ fn += `\n${ indent } if (l === ${ leaf . segments . length } ) {`
307
+ fn += `\n${ indent } return '${ leaf . path } ';`
321
308
fn += `\n${ indent } }`
322
309
}
310
+ if ( deeperAfter . length > 0 ) {
311
+ recursiveStaticMatch (
312
+ deeperAfter ,
313
+ depth + 1 + skipDepth ,
314
+ hasCondition ? indent + ' ' : indent ,
315
+ )
316
+ }
323
317
if ( hasCondition ) {
324
318
fn += `\n${ indent } }`
325
319
}
326
320
} else {
327
321
const leaf = candidates [ 0 ] !
328
322
329
323
// Check if this route contains a wildcard segment
330
- const wildcardIndex = leaf . findIndex ( ( s ) => s && s . type === SEGMENT_TYPE_WILDCARD )
324
+ const wildcardIndex = leaf . segments . findIndex ( ( s ) => s && s . type === SEGMENT_TYPE_WILDCARD )
331
325
332
326
if ( wildcardIndex !== - 1 && wildcardIndex >= depth ) {
333
327
// This route has a wildcard at or after the current depth
334
- const wildcardSegment = leaf [ wildcardIndex ] !
335
- const done = `return '${ rebuildPath ( leaf ) } ';`
328
+ const wildcardSegment = leaf . segments [ wildcardIndex ] !
329
+ const done = `return '${ leaf . path } ';`
336
330
337
331
// For wildcards, we need to check:
338
332
// 1. All static/param segments before the wildcard match
@@ -343,7 +337,7 @@ describe('work in progress', () => {
343
337
344
338
// Add conditions for all segments before the wildcard
345
339
for ( let i = depth ; i < wildcardIndex ; i ++ ) {
346
- const segment = leaf [ i ] !
340
+ const segment = leaf . segments [ i ] !
347
341
const value = `baseSegments[${ i } ].value`
348
342
349
343
if ( segment . type === SEGMENT_TYPE_PARAM ) {
@@ -383,10 +377,10 @@ describe('work in progress', () => {
383
377
fn += `\n${ indent } }`
384
378
} else {
385
379
// No wildcard in this route, use the original logic
386
- const done = `return '${ rebuildPath ( leaf ) } ';`
387
- fn += `\n${ indent } if (l === ${ leaf . length } `
388
- for ( let i = depth ; i < leaf . length ; i ++ ) {
389
- const segment = leaf [ i ] !
380
+ const done = `return '${ leaf . path } ';`
381
+ fn += `\n${ indent } if (l === ${ leaf . segments . length } `
382
+ for ( let i = depth ; i < leaf . segments . length ; i ++ ) {
383
+ const segment = leaf . segments [ i ] !
390
384
const value = `baseSegments[${ i } ].value`
391
385
392
386
// For SEGMENT_TYPE_PARAM (type 1), check if base has static segment (type 0) that satisfies constraints
@@ -602,6 +596,9 @@ describe('work in progress', () => {
602
596
) {
603
597
return '/about';
604
598
}
599
+ if (l === 1) {
600
+ return '/';
601
+ }
605
602
if (l > 1) {
606
603
if (l === 4
607
604
&& baseSegments[2].value === 'bar'
@@ -616,9 +613,6 @@ describe('work in progress', () => {
616
613
return '/$id/foo/bar';
617
614
}
618
615
}
619
- if (l === 1) {
620
- return '/';
621
- }
622
616
}"
623
617
` )
624
618
} )
0 commit comments