@tanstack/router-core 1.171.17 → 1.171.18

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
@@ -750,13 +750,12 @@ export type ParseLocationFn<TRouteTree extends AnyRoute> = (
750
750
  previousLocation?: ParsedLocation<FullSearchSchema<TRouteTree>>,
751
751
  ) => ParsedLocation<FullSearchSchema<TRouteTree>>
752
752
 
753
- export type GetMatchRoutesFn = (pathname: string) => {
754
- matchedRoutes: ReadonlyArray<AnyRoute>
753
+ export type GetMatchRoutesFn = (pathname: string) => [
754
+ matchedRoutes: ReadonlyArray<AnyRoute>,
755
755
  /** exhaustive params, still in their string form */
756
- routeParams: Record<string, string>
757
- foundRoute: AnyRoute | undefined
758
- parseError?: unknown
759
- }
756
+ rawParams: Record<string, string>,
757
+ foundRoute: AnyRoute | undefined,
758
+ ]
760
759
 
761
760
  export type EmitFn = (routerEvent: RouterEvent) => void
762
761
 
@@ -964,12 +963,12 @@ export function runRouteLifecycle(
964
963
  }
965
964
  }
966
965
 
967
- type LightweightRouteMatchResult = {
968
- matchedRoutes: ReadonlyArray<AnyRoute>
969
- fullPath: string
970
- search: Record<string, unknown>
971
- params: Record<string, unknown>
972
- }
966
+ type LightweightRouteMatchResult = [
967
+ matchedRoutes: ReadonlyArray<AnyRoute>,
968
+ fullPath: string,
969
+ search: Record<string, unknown>,
970
+ params: Record<string, unknown>,
971
+ ]
973
972
 
974
973
  type LightweightRouteMatchCacheEntry = [
975
974
  lastMatchId: string | undefined,
@@ -1512,16 +1511,17 @@ export class RouterCore<
1512
1511
  next: ParsedLocation,
1513
1512
  opts?: MatchRoutesOpts,
1514
1513
  ): Array<AnyRouteMatch> {
1515
- const matchedRoutesResult = this.getMatchedRoutes(next.pathname)
1516
- const { foundRoute, routeParams } = matchedRoutesResult
1517
- let { matchedRoutes } = matchedRoutesResult
1514
+ const [initialMatchedRoutes, rawParams, foundRoute] = this.getMatchedRoutes(
1515
+ next.pathname,
1516
+ )
1517
+ let matchedRoutes = initialMatchedRoutes
1518
1518
  let isGlobalNotFound = false
1519
1519
 
1520
1520
  // Check to see if the route needs a 404 entry
1521
1521
  if (
1522
1522
  // If we found a route, and it's not an index route and we have left over path
1523
1523
  foundRoute
1524
- ? foundRoute.path !== '/' && routeParams['**']
1524
+ ? foundRoute.path !== '/' && rawParams['**']
1525
1525
  : // Or if we didn't find a route and we have left over path
1526
1526
  trimPathRight(next.pathname)
1527
1527
  ) {
@@ -1549,6 +1549,7 @@ export class RouterCore<
1549
1549
  : undefined
1550
1550
  }
1551
1551
 
1552
+ let strictParams: AnyRouteMatch['_strictParams'] | undefined
1552
1553
  for (let index = 0; index < matchedRoutes.length; index++) {
1553
1554
  const route = matchedRoutes[index]!
1554
1555
  // Take each matched route and resolve + validate its search params
@@ -1614,9 +1615,10 @@ export class RouterCore<
1614
1615
  }
1615
1616
  searchError ??= cause
1616
1617
  }
1618
+ // Match identity must only use the raw params captured from the URL.
1617
1619
  const { interpolatedPath, usedParams } = interpolatePath({
1618
1620
  path: route.fullPath,
1619
- params: routeParams,
1621
+ params: rawParams,
1620
1622
  decoder: this.pathParamsDecoder,
1621
1623
  server: this.isServer,
1622
1624
  })
@@ -1639,7 +1641,9 @@ export class RouterCore<
1639
1641
  : (this._cache.get(matchId) ??
1640
1642
  (previousMatch?.id === matchId ? previousMatch : undefined))
1641
1643
 
1642
- const strictParams = existingMatch?._strictParams ?? usedParams
1644
+ // Carry parsed ancestors forward without mutating the raw route params.
1645
+ strictParams =
1646
+ existingMatch?._strictParams ?? Object.assign(usedParams, strictParams)
1643
1647
 
1644
1648
  let paramsError: unknown
1645
1649
 
@@ -1661,8 +1665,6 @@ export class RouterCore<
1661
1665
  }
1662
1666
  }
1663
1667
 
1664
- Object.assign(routeParams, strictParams)
1665
-
1666
1668
  const cause = previousMatch ? 'stay' : 'enter'
1667
1669
 
1668
1670
  let match: AnyRouteMatch
@@ -1671,8 +1673,6 @@ export class RouterCore<
1671
1673
  match = {
1672
1674
  ...existingMatch,
1673
1675
  cause,
1674
- params: previousMatch?.params ?? routeParams,
1675
- _strictParams: strictParams,
1676
1676
  search: previousMatch
1677
1677
  ? nullReplaceEqualDeep(previousMatch.search, preMatchSearch)
1678
1678
  : nullReplaceEqualDeep(existingMatch.search, preMatchSearch),
@@ -1687,7 +1687,7 @@ export class RouterCore<
1687
1687
  ssr: (isServer ?? this.isServer) ? undefined : route.options.ssr,
1688
1688
  index,
1689
1689
  routeId: route.id,
1690
- params: previousMatch?.params ?? routeParams,
1690
+ params: previousMatch?.params ?? strictParams,
1691
1691
  _strictParams: strictParams,
1692
1692
  pathname: interpolatedPath,
1693
1693
  updatedAt: Date.now(),
@@ -1727,8 +1727,8 @@ export class RouterCore<
1727
1727
  const match = matches[index]!
1728
1728
  match.params =
1729
1729
  match.cause === 'stay'
1730
- ? nullReplaceEqualDeep(match.params, routeParams)
1731
- : routeParams
1730
+ ? nullReplaceEqualDeep(match.params, strictParams)
1731
+ : strictParams!
1732
1732
  if (opts?._controller) {
1733
1733
  match.context = {}
1734
1734
  }
@@ -1738,20 +1738,20 @@ export class RouterCore<
1738
1738
  }
1739
1739
 
1740
1740
  getMatchedRoutes: GetMatchRoutesFn = (pathname) => {
1741
- const routeParams: Record<string, string> = Object.create(null)
1741
+ const rawParams: Record<string, string> = Object.create(null)
1742
1742
  const match = findRouteMatch(
1743
1743
  trimPathRight(pathname),
1744
1744
  this.processedTree,
1745
1745
  true,
1746
1746
  )
1747
1747
  if (match) {
1748
- Object.assign(routeParams, match.rawParams)
1749
- }
1750
- return {
1751
- matchedRoutes: match?.branch || [this.routesById[rootRouteId]!],
1752
- routeParams,
1753
- foundRoute: match?.route,
1748
+ Object.assign(rawParams, match.rawParams)
1754
1749
  }
1750
+ return [
1751
+ match?.branch || [this.routesById[rootRouteId]!],
1752
+ rawParams,
1753
+ match?.route,
1754
+ ]
1755
1755
  }
1756
1756
 
1757
1757
  /**
@@ -1772,9 +1772,7 @@ export class RouterCore<
1772
1772
  return cached[1 /* result */]
1773
1773
  }
1774
1774
 
1775
- const { matchedRoutes, routeParams } = this.getMatchedRoutes(
1776
- location.pathname,
1777
- )
1775
+ const [matchedRoutes, rawParams] = this.getMatchedRoutes(location.pathname)
1778
1776
  const lastRoute = last(matchedRoutes)!
1779
1777
 
1780
1778
  // I don't know if we should run the full search middleware chain, or just validateSearch
@@ -1812,7 +1810,7 @@ export class RouterCore<
1812
1810
  // Parse params through the route chain
1813
1811
  const strictParams: Record<string, unknown> = Object.assign(
1814
1812
  Object.create(null),
1815
- routeParams,
1813
+ rawParams,
1816
1814
  )
1817
1815
  for (const route of matchedRoutes) {
1818
1816
  try {
@@ -1824,12 +1822,12 @@ export class RouterCore<
1824
1822
  params = strictParams
1825
1823
  }
1826
1824
 
1827
- const result = {
1825
+ const result: LightweightRouteMatchResult = [
1828
1826
  matchedRoutes,
1829
- fullPath: lastRoute.fullPath,
1830
- search: accumulatedSearch,
1827
+ lastRoute.fullPath,
1828
+ accumulatedSearch,
1831
1829
  params,
1832
- }
1830
+ ]
1833
1831
  this.lightweightCache.set(location, [lastStateMatchId, result])
1834
1832
  return result
1835
1833
  }
@@ -1862,14 +1860,17 @@ export class RouterCore<
1862
1860
  process.env.NODE_ENV !== 'production' &&
1863
1861
  dest._isNavigate
1864
1862
  ) {
1865
- const allFromMatches = this.getMatchedRoutes(dest.from).matchedRoutes
1863
+ const [allFromMatches] = this.getMatchedRoutes(dest.from)
1866
1864
 
1867
- const matchedFrom = findLast(lightweightResult.matchedRoutes, (d) => {
1868
- return comparePaths(d.fullPath, dest.from!)
1869
- })
1865
+ const matchedFrom = findLast(
1866
+ lightweightResult[0 /* matchedRoutes */],
1867
+ (d) => {
1868
+ return comparePaths(d.fullPath, dest.from!)
1869
+ },
1870
+ )
1870
1871
 
1871
1872
  const matchedCurrent = findLast(allFromMatches, (d) => {
1872
- return comparePaths(d.fullPath, lightweightResult.fullPath)
1873
+ return comparePaths(d.fullPath, lightweightResult[1 /* fullPath */])
1873
1874
  })
1874
1875
 
1875
1876
  // for from to be invalid it shouldn't just be unmatched to currentLocation
@@ -1882,15 +1883,15 @@ export class RouterCore<
1882
1883
  const defaultedFromPath =
1883
1884
  dest.unsafeRelative === 'path'
1884
1885
  ? currentLocation.pathname
1885
- : (dest.from ?? lightweightResult.fullPath)
1886
+ : (dest.from ?? lightweightResult[1 /* fullPath */])
1886
1887
  const destTo = dest.to ? `${dest.to}` : undefined
1887
1888
 
1888
1889
  // From search should always use the current location
1889
- const fromSearch = lightweightResult.search
1890
+ const fromSearch = lightweightResult[2 /* search */]
1890
1891
  // Same with params. It can't hurt to provide as many as possible
1891
1892
  const fromParams = Object.assign(
1892
1893
  Object.create(null),
1893
- lightweightResult.params,
1894
+ lightweightResult[3 /* params */],
1894
1895
  )
1895
1896
 
1896
1897
  const isAbsoluteTo = destTo?.charCodeAt(0) === 47
@@ -1918,14 +1919,13 @@ export class RouterCore<
1918
1919
  // typed destination mismatch, not a concrete URL to route-match.
1919
1920
  destRoutes = []
1920
1921
  } else {
1921
- const destMatchResult = this.getMatchedRoutes(nextTo)
1922
- destRoutes = destMatchResult.matchedRoutes
1922
+ const [matchedRoutes, rawParams, foundRoute] =
1923
+ this.getMatchedRoutes(nextTo)
1924
+ destRoutes = matchedRoutes
1923
1925
 
1924
1926
  if (
1925
1927
  this.options.notFoundRoute &&
1926
- (!destMatchResult.foundRoute ||
1927
- (destMatchResult.foundRoute.path !== '/' &&
1928
- destMatchResult.routeParams['**']))
1928
+ (!foundRoute || (foundRoute.path !== '/' && rawParams['**']))
1929
1929
  ) {
1930
1930
  destRoutes = [...destRoutes, this.options.notFoundRoute]
1931
1931
  }
@@ -1968,10 +1968,10 @@ export class RouterCore<
1968
1968
  !opts.leaveParams
1969
1969
  ) {
1970
1970
  try {
1971
- const roundTrip = this.getMatchedRoutes(nextPathname)
1972
- if (roundTrip.foundRoute?.id !== destRoute.id) {
1971
+ const foundRoute = this.getMatchedRoutes(nextPathname)[2]
1972
+ if (foundRoute?.id !== destRoute.id) {
1973
1973
  console.warn(
1974
- `Generated path "${nextPathname}" for route "${destRoute.id}" matched route "${roundTrip.foundRoute?.id}" instead. This can happen when multiple route templates resolve to the same URL. Use the route template that matches the intended route, or adjust params.stringify if it changed the target path.`,
1974
+ `Generated path "${nextPathname}" for route "${destRoute.id}" matched route "${foundRoute?.id}" instead. This can happen when multiple route templates resolve to the same URL. Use the route template that matches the intended route, or adjust params.stringify if it changed the target path.`,
1975
1975
  )
1976
1976
  }
1977
1977
  } catch {