@tanstack/router-core 1.132.0-alpha.2 → 1.132.0-alpha.4
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 +8 -2
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +6 -2
- 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/router.cjs +33 -702
- package/dist/cjs/router.cjs.map +1 -1
- package/dist/cjs/router.d.cts +18 -39
- 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 -1
- 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 +50 -0
- package/dist/cjs/ssr/serializer/transformer.cjs.map +1 -0
- package/dist/cjs/ssr/serializer/transformer.d.cts +18 -0
- package/dist/cjs/ssr/ssr-client.cjs +15 -1
- 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/utils.cjs +8 -7
- package/dist/cjs/utils.cjs.map +1 -1
- package/dist/cjs/utils.d.cts +1 -1
- package/dist/esm/index.d.ts +6 -2
- package/dist/esm/index.js +9 -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/router.d.ts +18 -39
- package/dist/esm/router.js +33 -702
- package/dist/esm/router.js.map +1 -1
- package/dist/esm/scroll-restoration.d.ts +1 -1
- 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 +18 -0
- package/dist/esm/ssr/serializer/transformer.js +50 -0
- package/dist/esm/ssr/serializer/transformer.js.map +1 -0
- package/dist/esm/ssr/ssr-client.d.ts +5 -1
- package/dist/esm/ssr/ssr-client.js +15 -1
- 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/utils.d.ts +1 -1
- package/dist/esm/utils.js +8 -7
- package/dist/esm/utils.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +12 -2
- package/src/load-matches.ts +955 -0
- package/src/qss.ts +27 -24
- package/src/redirect.ts +3 -3
- package/src/router.ts +66 -1050
- 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 +78 -0
- package/src/ssr/ssr-client.ts +30 -3
- package/src/ssr/ssr-server.ts +18 -10
- package/src/ssr/tsrScript.ts +5 -1
- package/src/utils.ts +11 -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,10 +8,9 @@ import invariant from 'tiny-invariant'
|
|
|
8
8
|
import {
|
|
9
9
|
createControlledPromise,
|
|
10
10
|
deepEqual,
|
|
11
|
+
findLast,
|
|
11
12
|
functionalUpdate,
|
|
12
|
-
isPromise,
|
|
13
13
|
last,
|
|
14
|
-
pick,
|
|
15
14
|
replaceEqualDeep,
|
|
16
15
|
} from './utils'
|
|
17
16
|
import {
|
|
@@ -35,6 +34,7 @@ import { defaultParseSearch, defaultStringifySearch } from './searchParams'
|
|
|
35
34
|
import { rootRouteId } from './root'
|
|
36
35
|
import { isRedirect, redirect } from './redirect'
|
|
37
36
|
import { createLRUCache } from './lru-cache'
|
|
37
|
+
import { loadMatches, loadRouteChunk, routeNeedsPreload } from './load-matches'
|
|
38
38
|
import type { ParsePathnameCache, Segment } from './path'
|
|
39
39
|
import type { SearchParser, SearchSerializer } from './searchParams'
|
|
40
40
|
import type { AnyRedirect, ResolvedRedirect } from './redirect'
|
|
@@ -57,13 +57,10 @@ import type {
|
|
|
57
57
|
AnyContext,
|
|
58
58
|
AnyRoute,
|
|
59
59
|
AnyRouteWithContext,
|
|
60
|
-
BeforeLoadContextOptions,
|
|
61
|
-
LoaderFnContext,
|
|
62
60
|
MakeRemountDepsOptionsUnion,
|
|
63
61
|
RouteContextOptions,
|
|
64
62
|
RouteMask,
|
|
65
63
|
SearchMiddleware,
|
|
66
|
-
SsrContextOptions,
|
|
67
64
|
} from './route'
|
|
68
65
|
import type {
|
|
69
66
|
FullSearchSchema,
|
|
@@ -120,6 +117,7 @@ export interface RouterOptions<
|
|
|
120
117
|
TDefaultStructuralSharingOption extends boolean = false,
|
|
121
118
|
TRouterHistory extends RouterHistory = RouterHistory,
|
|
122
119
|
TDehydrated extends Record<string, any> = Record<string, any>,
|
|
120
|
+
TTransformerConfig = any,
|
|
123
121
|
> extends RouterOptionsExtensions {
|
|
124
122
|
/**
|
|
125
123
|
* The history object that will be used to manage the browser history.
|
|
@@ -393,7 +391,9 @@ export interface RouterOptions<
|
|
|
393
391
|
*
|
|
394
392
|
* @default false
|
|
395
393
|
*/
|
|
396
|
-
scrollRestoration?:
|
|
394
|
+
scrollRestoration?:
|
|
395
|
+
| boolean
|
|
396
|
+
| ((opts: { location: ParsedLocation }) => boolean)
|
|
397
397
|
|
|
398
398
|
/**
|
|
399
399
|
* A function that will be called to get the key for the scroll restoration cache.
|
|
@@ -424,6 +424,8 @@ export interface RouterOptions<
|
|
|
424
424
|
* @default false
|
|
425
425
|
*/
|
|
426
426
|
disableGlobalCatchBoundary?: boolean
|
|
427
|
+
|
|
428
|
+
serializationAdapters?: TTransformerConfig
|
|
427
429
|
}
|
|
428
430
|
|
|
429
431
|
export interface RouterState<
|
|
@@ -532,13 +534,15 @@ export type RouterConstructorOptions<
|
|
|
532
534
|
TDefaultStructuralSharingOption extends boolean,
|
|
533
535
|
TRouterHistory extends RouterHistory,
|
|
534
536
|
TDehydrated extends Record<string, any>,
|
|
537
|
+
TTransformerConfig,
|
|
535
538
|
> = Omit<
|
|
536
539
|
RouterOptions<
|
|
537
540
|
TRouteTree,
|
|
538
541
|
TTrailingSlashOption,
|
|
539
542
|
TDefaultStructuralSharingOption,
|
|
540
543
|
TRouterHistory,
|
|
541
|
-
TDehydrated
|
|
544
|
+
TDehydrated,
|
|
545
|
+
TTransformerConfig
|
|
542
546
|
>,
|
|
543
547
|
'context'
|
|
544
548
|
> &
|
|
@@ -598,13 +602,15 @@ export type UpdateFn<
|
|
|
598
602
|
TDefaultStructuralSharingOption extends boolean,
|
|
599
603
|
TRouterHistory extends RouterHistory,
|
|
600
604
|
TDehydrated extends Record<string, any>,
|
|
605
|
+
TTransformerConfig extends any,
|
|
601
606
|
> = (
|
|
602
607
|
newOptions: RouterConstructorOptions<
|
|
603
608
|
TRouteTree,
|
|
604
609
|
TTrailingSlashOption,
|
|
605
610
|
TDefaultStructuralSharingOption,
|
|
606
611
|
TRouterHistory,
|
|
607
|
-
TDehydrated
|
|
612
|
+
TDehydrated,
|
|
613
|
+
TTransformerConfig
|
|
608
614
|
>,
|
|
609
615
|
) => void
|
|
610
616
|
|
|
@@ -689,10 +695,11 @@ export type AnyRouterWithContext<TContext> = RouterCore<
|
|
|
689
695
|
any,
|
|
690
696
|
any,
|
|
691
697
|
any,
|
|
698
|
+
any,
|
|
692
699
|
any
|
|
693
700
|
>
|
|
694
701
|
|
|
695
|
-
export type AnyRouter = RouterCore<any, any, any, any, any>
|
|
702
|
+
export type AnyRouter = RouterCore<any, any, any, any, any, any>
|
|
696
703
|
|
|
697
704
|
export interface ViewTransitionOptions {
|
|
698
705
|
types:
|
|
@@ -706,6 +713,7 @@ export interface ViewTransitionOptions {
|
|
|
706
713
|
}) => Array<string>)
|
|
707
714
|
}
|
|
708
715
|
|
|
716
|
+
// TODO where is this used? can we remove this?
|
|
709
717
|
export function defaultSerializeError(err: unknown) {
|
|
710
718
|
if (err instanceof Error) {
|
|
711
719
|
const obj = {
|
|
@@ -745,6 +753,7 @@ export type CreateRouterFn = <
|
|
|
745
753
|
TDefaultStructuralSharingOption extends boolean = false,
|
|
746
754
|
TRouterHistory extends RouterHistory = RouterHistory,
|
|
747
755
|
TDehydrated extends Record<string, any> = Record<string, any>,
|
|
756
|
+
TTransformerConfig = any,
|
|
748
757
|
>(
|
|
749
758
|
options: undefined extends number
|
|
750
759
|
? 'strictNullChecks must be enabled in tsconfig.json'
|
|
@@ -753,34 +762,25 @@ export type CreateRouterFn = <
|
|
|
753
762
|
TTrailingSlashOption,
|
|
754
763
|
TDefaultStructuralSharingOption,
|
|
755
764
|
TRouterHistory,
|
|
756
|
-
TDehydrated
|
|
765
|
+
TDehydrated,
|
|
766
|
+
TTransformerConfig
|
|
757
767
|
>,
|
|
758
768
|
) => RouterCore<
|
|
759
769
|
TRouteTree,
|
|
760
770
|
TTrailingSlashOption,
|
|
761
771
|
TDefaultStructuralSharingOption,
|
|
762
772
|
TRouterHistory,
|
|
763
|
-
TDehydrated
|
|
773
|
+
TDehydrated,
|
|
774
|
+
TTransformerConfig
|
|
764
775
|
>
|
|
765
776
|
|
|
766
|
-
type InnerLoadContext = {
|
|
767
|
-
location: ParsedLocation
|
|
768
|
-
firstBadMatchIndex?: number
|
|
769
|
-
rendered?: boolean
|
|
770
|
-
updateMatch: UpdateMatchFn
|
|
771
|
-
matches: Array<AnyRouteMatch>
|
|
772
|
-
preload?: boolean
|
|
773
|
-
onReady?: () => Promise<void>
|
|
774
|
-
sync?: boolean
|
|
775
|
-
matchPromises: Array<Promise<AnyRouteMatch>>
|
|
776
|
-
}
|
|
777
|
-
|
|
778
777
|
export class RouterCore<
|
|
779
778
|
in out TRouteTree extends AnyRoute,
|
|
780
779
|
in out TTrailingSlashOption extends TrailingSlashOption,
|
|
781
780
|
in out TDefaultStructuralSharingOption extends boolean,
|
|
782
781
|
in out TRouterHistory extends RouterHistory = RouterHistory,
|
|
783
782
|
in out TDehydrated extends Record<string, any> = Record<string, any>,
|
|
783
|
+
in out TTransformerConfig = any,
|
|
784
784
|
> {
|
|
785
785
|
// Option-independent properties
|
|
786
786
|
tempLocationKey: string | undefined = `${Math.round(
|
|
@@ -802,7 +802,8 @@ export class RouterCore<
|
|
|
802
802
|
TTrailingSlashOption,
|
|
803
803
|
TDefaultStructuralSharingOption,
|
|
804
804
|
TRouterHistory,
|
|
805
|
-
TDehydrated
|
|
805
|
+
TDehydrated,
|
|
806
|
+
TTransformerConfig
|
|
806
807
|
>,
|
|
807
808
|
'stringifySearch' | 'parseSearch' | 'context'
|
|
808
809
|
>
|
|
@@ -825,7 +826,8 @@ export class RouterCore<
|
|
|
825
826
|
TTrailingSlashOption,
|
|
826
827
|
TDefaultStructuralSharingOption,
|
|
827
828
|
TRouterHistory,
|
|
828
|
-
TDehydrated
|
|
829
|
+
TDehydrated,
|
|
830
|
+
TTransformerConfig
|
|
829
831
|
>,
|
|
830
832
|
) {
|
|
831
833
|
this.update({
|
|
@@ -863,7 +865,8 @@ export class RouterCore<
|
|
|
863
865
|
TTrailingSlashOption,
|
|
864
866
|
TDefaultStructuralSharingOption,
|
|
865
867
|
TRouterHistory,
|
|
866
|
-
TDehydrated
|
|
868
|
+
TDehydrated,
|
|
869
|
+
TTransformerConfig
|
|
867
870
|
> = (newOptions) => {
|
|
868
871
|
if (newOptions.notFoundRoute) {
|
|
869
872
|
console.warn(
|
|
@@ -948,7 +951,7 @@ export class RouterCore<
|
|
|
948
951
|
}
|
|
949
952
|
}
|
|
950
953
|
|
|
951
|
-
get state() {
|
|
954
|
+
get state(): RouterState<TRouteTree> {
|
|
952
955
|
return this.__store.state
|
|
953
956
|
}
|
|
954
957
|
|
|
@@ -1449,13 +1452,11 @@ export class RouterCore<
|
|
|
1449
1452
|
undefined,
|
|
1450
1453
|
).matchedRoutes
|
|
1451
1454
|
|
|
1452
|
-
const matchedFrom =
|
|
1453
|
-
.
|
|
1454
|
-
|
|
1455
|
-
return comparePaths(d.fullPath, fromPath)
|
|
1456
|
-
})
|
|
1455
|
+
const matchedFrom = findLast(allCurrentLocationMatches, (d) => {
|
|
1456
|
+
return comparePaths(d.fullPath, fromPath)
|
|
1457
|
+
})
|
|
1457
1458
|
|
|
1458
|
-
const matchedCurrent =
|
|
1459
|
+
const matchedCurrent = findLast(allFromMatches, (d) => {
|
|
1459
1460
|
return comparePaths(d.fullPath, currentLocation.pathname)
|
|
1460
1461
|
})
|
|
1461
1462
|
|
|
@@ -1480,20 +1481,20 @@ export class RouterCore<
|
|
|
1480
1481
|
: this.resolvePathWithBase(fromPath, '.')
|
|
1481
1482
|
|
|
1482
1483
|
// Resolve the next params
|
|
1483
|
-
|
|
1484
|
+
const nextParams =
|
|
1484
1485
|
dest.params === false || dest.params === null
|
|
1485
1486
|
? {}
|
|
1486
1487
|
: (dest.params ?? true) === true
|
|
1487
1488
|
? fromParams
|
|
1488
|
-
:
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1489
|
+
: Object.assign(
|
|
1490
|
+
fromParams,
|
|
1491
|
+
functionalUpdate(dest.params as any, fromParams),
|
|
1492
|
+
)
|
|
1492
1493
|
|
|
1493
1494
|
// Interpolate the path first to get the actual resolved path, then match against that
|
|
1494
1495
|
const interpolatedNextTo = interpolatePath({
|
|
1495
1496
|
path: nextTo,
|
|
1496
|
-
params: nextParams
|
|
1497
|
+
params: nextParams,
|
|
1497
1498
|
parseCache: this.parsePathnameCache,
|
|
1498
1499
|
}).interpolatedPath
|
|
1499
1500
|
|
|
@@ -1503,23 +1504,20 @@ export class RouterCore<
|
|
|
1503
1504
|
|
|
1504
1505
|
// If there are any params, we need to stringify them
|
|
1505
1506
|
if (Object.keys(nextParams).length > 0) {
|
|
1506
|
-
destRoutes
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
)
|
|
1511
|
-
}
|
|
1512
|
-
|
|
1513
|
-
.forEach((fn) => {
|
|
1514
|
-
nextParams = { ...nextParams!, ...fn!(nextParams) }
|
|
1515
|
-
})
|
|
1507
|
+
for (const route of destRoutes) {
|
|
1508
|
+
const fn =
|
|
1509
|
+
route.options.params?.stringify ?? route.options.stringifyParams
|
|
1510
|
+
if (fn) {
|
|
1511
|
+
Object.assign(nextParams, fn(nextParams))
|
|
1512
|
+
}
|
|
1513
|
+
}
|
|
1516
1514
|
}
|
|
1517
1515
|
|
|
1518
1516
|
const nextPathname = interpolatePath({
|
|
1519
1517
|
// Use the original template path for interpolation
|
|
1520
1518
|
// This preserves the original parameter syntax including optional parameters
|
|
1521
1519
|
path: nextTo,
|
|
1522
|
-
params: nextParams
|
|
1520
|
+
params: nextParams,
|
|
1523
1521
|
leaveWildcards: false,
|
|
1524
1522
|
leaveParams: opts.leaveParams,
|
|
1525
1523
|
decodeCharMap: this.pathParamsDecodeCharMap,
|
|
@@ -1529,20 +1527,20 @@ export class RouterCore<
|
|
|
1529
1527
|
// Resolve the next search
|
|
1530
1528
|
let nextSearch = fromSearch
|
|
1531
1529
|
if (opts._includeValidateSearch && this.options.search?.strict) {
|
|
1532
|
-
|
|
1530
|
+
const validatedSearch = {}
|
|
1533
1531
|
destRoutes.forEach((route) => {
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1532
|
+
if (route.options.validateSearch) {
|
|
1533
|
+
try {
|
|
1534
|
+
Object.assign(
|
|
1535
|
+
validatedSearch,
|
|
1536
|
+
validateSearch(route.options.validateSearch, {
|
|
1539
1537
|
...validatedSearch,
|
|
1540
1538
|
...nextSearch,
|
|
1541
|
-
})
|
|
1542
|
-
|
|
1539
|
+
}),
|
|
1540
|
+
)
|
|
1541
|
+
} catch {
|
|
1542
|
+
// ignore errors here because they are already handled in matchRoutes
|
|
1543
1543
|
}
|
|
1544
|
-
} catch {
|
|
1545
|
-
// ignore errors here because they are already handled in matchRoutes
|
|
1546
1544
|
}
|
|
1547
1545
|
})
|
|
1548
1546
|
nextSearch = validatedSearch
|
|
@@ -1629,7 +1627,7 @@ export class RouterCore<
|
|
|
1629
1627
|
if (foundMask) {
|
|
1630
1628
|
const { from: _from, ...maskProps } = foundMask
|
|
1631
1629
|
maskedDest = {
|
|
1632
|
-
|
|
1630
|
+
from: opts.from,
|
|
1633
1631
|
...maskProps,
|
|
1634
1632
|
params,
|
|
1635
1633
|
}
|
|
@@ -1647,7 +1645,7 @@ export class RouterCore<
|
|
|
1647
1645
|
|
|
1648
1646
|
if (opts.mask) {
|
|
1649
1647
|
return buildWithMatches(opts, {
|
|
1650
|
-
|
|
1648
|
+
from: opts.from,
|
|
1651
1649
|
...opts.mask,
|
|
1652
1650
|
})
|
|
1653
1651
|
}
|
|
@@ -1895,10 +1893,12 @@ export class RouterCore<
|
|
|
1895
1893
|
}),
|
|
1896
1894
|
})
|
|
1897
1895
|
|
|
1898
|
-
await
|
|
1896
|
+
await loadMatches({
|
|
1897
|
+
router: this,
|
|
1899
1898
|
sync: opts?.sync,
|
|
1900
1899
|
matches: this.state.pendingMatches as Array<AnyRouteMatch>,
|
|
1901
1900
|
location: next,
|
|
1901
|
+
updateMatch: this.updateMatch,
|
|
1902
1902
|
// eslint-disable-next-line @typescript-eslint/require-await
|
|
1903
1903
|
onReady: async () => {
|
|
1904
1904
|
// eslint-disable-next-line @typescript-eslint/require-await
|
|
@@ -2088,873 +2088,6 @@ export class RouterCore<
|
|
|
2088
2088
|
)
|
|
2089
2089
|
}
|
|
2090
2090
|
|
|
2091
|
-
private triggerOnReady = (
|
|
2092
|
-
innerLoadContext: InnerLoadContext,
|
|
2093
|
-
): void | Promise<void> => {
|
|
2094
|
-
if (!innerLoadContext.rendered) {
|
|
2095
|
-
innerLoadContext.rendered = true
|
|
2096
|
-
return innerLoadContext.onReady?.()
|
|
2097
|
-
}
|
|
2098
|
-
}
|
|
2099
|
-
|
|
2100
|
-
private resolvePreload = (
|
|
2101
|
-
innerLoadContext: InnerLoadContext,
|
|
2102
|
-
matchId: string,
|
|
2103
|
-
): boolean => {
|
|
2104
|
-
return !!(
|
|
2105
|
-
innerLoadContext.preload &&
|
|
2106
|
-
!this.state.matches.some((d) => d.id === matchId)
|
|
2107
|
-
)
|
|
2108
|
-
}
|
|
2109
|
-
|
|
2110
|
-
private handleRedirectAndNotFound = (
|
|
2111
|
-
innerLoadContext: InnerLoadContext,
|
|
2112
|
-
match: AnyRouteMatch | undefined,
|
|
2113
|
-
err: unknown,
|
|
2114
|
-
): void => {
|
|
2115
|
-
if (!isRedirect(err) && !isNotFound(err)) return
|
|
2116
|
-
|
|
2117
|
-
if (isRedirect(err) && err.redirectHandled && !err.options.reloadDocument) {
|
|
2118
|
-
throw err
|
|
2119
|
-
}
|
|
2120
|
-
|
|
2121
|
-
// in case of a redirecting match during preload, the match does not exist
|
|
2122
|
-
if (match) {
|
|
2123
|
-
match._nonReactive.beforeLoadPromise?.resolve()
|
|
2124
|
-
match._nonReactive.loaderPromise?.resolve()
|
|
2125
|
-
match._nonReactive.beforeLoadPromise = undefined
|
|
2126
|
-
match._nonReactive.loaderPromise = undefined
|
|
2127
|
-
|
|
2128
|
-
const status = isRedirect(err) ? 'redirected' : 'notFound'
|
|
2129
|
-
|
|
2130
|
-
innerLoadContext.updateMatch(match.id, (prev) => ({
|
|
2131
|
-
...prev,
|
|
2132
|
-
status,
|
|
2133
|
-
isFetching: false,
|
|
2134
|
-
error: err,
|
|
2135
|
-
}))
|
|
2136
|
-
|
|
2137
|
-
if (isNotFound(err) && !err.routeId) {
|
|
2138
|
-
err.routeId = match.routeId
|
|
2139
|
-
}
|
|
2140
|
-
|
|
2141
|
-
match._nonReactive.loadPromise?.resolve()
|
|
2142
|
-
}
|
|
2143
|
-
|
|
2144
|
-
if (isRedirect(err)) {
|
|
2145
|
-
innerLoadContext.rendered = true
|
|
2146
|
-
err.options._fromLocation = innerLoadContext.location
|
|
2147
|
-
err.redirectHandled = true
|
|
2148
|
-
err = this.resolveRedirect(err)
|
|
2149
|
-
throw err
|
|
2150
|
-
} else {
|
|
2151
|
-
this._handleNotFound(innerLoadContext, err)
|
|
2152
|
-
throw err
|
|
2153
|
-
}
|
|
2154
|
-
}
|
|
2155
|
-
|
|
2156
|
-
private shouldSkipLoader = (matchId: string): boolean => {
|
|
2157
|
-
const match = this.getMatch(matchId)!
|
|
2158
|
-
// upon hydration, we skip the loader if the match has been dehydrated on the server
|
|
2159
|
-
if (!this.isServer && match._nonReactive.dehydrated) {
|
|
2160
|
-
return true
|
|
2161
|
-
}
|
|
2162
|
-
|
|
2163
|
-
if (this.isServer) {
|
|
2164
|
-
if (match.ssr === false) {
|
|
2165
|
-
return true
|
|
2166
|
-
}
|
|
2167
|
-
}
|
|
2168
|
-
return false
|
|
2169
|
-
}
|
|
2170
|
-
|
|
2171
|
-
private handleSerialError = (
|
|
2172
|
-
innerLoadContext: InnerLoadContext,
|
|
2173
|
-
index: number,
|
|
2174
|
-
err: any,
|
|
2175
|
-
routerCode: string,
|
|
2176
|
-
): void => {
|
|
2177
|
-
const { id: matchId, routeId } = innerLoadContext.matches[index]!
|
|
2178
|
-
const route = this.looseRoutesById[routeId]!
|
|
2179
|
-
|
|
2180
|
-
// Much like suspense, we use a promise here to know if
|
|
2181
|
-
// we've been outdated by a new loadMatches call and
|
|
2182
|
-
// should abort the current async operation
|
|
2183
|
-
if (err instanceof Promise) {
|
|
2184
|
-
throw err
|
|
2185
|
-
}
|
|
2186
|
-
|
|
2187
|
-
err.routerCode = routerCode
|
|
2188
|
-
innerLoadContext.firstBadMatchIndex ??= index
|
|
2189
|
-
this.handleRedirectAndNotFound(
|
|
2190
|
-
innerLoadContext,
|
|
2191
|
-
this.getMatch(matchId),
|
|
2192
|
-
err,
|
|
2193
|
-
)
|
|
2194
|
-
|
|
2195
|
-
try {
|
|
2196
|
-
route.options.onError?.(err)
|
|
2197
|
-
} catch (errorHandlerErr) {
|
|
2198
|
-
err = errorHandlerErr
|
|
2199
|
-
this.handleRedirectAndNotFound(
|
|
2200
|
-
innerLoadContext,
|
|
2201
|
-
this.getMatch(matchId),
|
|
2202
|
-
err,
|
|
2203
|
-
)
|
|
2204
|
-
}
|
|
2205
|
-
|
|
2206
|
-
innerLoadContext.updateMatch(matchId, (prev) => {
|
|
2207
|
-
prev._nonReactive.beforeLoadPromise?.resolve()
|
|
2208
|
-
prev._nonReactive.beforeLoadPromise = undefined
|
|
2209
|
-
prev._nonReactive.loadPromise?.resolve()
|
|
2210
|
-
|
|
2211
|
-
return {
|
|
2212
|
-
...prev,
|
|
2213
|
-
error: err,
|
|
2214
|
-
status: 'error',
|
|
2215
|
-
isFetching: false,
|
|
2216
|
-
updatedAt: Date.now(),
|
|
2217
|
-
abortController: new AbortController(),
|
|
2218
|
-
}
|
|
2219
|
-
})
|
|
2220
|
-
}
|
|
2221
|
-
|
|
2222
|
-
private isBeforeLoadSsr = (
|
|
2223
|
-
innerLoadContext: InnerLoadContext,
|
|
2224
|
-
matchId: string,
|
|
2225
|
-
index: number,
|
|
2226
|
-
route: AnyRoute,
|
|
2227
|
-
): void | Promise<void> => {
|
|
2228
|
-
const existingMatch = this.getMatch(matchId)!
|
|
2229
|
-
const parentMatchId = innerLoadContext.matches[index - 1]?.id
|
|
2230
|
-
const parentMatch = parentMatchId
|
|
2231
|
-
? this.getMatch(parentMatchId)!
|
|
2232
|
-
: undefined
|
|
2233
|
-
|
|
2234
|
-
// in SPA mode, only SSR the root route
|
|
2235
|
-
if (this.isShell()) {
|
|
2236
|
-
existingMatch.ssr = matchId === rootRouteId
|
|
2237
|
-
return
|
|
2238
|
-
}
|
|
2239
|
-
|
|
2240
|
-
if (parentMatch?.ssr === false) {
|
|
2241
|
-
existingMatch.ssr = false
|
|
2242
|
-
return
|
|
2243
|
-
}
|
|
2244
|
-
|
|
2245
|
-
const parentOverride = (tempSsr: boolean | 'data-only') => {
|
|
2246
|
-
if (tempSsr === true && parentMatch?.ssr === 'data-only') {
|
|
2247
|
-
return 'data-only'
|
|
2248
|
-
}
|
|
2249
|
-
return tempSsr
|
|
2250
|
-
}
|
|
2251
|
-
|
|
2252
|
-
const defaultSsr = this.options.defaultSsr ?? true
|
|
2253
|
-
|
|
2254
|
-
if (route.options.ssr === undefined) {
|
|
2255
|
-
existingMatch.ssr = parentOverride(defaultSsr)
|
|
2256
|
-
return
|
|
2257
|
-
}
|
|
2258
|
-
|
|
2259
|
-
if (typeof route.options.ssr !== 'function') {
|
|
2260
|
-
existingMatch.ssr = parentOverride(route.options.ssr)
|
|
2261
|
-
return
|
|
2262
|
-
}
|
|
2263
|
-
const { search, params } = this.getMatch(matchId)!
|
|
2264
|
-
|
|
2265
|
-
const ssrFnContext: SsrContextOptions<any, any, any> = {
|
|
2266
|
-
search: makeMaybe(search, existingMatch.searchError),
|
|
2267
|
-
params: makeMaybe(params, existingMatch.paramsError),
|
|
2268
|
-
location: innerLoadContext.location,
|
|
2269
|
-
matches: innerLoadContext.matches.map((match) => ({
|
|
2270
|
-
index: match.index,
|
|
2271
|
-
pathname: match.pathname,
|
|
2272
|
-
fullPath: match.fullPath,
|
|
2273
|
-
staticData: match.staticData,
|
|
2274
|
-
id: match.id,
|
|
2275
|
-
routeId: match.routeId,
|
|
2276
|
-
search: makeMaybe(match.search, match.searchError),
|
|
2277
|
-
params: makeMaybe(match.params, match.paramsError),
|
|
2278
|
-
ssr: match.ssr,
|
|
2279
|
-
})),
|
|
2280
|
-
}
|
|
2281
|
-
|
|
2282
|
-
const tempSsr = route.options.ssr(ssrFnContext)
|
|
2283
|
-
if (isPromise(tempSsr)) {
|
|
2284
|
-
return tempSsr.then((ssr) => {
|
|
2285
|
-
existingMatch.ssr = parentOverride(ssr ?? defaultSsr)
|
|
2286
|
-
})
|
|
2287
|
-
}
|
|
2288
|
-
|
|
2289
|
-
existingMatch.ssr = parentOverride(tempSsr ?? defaultSsr)
|
|
2290
|
-
return
|
|
2291
|
-
}
|
|
2292
|
-
|
|
2293
|
-
private setupPendingTimeout = (
|
|
2294
|
-
innerLoadContext: InnerLoadContext,
|
|
2295
|
-
matchId: string,
|
|
2296
|
-
route: AnyRoute,
|
|
2297
|
-
): void => {
|
|
2298
|
-
const pendingMs = route.options.pendingMs ?? this.options.defaultPendingMs
|
|
2299
|
-
const shouldPending = !!(
|
|
2300
|
-
innerLoadContext.onReady &&
|
|
2301
|
-
!this.isServer &&
|
|
2302
|
-
!this.resolvePreload(innerLoadContext, matchId) &&
|
|
2303
|
-
(route.options.loader ||
|
|
2304
|
-
route.options.beforeLoad ||
|
|
2305
|
-
routeNeedsPreload(route)) &&
|
|
2306
|
-
typeof pendingMs === 'number' &&
|
|
2307
|
-
pendingMs !== Infinity &&
|
|
2308
|
-
(route.options.pendingComponent ??
|
|
2309
|
-
(this.options as any)?.defaultPendingComponent)
|
|
2310
|
-
)
|
|
2311
|
-
const match = this.getMatch(matchId)!
|
|
2312
|
-
if (shouldPending && match._nonReactive.pendingTimeout === undefined) {
|
|
2313
|
-
const pendingTimeout = setTimeout(() => {
|
|
2314
|
-
// Update the match and prematurely resolve the loadMatches promise so that
|
|
2315
|
-
// the pending component can start rendering
|
|
2316
|
-
this.triggerOnReady(innerLoadContext)
|
|
2317
|
-
}, pendingMs)
|
|
2318
|
-
match._nonReactive.pendingTimeout = pendingTimeout
|
|
2319
|
-
}
|
|
2320
|
-
}
|
|
2321
|
-
|
|
2322
|
-
private shouldExecuteBeforeLoad = (
|
|
2323
|
-
innerLoadContext: InnerLoadContext,
|
|
2324
|
-
matchId: string,
|
|
2325
|
-
route: AnyRoute,
|
|
2326
|
-
): boolean | Promise<boolean> => {
|
|
2327
|
-
const existingMatch = this.getMatch(matchId)!
|
|
2328
|
-
|
|
2329
|
-
// If we are in the middle of a load, either of these will be present
|
|
2330
|
-
// (not to be confused with `loadPromise`, which is always defined)
|
|
2331
|
-
if (
|
|
2332
|
-
!existingMatch._nonReactive.beforeLoadPromise &&
|
|
2333
|
-
!existingMatch._nonReactive.loaderPromise
|
|
2334
|
-
)
|
|
2335
|
-
return true
|
|
2336
|
-
|
|
2337
|
-
this.setupPendingTimeout(innerLoadContext, matchId, route)
|
|
2338
|
-
|
|
2339
|
-
const then = () => {
|
|
2340
|
-
let shouldExecuteBeforeLoad = true
|
|
2341
|
-
const match = this.getMatch(matchId)!
|
|
2342
|
-
if (match.status === 'error') {
|
|
2343
|
-
shouldExecuteBeforeLoad = true
|
|
2344
|
-
} else if (
|
|
2345
|
-
match.preload &&
|
|
2346
|
-
(match.status === 'redirected' || match.status === 'notFound')
|
|
2347
|
-
) {
|
|
2348
|
-
this.handleRedirectAndNotFound(innerLoadContext, match, match.error)
|
|
2349
|
-
}
|
|
2350
|
-
return shouldExecuteBeforeLoad
|
|
2351
|
-
}
|
|
2352
|
-
|
|
2353
|
-
// Wait for the beforeLoad to resolve before we continue
|
|
2354
|
-
return existingMatch._nonReactive.beforeLoadPromise
|
|
2355
|
-
? existingMatch._nonReactive.beforeLoadPromise.then(then)
|
|
2356
|
-
: then()
|
|
2357
|
-
}
|
|
2358
|
-
|
|
2359
|
-
private executeBeforeLoad = (
|
|
2360
|
-
innerLoadContext: InnerLoadContext,
|
|
2361
|
-
matchId: string,
|
|
2362
|
-
index: number,
|
|
2363
|
-
route: AnyRoute,
|
|
2364
|
-
): void | Promise<void> => {
|
|
2365
|
-
const match = this.getMatch(matchId)!
|
|
2366
|
-
|
|
2367
|
-
match._nonReactive.beforeLoadPromise = createControlledPromise<void>()
|
|
2368
|
-
// explicitly capture the previous loadPromise
|
|
2369
|
-
const prevLoadPromise = match._nonReactive.loadPromise
|
|
2370
|
-
match._nonReactive.loadPromise = createControlledPromise<void>(() => {
|
|
2371
|
-
prevLoadPromise?.resolve()
|
|
2372
|
-
})
|
|
2373
|
-
|
|
2374
|
-
const { paramsError, searchError } = match
|
|
2375
|
-
|
|
2376
|
-
if (paramsError) {
|
|
2377
|
-
this.handleSerialError(
|
|
2378
|
-
innerLoadContext,
|
|
2379
|
-
index,
|
|
2380
|
-
paramsError,
|
|
2381
|
-
'PARSE_PARAMS',
|
|
2382
|
-
)
|
|
2383
|
-
}
|
|
2384
|
-
|
|
2385
|
-
if (searchError) {
|
|
2386
|
-
this.handleSerialError(
|
|
2387
|
-
innerLoadContext,
|
|
2388
|
-
index,
|
|
2389
|
-
searchError,
|
|
2390
|
-
'VALIDATE_SEARCH',
|
|
2391
|
-
)
|
|
2392
|
-
}
|
|
2393
|
-
|
|
2394
|
-
this.setupPendingTimeout(innerLoadContext, matchId, route)
|
|
2395
|
-
|
|
2396
|
-
const abortController = new AbortController()
|
|
2397
|
-
|
|
2398
|
-
const parentMatchId = innerLoadContext.matches[index - 1]?.id
|
|
2399
|
-
const parentMatch = parentMatchId
|
|
2400
|
-
? this.getMatch(parentMatchId)!
|
|
2401
|
-
: undefined
|
|
2402
|
-
const parentMatchContext =
|
|
2403
|
-
parentMatch?.context ?? this.options.context ?? undefined
|
|
2404
|
-
|
|
2405
|
-
const context = { ...parentMatchContext, ...match.__routeContext }
|
|
2406
|
-
|
|
2407
|
-
let isPending = false
|
|
2408
|
-
const pending = () => {
|
|
2409
|
-
if (isPending) return
|
|
2410
|
-
isPending = true
|
|
2411
|
-
innerLoadContext.updateMatch(matchId, (prev) => ({
|
|
2412
|
-
...prev,
|
|
2413
|
-
isFetching: 'beforeLoad',
|
|
2414
|
-
fetchCount: prev.fetchCount + 1,
|
|
2415
|
-
abortController,
|
|
2416
|
-
context,
|
|
2417
|
-
}))
|
|
2418
|
-
}
|
|
2419
|
-
|
|
2420
|
-
const resolve = () => {
|
|
2421
|
-
match._nonReactive.beforeLoadPromise?.resolve()
|
|
2422
|
-
match._nonReactive.beforeLoadPromise = undefined
|
|
2423
|
-
innerLoadContext.updateMatch(matchId, (prev) => ({
|
|
2424
|
-
...prev,
|
|
2425
|
-
isFetching: false,
|
|
2426
|
-
}))
|
|
2427
|
-
}
|
|
2428
|
-
|
|
2429
|
-
// if there is no `beforeLoad` option, skip everything, batch update the store, return early
|
|
2430
|
-
if (!route.options.beforeLoad) {
|
|
2431
|
-
batch(() => {
|
|
2432
|
-
pending()
|
|
2433
|
-
resolve()
|
|
2434
|
-
})
|
|
2435
|
-
return
|
|
2436
|
-
}
|
|
2437
|
-
|
|
2438
|
-
const { search, params, cause } = match
|
|
2439
|
-
const preload = this.resolvePreload(innerLoadContext, matchId)
|
|
2440
|
-
const beforeLoadFnContext: BeforeLoadContextOptions<
|
|
2441
|
-
any,
|
|
2442
|
-
any,
|
|
2443
|
-
any,
|
|
2444
|
-
any,
|
|
2445
|
-
any
|
|
2446
|
-
> = {
|
|
2447
|
-
search,
|
|
2448
|
-
abortController,
|
|
2449
|
-
params,
|
|
2450
|
-
preload,
|
|
2451
|
-
context,
|
|
2452
|
-
location: innerLoadContext.location,
|
|
2453
|
-
navigate: (opts: any) =>
|
|
2454
|
-
this.navigate({ ...opts, _fromLocation: innerLoadContext.location }),
|
|
2455
|
-
buildLocation: this.buildLocation,
|
|
2456
|
-
cause: preload ? 'preload' : cause,
|
|
2457
|
-
matches: innerLoadContext.matches,
|
|
2458
|
-
}
|
|
2459
|
-
|
|
2460
|
-
const updateContext = (beforeLoadContext: any) => {
|
|
2461
|
-
if (beforeLoadContext === undefined) {
|
|
2462
|
-
batch(() => {
|
|
2463
|
-
pending()
|
|
2464
|
-
resolve()
|
|
2465
|
-
})
|
|
2466
|
-
return
|
|
2467
|
-
}
|
|
2468
|
-
if (isRedirect(beforeLoadContext) || isNotFound(beforeLoadContext)) {
|
|
2469
|
-
pending()
|
|
2470
|
-
this.handleSerialError(
|
|
2471
|
-
innerLoadContext,
|
|
2472
|
-
index,
|
|
2473
|
-
beforeLoadContext,
|
|
2474
|
-
'BEFORE_LOAD',
|
|
2475
|
-
)
|
|
2476
|
-
}
|
|
2477
|
-
|
|
2478
|
-
batch(() => {
|
|
2479
|
-
pending()
|
|
2480
|
-
innerLoadContext.updateMatch(matchId, (prev) => ({
|
|
2481
|
-
...prev,
|
|
2482
|
-
__beforeLoadContext: beforeLoadContext,
|
|
2483
|
-
context: {
|
|
2484
|
-
...prev.context,
|
|
2485
|
-
...beforeLoadContext,
|
|
2486
|
-
},
|
|
2487
|
-
}))
|
|
2488
|
-
resolve()
|
|
2489
|
-
})
|
|
2490
|
-
}
|
|
2491
|
-
|
|
2492
|
-
let beforeLoadContext
|
|
2493
|
-
try {
|
|
2494
|
-
beforeLoadContext = route.options.beforeLoad(beforeLoadFnContext)
|
|
2495
|
-
if (isPromise(beforeLoadContext)) {
|
|
2496
|
-
pending()
|
|
2497
|
-
return beforeLoadContext
|
|
2498
|
-
.catch((err) => {
|
|
2499
|
-
this.handleSerialError(innerLoadContext, index, err, 'BEFORE_LOAD')
|
|
2500
|
-
})
|
|
2501
|
-
.then(updateContext)
|
|
2502
|
-
}
|
|
2503
|
-
} catch (err) {
|
|
2504
|
-
pending()
|
|
2505
|
-
this.handleSerialError(innerLoadContext, index, err, 'BEFORE_LOAD')
|
|
2506
|
-
}
|
|
2507
|
-
|
|
2508
|
-
updateContext(beforeLoadContext)
|
|
2509
|
-
return
|
|
2510
|
-
}
|
|
2511
|
-
|
|
2512
|
-
private handleBeforeLoad = (
|
|
2513
|
-
innerLoadContext: InnerLoadContext,
|
|
2514
|
-
index: number,
|
|
2515
|
-
): void | Promise<void> => {
|
|
2516
|
-
const { id: matchId, routeId } = innerLoadContext.matches[index]!
|
|
2517
|
-
const route = this.looseRoutesById[routeId]!
|
|
2518
|
-
|
|
2519
|
-
const serverSsr = () => {
|
|
2520
|
-
// on the server, determine whether SSR the current match or not
|
|
2521
|
-
if (this.isServer) {
|
|
2522
|
-
const maybePromise = this.isBeforeLoadSsr(
|
|
2523
|
-
innerLoadContext,
|
|
2524
|
-
matchId,
|
|
2525
|
-
index,
|
|
2526
|
-
route,
|
|
2527
|
-
)
|
|
2528
|
-
if (isPromise(maybePromise)) return maybePromise.then(queueExecution)
|
|
2529
|
-
}
|
|
2530
|
-
return queueExecution()
|
|
2531
|
-
}
|
|
2532
|
-
|
|
2533
|
-
const queueExecution = () => {
|
|
2534
|
-
if (this.shouldSkipLoader(matchId)) return
|
|
2535
|
-
const shouldExecuteBeforeLoadResult = this.shouldExecuteBeforeLoad(
|
|
2536
|
-
innerLoadContext,
|
|
2537
|
-
matchId,
|
|
2538
|
-
route,
|
|
2539
|
-
)
|
|
2540
|
-
return isPromise(shouldExecuteBeforeLoadResult)
|
|
2541
|
-
? shouldExecuteBeforeLoadResult.then(execute)
|
|
2542
|
-
: execute(shouldExecuteBeforeLoadResult)
|
|
2543
|
-
}
|
|
2544
|
-
|
|
2545
|
-
const execute = (shouldExecuteBeforeLoad: boolean) => {
|
|
2546
|
-
if (shouldExecuteBeforeLoad) {
|
|
2547
|
-
// If we are not in the middle of a load OR the previous load failed, start it
|
|
2548
|
-
return this.executeBeforeLoad(innerLoadContext, matchId, index, route)
|
|
2549
|
-
}
|
|
2550
|
-
return
|
|
2551
|
-
}
|
|
2552
|
-
|
|
2553
|
-
return serverSsr()
|
|
2554
|
-
}
|
|
2555
|
-
|
|
2556
|
-
private executeHead = (
|
|
2557
|
-
innerLoadContext: InnerLoadContext,
|
|
2558
|
-
matchId: string,
|
|
2559
|
-
route: AnyRoute,
|
|
2560
|
-
): void | Promise<
|
|
2561
|
-
Pick<
|
|
2562
|
-
AnyRouteMatch,
|
|
2563
|
-
'meta' | 'links' | 'headScripts' | 'headers' | 'scripts' | 'styles'
|
|
2564
|
-
>
|
|
2565
|
-
> => {
|
|
2566
|
-
const match = this.getMatch(matchId)
|
|
2567
|
-
// in case of a redirecting match during preload, the match does not exist
|
|
2568
|
-
if (!match) {
|
|
2569
|
-
return
|
|
2570
|
-
}
|
|
2571
|
-
if (
|
|
2572
|
-
!route.options.head &&
|
|
2573
|
-
!route.options.scripts &&
|
|
2574
|
-
!route.options.headers
|
|
2575
|
-
) {
|
|
2576
|
-
return
|
|
2577
|
-
}
|
|
2578
|
-
const assetContext = {
|
|
2579
|
-
matches: innerLoadContext.matches,
|
|
2580
|
-
match,
|
|
2581
|
-
params: match.params,
|
|
2582
|
-
loaderData: match.loaderData,
|
|
2583
|
-
}
|
|
2584
|
-
|
|
2585
|
-
return Promise.all([
|
|
2586
|
-
route.options.head?.(assetContext),
|
|
2587
|
-
route.options.scripts?.(assetContext),
|
|
2588
|
-
route.options.headers?.(assetContext),
|
|
2589
|
-
]).then(([headFnContent, scripts, headers]) => {
|
|
2590
|
-
const meta = headFnContent?.meta
|
|
2591
|
-
const links = headFnContent?.links
|
|
2592
|
-
const headScripts = headFnContent?.scripts
|
|
2593
|
-
const styles = headFnContent?.styles
|
|
2594
|
-
|
|
2595
|
-
return {
|
|
2596
|
-
meta,
|
|
2597
|
-
links,
|
|
2598
|
-
headScripts,
|
|
2599
|
-
headers,
|
|
2600
|
-
scripts,
|
|
2601
|
-
styles,
|
|
2602
|
-
}
|
|
2603
|
-
})
|
|
2604
|
-
}
|
|
2605
|
-
|
|
2606
|
-
private potentialPendingMinPromise = (
|
|
2607
|
-
matchId: string,
|
|
2608
|
-
): void | ControlledPromise<void> => {
|
|
2609
|
-
const latestMatch = this.getMatch(matchId)!
|
|
2610
|
-
return latestMatch._nonReactive.minPendingPromise
|
|
2611
|
-
}
|
|
2612
|
-
|
|
2613
|
-
private getLoaderContext = (
|
|
2614
|
-
innerLoadContext: InnerLoadContext,
|
|
2615
|
-
matchId: string,
|
|
2616
|
-
index: number,
|
|
2617
|
-
route: AnyRoute,
|
|
2618
|
-
): LoaderFnContext => {
|
|
2619
|
-
const parentMatchPromise = innerLoadContext.matchPromises[index - 1] as any
|
|
2620
|
-
const { params, loaderDeps, abortController, context, cause } =
|
|
2621
|
-
this.getMatch(matchId)!
|
|
2622
|
-
|
|
2623
|
-
const preload = this.resolvePreload(innerLoadContext, matchId)
|
|
2624
|
-
|
|
2625
|
-
return {
|
|
2626
|
-
params,
|
|
2627
|
-
deps: loaderDeps,
|
|
2628
|
-
preload: !!preload,
|
|
2629
|
-
parentMatchPromise,
|
|
2630
|
-
abortController: abortController,
|
|
2631
|
-
context,
|
|
2632
|
-
location: innerLoadContext.location,
|
|
2633
|
-
navigate: (opts) =>
|
|
2634
|
-
this.navigate({ ...opts, _fromLocation: innerLoadContext.location }),
|
|
2635
|
-
cause: preload ? 'preload' : cause,
|
|
2636
|
-
route,
|
|
2637
|
-
}
|
|
2638
|
-
}
|
|
2639
|
-
|
|
2640
|
-
private runLoader = async (
|
|
2641
|
-
innerLoadContext: InnerLoadContext,
|
|
2642
|
-
matchId: string,
|
|
2643
|
-
index: number,
|
|
2644
|
-
route: AnyRoute,
|
|
2645
|
-
): Promise<void> => {
|
|
2646
|
-
try {
|
|
2647
|
-
// If the Matches component rendered
|
|
2648
|
-
// the pending component and needs to show it for
|
|
2649
|
-
// a minimum duration, we''ll wait for it to resolve
|
|
2650
|
-
// before committing to the match and resolving
|
|
2651
|
-
// the loadPromise
|
|
2652
|
-
|
|
2653
|
-
// Actually run the loader and handle the result
|
|
2654
|
-
try {
|
|
2655
|
-
if (!this.isServer || this.getMatch(matchId)!.ssr === true) {
|
|
2656
|
-
this.loadRouteChunk(route)
|
|
2657
|
-
}
|
|
2658
|
-
|
|
2659
|
-
// Kick off the loader!
|
|
2660
|
-
const loaderResult = route.options.loader?.(
|
|
2661
|
-
this.getLoaderContext(innerLoadContext, matchId, index, route),
|
|
2662
|
-
)
|
|
2663
|
-
const loaderResultIsPromise =
|
|
2664
|
-
route.options.loader && isPromise(loaderResult)
|
|
2665
|
-
|
|
2666
|
-
const willLoadSomething = !!(
|
|
2667
|
-
loaderResultIsPromise ||
|
|
2668
|
-
route._lazyPromise ||
|
|
2669
|
-
route._componentsPromise ||
|
|
2670
|
-
route.options.head ||
|
|
2671
|
-
route.options.scripts ||
|
|
2672
|
-
route.options.headers ||
|
|
2673
|
-
this.getMatch(matchId)!._nonReactive.minPendingPromise
|
|
2674
|
-
)
|
|
2675
|
-
|
|
2676
|
-
if (willLoadSomething) {
|
|
2677
|
-
innerLoadContext.updateMatch(matchId, (prev) => ({
|
|
2678
|
-
...prev,
|
|
2679
|
-
isFetching: 'loader',
|
|
2680
|
-
}))
|
|
2681
|
-
}
|
|
2682
|
-
|
|
2683
|
-
if (route.options.loader) {
|
|
2684
|
-
const loaderData = loaderResultIsPromise
|
|
2685
|
-
? await loaderResult
|
|
2686
|
-
: loaderResult
|
|
2687
|
-
|
|
2688
|
-
this.handleRedirectAndNotFound(
|
|
2689
|
-
innerLoadContext,
|
|
2690
|
-
this.getMatch(matchId),
|
|
2691
|
-
loaderData,
|
|
2692
|
-
)
|
|
2693
|
-
if (loaderData !== undefined) {
|
|
2694
|
-
innerLoadContext.updateMatch(matchId, (prev) => ({
|
|
2695
|
-
...prev,
|
|
2696
|
-
loaderData,
|
|
2697
|
-
}))
|
|
2698
|
-
}
|
|
2699
|
-
}
|
|
2700
|
-
|
|
2701
|
-
// Lazy option can modify the route options,
|
|
2702
|
-
// so we need to wait for it to resolve before
|
|
2703
|
-
// we can use the options
|
|
2704
|
-
if (route._lazyPromise) await route._lazyPromise
|
|
2705
|
-
const headResult = this.executeHead(innerLoadContext, matchId, route)
|
|
2706
|
-
const head = headResult ? await headResult : undefined
|
|
2707
|
-
const pendingPromise = this.potentialPendingMinPromise(matchId)
|
|
2708
|
-
if (pendingPromise) await pendingPromise
|
|
2709
|
-
|
|
2710
|
-
// Last but not least, wait for the the components
|
|
2711
|
-
// to be preloaded before we resolve the match
|
|
2712
|
-
if (route._componentsPromise) await route._componentsPromise
|
|
2713
|
-
innerLoadContext.updateMatch(matchId, (prev) => ({
|
|
2714
|
-
...prev,
|
|
2715
|
-
error: undefined,
|
|
2716
|
-
status: 'success',
|
|
2717
|
-
isFetching: false,
|
|
2718
|
-
updatedAt: Date.now(),
|
|
2719
|
-
...head,
|
|
2720
|
-
}))
|
|
2721
|
-
} catch (e) {
|
|
2722
|
-
let error = e
|
|
2723
|
-
|
|
2724
|
-
const pendingPromise = this.potentialPendingMinPromise(matchId)
|
|
2725
|
-
if (pendingPromise) await pendingPromise
|
|
2726
|
-
|
|
2727
|
-
this.handleRedirectAndNotFound(
|
|
2728
|
-
innerLoadContext,
|
|
2729
|
-
this.getMatch(matchId),
|
|
2730
|
-
e,
|
|
2731
|
-
)
|
|
2732
|
-
|
|
2733
|
-
try {
|
|
2734
|
-
route.options.onError?.(e)
|
|
2735
|
-
} catch (onErrorError) {
|
|
2736
|
-
error = onErrorError
|
|
2737
|
-
this.handleRedirectAndNotFound(
|
|
2738
|
-
innerLoadContext,
|
|
2739
|
-
this.getMatch(matchId),
|
|
2740
|
-
onErrorError,
|
|
2741
|
-
)
|
|
2742
|
-
}
|
|
2743
|
-
const headResult = this.executeHead(innerLoadContext, matchId, route)
|
|
2744
|
-
const head = headResult ? await headResult : undefined
|
|
2745
|
-
innerLoadContext.updateMatch(matchId, (prev) => ({
|
|
2746
|
-
...prev,
|
|
2747
|
-
error,
|
|
2748
|
-
status: 'error',
|
|
2749
|
-
isFetching: false,
|
|
2750
|
-
...head,
|
|
2751
|
-
}))
|
|
2752
|
-
}
|
|
2753
|
-
} catch (err) {
|
|
2754
|
-
const match = this.getMatch(matchId)
|
|
2755
|
-
// in case of a redirecting match during preload, the match does not exist
|
|
2756
|
-
if (match) {
|
|
2757
|
-
const headResult = this.executeHead(innerLoadContext, matchId, route)
|
|
2758
|
-
if (headResult) {
|
|
2759
|
-
const head = await headResult
|
|
2760
|
-
innerLoadContext.updateMatch(matchId, (prev) => ({
|
|
2761
|
-
...prev,
|
|
2762
|
-
...head,
|
|
2763
|
-
}))
|
|
2764
|
-
}
|
|
2765
|
-
match._nonReactive.loaderPromise = undefined
|
|
2766
|
-
}
|
|
2767
|
-
this.handleRedirectAndNotFound(innerLoadContext, match, err)
|
|
2768
|
-
}
|
|
2769
|
-
}
|
|
2770
|
-
|
|
2771
|
-
private loadRouteMatch = async (
|
|
2772
|
-
innerLoadContext: InnerLoadContext,
|
|
2773
|
-
index: number,
|
|
2774
|
-
): Promise<AnyRouteMatch> => {
|
|
2775
|
-
const { id: matchId, routeId } = innerLoadContext.matches[index]!
|
|
2776
|
-
let loaderShouldRunAsync = false
|
|
2777
|
-
let loaderIsRunningAsync = false
|
|
2778
|
-
const route = this.looseRoutesById[routeId]!
|
|
2779
|
-
|
|
2780
|
-
const prevMatch = this.getMatch(matchId)!
|
|
2781
|
-
if (this.shouldSkipLoader(matchId)) {
|
|
2782
|
-
if (this.isServer) {
|
|
2783
|
-
const headResult = this.executeHead(innerLoadContext, matchId, route)
|
|
2784
|
-
if (headResult) {
|
|
2785
|
-
const head = await headResult
|
|
2786
|
-
innerLoadContext.updateMatch(matchId, (prev) => ({
|
|
2787
|
-
...prev,
|
|
2788
|
-
...head,
|
|
2789
|
-
}))
|
|
2790
|
-
}
|
|
2791
|
-
return this.getMatch(matchId)!
|
|
2792
|
-
}
|
|
2793
|
-
}
|
|
2794
|
-
// there is a loaderPromise, so we are in the middle of a load
|
|
2795
|
-
else if (prevMatch._nonReactive.loaderPromise) {
|
|
2796
|
-
// do not block if we already have stale data we can show
|
|
2797
|
-
// but only if the ongoing load is not a preload since error handling is different for preloads
|
|
2798
|
-
// and we don't want to swallow errors
|
|
2799
|
-
if (
|
|
2800
|
-
prevMatch.status === 'success' &&
|
|
2801
|
-
!innerLoadContext.sync &&
|
|
2802
|
-
!prevMatch.preload
|
|
2803
|
-
) {
|
|
2804
|
-
return this.getMatch(matchId)!
|
|
2805
|
-
}
|
|
2806
|
-
await prevMatch._nonReactive.loaderPromise
|
|
2807
|
-
const match = this.getMatch(matchId)!
|
|
2808
|
-
if (match.error) {
|
|
2809
|
-
this.handleRedirectAndNotFound(innerLoadContext, match, match.error)
|
|
2810
|
-
}
|
|
2811
|
-
} else {
|
|
2812
|
-
// This is where all of the stale-while-revalidate magic happens
|
|
2813
|
-
const age = Date.now() - this.getMatch(matchId)!.updatedAt
|
|
2814
|
-
|
|
2815
|
-
const preload = this.resolvePreload(innerLoadContext, matchId)
|
|
2816
|
-
|
|
2817
|
-
const staleAge = preload
|
|
2818
|
-
? (route.options.preloadStaleTime ??
|
|
2819
|
-
this.options.defaultPreloadStaleTime ??
|
|
2820
|
-
30_000) // 30 seconds for preloads by default
|
|
2821
|
-
: (route.options.staleTime ?? this.options.defaultStaleTime ?? 0)
|
|
2822
|
-
|
|
2823
|
-
const shouldReloadOption = route.options.shouldReload
|
|
2824
|
-
|
|
2825
|
-
// Default to reloading the route all the time
|
|
2826
|
-
// Allow shouldReload to get the last say,
|
|
2827
|
-
// if provided.
|
|
2828
|
-
const shouldReload =
|
|
2829
|
-
typeof shouldReloadOption === 'function'
|
|
2830
|
-
? shouldReloadOption(
|
|
2831
|
-
this.getLoaderContext(innerLoadContext, matchId, index, route),
|
|
2832
|
-
)
|
|
2833
|
-
: shouldReloadOption
|
|
2834
|
-
|
|
2835
|
-
const nextPreload =
|
|
2836
|
-
!!preload && !this.state.matches.some((d) => d.id === matchId)
|
|
2837
|
-
const match = this.getMatch(matchId)!
|
|
2838
|
-
match._nonReactive.loaderPromise = createControlledPromise<void>()
|
|
2839
|
-
if (nextPreload !== match.preload) {
|
|
2840
|
-
innerLoadContext.updateMatch(matchId, (prev) => ({
|
|
2841
|
-
...prev,
|
|
2842
|
-
preload: nextPreload,
|
|
2843
|
-
}))
|
|
2844
|
-
}
|
|
2845
|
-
|
|
2846
|
-
// If the route is successful and still fresh, just resolve
|
|
2847
|
-
const { status, invalid } = this.getMatch(matchId)!
|
|
2848
|
-
loaderShouldRunAsync =
|
|
2849
|
-
status === 'success' && (invalid || (shouldReload ?? age > staleAge))
|
|
2850
|
-
if (preload && route.options.preload === false) {
|
|
2851
|
-
// Do nothing
|
|
2852
|
-
} else if (loaderShouldRunAsync && !innerLoadContext.sync) {
|
|
2853
|
-
loaderIsRunningAsync = true
|
|
2854
|
-
;(async () => {
|
|
2855
|
-
try {
|
|
2856
|
-
await this.runLoader(innerLoadContext, matchId, index, route)
|
|
2857
|
-
const match = this.getMatch(matchId)!
|
|
2858
|
-
match._nonReactive.loaderPromise?.resolve()
|
|
2859
|
-
match._nonReactive.loadPromise?.resolve()
|
|
2860
|
-
match._nonReactive.loaderPromise = undefined
|
|
2861
|
-
} catch (err) {
|
|
2862
|
-
if (isRedirect(err)) {
|
|
2863
|
-
await this.navigate(err.options)
|
|
2864
|
-
}
|
|
2865
|
-
}
|
|
2866
|
-
})()
|
|
2867
|
-
} else if (
|
|
2868
|
-
status !== 'success' ||
|
|
2869
|
-
(loaderShouldRunAsync && innerLoadContext.sync)
|
|
2870
|
-
) {
|
|
2871
|
-
await this.runLoader(innerLoadContext, matchId, index, route)
|
|
2872
|
-
} else {
|
|
2873
|
-
// if the loader did not run, still update head.
|
|
2874
|
-
// reason: parent's beforeLoad may have changed the route context
|
|
2875
|
-
// and only now do we know the route context (and that the loader would not run)
|
|
2876
|
-
const headResult = this.executeHead(innerLoadContext, matchId, route)
|
|
2877
|
-
if (headResult) {
|
|
2878
|
-
const head = await headResult
|
|
2879
|
-
innerLoadContext.updateMatch(matchId, (prev) => ({
|
|
2880
|
-
...prev,
|
|
2881
|
-
...head,
|
|
2882
|
-
}))
|
|
2883
|
-
}
|
|
2884
|
-
}
|
|
2885
|
-
}
|
|
2886
|
-
const match = this.getMatch(matchId)!
|
|
2887
|
-
if (!loaderIsRunningAsync) {
|
|
2888
|
-
match._nonReactive.loaderPromise?.resolve()
|
|
2889
|
-
match._nonReactive.loadPromise?.resolve()
|
|
2890
|
-
}
|
|
2891
|
-
|
|
2892
|
-
clearTimeout(match._nonReactive.pendingTimeout)
|
|
2893
|
-
match._nonReactive.pendingTimeout = undefined
|
|
2894
|
-
if (!loaderIsRunningAsync) match._nonReactive.loaderPromise = undefined
|
|
2895
|
-
match._nonReactive.dehydrated = undefined
|
|
2896
|
-
const nextIsFetching = loaderIsRunningAsync ? match.isFetching : false
|
|
2897
|
-
if (nextIsFetching !== match.isFetching || match.invalid !== false) {
|
|
2898
|
-
innerLoadContext.updateMatch(matchId, (prev) => ({
|
|
2899
|
-
...prev,
|
|
2900
|
-
isFetching: nextIsFetching,
|
|
2901
|
-
invalid: false,
|
|
2902
|
-
}))
|
|
2903
|
-
}
|
|
2904
|
-
return this.getMatch(matchId)!
|
|
2905
|
-
}
|
|
2906
|
-
|
|
2907
|
-
loadMatches = async (baseContext: {
|
|
2908
|
-
location: ParsedLocation
|
|
2909
|
-
matches: Array<AnyRouteMatch>
|
|
2910
|
-
preload?: boolean
|
|
2911
|
-
onReady?: () => Promise<void>
|
|
2912
|
-
updateMatch?: UpdateMatchFn
|
|
2913
|
-
sync?: boolean
|
|
2914
|
-
}): Promise<Array<MakeRouteMatch>> => {
|
|
2915
|
-
const innerLoadContext = baseContext as InnerLoadContext
|
|
2916
|
-
innerLoadContext.updateMatch ??= this.updateMatch
|
|
2917
|
-
innerLoadContext.matchPromises = []
|
|
2918
|
-
|
|
2919
|
-
// make sure the pending component is immediately rendered when hydrating a match that is not SSRed
|
|
2920
|
-
// the pending component was already rendered on the server and we want to keep it shown on the client until minPendingMs is reached
|
|
2921
|
-
if (!this.isServer && this.state.matches.some((d) => d._forcePending)) {
|
|
2922
|
-
this.triggerOnReady(innerLoadContext)
|
|
2923
|
-
}
|
|
2924
|
-
|
|
2925
|
-
try {
|
|
2926
|
-
// Execute all beforeLoads one by one
|
|
2927
|
-
for (let i = 0; i < innerLoadContext.matches.length; i++) {
|
|
2928
|
-
const beforeLoad = this.handleBeforeLoad(innerLoadContext, i)
|
|
2929
|
-
if (isPromise(beforeLoad)) await beforeLoad
|
|
2930
|
-
}
|
|
2931
|
-
|
|
2932
|
-
// Execute all loaders in parallel
|
|
2933
|
-
const max =
|
|
2934
|
-
innerLoadContext.firstBadMatchIndex ?? innerLoadContext.matches.length
|
|
2935
|
-
for (let i = 0; i < max; i++) {
|
|
2936
|
-
innerLoadContext.matchPromises.push(
|
|
2937
|
-
this.loadRouteMatch(innerLoadContext, i),
|
|
2938
|
-
)
|
|
2939
|
-
}
|
|
2940
|
-
await Promise.all(innerLoadContext.matchPromises)
|
|
2941
|
-
|
|
2942
|
-
const readyPromise = this.triggerOnReady(innerLoadContext)
|
|
2943
|
-
if (isPromise(readyPromise)) await readyPromise
|
|
2944
|
-
} catch (err) {
|
|
2945
|
-
if (isNotFound(err) && !innerLoadContext.preload) {
|
|
2946
|
-
const readyPromise = this.triggerOnReady(innerLoadContext)
|
|
2947
|
-
if (isPromise(readyPromise)) await readyPromise
|
|
2948
|
-
throw err
|
|
2949
|
-
}
|
|
2950
|
-
if (isRedirect(err)) {
|
|
2951
|
-
throw err
|
|
2952
|
-
}
|
|
2953
|
-
}
|
|
2954
|
-
|
|
2955
|
-
return innerLoadContext.matches
|
|
2956
|
-
}
|
|
2957
|
-
|
|
2958
2091
|
invalidate: InvalidateFn<
|
|
2959
2092
|
RouterCore<
|
|
2960
2093
|
TRouteTree,
|
|
@@ -3048,47 +2181,7 @@ export class RouterCore<
|
|
|
3048
2181
|
this.clearCache({ filter })
|
|
3049
2182
|
}
|
|
3050
2183
|
|
|
3051
|
-
loadRouteChunk =
|
|
3052
|
-
if (!route._lazyLoaded && route._lazyPromise === undefined) {
|
|
3053
|
-
if (route.lazyFn) {
|
|
3054
|
-
route._lazyPromise = route.lazyFn().then((lazyRoute) => {
|
|
3055
|
-
// explicitly don't copy over the lazy route's id
|
|
3056
|
-
const { id: _id, ...options } = lazyRoute.options
|
|
3057
|
-
Object.assign(route.options, options)
|
|
3058
|
-
route._lazyLoaded = true
|
|
3059
|
-
route._lazyPromise = undefined // gc promise, we won't need it anymore
|
|
3060
|
-
})
|
|
3061
|
-
} else {
|
|
3062
|
-
route._lazyLoaded = true
|
|
3063
|
-
}
|
|
3064
|
-
}
|
|
3065
|
-
|
|
3066
|
-
// If for some reason lazy resolves more lazy components...
|
|
3067
|
-
// We'll wait for that before we attempt to preload the
|
|
3068
|
-
// components themselves.
|
|
3069
|
-
if (!route._componentsLoaded && route._componentsPromise === undefined) {
|
|
3070
|
-
const loadComponents = () => {
|
|
3071
|
-
const preloads = []
|
|
3072
|
-
for (const type of componentTypes) {
|
|
3073
|
-
const preload = (route.options[type] as any)?.preload
|
|
3074
|
-
if (preload) preloads.push(preload())
|
|
3075
|
-
}
|
|
3076
|
-
if (preloads.length)
|
|
3077
|
-
return Promise.all(preloads).then(() => {
|
|
3078
|
-
route._componentsLoaded = true
|
|
3079
|
-
route._componentsPromise = undefined // gc promise, we won't need it anymore
|
|
3080
|
-
})
|
|
3081
|
-
route._componentsLoaded = true
|
|
3082
|
-
route._componentsPromise = undefined // gc promise, we won't need it anymore
|
|
3083
|
-
return
|
|
3084
|
-
}
|
|
3085
|
-
route._componentsPromise = route._lazyPromise
|
|
3086
|
-
? route._lazyPromise.then(loadComponents)
|
|
3087
|
-
: loadComponents()
|
|
3088
|
-
}
|
|
3089
|
-
|
|
3090
|
-
return route._componentsPromise
|
|
3091
|
-
}
|
|
2184
|
+
loadRouteChunk = loadRouteChunk
|
|
3092
2185
|
|
|
3093
2186
|
preloadRoute: PreloadRouteFn<
|
|
3094
2187
|
TRouteTree,
|
|
@@ -3128,7 +2221,8 @@ export class RouterCore<
|
|
|
3128
2221
|
})
|
|
3129
2222
|
|
|
3130
2223
|
try {
|
|
3131
|
-
matches = await
|
|
2224
|
+
matches = await loadMatches({
|
|
2225
|
+
router: this,
|
|
3132
2226
|
matches,
|
|
3133
2227
|
location: next,
|
|
3134
2228
|
preload: true,
|
|
@@ -3226,58 +2320,6 @@ export class RouterCore<
|
|
|
3226
2320
|
|
|
3227
2321
|
serverSsr?: ServerSsr
|
|
3228
2322
|
|
|
3229
|
-
private _handleNotFound = (
|
|
3230
|
-
innerLoadContext: InnerLoadContext,
|
|
3231
|
-
err: NotFoundError,
|
|
3232
|
-
) => {
|
|
3233
|
-
// Find the route that should handle the not found error
|
|
3234
|
-
// First check if a specific route is requested to show the error
|
|
3235
|
-
const routeCursor = this.routesById[err.routeId ?? ''] ?? this.routeTree
|
|
3236
|
-
const matchesByRouteId: Record<string, AnyRouteMatch> = {}
|
|
3237
|
-
|
|
3238
|
-
// Setup routesByRouteId object for quick access
|
|
3239
|
-
for (const match of innerLoadContext.matches) {
|
|
3240
|
-
matchesByRouteId[match.routeId] = match
|
|
3241
|
-
}
|
|
3242
|
-
|
|
3243
|
-
// Ensure a NotFoundComponent exists on the route
|
|
3244
|
-
if (
|
|
3245
|
-
!routeCursor.options.notFoundComponent &&
|
|
3246
|
-
(this.options as any)?.defaultNotFoundComponent
|
|
3247
|
-
) {
|
|
3248
|
-
routeCursor.options.notFoundComponent = (
|
|
3249
|
-
this.options as any
|
|
3250
|
-
).defaultNotFoundComponent
|
|
3251
|
-
}
|
|
3252
|
-
|
|
3253
|
-
// Ensure we have a notFoundComponent
|
|
3254
|
-
invariant(
|
|
3255
|
-
routeCursor.options.notFoundComponent,
|
|
3256
|
-
'No notFoundComponent found. Please set a notFoundComponent on your route or provide a defaultNotFoundComponent to the router.',
|
|
3257
|
-
)
|
|
3258
|
-
|
|
3259
|
-
// Find the match for this route
|
|
3260
|
-
const matchForRoute = matchesByRouteId[routeCursor.id]
|
|
3261
|
-
|
|
3262
|
-
invariant(
|
|
3263
|
-
matchForRoute,
|
|
3264
|
-
'Could not find match for route: ' + routeCursor.id,
|
|
3265
|
-
)
|
|
3266
|
-
|
|
3267
|
-
// Assign the error to the match - using non-null assertion since we've checked with invariant
|
|
3268
|
-
innerLoadContext.updateMatch(matchForRoute.id, (prev) => ({
|
|
3269
|
-
...prev,
|
|
3270
|
-
status: 'notFound',
|
|
3271
|
-
error: err,
|
|
3272
|
-
isFetching: false,
|
|
3273
|
-
}))
|
|
3274
|
-
|
|
3275
|
-
if ((err as any).routerCode === 'BEFORE_LOAD' && routeCursor.parentRoute) {
|
|
3276
|
-
err.routeId = routeCursor.parentRoute.id
|
|
3277
|
-
this._handleNotFound(innerLoadContext, err)
|
|
3278
|
-
}
|
|
3279
|
-
}
|
|
3280
|
-
|
|
3281
2323
|
hasNotFoundMatch = () => {
|
|
3282
2324
|
return this.__store.state.matches.some(
|
|
3283
2325
|
(d) => d.status === 'notFound' || d.globalNotFound,
|
|
@@ -3289,16 +2331,6 @@ export class SearchParamError extends Error {}
|
|
|
3289
2331
|
|
|
3290
2332
|
export class PathParamError extends Error {}
|
|
3291
2333
|
|
|
3292
|
-
function makeMaybe<TValue, TError>(
|
|
3293
|
-
value: TValue,
|
|
3294
|
-
error: TError,
|
|
3295
|
-
): { status: 'success'; value: TValue } | { status: 'error'; error: TError } {
|
|
3296
|
-
if (error) {
|
|
3297
|
-
return { status: 'error' as const, error }
|
|
3298
|
-
}
|
|
3299
|
-
return { status: 'success' as const, value }
|
|
3300
|
-
}
|
|
3301
|
-
|
|
3302
2334
|
const normalize = (str: string) =>
|
|
3303
2335
|
str.endsWith('/') && str.length > 1 ? str.slice(0, -1) : str
|
|
3304
2336
|
function comparePaths(a: string, b: string) {
|
|
@@ -3365,22 +2397,6 @@ function validateSearch(validateSearch: AnyValidator, input: unknown): unknown {
|
|
|
3365
2397
|
return {}
|
|
3366
2398
|
}
|
|
3367
2399
|
|
|
3368
|
-
export const componentTypes = [
|
|
3369
|
-
'component',
|
|
3370
|
-
'errorComponent',
|
|
3371
|
-
'pendingComponent',
|
|
3372
|
-
'notFoundComponent',
|
|
3373
|
-
] as const
|
|
3374
|
-
|
|
3375
|
-
function routeNeedsPreload(route: AnyRoute) {
|
|
3376
|
-
for (const componentType of componentTypes) {
|
|
3377
|
-
if ((route.options[componentType] as any)?.preload) {
|
|
3378
|
-
return true
|
|
3379
|
-
}
|
|
3380
|
-
}
|
|
3381
|
-
return false
|
|
3382
|
-
}
|
|
3383
|
-
|
|
3384
2400
|
interface RouteLike {
|
|
3385
2401
|
id: string
|
|
3386
2402
|
isRoot?: boolean
|