Skip to content

Commit c1f6dfe

Browse files
authored
fix(router-core): processRouteTree orders routes w/ prefix / suffix based on their lengths (#4850)
When ordering routes to determine "which one will match in priority", we try to follow the "more specific 1st" principle. Currently, 2 routes with the same pattern, but with prefixes of different lengths, are counted as having the same specificity, and thus are not re-ordered. For example: - `/f{$param}` - `/foo{$param}` In this case, assuming `f{$param}.tsx` is returned 1st by the OS when listing route files (which it is by alphabetical order), a path `/foobar` will match `/f{$param}`. This PR proposes we take the `length` of the `prefix` and `suffix` into account when sorting routes. In this example this would mean `/foo{$param}` takes priority because it is considered more specific.
1 parent 9061654 commit c1f6dfe

File tree

2 files changed

+46
-3
lines changed

2 files changed

+46
-3
lines changed

packages/router-core/src/router.ts

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3212,18 +3212,36 @@ export type ProcessRouteTreeResult<TRouteLike extends RouteLike> = {
32123212
const REQUIRED_PARAM_BASE_SCORE = 0.5
32133213
const OPTIONAL_PARAM_BASE_SCORE = 0.4
32143214
const WILDCARD_PARAM_BASE_SCORE = 0.25
3215+
const BOTH_PRESENCE_BASE_SCORE = 0.05
3216+
const PREFIX_PRESENCE_BASE_SCORE = 0.02
3217+
const SUFFIX_PRESENCE_BASE_SCORE = 0.01
3218+
const PREFIX_LENGTH_SCORE_MULTIPLIER = 0.0002
3219+
const SUFFIX_LENGTH_SCORE_MULTIPLIER = 0.0001
32153220

32163221
function handleParam(segment: Segment, baseScore: number) {
32173222
if (segment.prefixSegment && segment.suffixSegment) {
3218-
return baseScore + 0.05
3223+
return (
3224+
baseScore +
3225+
BOTH_PRESENCE_BASE_SCORE +
3226+
PREFIX_LENGTH_SCORE_MULTIPLIER * segment.prefixSegment.length +
3227+
SUFFIX_LENGTH_SCORE_MULTIPLIER * segment.suffixSegment.length
3228+
)
32193229
}
32203230

32213231
if (segment.prefixSegment) {
3222-
return baseScore + 0.02
3232+
return (
3233+
baseScore +
3234+
PREFIX_PRESENCE_BASE_SCORE +
3235+
PREFIX_LENGTH_SCORE_MULTIPLIER * segment.prefixSegment.length
3236+
)
32233237
}
32243238

32253239
if (segment.suffixSegment) {
3226-
return baseScore + 0.01
3240+
return (
3241+
baseScore +
3242+
SUFFIX_PRESENCE_BASE_SCORE +
3243+
SUFFIX_LENGTH_SCORE_MULTIPLIER * segment.suffixSegment.length
3244+
)
32273245
}
32283246

32293247
return baseScore

packages/router-core/tests/processRouteTree.test.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -425,5 +425,30 @@ describe('processRouteTree', () => {
425425
expect(result.flatRoutes.map((r) => r.id)).toEqual(expected)
426426
},
427427
)
428+
429+
it.each([
430+
{
431+
routes: ['/f{$param}', '/foo{$param}'],
432+
expected: ['/foo{$param}', '/f{$param}'],
433+
},
434+
{
435+
routes: ['/{$param}r', '/{$param}bar'],
436+
expected: ['/{$param}bar', '/{$param}r'],
437+
},
438+
{
439+
routes: ['/f{$param}bar', '/foo{$param}r'],
440+
expected: ['/foo{$param}r', '/f{$param}bar'],
441+
},
442+
{
443+
routes: ['/foo{$param}r', '/f{$param}baaaaaar'], // very long suffix can "override" prefix
444+
expected: ['/f{$param}baaaaaar', '/foo{$param}r'],
445+
},
446+
])(
447+
'length of prefix and suffix are considered in ranking: $routes',
448+
({ routes, expected }) => {
449+
const result = processRouteTree({ routeTree: createRouteTree(routes) })
450+
expect(result.flatRoutes.map((r) => r.id)).toEqual(expected)
451+
},
452+
)
428453
})
429454
})

0 commit comments

Comments
 (0)