@tanstack/react-router 1.31.1 → 1.31.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.
@@ -174,7 +174,9 @@ class Router {
174
174
  });
175
175
  };
176
176
  this.checkLatest = (promise) => {
177
- return this.latestLoadPromise !== promise ? this.latestLoadPromise : void 0;
177
+ if (this.latestLoadPromise !== promise) {
178
+ throw this.latestLoadPromise;
179
+ }
178
180
  };
179
181
  this.parseLocation = (previousLocation) => {
180
182
  const parse = ({
@@ -573,14 +575,131 @@ class Router {
573
575
  // to: toString,
574
576
  });
575
577
  };
578
+ this.load = async () => {
579
+ const promise = utils.createControlledPromise();
580
+ this.latestLoadPromise = promise;
581
+ let redirect;
582
+ let notFound$1;
583
+ this.startReactTransition(async () => {
584
+ try {
585
+ const next = this.latestLocation;
586
+ const prevLocation = this.state.resolvedLocation;
587
+ const pathDidChange = prevLocation.href !== next.href;
588
+ this.cancelMatches();
589
+ this.emit({
590
+ type: "onBeforeLoad",
591
+ fromLocation: prevLocation,
592
+ toLocation: next,
593
+ pathChanged: pathDidChange
594
+ });
595
+ let pendingMatches;
596
+ this.__store.batch(() => {
597
+ this.cleanCache();
598
+ pendingMatches = this.matchRoutes(next.pathname, next.search);
599
+ this.__store.setState((s) => ({
600
+ ...s,
601
+ status: "pending",
602
+ isLoading: true,
603
+ location: next,
604
+ pendingMatches,
605
+ // If a cached moved to pendingMatches, remove it from cachedMatches
606
+ cachedMatches: s.cachedMatches.filter((d) => {
607
+ return !pendingMatches.find((e) => e.id === d.id);
608
+ })
609
+ }));
610
+ });
611
+ await this.loadMatches({
612
+ matches: pendingMatches,
613
+ location: next,
614
+ checkLatest: () => this.checkLatest(promise),
615
+ onReady: async () => {
616
+ await this.startViewTransition(async () => {
617
+ let exitingMatches;
618
+ let enteringMatches;
619
+ let stayingMatches;
620
+ this.__store.batch(() => {
621
+ this.__store.setState((s) => {
622
+ const previousMatches = s.matches;
623
+ const newMatches = s.pendingMatches || s.matches;
624
+ exitingMatches = previousMatches.filter(
625
+ (match) => !newMatches.find((d) => d.id === match.id)
626
+ );
627
+ enteringMatches = newMatches.filter(
628
+ (match) => !previousMatches.find((d) => d.id === match.id)
629
+ );
630
+ stayingMatches = previousMatches.filter(
631
+ (match) => newMatches.find((d) => d.id === match.id)
632
+ );
633
+ return {
634
+ ...s,
635
+ isLoading: false,
636
+ matches: newMatches,
637
+ pendingMatches: void 0,
638
+ cachedMatches: [
639
+ ...s.cachedMatches,
640
+ ...exitingMatches.filter((d) => d.status !== "error")
641
+ ]
642
+ };
643
+ });
644
+ this.cleanCache();
645
+ });
646
+ [
647
+ [exitingMatches, "onLeave"],
648
+ [enteringMatches, "onEnter"],
649
+ [stayingMatches, "onStay"]
650
+ ].forEach(([matches, hook]) => {
651
+ matches.forEach((match) => {
652
+ var _a, _b;
653
+ (_b = (_a = this.looseRoutesById[match.routeId].options)[hook]) == null ? void 0 : _b.call(_a, match);
654
+ });
655
+ });
656
+ });
657
+ }
658
+ });
659
+ } catch (err) {
660
+ if (redirects.isResolvedRedirect(err)) {
661
+ redirect = err;
662
+ if (!this.isServer) {
663
+ this.navigate({ ...err, replace: true });
664
+ this.load();
665
+ }
666
+ } else if (notFound.isNotFound(err)) {
667
+ notFound$1 = err;
668
+ }
669
+ this.__store.setState((s) => ({
670
+ ...s,
671
+ statusCode: (redirect == null ? void 0 : redirect.statusCode) || notFound$1 ? 404 : s.matches.some((d) => d.status === "error") ? 500 : 200,
672
+ redirect
673
+ }));
674
+ }
675
+ promise.resolve();
676
+ });
677
+ return this.latestLoadPromise;
678
+ };
679
+ this.startViewTransition = async (fn) => {
680
+ var _a, _b;
681
+ const shouldViewTransition = this.shouldViewTransition ?? this.options.defaultViewTransition;
682
+ delete this.shouldViewTransition;
683
+ ((_b = (_a = shouldViewTransition && typeof document !== "undefined" ? document : void 0) == null ? void 0 : _a.startViewTransition) == null ? void 0 : _b.call(_a, fn)) || fn();
684
+ };
576
685
  this.loadMatches = async ({
577
686
  checkLatest,
578
687
  location,
579
688
  matches,
580
- preload
689
+ preload,
690
+ onReady
581
691
  }) => {
582
- let latestPromise;
583
692
  let firstBadMatchIndex;
693
+ let rendered = false;
694
+ const triggerOnReady = async () => {
695
+ if (!rendered) {
696
+ rendered = true;
697
+ await (onReady == null ? void 0 : onReady());
698
+ }
699
+ };
700
+ if (!this.isServer && !this.state.matches.length) {
701
+ triggerOnReady();
702
+ }
584
703
  const updateMatch = (id, updater, opts) => {
585
704
  var _a;
586
705
  let updated;
@@ -598,38 +717,51 @@ class Router {
598
717
  });
599
718
  return updated;
600
719
  };
720
+ const handleRedirectAndNotFound = (match, err) => {
721
+ if (redirects.isResolvedRedirect(err))
722
+ throw err;
723
+ if (redirects.isRedirect(err) || notFound.isNotFound(err)) {
724
+ updateMatch(match.id, (prev) => ({
725
+ ...prev,
726
+ status: redirects.isRedirect(err) ? "redirected" : notFound.isNotFound(err) ? "notFound" : "error",
727
+ isFetching: false,
728
+ error: err
729
+ }));
730
+ rendered = true;
731
+ if (!err.routeId) {
732
+ err.routeId = match.routeId;
733
+ }
734
+ if (redirects.isRedirect(err)) {
735
+ err = this.resolveRedirect(err);
736
+ throw err;
737
+ } else if (notFound.isNotFound(err)) {
738
+ this.handleNotFound(matches, err);
739
+ throw err;
740
+ }
741
+ }
742
+ };
601
743
  try {
602
744
  await new Promise((resolveAll, rejectAll) => {
603
745
  ;
604
746
  (async () => {
605
747
  var _a, _b;
606
748
  try {
607
- const handleRedirectAndNotFound = (match, err) => {
608
- if (redirects.isRedirect(err) || notFound.isNotFound(err)) {
609
- updateMatch(match.id, (prev) => ({
610
- ...prev,
611
- status: redirects.isRedirect(err) ? "redirected" : notFound.isNotFound(err) ? "notFound" : "error",
612
- isFetching: false,
613
- error: err
614
- }));
615
- if (!err.routeId) {
616
- ;
617
- err.routeId = match.routeId;
618
- }
619
- if (redirects.isRedirect(err)) {
620
- err = this.resolveRedirect(err);
621
- throw err;
622
- } else if (notFound.isNotFound(err)) {
623
- this.handleNotFound(matches, err);
624
- throw err;
625
- }
626
- }
627
- };
628
749
  for (let [index, match] of matches.entries()) {
629
750
  const parentMatch = matches[index - 1];
630
751
  const route2 = this.looseRoutesById[match.routeId];
631
752
  const abortController = new AbortController();
632
753
  let loadPromise = match.loadPromise;
754
+ const pendingMs = route2.options.pendingMs ?? this.options.defaultPendingMs;
755
+ const shouldPending = !!(onReady && !this.isServer && !preload && (route2.options.loader || route2.options.beforeLoad) && typeof pendingMs === "number" && pendingMs !== Infinity && (route2.options.pendingComponent ?? this.options.defaultPendingComponent));
756
+ if (shouldPending) {
757
+ setTimeout(() => {
758
+ try {
759
+ checkLatest();
760
+ triggerOnReady();
761
+ } catch {
762
+ }
763
+ }, pendingMs);
764
+ }
633
765
  if (match.isFetching) {
634
766
  continue;
635
767
  }
@@ -670,19 +802,6 @@ class Router {
670
802
  }
671
803
  try {
672
804
  const parentContext = (parentMatch == null ? void 0 : parentMatch.context) ?? this.options.context ?? {};
673
- const pendingMs = route2.options.pendingMs ?? this.options.defaultPendingMs;
674
- const pendingPromise = typeof pendingMs !== "number" || pendingMs <= 0 ? Promise.resolve() : new Promise((r) => {
675
- if (pendingMs !== Infinity)
676
- setTimeout(r, pendingMs);
677
- });
678
- const shouldPending = !this.isServer && !preload && (route2.options.loader || route2.options.beforeLoad) && typeof pendingMs === "number" && (route2.options.pendingComponent ?? this.options.defaultPendingComponent);
679
- if (shouldPending) {
680
- pendingPromise.then(async () => {
681
- if (latestPromise = checkLatest())
682
- return latestPromise;
683
- resolveAll();
684
- });
685
- }
686
805
  const beforeLoadContext = await ((_b = (_a = route2.options).beforeLoad) == null ? void 0 : _b.call(_a, {
687
806
  search: match.search,
688
807
  abortController,
@@ -694,8 +813,7 @@ class Router {
694
813
  buildLocation: this.buildLocation,
695
814
  cause: preload ? "preload" : match.cause
696
815
  })) ?? {};
697
- if (latestPromise = checkLatest())
698
- return latestPromise;
816
+ checkLatest();
699
817
  if (redirects.isRedirect(beforeLoadContext) || notFound.isNotFound(beforeLoadContext)) {
700
818
  handleSerialError(beforeLoadContext, "BEFORE_LOAD");
701
819
  }
@@ -719,8 +837,7 @@ class Router {
719
837
  updateMatch(match.id, () => match);
720
838
  }
721
839
  }
722
- if (latestPromise = checkLatest())
723
- return latestPromise;
840
+ checkLatest();
724
841
  const validResolvedMatches = matches.slice(0, firstBadMatchIndex);
725
842
  const matchPromises = [];
726
843
  await Promise.all(
@@ -749,8 +866,7 @@ class Router {
749
866
  const latestMatch = RouterProvider.getRouteMatch(this.state, match.id);
750
867
  if (latestMatch == null ? void 0 : latestMatch.minPendingPromise) {
751
868
  await latestMatch.minPendingPromise;
752
- if (latestPromise = checkLatest())
753
- return await latestPromise;
869
+ checkLatest();
754
870
  updateMatch(latestMatch.id, (prev) => ({
755
871
  ...prev,
756
872
  minPendingPromise: void 0
@@ -781,8 +897,7 @@ class Router {
781
897
  )
782
898
  );
783
899
  await lazyPromise;
784
- if (latestPromise = checkLatest())
785
- return await latestPromise;
900
+ checkLatest();
786
901
  loaderPromise = (_c = (_b2 = route2.options).loader) == null ? void 0 : _c.call(_b2, loaderContext);
787
902
  matches[index] = match = updateMatch(
788
903
  match.id,
@@ -793,14 +908,10 @@ class Router {
793
908
  );
794
909
  }
795
910
  const loaderData = await loaderPromise;
796
- if (latestPromise = checkLatest())
797
- return await latestPromise;
911
+ checkLatest();
798
912
  handleRedirectAndNotFound(match, loaderData);
799
- if (latestPromise = checkLatest())
800
- return await latestPromise;
801
913
  await potentialPendingMinPromise();
802
- if (latestPromise = checkLatest())
803
- return await latestPromise;
914
+ checkLatest();
804
915
  const meta = (_e = (_d = route2.options).meta) == null ? void 0 : _e.call(_d, {
805
916
  params: match.params,
806
917
  loaderData
@@ -819,12 +930,10 @@ class Router {
819
930
  headers
820
931
  }));
821
932
  } catch (e) {
933
+ checkLatest();
822
934
  let error = e;
823
- if (latestPromise = checkLatest())
824
- return await latestPromise;
825
935
  await potentialPendingMinPromise();
826
- if (latestPromise = checkLatest())
827
- return await latestPromise;
936
+ checkLatest();
828
937
  handleRedirectAndNotFound(match, e);
829
938
  try {
830
939
  (_i = (_h = route2.options).onError) == null ? void 0 : _i.call(_h, e);
@@ -840,8 +949,7 @@ class Router {
840
949
  }));
841
950
  }
842
951
  await componentsPromise;
843
- if (latestPromise = checkLatest())
844
- return await latestPromise;
952
+ checkLatest();
845
953
  match.loadPromise.resolve();
846
954
  };
847
955
  const age = Date.now() - match.updatedAt;
@@ -856,8 +964,7 @@ class Router {
856
964
  try {
857
965
  await fetch();
858
966
  } catch (err) {
859
- if (latestPromise = checkLatest())
860
- return await latestPromise;
967
+ checkLatest();
861
968
  handleRedirectAndNotFound(match, err);
862
969
  }
863
970
  };
@@ -870,14 +977,14 @@ class Router {
870
977
  }
871
978
  })
872
979
  );
873
- if (latestPromise = checkLatest())
874
- return await latestPromise;
980
+ checkLatest();
875
981
  resolveAll();
876
982
  } catch (err) {
877
983
  rejectAll(err);
878
984
  }
879
985
  })();
880
986
  });
987
+ await triggerOnReady();
881
988
  } catch (err) {
882
989
  if (redirects.isRedirect(err) || notFound.isNotFound(err)) {
883
990
  throw err;
@@ -902,122 +1009,6 @@ class Router {
902
1009
  });
903
1010
  return this.load();
904
1011
  };
905
- this.load = async () => {
906
- let resolveLoad;
907
- let rejectLoad;
908
- const promise = new Promise((resolve, reject) => {
909
- resolveLoad = resolve;
910
- rejectLoad = reject;
911
- });
912
- this.latestLoadPromise = promise;
913
- let latestPromise;
914
- this.startReactTransition(async () => {
915
- var _a, _b;
916
- try {
917
- const next = this.latestLocation;
918
- const prevLocation = this.state.resolvedLocation;
919
- const pathDidChange = prevLocation.href !== next.href;
920
- this.cancelMatches();
921
- this.emit({
922
- type: "onBeforeLoad",
923
- fromLocation: prevLocation,
924
- toLocation: next,
925
- pathChanged: pathDidChange
926
- });
927
- let pendingMatches;
928
- const previousMatches = this.state.matches;
929
- this.__store.batch(() => {
930
- this.cleanCache();
931
- pendingMatches = this.matchRoutes(next.pathname, next.search);
932
- this.__store.setState((s) => ({
933
- ...s,
934
- status: "pending",
935
- isLoading: true,
936
- location: next,
937
- pendingMatches,
938
- cachedMatches: s.cachedMatches.filter((d) => {
939
- return !pendingMatches.find((e) => e.id === d.id);
940
- })
941
- }));
942
- });
943
- let redirect;
944
- let notFound$1;
945
- const loadMatches = () => this.loadMatches({
946
- matches: pendingMatches,
947
- location: next,
948
- checkLatest: () => this.checkLatest(promise)
949
- });
950
- if (previousMatches.length || this.isServer) {
951
- try {
952
- await loadMatches();
953
- } catch (err) {
954
- if (redirects.isRedirect(err)) {
955
- redirect = err;
956
- } else if (notFound.isNotFound(err)) {
957
- notFound$1 = err;
958
- }
959
- }
960
- } else {
961
- loadMatches().catch((err) => {
962
- if (redirects.isRedirect(err)) {
963
- this.navigate({ ...err, replace: true });
964
- }
965
- this.load();
966
- });
967
- }
968
- if (latestPromise = this.checkLatest(promise)) {
969
- return latestPromise;
970
- }
971
- const exitingMatches = previousMatches.filter(
972
- (match) => !pendingMatches.find((d) => d.id === match.id)
973
- );
974
- const enteringMatches = pendingMatches.filter(
975
- (match) => !previousMatches.find((d) => d.id === match.id)
976
- );
977
- const stayingMatches = previousMatches.filter(
978
- (match) => pendingMatches.find((d) => d.id === match.id)
979
- );
980
- const shouldViewTransition = this.shouldViewTransition ?? this.options.defaultViewTransition;
981
- delete this.shouldViewTransition;
982
- const apply = () => {
983
- this.__store.batch(() => {
984
- this.__store.setState((s) => ({
985
- ...s,
986
- isLoading: false,
987
- matches: s.pendingMatches,
988
- pendingMatches: void 0,
989
- cachedMatches: [
990
- ...s.cachedMatches,
991
- ...exitingMatches.filter((d) => d.status !== "error")
992
- ],
993
- statusCode: (redirect == null ? void 0 : redirect.statusCode) || notFound$1 ? 404 : s.matches.some((d) => d.status === "error") ? 500 : 200,
994
- redirect
995
- }));
996
- this.cleanCache();
997
- });
998
- [
999
- [exitingMatches, "onLeave"],
1000
- [enteringMatches, "onEnter"],
1001
- [stayingMatches, "onStay"]
1002
- ].forEach(([matches, hook]) => {
1003
- matches.forEach((match) => {
1004
- var _a2, _b2;
1005
- (_b2 = (_a2 = this.looseRoutesById[match.routeId].options)[hook]) == null ? void 0 : _b2.call(_a2, match);
1006
- });
1007
- });
1008
- resolveLoad();
1009
- };
1010
- ((_b = (_a = shouldViewTransition && typeof document !== "undefined" ? document : void 0) == null ? void 0 : _a.startViewTransition) == null ? void 0 : _b.call(_a, apply)) || apply();
1011
- } catch (err) {
1012
- if (latestPromise = this.checkLatest(promise)) {
1013
- return latestPromise;
1014
- }
1015
- console.error("Load Error", err);
1016
- rejectLoad(err);
1017
- }
1018
- });
1019
- return this.latestLoadPromise;
1020
- };
1021
1012
  this.resolveRedirect = (err) => {
1022
1013
  const redirect = err;
1023
1014
  if (!redirect.href) {