@tanstack/react-router 1.28.1 → 1.28.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/Matches.cjs +45 -23
- package/dist/cjs/Matches.cjs.map +1 -1
- package/dist/cjs/Matches.d.cts +5 -6
- package/dist/cjs/fileRoute.d.cts +4 -4
- package/dist/cjs/index.cjs +0 -1
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +1 -1
- package/dist/cjs/link.cjs.map +1 -1
- package/dist/cjs/link.d.cts +2 -0
- package/dist/cjs/redirects.cjs.map +1 -1
- package/dist/cjs/redirects.d.cts +3 -1
- package/dist/cjs/route.cjs.map +1 -1
- package/dist/cjs/route.d.cts +2 -2
- package/dist/cjs/router.cjs +374 -327
- package/dist/cjs/router.cjs.map +1 -1
- package/dist/cjs/router.d.cts +2 -2
- package/dist/cjs/utils.cjs +20 -2
- package/dist/cjs/utils.cjs.map +1 -1
- package/dist/cjs/utils.d.cts +6 -1
- package/dist/esm/Matches.d.ts +5 -6
- package/dist/esm/Matches.js +46 -24
- package/dist/esm/Matches.js.map +1 -1
- package/dist/esm/fileRoute.d.ts +4 -4
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/index.js +1 -2
- package/dist/esm/link.d.ts +2 -0
- package/dist/esm/link.js.map +1 -1
- package/dist/esm/redirects.d.ts +3 -1
- package/dist/esm/redirects.js.map +1 -1
- package/dist/esm/route.d.ts +2 -2
- package/dist/esm/route.js.map +1 -1
- package/dist/esm/router.d.ts +2 -2
- package/dist/esm/router.js +375 -328
- package/dist/esm/router.js.map +1 -1
- package/dist/esm/utils.d.ts +6 -1
- package/dist/esm/utils.js +20 -2
- package/dist/esm/utils.js.map +1 -1
- package/package.json +4 -2
- package/src/Matches.tsx +73 -35
- package/src/index.tsx +0 -1
- package/src/link.tsx +2 -0
- package/src/redirects.ts +4 -2
- package/src/route.ts +2 -7
- package/src/router.ts +498 -426
- package/src/utils.ts +31 -2
package/src/router.ts
CHANGED
|
@@ -5,10 +5,10 @@ import warning from 'tiny-warning'
|
|
|
5
5
|
import { rootRouteId } from './route'
|
|
6
6
|
import { defaultParseSearch, defaultStringifySearch } from './searchParams'
|
|
7
7
|
import {
|
|
8
|
+
createControlledPromise,
|
|
8
9
|
deepEqual,
|
|
9
10
|
escapeJSON,
|
|
10
11
|
functionalUpdate,
|
|
11
|
-
isServer,
|
|
12
12
|
last,
|
|
13
13
|
pick,
|
|
14
14
|
replaceEqualDeep,
|
|
@@ -171,7 +171,6 @@ export interface RouterState<TRouteTree extends AnyRoute = AnyRoute> {
|
|
|
171
171
|
cachedMatches: Array<RouteMatch<TRouteTree>>
|
|
172
172
|
location: ParsedLocation<FullSearchSchema<TRouteTree>>
|
|
173
173
|
resolvedLocation: ParsedLocation<FullSearchSchema<TRouteTree>>
|
|
174
|
-
lastUpdated: number
|
|
175
174
|
statusCode: number
|
|
176
175
|
redirect?: ResolvedRedirect
|
|
177
176
|
}
|
|
@@ -193,6 +192,7 @@ export interface BuildNextOptions {
|
|
|
193
192
|
unmaskOnReload?: boolean
|
|
194
193
|
}
|
|
195
194
|
from?: string
|
|
195
|
+
_fromLocation?: ParsedLocation
|
|
196
196
|
}
|
|
197
197
|
|
|
198
198
|
export interface DehydratedRouterState {
|
|
@@ -321,6 +321,8 @@ export class Router<
|
|
|
321
321
|
}
|
|
322
322
|
}
|
|
323
323
|
|
|
324
|
+
isServer = typeof document === 'undefined'
|
|
325
|
+
|
|
324
326
|
// These are default implementations that can optionally be overridden
|
|
325
327
|
// by the router provider once rendered. We provide these so that the
|
|
326
328
|
// router can be used in a non-react environment if necessary
|
|
@@ -609,7 +611,7 @@ export class Router<
|
|
|
609
611
|
matchRoutes = <TRouteTree extends AnyRoute>(
|
|
610
612
|
pathname: string,
|
|
611
613
|
locationSearch: AnySearchSchema,
|
|
612
|
-
opts?: { preload?: boolean; throwOnError?: boolean
|
|
614
|
+
opts?: { preload?: boolean; throwOnError?: boolean },
|
|
613
615
|
): Array<RouteMatch<TRouteTree>> => {
|
|
614
616
|
let routeParams: Record<string, string> = {}
|
|
615
617
|
|
|
@@ -784,38 +786,68 @@ export class Router<
|
|
|
784
786
|
? 'stay'
|
|
785
787
|
: 'enter'
|
|
786
788
|
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
789
|
+
let match: AnyRouteMatch
|
|
790
|
+
|
|
791
|
+
if (existingMatch) {
|
|
792
|
+
match = {
|
|
793
|
+
...existingMatch,
|
|
794
|
+
cause,
|
|
795
|
+
params: routeParams,
|
|
796
|
+
}
|
|
797
|
+
} else {
|
|
798
|
+
const status =
|
|
799
|
+
route.options.loader || route.options.beforeLoad
|
|
800
|
+
? 'pending'
|
|
801
|
+
: 'success'
|
|
802
|
+
|
|
803
|
+
const loadPromise = createControlledPromise<void>()
|
|
804
|
+
|
|
805
|
+
// If it's already a success, resolve the load promise
|
|
806
|
+
if (status === 'success') {
|
|
807
|
+
loadPromise.resolve()
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
match = {
|
|
811
|
+
id: matchId,
|
|
812
|
+
routeId: route.id,
|
|
813
|
+
params: routeParams,
|
|
814
|
+
pathname: joinPaths([this.basepath, interpolatedPath]),
|
|
815
|
+
updatedAt: Date.now(),
|
|
816
|
+
search: {} as any,
|
|
817
|
+
searchError: undefined,
|
|
818
|
+
status: 'pending',
|
|
819
|
+
isFetching: false,
|
|
820
|
+
error: undefined,
|
|
821
|
+
paramsError: parseErrors[index],
|
|
822
|
+
loaderPromise: Promise.resolve(),
|
|
823
|
+
loadPromise,
|
|
824
|
+
routeContext: undefined!,
|
|
825
|
+
context: undefined!,
|
|
826
|
+
abortController: new AbortController(),
|
|
827
|
+
fetchCount: 0,
|
|
828
|
+
cause,
|
|
829
|
+
loaderDeps,
|
|
830
|
+
invalid: false,
|
|
831
|
+
preload: false,
|
|
832
|
+
links: route.options.links?.(),
|
|
833
|
+
scripts: route.options.scripts?.(),
|
|
834
|
+
staticData: route.options.staticData || {},
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
// If it's already a success, update the meta and headers
|
|
839
|
+
// These may get updated again if the match is refreshed
|
|
840
|
+
// due to being stale
|
|
841
|
+
if (match.status === 'success') {
|
|
842
|
+
match.meta = route.options.meta?.({
|
|
843
|
+
params: match.params,
|
|
844
|
+
loaderData: match.loaderData,
|
|
845
|
+
})
|
|
846
|
+
|
|
847
|
+
match.headers = route.options.headers?.({
|
|
848
|
+
loaderData: match.loaderData,
|
|
849
|
+
})
|
|
850
|
+
}
|
|
819
851
|
|
|
820
852
|
if (!opts?.preload) {
|
|
821
853
|
// If we have a global not found, mark the right match as global not found
|
|
@@ -851,28 +883,13 @@ export class Router<
|
|
|
851
883
|
} = {},
|
|
852
884
|
matches?: Array<AnyRouteMatch>,
|
|
853
885
|
): ParsedLocation => {
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
// href: dest.href,
|
|
862
|
-
// unmaskOnReload: dest.unmaskOnReload,
|
|
863
|
-
// }
|
|
864
|
-
// }
|
|
865
|
-
|
|
866
|
-
const relevantMatches = this.state.pendingMatches || this.state.matches
|
|
867
|
-
const fromSearch =
|
|
868
|
-
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
869
|
-
relevantMatches[relevantMatches.length - 1]?.search ||
|
|
870
|
-
this.latestLocation.search
|
|
871
|
-
|
|
872
|
-
const fromMatches = this.matchRoutes(
|
|
873
|
-
this.latestLocation.pathname,
|
|
874
|
-
fromSearch,
|
|
875
|
-
)
|
|
886
|
+
const fromPath = dest.from || this.latestLocation.pathname
|
|
887
|
+
let fromSearch = dest._fromLocation?.search || this.latestLocation.search
|
|
888
|
+
|
|
889
|
+
const fromMatches = this.matchRoutes(fromPath, fromSearch)
|
|
890
|
+
|
|
891
|
+
fromSearch = last(fromMatches)?.search || this.latestLocation.search
|
|
892
|
+
|
|
876
893
|
const stayingMatches = matches?.filter((d) =>
|
|
877
894
|
fromMatches.find((e) => e.routeId === d.routeId),
|
|
878
895
|
)
|
|
@@ -1162,12 +1179,14 @@ export class Router<
|
|
|
1162
1179
|
let latestPromise
|
|
1163
1180
|
let firstBadMatchIndex: number | undefined
|
|
1164
1181
|
|
|
1165
|
-
const updateMatch = (
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1182
|
+
const updateMatch = (
|
|
1183
|
+
id: string,
|
|
1184
|
+
updater: (match: AnyRouteMatch) => AnyRouteMatch,
|
|
1185
|
+
opts?: { remove?: boolean },
|
|
1186
|
+
) => {
|
|
1187
|
+
let updated!: AnyRouteMatch
|
|
1188
|
+
const isPending = this.state.pendingMatches?.find((d) => d.id === id)
|
|
1189
|
+
const isMatched = this.state.matches.find((d) => d.id === id)
|
|
1171
1190
|
|
|
1172
1191
|
const matchesKey = isPending
|
|
1173
1192
|
? 'pendingMatches'
|
|
@@ -1178,14 +1197,18 @@ export class Router<
|
|
|
1178
1197
|
this.__store.setState((s) => ({
|
|
1179
1198
|
...s,
|
|
1180
1199
|
[matchesKey]: opts?.remove
|
|
1181
|
-
? s[matchesKey]?.filter((d) => d.id !==
|
|
1182
|
-
: s[matchesKey]?.map((d) =>
|
|
1200
|
+
? s[matchesKey]?.filter((d) => d.id !== id)
|
|
1201
|
+
: s[matchesKey]?.map((d) =>
|
|
1202
|
+
d.id === id ? (updated = updater(d)) : d,
|
|
1203
|
+
),
|
|
1183
1204
|
}))
|
|
1205
|
+
|
|
1206
|
+
return updated
|
|
1184
1207
|
}
|
|
1185
1208
|
|
|
1186
1209
|
const handleMatchSpecialError = (match: AnyRouteMatch, err: any) => {
|
|
1187
|
-
match
|
|
1188
|
-
...
|
|
1210
|
+
updateMatch(match.id, (prev) => ({
|
|
1211
|
+
...prev,
|
|
1189
1212
|
status: isRedirect(err)
|
|
1190
1213
|
? 'redirected'
|
|
1191
1214
|
: isNotFound(err)
|
|
@@ -1193,9 +1216,7 @@ export class Router<
|
|
|
1193
1216
|
: 'error',
|
|
1194
1217
|
isFetching: false,
|
|
1195
1218
|
error: err,
|
|
1196
|
-
}
|
|
1197
|
-
|
|
1198
|
-
updateMatch(match)
|
|
1219
|
+
}))
|
|
1199
1220
|
|
|
1200
1221
|
if (!err.routeId) {
|
|
1201
1222
|
err.routeId = match.routeId
|
|
@@ -1204,349 +1225,394 @@ export class Router<
|
|
|
1204
1225
|
throw err
|
|
1205
1226
|
}
|
|
1206
1227
|
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
err = errorHandlerErr
|
|
1226
|
-
|
|
1227
|
-
if (isRedirect(err) || isNotFound(err)) {
|
|
1228
|
-
handleMatchSpecialError(match, errorHandlerErr)
|
|
1229
|
-
}
|
|
1230
|
-
}
|
|
1228
|
+
try {
|
|
1229
|
+
await new Promise<void>((resolveAll, rejectAll) => {
|
|
1230
|
+
;(async () => {
|
|
1231
|
+
try {
|
|
1232
|
+
// Check each match middleware to see if the route can be accessed
|
|
1233
|
+
// eslint-disable-next-line prefer-const
|
|
1234
|
+
for (let [index, match] of matches.entries()) {
|
|
1235
|
+
const parentMatch = matches[index - 1]
|
|
1236
|
+
const route = this.looseRoutesById[match.routeId]!
|
|
1237
|
+
const abortController = new AbortController()
|
|
1238
|
+
|
|
1239
|
+
const handleSerialError = (err: any, code: string) => {
|
|
1240
|
+
err.routerCode = code
|
|
1241
|
+
firstBadMatchIndex = firstBadMatchIndex ?? index
|
|
1242
|
+
|
|
1243
|
+
if (isRedirect(err) || isNotFound(err)) {
|
|
1244
|
+
handleMatchSpecialError(match, err)
|
|
1245
|
+
}
|
|
1231
1246
|
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
updatedAt: Date.now(),
|
|
1237
|
-
abortController: new AbortController(),
|
|
1238
|
-
}
|
|
1239
|
-
}
|
|
1247
|
+
try {
|
|
1248
|
+
route.options.onError?.(err)
|
|
1249
|
+
} catch (errorHandlerErr) {
|
|
1250
|
+
err = errorHandlerErr
|
|
1240
1251
|
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1252
|
+
if (isRedirect(err) || isNotFound(err)) {
|
|
1253
|
+
handleMatchSpecialError(match, errorHandlerErr)
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1244
1256
|
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1257
|
+
matches[index] = match = {
|
|
1258
|
+
...match,
|
|
1259
|
+
error: err,
|
|
1260
|
+
status: 'error',
|
|
1261
|
+
updatedAt: Date.now(),
|
|
1262
|
+
abortController: new AbortController(),
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1248
1265
|
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1266
|
+
if (match.paramsError) {
|
|
1267
|
+
handleSerialError(match.paramsError, 'PARSE_PARAMS')
|
|
1268
|
+
}
|
|
1252
1269
|
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
const pendingMs =
|
|
1257
|
-
route.options.pendingMs ?? this.options.defaultPendingMs
|
|
1258
|
-
const pendingPromise =
|
|
1259
|
-
typeof pendingMs === 'number' && pendingMs <= 0
|
|
1260
|
-
? Promise.resolve()
|
|
1261
|
-
: new Promise<void>((r) => setTimeout(r, pendingMs))
|
|
1262
|
-
|
|
1263
|
-
const beforeLoadContext =
|
|
1264
|
-
(await route.options.beforeLoad?.({
|
|
1265
|
-
search: match.search,
|
|
1266
|
-
abortController,
|
|
1267
|
-
params: match.params,
|
|
1268
|
-
preload: !!preload,
|
|
1269
|
-
context: parentContext,
|
|
1270
|
-
location,
|
|
1271
|
-
navigate: (opts: any) =>
|
|
1272
|
-
this.navigate({ ...opts, from: match.pathname }),
|
|
1273
|
-
buildLocation: this.buildLocation,
|
|
1274
|
-
cause: preload ? 'preload' : match.cause,
|
|
1275
|
-
})) ?? ({} as any)
|
|
1276
|
-
|
|
1277
|
-
if (isRedirect(beforeLoadContext) || isNotFound(beforeLoadContext)) {
|
|
1278
|
-
handleSerialError(beforeLoadContext, 'BEFORE_LOAD')
|
|
1279
|
-
}
|
|
1270
|
+
if (match.searchError) {
|
|
1271
|
+
handleSerialError(match.searchError, 'VALIDATE_SEARCH')
|
|
1272
|
+
}
|
|
1280
1273
|
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
}
|
|
1274
|
+
// if (match.globalNotFound && !preload) {
|
|
1275
|
+
// handleSerialError(notFound({ _global: true }), 'NOT_FOUND')
|
|
1276
|
+
// }
|
|
1285
1277
|
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1278
|
+
try {
|
|
1279
|
+
const parentContext =
|
|
1280
|
+
parentMatch?.context ?? this.options.context ?? {}
|
|
1281
|
+
|
|
1282
|
+
const pendingMs =
|
|
1283
|
+
route.options.pendingMs ?? this.options.defaultPendingMs
|
|
1284
|
+
const pendingPromise =
|
|
1285
|
+
typeof pendingMs !== 'number' || pendingMs <= 0
|
|
1286
|
+
? Promise.resolve()
|
|
1287
|
+
: new Promise<void>((r) => {
|
|
1288
|
+
if (pendingMs !== Infinity) setTimeout(r, pendingMs)
|
|
1289
|
+
})
|
|
1290
|
+
|
|
1291
|
+
const shouldPending =
|
|
1292
|
+
!this.isServer &&
|
|
1293
|
+
!preload &&
|
|
1294
|
+
(route.options.loader || route.options.beforeLoad) &&
|
|
1295
|
+
typeof pendingMs === 'number' &&
|
|
1296
|
+
(route.options.pendingComponent ??
|
|
1297
|
+
this.options.defaultPendingComponent)
|
|
1298
|
+
|
|
1299
|
+
if (shouldPending) {
|
|
1300
|
+
// If we might show a pending component, we need to wait for the
|
|
1301
|
+
// pending promise to resolve before we start showing that state
|
|
1302
|
+
pendingPromise.then(async () => {
|
|
1303
|
+
if ((latestPromise = checkLatest())) return latestPromise
|
|
1304
|
+
// Update the match and prematurely resolve the loadMatches promise so that
|
|
1305
|
+
// the pending component can start rendering
|
|
1306
|
+
resolveAll()
|
|
1307
|
+
})
|
|
1308
|
+
}
|
|
1298
1309
|
|
|
1299
|
-
|
|
1300
|
-
|
|
1310
|
+
const beforeLoadContext =
|
|
1311
|
+
(await route.options.beforeLoad?.({
|
|
1312
|
+
search: match.search,
|
|
1313
|
+
abortController,
|
|
1314
|
+
params: match.params,
|
|
1315
|
+
preload: !!preload,
|
|
1316
|
+
context: parentContext,
|
|
1317
|
+
location,
|
|
1318
|
+
navigate: (opts: any) =>
|
|
1319
|
+
this.navigate({ ...opts, from: match.pathname }),
|
|
1320
|
+
buildLocation: this.buildLocation,
|
|
1321
|
+
cause: preload ? 'preload' : match.cause,
|
|
1322
|
+
})) ?? ({} as any)
|
|
1323
|
+
|
|
1324
|
+
if (
|
|
1325
|
+
isRedirect(beforeLoadContext) ||
|
|
1326
|
+
isNotFound(beforeLoadContext)
|
|
1327
|
+
) {
|
|
1328
|
+
handleSerialError(beforeLoadContext, 'BEFORE_LOAD')
|
|
1329
|
+
}
|
|
1301
1330
|
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
const parentMatchPromise = matchPromises[index - 1]
|
|
1307
|
-
const route = this.looseRoutesById[match.routeId]!
|
|
1331
|
+
const context = {
|
|
1332
|
+
...parentContext,
|
|
1333
|
+
...beforeLoadContext,
|
|
1334
|
+
}
|
|
1308
1335
|
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1336
|
+
matches[index] = match = {
|
|
1337
|
+
...match,
|
|
1338
|
+
routeContext: replaceEqualDeep(
|
|
1339
|
+
match.routeContext,
|
|
1340
|
+
beforeLoadContext,
|
|
1341
|
+
),
|
|
1342
|
+
context: replaceEqualDeep(match.context, context),
|
|
1343
|
+
abortController,
|
|
1344
|
+
}
|
|
1345
|
+
} catch (err) {
|
|
1346
|
+
handleSerialError(err, 'BEFORE_LOAD')
|
|
1347
|
+
break
|
|
1348
|
+
}
|
|
1312
1349
|
}
|
|
1313
|
-
}
|
|
1314
|
-
|
|
1315
|
-
let loadPromise: Promise<void> | undefined
|
|
1316
|
-
|
|
1317
|
-
matches[index] = match = {
|
|
1318
|
-
...match,
|
|
1319
|
-
showPending: false,
|
|
1320
|
-
}
|
|
1321
1350
|
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
route.options.pendingMs ?? this.options.defaultPendingMs
|
|
1325
|
-
const pendingMinMs =
|
|
1326
|
-
route.options.pendingMinMs ?? this.options.defaultPendingMinMs
|
|
1327
|
-
|
|
1328
|
-
const loaderContext: LoaderFnContext = {
|
|
1329
|
-
params: match.params,
|
|
1330
|
-
deps: match.loaderDeps,
|
|
1331
|
-
preload: !!preload,
|
|
1332
|
-
parentMatchPromise,
|
|
1333
|
-
abortController: match.abortController,
|
|
1334
|
-
context: match.context,
|
|
1335
|
-
location,
|
|
1336
|
-
navigate: (opts) =>
|
|
1337
|
-
this.navigate({ ...opts, from: match.pathname } as any),
|
|
1338
|
-
cause: preload ? 'preload' : match.cause,
|
|
1339
|
-
route,
|
|
1340
|
-
}
|
|
1351
|
+
const validResolvedMatches = matches.slice(0, firstBadMatchIndex)
|
|
1352
|
+
const matchPromises: Array<Promise<any>> = []
|
|
1341
1353
|
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
} else {
|
|
1347
|
-
// If the user doesn't want the route to reload, just
|
|
1348
|
-
// resolve with the existing loader data
|
|
1354
|
+
await Promise.all(
|
|
1355
|
+
validResolvedMatches.map(async (match, index) => {
|
|
1356
|
+
const parentMatchPromise = matchPromises[index - 1]
|
|
1357
|
+
const route = this.looseRoutesById[match.routeId]!
|
|
1349
1358
|
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1359
|
+
const handleError = (err: any) => {
|
|
1360
|
+
if (isRedirect(err) || isNotFound(err)) {
|
|
1361
|
+
handleMatchSpecialError(match, err)
|
|
1362
|
+
}
|
|
1363
|
+
}
|
|
1353
1364
|
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1365
|
+
const loaderContext: LoaderFnContext = {
|
|
1366
|
+
params: match.params,
|
|
1367
|
+
deps: match.loaderDeps,
|
|
1368
|
+
preload: !!preload,
|
|
1369
|
+
parentMatchPromise,
|
|
1370
|
+
abortController: match.abortController,
|
|
1371
|
+
context: match.context,
|
|
1372
|
+
location,
|
|
1373
|
+
navigate: (opts) =>
|
|
1374
|
+
this.navigate({ ...opts, from: match.pathname } as any),
|
|
1375
|
+
cause: preload ? 'preload' : match.cause,
|
|
1376
|
+
route,
|
|
1359
1377
|
}
|
|
1360
1378
|
|
|
1361
|
-
const
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1379
|
+
const fetch = async () => {
|
|
1380
|
+
const existing = getRouteMatch(this.state, match.id)!
|
|
1381
|
+
let lazyPromise = Promise.resolve()
|
|
1382
|
+
let componentsPromise = Promise.resolve() as Promise<any>
|
|
1383
|
+
let loaderPromise = existing.loaderPromise
|
|
1384
|
+
let loadPromise = existing.loadPromise
|
|
1385
|
+
|
|
1386
|
+
// If the Matches component rendered
|
|
1387
|
+
// the pending component and needs to show it for
|
|
1388
|
+
// a minimum duration, we''ll wait for it to resolve
|
|
1389
|
+
// before committing to the match and resolving
|
|
1390
|
+
// the loadPromise
|
|
1391
|
+
const potentialPendingMinPromise = async () => {
|
|
1392
|
+
const latestMatch = getRouteMatch(this.state, match.id)
|
|
1393
|
+
|
|
1394
|
+
if (latestMatch?.minPendingPromise) {
|
|
1395
|
+
await latestMatch.minPendingPromise
|
|
1396
|
+
|
|
1397
|
+
if ((latestPromise = checkLatest()))
|
|
1398
|
+
return await latestPromise
|
|
1399
|
+
|
|
1400
|
+
updateMatch(latestMatch.id, (prev) => ({
|
|
1401
|
+
...prev,
|
|
1402
|
+
minPendingPromise: undefined,
|
|
1403
|
+
}))
|
|
1404
|
+
}
|
|
1405
|
+
}
|
|
1380
1406
|
|
|
1381
|
-
|
|
1382
|
-
|
|
1407
|
+
try {
|
|
1408
|
+
if (!match.isFetching) {
|
|
1409
|
+
// If the user doesn't want the route to reload, just
|
|
1410
|
+
// resolve with the existing loader data
|
|
1383
1411
|
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
lazyPromise,
|
|
1388
|
-
]).then((d) => d[1])
|
|
1389
|
-
}
|
|
1412
|
+
// if (match.fetchCount && match.status === 'success') {
|
|
1413
|
+
// resolve()
|
|
1414
|
+
// }
|
|
1390
1415
|
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1416
|
+
// Otherwise, load the route
|
|
1417
|
+
matches[index] = match = {
|
|
1418
|
+
...match,
|
|
1419
|
+
isFetching: true,
|
|
1420
|
+
fetchCount: match.fetchCount + 1,
|
|
1421
|
+
}
|
|
1395
1422
|
|
|
1396
|
-
|
|
1423
|
+
lazyPromise =
|
|
1424
|
+
route.lazyFn?.().then((lazyRoute) => {
|
|
1425
|
+
Object.assign(route.options, lazyRoute.options)
|
|
1426
|
+
}) || Promise.resolve()
|
|
1427
|
+
|
|
1428
|
+
// If for some reason lazy resolves more lazy components...
|
|
1429
|
+
// We'll wait for that before pre attempt to preload any
|
|
1430
|
+
// components themselves.
|
|
1431
|
+
componentsPromise = lazyPromise.then(() =>
|
|
1432
|
+
Promise.all(
|
|
1433
|
+
componentTypes.map(async (type) => {
|
|
1434
|
+
const component = route.options[type]
|
|
1435
|
+
|
|
1436
|
+
if ((component as any)?.preload) {
|
|
1437
|
+
await (component as any).preload()
|
|
1438
|
+
}
|
|
1439
|
+
}),
|
|
1440
|
+
),
|
|
1441
|
+
)
|
|
1442
|
+
|
|
1443
|
+
// Lazy option can modify the route options,
|
|
1444
|
+
// so we need to wait for it to resolve before
|
|
1445
|
+
// we can use the options
|
|
1446
|
+
await lazyPromise
|
|
1447
|
+
|
|
1448
|
+
if ((latestPromise = checkLatest()))
|
|
1449
|
+
return await latestPromise
|
|
1450
|
+
|
|
1451
|
+
// Kick off the loader!
|
|
1452
|
+
loaderPromise = route.options.loader?.(loaderContext)
|
|
1453
|
+
|
|
1454
|
+
const previousResolve = loadPromise.resolve
|
|
1455
|
+
// Create a new one
|
|
1456
|
+
loadPromise = createControlledPromise<void>(
|
|
1457
|
+
// Resolve the old when we we resolve the new one
|
|
1458
|
+
previousResolve,
|
|
1459
|
+
)
|
|
1460
|
+
}
|
|
1461
|
+
|
|
1462
|
+
matches[index] = match = updateMatch(match.id, (prev) => ({
|
|
1463
|
+
...prev,
|
|
1464
|
+
loaderPromise,
|
|
1465
|
+
loadPromise,
|
|
1466
|
+
}))
|
|
1467
|
+
|
|
1468
|
+
const loaderData = await loaderPromise
|
|
1469
|
+
if ((latestPromise = checkLatest()))
|
|
1470
|
+
return await latestPromise
|
|
1471
|
+
|
|
1472
|
+
handleError(loaderData)
|
|
1473
|
+
|
|
1474
|
+
if ((latestPromise = checkLatest()))
|
|
1475
|
+
return await latestPromise
|
|
1476
|
+
|
|
1477
|
+
await potentialPendingMinPromise()
|
|
1478
|
+
if ((latestPromise = checkLatest()))
|
|
1479
|
+
return await latestPromise
|
|
1480
|
+
|
|
1481
|
+
const meta = route.options.meta?.({
|
|
1482
|
+
params: match.params,
|
|
1483
|
+
loaderData,
|
|
1484
|
+
})
|
|
1485
|
+
|
|
1486
|
+
const headers = route.options.headers?.({
|
|
1487
|
+
loaderData,
|
|
1488
|
+
})
|
|
1489
|
+
|
|
1490
|
+
matches[index] = match = updateMatch(match.id, (prev) => ({
|
|
1491
|
+
...prev,
|
|
1492
|
+
error: undefined,
|
|
1493
|
+
status: 'success',
|
|
1494
|
+
isFetching: false,
|
|
1495
|
+
updatedAt: Date.now(),
|
|
1496
|
+
loaderData,
|
|
1497
|
+
meta,
|
|
1498
|
+
headers,
|
|
1499
|
+
}))
|
|
1500
|
+
} catch (e) {
|
|
1501
|
+
let error = e
|
|
1502
|
+
if ((latestPromise = checkLatest()))
|
|
1503
|
+
return await latestPromise
|
|
1504
|
+
|
|
1505
|
+
await potentialPendingMinPromise()
|
|
1506
|
+
if ((latestPromise = checkLatest()))
|
|
1507
|
+
return await latestPromise
|
|
1508
|
+
|
|
1509
|
+
handleError(e)
|
|
1510
|
+
|
|
1511
|
+
try {
|
|
1512
|
+
route.options.onError?.(e)
|
|
1513
|
+
} catch (onErrorError) {
|
|
1514
|
+
error = onErrorError
|
|
1515
|
+
handleError(onErrorError)
|
|
1516
|
+
}
|
|
1517
|
+
|
|
1518
|
+
matches[index] = match = updateMatch(match.id, (prev) => ({
|
|
1519
|
+
...prev,
|
|
1520
|
+
error,
|
|
1521
|
+
status: 'error',
|
|
1522
|
+
isFetching: false,
|
|
1523
|
+
}))
|
|
1524
|
+
}
|
|
1397
1525
|
|
|
1398
|
-
|
|
1399
|
-
|
|
1526
|
+
// Last but not least, wait for the the component
|
|
1527
|
+
// to be preloaded before we resolve the match
|
|
1528
|
+
await componentsPromise
|
|
1400
1529
|
|
|
1401
|
-
|
|
1530
|
+
if ((latestPromise = checkLatest()))
|
|
1531
|
+
return await latestPromise
|
|
1402
1532
|
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
}
|
|
1533
|
+
loadPromise.resolve()
|
|
1534
|
+
}
|
|
1406
1535
|
|
|
1407
|
-
|
|
1536
|
+
// This is where all of the stale-while-revalidate magic happens
|
|
1537
|
+
const age = Date.now() - match.updatedAt
|
|
1408
1538
|
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
}),
|
|
1417
|
-
])
|
|
1418
|
-
|
|
1419
|
-
matches[index] = match = {
|
|
1420
|
-
...match,
|
|
1421
|
-
error: undefined,
|
|
1422
|
-
status: 'success',
|
|
1423
|
-
isFetching: false,
|
|
1424
|
-
updatedAt: Date.now(),
|
|
1425
|
-
loaderData,
|
|
1426
|
-
loadPromise: undefined,
|
|
1427
|
-
meta,
|
|
1428
|
-
headers,
|
|
1429
|
-
}
|
|
1430
|
-
} catch (e) {
|
|
1431
|
-
let error = e
|
|
1432
|
-
if ((latestPromise = checkLatest())) return await latestPromise
|
|
1539
|
+
const staleAge = preload
|
|
1540
|
+
? route.options.preloadStaleTime ??
|
|
1541
|
+
this.options.defaultPreloadStaleTime ??
|
|
1542
|
+
30_000 // 30 seconds for preloads by default
|
|
1543
|
+
: route.options.staleTime ??
|
|
1544
|
+
this.options.defaultStaleTime ??
|
|
1545
|
+
0
|
|
1433
1546
|
|
|
1434
|
-
|
|
1547
|
+
const shouldReloadOption = route.options.shouldReload
|
|
1435
1548
|
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1549
|
+
// Default to reloading the route all the time
|
|
1550
|
+
// Allow shouldReload to get the last say,
|
|
1551
|
+
// if provided.
|
|
1552
|
+
const shouldReload =
|
|
1553
|
+
typeof shouldReloadOption === 'function'
|
|
1554
|
+
? shouldReloadOption(loaderContext)
|
|
1555
|
+
: shouldReloadOption
|
|
1442
1556
|
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
}
|
|
1557
|
+
matches[index] = match = {
|
|
1558
|
+
...match,
|
|
1559
|
+
preload:
|
|
1560
|
+
!!preload &&
|
|
1561
|
+
!this.state.matches.find((d) => d.id === match.id),
|
|
1562
|
+
}
|
|
1450
1563
|
|
|
1451
|
-
|
|
1452
|
-
|
|
1564
|
+
const fetchWithRedirect = async () => {
|
|
1565
|
+
try {
|
|
1566
|
+
await fetch()
|
|
1567
|
+
} catch (err) {
|
|
1568
|
+
if ((latestPromise = checkLatest()))
|
|
1569
|
+
return await latestPromise
|
|
1453
1570
|
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
const staleAge = preload
|
|
1458
|
-
? route.options.preloadStaleTime ??
|
|
1459
|
-
this.options.defaultPreloadStaleTime ??
|
|
1460
|
-
30_000 // 30 seconds for preloads by default
|
|
1461
|
-
: route.options.staleTime ?? this.options.defaultStaleTime ?? 0
|
|
1462
|
-
|
|
1463
|
-
const shouldReloadOption = route.options.shouldReload
|
|
1464
|
-
|
|
1465
|
-
// Default to reloading the route all the time
|
|
1466
|
-
// Allow shouldReload to get the last say,
|
|
1467
|
-
// if provided.
|
|
1468
|
-
const shouldReload =
|
|
1469
|
-
typeof shouldReloadOption === 'function'
|
|
1470
|
-
? shouldReloadOption(loaderContext)
|
|
1471
|
-
: shouldReloadOption
|
|
1472
|
-
|
|
1473
|
-
matches[index] = match = {
|
|
1474
|
-
...match,
|
|
1475
|
-
preload:
|
|
1476
|
-
!!preload && !this.state.matches.find((d) => d.id === match.id),
|
|
1477
|
-
}
|
|
1571
|
+
if (isRedirect(err)) {
|
|
1572
|
+
const redirect = this.resolveRedirect(err)
|
|
1478
1573
|
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
(match.invalid || (shouldReload ?? age > staleAge))
|
|
1483
|
-
) {
|
|
1484
|
-
;(async () => {
|
|
1485
|
-
try {
|
|
1486
|
-
await fetch()
|
|
1487
|
-
} catch (err) {
|
|
1488
|
-
console.info('Background Fetching Error', err)
|
|
1489
|
-
|
|
1490
|
-
if (isRedirect(err)) {
|
|
1491
|
-
const isActive = (
|
|
1492
|
-
this.state.pendingMatches || this.state.matches
|
|
1493
|
-
).find((d) => d.id === match.id)
|
|
1494
|
-
|
|
1495
|
-
// Redirects should not be persisted
|
|
1496
|
-
handleError(err)
|
|
1497
|
-
|
|
1498
|
-
// If the route is still active, redirect
|
|
1499
|
-
// TODO: Do we really need this?
|
|
1500
|
-
invariant(
|
|
1501
|
-
false,
|
|
1502
|
-
'You need to redirect from a background fetch? This is not supported yet. File an issue.',
|
|
1503
|
-
)
|
|
1504
|
-
// if (isActive) {
|
|
1505
|
-
// this.handleRedirect(err)
|
|
1506
|
-
// }
|
|
1507
|
-
}
|
|
1508
|
-
}
|
|
1509
|
-
})()
|
|
1574
|
+
if (!preload && !this.isServer) {
|
|
1575
|
+
this.navigate({ ...(redirect as any), replace: true })
|
|
1576
|
+
}
|
|
1510
1577
|
|
|
1511
|
-
|
|
1512
|
-
|
|
1578
|
+
throw redirect
|
|
1579
|
+
} else if (isNotFound(err)) {
|
|
1580
|
+
if (!preload) this.handleNotFound(matches, err)
|
|
1581
|
+
throw err
|
|
1582
|
+
}
|
|
1513
1583
|
|
|
1514
|
-
|
|
1515
|
-
!preload &&
|
|
1516
|
-
route.options.loader &&
|
|
1517
|
-
typeof pendingMs === 'number' &&
|
|
1518
|
-
(route.options.pendingComponent ??
|
|
1519
|
-
this.options.defaultPendingComponent)
|
|
1520
|
-
|
|
1521
|
-
if (match.status !== 'success') {
|
|
1522
|
-
try {
|
|
1523
|
-
if (shouldPending) {
|
|
1524
|
-
match.pendingPromise?.then(async () => {
|
|
1525
|
-
if ((latestPromise = checkLatest())) return latestPromise
|
|
1526
|
-
|
|
1527
|
-
didShowPending = true
|
|
1528
|
-
matches[index] = match = {
|
|
1529
|
-
...match,
|
|
1530
|
-
showPending: true,
|
|
1584
|
+
handleError(err)
|
|
1531
1585
|
}
|
|
1586
|
+
}
|
|
1532
1587
|
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1588
|
+
// If the route is successful and still fresh, just resolve
|
|
1589
|
+
if (
|
|
1590
|
+
match.status === 'success' &&
|
|
1591
|
+
(match.invalid || (shouldReload ?? age > staleAge))
|
|
1592
|
+
) {
|
|
1593
|
+
fetchWithRedirect()
|
|
1594
|
+
return
|
|
1595
|
+
}
|
|
1537
1596
|
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1597
|
+
if (match.status !== 'success') {
|
|
1598
|
+
await fetchWithRedirect()
|
|
1599
|
+
}
|
|
1600
|
+
}),
|
|
1601
|
+
)
|
|
1543
1602
|
|
|
1544
|
-
|
|
1545
|
-
}),
|
|
1546
|
-
)
|
|
1547
|
-
})
|
|
1603
|
+
if ((latestPromise = checkLatest())) return await latestPromise
|
|
1548
1604
|
|
|
1549
|
-
|
|
1605
|
+
resolveAll()
|
|
1606
|
+
} catch (err) {
|
|
1607
|
+
rejectAll(err)
|
|
1608
|
+
}
|
|
1609
|
+
})()
|
|
1610
|
+
})
|
|
1611
|
+
} catch (err) {
|
|
1612
|
+
if (isRedirect(err) || isNotFound(err)) {
|
|
1613
|
+
throw err
|
|
1614
|
+
}
|
|
1615
|
+
}
|
|
1550
1616
|
|
|
1551
1617
|
return matches
|
|
1552
1618
|
}
|
|
@@ -1569,68 +1635,74 @@ export class Router<
|
|
|
1569
1635
|
}
|
|
1570
1636
|
|
|
1571
1637
|
load = async (): Promise<void> => {
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
const next = this.latestLocation
|
|
1575
|
-
const prevLocation = this.state.resolvedLocation
|
|
1576
|
-
const pathDidChange = prevLocation.href !== next.href
|
|
1577
|
-
let latestPromise: Promise<void> | undefined | null
|
|
1578
|
-
|
|
1579
|
-
// Cancel any pending matches
|
|
1580
|
-
this.cancelMatches()
|
|
1581
|
-
|
|
1582
|
-
this.emit({
|
|
1583
|
-
type: 'onBeforeLoad',
|
|
1584
|
-
fromLocation: prevLocation,
|
|
1585
|
-
toLocation: next,
|
|
1586
|
-
pathChanged: pathDidChange,
|
|
1587
|
-
})
|
|
1638
|
+
let resolveLoad!: (value: void) => void
|
|
1639
|
+
let rejectLoad!: (reason: any) => void
|
|
1588
1640
|
|
|
1589
|
-
|
|
1590
|
-
|
|
1641
|
+
const promise = new Promise<void>((resolve, reject) => {
|
|
1642
|
+
resolveLoad = resolve
|
|
1643
|
+
rejectLoad = reject
|
|
1644
|
+
})
|
|
1645
|
+
|
|
1646
|
+
this.latestLoadPromise = promise
|
|
1647
|
+
|
|
1648
|
+
let latestPromise: Promise<void> | undefined | null
|
|
1649
|
+
;(async () => {
|
|
1650
|
+
try {
|
|
1651
|
+
const next = this.latestLocation
|
|
1652
|
+
const prevLocation = this.state.resolvedLocation
|
|
1653
|
+
const pathDidChange = prevLocation.href !== next.href
|
|
1591
1654
|
|
|
1592
|
-
|
|
1593
|
-
this.
|
|
1655
|
+
// Cancel any pending matches
|
|
1656
|
+
this.cancelMatches()
|
|
1594
1657
|
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1658
|
+
this.emit({
|
|
1659
|
+
type: 'onBeforeLoad',
|
|
1660
|
+
fromLocation: prevLocation,
|
|
1661
|
+
toLocation: next,
|
|
1662
|
+
pathChanged: pathDidChange,
|
|
1598
1663
|
})
|
|
1599
1664
|
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1665
|
+
let pendingMatches!: Array<RouteMatch<any, any>>
|
|
1666
|
+
const previousMatches = this.state.matches
|
|
1667
|
+
|
|
1668
|
+
this.__store.batch(() => {
|
|
1669
|
+
this.cleanCache()
|
|
1670
|
+
|
|
1671
|
+
// Match the routes
|
|
1672
|
+
pendingMatches = this.matchRoutes(next.pathname, next.search)
|
|
1673
|
+
|
|
1674
|
+
// Ingest the new matches
|
|
1675
|
+
// If a cached moved to pendingMatches, remove it from cachedMatches
|
|
1676
|
+
this.__store.setState((s) => ({
|
|
1677
|
+
...s,
|
|
1678
|
+
isLoading: true,
|
|
1679
|
+
location: next,
|
|
1680
|
+
pendingMatches,
|
|
1681
|
+
cachedMatches: s.cachedMatches.filter((d) => {
|
|
1682
|
+
return !pendingMatches.find((e) => e.id === d.id)
|
|
1683
|
+
}),
|
|
1684
|
+
}))
|
|
1685
|
+
})
|
|
1612
1686
|
|
|
1613
|
-
try {
|
|
1614
1687
|
let redirect: ResolvedRedirect | undefined
|
|
1615
1688
|
let notFound: NotFoundError | undefined
|
|
1616
1689
|
|
|
1617
1690
|
try {
|
|
1618
1691
|
// Load the matches
|
|
1619
|
-
|
|
1692
|
+
const loadMatchesPromise = this.loadMatches({
|
|
1620
1693
|
matches: pendingMatches,
|
|
1621
1694
|
location: next,
|
|
1622
1695
|
checkLatest: () => this.checkLatest(promise),
|
|
1623
1696
|
})
|
|
1697
|
+
|
|
1698
|
+
if (previousMatches.length || this.isServer) {
|
|
1699
|
+
await loadMatchesPromise
|
|
1700
|
+
}
|
|
1624
1701
|
} catch (err) {
|
|
1625
1702
|
if (isRedirect(err)) {
|
|
1626
|
-
redirect =
|
|
1627
|
-
|
|
1628
|
-
if (!isServer) {
|
|
1629
|
-
this.navigate({ ...(redirect as any), replace: true })
|
|
1630
|
-
}
|
|
1703
|
+
redirect = err as ResolvedRedirect
|
|
1631
1704
|
} else if (isNotFound(err)) {
|
|
1632
1705
|
notFound = err
|
|
1633
|
-
this.handleNotFound(pendingMatches, err)
|
|
1634
1706
|
}
|
|
1635
1707
|
|
|
1636
1708
|
// Swallow all other errors that happen inside
|
|
@@ -1696,20 +1768,18 @@ export class Router<
|
|
|
1696
1768
|
pathChanged: pathDidChange,
|
|
1697
1769
|
})
|
|
1698
1770
|
|
|
1699
|
-
|
|
1771
|
+
resolveLoad()
|
|
1700
1772
|
} catch (err) {
|
|
1701
1773
|
// Only apply the latest transition
|
|
1702
1774
|
if ((latestPromise = this.checkLatest(promise))) {
|
|
1703
1775
|
return latestPromise
|
|
1704
1776
|
}
|
|
1705
1777
|
|
|
1706
|
-
console.
|
|
1778
|
+
console.error('Load Error', err)
|
|
1707
1779
|
|
|
1708
|
-
|
|
1780
|
+
rejectLoad(err)
|
|
1709
1781
|
}
|
|
1710
|
-
})
|
|
1711
|
-
|
|
1712
|
-
this.latestLoadPromise = promise
|
|
1782
|
+
})()
|
|
1713
1783
|
|
|
1714
1784
|
return this.latestLoadPromise
|
|
1715
1785
|
}
|
|
@@ -1812,7 +1882,11 @@ export class Router<
|
|
|
1812
1882
|
return matches
|
|
1813
1883
|
} catch (err) {
|
|
1814
1884
|
if (isRedirect(err)) {
|
|
1815
|
-
return await this.preloadRoute(
|
|
1885
|
+
return await this.preloadRoute({
|
|
1886
|
+
_fromDest: next,
|
|
1887
|
+
from: next.pathname,
|
|
1888
|
+
...(err as any),
|
|
1889
|
+
})
|
|
1816
1890
|
}
|
|
1817
1891
|
// Preload errors are not fatal, but we should still log them
|
|
1818
1892
|
console.error(err)
|
|
@@ -2012,7 +2086,6 @@ export class Router<
|
|
|
2012
2086
|
return {
|
|
2013
2087
|
...s,
|
|
2014
2088
|
matches: matches as any,
|
|
2015
|
-
lastUpdated: Date.now(),
|
|
2016
2089
|
}
|
|
2017
2090
|
})
|
|
2018
2091
|
}
|
|
@@ -2099,7 +2172,6 @@ export function getInitialRouterState(
|
|
|
2099
2172
|
matches: [],
|
|
2100
2173
|
pendingMatches: [],
|
|
2101
2174
|
cachedMatches: [],
|
|
2102
|
-
lastUpdated: 0,
|
|
2103
2175
|
statusCode: 200,
|
|
2104
2176
|
}
|
|
2105
2177
|
}
|