@tanstack/router-core 1.168.18 → 1.169.0
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/dist/cjs/index.cjs +1 -0
- package/dist/cjs/index.d.cts +1 -1
- package/dist/cjs/new-process-route-tree.cjs +34 -46
- package/dist/cjs/new-process-route-tree.cjs.map +1 -1
- package/dist/cjs/new-process-route-tree.d.cts +4 -26
- package/dist/cjs/path.cjs +1 -22
- package/dist/cjs/path.cjs.map +1 -1
- package/dist/cjs/route.cjs.map +1 -1
- package/dist/cjs/route.d.cts +1 -32
- package/dist/cjs/router.cjs +34 -19
- package/dist/cjs/router.cjs.map +1 -1
- package/dist/cjs/router.d.cts +3 -4
- package/dist/cjs/utils.cjs +5 -0
- package/dist/cjs/utils.cjs.map +1 -1
- package/dist/cjs/utils.d.cts +1 -0
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/index.js +2 -2
- package/dist/esm/new-process-route-tree.d.ts +4 -26
- package/dist/esm/new-process-route-tree.js +34 -47
- package/dist/esm/new-process-route-tree.js.map +1 -1
- package/dist/esm/path.js +1 -22
- package/dist/esm/path.js.map +1 -1
- package/dist/esm/route.d.ts +1 -32
- package/dist/esm/route.js.map +1 -1
- package/dist/esm/router.d.ts +3 -4
- package/dist/esm/router.js +36 -21
- package/dist/esm/router.js.map +1 -1
- package/dist/esm/utils.d.ts +1 -0
- package/dist/esm/utils.js +5 -1
- package/dist/esm/utils.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +1 -0
- package/src/new-process-route-tree.ts +42 -77
- package/src/path.ts +1 -27
- package/src/route.ts +5 -34
- package/src/router.ts +76 -54
- package/src/utils.ts +7 -0
package/src/router.ts
CHANGED
|
@@ -8,12 +8,14 @@ import {
|
|
|
8
8
|
encodePathLikeUrl,
|
|
9
9
|
findLast,
|
|
10
10
|
functionalUpdate,
|
|
11
|
+
hasKeys,
|
|
11
12
|
isDangerousProtocol,
|
|
12
13
|
last,
|
|
13
14
|
nullReplaceEqualDeep,
|
|
14
15
|
replaceEqualDeep,
|
|
15
16
|
} from './utils'
|
|
16
17
|
import {
|
|
18
|
+
buildRouteBranch,
|
|
17
19
|
findFlatMatch,
|
|
18
20
|
findRouteMatch,
|
|
19
21
|
findSingleMatch,
|
|
@@ -732,8 +734,6 @@ export type GetMatchRoutesFn = (pathname: string) => {
|
|
|
732
734
|
matchedRoutes: ReadonlyArray<AnyRoute>
|
|
733
735
|
/** exhaustive params, still in their string form */
|
|
734
736
|
routeParams: Record<string, string>
|
|
735
|
-
/** partial params, parsed from routeParams during matching */
|
|
736
|
-
parsedParams: Record<string, unknown> | undefined
|
|
737
737
|
foundRoute: AnyRoute | undefined
|
|
738
738
|
parseError?: unknown
|
|
739
739
|
}
|
|
@@ -972,6 +972,7 @@ export class RouterCore<
|
|
|
972
972
|
routesByPath!: RoutesByPath<TRouteTree>
|
|
973
973
|
processedTree!: ProcessedTree<TRouteTree, any, any>
|
|
974
974
|
resolvePathCache!: LRUCache<string, string>
|
|
975
|
+
private routeBranchCache = new WeakMap<AnyRoute, ReadonlyArray<AnyRoute>>()
|
|
975
976
|
isServer!: boolean
|
|
976
977
|
pathParamsDecoder?: (encoded: string) => string
|
|
977
978
|
protocolAllowlist!: Set<string>
|
|
@@ -1343,15 +1344,23 @@ export class RouterCore<
|
|
|
1343
1344
|
return location
|
|
1344
1345
|
}
|
|
1345
1346
|
|
|
1346
|
-
/** Resolve a path
|
|
1347
|
+
/** Resolve a path using the router's trailing-slash policy. */
|
|
1347
1348
|
resolvePathWithBase = (from: string, path: string) => {
|
|
1348
|
-
|
|
1349
|
+
return resolvePath({
|
|
1349
1350
|
base: from,
|
|
1350
|
-
to: cleanPath(path),
|
|
1351
|
+
to: path.includes('//') ? cleanPath(path) : path,
|
|
1351
1352
|
trailingSlash: this.options.trailingSlash,
|
|
1352
1353
|
cache: this.resolvePathCache,
|
|
1353
1354
|
})
|
|
1354
|
-
|
|
1355
|
+
}
|
|
1356
|
+
|
|
1357
|
+
private getRouteBranch(route: AnyRoute) {
|
|
1358
|
+
let branch = this.routeBranchCache.get(route)
|
|
1359
|
+
if (!branch) {
|
|
1360
|
+
branch = buildRouteBranch(route)
|
|
1361
|
+
this.routeBranchCache.set(route, branch)
|
|
1362
|
+
}
|
|
1363
|
+
return branch
|
|
1355
1364
|
}
|
|
1356
1365
|
|
|
1357
1366
|
get looseRoutesById() {
|
|
@@ -1391,7 +1400,7 @@ export class RouterCore<
|
|
|
1391
1400
|
opts?: MatchRoutesOpts,
|
|
1392
1401
|
): Array<AnyRouteMatch> {
|
|
1393
1402
|
const matchedRoutesResult = this.getMatchedRoutes(next.pathname)
|
|
1394
|
-
const { foundRoute, routeParams
|
|
1403
|
+
const { foundRoute, routeParams } = matchedRoutesResult
|
|
1395
1404
|
let { matchedRoutes } = matchedRoutesResult
|
|
1396
1405
|
let isGlobalNotFound = false
|
|
1397
1406
|
|
|
@@ -1517,7 +1526,7 @@ export class RouterCore<
|
|
|
1517
1526
|
|
|
1518
1527
|
if (!existingMatch) {
|
|
1519
1528
|
try {
|
|
1520
|
-
extractStrictParams(route,
|
|
1529
|
+
extractStrictParams(route, strictParams)
|
|
1521
1530
|
} catch (err: any) {
|
|
1522
1531
|
if (isNotFound(err) || isRedirect(err)) {
|
|
1523
1532
|
paramsError = err
|
|
@@ -1687,7 +1696,7 @@ export class RouterCore<
|
|
|
1687
1696
|
search: Record<string, unknown>
|
|
1688
1697
|
params: Record<string, unknown>
|
|
1689
1698
|
} {
|
|
1690
|
-
const { matchedRoutes, routeParams
|
|
1699
|
+
const { matchedRoutes, routeParams } = this.getMatchedRoutes(
|
|
1691
1700
|
location.pathname,
|
|
1692
1701
|
)
|
|
1693
1702
|
const lastRoute = last(matchedRoutes)!
|
|
@@ -1734,12 +1743,7 @@ export class RouterCore<
|
|
|
1734
1743
|
)
|
|
1735
1744
|
for (const route of matchedRoutes) {
|
|
1736
1745
|
try {
|
|
1737
|
-
extractStrictParams(
|
|
1738
|
-
route,
|
|
1739
|
-
routeParams,
|
|
1740
|
-
parsedParams ?? {},
|
|
1741
|
-
strictParams,
|
|
1742
|
-
)
|
|
1746
|
+
extractStrictParams(route, strictParams)
|
|
1743
1747
|
} catch {
|
|
1744
1748
|
// Ignore errors, we're not actually routing
|
|
1745
1749
|
}
|
|
@@ -1835,9 +1839,7 @@ export class RouterCore<
|
|
|
1835
1839
|
dest.unsafeRelative === 'path'
|
|
1836
1840
|
? currentLocation.pathname
|
|
1837
1841
|
: (dest.from ?? lightweightResult.fullPath)
|
|
1838
|
-
|
|
1839
|
-
// ensure this includes the basePath if set
|
|
1840
|
-
const fromPath = this.resolvePathWithBase(defaultedFromPath, '.')
|
|
1842
|
+
const destTo = dest.to ? `${dest.to}` : undefined
|
|
1841
1843
|
|
|
1842
1844
|
// From search should always use the current location
|
|
1843
1845
|
const fromSearch = lightweightResult.search
|
|
@@ -1847,11 +1849,15 @@ export class RouterCore<
|
|
|
1847
1849
|
lightweightResult.params,
|
|
1848
1850
|
)
|
|
1849
1851
|
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1852
|
+
const isAbsoluteTo = destTo?.charCodeAt(0) === 47
|
|
1853
|
+
const sourcePath = isAbsoluteTo
|
|
1854
|
+
? '/'
|
|
1855
|
+
: this.resolvePathWithBase(defaultedFromPath, '.')
|
|
1856
|
+
|
|
1857
|
+
// Resolve the destination. Absolute destinations don't need the source path.
|
|
1858
|
+
const nextTo = destTo
|
|
1859
|
+
? this.resolvePathWithBase(sourcePath, destTo)
|
|
1860
|
+
: sourcePath
|
|
1855
1861
|
|
|
1856
1862
|
// Resolve the next params
|
|
1857
1863
|
const nextParams =
|
|
@@ -1864,24 +1870,33 @@ export class RouterCore<
|
|
|
1864
1870
|
functionalUpdate(dest.params as any, fromParams),
|
|
1865
1871
|
)
|
|
1866
1872
|
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
let destRoutes
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1873
|
+
const destRoute = this.routesByPath[
|
|
1874
|
+
trimPathRight(nextTo) as keyof typeof this.routesByPath
|
|
1875
|
+
] as AnyRoute | undefined
|
|
1876
|
+
|
|
1877
|
+
let destRoutes: ReadonlyArray<AnyRoute>
|
|
1878
|
+
if (destRoute) {
|
|
1879
|
+
destRoutes = this.getRouteBranch(destRoute)
|
|
1880
|
+
} else if (nextTo.includes('$')) {
|
|
1881
|
+
// Route templates must match routesByPath exactly. A miss here is a
|
|
1882
|
+
// typed destination mismatch, not a concrete URL to route-match.
|
|
1883
|
+
destRoutes = []
|
|
1884
|
+
} else {
|
|
1885
|
+
const destMatchResult = this.getMatchedRoutes(nextTo)
|
|
1886
|
+
destRoutes = destMatchResult.matchedRoutes
|
|
1878
1887
|
|
|
1879
|
-
|
|
1880
|
-
|
|
1888
|
+
if (
|
|
1889
|
+
this.options.notFoundRoute &&
|
|
1890
|
+
(!destMatchResult.foundRoute ||
|
|
1891
|
+
(destMatchResult.foundRoute.path !== '/' &&
|
|
1892
|
+
destMatchResult.routeParams['**']))
|
|
1893
|
+
) {
|
|
1894
|
+
destRoutes = [...destRoutes, this.options.notFoundRoute]
|
|
1895
|
+
}
|
|
1881
1896
|
}
|
|
1882
1897
|
|
|
1883
1898
|
// If there are any params, we need to stringify them
|
|
1884
|
-
if (
|
|
1899
|
+
if (destRoutes.length && hasKeys(nextParams)) {
|
|
1885
1900
|
for (const route of destRoutes) {
|
|
1886
1901
|
const fn =
|
|
1887
1902
|
route.options.params?.stringify ?? route.options.stringifyParams
|
|
@@ -1900,8 +1915,7 @@ export class RouterCore<
|
|
|
1900
1915
|
}
|
|
1901
1916
|
|
|
1902
1917
|
const nextPathname = opts.leaveParams
|
|
1903
|
-
? //
|
|
1904
|
-
// This preserves the original parameter syntax including optional parameters
|
|
1918
|
+
? // Keep path params uninterpolated for matchRoute/template matching.
|
|
1905
1919
|
nextTo
|
|
1906
1920
|
: decodePath(
|
|
1907
1921
|
interpolatePath({
|
|
@@ -1912,6 +1926,24 @@ export class RouterCore<
|
|
|
1912
1926
|
}).interpolatedPath,
|
|
1913
1927
|
).path
|
|
1914
1928
|
|
|
1929
|
+
if (
|
|
1930
|
+
process.env.NODE_ENV !== 'production' &&
|
|
1931
|
+
destRoute &&
|
|
1932
|
+
!opts.leaveParams
|
|
1933
|
+
) {
|
|
1934
|
+
try {
|
|
1935
|
+
const roundTrip = this.getMatchedRoutes(nextPathname)
|
|
1936
|
+
if (roundTrip.foundRoute?.id !== destRoute.id) {
|
|
1937
|
+
console.warn(
|
|
1938
|
+
`Generated path "${nextPathname}" for route "${destRoute.id}" did not match the same route after params.stringify.`,
|
|
1939
|
+
)
|
|
1940
|
+
}
|
|
1941
|
+
} catch {
|
|
1942
|
+
// Ignore roundtrip validation errors. The generated location will be
|
|
1943
|
+
// handled by the normal navigation flow.
|
|
1944
|
+
}
|
|
1945
|
+
}
|
|
1946
|
+
|
|
1915
1947
|
// Resolve the next search
|
|
1916
1948
|
let nextSearch = fromSearch
|
|
1917
1949
|
if (opts._includeValidateSearch && this.options.search?.strict) {
|
|
@@ -3015,17 +3047,15 @@ export function getMatchedRoutes<TRouteLike extends RouteLike>({
|
|
|
3015
3047
|
const trimmedPath = trimPathRight(pathname)
|
|
3016
3048
|
|
|
3017
3049
|
let foundRoute: TRouteLike | undefined = undefined
|
|
3018
|
-
let parsedParams: Record<string, unknown> | undefined = undefined
|
|
3019
3050
|
const match = findRouteMatch<TRouteLike>(trimmedPath, processedTree, true)
|
|
3020
3051
|
if (match) {
|
|
3021
3052
|
foundRoute = match.route
|
|
3022
3053
|
Object.assign(routeParams, match.rawParams) // Copy params, because they're cached
|
|
3023
|
-
parsedParams = Object.assign(Object.create(null), match.parsedParams)
|
|
3024
3054
|
}
|
|
3025
3055
|
|
|
3026
3056
|
const matchedRoutes = match?.branch || [routesById[rootRouteId]!]
|
|
3027
3057
|
|
|
3028
|
-
return { matchedRoutes, routeParams, foundRoute
|
|
3058
|
+
return { matchedRoutes, routeParams, foundRoute }
|
|
3029
3059
|
}
|
|
3030
3060
|
|
|
3031
3061
|
/**
|
|
@@ -3177,22 +3207,14 @@ function findGlobalNotFoundRouteId(
|
|
|
3177
3207
|
|
|
3178
3208
|
function extractStrictParams(
|
|
3179
3209
|
route: AnyRoute,
|
|
3180
|
-
referenceParams: Record<string, unknown>,
|
|
3181
|
-
parsedParams: Record<string, unknown>,
|
|
3182
3210
|
accumulatedParams: Record<string, unknown>,
|
|
3183
3211
|
) {
|
|
3184
3212
|
const parseParams = route.options.params?.parse ?? route.options.parseParams
|
|
3185
3213
|
if (parseParams) {
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
if (key in parsedParams) {
|
|
3190
|
-
accumulatedParams[key] = parsedParams[key]
|
|
3191
|
-
}
|
|
3192
|
-
}
|
|
3193
|
-
} else {
|
|
3194
|
-
const result = parseParams(accumulatedParams as Record<string, string>)
|
|
3195
|
-
Object.assign(accumulatedParams, result)
|
|
3214
|
+
const result = parseParams(accumulatedParams as Record<string, string>)
|
|
3215
|
+
if (result === false) {
|
|
3216
|
+
throw new Error('Route params.parse returned false for a matched route')
|
|
3196
3217
|
}
|
|
3218
|
+
Object.assign(accumulatedParams, result)
|
|
3197
3219
|
}
|
|
3198
3220
|
}
|
package/src/utils.ts
CHANGED
|
@@ -215,6 +215,13 @@ export function functionalUpdate<TPrevious, TResult = TPrevious>(
|
|
|
215
215
|
const hasOwn = Object.prototype.hasOwnProperty
|
|
216
216
|
const isEnumerable = Object.prototype.propertyIsEnumerable
|
|
217
217
|
|
|
218
|
+
export function hasKeys(obj: Record<string, unknown>) {
|
|
219
|
+
for (const key in obj) {
|
|
220
|
+
if (hasOwn.call(obj, key)) return true
|
|
221
|
+
}
|
|
222
|
+
return false
|
|
223
|
+
}
|
|
224
|
+
|
|
218
225
|
const createNull = () => Object.create(null)
|
|
219
226
|
export const nullReplaceEqualDeep: typeof replaceEqualDeep = (prev, next) =>
|
|
220
227
|
replaceEqualDeep(prev, next, createNull)
|