@tanstack/react-router 1.28.1 → 1.28.3
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/RouterProvider.cjs +2 -2
- package/dist/cjs/RouterProvider.cjs.map +1 -1
- 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/RouterProvider.js +2 -2
- package/dist/esm/RouterProvider.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/RouterProvider.tsx +2 -0
- 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/dist/esm/router.js
CHANGED
|
@@ -4,7 +4,7 @@ import invariant from "tiny-invariant";
|
|
|
4
4
|
import warning from "tiny-warning";
|
|
5
5
|
import { rootRouteId } from "./route.js";
|
|
6
6
|
import { defaultStringifySearch, defaultParseSearch } from "./searchParams.js";
|
|
7
|
-
import { replaceEqualDeep, pick,
|
|
7
|
+
import { createControlledPromise, replaceEqualDeep, pick, last, deepEqual, escapeJSON, functionalUpdate } from "./utils.js";
|
|
8
8
|
import { getRouteMatch } from "./RouterProvider.js";
|
|
9
9
|
import { trimPath, trimPathLeft, parsePathname, resolvePath, cleanPath, matchPathname, trimPathRight, interpolatePath, joinPaths } from "./path.js";
|
|
10
10
|
import { isRedirect } from "./redirects.js";
|
|
@@ -31,6 +31,7 @@ class Router {
|
|
|
31
31
|
this.latestLoadPromise = Promise.resolve();
|
|
32
32
|
this.subscribers = /* @__PURE__ */ new Set();
|
|
33
33
|
this.injectedHtml = [];
|
|
34
|
+
this.isServer = typeof document === "undefined";
|
|
34
35
|
this.startReactTransition = (fn) => fn();
|
|
35
36
|
this.update = (newOptions) => {
|
|
36
37
|
if (newOptions.notFoundRoute) {
|
|
@@ -286,7 +287,7 @@ class Router {
|
|
|
286
287
|
});
|
|
287
288
|
const matches = [];
|
|
288
289
|
matchedRoutes.forEach((route, index) => {
|
|
289
|
-
var _a, _b, _c, _d, _e, _f;
|
|
290
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
|
|
290
291
|
const parentMatch = matches[index - 1];
|
|
291
292
|
const [preMatchSearch, searchError] = (() => {
|
|
292
293
|
const parentSearch = (parentMatch == null ? void 0 : parentMatch.search) ?? locationSearch;
|
|
@@ -325,36 +326,55 @@ class Router {
|
|
|
325
326
|
}) + loaderDepsHash;
|
|
326
327
|
const existingMatch = getRouteMatch(this.state, matchId);
|
|
327
328
|
const cause = this.state.matches.find((d) => d.id === matchId) ? "stay" : "enter";
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
329
|
+
let match;
|
|
330
|
+
if (existingMatch) {
|
|
331
|
+
match = {
|
|
332
|
+
...existingMatch,
|
|
333
|
+
cause,
|
|
334
|
+
params: routeParams
|
|
335
|
+
};
|
|
336
|
+
} else {
|
|
337
|
+
const status = route.options.loader || route.options.beforeLoad ? "pending" : "success";
|
|
338
|
+
const loadPromise = createControlledPromise();
|
|
339
|
+
if (status === "success") {
|
|
340
|
+
loadPromise.resolve();
|
|
341
|
+
}
|
|
342
|
+
match = {
|
|
343
|
+
id: matchId,
|
|
344
|
+
routeId: route.id,
|
|
345
|
+
params: routeParams,
|
|
346
|
+
pathname: joinPaths([this.basepath, interpolatedPath]),
|
|
347
|
+
updatedAt: Date.now(),
|
|
348
|
+
search: {},
|
|
349
|
+
searchError: void 0,
|
|
350
|
+
status: "pending",
|
|
351
|
+
isFetching: false,
|
|
352
|
+
error: void 0,
|
|
353
|
+
paramsError: parseErrors[index],
|
|
354
|
+
loaderPromise: Promise.resolve(),
|
|
355
|
+
loadPromise,
|
|
356
|
+
routeContext: void 0,
|
|
357
|
+
context: void 0,
|
|
358
|
+
abortController: new AbortController(),
|
|
359
|
+
fetchCount: 0,
|
|
360
|
+
cause,
|
|
361
|
+
loaderDeps,
|
|
362
|
+
invalid: false,
|
|
363
|
+
preload: false,
|
|
364
|
+
links: (_d = (_c = route.options).links) == null ? void 0 : _d.call(_c),
|
|
365
|
+
scripts: (_f = (_e = route.options).scripts) == null ? void 0 : _f.call(_e),
|
|
366
|
+
staticData: route.options.staticData || {}
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
if (match.status === "success") {
|
|
370
|
+
match.meta = (_h = (_g = route.options).meta) == null ? void 0 : _h.call(_g, {
|
|
371
|
+
params: match.params,
|
|
372
|
+
loaderData: match.loaderData
|
|
373
|
+
});
|
|
374
|
+
match.headers = (_j = (_i = route.options).headers) == null ? void 0 : _j.call(_i, {
|
|
375
|
+
loaderData: match.loaderData
|
|
376
|
+
});
|
|
377
|
+
}
|
|
358
378
|
if (!(opts == null ? void 0 : opts.preload)) {
|
|
359
379
|
match.globalNotFound = globalNotFoundRouteId === route.id;
|
|
360
380
|
}
|
|
@@ -376,25 +396,20 @@ class Router {
|
|
|
376
396
|
};
|
|
377
397
|
this.buildLocation = (opts) => {
|
|
378
398
|
const build = (dest = {}, matches) => {
|
|
379
|
-
var _a, _b, _c;
|
|
380
|
-
const
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
);
|
|
385
|
-
const fromMatches = this.matchRoutes(
|
|
386
|
-
this.latestLocation.pathname,
|
|
387
|
-
fromSearch
|
|
388
|
-
);
|
|
399
|
+
var _a, _b, _c, _d;
|
|
400
|
+
const fromPath = dest.from || this.latestLocation.pathname;
|
|
401
|
+
let fromSearch = ((_a = dest._fromLocation) == null ? void 0 : _a.search) || this.latestLocation.search;
|
|
402
|
+
const fromMatches = this.matchRoutes(fromPath, fromSearch);
|
|
403
|
+
fromSearch = ((_b = last(fromMatches)) == null ? void 0 : _b.search) || this.latestLocation.search;
|
|
389
404
|
const stayingMatches = matches == null ? void 0 : matches.filter(
|
|
390
405
|
(d) => fromMatches.find((e) => e.routeId === d.routeId)
|
|
391
406
|
);
|
|
392
|
-
const fromRoute = this.looseRoutesById[(
|
|
407
|
+
const fromRoute = this.looseRoutesById[(_c = last(fromMatches)) == null ? void 0 : _c.routeId];
|
|
393
408
|
let pathname = dest.to ? this.resolvePathWithBase(
|
|
394
409
|
dest.from ?? this.latestLocation.pathname,
|
|
395
410
|
`${dest.to}`
|
|
396
411
|
) : this.resolvePathWithBase(fromRoute == null ? void 0 : fromRoute.fullPath, fromRoute == null ? void 0 : fromRoute.fullPath);
|
|
397
|
-
const prevParams = { ...(
|
|
412
|
+
const prevParams = { ...(_d = last(fromMatches)) == null ? void 0 : _d.params };
|
|
398
413
|
let nextParams = (dest.params ?? true) === true ? prevParams : { ...prevParams, ...functionalUpdate(dest.params, prevParams) };
|
|
399
414
|
if (Object.keys(nextParams).length > 0) {
|
|
400
415
|
matches == null ? void 0 : matches.map((d) => this.looseRoutesById[d.routeId].options.stringifyParams).filter(Boolean).forEach((fn) => {
|
|
@@ -561,275 +576,302 @@ class Router {
|
|
|
561
576
|
matches,
|
|
562
577
|
preload
|
|
563
578
|
}) => {
|
|
564
|
-
var _a, _b;
|
|
565
579
|
let latestPromise;
|
|
566
580
|
let firstBadMatchIndex;
|
|
567
|
-
const updateMatch = (
|
|
568
|
-
var
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
);
|
|
572
|
-
const isMatched = this.state.matches.find((d) => d.id === match.id);
|
|
581
|
+
const updateMatch = (id, updater, opts) => {
|
|
582
|
+
var _a;
|
|
583
|
+
let updated;
|
|
584
|
+
const isPending = (_a = this.state.pendingMatches) == null ? void 0 : _a.find((d) => d.id === id);
|
|
585
|
+
const isMatched = this.state.matches.find((d) => d.id === id);
|
|
573
586
|
const matchesKey = isPending ? "pendingMatches" : isMatched ? "matches" : "cachedMatches";
|
|
574
587
|
this.__store.setState((s) => {
|
|
575
|
-
var
|
|
588
|
+
var _a2, _b;
|
|
576
589
|
return {
|
|
577
590
|
...s,
|
|
578
|
-
[matchesKey]: (opts == null ? void 0 : opts.remove) ? (
|
|
591
|
+
[matchesKey]: (opts == null ? void 0 : opts.remove) ? (_a2 = s[matchesKey]) == null ? void 0 : _a2.filter((d) => d.id !== id) : (_b = s[matchesKey]) == null ? void 0 : _b.map(
|
|
592
|
+
(d) => d.id === id ? updated = updater(d) : d
|
|
593
|
+
)
|
|
579
594
|
};
|
|
580
595
|
});
|
|
596
|
+
return updated;
|
|
581
597
|
};
|
|
582
598
|
const handleMatchSpecialError = (match, err) => {
|
|
583
|
-
match
|
|
584
|
-
...
|
|
599
|
+
updateMatch(match.id, (prev) => ({
|
|
600
|
+
...prev,
|
|
585
601
|
status: isRedirect(err) ? "redirected" : isNotFound(err) ? "notFound" : "error",
|
|
586
602
|
isFetching: false,
|
|
587
603
|
error: err
|
|
588
|
-
};
|
|
589
|
-
updateMatch(match);
|
|
604
|
+
}));
|
|
590
605
|
if (!err.routeId) {
|
|
591
606
|
err.routeId = match.routeId;
|
|
592
607
|
}
|
|
593
608
|
throw err;
|
|
594
609
|
};
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
};
|
|
621
|
-
};
|
|
622
|
-
if (match.paramsError) {
|
|
623
|
-
handleSerialError(match.paramsError, "PARSE_PARAMS");
|
|
624
|
-
}
|
|
625
|
-
if (match.searchError) {
|
|
626
|
-
handleSerialError(match.searchError, "VALIDATE_SEARCH");
|
|
627
|
-
}
|
|
628
|
-
try {
|
|
629
|
-
const parentContext = (parentMatch == null ? void 0 : parentMatch.context) ?? this.options.context ?? {};
|
|
630
|
-
const pendingMs = route.options.pendingMs ?? this.options.defaultPendingMs;
|
|
631
|
-
const pendingPromise = typeof pendingMs === "number" && pendingMs <= 0 ? Promise.resolve() : new Promise((r) => setTimeout(r, pendingMs));
|
|
632
|
-
const beforeLoadContext = await ((_b = (_a = route.options).beforeLoad) == null ? void 0 : _b.call(_a, {
|
|
633
|
-
search: match.search,
|
|
634
|
-
abortController,
|
|
635
|
-
params: match.params,
|
|
636
|
-
preload: !!preload,
|
|
637
|
-
context: parentContext,
|
|
638
|
-
location,
|
|
639
|
-
navigate: (opts) => this.navigate({ ...opts, from: match.pathname }),
|
|
640
|
-
buildLocation: this.buildLocation,
|
|
641
|
-
cause: preload ? "preload" : match.cause
|
|
642
|
-
})) ?? {};
|
|
643
|
-
if (isRedirect(beforeLoadContext) || isNotFound(beforeLoadContext)) {
|
|
644
|
-
handleSerialError(beforeLoadContext, "BEFORE_LOAD");
|
|
645
|
-
}
|
|
646
|
-
const context = {
|
|
647
|
-
...parentContext,
|
|
648
|
-
...beforeLoadContext
|
|
649
|
-
};
|
|
650
|
-
matches[index] = match = {
|
|
651
|
-
...match,
|
|
652
|
-
routeContext: replaceEqualDeep(match.routeContext, beforeLoadContext),
|
|
653
|
-
context: replaceEqualDeep(match.context, context),
|
|
654
|
-
abortController,
|
|
655
|
-
pendingPromise
|
|
656
|
-
};
|
|
657
|
-
} catch (err) {
|
|
658
|
-
handleSerialError(err, "BEFORE_LOAD");
|
|
659
|
-
break;
|
|
660
|
-
}
|
|
661
|
-
}
|
|
662
|
-
const validResolvedMatches = matches.slice(0, firstBadMatchIndex);
|
|
663
|
-
const matchPromises = [];
|
|
664
|
-
validResolvedMatches.forEach((match, index) => {
|
|
665
|
-
matchPromises.push(
|
|
666
|
-
// eslint-disable-next-line no-async-promise-executor
|
|
667
|
-
new Promise(async (resolve, reject) => {
|
|
668
|
-
var _a2;
|
|
669
|
-
const parentMatchPromise = matchPromises[index - 1];
|
|
670
|
-
const route = this.looseRoutesById[match.routeId];
|
|
671
|
-
const handleError = (err) => {
|
|
672
|
-
if (isRedirect(err) || isNotFound(err)) {
|
|
673
|
-
handleMatchSpecialError(match, err);
|
|
674
|
-
}
|
|
675
|
-
};
|
|
676
|
-
let loadPromise;
|
|
677
|
-
matches[index] = match = {
|
|
678
|
-
...match,
|
|
679
|
-
showPending: false
|
|
680
|
-
};
|
|
681
|
-
let didShowPending = false;
|
|
682
|
-
const pendingMs = route.options.pendingMs ?? this.options.defaultPendingMs;
|
|
683
|
-
const pendingMinMs = route.options.pendingMinMs ?? this.options.defaultPendingMinMs;
|
|
684
|
-
const loaderContext = {
|
|
685
|
-
params: match.params,
|
|
686
|
-
deps: match.loaderDeps,
|
|
687
|
-
preload: !!preload,
|
|
688
|
-
parentMatchPromise,
|
|
689
|
-
abortController: match.abortController,
|
|
690
|
-
context: match.context,
|
|
691
|
-
location,
|
|
692
|
-
navigate: (opts) => this.navigate({ ...opts, from: match.pathname }),
|
|
693
|
-
cause: preload ? "preload" : match.cause,
|
|
694
|
-
route
|
|
695
|
-
};
|
|
696
|
-
const fetch = async () => {
|
|
697
|
-
var _a3, _b2, _c, _d, _e, _f, _g, _h, _i, _j;
|
|
698
|
-
try {
|
|
699
|
-
if (match.isFetching) {
|
|
700
|
-
loadPromise = (_a3 = getRouteMatch(this.state, match.id)) == null ? void 0 : _a3.loadPromise;
|
|
701
|
-
} else {
|
|
610
|
+
try {
|
|
611
|
+
await new Promise((resolveAll, rejectAll) => {
|
|
612
|
+
;
|
|
613
|
+
(async () => {
|
|
614
|
+
var _a, _b;
|
|
615
|
+
try {
|
|
616
|
+
for (let [index, match] of matches.entries()) {
|
|
617
|
+
const parentMatch = matches[index - 1];
|
|
618
|
+
const route = this.looseRoutesById[match.routeId];
|
|
619
|
+
const abortController = new AbortController();
|
|
620
|
+
const handleSerialError = (err, code) => {
|
|
621
|
+
var _a2, _b2;
|
|
622
|
+
err.routerCode = code;
|
|
623
|
+
firstBadMatchIndex = firstBadMatchIndex ?? index;
|
|
624
|
+
if (isRedirect(err) || isNotFound(err)) {
|
|
625
|
+
handleMatchSpecialError(match, err);
|
|
626
|
+
}
|
|
627
|
+
try {
|
|
628
|
+
(_b2 = (_a2 = route.options).onError) == null ? void 0 : _b2.call(_a2, err);
|
|
629
|
+
} catch (errorHandlerErr) {
|
|
630
|
+
err = errorHandlerErr;
|
|
631
|
+
if (isRedirect(err) || isNotFound(err)) {
|
|
632
|
+
handleMatchSpecialError(match, errorHandlerErr);
|
|
633
|
+
}
|
|
634
|
+
}
|
|
702
635
|
matches[index] = match = {
|
|
703
636
|
...match,
|
|
704
|
-
|
|
705
|
-
|
|
637
|
+
error: err,
|
|
638
|
+
status: "error",
|
|
639
|
+
updatedAt: Date.now(),
|
|
640
|
+
abortController: new AbortController()
|
|
706
641
|
};
|
|
707
|
-
const lazyPromise = ((_b2 = route.lazyFn) == null ? void 0 : _b2.call(route).then((lazyRoute) => {
|
|
708
|
-
Object.assign(route.options, lazyRoute.options);
|
|
709
|
-
})) || Promise.resolve();
|
|
710
|
-
const componentsPromise = lazyPromise.then(
|
|
711
|
-
() => Promise.all(
|
|
712
|
-
componentTypes.map(async (type) => {
|
|
713
|
-
const component = route.options[type];
|
|
714
|
-
if (component == null ? void 0 : component.preload) {
|
|
715
|
-
await component.preload();
|
|
716
|
-
}
|
|
717
|
-
})
|
|
718
|
-
)
|
|
719
|
-
);
|
|
720
|
-
const loaderPromise = (_d = (_c = route.options).loader) == null ? void 0 : _d.call(_c, loaderContext);
|
|
721
|
-
loadPromise = Promise.all([
|
|
722
|
-
componentsPromise,
|
|
723
|
-
loaderPromise,
|
|
724
|
-
lazyPromise
|
|
725
|
-
]).then((d) => d[1]);
|
|
726
|
-
}
|
|
727
|
-
matches[index] = match = {
|
|
728
|
-
...match,
|
|
729
|
-
loadPromise
|
|
730
642
|
};
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
if (latestPromise = checkLatest())
|
|
734
|
-
return await latestPromise;
|
|
735
|
-
handleError(loaderData);
|
|
736
|
-
if (didShowPending && pendingMinMs) {
|
|
737
|
-
await new Promise((r) => setTimeout(r, pendingMinMs));
|
|
643
|
+
if (match.paramsError) {
|
|
644
|
+
handleSerialError(match.paramsError, "PARSE_PARAMS");
|
|
738
645
|
}
|
|
739
|
-
if (
|
|
740
|
-
|
|
741
|
-
const [meta, headers] = await Promise.all([
|
|
742
|
-
(_f = (_e = route.options).meta) == null ? void 0 : _f.call(_e, {
|
|
743
|
-
params: match.params,
|
|
744
|
-
loaderData
|
|
745
|
-
}),
|
|
746
|
-
(_h = (_g = route.options).headers) == null ? void 0 : _h.call(_g, {
|
|
747
|
-
loaderData
|
|
748
|
-
})
|
|
749
|
-
]);
|
|
750
|
-
matches[index] = match = {
|
|
751
|
-
...match,
|
|
752
|
-
error: void 0,
|
|
753
|
-
status: "success",
|
|
754
|
-
isFetching: false,
|
|
755
|
-
updatedAt: Date.now(),
|
|
756
|
-
loaderData,
|
|
757
|
-
loadPromise: void 0,
|
|
758
|
-
meta,
|
|
759
|
-
headers
|
|
760
|
-
};
|
|
761
|
-
} catch (e) {
|
|
762
|
-
let error = e;
|
|
763
|
-
if (latestPromise = checkLatest())
|
|
764
|
-
return await latestPromise;
|
|
765
|
-
handleError(e);
|
|
766
|
-
try {
|
|
767
|
-
(_j = (_i = route.options).onError) == null ? void 0 : _j.call(_i, e);
|
|
768
|
-
} catch (onErrorError) {
|
|
769
|
-
error = onErrorError;
|
|
770
|
-
handleError(onErrorError);
|
|
646
|
+
if (match.searchError) {
|
|
647
|
+
handleSerialError(match.searchError, "VALIDATE_SEARCH");
|
|
771
648
|
}
|
|
772
|
-
matches[index] = match = {
|
|
773
|
-
...match,
|
|
774
|
-
error,
|
|
775
|
-
status: "error",
|
|
776
|
-
isFetching: false
|
|
777
|
-
};
|
|
778
|
-
}
|
|
779
|
-
updateMatch(match);
|
|
780
|
-
};
|
|
781
|
-
const age = Date.now() - match.updatedAt;
|
|
782
|
-
const staleAge = preload ? route.options.preloadStaleTime ?? this.options.defaultPreloadStaleTime ?? 3e4 : route.options.staleTime ?? this.options.defaultStaleTime ?? 0;
|
|
783
|
-
const shouldReloadOption = route.options.shouldReload;
|
|
784
|
-
const shouldReload = typeof shouldReloadOption === "function" ? shouldReloadOption(loaderContext) : shouldReloadOption;
|
|
785
|
-
matches[index] = match = {
|
|
786
|
-
...match,
|
|
787
|
-
preload: !!preload && !this.state.matches.find((d) => d.id === match.id)
|
|
788
|
-
};
|
|
789
|
-
if (match.status === "success" && (match.invalid || (shouldReload ?? age > staleAge))) {
|
|
790
|
-
(async () => {
|
|
791
649
|
try {
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
handleError(err);
|
|
798
|
-
invariant(
|
|
799
|
-
false,
|
|
800
|
-
"You need to redirect from a background fetch? This is not supported yet. File an issue."
|
|
801
|
-
);
|
|
802
|
-
}
|
|
803
|
-
}
|
|
804
|
-
})();
|
|
805
|
-
return resolve();
|
|
806
|
-
}
|
|
807
|
-
const shouldPending = !preload && route.options.loader && typeof pendingMs === "number" && (route.options.pendingComponent ?? this.options.defaultPendingComponent);
|
|
808
|
-
if (match.status !== "success") {
|
|
809
|
-
try {
|
|
810
|
-
if (shouldPending) {
|
|
811
|
-
(_a2 = match.pendingPromise) == null ? void 0 : _a2.then(async () => {
|
|
812
|
-
if (latestPromise = checkLatest())
|
|
813
|
-
return latestPromise;
|
|
814
|
-
didShowPending = true;
|
|
815
|
-
matches[index] = match = {
|
|
816
|
-
...match,
|
|
817
|
-
showPending: true
|
|
818
|
-
};
|
|
819
|
-
updateMatch(match);
|
|
820
|
-
resolve();
|
|
650
|
+
const parentContext = (parentMatch == null ? void 0 : parentMatch.context) ?? this.options.context ?? {};
|
|
651
|
+
const pendingMs = route.options.pendingMs ?? this.options.defaultPendingMs;
|
|
652
|
+
const pendingPromise = typeof pendingMs !== "number" || pendingMs <= 0 ? Promise.resolve() : new Promise((r) => {
|
|
653
|
+
if (pendingMs !== Infinity)
|
|
654
|
+
setTimeout(r, pendingMs);
|
|
821
655
|
});
|
|
656
|
+
const shouldPending = !this.isServer && !preload && (route.options.loader || route.options.beforeLoad) && typeof pendingMs === "number" && (route.options.pendingComponent ?? this.options.defaultPendingComponent);
|
|
657
|
+
if (shouldPending) {
|
|
658
|
+
pendingPromise.then(async () => {
|
|
659
|
+
if (latestPromise = checkLatest())
|
|
660
|
+
return latestPromise;
|
|
661
|
+
resolveAll();
|
|
662
|
+
});
|
|
663
|
+
}
|
|
664
|
+
const beforeLoadContext = await ((_b = (_a = route.options).beforeLoad) == null ? void 0 : _b.call(_a, {
|
|
665
|
+
search: match.search,
|
|
666
|
+
abortController,
|
|
667
|
+
params: match.params,
|
|
668
|
+
preload: !!preload,
|
|
669
|
+
context: parentContext,
|
|
670
|
+
location,
|
|
671
|
+
navigate: (opts) => this.navigate({ ...opts, from: match.pathname }),
|
|
672
|
+
buildLocation: this.buildLocation,
|
|
673
|
+
cause: preload ? "preload" : match.cause
|
|
674
|
+
})) ?? {};
|
|
675
|
+
if (isRedirect(beforeLoadContext) || isNotFound(beforeLoadContext)) {
|
|
676
|
+
handleSerialError(beforeLoadContext, "BEFORE_LOAD");
|
|
677
|
+
}
|
|
678
|
+
const context = {
|
|
679
|
+
...parentContext,
|
|
680
|
+
...beforeLoadContext
|
|
681
|
+
};
|
|
682
|
+
matches[index] = match = {
|
|
683
|
+
...match,
|
|
684
|
+
routeContext: replaceEqualDeep(
|
|
685
|
+
match.routeContext,
|
|
686
|
+
beforeLoadContext
|
|
687
|
+
),
|
|
688
|
+
context: replaceEqualDeep(match.context, context),
|
|
689
|
+
abortController
|
|
690
|
+
};
|
|
691
|
+
} catch (err) {
|
|
692
|
+
handleSerialError(err, "BEFORE_LOAD");
|
|
693
|
+
break;
|
|
822
694
|
}
|
|
823
|
-
await fetch();
|
|
824
|
-
} catch (err) {
|
|
825
|
-
reject(err);
|
|
826
695
|
}
|
|
696
|
+
const validResolvedMatches = matches.slice(0, firstBadMatchIndex);
|
|
697
|
+
const matchPromises = [];
|
|
698
|
+
await Promise.all(
|
|
699
|
+
validResolvedMatches.map(async (match, index) => {
|
|
700
|
+
const parentMatchPromise = matchPromises[index - 1];
|
|
701
|
+
const route = this.looseRoutesById[match.routeId];
|
|
702
|
+
const handleError = (err) => {
|
|
703
|
+
if (isRedirect(err) || isNotFound(err)) {
|
|
704
|
+
handleMatchSpecialError(match, err);
|
|
705
|
+
}
|
|
706
|
+
};
|
|
707
|
+
const loaderContext = {
|
|
708
|
+
params: match.params,
|
|
709
|
+
deps: match.loaderDeps,
|
|
710
|
+
preload: !!preload,
|
|
711
|
+
parentMatchPromise,
|
|
712
|
+
abortController: match.abortController,
|
|
713
|
+
context: match.context,
|
|
714
|
+
location,
|
|
715
|
+
navigate: (opts) => this.navigate({ ...opts, from: match.pathname }),
|
|
716
|
+
cause: preload ? "preload" : match.cause,
|
|
717
|
+
route
|
|
718
|
+
};
|
|
719
|
+
const fetch = async () => {
|
|
720
|
+
var _a2, _b2, _c, _d, _e, _f, _g, _h, _i;
|
|
721
|
+
const existing = getRouteMatch(this.state, match.id);
|
|
722
|
+
let lazyPromise = Promise.resolve();
|
|
723
|
+
let componentsPromise = Promise.resolve();
|
|
724
|
+
let loaderPromise = existing.loaderPromise;
|
|
725
|
+
let loadPromise = existing.loadPromise;
|
|
726
|
+
const potentialPendingMinPromise = async () => {
|
|
727
|
+
const latestMatch = getRouteMatch(this.state, match.id);
|
|
728
|
+
if (latestMatch == null ? void 0 : latestMatch.minPendingPromise) {
|
|
729
|
+
await latestMatch.minPendingPromise;
|
|
730
|
+
if (latestPromise = checkLatest())
|
|
731
|
+
return await latestPromise;
|
|
732
|
+
updateMatch(latestMatch.id, (prev) => ({
|
|
733
|
+
...prev,
|
|
734
|
+
minPendingPromise: void 0
|
|
735
|
+
}));
|
|
736
|
+
}
|
|
737
|
+
};
|
|
738
|
+
try {
|
|
739
|
+
if (!match.isFetching) {
|
|
740
|
+
matches[index] = match = {
|
|
741
|
+
...match,
|
|
742
|
+
isFetching: true,
|
|
743
|
+
fetchCount: match.fetchCount + 1
|
|
744
|
+
};
|
|
745
|
+
lazyPromise = ((_a2 = route.lazyFn) == null ? void 0 : _a2.call(route).then((lazyRoute) => {
|
|
746
|
+
Object.assign(route.options, lazyRoute.options);
|
|
747
|
+
})) || Promise.resolve();
|
|
748
|
+
componentsPromise = lazyPromise.then(
|
|
749
|
+
() => Promise.all(
|
|
750
|
+
componentTypes.map(async (type) => {
|
|
751
|
+
const component = route.options[type];
|
|
752
|
+
if (component == null ? void 0 : component.preload) {
|
|
753
|
+
await component.preload();
|
|
754
|
+
}
|
|
755
|
+
})
|
|
756
|
+
)
|
|
757
|
+
);
|
|
758
|
+
await lazyPromise;
|
|
759
|
+
if (latestPromise = checkLatest())
|
|
760
|
+
return await latestPromise;
|
|
761
|
+
loaderPromise = (_c = (_b2 = route.options).loader) == null ? void 0 : _c.call(_b2, loaderContext);
|
|
762
|
+
const previousResolve = loadPromise.resolve;
|
|
763
|
+
loadPromise = createControlledPromise(
|
|
764
|
+
// Resolve the old when we we resolve the new one
|
|
765
|
+
previousResolve
|
|
766
|
+
);
|
|
767
|
+
}
|
|
768
|
+
matches[index] = match = updateMatch(match.id, (prev) => ({
|
|
769
|
+
...prev,
|
|
770
|
+
loaderPromise,
|
|
771
|
+
loadPromise
|
|
772
|
+
}));
|
|
773
|
+
const loaderData = await loaderPromise;
|
|
774
|
+
if (latestPromise = checkLatest())
|
|
775
|
+
return await latestPromise;
|
|
776
|
+
handleError(loaderData);
|
|
777
|
+
if (latestPromise = checkLatest())
|
|
778
|
+
return await latestPromise;
|
|
779
|
+
await potentialPendingMinPromise();
|
|
780
|
+
if (latestPromise = checkLatest())
|
|
781
|
+
return await latestPromise;
|
|
782
|
+
const meta = (_e = (_d = route.options).meta) == null ? void 0 : _e.call(_d, {
|
|
783
|
+
params: match.params,
|
|
784
|
+
loaderData
|
|
785
|
+
});
|
|
786
|
+
const headers = (_g = (_f = route.options).headers) == null ? void 0 : _g.call(_f, {
|
|
787
|
+
loaderData
|
|
788
|
+
});
|
|
789
|
+
matches[index] = match = updateMatch(match.id, (prev) => ({
|
|
790
|
+
...prev,
|
|
791
|
+
error: void 0,
|
|
792
|
+
status: "success",
|
|
793
|
+
isFetching: false,
|
|
794
|
+
updatedAt: Date.now(),
|
|
795
|
+
loaderData,
|
|
796
|
+
meta,
|
|
797
|
+
headers
|
|
798
|
+
}));
|
|
799
|
+
} catch (e) {
|
|
800
|
+
let error = e;
|
|
801
|
+
if (latestPromise = checkLatest())
|
|
802
|
+
return await latestPromise;
|
|
803
|
+
await potentialPendingMinPromise();
|
|
804
|
+
if (latestPromise = checkLatest())
|
|
805
|
+
return await latestPromise;
|
|
806
|
+
handleError(e);
|
|
807
|
+
try {
|
|
808
|
+
(_i = (_h = route.options).onError) == null ? void 0 : _i.call(_h, e);
|
|
809
|
+
} catch (onErrorError) {
|
|
810
|
+
error = onErrorError;
|
|
811
|
+
handleError(onErrorError);
|
|
812
|
+
}
|
|
813
|
+
matches[index] = match = updateMatch(match.id, (prev) => ({
|
|
814
|
+
...prev,
|
|
815
|
+
error,
|
|
816
|
+
status: "error",
|
|
817
|
+
isFetching: false
|
|
818
|
+
}));
|
|
819
|
+
}
|
|
820
|
+
await componentsPromise;
|
|
821
|
+
if (latestPromise = checkLatest())
|
|
822
|
+
return await latestPromise;
|
|
823
|
+
loadPromise.resolve();
|
|
824
|
+
};
|
|
825
|
+
const age = Date.now() - match.updatedAt;
|
|
826
|
+
const staleAge = preload ? route.options.preloadStaleTime ?? this.options.defaultPreloadStaleTime ?? 3e4 : route.options.staleTime ?? this.options.defaultStaleTime ?? 0;
|
|
827
|
+
const shouldReloadOption = route.options.shouldReload;
|
|
828
|
+
const shouldReload = typeof shouldReloadOption === "function" ? shouldReloadOption(loaderContext) : shouldReloadOption;
|
|
829
|
+
matches[index] = match = {
|
|
830
|
+
...match,
|
|
831
|
+
preload: !!preload && !this.state.matches.find((d) => d.id === match.id)
|
|
832
|
+
};
|
|
833
|
+
const fetchWithRedirect = async () => {
|
|
834
|
+
try {
|
|
835
|
+
await fetch();
|
|
836
|
+
} catch (err) {
|
|
837
|
+
if (latestPromise = checkLatest())
|
|
838
|
+
return await latestPromise;
|
|
839
|
+
if (isRedirect(err)) {
|
|
840
|
+
const redirect = this.resolveRedirect(err);
|
|
841
|
+
if (!preload && !this.isServer) {
|
|
842
|
+
this.navigate({ ...redirect, replace: true });
|
|
843
|
+
}
|
|
844
|
+
throw redirect;
|
|
845
|
+
} else if (isNotFound(err)) {
|
|
846
|
+
if (!preload)
|
|
847
|
+
this.handleNotFound(matches, err);
|
|
848
|
+
throw err;
|
|
849
|
+
}
|
|
850
|
+
handleError(err);
|
|
851
|
+
}
|
|
852
|
+
};
|
|
853
|
+
if (match.status === "success" && (match.invalid || (shouldReload ?? age > staleAge))) {
|
|
854
|
+
fetchWithRedirect();
|
|
855
|
+
return;
|
|
856
|
+
}
|
|
857
|
+
if (match.status !== "success") {
|
|
858
|
+
await fetchWithRedirect();
|
|
859
|
+
}
|
|
860
|
+
})
|
|
861
|
+
);
|
|
862
|
+
if (latestPromise = checkLatest())
|
|
863
|
+
return await latestPromise;
|
|
864
|
+
resolveAll();
|
|
865
|
+
} catch (err) {
|
|
866
|
+
rejectAll(err);
|
|
827
867
|
}
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
868
|
+
})();
|
|
869
|
+
});
|
|
870
|
+
} catch (err) {
|
|
871
|
+
if (isRedirect(err) || isNotFound(err)) {
|
|
872
|
+
throw err;
|
|
873
|
+
}
|
|
874
|
+
}
|
|
833
875
|
return matches;
|
|
834
876
|
};
|
|
835
877
|
this.invalidate = () => {
|
|
@@ -850,53 +892,57 @@ class Router {
|
|
|
850
892
|
return this.load();
|
|
851
893
|
};
|
|
852
894
|
this.load = async () => {
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
toLocation: next,
|
|
863
|
-
pathChanged: pathDidChange
|
|
864
|
-
});
|
|
865
|
-
let pendingMatches;
|
|
866
|
-
const previousMatches = this.state.matches;
|
|
867
|
-
this.__store.batch(() => {
|
|
868
|
-
this.cleanCache();
|
|
869
|
-
pendingMatches = this.matchRoutes(next.pathname, next.search, {
|
|
870
|
-
debug: true
|
|
871
|
-
});
|
|
872
|
-
this.__store.setState((s) => ({
|
|
873
|
-
...s,
|
|
874
|
-
isLoading: true,
|
|
875
|
-
location: next,
|
|
876
|
-
pendingMatches,
|
|
877
|
-
cachedMatches: s.cachedMatches.filter((d) => {
|
|
878
|
-
return !pendingMatches.find((e) => e.id === d.id);
|
|
879
|
-
})
|
|
880
|
-
}));
|
|
881
|
-
});
|
|
895
|
+
let resolveLoad;
|
|
896
|
+
let rejectLoad;
|
|
897
|
+
const promise = new Promise((resolve, reject) => {
|
|
898
|
+
resolveLoad = resolve;
|
|
899
|
+
rejectLoad = reject;
|
|
900
|
+
});
|
|
901
|
+
this.latestLoadPromise = promise;
|
|
902
|
+
let latestPromise;
|
|
903
|
+
(async () => {
|
|
882
904
|
try {
|
|
905
|
+
const next = this.latestLocation;
|
|
906
|
+
const prevLocation = this.state.resolvedLocation;
|
|
907
|
+
const pathDidChange = prevLocation.href !== next.href;
|
|
908
|
+
this.cancelMatches();
|
|
909
|
+
this.emit({
|
|
910
|
+
type: "onBeforeLoad",
|
|
911
|
+
fromLocation: prevLocation,
|
|
912
|
+
toLocation: next,
|
|
913
|
+
pathChanged: pathDidChange
|
|
914
|
+
});
|
|
915
|
+
let pendingMatches;
|
|
916
|
+
const previousMatches = this.state.matches;
|
|
917
|
+
this.__store.batch(() => {
|
|
918
|
+
this.cleanCache();
|
|
919
|
+
pendingMatches = this.matchRoutes(next.pathname, next.search);
|
|
920
|
+
this.__store.setState((s) => ({
|
|
921
|
+
...s,
|
|
922
|
+
isLoading: true,
|
|
923
|
+
location: next,
|
|
924
|
+
pendingMatches,
|
|
925
|
+
cachedMatches: s.cachedMatches.filter((d) => {
|
|
926
|
+
return !pendingMatches.find((e) => e.id === d.id);
|
|
927
|
+
})
|
|
928
|
+
}));
|
|
929
|
+
});
|
|
883
930
|
let redirect;
|
|
884
931
|
let notFound;
|
|
885
932
|
try {
|
|
886
|
-
|
|
933
|
+
const loadMatchesPromise = this.loadMatches({
|
|
887
934
|
matches: pendingMatches,
|
|
888
935
|
location: next,
|
|
889
936
|
checkLatest: () => this.checkLatest(promise)
|
|
890
937
|
});
|
|
938
|
+
if (previousMatches.length || this.isServer) {
|
|
939
|
+
await loadMatchesPromise;
|
|
940
|
+
}
|
|
891
941
|
} catch (err) {
|
|
892
942
|
if (isRedirect(err)) {
|
|
893
|
-
redirect =
|
|
894
|
-
if (!isServer) {
|
|
895
|
-
this.navigate({ ...redirect, replace: true });
|
|
896
|
-
}
|
|
943
|
+
redirect = err;
|
|
897
944
|
} else if (isNotFound(err)) {
|
|
898
945
|
notFound = err;
|
|
899
|
-
this.handleNotFound(pendingMatches, err);
|
|
900
946
|
}
|
|
901
947
|
}
|
|
902
948
|
if (latestPromise = this.checkLatest(promise)) {
|
|
@@ -942,16 +988,15 @@ class Router {
|
|
|
942
988
|
toLocation: next,
|
|
943
989
|
pathChanged: pathDidChange
|
|
944
990
|
});
|
|
945
|
-
|
|
991
|
+
resolveLoad();
|
|
946
992
|
} catch (err) {
|
|
947
993
|
if (latestPromise = this.checkLatest(promise)) {
|
|
948
994
|
return latestPromise;
|
|
949
995
|
}
|
|
950
|
-
console.
|
|
951
|
-
|
|
996
|
+
console.error("Load Error", err);
|
|
997
|
+
rejectLoad(err);
|
|
952
998
|
}
|
|
953
|
-
});
|
|
954
|
-
this.latestLoadPromise = promise;
|
|
999
|
+
})();
|
|
955
1000
|
return this.latestLoadPromise;
|
|
956
1001
|
};
|
|
957
1002
|
this.resolveRedirect = (err) => {
|
|
@@ -1015,7 +1060,11 @@ class Router {
|
|
|
1015
1060
|
return matches;
|
|
1016
1061
|
} catch (err) {
|
|
1017
1062
|
if (isRedirect(err)) {
|
|
1018
|
-
return await this.preloadRoute(
|
|
1063
|
+
return await this.preloadRoute({
|
|
1064
|
+
_fromDest: next,
|
|
1065
|
+
from: next.pathname,
|
|
1066
|
+
...err
|
|
1067
|
+
});
|
|
1019
1068
|
}
|
|
1020
1069
|
console.error(err);
|
|
1021
1070
|
return void 0;
|
|
@@ -1156,8 +1205,7 @@ class Router {
|
|
|
1156
1205
|
this.__store.setState((s) => {
|
|
1157
1206
|
return {
|
|
1158
1207
|
...s,
|
|
1159
|
-
matches
|
|
1160
|
-
lastUpdated: Date.now()
|
|
1208
|
+
matches
|
|
1161
1209
|
};
|
|
1162
1210
|
});
|
|
1163
1211
|
};
|
|
@@ -1232,7 +1280,6 @@ function getInitialRouterState(location) {
|
|
|
1232
1280
|
matches: [],
|
|
1233
1281
|
pendingMatches: [],
|
|
1234
1282
|
cachedMatches: [],
|
|
1235
|
-
lastUpdated: 0,
|
|
1236
1283
|
statusCode: 200
|
|
1237
1284
|
};
|
|
1238
1285
|
}
|