@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.
Files changed (45) hide show
  1. package/dist/cjs/Matches.cjs +45 -23
  2. package/dist/cjs/Matches.cjs.map +1 -1
  3. package/dist/cjs/Matches.d.cts +5 -6
  4. package/dist/cjs/fileRoute.d.cts +4 -4
  5. package/dist/cjs/index.cjs +0 -1
  6. package/dist/cjs/index.cjs.map +1 -1
  7. package/dist/cjs/index.d.cts +1 -1
  8. package/dist/cjs/link.cjs.map +1 -1
  9. package/dist/cjs/link.d.cts +2 -0
  10. package/dist/cjs/redirects.cjs.map +1 -1
  11. package/dist/cjs/redirects.d.cts +3 -1
  12. package/dist/cjs/route.cjs.map +1 -1
  13. package/dist/cjs/route.d.cts +2 -2
  14. package/dist/cjs/router.cjs +374 -327
  15. package/dist/cjs/router.cjs.map +1 -1
  16. package/dist/cjs/router.d.cts +2 -2
  17. package/dist/cjs/utils.cjs +20 -2
  18. package/dist/cjs/utils.cjs.map +1 -1
  19. package/dist/cjs/utils.d.cts +6 -1
  20. package/dist/esm/Matches.d.ts +5 -6
  21. package/dist/esm/Matches.js +46 -24
  22. package/dist/esm/Matches.js.map +1 -1
  23. package/dist/esm/fileRoute.d.ts +4 -4
  24. package/dist/esm/index.d.ts +1 -1
  25. package/dist/esm/index.js +1 -2
  26. package/dist/esm/link.d.ts +2 -0
  27. package/dist/esm/link.js.map +1 -1
  28. package/dist/esm/redirects.d.ts +3 -1
  29. package/dist/esm/redirects.js.map +1 -1
  30. package/dist/esm/route.d.ts +2 -2
  31. package/dist/esm/route.js.map +1 -1
  32. package/dist/esm/router.d.ts +2 -2
  33. package/dist/esm/router.js +375 -328
  34. package/dist/esm/router.js.map +1 -1
  35. package/dist/esm/utils.d.ts +6 -1
  36. package/dist/esm/utils.js +20 -2
  37. package/dist/esm/utils.js.map +1 -1
  38. package/package.json +4 -2
  39. package/src/Matches.tsx +73 -35
  40. package/src/index.tsx +0 -1
  41. package/src/link.tsx +2 -0
  42. package/src/redirects.ts +4 -2
  43. package/src/route.ts +2 -7
  44. package/src/router.ts +498 -426
  45. package/src/utils.ts +31 -2
@@ -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, isServer, last, deepEqual, escapeJSON, functionalUpdate } from "./utils.js";
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
- const match = existingMatch ? {
329
- ...existingMatch,
330
- cause,
331
- params: routeParams
332
- } : {
333
- id: matchId,
334
- routeId: route.id,
335
- params: routeParams,
336
- pathname: joinPaths([this.basepath, interpolatedPath]),
337
- updatedAt: Date.now(),
338
- search: {},
339
- searchError: void 0,
340
- status: "pending",
341
- showPending: false,
342
- isFetching: false,
343
- error: void 0,
344
- paramsError: parseErrors[index],
345
- loadPromise: Promise.resolve(),
346
- routeContext: void 0,
347
- context: void 0,
348
- abortController: new AbortController(),
349
- fetchCount: 0,
350
- cause,
351
- loaderDeps,
352
- invalid: false,
353
- preload: false,
354
- links: (_d = (_c = route.options).links) == null ? void 0 : _d.call(_c),
355
- scripts: (_f = (_e = route.options).scripts) == null ? void 0 : _f.call(_e),
356
- staticData: route.options.staticData || {}
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 relevantMatches = this.state.pendingMatches || this.state.matches;
381
- const fromSearch = (
382
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
383
- ((_a = relevantMatches[relevantMatches.length - 1]) == null ? void 0 : _a.search) || this.latestLocation.search
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[(_b = last(fromMatches)) == null ? void 0 : _b.routeId];
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 = { ...(_c = last(fromMatches)) == null ? void 0 : _c.params };
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 = (match, opts) => {
568
- var _a2;
569
- const isPending = (_a2 = this.state.pendingMatches) == null ? void 0 : _a2.find(
570
- (d) => d.id === match.id
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 _a3, _b2;
588
+ var _a2, _b;
576
589
  return {
577
590
  ...s,
578
- [matchesKey]: (opts == null ? void 0 : opts.remove) ? (_a3 = s[matchesKey]) == null ? void 0 : _a3.filter((d) => d.id !== match.id) : (_b2 = s[matchesKey]) == null ? void 0 : _b2.map((d) => d.id === match.id ? match : d)
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
- ...match,
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
- for (let [index, match] of matches.entries()) {
596
- const parentMatch = matches[index - 1];
597
- const route = this.looseRoutesById[match.routeId];
598
- const abortController = new AbortController();
599
- const handleSerialError = (err, code) => {
600
- var _a2, _b2;
601
- err.routerCode = code;
602
- firstBadMatchIndex = firstBadMatchIndex ?? index;
603
- if (isRedirect(err) || isNotFound(err)) {
604
- handleMatchSpecialError(match, err);
605
- }
606
- try {
607
- (_b2 = (_a2 = route.options).onError) == null ? void 0 : _b2.call(_a2, err);
608
- } catch (errorHandlerErr) {
609
- err = errorHandlerErr;
610
- if (isRedirect(err) || isNotFound(err)) {
611
- handleMatchSpecialError(match, errorHandlerErr);
612
- }
613
- }
614
- matches[index] = match = {
615
- ...match,
616
- error: err,
617
- status: "error",
618
- updatedAt: Date.now(),
619
- abortController: new AbortController()
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
- isFetching: true,
705
- fetchCount: match.fetchCount + 1
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
- updateMatch(match);
732
- const loaderData = await loadPromise;
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 (latestPromise = checkLatest())
740
- return await latestPromise;
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
- await fetch();
793
- } catch (err) {
794
- console.info("Background Fetching Error", err);
795
- if (isRedirect(err)) {
796
- (this.state.pendingMatches || this.state.matches).find((d) => d.id === match.id);
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
- resolve();
829
- })
830
- );
831
- });
832
- await Promise.all(matchPromises);
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
- const promise = new Promise(async (resolve, reject) => {
854
- const next = this.latestLocation;
855
- const prevLocation = this.state.resolvedLocation;
856
- const pathDidChange = prevLocation.href !== next.href;
857
- let latestPromise;
858
- this.cancelMatches();
859
- this.emit({
860
- type: "onBeforeLoad",
861
- fromLocation: prevLocation,
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
- await this.loadMatches({
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 = this.resolveRedirect(err);
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
- resolve();
991
+ resolveLoad();
946
992
  } catch (err) {
947
993
  if (latestPromise = this.checkLatest(promise)) {
948
994
  return latestPromise;
949
995
  }
950
- console.log("Load Error", err);
951
- reject(err);
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(err);
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
  }