@benjavicente/router-core 1.168.9 → 1.169.2
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/Matches.cjs +1 -1
- package/dist/cjs/Matches.cjs.map +1 -1
- package/dist/cjs/config.cjs +1 -1
- package/dist/cjs/config.cjs.map +1 -1
- package/dist/cjs/defer.cjs +1 -1
- package/dist/cjs/defer.cjs.map +1 -1
- package/dist/cjs/hash-scroll.cjs +1 -1
- package/dist/cjs/hash-scroll.cjs.map +1 -1
- package/dist/cjs/index.cjs +4 -0
- package/dist/cjs/index.d.cts +3 -3
- package/dist/cjs/isServer/client.cjs +1 -1
- package/dist/cjs/isServer/client.cjs.map +1 -1
- package/dist/cjs/isServer/development.cjs +1 -1
- package/dist/cjs/isServer/development.cjs.map +1 -1
- package/dist/cjs/isServer/server.cjs +1 -1
- package/dist/cjs/isServer/server.cjs.map +1 -1
- package/dist/cjs/link.cjs +1 -1
- package/dist/cjs/link.cjs.map +1 -1
- package/dist/cjs/load-matches.cjs +26 -26
- package/dist/cjs/load-matches.cjs.map +1 -1
- package/dist/cjs/manifest.cjs +31 -0
- package/dist/cjs/manifest.cjs.map +1 -1
- package/dist/cjs/manifest.d.cts +8 -0
- package/dist/cjs/new-process-route-tree.cjs +55 -67
- 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/not-found.cjs +1 -1
- package/dist/cjs/not-found.cjs.map +1 -1
- package/dist/cjs/path.cjs +1 -22
- package/dist/cjs/path.cjs.map +1 -1
- package/dist/cjs/root.cjs +1 -1
- package/dist/cjs/root.cjs.map +1 -1
- package/dist/cjs/route.cjs.map +1 -1
- package/dist/cjs/route.d.cts +6 -34
- package/dist/cjs/router.cjs +94 -80
- package/dist/cjs/router.cjs.map +1 -1
- package/dist/cjs/router.d.cts +6 -5
- package/dist/cjs/scroll-restoration-script/server.cjs +1 -1
- package/dist/cjs/scroll-restoration-script/server.cjs.map +1 -1
- package/dist/cjs/scroll-restoration.cjs +7 -7
- package/dist/cjs/scroll-restoration.cjs.map +1 -1
- package/dist/cjs/searchParams.cjs +2 -2
- package/dist/cjs/searchParams.cjs.map +1 -1
- package/dist/cjs/ssr/constants.cjs +2 -2
- package/dist/cjs/ssr/constants.cjs.map +1 -1
- package/dist/cjs/ssr/createRequestHandler.cjs +2 -2
- package/dist/cjs/ssr/createRequestHandler.cjs.map +1 -1
- package/dist/cjs/ssr/serializer/RawStream.cjs +50 -41
- package/dist/cjs/ssr/serializer/RawStream.cjs.map +1 -1
- package/dist/cjs/ssr/serializer/RawStream.d.cts +12 -4
- package/dist/cjs/ssr/serializer/ShallowErrorPlugin.cjs +1 -1
- package/dist/cjs/ssr/serializer/ShallowErrorPlugin.cjs.map +1 -1
- package/dist/cjs/ssr/serializer/ShallowErrorPlugin.d.cts +2 -2
- package/dist/cjs/ssr/serializer/seroval-plugins.cjs +1 -1
- package/dist/cjs/ssr/serializer/seroval-plugins.cjs.map +1 -1
- package/dist/cjs/ssr/serializer/seroval-plugins.d.cts +2 -1
- package/dist/cjs/ssr/serializer/transformer.cjs +16 -14
- package/dist/cjs/ssr/serializer/transformer.cjs.map +1 -1
- package/dist/cjs/ssr/serializer/transformer.d.cts +24 -23
- package/dist/cjs/ssr/ssr-client.cjs +9 -9
- package/dist/cjs/ssr/ssr-client.cjs.map +1 -1
- package/dist/cjs/ssr/ssr-server.cjs +110 -22
- package/dist/cjs/ssr/ssr-server.cjs.map +1 -1
- package/dist/cjs/ssr/ssr-server.d.cts +4 -2
- package/dist/cjs/ssr/transformStreamWithRouter.cjs +10 -7
- package/dist/cjs/ssr/transformStreamWithRouter.cjs.map +1 -1
- package/dist/cjs/stores.cjs +57 -57
- package/dist/cjs/stores.cjs.map +1 -1
- package/dist/cjs/stores.d.cts +16 -16
- package/dist/cjs/utils.cjs +12 -7
- package/dist/cjs/utils.cjs.map +1 -1
- package/dist/cjs/utils.d.cts +1 -0
- package/dist/esm/Matches.js +1 -1
- package/dist/esm/Matches.js.map +1 -1
- package/dist/esm/config.js +1 -1
- package/dist/esm/config.js.map +1 -1
- package/dist/esm/defer.js +1 -1
- package/dist/esm/defer.js.map +1 -1
- package/dist/esm/hash-scroll.js +1 -1
- package/dist/esm/hash-scroll.js.map +1 -1
- package/dist/esm/index.d.ts +3 -3
- package/dist/esm/index.js +3 -3
- package/dist/esm/isServer/client.js +1 -1
- package/dist/esm/isServer/client.js.map +1 -1
- package/dist/esm/isServer/development.js +1 -1
- package/dist/esm/isServer/development.js.map +1 -1
- package/dist/esm/isServer/server.js +1 -1
- package/dist/esm/isServer/server.js.map +1 -1
- package/dist/esm/link.js +1 -1
- package/dist/esm/link.js.map +1 -1
- package/dist/esm/load-matches.js +26 -26
- package/dist/esm/load-matches.js.map +1 -1
- package/dist/esm/manifest.d.ts +8 -0
- package/dist/esm/manifest.js +28 -1
- package/dist/esm/manifest.js.map +1 -1
- package/dist/esm/new-process-route-tree.d.ts +4 -26
- package/dist/esm/new-process-route-tree.js +55 -68
- package/dist/esm/new-process-route-tree.js.map +1 -1
- package/dist/esm/not-found.js +1 -1
- package/dist/esm/not-found.js.map +1 -1
- package/dist/esm/path.js +1 -22
- package/dist/esm/path.js.map +1 -1
- package/dist/esm/root.js +1 -1
- package/dist/esm/root.js.map +1 -1
- package/dist/esm/route.d.ts +6 -34
- package/dist/esm/route.js.map +1 -1
- package/dist/esm/router.d.ts +6 -5
- package/dist/esm/router.js +96 -82
- package/dist/esm/router.js.map +1 -1
- package/dist/esm/scroll-restoration-script/server.js +1 -1
- package/dist/esm/scroll-restoration-script/server.js.map +1 -1
- package/dist/esm/scroll-restoration.js +7 -7
- package/dist/esm/scroll-restoration.js.map +1 -1
- package/dist/esm/searchParams.js +2 -2
- package/dist/esm/searchParams.js.map +1 -1
- package/dist/esm/ssr/constants.js +2 -2
- package/dist/esm/ssr/constants.js.map +1 -1
- package/dist/esm/ssr/createRequestHandler.js +2 -2
- package/dist/esm/ssr/createRequestHandler.js.map +1 -1
- package/dist/esm/ssr/serializer/RawStream.d.ts +12 -4
- package/dist/esm/ssr/serializer/RawStream.js +50 -41
- package/dist/esm/ssr/serializer/RawStream.js.map +1 -1
- package/dist/esm/ssr/serializer/ShallowErrorPlugin.d.ts +2 -2
- package/dist/esm/ssr/serializer/ShallowErrorPlugin.js +1 -1
- package/dist/esm/ssr/serializer/ShallowErrorPlugin.js.map +1 -1
- package/dist/esm/ssr/serializer/seroval-plugins.d.ts +2 -1
- package/dist/esm/ssr/serializer/seroval-plugins.js +1 -1
- package/dist/esm/ssr/serializer/seroval-plugins.js.map +1 -1
- package/dist/esm/ssr/serializer/transformer.d.ts +24 -23
- package/dist/esm/ssr/serializer/transformer.js +16 -14
- package/dist/esm/ssr/serializer/transformer.js.map +1 -1
- package/dist/esm/ssr/ssr-client.js +9 -9
- package/dist/esm/ssr/ssr-client.js.map +1 -1
- package/dist/esm/ssr/ssr-server.d.ts +4 -2
- package/dist/esm/ssr/ssr-server.js +110 -22
- package/dist/esm/ssr/ssr-server.js.map +1 -1
- package/dist/esm/ssr/transformStreamWithRouter.js +10 -7
- package/dist/esm/ssr/transformStreamWithRouter.js.map +1 -1
- package/dist/esm/stores.d.ts +16 -16
- package/dist/esm/stores.js +58 -58
- package/dist/esm/stores.js.map +1 -1
- package/dist/esm/utils.d.ts +1 -0
- package/dist/esm/utils.js +12 -8
- package/dist/esm/utils.js.map +1 -1
- package/package.json +4 -8
- package/skills/router-core/auth-and-guards/SKILL.md +41 -0
- package/skills/router-core/ssr/SKILL.md +59 -5
- package/skills/router-core/type-safety/SKILL.md +37 -42
- package/src/hash-scroll.ts +1 -1
- package/src/index.ts +9 -2
- package/src/load-matches.ts +14 -15
- package/src/manifest.ts +46 -0
- package/src/new-process-route-tree.ts +93 -106
- package/src/not-found.ts +1 -1
- package/src/path.ts +1 -27
- package/src/route.ts +10 -36
- package/src/router.ts +151 -140
- package/src/scroll-restoration.ts +1 -1
- package/src/ssr/createRequestHandler.ts +4 -5
- package/src/ssr/serializer/RawStream.ts +65 -56
- package/src/ssr/serializer/ShallowErrorPlugin.ts +2 -2
- package/src/ssr/serializer/seroval-plugins.ts +2 -1
- package/src/ssr/serializer/transformer.ts +71 -76
- package/src/ssr/ssr-client.ts +8 -12
- package/src/ssr/ssr-server.ts +170 -14
- package/src/ssr/transformStreamWithRouter.ts +3 -0
- package/src/stores.ts +86 -86
- package/src/utils.ts +7 -0
- package/bin/intent.js +0 -25
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
|
}
|
|
@@ -799,7 +799,9 @@ export interface ServerSsr {
|
|
|
799
799
|
setRenderFinished: () => void
|
|
800
800
|
cleanup: () => void
|
|
801
801
|
onSerializationFinished: (listener: () => void) => void
|
|
802
|
-
dehydrate: (
|
|
802
|
+
dehydrate: (opts?: {
|
|
803
|
+
requestAssets?: Array<RouterManagedTag>
|
|
804
|
+
}) => Promise<void>
|
|
803
805
|
takeBufferedScripts: () => RouterManagedTag | undefined
|
|
804
806
|
/**
|
|
805
807
|
* Takes any buffered HTML that was injected.
|
|
@@ -970,6 +972,7 @@ export class RouterCore<
|
|
|
970
972
|
routesByPath!: RoutesByPath<TRouteTree>
|
|
971
973
|
processedTree!: ProcessedTree<TRouteTree, any, any>
|
|
972
974
|
resolvePathCache!: LRUCache<string, string>
|
|
975
|
+
private routeBranchCache = new WeakMap<AnyRoute, ReadonlyArray<AnyRoute>>()
|
|
973
976
|
isServer!: boolean
|
|
974
977
|
pathParamsDecoder?: (encoded: string) => string
|
|
975
978
|
protocolAllowlist!: Set<string>
|
|
@@ -1165,7 +1168,7 @@ export class RouterCore<
|
|
|
1165
1168
|
}
|
|
1166
1169
|
|
|
1167
1170
|
if (needsLocationUpdate && this.stores) {
|
|
1168
|
-
this.stores.location.
|
|
1171
|
+
this.stores.location.set(this.latestLocation)
|
|
1169
1172
|
}
|
|
1170
1173
|
|
|
1171
1174
|
if (
|
|
@@ -1174,13 +1177,13 @@ export class RouterCore<
|
|
|
1174
1177
|
typeof window.CSS?.supports === 'function'
|
|
1175
1178
|
) {
|
|
1176
1179
|
this.isViewTransitionTypesSupported = window.CSS.supports(
|
|
1177
|
-
'selector(:active-view-transition-type(a)',
|
|
1180
|
+
'selector(:active-view-transition-type(a))',
|
|
1178
1181
|
)
|
|
1179
1182
|
}
|
|
1180
1183
|
}
|
|
1181
1184
|
|
|
1182
1185
|
get state(): RouterState<TRouteTree> {
|
|
1183
|
-
return this.stores.__store.
|
|
1186
|
+
return this.stores.__store.get()
|
|
1184
1187
|
}
|
|
1185
1188
|
|
|
1186
1189
|
updateLatestLocation = () => {
|
|
@@ -1339,15 +1342,23 @@ export class RouterCore<
|
|
|
1339
1342
|
return location
|
|
1340
1343
|
}
|
|
1341
1344
|
|
|
1342
|
-
/** Resolve a path
|
|
1345
|
+
/** Resolve a path using the router's trailing-slash policy. */
|
|
1343
1346
|
resolvePathWithBase = (from: string, path: string) => {
|
|
1344
|
-
|
|
1347
|
+
return resolvePath({
|
|
1345
1348
|
base: from,
|
|
1346
|
-
to: cleanPath(path),
|
|
1349
|
+
to: path.includes('//') ? cleanPath(path) : path,
|
|
1347
1350
|
trailingSlash: this.options.trailingSlash,
|
|
1348
1351
|
cache: this.resolvePathCache,
|
|
1349
1352
|
})
|
|
1350
|
-
|
|
1353
|
+
}
|
|
1354
|
+
|
|
1355
|
+
private getRouteBranch(route: AnyRoute) {
|
|
1356
|
+
let branch = this.routeBranchCache.get(route)
|
|
1357
|
+
if (!branch) {
|
|
1358
|
+
branch = buildRouteBranch(route)
|
|
1359
|
+
this.routeBranchCache.set(route, branch)
|
|
1360
|
+
}
|
|
1361
|
+
return branch
|
|
1351
1362
|
}
|
|
1352
1363
|
|
|
1353
1364
|
get looseRoutesById() {
|
|
@@ -1387,7 +1398,7 @@ export class RouterCore<
|
|
|
1387
1398
|
opts?: MatchRoutesOpts,
|
|
1388
1399
|
): Array<AnyRouteMatch> {
|
|
1389
1400
|
const matchedRoutesResult = this.getMatchedRoutes(next.pathname)
|
|
1390
|
-
const { foundRoute, routeParams
|
|
1401
|
+
const { foundRoute, routeParams } = matchedRoutesResult
|
|
1391
1402
|
let { matchedRoutes } = matchedRoutesResult
|
|
1392
1403
|
let isGlobalNotFound = false
|
|
1393
1404
|
|
|
@@ -1416,9 +1427,9 @@ export class RouterCore<
|
|
|
1416
1427
|
// Snapshot of active match state keyed by routeId, used to stabilise
|
|
1417
1428
|
// params/search across navigations.
|
|
1418
1429
|
const previousActiveMatchesByRouteId = new Map<string, AnyRouteMatch>()
|
|
1419
|
-
for (const store of this.stores.
|
|
1430
|
+
for (const store of this.stores.matchStores.values()) {
|
|
1420
1431
|
if (store.routeId) {
|
|
1421
|
-
previousActiveMatchesByRouteId.set(store.routeId, store.
|
|
1432
|
+
previousActiveMatchesByRouteId.set(store.routeId, store.get())
|
|
1422
1433
|
}
|
|
1423
1434
|
}
|
|
1424
1435
|
|
|
@@ -1513,7 +1524,7 @@ export class RouterCore<
|
|
|
1513
1524
|
|
|
1514
1525
|
if (!existingMatch) {
|
|
1515
1526
|
try {
|
|
1516
|
-
extractStrictParams(route,
|
|
1527
|
+
extractStrictParams(route, strictParams)
|
|
1517
1528
|
} catch (err: any) {
|
|
1518
1529
|
if (isNotFound(err) || isRedirect(err)) {
|
|
1519
1530
|
paramsError = err
|
|
@@ -1683,7 +1694,7 @@ export class RouterCore<
|
|
|
1683
1694
|
search: Record<string, unknown>
|
|
1684
1695
|
params: Record<string, unknown>
|
|
1685
1696
|
} {
|
|
1686
|
-
const { matchedRoutes, routeParams
|
|
1697
|
+
const { matchedRoutes, routeParams } = this.getMatchedRoutes(
|
|
1687
1698
|
location.pathname,
|
|
1688
1699
|
)
|
|
1689
1700
|
const lastRoute = last(matchedRoutes)!
|
|
@@ -1711,10 +1722,9 @@ export class RouterCore<
|
|
|
1711
1722
|
}
|
|
1712
1723
|
|
|
1713
1724
|
// Determine params: reuse from state if possible, otherwise parse
|
|
1714
|
-
const lastStateMatchId = last(this.stores.matchesId.
|
|
1725
|
+
const lastStateMatchId = last(this.stores.matchesId.get())
|
|
1715
1726
|
const lastStateMatch =
|
|
1716
|
-
lastStateMatchId &&
|
|
1717
|
-
this.stores.activeMatchStoresById.get(lastStateMatchId)?.state
|
|
1727
|
+
lastStateMatchId && this.stores.matchStores.get(lastStateMatchId)?.get()
|
|
1718
1728
|
const canReuseParams =
|
|
1719
1729
|
lastStateMatch &&
|
|
1720
1730
|
lastStateMatch.routeId === lastRoute.id &&
|
|
@@ -1731,12 +1741,7 @@ export class RouterCore<
|
|
|
1731
1741
|
)
|
|
1732
1742
|
for (const route of matchedRoutes) {
|
|
1733
1743
|
try {
|
|
1734
|
-
extractStrictParams(
|
|
1735
|
-
route,
|
|
1736
|
-
routeParams,
|
|
1737
|
-
parsedParams ?? {},
|
|
1738
|
-
strictParams,
|
|
1739
|
-
)
|
|
1744
|
+
extractStrictParams(route, strictParams)
|
|
1740
1745
|
} catch {
|
|
1741
1746
|
// Ignore errors, we're not actually routing
|
|
1742
1747
|
}
|
|
@@ -1763,16 +1768,16 @@ export class RouterCore<
|
|
|
1763
1768
|
}
|
|
1764
1769
|
|
|
1765
1770
|
cancelMatches = () => {
|
|
1766
|
-
this.stores.
|
|
1771
|
+
this.stores.pendingIds.get().forEach((matchId) => {
|
|
1767
1772
|
this.cancelMatch(matchId)
|
|
1768
1773
|
})
|
|
1769
1774
|
|
|
1770
|
-
this.stores.matchesId.
|
|
1771
|
-
if (this.stores.
|
|
1775
|
+
this.stores.matchesId.get().forEach((matchId) => {
|
|
1776
|
+
if (this.stores.pendingMatchStores.has(matchId)) {
|
|
1772
1777
|
return
|
|
1773
1778
|
}
|
|
1774
1779
|
|
|
1775
|
-
const match = this.stores.
|
|
1780
|
+
const match = this.stores.matchStores.get(matchId)?.get()
|
|
1776
1781
|
if (!match) {
|
|
1777
1782
|
return
|
|
1778
1783
|
}
|
|
@@ -1832,9 +1837,7 @@ export class RouterCore<
|
|
|
1832
1837
|
dest.unsafeRelative === 'path'
|
|
1833
1838
|
? currentLocation.pathname
|
|
1834
1839
|
: (dest.from ?? lightweightResult.fullPath)
|
|
1835
|
-
|
|
1836
|
-
// ensure this includes the basePath if set
|
|
1837
|
-
const fromPath = this.resolvePathWithBase(defaultedFromPath, '.')
|
|
1840
|
+
const destTo = dest.to ? `${dest.to}` : undefined
|
|
1838
1841
|
|
|
1839
1842
|
// From search should always use the current location
|
|
1840
1843
|
const fromSearch = lightweightResult.search
|
|
@@ -1844,11 +1847,15 @@ export class RouterCore<
|
|
|
1844
1847
|
lightweightResult.params,
|
|
1845
1848
|
)
|
|
1846
1849
|
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1850
|
+
const isAbsoluteTo = destTo?.charCodeAt(0) === 47
|
|
1851
|
+
const sourcePath = isAbsoluteTo
|
|
1852
|
+
? '/'
|
|
1853
|
+
: this.resolvePathWithBase(defaultedFromPath, '.')
|
|
1854
|
+
|
|
1855
|
+
// Resolve the destination. Absolute destinations don't need the source path.
|
|
1856
|
+
const nextTo = destTo
|
|
1857
|
+
? this.resolvePathWithBase(sourcePath, destTo)
|
|
1858
|
+
: sourcePath
|
|
1852
1859
|
|
|
1853
1860
|
// Resolve the next params
|
|
1854
1861
|
const nextParams =
|
|
@@ -1861,24 +1868,33 @@ export class RouterCore<
|
|
|
1861
1868
|
functionalUpdate(dest.params as any, fromParams),
|
|
1862
1869
|
)
|
|
1863
1870
|
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
let destRoutes
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1871
|
+
const destRoute = this.routesByPath[
|
|
1872
|
+
trimPathRight(nextTo) as keyof typeof this.routesByPath
|
|
1873
|
+
] as AnyRoute | undefined
|
|
1874
|
+
|
|
1875
|
+
let destRoutes: ReadonlyArray<AnyRoute>
|
|
1876
|
+
if (destRoute) {
|
|
1877
|
+
destRoutes = this.getRouteBranch(destRoute)
|
|
1878
|
+
} else if (nextTo.includes('$')) {
|
|
1879
|
+
// Route templates must match routesByPath exactly. A miss here is a
|
|
1880
|
+
// typed destination mismatch, not a concrete URL to route-match.
|
|
1881
|
+
destRoutes = []
|
|
1882
|
+
} else {
|
|
1883
|
+
const destMatchResult = this.getMatchedRoutes(nextTo)
|
|
1884
|
+
destRoutes = destMatchResult.matchedRoutes
|
|
1875
1885
|
|
|
1876
|
-
|
|
1877
|
-
|
|
1886
|
+
if (
|
|
1887
|
+
this.options.notFoundRoute &&
|
|
1888
|
+
(!destMatchResult.foundRoute ||
|
|
1889
|
+
(destMatchResult.foundRoute.path !== '/' &&
|
|
1890
|
+
destMatchResult.routeParams['**']))
|
|
1891
|
+
) {
|
|
1892
|
+
destRoutes = [...destRoutes, this.options.notFoundRoute]
|
|
1893
|
+
}
|
|
1878
1894
|
}
|
|
1879
1895
|
|
|
1880
1896
|
// If there are any params, we need to stringify them
|
|
1881
|
-
if (
|
|
1897
|
+
if (destRoutes.length && hasKeys(nextParams)) {
|
|
1882
1898
|
for (const route of destRoutes) {
|
|
1883
1899
|
const fn =
|
|
1884
1900
|
route.options.params?.stringify ?? route.options.stringifyParams
|
|
@@ -1897,8 +1913,7 @@ export class RouterCore<
|
|
|
1897
1913
|
}
|
|
1898
1914
|
|
|
1899
1915
|
const nextPathname = opts.leaveParams
|
|
1900
|
-
? //
|
|
1901
|
-
// This preserves the original parameter syntax including optional parameters
|
|
1916
|
+
? // Keep path params uninterpolated for matchRoute/template matching.
|
|
1902
1917
|
nextTo
|
|
1903
1918
|
: decodePath(
|
|
1904
1919
|
interpolatePath({
|
|
@@ -1909,6 +1924,24 @@ export class RouterCore<
|
|
|
1909
1924
|
}).interpolatedPath,
|
|
1910
1925
|
).path
|
|
1911
1926
|
|
|
1927
|
+
if (
|
|
1928
|
+
process.env.NODE_ENV !== 'production' &&
|
|
1929
|
+
destRoute &&
|
|
1930
|
+
!opts.leaveParams
|
|
1931
|
+
) {
|
|
1932
|
+
try {
|
|
1933
|
+
const roundTrip = this.getMatchedRoutes(nextPathname)
|
|
1934
|
+
if (roundTrip.foundRoute?.id !== destRoute.id) {
|
|
1935
|
+
console.warn(
|
|
1936
|
+
`Generated path "${nextPathname}" for route "${destRoute.id}" did not match the same route after params.stringify.`,
|
|
1937
|
+
)
|
|
1938
|
+
}
|
|
1939
|
+
} catch {
|
|
1940
|
+
// Ignore roundtrip validation errors. The generated location will be
|
|
1941
|
+
// handled by the normal navigation flow.
|
|
1942
|
+
}
|
|
1943
|
+
}
|
|
1944
|
+
|
|
1912
1945
|
// Resolve the next search
|
|
1913
1946
|
let nextSearch = fromSearch
|
|
1914
1947
|
if (opts._includeValidateSearch && this.options.search?.strict) {
|
|
@@ -2352,19 +2385,19 @@ export class RouterCore<
|
|
|
2352
2385
|
// Match the routes
|
|
2353
2386
|
const pendingMatches = this.matchRoutes(this.latestLocation)
|
|
2354
2387
|
|
|
2355
|
-
const nextCachedMatches = this.stores.
|
|
2356
|
-
|
|
2357
|
-
|
|
2388
|
+
const nextCachedMatches = this.stores.cachedMatches
|
|
2389
|
+
.get()
|
|
2390
|
+
.filter((d) => !pendingMatches.some((e) => e.id === d.id))
|
|
2358
2391
|
|
|
2359
2392
|
// Ingest the new matches
|
|
2360
2393
|
this.batch(() => {
|
|
2361
|
-
this.stores.status.
|
|
2362
|
-
this.stores.statusCode.
|
|
2363
|
-
this.stores.isLoading.
|
|
2364
|
-
this.stores.location.
|
|
2365
|
-
this.stores.
|
|
2394
|
+
this.stores.status.set('pending')
|
|
2395
|
+
this.stores.statusCode.set(200)
|
|
2396
|
+
this.stores.isLoading.set(true)
|
|
2397
|
+
this.stores.location.set(this.latestLocation)
|
|
2398
|
+
this.stores.setPending(pendingMatches)
|
|
2366
2399
|
// If a cached match moved to pending matches, remove it from cached matches
|
|
2367
|
-
this.stores.
|
|
2400
|
+
this.stores.setCached(nextCachedMatches)
|
|
2368
2401
|
})
|
|
2369
2402
|
}
|
|
2370
2403
|
|
|
@@ -2373,7 +2406,7 @@ export class RouterCore<
|
|
|
2373
2406
|
let notFound: NotFoundError | undefined
|
|
2374
2407
|
let loadPromise: Promise<void>
|
|
2375
2408
|
const previousLocation =
|
|
2376
|
-
this.stores.resolvedLocation.
|
|
2409
|
+
this.stores.resolvedLocation.get() ?? this.stores.location.get()
|
|
2377
2410
|
|
|
2378
2411
|
// eslint-disable-next-line prefer-const
|
|
2379
2412
|
loadPromise = new Promise<void>((resolve) => {
|
|
@@ -2381,10 +2414,10 @@ export class RouterCore<
|
|
|
2381
2414
|
try {
|
|
2382
2415
|
this.beforeLoad()
|
|
2383
2416
|
const next = this.latestLocation
|
|
2384
|
-
const prevLocation = this.stores.resolvedLocation.
|
|
2417
|
+
const prevLocation = this.stores.resolvedLocation.get()
|
|
2385
2418
|
const locationChangeInfo = getLocationChangeInfo(next, prevLocation)
|
|
2386
2419
|
|
|
2387
|
-
if (!this.stores.redirect.
|
|
2420
|
+
if (!this.stores.redirect.get()) {
|
|
2388
2421
|
this.emit({
|
|
2389
2422
|
type: 'onBeforeNavigate',
|
|
2390
2423
|
...locationChangeInfo,
|
|
@@ -2400,7 +2433,7 @@ export class RouterCore<
|
|
|
2400
2433
|
router: this,
|
|
2401
2434
|
sync: opts?.sync,
|
|
2402
2435
|
forceStaleReload: previousLocation.href === next.href,
|
|
2403
|
-
matches: this.stores.
|
|
2436
|
+
matches: this.stores.pendingMatches.get(),
|
|
2404
2437
|
location: next,
|
|
2405
2438
|
updateMatch: this.updateMatch,
|
|
2406
2439
|
// eslint-disable-next-line @typescript-eslint/require-await
|
|
@@ -2424,27 +2457,25 @@ export class RouterCore<
|
|
|
2424
2457
|
let hookStayingMatches: Array<AnyRouteMatch> | null = null
|
|
2425
2458
|
|
|
2426
2459
|
this.batch(() => {
|
|
2427
|
-
const pendingMatches =
|
|
2428
|
-
this.stores.pendingMatchesSnapshot.state
|
|
2460
|
+
const pendingMatches = this.stores.pendingMatches.get()
|
|
2429
2461
|
const mountPending = pendingMatches.length
|
|
2430
|
-
const currentMatches =
|
|
2431
|
-
this.stores.activeMatchesSnapshot.state
|
|
2462
|
+
const currentMatches = this.stores.matches.get()
|
|
2432
2463
|
|
|
2433
2464
|
exitingMatches = mountPending
|
|
2434
2465
|
? currentMatches.filter(
|
|
2435
2466
|
(match) =>
|
|
2436
|
-
!this.stores.
|
|
2467
|
+
!this.stores.pendingMatchStores.has(match.id),
|
|
2437
2468
|
)
|
|
2438
2469
|
: null
|
|
2439
2470
|
|
|
2440
2471
|
// Lifecycle-hook identity: routeId only (route presence in tree)
|
|
2441
2472
|
// Build routeId sets from pools to avoid derived stores.
|
|
2442
2473
|
const pendingRouteIds = new Set<string>()
|
|
2443
|
-
for (const s of this.stores.
|
|
2474
|
+
for (const s of this.stores.pendingMatchStores.values()) {
|
|
2444
2475
|
if (s.routeId) pendingRouteIds.add(s.routeId)
|
|
2445
2476
|
}
|
|
2446
2477
|
const activeRouteIds = new Set<string>()
|
|
2447
|
-
for (const s of this.stores.
|
|
2478
|
+
for (const s of this.stores.matchStores.values()) {
|
|
2448
2479
|
if (s.routeId) activeRouteIds.add(s.routeId)
|
|
2449
2480
|
}
|
|
2450
2481
|
|
|
@@ -2464,8 +2495,8 @@ export class RouterCore<
|
|
|
2464
2495
|
)
|
|
2465
2496
|
: currentMatches
|
|
2466
2497
|
|
|
2467
|
-
this.stores.isLoading.
|
|
2468
|
-
this.stores.loadedAt.
|
|
2498
|
+
this.stores.isLoading.set(false)
|
|
2499
|
+
this.stores.loadedAt.set(Date.now())
|
|
2469
2500
|
/**
|
|
2470
2501
|
* When committing new matches, cache any exiting matches that are still usable.
|
|
2471
2502
|
* Routes that resolved with `status: 'error'` or `status: 'notFound'` are
|
|
@@ -2473,10 +2504,10 @@ export class RouterCore<
|
|
|
2473
2504
|
* or reloads re-run their loaders instead of reusing the failed/not-found data.
|
|
2474
2505
|
*/
|
|
2475
2506
|
if (mountPending) {
|
|
2476
|
-
this.stores.
|
|
2477
|
-
this.stores.
|
|
2478
|
-
this.stores.
|
|
2479
|
-
...this.stores.
|
|
2507
|
+
this.stores.setMatches(pendingMatches)
|
|
2508
|
+
this.stores.setPending([])
|
|
2509
|
+
this.stores.setCached([
|
|
2510
|
+
...this.stores.cachedMatches.get(),
|
|
2480
2511
|
...exitingMatches!.filter(
|
|
2481
2512
|
(d) =>
|
|
2482
2513
|
d.status !== 'error' &&
|
|
@@ -2523,15 +2554,13 @@ export class RouterCore<
|
|
|
2523
2554
|
? redirect.status
|
|
2524
2555
|
: notFound
|
|
2525
2556
|
? 404
|
|
2526
|
-
: this.stores.
|
|
2527
|
-
(d) => d.status === 'error',
|
|
2528
|
-
)
|
|
2557
|
+
: this.stores.matches.get().some((d) => d.status === 'error')
|
|
2529
2558
|
? 500
|
|
2530
2559
|
: 200
|
|
2531
2560
|
|
|
2532
2561
|
this.batch(() => {
|
|
2533
|
-
this.stores.statusCode.
|
|
2534
|
-
this.stores.redirect.
|
|
2562
|
+
this.stores.statusCode.set(nextStatusCode)
|
|
2563
|
+
this.stores.redirect.set(redirect)
|
|
2535
2564
|
})
|
|
2536
2565
|
}
|
|
2537
2566
|
|
|
@@ -2559,13 +2588,11 @@ export class RouterCore<
|
|
|
2559
2588
|
let newStatusCode: number | undefined = undefined
|
|
2560
2589
|
if (this.hasNotFoundMatch()) {
|
|
2561
2590
|
newStatusCode = 404
|
|
2562
|
-
} else if (
|
|
2563
|
-
this.stores.activeMatchesSnapshot.state.some((d) => d.status === 'error')
|
|
2564
|
-
) {
|
|
2591
|
+
} else if (this.stores.matches.get().some((d) => d.status === 'error')) {
|
|
2565
2592
|
newStatusCode = 500
|
|
2566
2593
|
}
|
|
2567
2594
|
if (newStatusCode !== undefined) {
|
|
2568
|
-
this.stores.statusCode.
|
|
2595
|
+
this.stores.statusCode.set(newStatusCode)
|
|
2569
2596
|
}
|
|
2570
2597
|
}
|
|
2571
2598
|
|
|
@@ -2594,7 +2621,7 @@ export class RouterCore<
|
|
|
2594
2621
|
this.isViewTransitionTypesSupported
|
|
2595
2622
|
) {
|
|
2596
2623
|
const next = this.latestLocation
|
|
2597
|
-
const prevLocation = this.stores.resolvedLocation.
|
|
2624
|
+
const prevLocation = this.stores.resolvedLocation.get()
|
|
2598
2625
|
|
|
2599
2626
|
const resolvedViewTransitionTypes =
|
|
2600
2627
|
typeof shouldViewTransition.types === 'function'
|
|
@@ -2624,30 +2651,30 @@ export class RouterCore<
|
|
|
2624
2651
|
|
|
2625
2652
|
updateMatch: UpdateMatchFn = (id, updater) => {
|
|
2626
2653
|
this.startTransition(() => {
|
|
2627
|
-
const pendingMatch = this.stores.
|
|
2654
|
+
const pendingMatch = this.stores.pendingMatchStores.get(id)
|
|
2628
2655
|
if (pendingMatch) {
|
|
2629
|
-
pendingMatch.
|
|
2656
|
+
pendingMatch.set(updater)
|
|
2630
2657
|
return
|
|
2631
2658
|
}
|
|
2632
2659
|
|
|
2633
|
-
const activeMatch = this.stores.
|
|
2660
|
+
const activeMatch = this.stores.matchStores.get(id)
|
|
2634
2661
|
if (activeMatch) {
|
|
2635
|
-
activeMatch.
|
|
2662
|
+
activeMatch.set(updater)
|
|
2636
2663
|
return
|
|
2637
2664
|
}
|
|
2638
2665
|
|
|
2639
|
-
const cachedMatch = this.stores.
|
|
2666
|
+
const cachedMatch = this.stores.cachedMatchStores.get(id)
|
|
2640
2667
|
if (cachedMatch) {
|
|
2641
|
-
const next = updater(cachedMatch.
|
|
2668
|
+
const next = updater(cachedMatch.get())
|
|
2642
2669
|
if (next.status === 'redirected') {
|
|
2643
|
-
const deleted = this.stores.
|
|
2670
|
+
const deleted = this.stores.cachedMatchStores.delete(id)
|
|
2644
2671
|
if (deleted) {
|
|
2645
|
-
this.stores.
|
|
2672
|
+
this.stores.cachedIds.set((prev) =>
|
|
2646
2673
|
prev.filter((matchId) => matchId !== id),
|
|
2647
2674
|
)
|
|
2648
2675
|
}
|
|
2649
2676
|
} else {
|
|
2650
|
-
cachedMatch.
|
|
2677
|
+
cachedMatch.set(next)
|
|
2651
2678
|
}
|
|
2652
2679
|
}
|
|
2653
2680
|
})
|
|
@@ -2655,9 +2682,9 @@ export class RouterCore<
|
|
|
2655
2682
|
|
|
2656
2683
|
getMatch: GetMatchFn = (matchId: string): AnyRouteMatch | undefined => {
|
|
2657
2684
|
return (
|
|
2658
|
-
this.stores.
|
|
2659
|
-
this.stores.
|
|
2660
|
-
this.stores.
|
|
2685
|
+
this.stores.cachedMatchStores.get(matchId)?.get() ??
|
|
2686
|
+
this.stores.pendingMatchStores.get(matchId)?.get() ??
|
|
2687
|
+
this.stores.matchStores.get(matchId)?.get()
|
|
2661
2688
|
)
|
|
2662
2689
|
}
|
|
2663
2690
|
|
|
@@ -2694,15 +2721,9 @@ export class RouterCore<
|
|
|
2694
2721
|
}
|
|
2695
2722
|
|
|
2696
2723
|
this.batch(() => {
|
|
2697
|
-
this.stores.
|
|
2698
|
-
|
|
2699
|
-
)
|
|
2700
|
-
this.stores.setCachedMatches(
|
|
2701
|
-
this.stores.cachedMatchesSnapshot.state.map(invalidate),
|
|
2702
|
-
)
|
|
2703
|
-
this.stores.setPendingMatches(
|
|
2704
|
-
this.stores.pendingMatchesSnapshot.state.map(invalidate),
|
|
2705
|
-
)
|
|
2724
|
+
this.stores.setMatches(this.stores.matches.get().map(invalidate))
|
|
2725
|
+
this.stores.setCached(this.stores.cachedMatches.get().map(invalidate))
|
|
2726
|
+
this.stores.setPending(this.stores.pendingMatches.get().map(invalidate))
|
|
2706
2727
|
})
|
|
2707
2728
|
|
|
2708
2729
|
this.shouldViewTransition = false
|
|
@@ -2760,13 +2781,13 @@ export class RouterCore<
|
|
|
2760
2781
|
clearCache: ClearCacheFn<this> = (opts) => {
|
|
2761
2782
|
const filter = opts?.filter
|
|
2762
2783
|
if (filter !== undefined) {
|
|
2763
|
-
this.stores.
|
|
2764
|
-
this.stores.
|
|
2765
|
-
(
|
|
2766
|
-
|
|
2784
|
+
this.stores.setCached(
|
|
2785
|
+
this.stores.cachedMatches
|
|
2786
|
+
.get()
|
|
2787
|
+
.filter((m) => !filter(m as MakeRouteMatchUnion<this>)),
|
|
2767
2788
|
)
|
|
2768
2789
|
} else {
|
|
2769
|
-
this.stores.
|
|
2790
|
+
this.stores.setCached([])
|
|
2770
2791
|
}
|
|
2771
2792
|
}
|
|
2772
2793
|
|
|
@@ -2814,13 +2835,13 @@ export class RouterCore<
|
|
|
2814
2835
|
})
|
|
2815
2836
|
|
|
2816
2837
|
const activeMatchIds = new Set([
|
|
2817
|
-
...this.stores.matchesId.
|
|
2818
|
-
...this.stores.
|
|
2838
|
+
...this.stores.matchesId.get(),
|
|
2839
|
+
...this.stores.pendingIds.get(),
|
|
2819
2840
|
])
|
|
2820
2841
|
|
|
2821
2842
|
const loadedMatchIds = new Set([
|
|
2822
2843
|
...activeMatchIds,
|
|
2823
|
-
...this.stores.
|
|
2844
|
+
...this.stores.cachedIds.get(),
|
|
2824
2845
|
])
|
|
2825
2846
|
|
|
2826
2847
|
// If the matches are already loaded, we need to add them to the cached matches.
|
|
@@ -2828,8 +2849,8 @@ export class RouterCore<
|
|
|
2828
2849
|
(match) => !loadedMatchIds.has(match.id),
|
|
2829
2850
|
)
|
|
2830
2851
|
if (matchesToCache.length) {
|
|
2831
|
-
const cachedMatches = this.stores.
|
|
2832
|
-
this.stores.
|
|
2852
|
+
const cachedMatches = this.stores.cachedMatches.get()
|
|
2853
|
+
this.stores.setCached([...cachedMatches, ...matchesToCache])
|
|
2833
2854
|
}
|
|
2834
2855
|
|
|
2835
2856
|
try {
|
|
@@ -2884,16 +2905,16 @@ export class RouterCore<
|
|
|
2884
2905
|
}
|
|
2885
2906
|
const next = this.buildLocation(matchLocation as any)
|
|
2886
2907
|
|
|
2887
|
-
if (opts?.pending && this.stores.status.
|
|
2908
|
+
if (opts?.pending && this.stores.status.get() !== 'pending') {
|
|
2888
2909
|
return false
|
|
2889
2910
|
}
|
|
2890
2911
|
|
|
2891
2912
|
const pending =
|
|
2892
|
-
opts?.pending === undefined ? !this.stores.isLoading.
|
|
2913
|
+
opts?.pending === undefined ? !this.stores.isLoading.get() : opts.pending
|
|
2893
2914
|
|
|
2894
2915
|
const baseLocation = pending
|
|
2895
2916
|
? this.latestLocation
|
|
2896
|
-
: this.stores.resolvedLocation.
|
|
2917
|
+
: this.stores.resolvedLocation.get() || this.stores.location.get()
|
|
2897
2918
|
|
|
2898
2919
|
const match = findSingleMatch(
|
|
2899
2920
|
next.pathname,
|
|
@@ -2929,9 +2950,9 @@ export class RouterCore<
|
|
|
2929
2950
|
serverSsr?: ServerSsr
|
|
2930
2951
|
|
|
2931
2952
|
hasNotFoundMatch = () => {
|
|
2932
|
-
return this.stores.
|
|
2933
|
-
(
|
|
2934
|
-
|
|
2953
|
+
return this.stores.matches
|
|
2954
|
+
.get()
|
|
2955
|
+
.some((d) => d.status === 'notFound' || d.globalNotFound)
|
|
2935
2956
|
}
|
|
2936
2957
|
}
|
|
2937
2958
|
|
|
@@ -3024,17 +3045,15 @@ export function getMatchedRoutes<TRouteLike extends RouteLike>({
|
|
|
3024
3045
|
const trimmedPath = trimPathRight(pathname)
|
|
3025
3046
|
|
|
3026
3047
|
let foundRoute: TRouteLike | undefined = undefined
|
|
3027
|
-
let parsedParams: Record<string, unknown> | undefined = undefined
|
|
3028
3048
|
const match = findRouteMatch<TRouteLike>(trimmedPath, processedTree, true)
|
|
3029
3049
|
if (match) {
|
|
3030
3050
|
foundRoute = match.route
|
|
3031
3051
|
Object.assign(routeParams, match.rawParams) // Copy params, because they're cached
|
|
3032
|
-
parsedParams = Object.assign(Object.create(null), match.parsedParams)
|
|
3033
3052
|
}
|
|
3034
3053
|
|
|
3035
3054
|
const matchedRoutes = match?.branch || [routesById[rootRouteId]!]
|
|
3036
3055
|
|
|
3037
|
-
return { matchedRoutes, routeParams, foundRoute
|
|
3056
|
+
return { matchedRoutes, routeParams, foundRoute }
|
|
3038
3057
|
}
|
|
3039
3058
|
|
|
3040
3059
|
/**
|
|
@@ -3186,22 +3205,14 @@ function findGlobalNotFoundRouteId(
|
|
|
3186
3205
|
|
|
3187
3206
|
function extractStrictParams(
|
|
3188
3207
|
route: AnyRoute,
|
|
3189
|
-
referenceParams: Record<string, unknown>,
|
|
3190
|
-
parsedParams: Record<string, unknown>,
|
|
3191
3208
|
accumulatedParams: Record<string, unknown>,
|
|
3192
3209
|
) {
|
|
3193
3210
|
const parseParams = route.options.params?.parse ?? route.options.parseParams
|
|
3194
3211
|
if (parseParams) {
|
|
3195
|
-
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
if (key in parsedParams) {
|
|
3199
|
-
accumulatedParams[key] = parsedParams[key]
|
|
3200
|
-
}
|
|
3201
|
-
}
|
|
3202
|
-
} else {
|
|
3203
|
-
const result = parseParams(accumulatedParams as Record<string, string>)
|
|
3204
|
-
Object.assign(accumulatedParams, result)
|
|
3212
|
+
const result = parseParams(accumulatedParams as Record<string, string>)
|
|
3213
|
+
if (result === false) {
|
|
3214
|
+
throw new Error('Route params.parse returned false for a matched route')
|
|
3205
3215
|
}
|
|
3216
|
+
Object.assign(accumulatedParams, result)
|
|
3206
3217
|
}
|
|
3207
3218
|
}
|
|
@@ -233,7 +233,7 @@ export function setupScrollRestoration(router: AnyRouter, force?: boolean) {
|
|
|
233
233
|
window.addEventListener('pagehide', () => {
|
|
234
234
|
snapshotCurrentScrollTargets(
|
|
235
235
|
getKey(
|
|
236
|
-
router.stores.resolvedLocation.
|
|
236
|
+
router.stores.resolvedLocation.get() ?? router.stores.location.get(),
|
|
237
237
|
),
|
|
238
238
|
)
|
|
239
239
|
cache.persist()
|
|
@@ -78,13 +78,12 @@ export function createRequestHandler<TRouter extends AnyRouter>({
|
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
function getRequestHeaders(opts: { router: AnyRouter }): Headers {
|
|
81
|
-
const matchHeaders =
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
)
|
|
81
|
+
const matchHeaders = opts.router.stores.matches
|
|
82
|
+
.get()
|
|
83
|
+
.map<AnyHeaders>((match) => match.headers)
|
|
85
84
|
|
|
86
85
|
// Handle Redirects
|
|
87
|
-
const redirect = opts.router.stores.redirect.
|
|
86
|
+
const redirect = opts.router.stores.redirect.get()
|
|
88
87
|
if (redirect) {
|
|
89
88
|
matchHeaders.push(redirect.headers)
|
|
90
89
|
}
|