@tanstack/react-router 1.16.2 → 1.16.6

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");
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;
592
576
  }
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(
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
  })
@@ -860,6 +843,11 @@ class Router {
860
843
  checkLatest: () => this.checkLatest(promise)
861
844
  });
862
845
  } catch (err) {
846
+ if (isRedirect(err)) {
847
+ this.handleRedirect(err);
848
+ } else if (isNotFound(err)) {
849
+ this.handleNotFound(pendingMatches, err);
850
+ }
863
851
  }
864
852
  if (latestPromise = this.checkLatest(promise)) {
865
853
  return latestPromise;
@@ -913,6 +901,15 @@ class Router {
913
901
  this.latestLoadPromise = promise;
914
902
  return this.latestLoadPromise;
915
903
  };
904
+ this.handleRedirect = (err) => {
905
+ if (!err.href) {
906
+ err.href = this.buildLocation(err).href;
907
+ }
908
+ if (isServer) {
909
+ throw err;
910
+ }
911
+ this.navigate(err);
912
+ };
916
913
  this.cleanCache = () => {
917
914
  this.__store.setState((s) => {
918
915
  return {
@@ -951,12 +948,19 @@ class Router {
951
948
  }
952
949
  });
953
950
  });
954
- matches = await this.loadMatches({
955
- matches,
956
- preload: true,
957
- checkLatest: () => void 0
958
- });
959
- return matches;
951
+ try {
952
+ matches = await this.loadMatches({
953
+ matches,
954
+ preload: true,
955
+ checkLatest: () => void 0
956
+ });
957
+ return matches;
958
+ } catch (err) {
959
+ if (!isRedirect(err) && !isNotFound(err)) {
960
+ console.error(err);
961
+ }
962
+ return void 0;
963
+ }
960
964
  };
961
965
  this.matchRoute = (location, opts) => {
962
966
  const matchLocation = {
@@ -1092,6 +1096,7 @@ class Router {
1092
1096
  ...match,
1093
1097
  ...dehydratedMatch,
1094
1098
  meta: (_b2 = (_a2 = route.options).meta) == null ? void 0 : _b2.call(_a2, {
1099
+ params: match.params,
1095
1100
  loaderData: dehydratedMatch.loaderData
1096
1101
  }),
1097
1102
  links: (_d = (_c2 = route.options).links) == null ? void 0 : _d.call(_c2),
@@ -1108,27 +1113,29 @@ class Router {
1108
1113
  };
1109
1114
  });
1110
1115
  };
1111
- this.updateMatchesWithNotFound = (matches, currentMatch, err) => {
1116
+ this.handleNotFound = (matches, err) => {
1112
1117
  const matchesByRouteId = Object.fromEntries(
1113
1118
  matches.map((match) => [match.routeId, match])
1114
1119
  );
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;
1120
+ if (!err.global && err.routeId) {
1121
+ let currentRoute = this.looseRoutesById[err.routeId];
1122
+ if (currentRoute) {
1123
+ while (!currentRoute.options.notFoundComponent) {
1124
+ currentRoute = currentRoute == null ? void 0 : currentRoute.parentRoute;
1125
+ invariant(
1126
+ currentRoute,
1127
+ "Found invalid route tree while trying to find not-found handler."
1128
+ );
1129
+ if (currentRoute.id === rootRouteId)
1130
+ break;
1131
+ }
1132
+ const match = matchesByRouteId[currentRoute.id];
1133
+ invariant(match, "Could not find match for route: " + currentRoute.id);
1134
+ match.notFoundError = err;
1135
+ return;
1127
1136
  }
1128
- const match = matchesByRouteId[currentRoute.id];
1129
- invariant(match, "Could not find match for route: " + currentRoute.id);
1130
- match.notFoundError = err;
1131
1137
  }
1138
+ matchesByRouteId[rootRouteId].notFoundError = err;
1132
1139
  };
1133
1140
  this.hasNotFoundMatch = () => {
1134
1141
  return this.__store.state.matches.some((d) => d.notFoundError);