Skip to content

Commit 4a850d1

Browse files
committed
fix(router): prioritize static suffix over dynamic segment in route matching
1 parent 1b75808 commit 4a850d1

File tree

1 file changed

+43
-13
lines changed

1 file changed

+43
-13
lines changed

packages/router-core/src/router.ts

Lines changed: 43 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,11 @@ import {
1111
} from './utils'
1212
import { processRouteTree } from './process-route-tree'
1313
import {
14+
SEGMENT_TYPE_PATHNAME,
1415
cleanPath,
1516
interpolatePath,
1617
matchPathname,
18+
parsePathname,
1719
resolvePath,
1820
trimPath,
1921
trimPathRight,
@@ -31,7 +33,7 @@ import {
3133
executeRewriteOutput,
3234
rewriteBasepath,
3335
} from './rewrite'
34-
import type { ParsePathnameCache } from './path'
36+
import type { ParsePathnameCache, Segment } from './path'
3537
import type { SearchParser, SearchSerializer } from './searchParams'
3638
import type { AnyRedirect, ResolvedRedirect } from './redirect'
3739
import type {
@@ -2686,26 +2688,54 @@ export function getMatchedRoutes<TRouteLike extends RouteLike>({
26862688
let fuzzyMatch:
26872689
| { foundRoute: TRouteLike; routeParams: Record<string, string> }
26882690
| undefined = undefined
2691+
const exactMatches: Array<{
2692+
route: TRouteLike
2693+
routeParams: Record<string, string>
2694+
endsWithStatic: boolean
2695+
}> = []
2696+
2697+
const getLastNonSlashSegment = (segments: ReadonlyArray<Segment>) => {
2698+
for (let i = segments.length - 1; i >= 0; i--) {
2699+
if (segments[i]?.value !== '/') return segments[i]
2700+
}
2701+
return undefined
2702+
}
2703+
26892704
for (const route of flatRoutes) {
26902705
const matchedParams = getMatchedParams(route)
26912706

26922707
if (matchedParams) {
2693-
if (
2694-
route.path !== '/' &&
2695-
(matchedParams as Record<string, string>)['**']
2696-
) {
2697-
if (!fuzzyMatch) {
2698-
fuzzyMatch = { foundRoute: route, routeParams: matchedParams }
2699-
}
2708+
const isFuzzy =
2709+
route.path !== '/' && (matchedParams as Record<string, string>)['**']
2710+
2711+
if (isFuzzy) {
2712+
fuzzyMatch ??= { foundRoute: route, routeParams: matchedParams }
27002713
} else {
2701-
foundRoute = route
2702-
routeParams = matchedParams
2703-
break
2714+
const routeSegments = parsePathname(route.fullPath, parseCache)
2715+
const pathSegments = parsePathname(trimmedPath, parseCache)
2716+
const lastRouteSegment = getLastNonSlashSegment(routeSegments)
2717+
const lastPathSegment = getLastNonSlashSegment(pathSegments)
2718+
const endsWithStatic =
2719+
lastRouteSegment?.type === SEGMENT_TYPE_PATHNAME &&
2720+
lastPathSegment?.type === SEGMENT_TYPE_PATHNAME &&
2721+
lastRouteSegment.value === lastPathSegment.value
2722+
2723+
exactMatches.push({
2724+
route,
2725+
routeParams: matchedParams,
2726+
endsWithStatic: !!endsWithStatic,
2727+
})
27042728
}
27052729
}
27062730
}
2707-
// did not find a perfect fit, so take the fuzzy matching route if it exists
2708-
if (!foundRoute && fuzzyMatch) {
2731+
2732+
if (exactMatches.length > 0) {
2733+
exactMatches.sort(
2734+
(a, b) => Number(b.endsWithStatic) - Number(a.endsWithStatic),
2735+
)
2736+
foundRoute = exactMatches[0]!.route
2737+
routeParams = exactMatches[0]!.routeParams
2738+
} else if (fuzzyMatch) {
27092739
foundRoute = fuzzyMatch.foundRoute
27102740
routeParams = fuzzyMatch.routeParams
27112741
}

0 commit comments

Comments
 (0)