@tanstack/router-core 1.128.4 → 1.128.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/router.ts CHANGED
@@ -14,6 +14,10 @@ import {
14
14
  replaceEqualDeep,
15
15
  } from './utils'
16
16
  import {
17
+ SEGMENT_TYPE_OPTIONAL_PARAM,
18
+ SEGMENT_TYPE_PARAM,
19
+ SEGMENT_TYPE_PATHNAME,
20
+ SEGMENT_TYPE_WILDCARD,
17
21
  cleanPath,
18
22
  interpolatePath,
19
23
  joinPaths,
@@ -3246,7 +3250,7 @@ export function processRouteTree<TRouteLike extends RouteLike>({
3246
3250
  const scoredRoutes: Array<{
3247
3251
  child: TRouteLike
3248
3252
  trimmed: string
3249
- parsed: Array<Segment>
3253
+ parsed: ReadonlyArray<Segment>
3250
3254
  index: number
3251
3255
  scores: Array<number>
3252
3256
  hasStaticAfter: boolean
@@ -3261,12 +3265,14 @@ export function processRouteTree<TRouteLike extends RouteLike>({
3261
3265
  }
3262
3266
 
3263
3267
  const trimmed = trimPathLeft(d.fullPath)
3264
- const parsed = parsePathname(trimmed)
3268
+ let parsed = parsePathname(trimmed)
3265
3269
 
3266
3270
  // Removes the leading slash if it is not the only remaining segment
3267
- while (parsed.length > 1 && parsed[0]?.value === '/') {
3268
- parsed.shift()
3271
+ let skip = 0
3272
+ while (parsed.length > skip + 1 && parsed[skip]?.value === '/') {
3273
+ skip++
3269
3274
  }
3275
+ if (skip > 0) parsed = parsed.slice(skip)
3270
3276
 
3271
3277
  let optionalParamCount = 0
3272
3278
  let hasStaticAfter = false
@@ -3276,12 +3282,12 @@ export function processRouteTree<TRouteLike extends RouteLike>({
3276
3282
  }
3277
3283
 
3278
3284
  let baseScore: number | undefined = undefined
3279
- if (segment.type === 'param') {
3285
+ if (segment.type === SEGMENT_TYPE_PARAM) {
3280
3286
  baseScore = REQUIRED_PARAM_BASE_SCORE
3281
- } else if (segment.type === 'optional-param') {
3287
+ } else if (segment.type === SEGMENT_TYPE_OPTIONAL_PARAM) {
3282
3288
  baseScore = OPTIONAL_PARAM_BASE_SCORE
3283
3289
  optionalParamCount++
3284
- } else if (segment.type === 'wildcard') {
3290
+ } else if (segment.type === SEGMENT_TYPE_WILDCARD) {
3285
3291
  baseScore = WILDCARD_PARAM_BASE_SCORE
3286
3292
  }
3287
3293
 
@@ -3291,7 +3297,10 @@ export function processRouteTree<TRouteLike extends RouteLike>({
3291
3297
  // JUST FOR SORTING, NOT FOR MATCHING
3292
3298
  for (let i = index + 1; i < parsed.length; i++) {
3293
3299
  const nextSegment = parsed[i]!
3294
- if (nextSegment.type === 'pathname' && nextSegment.value !== '/') {
3300
+ if (
3301
+ nextSegment.type === SEGMENT_TYPE_PATHNAME &&
3302
+ nextSegment.value !== '/'
3303
+ ) {
3295
3304
  hasStaticAfter = true
3296
3305
  return handleParam(segment, baseScore + 0.2)
3297
3306
  }
@@ -3384,7 +3393,8 @@ export function getMatchedRoutes<TRouteLike extends RouteLike>({
3384
3393
  const result = matchPathname(basepath, trimmedPath, {
3385
3394
  to: route.fullPath,
3386
3395
  caseSensitive: route.options?.caseSensitive ?? caseSensitive,
3387
- fuzzy: false,
3396
+ // we need fuzzy matching for `notFoundMode: 'fuzzy'`
3397
+ fuzzy: true,
3388
3398
  })
3389
3399
  return result
3390
3400
  }
@@ -3394,16 +3404,34 @@ export function getMatchedRoutes<TRouteLike extends RouteLike>({
3394
3404
  if (foundRoute) {
3395
3405
  routeParams = getMatchedParams(foundRoute)!
3396
3406
  } else {
3397
- foundRoute = flatRoutes.find((route) => {
3407
+ // iterate over flatRoutes to find the best match
3408
+ // if we find a fuzzy matching route, keep looking for a perfect fit
3409
+ let fuzzyMatch:
3410
+ | { foundRoute: TRouteLike; routeParams: Record<string, string> }
3411
+ | undefined = undefined
3412
+ for (const route of flatRoutes) {
3398
3413
  const matchedParams = getMatchedParams(route)
3399
3414
 
3400
3415
  if (matchedParams) {
3401
- routeParams = matchedParams
3402
- return true
3416
+ if (
3417
+ route.path !== '/' &&
3418
+ (matchedParams as Record<string, string>)['**']
3419
+ ) {
3420
+ if (!fuzzyMatch) {
3421
+ fuzzyMatch = { foundRoute: route, routeParams: matchedParams }
3422
+ }
3423
+ } else {
3424
+ foundRoute = route
3425
+ routeParams = matchedParams
3426
+ break
3427
+ }
3403
3428
  }
3404
-
3405
- return false
3406
- })
3429
+ }
3430
+ // did not find a perfect fit, so take the fuzzy matching route if it exists
3431
+ if (!foundRoute && fuzzyMatch) {
3432
+ foundRoute = fuzzyMatch.foundRoute
3433
+ routeParams = fuzzyMatch.routeParams
3434
+ }
3407
3435
  }
3408
3436
 
3409
3437
  let routeCursor: TRouteLike = foundRoute || routesById[rootRouteId]!
@@ -3412,8 +3440,9 @@ export function getMatchedRoutes<TRouteLike extends RouteLike>({
3412
3440
 
3413
3441
  while (routeCursor.parentRoute) {
3414
3442
  routeCursor = routeCursor.parentRoute as TRouteLike
3415
- matchedRoutes.unshift(routeCursor)
3443
+ matchedRoutes.push(routeCursor)
3416
3444
  }
3445
+ matchedRoutes.reverse()
3417
3446
 
3418
3447
  return { matchedRoutes, routeParams, foundRoute }
3419
3448
  }