@tanstack/router-core 1.132.0-alpha.0 → 1.132.0-alpha.12
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.map +1 -1
- package/dist/cjs/Matches.d.cts +9 -11
- package/dist/cjs/config.cjs +10 -0
- package/dist/cjs/config.cjs.map +1 -0
- package/dist/cjs/config.d.cts +17 -0
- package/dist/cjs/fileRoute.d.cts +3 -2
- package/dist/cjs/index.cjs +10 -2
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +8 -3
- package/dist/cjs/load-matches.cjs +636 -0
- package/dist/cjs/load-matches.cjs.map +1 -0
- package/dist/cjs/load-matches.d.cts +16 -0
- package/dist/cjs/qss.cjs +19 -19
- package/dist/cjs/qss.cjs.map +1 -1
- package/dist/cjs/qss.d.cts +6 -4
- package/dist/cjs/redirect.cjs +3 -3
- package/dist/cjs/redirect.cjs.map +1 -1
- package/dist/cjs/route.cjs.map +1 -1
- package/dist/cjs/route.d.cts +42 -41
- package/dist/cjs/router.cjs +85 -654
- package/dist/cjs/router.cjs.map +1 -1
- package/dist/cjs/router.d.cts +19 -23
- package/dist/cjs/scroll-restoration.cjs +32 -29
- package/dist/cjs/scroll-restoration.cjs.map +1 -1
- package/dist/cjs/scroll-restoration.d.cts +1 -10
- package/dist/cjs/searchParams.cjs +7 -15
- package/dist/cjs/searchParams.cjs.map +1 -1
- package/dist/cjs/ssr/constants.cjs +5 -0
- package/dist/cjs/ssr/constants.cjs.map +1 -0
- package/dist/cjs/ssr/constants.d.cts +1 -0
- package/dist/cjs/ssr/{seroval-plugins.cjs → serializer/ShallowErrorPlugin.cjs} +2 -2
- package/dist/cjs/ssr/serializer/ShallowErrorPlugin.cjs.map +1 -0
- package/dist/cjs/ssr/{seroval-plugins.d.cts → serializer/ShallowErrorPlugin.d.cts} +1 -2
- package/dist/cjs/ssr/serializer/seroval-plugins.cjs +11 -0
- package/dist/cjs/ssr/serializer/seroval-plugins.cjs.map +1 -0
- package/dist/cjs/ssr/serializer/seroval-plugins.d.cts +2 -0
- package/dist/cjs/ssr/serializer/transformer.cjs +52 -0
- package/dist/cjs/ssr/serializer/transformer.cjs.map +1 -0
- package/dist/cjs/ssr/serializer/transformer.d.cts +56 -0
- package/dist/cjs/ssr/server.d.cts +5 -0
- package/dist/cjs/ssr/ssr-client.cjs +53 -40
- package/dist/cjs/ssr/ssr-client.cjs.map +1 -1
- package/dist/cjs/ssr/ssr-client.d.cts +5 -1
- package/dist/cjs/ssr/ssr-server.cjs +12 -10
- package/dist/cjs/ssr/ssr-server.cjs.map +1 -1
- package/dist/cjs/ssr/ssr-server.d.cts +0 -1
- package/dist/cjs/ssr/tsrScript.cjs +1 -1
- package/dist/cjs/ssr/tsrScript.cjs.map +1 -1
- package/dist/cjs/typePrimitives.d.cts +6 -6
- package/dist/cjs/utils.cjs +14 -7
- package/dist/cjs/utils.cjs.map +1 -1
- package/dist/cjs/utils.d.cts +2 -1
- package/dist/esm/Matches.d.ts +9 -11
- package/dist/esm/Matches.js.map +1 -1
- package/dist/esm/config.d.ts +17 -0
- package/dist/esm/config.js +10 -0
- package/dist/esm/config.js.map +1 -0
- package/dist/esm/fileRoute.d.ts +3 -2
- package/dist/esm/index.d.ts +8 -3
- package/dist/esm/index.js +11 -3
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/load-matches.d.ts +16 -0
- package/dist/esm/load-matches.js +636 -0
- package/dist/esm/load-matches.js.map +1 -0
- package/dist/esm/qss.d.ts +6 -4
- package/dist/esm/qss.js +19 -19
- package/dist/esm/qss.js.map +1 -1
- package/dist/esm/redirect.js +3 -3
- package/dist/esm/redirect.js.map +1 -1
- package/dist/esm/route.d.ts +42 -41
- package/dist/esm/route.js.map +1 -1
- package/dist/esm/router.d.ts +19 -23
- package/dist/esm/router.js +85 -654
- package/dist/esm/router.js.map +1 -1
- package/dist/esm/scroll-restoration.d.ts +1 -10
- package/dist/esm/scroll-restoration.js +32 -29
- package/dist/esm/scroll-restoration.js.map +1 -1
- package/dist/esm/searchParams.js +7 -15
- package/dist/esm/searchParams.js.map +1 -1
- package/dist/esm/ssr/constants.d.ts +1 -0
- package/dist/esm/ssr/constants.js +5 -0
- package/dist/esm/ssr/constants.js.map +1 -0
- package/dist/esm/ssr/{seroval-plugins.d.ts → serializer/ShallowErrorPlugin.d.ts} +1 -2
- package/dist/esm/ssr/{seroval-plugins.js → serializer/ShallowErrorPlugin.js} +2 -2
- package/dist/esm/ssr/serializer/ShallowErrorPlugin.js.map +1 -0
- package/dist/esm/ssr/serializer/seroval-plugins.d.ts +2 -0
- package/dist/esm/ssr/serializer/seroval-plugins.js +11 -0
- package/dist/esm/ssr/serializer/seroval-plugins.js.map +1 -0
- package/dist/esm/ssr/serializer/transformer.d.ts +56 -0
- package/dist/esm/ssr/serializer/transformer.js +52 -0
- package/dist/esm/ssr/serializer/transformer.js.map +1 -0
- package/dist/esm/ssr/server.d.ts +5 -0
- package/dist/esm/ssr/ssr-client.d.ts +5 -1
- package/dist/esm/ssr/ssr-client.js +53 -40
- package/dist/esm/ssr/ssr-client.js.map +1 -1
- package/dist/esm/ssr/ssr-server.d.ts +0 -1
- package/dist/esm/ssr/ssr-server.js +12 -10
- package/dist/esm/ssr/ssr-server.js.map +1 -1
- package/dist/esm/ssr/tsrScript.js +1 -1
- package/dist/esm/ssr/tsrScript.js.map +1 -1
- package/dist/esm/typePrimitives.d.ts +6 -6
- package/dist/esm/utils.d.ts +2 -1
- package/dist/esm/utils.js +14 -7
- package/dist/esm/utils.js.map +1 -1
- package/package.json +2 -2
- package/src/Matches.ts +18 -10
- package/src/config.ts +42 -0
- package/src/fileRoute.ts +15 -3
- package/src/index.ts +24 -2
- package/src/load-matches.ts +955 -0
- package/src/qss.ts +27 -24
- package/src/redirect.ts +3 -3
- package/src/route.ts +146 -35
- package/src/router.ts +135 -925
- package/src/scroll-restoration.ts +42 -37
- package/src/searchParams.ts +8 -19
- package/src/ssr/constants.ts +1 -0
- package/src/ssr/{seroval-plugins.ts → serializer/ShallowErrorPlugin.ts} +2 -2
- package/src/ssr/serializer/seroval-plugins.ts +9 -0
- package/src/ssr/serializer/transformer.ts +215 -0
- package/src/ssr/server.ts +6 -0
- package/src/ssr/ssr-client.ts +72 -44
- package/src/ssr/ssr-server.ts +18 -10
- package/src/ssr/tsrScript.ts +5 -1
- package/src/typePrimitives.ts +6 -6
- package/src/utils.ts +21 -10
- package/dist/cjs/ssr/seroval-plugins.cjs.map +0 -1
- package/dist/esm/ssr/seroval-plugins.js.map +0 -1
package/src/router.ts
CHANGED
|
@@ -8,9 +8,9 @@ import invariant from 'tiny-invariant'
|
|
|
8
8
|
import {
|
|
9
9
|
createControlledPromise,
|
|
10
10
|
deepEqual,
|
|
11
|
+
findLast,
|
|
11
12
|
functionalUpdate,
|
|
12
13
|
last,
|
|
13
|
-
pick,
|
|
14
14
|
replaceEqualDeep,
|
|
15
15
|
} from './utils'
|
|
16
16
|
import {
|
|
@@ -34,6 +34,7 @@ import { defaultParseSearch, defaultStringifySearch } from './searchParams'
|
|
|
34
34
|
import { rootRouteId } from './root'
|
|
35
35
|
import { isRedirect, redirect } from './redirect'
|
|
36
36
|
import { createLRUCache } from './lru-cache'
|
|
37
|
+
import { loadMatches, loadRouteChunk, routeNeedsPreload } from './load-matches'
|
|
37
38
|
import type { ParsePathnameCache, Segment } from './path'
|
|
38
39
|
import type { SearchParser, SearchSerializer } from './searchParams'
|
|
39
40
|
import type { AnyRedirect, ResolvedRedirect } from './redirect'
|
|
@@ -45,6 +46,7 @@ import type {
|
|
|
45
46
|
} from '@tanstack/history'
|
|
46
47
|
import type {
|
|
47
48
|
Awaitable,
|
|
49
|
+
Constrain,
|
|
48
50
|
ControlledPromise,
|
|
49
51
|
NoInfer,
|
|
50
52
|
NonNullableUpdater,
|
|
@@ -56,13 +58,10 @@ import type {
|
|
|
56
58
|
AnyContext,
|
|
57
59
|
AnyRoute,
|
|
58
60
|
AnyRouteWithContext,
|
|
59
|
-
BeforeLoadContextOptions,
|
|
60
|
-
LoaderFnContext,
|
|
61
61
|
MakeRemountDepsOptionsUnion,
|
|
62
62
|
RouteContextOptions,
|
|
63
63
|
RouteMask,
|
|
64
64
|
SearchMiddleware,
|
|
65
|
-
SsrContextOptions,
|
|
66
65
|
} from './route'
|
|
67
66
|
import type {
|
|
68
67
|
FullSearchSchema,
|
|
@@ -86,6 +85,11 @@ import type { Manifest } from './manifest'
|
|
|
86
85
|
import type { AnySchema, AnyValidator } from './validators'
|
|
87
86
|
import type { NavigateOptions, ResolveRelativePath, ToOptions } from './link'
|
|
88
87
|
import type { NotFoundError } from './not-found'
|
|
88
|
+
import type {
|
|
89
|
+
AnySerializationAdapter,
|
|
90
|
+
ValidateSerializableInput,
|
|
91
|
+
} from './ssr/serializer/transformer'
|
|
92
|
+
import type { AnyRouterConfig } from './config'
|
|
89
93
|
|
|
90
94
|
export type ControllablePromise<T = any> = Promise<T> & {
|
|
91
95
|
resolve: (value: T) => void
|
|
@@ -96,6 +100,8 @@ export type InjectedHtmlEntry = Promise<string>
|
|
|
96
100
|
|
|
97
101
|
export interface DefaultRegister {
|
|
98
102
|
router: AnyRouter
|
|
103
|
+
config: AnyRouterConfig
|
|
104
|
+
ssr: SSROption
|
|
99
105
|
}
|
|
100
106
|
|
|
101
107
|
export interface Register extends DefaultRegister {
|
|
@@ -113,12 +119,14 @@ export interface DefaultRouterOptionsExtensions {}
|
|
|
113
119
|
export interface RouterOptionsExtensions
|
|
114
120
|
extends DefaultRouterOptionsExtensions {}
|
|
115
121
|
|
|
122
|
+
export type SSROption = boolean | 'data-only'
|
|
123
|
+
|
|
116
124
|
export interface RouterOptions<
|
|
117
125
|
TRouteTree extends AnyRoute,
|
|
118
126
|
TTrailingSlashOption extends TrailingSlashOption,
|
|
119
127
|
TDefaultStructuralSharingOption extends boolean = false,
|
|
120
128
|
TRouterHistory extends RouterHistory = RouterHistory,
|
|
121
|
-
TDehydrated
|
|
129
|
+
TDehydrated = undefined,
|
|
122
130
|
> extends RouterOptionsExtensions {
|
|
123
131
|
/**
|
|
124
132
|
* The history object that will be used to manage the browser history.
|
|
@@ -286,7 +294,10 @@ export interface RouterOptions<
|
|
|
286
294
|
* @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RouterOptionsType#dehydrate-method)
|
|
287
295
|
* @link [Guide](https://tanstack.com/router/latest/docs/framework/react/guide/external-data-loading#critical-dehydrationhydration)
|
|
288
296
|
*/
|
|
289
|
-
dehydrate?: () =>
|
|
297
|
+
dehydrate?: () => Constrain<
|
|
298
|
+
TDehydrated,
|
|
299
|
+
ValidateSerializableInput<Register, TDehydrated>
|
|
300
|
+
>
|
|
290
301
|
/**
|
|
291
302
|
* A function that will be called when the router is hydrated.
|
|
292
303
|
*
|
|
@@ -356,7 +367,7 @@ export interface RouterOptions<
|
|
|
356
367
|
*
|
|
357
368
|
* @default true
|
|
358
369
|
*/
|
|
359
|
-
defaultSsr?:
|
|
370
|
+
defaultSsr?: SSROption
|
|
360
371
|
|
|
361
372
|
search?: {
|
|
362
373
|
/**
|
|
@@ -392,7 +403,9 @@ export interface RouterOptions<
|
|
|
392
403
|
*
|
|
393
404
|
* @default false
|
|
394
405
|
*/
|
|
395
|
-
scrollRestoration?:
|
|
406
|
+
scrollRestoration?:
|
|
407
|
+
| boolean
|
|
408
|
+
| ((opts: { location: ParsedLocation }) => boolean)
|
|
396
409
|
|
|
397
410
|
/**
|
|
398
411
|
* A function that will be called to get the key for the scroll restoration cache.
|
|
@@ -423,6 +436,8 @@ export interface RouterOptions<
|
|
|
423
436
|
* @default false
|
|
424
437
|
*/
|
|
425
438
|
disableGlobalCatchBoundary?: boolean
|
|
439
|
+
|
|
440
|
+
serializationAdapters?: ReadonlyArray<AnySerializationAdapter>
|
|
426
441
|
}
|
|
427
442
|
|
|
428
443
|
export interface RouterState<
|
|
@@ -614,8 +629,8 @@ export type InvalidateFn<TRouter extends AnyRouter> = (opts?: {
|
|
|
614
629
|
}) => Promise<void>
|
|
615
630
|
|
|
616
631
|
export type ParseLocationFn<TRouteTree extends AnyRoute> = (
|
|
632
|
+
locationToParse: HistoryLocation,
|
|
617
633
|
previousLocation?: ParsedLocation<FullSearchSchema<TRouteTree>>,
|
|
618
|
-
locationToParse?: HistoryLocation,
|
|
619
634
|
) => ParsedLocation<FullSearchSchema<TRouteTree>>
|
|
620
635
|
|
|
621
636
|
export type GetMatchRoutesFn = (
|
|
@@ -705,6 +720,7 @@ export interface ViewTransitionOptions {
|
|
|
705
720
|
}) => Array<string>)
|
|
706
721
|
}
|
|
707
722
|
|
|
723
|
+
// TODO where is this used? can we remove this?
|
|
708
724
|
export function defaultSerializeError(err: unknown) {
|
|
709
725
|
if (err instanceof Error) {
|
|
710
726
|
const obj = {
|
|
@@ -901,7 +917,7 @@ export class RouterCore<
|
|
|
901
917
|
initialEntries: [this.basepath || '/'],
|
|
902
918
|
})
|
|
903
919
|
: createBrowserHistory()) as TRouterHistory)
|
|
904
|
-
this.
|
|
920
|
+
this.updateLatestLocation()
|
|
905
921
|
}
|
|
906
922
|
|
|
907
923
|
if (this.options.routeTree !== this.routeTree) {
|
|
@@ -935,10 +951,17 @@ export class RouterCore<
|
|
|
935
951
|
}
|
|
936
952
|
}
|
|
937
953
|
|
|
938
|
-
get state() {
|
|
954
|
+
get state(): RouterState<TRouteTree> {
|
|
939
955
|
return this.__store.state
|
|
940
956
|
}
|
|
941
957
|
|
|
958
|
+
updateLatestLocation = () => {
|
|
959
|
+
this.latestLocation = this.parseLocation(
|
|
960
|
+
this.history.location,
|
|
961
|
+
this.latestLocation,
|
|
962
|
+
)
|
|
963
|
+
}
|
|
964
|
+
|
|
942
965
|
buildRouteTree = () => {
|
|
943
966
|
const { routesById, routesByPath, flatRoutes } = processRouteTree({
|
|
944
967
|
routeTree: this.routeTree,
|
|
@@ -985,8 +1008,8 @@ export class RouterCore<
|
|
|
985
1008
|
}
|
|
986
1009
|
|
|
987
1010
|
parseLocation: ParseLocationFn<TRouteTree> = (
|
|
988
|
-
previousLocation,
|
|
989
1011
|
locationToParse,
|
|
1012
|
+
previousLocation,
|
|
990
1013
|
) => {
|
|
991
1014
|
const parse = ({
|
|
992
1015
|
pathname,
|
|
@@ -1007,7 +1030,7 @@ export class RouterCore<
|
|
|
1007
1030
|
}
|
|
1008
1031
|
}
|
|
1009
1032
|
|
|
1010
|
-
const location = parse(locationToParse
|
|
1033
|
+
const location = parse(locationToParse)
|
|
1011
1034
|
|
|
1012
1035
|
const { __tempLocation, __tempKey } = location.state
|
|
1013
1036
|
|
|
@@ -1139,8 +1162,8 @@ export class RouterCore<
|
|
|
1139
1162
|
const parentMatchId = parentMatch?.id
|
|
1140
1163
|
|
|
1141
1164
|
const parentContext = !parentMatchId
|
|
1142
|
-
? ((this.options.context as any) ??
|
|
1143
|
-
: (parentMatch.context ?? this.options.context ??
|
|
1165
|
+
? ((this.options.context as any) ?? undefined)
|
|
1166
|
+
: (parentMatch.context ?? this.options.context ?? undefined)
|
|
1144
1167
|
|
|
1145
1168
|
return parentContext
|
|
1146
1169
|
}
|
|
@@ -1162,12 +1185,12 @@ export class RouterCore<
|
|
|
1162
1185
|
] = (() => {
|
|
1163
1186
|
// Validate the search params and stabilize them
|
|
1164
1187
|
const parentSearch = parentMatch?.search ?? next.search
|
|
1165
|
-
const parentStrictSearch = parentMatch?._strictSearch ??
|
|
1188
|
+
const parentStrictSearch = parentMatch?._strictSearch ?? undefined
|
|
1166
1189
|
|
|
1167
1190
|
try {
|
|
1168
1191
|
const strictSearch =
|
|
1169
1192
|
validateSearch(route.options.validateSearch, { ...parentSearch }) ??
|
|
1170
|
-
|
|
1193
|
+
undefined
|
|
1171
1194
|
|
|
1172
1195
|
return [
|
|
1173
1196
|
{
|
|
@@ -1277,7 +1300,10 @@ export class RouterCore<
|
|
|
1277
1300
|
isFetching: false,
|
|
1278
1301
|
error: undefined,
|
|
1279
1302
|
paramsError: parseErrors[index],
|
|
1280
|
-
__routeContext:
|
|
1303
|
+
__routeContext: undefined,
|
|
1304
|
+
_nonReactive: {
|
|
1305
|
+
loadPromise: createControlledPromise(),
|
|
1306
|
+
},
|
|
1281
1307
|
__beforeLoadContext: undefined,
|
|
1282
1308
|
context: {},
|
|
1283
1309
|
abortController: new AbortController(),
|
|
@@ -1293,7 +1319,6 @@ export class RouterCore<
|
|
|
1293
1319
|
headScripts: undefined,
|
|
1294
1320
|
meta: undefined,
|
|
1295
1321
|
staticData: route.options.staticData || {},
|
|
1296
|
-
loadPromise: createControlledPromise(),
|
|
1297
1322
|
fullPath: route.fullPath,
|
|
1298
1323
|
}
|
|
1299
1324
|
}
|
|
@@ -1328,22 +1353,25 @@ export class RouterCore<
|
|
|
1328
1353
|
const parentContext = getParentContext(parentMatch)
|
|
1329
1354
|
|
|
1330
1355
|
// Update the match's context
|
|
1331
|
-
const contextFnContext: RouteContextOptions<any, any, any, any> = {
|
|
1332
|
-
deps: match.loaderDeps,
|
|
1333
|
-
params: match.params,
|
|
1334
|
-
context: parentContext,
|
|
1335
|
-
location: next,
|
|
1336
|
-
navigate: (opts: any) =>
|
|
1337
|
-
this.navigate({ ...opts, _fromLocation: next }),
|
|
1338
|
-
buildLocation: this.buildLocation,
|
|
1339
|
-
cause: match.cause,
|
|
1340
|
-
abortController: match.abortController,
|
|
1341
|
-
preload: !!match.preload,
|
|
1342
|
-
matches,
|
|
1343
|
-
}
|
|
1344
1356
|
|
|
1345
|
-
|
|
1346
|
-
|
|
1357
|
+
if (route.options.context) {
|
|
1358
|
+
const contextFnContext: RouteContextOptions<any, any, any, any> = {
|
|
1359
|
+
deps: match.loaderDeps,
|
|
1360
|
+
params: match.params,
|
|
1361
|
+
context: parentContext ?? {},
|
|
1362
|
+
location: next,
|
|
1363
|
+
navigate: (opts: any) =>
|
|
1364
|
+
this.navigate({ ...opts, _fromLocation: next }),
|
|
1365
|
+
buildLocation: this.buildLocation,
|
|
1366
|
+
cause: match.cause,
|
|
1367
|
+
abortController: match.abortController,
|
|
1368
|
+
preload: !!match.preload,
|
|
1369
|
+
matches,
|
|
1370
|
+
}
|
|
1371
|
+
// Get the route context
|
|
1372
|
+
match.__routeContext =
|
|
1373
|
+
route.options.context(contextFnContext) ?? undefined
|
|
1374
|
+
}
|
|
1347
1375
|
|
|
1348
1376
|
match.context = {
|
|
1349
1377
|
...parentContext,
|
|
@@ -1381,13 +1409,8 @@ export class RouterCore<
|
|
|
1381
1409
|
if (!match) return
|
|
1382
1410
|
|
|
1383
1411
|
match.abortController.abort()
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
return {
|
|
1387
|
-
...prev,
|
|
1388
|
-
pendingTimeout: undefined,
|
|
1389
|
-
}
|
|
1390
|
-
})
|
|
1412
|
+
clearTimeout(match._nonReactive.pendingTimeout)
|
|
1413
|
+
match._nonReactive.pendingTimeout = undefined
|
|
1391
1414
|
}
|
|
1392
1415
|
|
|
1393
1416
|
cancelMatches = () => {
|
|
@@ -1409,106 +1432,94 @@ export class RouterCore<
|
|
|
1409
1432
|
_buildLocation: true,
|
|
1410
1433
|
})
|
|
1411
1434
|
|
|
1435
|
+
// Now let's find the starting pathname
|
|
1436
|
+
// This should default to the current location if no from is provided
|
|
1412
1437
|
const lastMatch = last(allCurrentLocationMatches)!
|
|
1413
1438
|
|
|
1414
|
-
//
|
|
1415
|
-
//
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
// If the route is changing we need to find the relative fromPath
|
|
1427
|
-
if (dest.unsafeRelative === 'path') {
|
|
1428
|
-
fromPath = currentLocation.pathname
|
|
1429
|
-
} else if (routeIsChanging && dest.from) {
|
|
1430
|
-
fromPath = dest.from
|
|
1431
|
-
|
|
1432
|
-
// do this check only on navigations during test or development
|
|
1433
|
-
if (process.env.NODE_ENV !== 'production' && dest._isNavigate) {
|
|
1434
|
-
const allFromMatches = this.getMatchedRoutes(
|
|
1435
|
-
dest.from,
|
|
1436
|
-
undefined,
|
|
1437
|
-
).matchedRoutes
|
|
1439
|
+
// check that from path exists in the current route tree
|
|
1440
|
+
// do this check only on navigations during test or development
|
|
1441
|
+
if (
|
|
1442
|
+
dest.from &&
|
|
1443
|
+
process.env.NODE_ENV !== 'production' &&
|
|
1444
|
+
dest._isNavigate
|
|
1445
|
+
) {
|
|
1446
|
+
const allFromMatches = this.getMatchedRoutes(
|
|
1447
|
+
dest.from,
|
|
1448
|
+
undefined,
|
|
1449
|
+
).matchedRoutes
|
|
1438
1450
|
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
return comparePaths(d.fullPath, fromPath)
|
|
1443
|
-
})
|
|
1451
|
+
const matchedFrom = findLast(allCurrentLocationMatches, (d) => {
|
|
1452
|
+
return comparePaths(d.fullPath, dest.from!)
|
|
1453
|
+
})
|
|
1444
1454
|
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1455
|
+
const matchedCurrent = findLast(allFromMatches, (d) => {
|
|
1456
|
+
return comparePaths(d.fullPath, lastMatch.fullPath)
|
|
1457
|
+
})
|
|
1448
1458
|
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
}
|
|
1459
|
+
// for from to be invalid it shouldn't just be unmatched to currentLocation
|
|
1460
|
+
// but the currentLocation should also be unmatched to from
|
|
1461
|
+
if (!matchedFrom && !matchedCurrent) {
|
|
1462
|
+
console.warn(`Could not find match for from: ${dest.from}`)
|
|
1454
1463
|
}
|
|
1455
1464
|
}
|
|
1456
1465
|
|
|
1466
|
+
const defaultedFromPath =
|
|
1467
|
+
dest.unsafeRelative === 'path'
|
|
1468
|
+
? currentLocation.pathname
|
|
1469
|
+
: (dest.from ?? lastMatch.fullPath)
|
|
1470
|
+
|
|
1471
|
+
// ensure this includes the basePath if set
|
|
1472
|
+
const fromPath = this.resolvePathWithBase(defaultedFromPath, '.')
|
|
1473
|
+
|
|
1457
1474
|
// From search should always use the current location
|
|
1458
1475
|
const fromSearch = lastMatch.search
|
|
1459
1476
|
// Same with params. It can't hurt to provide as many as possible
|
|
1460
1477
|
const fromParams = { ...lastMatch.params }
|
|
1461
1478
|
|
|
1462
1479
|
// Resolve the next to
|
|
1480
|
+
// ensure this includes the basePath if set
|
|
1463
1481
|
const nextTo = dest.to
|
|
1464
1482
|
? this.resolvePathWithBase(fromPath, `${dest.to}`)
|
|
1465
1483
|
: this.resolvePathWithBase(fromPath, '.')
|
|
1466
1484
|
|
|
1467
1485
|
// Resolve the next params
|
|
1468
|
-
|
|
1486
|
+
const nextParams =
|
|
1469
1487
|
dest.params === false || dest.params === null
|
|
1470
1488
|
? {}
|
|
1471
1489
|
: (dest.params ?? true) === true
|
|
1472
1490
|
? fromParams
|
|
1473
|
-
:
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1491
|
+
: Object.assign(
|
|
1492
|
+
fromParams,
|
|
1493
|
+
functionalUpdate(dest.params as any, fromParams),
|
|
1494
|
+
)
|
|
1477
1495
|
|
|
1478
1496
|
// Interpolate the path first to get the actual resolved path, then match against that
|
|
1479
1497
|
const interpolatedNextTo = interpolatePath({
|
|
1480
1498
|
path: nextTo,
|
|
1481
|
-
params: nextParams
|
|
1499
|
+
params: nextParams,
|
|
1482
1500
|
parseCache: this.parsePathnameCache,
|
|
1483
1501
|
}).interpolatedPath
|
|
1484
1502
|
|
|
1485
|
-
const destRoutes = this.matchRoutes(
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
{
|
|
1489
|
-
_buildLocation: true,
|
|
1490
|
-
},
|
|
1491
|
-
).map((d) => this.looseRoutesById[d.routeId]!)
|
|
1503
|
+
const destRoutes = this.matchRoutes(interpolatedNextTo, undefined, {
|
|
1504
|
+
_buildLocation: true,
|
|
1505
|
+
}).map((d) => this.looseRoutesById[d.routeId]!)
|
|
1492
1506
|
|
|
1493
1507
|
// If there are any params, we need to stringify them
|
|
1494
1508
|
if (Object.keys(nextParams).length > 0) {
|
|
1495
|
-
destRoutes
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
)
|
|
1500
|
-
}
|
|
1501
|
-
|
|
1502
|
-
.forEach((fn) => {
|
|
1503
|
-
nextParams = { ...nextParams!, ...fn!(nextParams) }
|
|
1504
|
-
})
|
|
1509
|
+
for (const route of destRoutes) {
|
|
1510
|
+
const fn =
|
|
1511
|
+
route.options.params?.stringify ?? route.options.stringifyParams
|
|
1512
|
+
if (fn) {
|
|
1513
|
+
Object.assign(nextParams, fn(nextParams))
|
|
1514
|
+
}
|
|
1515
|
+
}
|
|
1505
1516
|
}
|
|
1506
1517
|
|
|
1507
1518
|
const nextPathname = interpolatePath({
|
|
1508
1519
|
// Use the original template path for interpolation
|
|
1509
1520
|
// This preserves the original parameter syntax including optional parameters
|
|
1510
1521
|
path: nextTo,
|
|
1511
|
-
params: nextParams
|
|
1522
|
+
params: nextParams,
|
|
1512
1523
|
leaveWildcards: false,
|
|
1513
1524
|
leaveParams: opts.leaveParams,
|
|
1514
1525
|
decodeCharMap: this.pathParamsDecodeCharMap,
|
|
@@ -1518,20 +1529,20 @@ export class RouterCore<
|
|
|
1518
1529
|
// Resolve the next search
|
|
1519
1530
|
let nextSearch = fromSearch
|
|
1520
1531
|
if (opts._includeValidateSearch && this.options.search?.strict) {
|
|
1521
|
-
|
|
1532
|
+
const validatedSearch = {}
|
|
1522
1533
|
destRoutes.forEach((route) => {
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1534
|
+
if (route.options.validateSearch) {
|
|
1535
|
+
try {
|
|
1536
|
+
Object.assign(
|
|
1537
|
+
validatedSearch,
|
|
1538
|
+
validateSearch(route.options.validateSearch, {
|
|
1528
1539
|
...validatedSearch,
|
|
1529
1540
|
...nextSearch,
|
|
1530
|
-
})
|
|
1531
|
-
|
|
1541
|
+
}),
|
|
1542
|
+
)
|
|
1543
|
+
} catch {
|
|
1544
|
+
// ignore errors here because they are already handled in matchRoutes
|
|
1532
1545
|
}
|
|
1533
|
-
} catch {
|
|
1534
|
-
// ignore errors here because they are already handled in matchRoutes
|
|
1535
1546
|
}
|
|
1536
1547
|
})
|
|
1537
1548
|
nextSearch = validatedSearch
|
|
@@ -1618,7 +1629,7 @@ export class RouterCore<
|
|
|
1618
1629
|
if (foundMask) {
|
|
1619
1630
|
const { from: _from, ...maskProps } = foundMask
|
|
1620
1631
|
maskedDest = {
|
|
1621
|
-
|
|
1632
|
+
from: opts.from,
|
|
1622
1633
|
...maskProps,
|
|
1623
1634
|
params,
|
|
1624
1635
|
}
|
|
@@ -1636,7 +1647,7 @@ export class RouterCore<
|
|
|
1636
1647
|
|
|
1637
1648
|
if (opts.mask) {
|
|
1638
1649
|
return buildWithMatches(opts, {
|
|
1639
|
-
|
|
1650
|
+
from: opts.from,
|
|
1640
1651
|
...opts.mask,
|
|
1641
1652
|
})
|
|
1642
1653
|
}
|
|
@@ -1805,7 +1816,7 @@ export class RouterCore<
|
|
|
1805
1816
|
beforeLoad = () => {
|
|
1806
1817
|
// Cancel any pending matches
|
|
1807
1818
|
this.cancelMatches()
|
|
1808
|
-
this.
|
|
1819
|
+
this.updateLatestLocation()
|
|
1809
1820
|
|
|
1810
1821
|
if (this.isServer) {
|
|
1811
1822
|
// for SPAs on the initial load, this is handled by the Transitioner
|
|
@@ -1884,10 +1895,12 @@ export class RouterCore<
|
|
|
1884
1895
|
}),
|
|
1885
1896
|
})
|
|
1886
1897
|
|
|
1887
|
-
await
|
|
1898
|
+
await loadMatches({
|
|
1899
|
+
router: this,
|
|
1888
1900
|
sync: opts?.sync,
|
|
1889
1901
|
matches: this.state.pendingMatches as Array<AnyRouteMatch>,
|
|
1890
1902
|
location: next,
|
|
1903
|
+
updateMatch: this.updateMatch,
|
|
1891
1904
|
// eslint-disable-next-line @typescript-eslint/require-await
|
|
1892
1905
|
onReady: async () => {
|
|
1893
1906
|
// eslint-disable-next-line @typescript-eslint/require-await
|
|
@@ -2077,704 +2090,6 @@ export class RouterCore<
|
|
|
2077
2090
|
)
|
|
2078
2091
|
}
|
|
2079
2092
|
|
|
2080
|
-
loadMatches = async ({
|
|
2081
|
-
location,
|
|
2082
|
-
matches,
|
|
2083
|
-
preload: allPreload,
|
|
2084
|
-
onReady,
|
|
2085
|
-
updateMatch = this.updateMatch,
|
|
2086
|
-
sync,
|
|
2087
|
-
}: {
|
|
2088
|
-
location: ParsedLocation
|
|
2089
|
-
matches: Array<AnyRouteMatch>
|
|
2090
|
-
preload?: boolean
|
|
2091
|
-
onReady?: () => Promise<void>
|
|
2092
|
-
updateMatch?: (
|
|
2093
|
-
id: string,
|
|
2094
|
-
updater: (match: AnyRouteMatch) => AnyRouteMatch,
|
|
2095
|
-
) => void
|
|
2096
|
-
getMatch?: (matchId: string) => AnyRouteMatch | undefined
|
|
2097
|
-
sync?: boolean
|
|
2098
|
-
}): Promise<Array<MakeRouteMatch>> => {
|
|
2099
|
-
let firstBadMatchIndex: number | undefined
|
|
2100
|
-
let rendered = false
|
|
2101
|
-
|
|
2102
|
-
const triggerOnReady = async () => {
|
|
2103
|
-
if (!rendered) {
|
|
2104
|
-
rendered = true
|
|
2105
|
-
await onReady?.()
|
|
2106
|
-
}
|
|
2107
|
-
}
|
|
2108
|
-
|
|
2109
|
-
const resolvePreload = (matchId: string) => {
|
|
2110
|
-
return !!(allPreload && !this.state.matches.some((d) => d.id === matchId))
|
|
2111
|
-
}
|
|
2112
|
-
|
|
2113
|
-
// make sure the pending component is immediately rendered when hydrating a match that is not SSRed
|
|
2114
|
-
// the pending component was already rendered on the server and we want to keep it shown on the client until minPendingMs is reached
|
|
2115
|
-
if (!this.isServer && this.state.matches.some((d) => d._forcePending)) {
|
|
2116
|
-
triggerOnReady()
|
|
2117
|
-
}
|
|
2118
|
-
|
|
2119
|
-
const handleRedirectAndNotFound = (match: AnyRouteMatch, err: any) => {
|
|
2120
|
-
if (isRedirect(err) || isNotFound(err)) {
|
|
2121
|
-
if (isRedirect(err)) {
|
|
2122
|
-
if (err.redirectHandled) {
|
|
2123
|
-
if (!err.options.reloadDocument) {
|
|
2124
|
-
throw err
|
|
2125
|
-
}
|
|
2126
|
-
}
|
|
2127
|
-
}
|
|
2128
|
-
|
|
2129
|
-
match.beforeLoadPromise?.resolve()
|
|
2130
|
-
match.loaderPromise?.resolve()
|
|
2131
|
-
|
|
2132
|
-
updateMatch(match.id, (prev) => ({
|
|
2133
|
-
...prev,
|
|
2134
|
-
status: isRedirect(err)
|
|
2135
|
-
? 'redirected'
|
|
2136
|
-
: isNotFound(err)
|
|
2137
|
-
? 'notFound'
|
|
2138
|
-
: 'error',
|
|
2139
|
-
isFetching: false,
|
|
2140
|
-
error: err,
|
|
2141
|
-
beforeLoadPromise: undefined,
|
|
2142
|
-
loaderPromise: undefined,
|
|
2143
|
-
}))
|
|
2144
|
-
|
|
2145
|
-
if (!(err as any).routeId) {
|
|
2146
|
-
;(err as any).routeId = match.routeId
|
|
2147
|
-
}
|
|
2148
|
-
|
|
2149
|
-
match.loadPromise?.resolve()
|
|
2150
|
-
|
|
2151
|
-
if (isRedirect(err)) {
|
|
2152
|
-
rendered = true
|
|
2153
|
-
err.options._fromLocation = location
|
|
2154
|
-
err.redirectHandled = true
|
|
2155
|
-
err = this.resolveRedirect(err)
|
|
2156
|
-
throw err
|
|
2157
|
-
} else if (isNotFound(err)) {
|
|
2158
|
-
this._handleNotFound(matches, err, {
|
|
2159
|
-
updateMatch,
|
|
2160
|
-
})
|
|
2161
|
-
throw err
|
|
2162
|
-
}
|
|
2163
|
-
}
|
|
2164
|
-
}
|
|
2165
|
-
|
|
2166
|
-
const shouldSkipLoader = (matchId: string) => {
|
|
2167
|
-
const match = this.getMatch(matchId)!
|
|
2168
|
-
// upon hydration, we skip the loader if the match has been dehydrated on the server
|
|
2169
|
-
if (!this.isServer && match._dehydrated) {
|
|
2170
|
-
return true
|
|
2171
|
-
}
|
|
2172
|
-
|
|
2173
|
-
if (this.isServer) {
|
|
2174
|
-
if (match.ssr === false) {
|
|
2175
|
-
return true
|
|
2176
|
-
}
|
|
2177
|
-
}
|
|
2178
|
-
return false
|
|
2179
|
-
}
|
|
2180
|
-
|
|
2181
|
-
try {
|
|
2182
|
-
await new Promise<void>((resolveAll, rejectAll) => {
|
|
2183
|
-
;(async () => {
|
|
2184
|
-
try {
|
|
2185
|
-
const handleSerialError = (
|
|
2186
|
-
index: number,
|
|
2187
|
-
err: any,
|
|
2188
|
-
routerCode: string,
|
|
2189
|
-
) => {
|
|
2190
|
-
const { id: matchId, routeId } = matches[index]!
|
|
2191
|
-
const route = this.looseRoutesById[routeId]!
|
|
2192
|
-
|
|
2193
|
-
// Much like suspense, we use a promise here to know if
|
|
2194
|
-
// we've been outdated by a new loadMatches call and
|
|
2195
|
-
// should abort the current async operation
|
|
2196
|
-
if (err instanceof Promise) {
|
|
2197
|
-
throw err
|
|
2198
|
-
}
|
|
2199
|
-
|
|
2200
|
-
err.routerCode = routerCode
|
|
2201
|
-
firstBadMatchIndex = firstBadMatchIndex ?? index
|
|
2202
|
-
handleRedirectAndNotFound(this.getMatch(matchId)!, err)
|
|
2203
|
-
|
|
2204
|
-
try {
|
|
2205
|
-
route.options.onError?.(err)
|
|
2206
|
-
} catch (errorHandlerErr) {
|
|
2207
|
-
err = errorHandlerErr
|
|
2208
|
-
handleRedirectAndNotFound(this.getMatch(matchId)!, err)
|
|
2209
|
-
}
|
|
2210
|
-
|
|
2211
|
-
updateMatch(matchId, (prev) => {
|
|
2212
|
-
prev.beforeLoadPromise?.resolve()
|
|
2213
|
-
prev.loadPromise?.resolve()
|
|
2214
|
-
|
|
2215
|
-
return {
|
|
2216
|
-
...prev,
|
|
2217
|
-
error: err,
|
|
2218
|
-
status: 'error',
|
|
2219
|
-
isFetching: false,
|
|
2220
|
-
updatedAt: Date.now(),
|
|
2221
|
-
abortController: new AbortController(),
|
|
2222
|
-
beforeLoadPromise: undefined,
|
|
2223
|
-
}
|
|
2224
|
-
})
|
|
2225
|
-
}
|
|
2226
|
-
|
|
2227
|
-
for (const [index, { id: matchId, routeId }] of matches.entries()) {
|
|
2228
|
-
const existingMatch = this.getMatch(matchId)!
|
|
2229
|
-
const parentMatchId = matches[index - 1]?.id
|
|
2230
|
-
const parentMatch = parentMatchId
|
|
2231
|
-
? this.getMatch(parentMatchId)!
|
|
2232
|
-
: undefined
|
|
2233
|
-
|
|
2234
|
-
const route = this.looseRoutesById[routeId]!
|
|
2235
|
-
|
|
2236
|
-
const pendingMs =
|
|
2237
|
-
route.options.pendingMs ?? this.options.defaultPendingMs
|
|
2238
|
-
|
|
2239
|
-
// on the server, determine whether SSR the current match or not
|
|
2240
|
-
if (this.isServer) {
|
|
2241
|
-
let ssr: boolean | 'data-only'
|
|
2242
|
-
// in SPA mode, only SSR the root route
|
|
2243
|
-
if (this.isShell()) {
|
|
2244
|
-
ssr = matchId === rootRouteId
|
|
2245
|
-
} else {
|
|
2246
|
-
const defaultSsr = this.options.defaultSsr ?? true
|
|
2247
|
-
if (parentMatch?.ssr === false) {
|
|
2248
|
-
ssr = false
|
|
2249
|
-
} else {
|
|
2250
|
-
let tempSsr: boolean | 'data-only'
|
|
2251
|
-
if (route.options.ssr === undefined) {
|
|
2252
|
-
tempSsr = defaultSsr
|
|
2253
|
-
} else if (typeof route.options.ssr === 'function') {
|
|
2254
|
-
const { search, params } = this.getMatch(matchId)!
|
|
2255
|
-
|
|
2256
|
-
function makeMaybe(value: any, error: any) {
|
|
2257
|
-
if (error) {
|
|
2258
|
-
return { status: 'error' as const, error }
|
|
2259
|
-
}
|
|
2260
|
-
return { status: 'success' as const, value }
|
|
2261
|
-
}
|
|
2262
|
-
|
|
2263
|
-
const ssrFnContext: SsrContextOptions<any, any, any> = {
|
|
2264
|
-
search: makeMaybe(search, existingMatch.searchError),
|
|
2265
|
-
params: makeMaybe(params, existingMatch.paramsError),
|
|
2266
|
-
location,
|
|
2267
|
-
matches: matches.map((match) => ({
|
|
2268
|
-
index: match.index,
|
|
2269
|
-
pathname: match.pathname,
|
|
2270
|
-
fullPath: match.fullPath,
|
|
2271
|
-
staticData: match.staticData,
|
|
2272
|
-
id: match.id,
|
|
2273
|
-
routeId: match.routeId,
|
|
2274
|
-
search: makeMaybe(match.search, match.searchError),
|
|
2275
|
-
params: makeMaybe(match.params, match.paramsError),
|
|
2276
|
-
ssr: match.ssr,
|
|
2277
|
-
})),
|
|
2278
|
-
}
|
|
2279
|
-
tempSsr =
|
|
2280
|
-
(await route.options.ssr(ssrFnContext)) ?? defaultSsr
|
|
2281
|
-
} else {
|
|
2282
|
-
tempSsr = route.options.ssr
|
|
2283
|
-
}
|
|
2284
|
-
|
|
2285
|
-
if (tempSsr === true && parentMatch?.ssr === 'data-only') {
|
|
2286
|
-
ssr = 'data-only'
|
|
2287
|
-
} else {
|
|
2288
|
-
ssr = tempSsr
|
|
2289
|
-
}
|
|
2290
|
-
}
|
|
2291
|
-
}
|
|
2292
|
-
updateMatch(matchId, (prev) => ({
|
|
2293
|
-
...prev,
|
|
2294
|
-
ssr,
|
|
2295
|
-
}))
|
|
2296
|
-
}
|
|
2297
|
-
|
|
2298
|
-
if (shouldSkipLoader(matchId)) {
|
|
2299
|
-
continue
|
|
2300
|
-
}
|
|
2301
|
-
|
|
2302
|
-
const shouldPending = !!(
|
|
2303
|
-
onReady &&
|
|
2304
|
-
!this.isServer &&
|
|
2305
|
-
!resolvePreload(matchId) &&
|
|
2306
|
-
(route.options.loader ||
|
|
2307
|
-
route.options.beforeLoad ||
|
|
2308
|
-
routeNeedsPreload(route)) &&
|
|
2309
|
-
typeof pendingMs === 'number' &&
|
|
2310
|
-
pendingMs !== Infinity &&
|
|
2311
|
-
(route.options.pendingComponent ??
|
|
2312
|
-
(this.options as any)?.defaultPendingComponent)
|
|
2313
|
-
)
|
|
2314
|
-
|
|
2315
|
-
let executeBeforeLoad = true
|
|
2316
|
-
const setupPendingTimeout = () => {
|
|
2317
|
-
if (
|
|
2318
|
-
shouldPending &&
|
|
2319
|
-
this.getMatch(matchId)!.pendingTimeout === undefined
|
|
2320
|
-
) {
|
|
2321
|
-
const pendingTimeout = setTimeout(() => {
|
|
2322
|
-
try {
|
|
2323
|
-
// Update the match and prematurely resolve the loadMatches promise so that
|
|
2324
|
-
// the pending component can start rendering
|
|
2325
|
-
triggerOnReady()
|
|
2326
|
-
} catch {}
|
|
2327
|
-
}, pendingMs)
|
|
2328
|
-
updateMatch(matchId, (prev) => ({
|
|
2329
|
-
...prev,
|
|
2330
|
-
pendingTimeout,
|
|
2331
|
-
}))
|
|
2332
|
-
}
|
|
2333
|
-
}
|
|
2334
|
-
if (
|
|
2335
|
-
// If we are in the middle of a load, either of these will be present
|
|
2336
|
-
// (not to be confused with `loadPromise`, which is always defined)
|
|
2337
|
-
existingMatch.beforeLoadPromise ||
|
|
2338
|
-
existingMatch.loaderPromise
|
|
2339
|
-
) {
|
|
2340
|
-
setupPendingTimeout()
|
|
2341
|
-
|
|
2342
|
-
// Wait for the beforeLoad to resolve before we continue
|
|
2343
|
-
await existingMatch.beforeLoadPromise
|
|
2344
|
-
const match = this.getMatch(matchId)!
|
|
2345
|
-
if (match.status === 'error') {
|
|
2346
|
-
executeBeforeLoad = true
|
|
2347
|
-
} else if (
|
|
2348
|
-
match.preload &&
|
|
2349
|
-
(match.status === 'redirected' || match.status === 'notFound')
|
|
2350
|
-
) {
|
|
2351
|
-
handleRedirectAndNotFound(match, match.error)
|
|
2352
|
-
}
|
|
2353
|
-
}
|
|
2354
|
-
if (executeBeforeLoad) {
|
|
2355
|
-
// If we are not in the middle of a load OR the previous load failed, start it
|
|
2356
|
-
try {
|
|
2357
|
-
updateMatch(matchId, (prev) => {
|
|
2358
|
-
// explicitly capture the previous loadPromise
|
|
2359
|
-
const prevLoadPromise = prev.loadPromise
|
|
2360
|
-
return {
|
|
2361
|
-
...prev,
|
|
2362
|
-
loadPromise: createControlledPromise<void>(() => {
|
|
2363
|
-
prevLoadPromise?.resolve()
|
|
2364
|
-
}),
|
|
2365
|
-
beforeLoadPromise: createControlledPromise<void>(),
|
|
2366
|
-
}
|
|
2367
|
-
})
|
|
2368
|
-
|
|
2369
|
-
const { paramsError, searchError } = this.getMatch(matchId)!
|
|
2370
|
-
|
|
2371
|
-
if (paramsError) {
|
|
2372
|
-
handleSerialError(index, paramsError, 'PARSE_PARAMS')
|
|
2373
|
-
}
|
|
2374
|
-
|
|
2375
|
-
if (searchError) {
|
|
2376
|
-
handleSerialError(index, searchError, 'VALIDATE_SEARCH')
|
|
2377
|
-
}
|
|
2378
|
-
|
|
2379
|
-
setupPendingTimeout()
|
|
2380
|
-
|
|
2381
|
-
const abortController = new AbortController()
|
|
2382
|
-
|
|
2383
|
-
const parentMatchContext =
|
|
2384
|
-
parentMatch?.context ?? this.options.context ?? {}
|
|
2385
|
-
|
|
2386
|
-
updateMatch(matchId, (prev) => ({
|
|
2387
|
-
...prev,
|
|
2388
|
-
isFetching: 'beforeLoad',
|
|
2389
|
-
fetchCount: prev.fetchCount + 1,
|
|
2390
|
-
abortController,
|
|
2391
|
-
context: {
|
|
2392
|
-
...parentMatchContext,
|
|
2393
|
-
...prev.__routeContext,
|
|
2394
|
-
},
|
|
2395
|
-
}))
|
|
2396
|
-
|
|
2397
|
-
const { search, params, context, cause } =
|
|
2398
|
-
this.getMatch(matchId)!
|
|
2399
|
-
|
|
2400
|
-
const preload = resolvePreload(matchId)
|
|
2401
|
-
|
|
2402
|
-
const beforeLoadFnContext: BeforeLoadContextOptions<
|
|
2403
|
-
any,
|
|
2404
|
-
any,
|
|
2405
|
-
any,
|
|
2406
|
-
any,
|
|
2407
|
-
any
|
|
2408
|
-
> = {
|
|
2409
|
-
search,
|
|
2410
|
-
abortController,
|
|
2411
|
-
params,
|
|
2412
|
-
preload,
|
|
2413
|
-
context,
|
|
2414
|
-
location,
|
|
2415
|
-
navigate: (opts: any) =>
|
|
2416
|
-
this.navigate({ ...opts, _fromLocation: location }),
|
|
2417
|
-
buildLocation: this.buildLocation,
|
|
2418
|
-
cause: preload ? 'preload' : cause,
|
|
2419
|
-
matches,
|
|
2420
|
-
}
|
|
2421
|
-
|
|
2422
|
-
const beforeLoadContext =
|
|
2423
|
-
await route.options.beforeLoad?.(beforeLoadFnContext)
|
|
2424
|
-
|
|
2425
|
-
if (
|
|
2426
|
-
isRedirect(beforeLoadContext) ||
|
|
2427
|
-
isNotFound(beforeLoadContext)
|
|
2428
|
-
) {
|
|
2429
|
-
handleSerialError(index, beforeLoadContext, 'BEFORE_LOAD')
|
|
2430
|
-
}
|
|
2431
|
-
|
|
2432
|
-
updateMatch(matchId, (prev) => {
|
|
2433
|
-
return {
|
|
2434
|
-
...prev,
|
|
2435
|
-
__beforeLoadContext: beforeLoadContext,
|
|
2436
|
-
context: {
|
|
2437
|
-
...parentMatchContext,
|
|
2438
|
-
...prev.__routeContext,
|
|
2439
|
-
...beforeLoadContext,
|
|
2440
|
-
},
|
|
2441
|
-
abortController,
|
|
2442
|
-
}
|
|
2443
|
-
})
|
|
2444
|
-
} catch (err) {
|
|
2445
|
-
handleSerialError(index, err, 'BEFORE_LOAD')
|
|
2446
|
-
}
|
|
2447
|
-
|
|
2448
|
-
updateMatch(matchId, (prev) => {
|
|
2449
|
-
prev.beforeLoadPromise?.resolve()
|
|
2450
|
-
|
|
2451
|
-
return {
|
|
2452
|
-
...prev,
|
|
2453
|
-
beforeLoadPromise: undefined,
|
|
2454
|
-
isFetching: false,
|
|
2455
|
-
}
|
|
2456
|
-
})
|
|
2457
|
-
}
|
|
2458
|
-
}
|
|
2459
|
-
|
|
2460
|
-
const validResolvedMatches = matches.slice(0, firstBadMatchIndex)
|
|
2461
|
-
const matchPromises: Array<Promise<AnyRouteMatch>> = []
|
|
2462
|
-
|
|
2463
|
-
validResolvedMatches.forEach(({ id: matchId, routeId }, index) => {
|
|
2464
|
-
matchPromises.push(
|
|
2465
|
-
(async () => {
|
|
2466
|
-
let loaderShouldRunAsync = false
|
|
2467
|
-
let loaderIsRunningAsync = false
|
|
2468
|
-
const route = this.looseRoutesById[routeId]!
|
|
2469
|
-
|
|
2470
|
-
const executeHead = async () => {
|
|
2471
|
-
const match = this.getMatch(matchId)
|
|
2472
|
-
// in case of a redirecting match during preload, the match does not exist
|
|
2473
|
-
if (!match) {
|
|
2474
|
-
return
|
|
2475
|
-
}
|
|
2476
|
-
const assetContext = {
|
|
2477
|
-
matches,
|
|
2478
|
-
match,
|
|
2479
|
-
params: match.params,
|
|
2480
|
-
loaderData: match.loaderData,
|
|
2481
|
-
}
|
|
2482
|
-
const headFnContent =
|
|
2483
|
-
await route.options.head?.(assetContext)
|
|
2484
|
-
const meta = headFnContent?.meta
|
|
2485
|
-
const links = headFnContent?.links
|
|
2486
|
-
const headScripts = headFnContent?.scripts
|
|
2487
|
-
const styles = headFnContent?.styles
|
|
2488
|
-
|
|
2489
|
-
const scripts = await route.options.scripts?.(assetContext)
|
|
2490
|
-
const headers = await route.options.headers?.(assetContext)
|
|
2491
|
-
return {
|
|
2492
|
-
meta,
|
|
2493
|
-
links,
|
|
2494
|
-
headScripts,
|
|
2495
|
-
headers,
|
|
2496
|
-
scripts,
|
|
2497
|
-
styles,
|
|
2498
|
-
}
|
|
2499
|
-
}
|
|
2500
|
-
|
|
2501
|
-
const potentialPendingMinPromise = async () => {
|
|
2502
|
-
const latestMatch = this.getMatch(matchId)!
|
|
2503
|
-
if (latestMatch.minPendingPromise) {
|
|
2504
|
-
await latestMatch.minPendingPromise
|
|
2505
|
-
}
|
|
2506
|
-
}
|
|
2507
|
-
|
|
2508
|
-
const prevMatch = this.getMatch(matchId)!
|
|
2509
|
-
if (shouldSkipLoader(matchId)) {
|
|
2510
|
-
if (this.isServer) {
|
|
2511
|
-
const head = await executeHead()
|
|
2512
|
-
updateMatch(matchId, (prev) => ({
|
|
2513
|
-
...prev,
|
|
2514
|
-
...head,
|
|
2515
|
-
}))
|
|
2516
|
-
return this.getMatch(matchId)!
|
|
2517
|
-
}
|
|
2518
|
-
}
|
|
2519
|
-
// there is a loaderPromise, so we are in the middle of a load
|
|
2520
|
-
else if (prevMatch.loaderPromise) {
|
|
2521
|
-
// do not block if we already have stale data we can show
|
|
2522
|
-
// but only if the ongoing load is not a preload since error handling is different for preloads
|
|
2523
|
-
// and we don't want to swallow errors
|
|
2524
|
-
if (
|
|
2525
|
-
prevMatch.status === 'success' &&
|
|
2526
|
-
!sync &&
|
|
2527
|
-
!prevMatch.preload
|
|
2528
|
-
) {
|
|
2529
|
-
return this.getMatch(matchId)!
|
|
2530
|
-
}
|
|
2531
|
-
await prevMatch.loaderPromise
|
|
2532
|
-
const match = this.getMatch(matchId)!
|
|
2533
|
-
if (match.error) {
|
|
2534
|
-
handleRedirectAndNotFound(match, match.error)
|
|
2535
|
-
}
|
|
2536
|
-
} else {
|
|
2537
|
-
const parentMatchPromise = matchPromises[index - 1] as any
|
|
2538
|
-
|
|
2539
|
-
const getLoaderContext = (): LoaderFnContext => {
|
|
2540
|
-
const {
|
|
2541
|
-
params,
|
|
2542
|
-
loaderDeps,
|
|
2543
|
-
abortController,
|
|
2544
|
-
context,
|
|
2545
|
-
cause,
|
|
2546
|
-
} = this.getMatch(matchId)!
|
|
2547
|
-
|
|
2548
|
-
const preload = resolvePreload(matchId)
|
|
2549
|
-
|
|
2550
|
-
return {
|
|
2551
|
-
params,
|
|
2552
|
-
deps: loaderDeps,
|
|
2553
|
-
preload: !!preload,
|
|
2554
|
-
parentMatchPromise,
|
|
2555
|
-
abortController: abortController,
|
|
2556
|
-
context,
|
|
2557
|
-
location,
|
|
2558
|
-
navigate: (opts) =>
|
|
2559
|
-
this.navigate({ ...opts, _fromLocation: location }),
|
|
2560
|
-
cause: preload ? 'preload' : cause,
|
|
2561
|
-
route,
|
|
2562
|
-
}
|
|
2563
|
-
}
|
|
2564
|
-
|
|
2565
|
-
// This is where all of the stale-while-revalidate magic happens
|
|
2566
|
-
const age = Date.now() - this.getMatch(matchId)!.updatedAt
|
|
2567
|
-
|
|
2568
|
-
const preload = resolvePreload(matchId)
|
|
2569
|
-
|
|
2570
|
-
const staleAge = preload
|
|
2571
|
-
? (route.options.preloadStaleTime ??
|
|
2572
|
-
this.options.defaultPreloadStaleTime ??
|
|
2573
|
-
30_000) // 30 seconds for preloads by default
|
|
2574
|
-
: (route.options.staleTime ??
|
|
2575
|
-
this.options.defaultStaleTime ??
|
|
2576
|
-
0)
|
|
2577
|
-
|
|
2578
|
-
const shouldReloadOption = route.options.shouldReload
|
|
2579
|
-
|
|
2580
|
-
// Default to reloading the route all the time
|
|
2581
|
-
// Allow shouldReload to get the last say,
|
|
2582
|
-
// if provided.
|
|
2583
|
-
const shouldReload =
|
|
2584
|
-
typeof shouldReloadOption === 'function'
|
|
2585
|
-
? shouldReloadOption(getLoaderContext())
|
|
2586
|
-
: shouldReloadOption
|
|
2587
|
-
|
|
2588
|
-
updateMatch(matchId, (prev) => ({
|
|
2589
|
-
...prev,
|
|
2590
|
-
loaderPromise: createControlledPromise<void>(),
|
|
2591
|
-
preload:
|
|
2592
|
-
!!preload &&
|
|
2593
|
-
!this.state.matches.some((d) => d.id === matchId),
|
|
2594
|
-
}))
|
|
2595
|
-
|
|
2596
|
-
const runLoader = async () => {
|
|
2597
|
-
try {
|
|
2598
|
-
// If the Matches component rendered
|
|
2599
|
-
// the pending component and needs to show it for
|
|
2600
|
-
// a minimum duration, we''ll wait for it to resolve
|
|
2601
|
-
// before committing to the match and resolving
|
|
2602
|
-
// the loadPromise
|
|
2603
|
-
|
|
2604
|
-
// Actually run the loader and handle the result
|
|
2605
|
-
try {
|
|
2606
|
-
if (
|
|
2607
|
-
!this.isServer ||
|
|
2608
|
-
(this.isServer &&
|
|
2609
|
-
this.getMatch(matchId)!.ssr === true)
|
|
2610
|
-
) {
|
|
2611
|
-
this.loadRouteChunk(route)
|
|
2612
|
-
}
|
|
2613
|
-
|
|
2614
|
-
updateMatch(matchId, (prev) => ({
|
|
2615
|
-
...prev,
|
|
2616
|
-
isFetching: 'loader',
|
|
2617
|
-
}))
|
|
2618
|
-
|
|
2619
|
-
// Kick off the loader!
|
|
2620
|
-
const loaderData =
|
|
2621
|
-
await route.options.loader?.(getLoaderContext())
|
|
2622
|
-
|
|
2623
|
-
handleRedirectAndNotFound(
|
|
2624
|
-
this.getMatch(matchId)!,
|
|
2625
|
-
loaderData,
|
|
2626
|
-
)
|
|
2627
|
-
updateMatch(matchId, (prev) => ({
|
|
2628
|
-
...prev,
|
|
2629
|
-
loaderData,
|
|
2630
|
-
}))
|
|
2631
|
-
|
|
2632
|
-
// Lazy option can modify the route options,
|
|
2633
|
-
// so we need to wait for it to resolve before
|
|
2634
|
-
// we can use the options
|
|
2635
|
-
await route._lazyPromise
|
|
2636
|
-
const head = await executeHead()
|
|
2637
|
-
await potentialPendingMinPromise()
|
|
2638
|
-
|
|
2639
|
-
// Last but not least, wait for the the components
|
|
2640
|
-
// to be preloaded before we resolve the match
|
|
2641
|
-
await route._componentsPromise
|
|
2642
|
-
updateMatch(matchId, (prev) => ({
|
|
2643
|
-
...prev,
|
|
2644
|
-
error: undefined,
|
|
2645
|
-
status: 'success',
|
|
2646
|
-
isFetching: false,
|
|
2647
|
-
updatedAt: Date.now(),
|
|
2648
|
-
...head,
|
|
2649
|
-
}))
|
|
2650
|
-
} catch (e) {
|
|
2651
|
-
let error = e
|
|
2652
|
-
|
|
2653
|
-
await potentialPendingMinPromise()
|
|
2654
|
-
|
|
2655
|
-
handleRedirectAndNotFound(this.getMatch(matchId)!, e)
|
|
2656
|
-
|
|
2657
|
-
try {
|
|
2658
|
-
route.options.onError?.(e)
|
|
2659
|
-
} catch (onErrorError) {
|
|
2660
|
-
error = onErrorError
|
|
2661
|
-
handleRedirectAndNotFound(
|
|
2662
|
-
this.getMatch(matchId)!,
|
|
2663
|
-
onErrorError,
|
|
2664
|
-
)
|
|
2665
|
-
}
|
|
2666
|
-
const head = await executeHead()
|
|
2667
|
-
updateMatch(matchId, (prev) => ({
|
|
2668
|
-
...prev,
|
|
2669
|
-
error,
|
|
2670
|
-
status: 'error',
|
|
2671
|
-
isFetching: false,
|
|
2672
|
-
...head,
|
|
2673
|
-
}))
|
|
2674
|
-
}
|
|
2675
|
-
} catch (err) {
|
|
2676
|
-
const head = await executeHead()
|
|
2677
|
-
|
|
2678
|
-
updateMatch(matchId, (prev) => ({
|
|
2679
|
-
...prev,
|
|
2680
|
-
loaderPromise: undefined,
|
|
2681
|
-
...head,
|
|
2682
|
-
}))
|
|
2683
|
-
handleRedirectAndNotFound(this.getMatch(matchId)!, err)
|
|
2684
|
-
}
|
|
2685
|
-
}
|
|
2686
|
-
|
|
2687
|
-
// If the route is successful and still fresh, just resolve
|
|
2688
|
-
const { status, invalid } = this.getMatch(matchId)!
|
|
2689
|
-
loaderShouldRunAsync =
|
|
2690
|
-
status === 'success' &&
|
|
2691
|
-
(invalid || (shouldReload ?? age > staleAge))
|
|
2692
|
-
if (preload && route.options.preload === false) {
|
|
2693
|
-
// Do nothing
|
|
2694
|
-
} else if (loaderShouldRunAsync && !sync) {
|
|
2695
|
-
loaderIsRunningAsync = true
|
|
2696
|
-
;(async () => {
|
|
2697
|
-
try {
|
|
2698
|
-
await runLoader()
|
|
2699
|
-
const { loaderPromise, loadPromise } =
|
|
2700
|
-
this.getMatch(matchId)!
|
|
2701
|
-
loaderPromise?.resolve()
|
|
2702
|
-
loadPromise?.resolve()
|
|
2703
|
-
updateMatch(matchId, (prev) => ({
|
|
2704
|
-
...prev,
|
|
2705
|
-
loaderPromise: undefined,
|
|
2706
|
-
}))
|
|
2707
|
-
} catch (err) {
|
|
2708
|
-
if (isRedirect(err)) {
|
|
2709
|
-
await this.navigate(err.options)
|
|
2710
|
-
}
|
|
2711
|
-
}
|
|
2712
|
-
})()
|
|
2713
|
-
} else if (
|
|
2714
|
-
status !== 'success' ||
|
|
2715
|
-
(loaderShouldRunAsync && sync)
|
|
2716
|
-
) {
|
|
2717
|
-
await runLoader()
|
|
2718
|
-
} else {
|
|
2719
|
-
// if the loader did not run, still update head.
|
|
2720
|
-
// reason: parent's beforeLoad may have changed the route context
|
|
2721
|
-
// and only now do we know the route context (and that the loader would not run)
|
|
2722
|
-
const head = await executeHead()
|
|
2723
|
-
updateMatch(matchId, (prev) => ({
|
|
2724
|
-
...prev,
|
|
2725
|
-
...head,
|
|
2726
|
-
}))
|
|
2727
|
-
}
|
|
2728
|
-
}
|
|
2729
|
-
if (!loaderIsRunningAsync) {
|
|
2730
|
-
const { loaderPromise, loadPromise } =
|
|
2731
|
-
this.getMatch(matchId)!
|
|
2732
|
-
loaderPromise?.resolve()
|
|
2733
|
-
loadPromise?.resolve()
|
|
2734
|
-
}
|
|
2735
|
-
|
|
2736
|
-
updateMatch(matchId, (prev) => {
|
|
2737
|
-
clearTimeout(prev.pendingTimeout)
|
|
2738
|
-
return {
|
|
2739
|
-
...prev,
|
|
2740
|
-
isFetching: loaderIsRunningAsync
|
|
2741
|
-
? prev.isFetching
|
|
2742
|
-
: false,
|
|
2743
|
-
loaderPromise: loaderIsRunningAsync
|
|
2744
|
-
? prev.loaderPromise
|
|
2745
|
-
: undefined,
|
|
2746
|
-
invalid: false,
|
|
2747
|
-
pendingTimeout: undefined,
|
|
2748
|
-
_dehydrated: undefined,
|
|
2749
|
-
}
|
|
2750
|
-
})
|
|
2751
|
-
return this.getMatch(matchId)!
|
|
2752
|
-
})(),
|
|
2753
|
-
)
|
|
2754
|
-
})
|
|
2755
|
-
|
|
2756
|
-
await Promise.all(matchPromises)
|
|
2757
|
-
|
|
2758
|
-
resolveAll()
|
|
2759
|
-
} catch (err) {
|
|
2760
|
-
rejectAll(err)
|
|
2761
|
-
}
|
|
2762
|
-
})()
|
|
2763
|
-
})
|
|
2764
|
-
await triggerOnReady()
|
|
2765
|
-
} catch (err) {
|
|
2766
|
-
if (isRedirect(err) || isNotFound(err)) {
|
|
2767
|
-
if (isNotFound(err) && !allPreload) {
|
|
2768
|
-
await triggerOnReady()
|
|
2769
|
-
}
|
|
2770
|
-
|
|
2771
|
-
throw err
|
|
2772
|
-
}
|
|
2773
|
-
}
|
|
2774
|
-
|
|
2775
|
-
return matches
|
|
2776
|
-
}
|
|
2777
|
-
|
|
2778
2093
|
invalidate: InvalidateFn<
|
|
2779
2094
|
RouterCore<
|
|
2780
2095
|
TRouteTree,
|
|
@@ -2791,7 +2106,7 @@ export class RouterCore<
|
|
|
2791
2106
|
invalid: true,
|
|
2792
2107
|
...(opts?.forcePending || d.status === 'error'
|
|
2793
2108
|
? ({ status: 'pending', error: undefined } as const)
|
|
2794
|
-
:
|
|
2109
|
+
: undefined),
|
|
2795
2110
|
}
|
|
2796
2111
|
}
|
|
2797
2112
|
return d
|
|
@@ -2868,36 +2183,7 @@ export class RouterCore<
|
|
|
2868
2183
|
this.clearCache({ filter })
|
|
2869
2184
|
}
|
|
2870
2185
|
|
|
2871
|
-
loadRouteChunk =
|
|
2872
|
-
if (route._lazyPromise === undefined) {
|
|
2873
|
-
if (route.lazyFn) {
|
|
2874
|
-
route._lazyPromise = route.lazyFn().then((lazyRoute) => {
|
|
2875
|
-
// explicitly don't copy over the lazy route's id
|
|
2876
|
-
const { id: _id, ...options } = lazyRoute.options
|
|
2877
|
-
Object.assign(route.options, options)
|
|
2878
|
-
})
|
|
2879
|
-
} else {
|
|
2880
|
-
route._lazyPromise = Promise.resolve()
|
|
2881
|
-
}
|
|
2882
|
-
}
|
|
2883
|
-
|
|
2884
|
-
// If for some reason lazy resolves more lazy components...
|
|
2885
|
-
// We'll wait for that before pre attempt to preload any
|
|
2886
|
-
// components themselves.
|
|
2887
|
-
if (route._componentsPromise === undefined) {
|
|
2888
|
-
route._componentsPromise = route._lazyPromise.then(() =>
|
|
2889
|
-
Promise.all(
|
|
2890
|
-
componentTypes.map(async (type) => {
|
|
2891
|
-
const component = route.options[type]
|
|
2892
|
-
if ((component as any)?.preload) {
|
|
2893
|
-
await (component as any).preload()
|
|
2894
|
-
}
|
|
2895
|
-
}),
|
|
2896
|
-
),
|
|
2897
|
-
)
|
|
2898
|
-
}
|
|
2899
|
-
return route._componentsPromise
|
|
2900
|
-
}
|
|
2186
|
+
loadRouteChunk = loadRouteChunk
|
|
2901
2187
|
|
|
2902
2188
|
preloadRoute: PreloadRouteFn<
|
|
2903
2189
|
TRouteTree,
|
|
@@ -2937,7 +2223,8 @@ export class RouterCore<
|
|
|
2937
2223
|
})
|
|
2938
2224
|
|
|
2939
2225
|
try {
|
|
2940
|
-
matches = await
|
|
2226
|
+
matches = await loadMatches({
|
|
2227
|
+
router: this,
|
|
2941
2228
|
matches,
|
|
2942
2229
|
location: next,
|
|
2943
2230
|
preload: true,
|
|
@@ -3035,68 +2322,6 @@ export class RouterCore<
|
|
|
3035
2322
|
|
|
3036
2323
|
serverSsr?: ServerSsr
|
|
3037
2324
|
|
|
3038
|
-
_handleNotFound = (
|
|
3039
|
-
matches: Array<AnyRouteMatch>,
|
|
3040
|
-
err: NotFoundError,
|
|
3041
|
-
{
|
|
3042
|
-
updateMatch = this.updateMatch,
|
|
3043
|
-
}: {
|
|
3044
|
-
updateMatch?: (
|
|
3045
|
-
id: string,
|
|
3046
|
-
updater: (match: AnyRouteMatch) => AnyRouteMatch,
|
|
3047
|
-
) => void
|
|
3048
|
-
} = {},
|
|
3049
|
-
) => {
|
|
3050
|
-
// Find the route that should handle the not found error
|
|
3051
|
-
// First check if a specific route is requested to show the error
|
|
3052
|
-
const routeCursor = this.routesById[err.routeId ?? ''] ?? this.routeTree
|
|
3053
|
-
const matchesByRouteId: Record<string, AnyRouteMatch> = {}
|
|
3054
|
-
|
|
3055
|
-
// Setup routesByRouteId object for quick access
|
|
3056
|
-
for (const match of matches) {
|
|
3057
|
-
matchesByRouteId[match.routeId] = match
|
|
3058
|
-
}
|
|
3059
|
-
|
|
3060
|
-
// Ensure a NotFoundComponent exists on the route
|
|
3061
|
-
if (
|
|
3062
|
-
!routeCursor.options.notFoundComponent &&
|
|
3063
|
-
(this.options as any)?.defaultNotFoundComponent
|
|
3064
|
-
) {
|
|
3065
|
-
routeCursor.options.notFoundComponent = (
|
|
3066
|
-
this.options as any
|
|
3067
|
-
).defaultNotFoundComponent
|
|
3068
|
-
}
|
|
3069
|
-
|
|
3070
|
-
// Ensure we have a notFoundComponent
|
|
3071
|
-
invariant(
|
|
3072
|
-
routeCursor.options.notFoundComponent,
|
|
3073
|
-
'No notFoundComponent found. Please set a notFoundComponent on your route or provide a defaultNotFoundComponent to the router.',
|
|
3074
|
-
)
|
|
3075
|
-
|
|
3076
|
-
// Find the match for this route
|
|
3077
|
-
const matchForRoute = matchesByRouteId[routeCursor.id]
|
|
3078
|
-
|
|
3079
|
-
invariant(
|
|
3080
|
-
matchForRoute,
|
|
3081
|
-
'Could not find match for route: ' + routeCursor.id,
|
|
3082
|
-
)
|
|
3083
|
-
|
|
3084
|
-
// Assign the error to the match - using non-null assertion since we've checked with invariant
|
|
3085
|
-
updateMatch(matchForRoute.id, (prev) => ({
|
|
3086
|
-
...prev,
|
|
3087
|
-
status: 'notFound',
|
|
3088
|
-
error: err,
|
|
3089
|
-
isFetching: false,
|
|
3090
|
-
}))
|
|
3091
|
-
|
|
3092
|
-
if ((err as any).routerCode === 'BEFORE_LOAD' && routeCursor.parentRoute) {
|
|
3093
|
-
err.routeId = routeCursor.parentRoute.id
|
|
3094
|
-
this._handleNotFound(matches, err, {
|
|
3095
|
-
updateMatch,
|
|
3096
|
-
})
|
|
3097
|
-
}
|
|
3098
|
-
}
|
|
3099
|
-
|
|
3100
2325
|
hasNotFoundMatch = () => {
|
|
3101
2326
|
return this.__store.state.matches.some(
|
|
3102
2327
|
(d) => d.status === 'notFound' || d.globalNotFound,
|
|
@@ -3174,22 +2399,6 @@ function validateSearch(validateSearch: AnyValidator, input: unknown): unknown {
|
|
|
3174
2399
|
return {}
|
|
3175
2400
|
}
|
|
3176
2401
|
|
|
3177
|
-
export const componentTypes = [
|
|
3178
|
-
'component',
|
|
3179
|
-
'errorComponent',
|
|
3180
|
-
'pendingComponent',
|
|
3181
|
-
'notFoundComponent',
|
|
3182
|
-
] as const
|
|
3183
|
-
|
|
3184
|
-
function routeNeedsPreload(route: AnyRoute) {
|
|
3185
|
-
for (const componentType of componentTypes) {
|
|
3186
|
-
if ((route.options[componentType] as any)?.preload) {
|
|
3187
|
-
return true
|
|
3188
|
-
}
|
|
3189
|
-
}
|
|
3190
|
-
return false
|
|
3191
|
-
}
|
|
3192
|
-
|
|
3193
2402
|
interface RouteLike {
|
|
3194
2403
|
id: string
|
|
3195
2404
|
isRoot?: boolean
|
|
@@ -3562,7 +2771,8 @@ function applySearchMiddleware({
|
|
|
3562
2771
|
try {
|
|
3563
2772
|
const validatedSearch = {
|
|
3564
2773
|
...result,
|
|
3565
|
-
...(validateSearch(route.options.validateSearch, result) ??
|
|
2774
|
+
...(validateSearch(route.options.validateSearch, result) ??
|
|
2775
|
+
undefined),
|
|
3566
2776
|
}
|
|
3567
2777
|
return validatedSearch
|
|
3568
2778
|
} catch {
|