Skip to content

Commit 62a761c

Browse files
authored
fix(router-core): favor index matches, or deepest non-index (#5933)
1 parent 5e130d7 commit 62a761c

File tree

2 files changed

+34
-8
lines changed

2 files changed

+34
-8
lines changed

packages/router-core/src/new-process-route-tree.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -878,7 +878,7 @@ function getNodeMatch<T extends RouteLike>(
878878
}
879879

880880
// perfect match, no need to continue
881-
if (statics === partsLength) return bestMatch
881+
if (statics === partsLength && node.isIndex) return bestMatch
882882
}
883883
// beyond the length of the path parts, only skipped optional segments or wildcard segments can match
884884
if (!node.optional && !node.wildcard) continue
@@ -1049,6 +1049,11 @@ function isFrameMoreSpecific(
10491049
next.statics > prev.statics ||
10501050
(next.statics === prev.statics &&
10511051
(next.dynamics > prev.dynamics ||
1052-
(next.dynamics === prev.dynamics && next.optionals > prev.optionals)))
1052+
(next.dynamics === prev.dynamics &&
1053+
(next.optionals > prev.optionals ||
1054+
(next.optionals === prev.optionals &&
1055+
(next.node.isIndex > prev.node.isIndex ||
1056+
(next.node.isIndex === prev.node.isIndex &&
1057+
next.depth > prev.depth)))))))
10531058
)
10541059
}

packages/router-core/tests/new-process-route-tree.test.ts

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,9 @@ describe('findRouteMatch', () => {
167167
})
168168
it('/optional/static/static vs /static/static', () => {
169169
const tree = makeTree(['/{-$other}/posts/new', '/posts/new'])
170-
expect(findRouteMatch('/posts/new', tree)?.route.id).toBe('/posts/new')
170+
expect(findRouteMatch('/posts/new', tree)?.route.id).toBe(
171+
'/{-$other}/posts/new',
172+
)
171173
})
172174
it('/optional/static/static/dynamic vs /static/dynamic/static/dynamic', () => {
173175
const tree = makeTree(['/{-$other}/posts/a/b/$c', '/posts/$a/b/$c'])
@@ -223,6 +225,14 @@ describe('findRouteMatch', () => {
223225
expect(findRouteMatch('/a/b', tree)?.route.id).toBe('/a/{-$b}/$c')
224226
})
225227
})
228+
it('optional child vs. shorter route', () => {
229+
const tree = makeTree(['/a', '/a/{-$b}'])
230+
expect(findRouteMatch('/a', tree)?.route.id).toBe('/a/{-$b}')
231+
232+
// but index can still win over optional child
233+
const treeWithIndex = makeTree(['/a/', '/a/{-$b}'])
234+
expect(findRouteMatch('/a', treeWithIndex)?.route.id).toBe('/a/')
235+
})
226236
})
227237
})
228238

@@ -487,14 +497,25 @@ describe('findRouteMatch', () => {
487497
const present = findRouteMatch('/yo/foo123bar/ma', tree)
488498
expect(present?.route.id).toBe('/yo/foo{-$id}bar/ma')
489499
})
490-
it('edge-case: ???', () => {
500+
it('edge-case: deeper optional chain vs. shallower optional chain', () => {
491501
// This test comes from the previous processRouteTree tests.
492-
// > This demonstrates that `/foo/{-$p}.tsx` will be matched, not `/foo/{-$p}/{-$x}.tsx`
493-
// > This route has 1 optional parameter, making it more specific than the route with 2
502+
// > This demonstrates that `/foo/{-$p}/{-$x}.tsx` will be matched, not `/foo/{-$p}.tsx`
503+
// > This route has 2 optional parameter, making it more specific than the route with 1
494504
const tree = makeTree(['/foo/{-$p}.tsx', '/foo/{-$p}/{-$x}.tsx'])
495-
expect(findRouteMatch('/foo', tree)?.route.id).toBe('/foo/{-$p}.tsx')
505+
expect(findRouteMatch('/foo', tree)?.route.id).toBe(
506+
'/foo/{-$p}/{-$x}.tsx',
507+
)
496508
expect(findRouteMatch('/foo/bar.tsx', tree)?.route.id).toBe(
497-
'/foo/{-$p}.tsx',
509+
'/foo/{-$p}/{-$x}.tsx',
510+
)
511+
512+
// but index can still win over deeper optional chain
513+
const treeWithIndex = makeTree([
514+
'/foo/{-$p}.tsx/',
515+
'/foo/{-$p}/{-$x}.tsx',
516+
])
517+
expect(findRouteMatch('/foo/', treeWithIndex)?.route.id).toBe(
518+
'/foo/{-$p}.tsx/',
498519
)
499520
})
500521

0 commit comments

Comments
 (0)