@tanstack/router-core 1.145.11 → 1.146.1

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
@@ -698,8 +698,12 @@ export type ParseLocationFn<TRouteTree extends AnyRoute> = (
698
698
 
699
699
  export type GetMatchRoutesFn = (pathname: string) => {
700
700
  matchedRoutes: ReadonlyArray<AnyRoute>
701
+ /** exhaustive params, still in their string form */
701
702
  routeParams: Record<string, string>
703
+ /** partial params, parsed from routeParams during matching */
704
+ parsedParams: Record<string, unknown> | undefined
702
705
  foundRoute: AnyRoute | undefined
706
+ parseError?: unknown
703
707
  }
704
708
 
705
709
  export type EmitFn = (routerEvent: RouterEvent) => void
@@ -1260,7 +1264,7 @@ export class RouterCore<
1260
1264
  opts?: MatchRoutesOpts,
1261
1265
  ): Array<AnyRouteMatch> {
1262
1266
  const matchedRoutesResult = this.getMatchedRoutes(next.pathname)
1263
- const { foundRoute, routeParams } = matchedRoutesResult
1267
+ const { foundRoute, routeParams, parsedParams } = matchedRoutesResult
1264
1268
  let { matchedRoutes } = matchedRoutesResult
1265
1269
  let isGlobalNotFound = false
1266
1270
 
@@ -1401,26 +1405,34 @@ export class RouterCore<
1401
1405
  let paramsError: unknown = undefined
1402
1406
 
1403
1407
  if (!existingMatch) {
1404
- const strictParseParams =
1405
- route.options.params?.parse ?? route.options.parseParams
1406
-
1407
- if (strictParseParams) {
1408
- try {
1409
- Object.assign(
1410
- strictParams,
1411
- strictParseParams(strictParams as Record<string, string>),
1412
- )
1413
- } catch (err: any) {
1414
- if (isNotFound(err) || isRedirect(err)) {
1415
- paramsError = err
1416
- } else {
1417
- paramsError = new PathParamError(err.message, {
1418
- cause: err,
1419
- })
1408
+ if (route.options.skipRouteOnParseError) {
1409
+ for (const key in usedParams) {
1410
+ if (key in parsedParams!) {
1411
+ strictParams[key] = parsedParams![key]
1420
1412
  }
1413
+ }
1414
+ } else {
1415
+ const strictParseParams =
1416
+ route.options.params?.parse ?? route.options.parseParams
1421
1417
 
1422
- if (opts?.throwOnError) {
1423
- throw paramsError
1418
+ if (strictParseParams) {
1419
+ try {
1420
+ Object.assign(
1421
+ strictParams,
1422
+ strictParseParams(strictParams as Record<string, string>),
1423
+ )
1424
+ } catch (err: any) {
1425
+ if (isNotFound(err) || isRedirect(err)) {
1426
+ paramsError = err
1427
+ } else {
1428
+ paramsError = new PathParamError(err.message, {
1429
+ cause: err,
1430
+ })
1431
+ }
1432
+
1433
+ if (opts?.throwOnError) {
1434
+ throw paramsError
1435
+ }
1424
1436
  }
1425
1437
  }
1426
1438
  }
@@ -1802,7 +1814,7 @@ export class RouterCore<
1802
1814
  this.processedTree,
1803
1815
  )
1804
1816
  if (match) {
1805
- Object.assign(params, match.params) // Copy params, because they're cached
1817
+ Object.assign(params, match.rawParams) // Copy params, because they're cached
1806
1818
  const {
1807
1819
  from: _from,
1808
1820
  params: maskParams,
@@ -1965,7 +1977,14 @@ export class RouterCore<
1965
1977
  const parsed = parseHref(href, {
1966
1978
  __TSR_index: replace ? currentIndex : currentIndex + 1,
1967
1979
  })
1968
- rest.to = parsed.pathname
1980
+
1981
+ // If the href contains the basepath, we need to strip it before setting `to`
1982
+ // because `buildLocation` will add the basepath back when creating the final URL.
1983
+ // Without this, hrefs like '/app/about' would become '/app/app/about'.
1984
+ const hrefUrl = new URL(parsed.pathname, this.origin)
1985
+ const rewrittenUrl = executeRewriteInput(this.rewrite, hrefUrl)
1986
+
1987
+ rest.to = rewrittenUrl.pathname
1969
1988
  rest.search = this.options.parseSearch(parsed.search)
1970
1989
  // remove the leading `#` from the hash
1971
1990
  rest.hash = parsed.hash.slice(1)
@@ -2028,12 +2047,18 @@ export class RouterCore<
2028
2047
  }
2029
2048
 
2030
2049
  if (reloadDocument) {
2031
- if (!href || (!publicHref && !hrefIsUrl)) {
2050
+ // When to is provided, always build a location to get the proper publicHref
2051
+ // (this handles redirects where href might be an internal path from resolveRedirect)
2052
+ // When only href is provided (no to), use it directly as it should already
2053
+ // be a complete path (possibly with basepath)
2054
+ if (to !== undefined || !href) {
2032
2055
  const location = this.buildLocation({ to, ...rest } as any)
2033
2056
  href = href ?? location.url.href
2034
2057
  publicHref = publicHref ?? location.url.href
2035
2058
  }
2036
2059
 
2060
+ // Use publicHref when available and href is not a full URL,
2061
+ // otherwise use href directly (which may already include basepath)
2037
2062
  const reloadHref = !hrefIsUrl && publicHref ? publicHref : href
2038
2063
 
2039
2064
  // Check blockers for external URLs unless ignoreBlocker is true
@@ -2601,18 +2626,18 @@ export class RouterCore<
2601
2626
  }
2602
2627
 
2603
2628
  if (location.params) {
2604
- if (!deepEqual(match.params, location.params, { partial: true })) {
2629
+ if (!deepEqual(match.rawParams, location.params, { partial: true })) {
2605
2630
  return false
2606
2631
  }
2607
2632
  }
2608
2633
 
2609
2634
  if (opts?.includeSearch ?? true) {
2610
2635
  return deepEqual(baseLocation.search, next.search, { partial: true })
2611
- ? match.params
2636
+ ? match.rawParams
2612
2637
  : false
2613
2638
  }
2614
2639
 
2615
- return match.params
2640
+ return match.rawParams
2616
2641
  }
2617
2642
 
2618
2643
  ssr?: {
@@ -2719,15 +2744,17 @@ export function getMatchedRoutes<TRouteLike extends RouteLike>({
2719
2744
  const trimmedPath = trimPathRight(pathname)
2720
2745
 
2721
2746
  let foundRoute: TRouteLike | undefined = undefined
2747
+ let parsedParams: Record<string, unknown> | undefined = undefined
2722
2748
  const match = findRouteMatch<TRouteLike>(trimmedPath, processedTree, true)
2723
2749
  if (match) {
2724
2750
  foundRoute = match.route
2725
- Object.assign(routeParams, match.params) // Copy params, because they're cached
2751
+ Object.assign(routeParams, match.rawParams) // Copy params, because they're cached
2752
+ parsedParams = Object.assign({}, match.parsedParams)
2726
2753
  }
2727
2754
 
2728
2755
  const matchedRoutes = match?.branch || [routesById[rootRouteId]!]
2729
2756
 
2730
- return { matchedRoutes, routeParams, foundRoute }
2757
+ return { matchedRoutes, routeParams, foundRoute, parsedParams }
2731
2758
  }
2732
2759
 
2733
2760
  function applySearchMiddleware({