@tanstack/router-core 1.128.3 → 1.128.4

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/router-core",
3
- "version": "1.128.3",
3
+ "version": "1.128.4",
4
4
  "description": "Modern and scalable routing for React applications",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
package/src/path.ts CHANGED
@@ -7,6 +7,8 @@ export interface Segment {
7
7
  value: string
8
8
  prefixSegment?: string
9
9
  suffixSegment?: string
10
+ // Indicates if there is a static segment after this required/optional param
11
+ hasStaticAfter?: boolean
10
12
  }
11
13
 
12
14
  export function joinPaths(paths: Array<string | undefined>) {
package/src/router.ts CHANGED
@@ -29,6 +29,7 @@ import { setupScrollRestoration } from './scroll-restoration'
29
29
  import { defaultParseSearch, defaultStringifySearch } from './searchParams'
30
30
  import { rootRouteId } from './root'
31
31
  import { isRedirect, redirect } from './redirect'
32
+ import type { Segment } from './path'
32
33
  import type { SearchParser, SearchSerializer } from './searchParams'
33
34
  import type { AnyRedirect, ResolvedRedirect } from './redirect'
34
35
  import type {
@@ -3178,6 +3179,27 @@ export type ProcessRouteTreeResult<TRouteLike extends RouteLike> = {
3178
3179
  routesByPath: Record<string, TRouteLike>
3179
3180
  flatRoutes: Array<TRouteLike>
3180
3181
  }
3182
+
3183
+ const REQUIRED_PARAM_BASE_SCORE = 0.5
3184
+ const OPTIONAL_PARAM_BASE_SCORE = 0.4
3185
+ const WILDCARD_PARAM_BASE_SCORE = 0.25
3186
+
3187
+ function handleParam(segment: Segment, baseScore: number) {
3188
+ if (segment.prefixSegment && segment.suffixSegment) {
3189
+ return baseScore + 0.05
3190
+ }
3191
+
3192
+ if (segment.prefixSegment) {
3193
+ return baseScore + 0.02
3194
+ }
3195
+
3196
+ if (segment.suffixSegment) {
3197
+ return baseScore + 0.01
3198
+ }
3199
+
3200
+ return baseScore
3201
+ }
3202
+
3181
3203
  export function processRouteTree<TRouteLike extends RouteLike>({
3182
3204
  routeTree,
3183
3205
  initRoute,
@@ -3224,9 +3246,11 @@ export function processRouteTree<TRouteLike extends RouteLike>({
3224
3246
  const scoredRoutes: Array<{
3225
3247
  child: TRouteLike
3226
3248
  trimmed: string
3227
- parsed: ReturnType<typeof parsePathname>
3249
+ parsed: Array<Segment>
3228
3250
  index: number
3229
3251
  scores: Array<number>
3252
+ hasStaticAfter: boolean
3253
+ optionalParamCount: number
3230
3254
  }> = []
3231
3255
 
3232
3256
  const routes: Array<TRouteLike> = Object.values(routesById)
@@ -3244,63 +3268,50 @@ export function processRouteTree<TRouteLike extends RouteLike>({
3244
3268
  parsed.shift()
3245
3269
  }
3246
3270
 
3247
- const scores = parsed.map((segment) => {
3271
+ let optionalParamCount = 0
3272
+ let hasStaticAfter = false
3273
+ const scores = parsed.map((segment, index) => {
3248
3274
  if (segment.value === '/') {
3249
3275
  return 0.75
3250
3276
  }
3251
3277
 
3278
+ let baseScore: number | undefined = undefined
3252
3279
  if (segment.type === 'param') {
3253
- if (segment.prefixSegment && segment.suffixSegment) {
3254
- return 0.55
3255
- }
3256
-
3257
- if (segment.prefixSegment) {
3258
- return 0.52
3259
- }
3260
-
3261
- if (segment.suffixSegment) {
3262
- return 0.51
3263
- }
3264
-
3265
- return 0.5
3280
+ baseScore = REQUIRED_PARAM_BASE_SCORE
3281
+ } else if (segment.type === 'optional-param') {
3282
+ baseScore = OPTIONAL_PARAM_BASE_SCORE
3283
+ optionalParamCount++
3284
+ } else if (segment.type === 'wildcard') {
3285
+ baseScore = WILDCARD_PARAM_BASE_SCORE
3266
3286
  }
3267
3287
 
3268
- if (segment.type === 'optional-param') {
3269
- if (segment.prefixSegment && segment.suffixSegment) {
3270
- return 0.45
3271
- }
3272
-
3273
- if (segment.prefixSegment) {
3274
- return 0.42
3275
- }
3276
-
3277
- if (segment.suffixSegment) {
3278
- return 0.41
3279
- }
3280
-
3281
- return 0.4
3282
- }
3283
-
3284
- if (segment.type === 'wildcard') {
3285
- if (segment.prefixSegment && segment.suffixSegment) {
3286
- return 0.3
3287
- }
3288
-
3289
- if (segment.prefixSegment) {
3290
- return 0.27
3291
- }
3292
-
3293
- if (segment.suffixSegment) {
3294
- return 0.26
3288
+ if (baseScore) {
3289
+ // if there is any static segment (that is not an index) after a required / optional param,
3290
+ // we will boost this param so it ranks higher than a required/optional param without a static segment after it
3291
+ // JUST FOR SORTING, NOT FOR MATCHING
3292
+ for (let i = index + 1; i < parsed.length; i++) {
3293
+ const nextSegment = parsed[i]!
3294
+ if (nextSegment.type === 'pathname' && nextSegment.value !== '/') {
3295
+ hasStaticAfter = true
3296
+ return handleParam(segment, baseScore + 0.2)
3297
+ }
3295
3298
  }
3296
3299
 
3297
- return 0.25
3300
+ return handleParam(segment, baseScore)
3298
3301
  }
3299
3302
 
3300
3303
  return 1
3301
3304
  })
3302
3305
 
3303
- scoredRoutes.push({ child: d, trimmed, parsed, index: i, scores })
3306
+ scoredRoutes.push({
3307
+ child: d,
3308
+ trimmed,
3309
+ parsed,
3310
+ index: i,
3311
+ scores,
3312
+ optionalParamCount,
3313
+ hasStaticAfter,
3314
+ })
3304
3315
  })
3305
3316
 
3306
3317
  const flatRoutes = scoredRoutes
@@ -3316,17 +3327,16 @@ export function processRouteTree<TRouteLike extends RouteLike>({
3316
3327
 
3317
3328
  // If all common segments have equal scores, then consider length and specificity
3318
3329
  if (a.scores.length !== b.scores.length) {
3319
- // Count optional parameters in each route
3320
- const aOptionalCount = a.parsed.filter(
3321
- (seg) => seg.type === 'optional-param',
3322
- ).length
3323
- const bOptionalCount = b.parsed.filter(
3324
- (seg) => seg.type === 'optional-param',
3325
- ).length
3326
-
3327
3330
  // If different number of optional parameters, fewer optional parameters wins (more specific)
3328
- if (aOptionalCount !== bOptionalCount) {
3329
- return aOptionalCount - bOptionalCount
3331
+ // only if both or none of the routes has static segments after the params
3332
+ if (a.optionalParamCount !== b.optionalParamCount) {
3333
+ if (a.hasStaticAfter === b.hasStaticAfter) {
3334
+ return a.optionalParamCount - b.optionalParamCount
3335
+ } else if (a.hasStaticAfter && !b.hasStaticAfter) {
3336
+ return -1
3337
+ } else if (!a.hasStaticAfter && b.hasStaticAfter) {
3338
+ return 1
3339
+ }
3330
3340
  }
3331
3341
 
3332
3342
  // If same number of optional parameters, longer path wins (for static segments)