@tanstack/router-core 1.128.0 → 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.0",
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 {
@@ -1819,6 +1820,7 @@ export class RouterCore<
1819
1820
  this.__store.setState((s) => ({
1820
1821
  ...s,
1821
1822
  status: 'pending',
1823
+ statusCode: 200,
1822
1824
  isLoading: true,
1823
1825
  location: this.latestLocation,
1824
1826
  pendingMatches,
@@ -3177,6 +3179,27 @@ export type ProcessRouteTreeResult<TRouteLike extends RouteLike> = {
3177
3179
  routesByPath: Record<string, TRouteLike>
3178
3180
  flatRoutes: Array<TRouteLike>
3179
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
+
3180
3203
  export function processRouteTree<TRouteLike extends RouteLike>({
3181
3204
  routeTree,
3182
3205
  initRoute,
@@ -3223,9 +3246,11 @@ export function processRouteTree<TRouteLike extends RouteLike>({
3223
3246
  const scoredRoutes: Array<{
3224
3247
  child: TRouteLike
3225
3248
  trimmed: string
3226
- parsed: ReturnType<typeof parsePathname>
3249
+ parsed: Array<Segment>
3227
3250
  index: number
3228
3251
  scores: Array<number>
3252
+ hasStaticAfter: boolean
3253
+ optionalParamCount: number
3229
3254
  }> = []
3230
3255
 
3231
3256
  const routes: Array<TRouteLike> = Object.values(routesById)
@@ -3243,63 +3268,50 @@ export function processRouteTree<TRouteLike extends RouteLike>({
3243
3268
  parsed.shift()
3244
3269
  }
3245
3270
 
3246
- const scores = parsed.map((segment) => {
3271
+ let optionalParamCount = 0
3272
+ let hasStaticAfter = false
3273
+ const scores = parsed.map((segment, index) => {
3247
3274
  if (segment.value === '/') {
3248
3275
  return 0.75
3249
3276
  }
3250
3277
 
3278
+ let baseScore: number | undefined = undefined
3251
3279
  if (segment.type === 'param') {
3252
- if (segment.prefixSegment && segment.suffixSegment) {
3253
- return 0.55
3254
- }
3255
-
3256
- if (segment.prefixSegment) {
3257
- return 0.52
3258
- }
3259
-
3260
- if (segment.suffixSegment) {
3261
- return 0.51
3262
- }
3263
-
3264
- 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
3265
3286
  }
3266
3287
 
3267
- if (segment.type === 'optional-param') {
3268
- if (segment.prefixSegment && segment.suffixSegment) {
3269
- return 0.45
3270
- }
3271
-
3272
- if (segment.prefixSegment) {
3273
- return 0.42
3274
- }
3275
-
3276
- if (segment.suffixSegment) {
3277
- return 0.41
3278
- }
3279
-
3280
- return 0.4
3281
- }
3282
-
3283
- if (segment.type === 'wildcard') {
3284
- if (segment.prefixSegment && segment.suffixSegment) {
3285
- return 0.3
3286
- }
3287
-
3288
- if (segment.prefixSegment) {
3289
- return 0.27
3290
- }
3291
-
3292
- if (segment.suffixSegment) {
3293
- 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
+ }
3294
3298
  }
3295
3299
 
3296
- return 0.25
3300
+ return handleParam(segment, baseScore)
3297
3301
  }
3298
3302
 
3299
3303
  return 1
3300
3304
  })
3301
3305
 
3302
- 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
+ })
3303
3315
  })
3304
3316
 
3305
3317
  const flatRoutes = scoredRoutes
@@ -3315,17 +3327,16 @@ export function processRouteTree<TRouteLike extends RouteLike>({
3315
3327
 
3316
3328
  // If all common segments have equal scores, then consider length and specificity
3317
3329
  if (a.scores.length !== b.scores.length) {
3318
- // Count optional parameters in each route
3319
- const aOptionalCount = a.parsed.filter(
3320
- (seg) => seg.type === 'optional-param',
3321
- ).length
3322
- const bOptionalCount = b.parsed.filter(
3323
- (seg) => seg.type === 'optional-param',
3324
- ).length
3325
-
3326
3330
  // If different number of optional parameters, fewer optional parameters wins (more specific)
3327
- if (aOptionalCount !== bOptionalCount) {
3328
- 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
+ }
3329
3340
  }
3330
3341
 
3331
3342
  // If same number of optional parameters, longer path wins (for static segments)