@tanstack/react-router 1.16.2 → 1.16.5

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.
@@ -2,7 +2,7 @@ import { createBrowserHistory, createMemoryHistory } from "@tanstack/history";
2
2
  import { Store } from "@tanstack/react-store";
3
3
  import { rootRouteId } from "./route.js";
4
4
  import { defaultStringifySearch, defaultParseSearch } from "./searchParams.js";
5
- import { replaceEqualDeep, pick, deepEqual, escapeJSON, last, functionalUpdate } from "./utils.js";
5
+ import { replaceEqualDeep, pick, isServer, deepEqual, escapeJSON, last, functionalUpdate } from "./utils.js";
6
6
  import { getRouteMatch } from "./RouterProvider.js";
7
7
  import { trimPath, trimPathLeft, parsePathname, resolvePath, cleanPath, matchPathname, trimPathRight, interpolatePath, joinPaths } from "./path.js";
8
8
  import invariant from "tiny-invariant";
@@ -552,111 +552,94 @@ class Router {
552
552
  };
553
553
  });
554
554
  };
555
- try {
556
- for (let [index, match] of matches.entries()) {
557
- const parentMatch = matches[index - 1];
558
- const route = this.looseRoutesById[match.routeId];
559
- const abortController = new AbortController();
560
- const handleErrorAndRedirect = (err, code) => {
561
- var _a2, _b2;
562
- err.routerCode = code;
563
- firstBadMatchIndex = firstBadMatchIndex ?? index;
564
- if (isRedirect(err)) {
565
- throw err;
566
- }
567
- if (isNotFound(err)) {
568
- this.updateMatchesWithNotFound(matches, match, err);
569
- }
570
- try {
571
- (_b2 = (_a2 = route.options).onError) == null ? void 0 : _b2.call(_a2, err);
572
- } catch (errorHandlerErr) {
573
- err = errorHandlerErr;
574
- if (isRedirect(errorHandlerErr)) {
575
- throw errorHandlerErr;
576
- }
577
- }
578
- matches[index] = match = {
579
- ...match,
580
- error: err,
581
- status: "error",
582
- updatedAt: Date.now(),
583
- abortController: new AbortController()
584
- };
585
- };
555
+ for (let [index, match] of matches.entries()) {
556
+ const parentMatch = matches[index - 1];
557
+ const route = this.looseRoutesById[match.routeId];
558
+ const abortController = new AbortController();
559
+ const handleError = (err, code) => {
560
+ var _a2, _b2;
561
+ err.routerCode = code;
562
+ firstBadMatchIndex = firstBadMatchIndex ?? index;
563
+ if (isRedirect(err)) {
564
+ throw err;
565
+ }
566
+ if (isNotFound(err)) {
567
+ err.routeId = match.routeId;
568
+ throw err;
569
+ }
586
570
  try {
587
- if (match.paramsError) {
588
- handleErrorAndRedirect(match.paramsError, "PARSE_PARAMS");
589
- }
590
- if (match.searchError) {
591
- handleErrorAndRedirect(match.searchError, "VALIDATE_SEARCH");
592
- }
593
- const parentContext = (parentMatch == null ? void 0 : parentMatch.context) ?? this.options.context ?? {};
594
- const pendingMs = route.options.pendingMs ?? this.options.defaultPendingMs;
595
- const pendingPromise = typeof pendingMs === "number" && pendingMs <= 0 ? Promise.resolve() : new Promise((r) => setTimeout(r, pendingMs));
596
- const beforeLoadContext = await ((_b = (_a = route.options).beforeLoad) == null ? void 0 : _b.call(_a, {
597
- search: match.search,
598
- abortController,
599
- params: match.params,
600
- preload: !!preload,
601
- context: parentContext,
602
- location: this.state.location,
603
- // TOOD: just expose state and router, etc
604
- navigate: (opts) => this.navigate({ ...opts, from: match.pathname }),
605
- buildLocation: this.buildLocation,
606
- cause: preload ? "preload" : match.cause
607
- })) ?? {};
608
- if (isRedirect(beforeLoadContext)) {
609
- throw beforeLoadContext;
571
+ (_b2 = (_a2 = route.options).onError) == null ? void 0 : _b2.call(_a2, err);
572
+ } catch (errorHandlerErr) {
573
+ err = errorHandlerErr;
574
+ if (isRedirect(errorHandlerErr)) {
575
+ throw errorHandlerErr;
610
576
  }
611
- const context = {
612
- ...parentContext,
613
- ...beforeLoadContext
614
- };
615
- matches[index] = match = {
616
- ...match,
617
- routeContext: replaceEqualDeep(
618
- match.routeContext,
619
- beforeLoadContext
620
- ),
621
- context: replaceEqualDeep(match.context, context),
622
- abortController,
623
- pendingPromise
624
- };
625
- } catch (err) {
626
- handleErrorAndRedirect(err, "BEFORE_LOAD");
627
- break;
628
577
  }
578
+ matches[index] = match = {
579
+ ...match,
580
+ error: err,
581
+ status: "error",
582
+ updatedAt: Date.now(),
583
+ abortController: new AbortController()
584
+ };
585
+ };
586
+ try {
587
+ if (match.paramsError) {
588
+ handleError(match.paramsError, "PARSE_PARAMS");
589
+ }
590
+ if (match.searchError) {
591
+ handleError(match.searchError, "VALIDATE_SEARCH");
592
+ }
593
+ const parentContext = (parentMatch == null ? void 0 : parentMatch.context) ?? this.options.context ?? {};
594
+ const pendingMs = route.options.pendingMs ?? this.options.defaultPendingMs;
595
+ const pendingPromise = typeof pendingMs === "number" && pendingMs <= 0 ? Promise.resolve() : new Promise((r) => setTimeout(r, pendingMs));
596
+ const beforeLoadContext = await ((_b = (_a = route.options).beforeLoad) == null ? void 0 : _b.call(_a, {
597
+ search: match.search,
598
+ abortController,
599
+ params: match.params,
600
+ preload: !!preload,
601
+ context: parentContext,
602
+ location: this.state.location,
603
+ // TOOD: just expose state and router, etc
604
+ navigate: (opts) => this.navigate({ ...opts, from: match.pathname }),
605
+ buildLocation: this.buildLocation,
606
+ cause: preload ? "preload" : match.cause
607
+ })) ?? {};
608
+ if (isRedirect(beforeLoadContext)) {
609
+ throw beforeLoadContext;
610
+ }
611
+ const context = {
612
+ ...parentContext,
613
+ ...beforeLoadContext
614
+ };
615
+ matches[index] = match = {
616
+ ...match,
617
+ routeContext: replaceEqualDeep(match.routeContext, beforeLoadContext),
618
+ context: replaceEqualDeep(match.context, context),
619
+ abortController,
620
+ pendingPromise
621
+ };
622
+ } catch (err) {
623
+ handleError(err, "BEFORE_LOAD");
624
+ break;
629
625
  }
630
- } catch (err) {
631
- if (isRedirect(err)) {
632
- if (!preload)
633
- this.navigate(err);
634
- return matches;
635
- }
636
- throw err;
637
626
  }
638
627
  const validResolvedMatches = matches.slice(0, firstBadMatchIndex);
639
628
  const matchPromises = [];
640
629
  validResolvedMatches.forEach((match, index) => {
641
630
  matchPromises.push(
642
- new Promise(async (resolve) => {
631
+ new Promise(async (resolve, reject) => {
643
632
  var _a2;
644
633
  const parentMatchPromise = matchPromises[index - 1];
645
634
  const route = this.looseRoutesById[match.routeId];
646
- const handleErrorAndRedirect = (err) => {
635
+ const handleError = (err) => {
647
636
  if (isRedirect(err)) {
648
- if (!preload) {
649
- this.navigate(err);
650
- }
651
- return true;
637
+ throw err;
652
638
  }
653
639
  if (isNotFound(err)) {
654
- if (!preload) {
655
- this.updateMatchesWithNotFound(matches, match, err);
656
- }
657
- return true;
640
+ err.routeId = match.routeId;
641
+ throw err;
658
642
  }
659
- return false;
660
643
  };
661
644
  let loadPromise;
662
645
  matches[index] = match = {
@@ -724,10 +707,7 @@ class Router {
724
707
  const loaderData = await loadPromise;
725
708
  if (latestPromise = checkLatest())
726
709
  return await latestPromise;
727
- if (isRedirect(loaderData) || isNotFound(loaderData)) {
728
- if (handleErrorAndRedirect(loaderData))
729
- return;
730
- }
710
+ handleError(loaderData);
731
711
  if (didShowPending && pendingMinMs) {
732
712
  await new Promise((r) => setTimeout(r, pendingMinMs));
733
713
  }
@@ -735,6 +715,7 @@ class Router {
735
715
  return await latestPromise;
736
716
  const [meta, headers] = await Promise.all([
737
717
  (_d = (_c = route.options).meta) == null ? void 0 : _d.call(_c, {
718
+ params: match.params,
738
719
  loaderData
739
720
  }),
740
721
  (_f = (_e = route.options).headers) == null ? void 0 : _f.call(_e, {
@@ -755,14 +736,12 @@ class Router {
755
736
  } catch (error) {
756
737
  if (latestPromise = checkLatest())
757
738
  return await latestPromise;
758
- if (handleErrorAndRedirect(error))
759
- return;
739
+ handleError(error);
760
740
  try {
761
741
  (_h = (_g = route.options).onError) == null ? void 0 : _h.call(_g, error);
762
742
  } catch (onErrorError) {
763
743
  error = onErrorError;
764
- if (handleErrorAndRedirect(onErrorError))
765
- return;
744
+ handleError(onErrorError);
766
745
  }
767
746
  matches[index] = match = {
768
747
  ...match,
@@ -782,23 +761,27 @@ class Router {
782
761
  ...match,
783
762
  preload: !!preload && !this.state.matches.find((d) => d.id === match.id)
784
763
  };
785
- if (match.status !== "success") {
786
- if (shouldPending) {
787
- (_a2 = match.pendingPromise) == null ? void 0 : _a2.then(async () => {
788
- if (latestPromise = checkLatest())
789
- return latestPromise;
790
- didShowPending = true;
791
- matches[index] = match = {
792
- ...match,
793
- showPending: true
794
- };
795
- updateMatch(match);
796
- resolve();
797
- });
764
+ try {
765
+ if (match.status !== "success") {
766
+ if (shouldPending) {
767
+ (_a2 = match.pendingPromise) == null ? void 0 : _a2.then(async () => {
768
+ if (latestPromise = checkLatest())
769
+ return latestPromise;
770
+ didShowPending = true;
771
+ matches[index] = match = {
772
+ ...match,
773
+ showPending: true
774
+ };
775
+ updateMatch(match);
776
+ resolve();
777
+ });
778
+ }
779
+ await fetch();
780
+ } else if (match.invalid || (shouldReload ?? age > staleAge)) {
781
+ fetch();
798
782
  }
799
- await fetch();
800
- } else if (match.invalid || (shouldReload ?? age > staleAge)) {
801
- fetch();
783
+ } catch (err) {
784
+ reject(err);
802
785
  }
803
786
  resolve();
804
787
  })
@@ -808,20 +791,6 @@ class Router {
808
791
  return matches;
809
792
  };
810
793
  this.invalidate = () => {
811
- const invalidate = (d) => ({
812
- ...d,
813
- invalid: true
814
- });
815
- this.__store.setState((s) => {
816
- var _a;
817
- return {
818
- ...s,
819
- matches: s.matches.map(invalidate),
820
- cachedMatches: s.cachedMatches.map(invalidate),
821
- pendingMatches: (_a = s.pendingMatches) == null ? void 0 : _a.map(invalidate)
822
- };
823
- });
824
- this.load();
825
794
  };
826
795
  this.load = async () => {
827
796
  const promise = new Promise(async (resolve, reject) => {
@@ -860,6 +829,11 @@ class Router {
860
829
  checkLatest: () => this.checkLatest(promise)
861
830
  });
862
831
  } catch (err) {
832
+ if (isRedirect(err)) {
833
+ this.handleRedirect(err);
834
+ } else if (isNotFound(err)) {
835
+ this.handleNotFound(pendingMatches, err);
836
+ }
863
837
  }
864
838
  if (latestPromise = this.checkLatest(promise)) {
865
839
  return latestPromise;
@@ -913,6 +887,15 @@ class Router {
913
887
  this.latestLoadPromise = promise;
914
888
  return this.latestLoadPromise;
915
889
  };
890
+ this.handleRedirect = (err) => {
891
+ if (!err.href) {
892
+ err.href = this.buildLocation(err).href;
893
+ }
894
+ if (isServer) {
895
+ throw err;
896
+ }
897
+ this.navigate(err);
898
+ };
916
899
  this.cleanCache = () => {
917
900
  this.__store.setState((s) => {
918
901
  return {
@@ -951,12 +934,19 @@ class Router {
951
934
  }
952
935
  });
953
936
  });
954
- matches = await this.loadMatches({
955
- matches,
956
- preload: true,
957
- checkLatest: () => void 0
958
- });
959
- return matches;
937
+ try {
938
+ matches = await this.loadMatches({
939
+ matches,
940
+ preload: true,
941
+ checkLatest: () => void 0
942
+ });
943
+ return matches;
944
+ } catch (err) {
945
+ if (!isRedirect(err) && !isNotFound(err)) {
946
+ console.error(err);
947
+ }
948
+ return void 0;
949
+ }
960
950
  };
961
951
  this.matchRoute = (location, opts) => {
962
952
  const matchLocation = {
@@ -1092,6 +1082,7 @@ class Router {
1092
1082
  ...match,
1093
1083
  ...dehydratedMatch,
1094
1084
  meta: (_b2 = (_a2 = route.options).meta) == null ? void 0 : _b2.call(_a2, {
1085
+ params: match.params,
1095
1086
  loaderData: dehydratedMatch.loaderData
1096
1087
  }),
1097
1088
  links: (_d = (_c2 = route.options).links) == null ? void 0 : _d.call(_c2),
@@ -1108,27 +1099,29 @@ class Router {
1108
1099
  };
1109
1100
  });
1110
1101
  };
1111
- this.updateMatchesWithNotFound = (matches, currentMatch, err) => {
1102
+ this.handleNotFound = (matches, err) => {
1112
1103
  const matchesByRouteId = Object.fromEntries(
1113
1104
  matches.map((match) => [match.routeId, match])
1114
1105
  );
1115
- if (err.global) {
1116
- matchesByRouteId[rootRouteId].notFoundError = err;
1117
- } else {
1118
- let currentRoute = this.routesById[err.route ?? currentMatch.routeId];
1119
- while (!currentRoute.options.notFoundComponent) {
1120
- currentRoute = currentRoute == null ? void 0 : currentRoute.parentRoute;
1121
- invariant(
1122
- currentRoute,
1123
- "Found invalid route tree while trying to find not-found handler."
1124
- );
1125
- if (currentRoute.id === rootRouteId)
1126
- break;
1106
+ if (!err.global && err.routeId) {
1107
+ let currentRoute = this.looseRoutesById[err.routeId];
1108
+ if (currentRoute) {
1109
+ while (!currentRoute.options.notFoundComponent) {
1110
+ currentRoute = currentRoute == null ? void 0 : currentRoute.parentRoute;
1111
+ invariant(
1112
+ currentRoute,
1113
+ "Found invalid route tree while trying to find not-found handler."
1114
+ );
1115
+ if (currentRoute.id === rootRouteId)
1116
+ break;
1117
+ }
1118
+ const match = matchesByRouteId[currentRoute.id];
1119
+ invariant(match, "Could not find match for route: " + currentRoute.id);
1120
+ match.notFoundError = err;
1121
+ return;
1127
1122
  }
1128
- const match = matchesByRouteId[currentRoute.id];
1129
- invariant(match, "Could not find match for route: " + currentRoute.id);
1130
- match.notFoundError = err;
1131
1123
  }
1124
+ matchesByRouteId[rootRouteId].notFoundError = err;
1132
1125
  };
1133
1126
  this.hasNotFoundMatch = () => {
1134
1127
  return this.__store.state.matches.some((d) => d.notFoundError);