@tanstack/router-core 1.132.0-alpha.0 → 1.132.0-alpha.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.
@@ -1,7 +1,7 @@
1
1
  import { Store, batch } from "@tanstack/store";
2
2
  import { createMemoryHistory, createBrowserHistory, parseHref } from "@tanstack/history";
3
3
  import invariant from "tiny-invariant";
4
- import { pick, createControlledPromise, deepEqual, replaceEqualDeep, last, functionalUpdate } from "./utils.js";
4
+ import { pick, createControlledPromise, isPromise, deepEqual, replaceEqualDeep, last, functionalUpdate } from "./utils.js";
5
5
  import { trimPath, resolvePath, cleanPath, matchPathname, trimPathRight, interpolatePath, joinPaths, trimPathLeft, parsePathname, SEGMENT_TYPE_PARAM, SEGMENT_TYPE_OPTIONAL_PARAM, SEGMENT_TYPE_WILDCARD, SEGMENT_TYPE_PATHNAME } from "./path.js";
6
6
  import { isNotFound } from "./not-found.js";
7
7
  import { setupScrollRestoration } from "./scroll-restoration.js";
@@ -210,13 +210,8 @@ class RouterCore {
210
210
  const match = this.getMatch(id);
211
211
  if (!match) return;
212
212
  match.abortController.abort();
213
- this.updateMatch(id, (prev) => {
214
- clearTimeout(prev.pendingTimeout);
215
- return {
216
- ...prev,
217
- pendingTimeout: void 0
218
- };
219
- });
213
+ clearTimeout(match._nonReactive.pendingTimeout);
214
+ match._nonReactive.pendingTimeout = void 0;
220
215
  };
221
216
  this.cancelMatches = () => {
222
217
  this.state.pendingMatches?.forEach((match) => {
@@ -230,7 +225,7 @@ class RouterCore {
230
225
  _buildLocation: true
231
226
  });
232
227
  const lastMatch = last(allCurrentLocationMatches);
233
- let fromPath = lastMatch.fullPath;
228
+ let fromPath = this.resolvePathWithBase(lastMatch.fullPath, ".");
234
229
  const toPath = dest.to ? this.resolvePathWithBase(fromPath, `${dest.to}`) : this.resolvePathWithBase(fromPath, ".");
235
230
  const routeIsChanging = !!dest.to && !comparePaths(dest.to.toString(), fromPath) && !comparePaths(toPath, fromPath);
236
231
  if (dest.unsafeRelative === "path") {
@@ -253,6 +248,7 @@ class RouterCore {
253
248
  }
254
249
  }
255
250
  }
251
+ fromPath = this.resolvePathWithBase(fromPath, ".");
256
252
  const fromSearch = lastMatch.search;
257
253
  const fromParams = { ...lastMatch.params };
258
254
  const nextTo = dest.to ? this.resolvePathWithBase(fromPath, `${dest.to}`) : this.resolvePathWithBase(fromPath, ".");
@@ -265,13 +261,9 @@ class RouterCore {
265
261
  params: nextParams ?? {},
266
262
  parseCache: this.parsePathnameCache
267
263
  }).interpolatedPath;
268
- const destRoutes = this.matchRoutes(
269
- interpolatedNextTo,
270
- {},
271
- {
272
- _buildLocation: true
273
- }
274
- ).map((d) => this.looseRoutesById[d.routeId]);
264
+ const destRoutes = this.matchRoutes(interpolatedNextTo, void 0, {
265
+ _buildLocation: true
266
+ }).map((d) => this.looseRoutesById[d.routeId]);
275
267
  if (Object.keys(nextParams).length > 0) {
276
268
  destRoutes.map((route) => {
277
269
  return route.options.params?.stringify ?? route.options.stringifyParams;
@@ -693,502 +685,597 @@ class RouterCore {
693
685
  const findFn = (d) => d.id === matchId;
694
686
  return this.state.cachedMatches.find(findFn) ?? this.state.pendingMatches?.find(findFn) ?? this.state.matches.find(findFn);
695
687
  };
696
- this.loadMatches = async ({
697
- location,
698
- matches,
699
- preload: allPreload,
700
- onReady,
701
- updateMatch = this.updateMatch,
702
- sync
703
- }) => {
704
- let firstBadMatchIndex;
705
- let rendered = false;
706
- const triggerOnReady = async () => {
707
- if (!rendered) {
708
- rendered = true;
709
- await onReady?.();
688
+ this.triggerOnReady = (innerLoadContext) => {
689
+ if (!innerLoadContext.rendered) {
690
+ innerLoadContext.rendered = true;
691
+ return innerLoadContext.onReady?.();
692
+ }
693
+ };
694
+ this.resolvePreload = (innerLoadContext, matchId) => {
695
+ return !!(innerLoadContext.preload && !this.state.matches.some((d) => d.id === matchId));
696
+ };
697
+ this.handleRedirectAndNotFound = (innerLoadContext, match, err) => {
698
+ if (!isRedirect(err) && !isNotFound(err)) return;
699
+ if (isRedirect(err) && err.redirectHandled && !err.options.reloadDocument) {
700
+ throw err;
701
+ }
702
+ if (match) {
703
+ match._nonReactive.beforeLoadPromise?.resolve();
704
+ match._nonReactive.loaderPromise?.resolve();
705
+ match._nonReactive.beforeLoadPromise = void 0;
706
+ match._nonReactive.loaderPromise = void 0;
707
+ const status = isRedirect(err) ? "redirected" : "notFound";
708
+ innerLoadContext.updateMatch(match.id, (prev) => ({
709
+ ...prev,
710
+ status,
711
+ isFetching: false,
712
+ error: err
713
+ }));
714
+ if (isNotFound(err) && !err.routeId) {
715
+ err.routeId = match.routeId;
716
+ }
717
+ match._nonReactive.loadPromise?.resolve();
718
+ }
719
+ if (isRedirect(err)) {
720
+ innerLoadContext.rendered = true;
721
+ err.options._fromLocation = innerLoadContext.location;
722
+ err.redirectHandled = true;
723
+ err = this.resolveRedirect(err);
724
+ throw err;
725
+ } else {
726
+ this._handleNotFound(innerLoadContext, err);
727
+ throw err;
728
+ }
729
+ };
730
+ this.shouldSkipLoader = (matchId) => {
731
+ const match = this.getMatch(matchId);
732
+ if (!this.isServer && match._nonReactive.dehydrated) {
733
+ return true;
734
+ }
735
+ if (this.isServer) {
736
+ if (match.ssr === false) {
737
+ return true;
738
+ }
739
+ }
740
+ return false;
741
+ };
742
+ this.handleSerialError = (innerLoadContext, index, err, routerCode) => {
743
+ const { id: matchId, routeId } = innerLoadContext.matches[index];
744
+ const route = this.looseRoutesById[routeId];
745
+ if (err instanceof Promise) {
746
+ throw err;
747
+ }
748
+ err.routerCode = routerCode;
749
+ innerLoadContext.firstBadMatchIndex ??= index;
750
+ this.handleRedirectAndNotFound(
751
+ innerLoadContext,
752
+ this.getMatch(matchId),
753
+ err
754
+ );
755
+ try {
756
+ route.options.onError?.(err);
757
+ } catch (errorHandlerErr) {
758
+ err = errorHandlerErr;
759
+ this.handleRedirectAndNotFound(
760
+ innerLoadContext,
761
+ this.getMatch(matchId),
762
+ err
763
+ );
764
+ }
765
+ innerLoadContext.updateMatch(matchId, (prev) => {
766
+ prev._nonReactive.beforeLoadPromise?.resolve();
767
+ prev._nonReactive.beforeLoadPromise = void 0;
768
+ prev._nonReactive.loadPromise?.resolve();
769
+ return {
770
+ ...prev,
771
+ error: err,
772
+ status: "error",
773
+ isFetching: false,
774
+ updatedAt: Date.now(),
775
+ abortController: new AbortController()
776
+ };
777
+ });
778
+ };
779
+ this.isBeforeLoadSsr = (innerLoadContext, matchId, index, route) => {
780
+ const existingMatch = this.getMatch(matchId);
781
+ const parentMatchId = innerLoadContext.matches[index - 1]?.id;
782
+ const parentMatch = parentMatchId ? this.getMatch(parentMatchId) : void 0;
783
+ if (this.isShell()) {
784
+ existingMatch.ssr = matchId === rootRouteId;
785
+ return;
786
+ }
787
+ if (parentMatch?.ssr === false) {
788
+ existingMatch.ssr = false;
789
+ return;
790
+ }
791
+ const parentOverride = (tempSsr2) => {
792
+ if (tempSsr2 === true && parentMatch?.ssr === "data-only") {
793
+ return "data-only";
710
794
  }
795
+ return tempSsr2;
711
796
  };
712
- const resolvePreload = (matchId) => {
713
- return !!(allPreload && !this.state.matches.some((d) => d.id === matchId));
797
+ const defaultSsr = this.options.defaultSsr ?? true;
798
+ if (route.options.ssr === void 0) {
799
+ existingMatch.ssr = parentOverride(defaultSsr);
800
+ return;
801
+ }
802
+ if (typeof route.options.ssr !== "function") {
803
+ existingMatch.ssr = parentOverride(route.options.ssr);
804
+ return;
805
+ }
806
+ const { search, params } = this.getMatch(matchId);
807
+ const ssrFnContext = {
808
+ search: makeMaybe(search, existingMatch.searchError),
809
+ params: makeMaybe(params, existingMatch.paramsError),
810
+ location: innerLoadContext.location,
811
+ matches: innerLoadContext.matches.map((match) => ({
812
+ index: match.index,
813
+ pathname: match.pathname,
814
+ fullPath: match.fullPath,
815
+ staticData: match.staticData,
816
+ id: match.id,
817
+ routeId: match.routeId,
818
+ search: makeMaybe(match.search, match.searchError),
819
+ params: makeMaybe(match.params, match.paramsError),
820
+ ssr: match.ssr
821
+ }))
714
822
  };
715
- if (!this.isServer && this.state.matches.some((d) => d._forcePending)) {
716
- triggerOnReady();
717
- }
718
- const handleRedirectAndNotFound = (match, err) => {
719
- if (isRedirect(err) || isNotFound(err)) {
720
- if (isRedirect(err)) {
721
- if (err.redirectHandled) {
722
- if (!err.options.reloadDocument) {
723
- throw err;
724
- }
823
+ const tempSsr = route.options.ssr(ssrFnContext);
824
+ if (isPromise(tempSsr)) {
825
+ return tempSsr.then((ssr) => {
826
+ existingMatch.ssr = parentOverride(ssr ?? defaultSsr);
827
+ });
828
+ }
829
+ existingMatch.ssr = parentOverride(tempSsr ?? defaultSsr);
830
+ return;
831
+ };
832
+ this.setupPendingTimeout = (innerLoadContext, matchId, route) => {
833
+ const pendingMs = route.options.pendingMs ?? this.options.defaultPendingMs;
834
+ const shouldPending = !!(innerLoadContext.onReady && !this.isServer && !this.resolvePreload(innerLoadContext, matchId) && (route.options.loader || route.options.beforeLoad || routeNeedsPreload(route)) && typeof pendingMs === "number" && pendingMs !== Infinity && (route.options.pendingComponent ?? this.options?.defaultPendingComponent));
835
+ const match = this.getMatch(matchId);
836
+ if (shouldPending && match._nonReactive.pendingTimeout === void 0) {
837
+ const pendingTimeout = setTimeout(() => {
838
+ this.triggerOnReady(innerLoadContext);
839
+ }, pendingMs);
840
+ match._nonReactive.pendingTimeout = pendingTimeout;
841
+ }
842
+ };
843
+ this.shouldExecuteBeforeLoad = (innerLoadContext, matchId, route) => {
844
+ const existingMatch = this.getMatch(matchId);
845
+ if (!existingMatch._nonReactive.beforeLoadPromise && !existingMatch._nonReactive.loaderPromise)
846
+ return true;
847
+ this.setupPendingTimeout(innerLoadContext, matchId, route);
848
+ const then = () => {
849
+ let shouldExecuteBeforeLoad = true;
850
+ const match = this.getMatch(matchId);
851
+ if (match.status === "error") {
852
+ shouldExecuteBeforeLoad = true;
853
+ } else if (match.preload && (match.status === "redirected" || match.status === "notFound")) {
854
+ this.handleRedirectAndNotFound(innerLoadContext, match, match.error);
855
+ }
856
+ return shouldExecuteBeforeLoad;
857
+ };
858
+ return existingMatch._nonReactive.beforeLoadPromise ? existingMatch._nonReactive.beforeLoadPromise.then(then) : then();
859
+ };
860
+ this.executeBeforeLoad = (innerLoadContext, matchId, index, route) => {
861
+ const match = this.getMatch(matchId);
862
+ match._nonReactive.beforeLoadPromise = createControlledPromise();
863
+ const prevLoadPromise = match._nonReactive.loadPromise;
864
+ match._nonReactive.loadPromise = createControlledPromise(() => {
865
+ prevLoadPromise?.resolve();
866
+ });
867
+ const { paramsError, searchError } = match;
868
+ if (paramsError) {
869
+ this.handleSerialError(
870
+ innerLoadContext,
871
+ index,
872
+ paramsError,
873
+ "PARSE_PARAMS"
874
+ );
875
+ }
876
+ if (searchError) {
877
+ this.handleSerialError(
878
+ innerLoadContext,
879
+ index,
880
+ searchError,
881
+ "VALIDATE_SEARCH"
882
+ );
883
+ }
884
+ this.setupPendingTimeout(innerLoadContext, matchId, route);
885
+ const abortController = new AbortController();
886
+ const parentMatchId = innerLoadContext.matches[index - 1]?.id;
887
+ const parentMatch = parentMatchId ? this.getMatch(parentMatchId) : void 0;
888
+ const parentMatchContext = parentMatch?.context ?? this.options.context ?? void 0;
889
+ const context = { ...parentMatchContext, ...match.__routeContext };
890
+ let isPending = false;
891
+ const pending = () => {
892
+ if (isPending) return;
893
+ isPending = true;
894
+ innerLoadContext.updateMatch(matchId, (prev) => ({
895
+ ...prev,
896
+ isFetching: "beforeLoad",
897
+ fetchCount: prev.fetchCount + 1,
898
+ abortController,
899
+ context
900
+ }));
901
+ };
902
+ const resolve = () => {
903
+ match._nonReactive.beforeLoadPromise?.resolve();
904
+ match._nonReactive.beforeLoadPromise = void 0;
905
+ innerLoadContext.updateMatch(matchId, (prev) => ({
906
+ ...prev,
907
+ isFetching: false
908
+ }));
909
+ };
910
+ if (!route.options.beforeLoad) {
911
+ batch(() => {
912
+ pending();
913
+ resolve();
914
+ });
915
+ return;
916
+ }
917
+ const { search, params, cause } = match;
918
+ const preload = this.resolvePreload(innerLoadContext, matchId);
919
+ const beforeLoadFnContext = {
920
+ search,
921
+ abortController,
922
+ params,
923
+ preload,
924
+ context,
925
+ location: innerLoadContext.location,
926
+ navigate: (opts) => this.navigate({ ...opts, _fromLocation: innerLoadContext.location }),
927
+ buildLocation: this.buildLocation,
928
+ cause: preload ? "preload" : cause,
929
+ matches: innerLoadContext.matches
930
+ };
931
+ const updateContext = (beforeLoadContext2) => {
932
+ if (beforeLoadContext2 === void 0) {
933
+ batch(() => {
934
+ pending();
935
+ resolve();
936
+ });
937
+ return;
938
+ }
939
+ if (isRedirect(beforeLoadContext2) || isNotFound(beforeLoadContext2)) {
940
+ pending();
941
+ this.handleSerialError(
942
+ innerLoadContext,
943
+ index,
944
+ beforeLoadContext2,
945
+ "BEFORE_LOAD"
946
+ );
947
+ }
948
+ batch(() => {
949
+ pending();
950
+ innerLoadContext.updateMatch(matchId, (prev) => ({
951
+ ...prev,
952
+ __beforeLoadContext: beforeLoadContext2,
953
+ context: {
954
+ ...prev.context,
955
+ ...beforeLoadContext2
956
+ }
957
+ }));
958
+ resolve();
959
+ });
960
+ };
961
+ let beforeLoadContext;
962
+ try {
963
+ beforeLoadContext = route.options.beforeLoad(beforeLoadFnContext);
964
+ if (isPromise(beforeLoadContext)) {
965
+ pending();
966
+ return beforeLoadContext.catch((err) => {
967
+ this.handleSerialError(innerLoadContext, index, err, "BEFORE_LOAD");
968
+ }).then(updateContext);
969
+ }
970
+ } catch (err) {
971
+ pending();
972
+ this.handleSerialError(innerLoadContext, index, err, "BEFORE_LOAD");
973
+ }
974
+ updateContext(beforeLoadContext);
975
+ return;
976
+ };
977
+ this.handleBeforeLoad = (innerLoadContext, index) => {
978
+ const { id: matchId, routeId } = innerLoadContext.matches[index];
979
+ const route = this.looseRoutesById[routeId];
980
+ const serverSsr = () => {
981
+ if (this.isServer) {
982
+ const maybePromise = this.isBeforeLoadSsr(
983
+ innerLoadContext,
984
+ matchId,
985
+ index,
986
+ route
987
+ );
988
+ if (isPromise(maybePromise)) return maybePromise.then(queueExecution);
989
+ }
990
+ return queueExecution();
991
+ };
992
+ const queueExecution = () => {
993
+ if (this.shouldSkipLoader(matchId)) return;
994
+ const shouldExecuteBeforeLoadResult = this.shouldExecuteBeforeLoad(
995
+ innerLoadContext,
996
+ matchId,
997
+ route
998
+ );
999
+ return isPromise(shouldExecuteBeforeLoadResult) ? shouldExecuteBeforeLoadResult.then(execute) : execute(shouldExecuteBeforeLoadResult);
1000
+ };
1001
+ const execute = (shouldExecuteBeforeLoad) => {
1002
+ if (shouldExecuteBeforeLoad) {
1003
+ return this.executeBeforeLoad(innerLoadContext, matchId, index, route);
1004
+ }
1005
+ return;
1006
+ };
1007
+ return serverSsr();
1008
+ };
1009
+ this.executeHead = (innerLoadContext, matchId, route) => {
1010
+ const match = this.getMatch(matchId);
1011
+ if (!match) {
1012
+ return;
1013
+ }
1014
+ if (!route.options.head && !route.options.scripts && !route.options.headers) {
1015
+ return;
1016
+ }
1017
+ const assetContext = {
1018
+ matches: innerLoadContext.matches,
1019
+ match,
1020
+ params: match.params,
1021
+ loaderData: match.loaderData
1022
+ };
1023
+ return Promise.all([
1024
+ route.options.head?.(assetContext),
1025
+ route.options.scripts?.(assetContext),
1026
+ route.options.headers?.(assetContext)
1027
+ ]).then(([headFnContent, scripts, headers]) => {
1028
+ const meta = headFnContent?.meta;
1029
+ const links = headFnContent?.links;
1030
+ const headScripts = headFnContent?.scripts;
1031
+ const styles = headFnContent?.styles;
1032
+ return {
1033
+ meta,
1034
+ links,
1035
+ headScripts,
1036
+ headers,
1037
+ scripts,
1038
+ styles
1039
+ };
1040
+ });
1041
+ };
1042
+ this.potentialPendingMinPromise = (matchId) => {
1043
+ const latestMatch = this.getMatch(matchId);
1044
+ return latestMatch._nonReactive.minPendingPromise;
1045
+ };
1046
+ this.getLoaderContext = (innerLoadContext, matchId, index, route) => {
1047
+ const parentMatchPromise = innerLoadContext.matchPromises[index - 1];
1048
+ const { params, loaderDeps, abortController, context, cause } = this.getMatch(matchId);
1049
+ const preload = this.resolvePreload(innerLoadContext, matchId);
1050
+ return {
1051
+ params,
1052
+ deps: loaderDeps,
1053
+ preload: !!preload,
1054
+ parentMatchPromise,
1055
+ abortController,
1056
+ context,
1057
+ location: innerLoadContext.location,
1058
+ navigate: (opts) => this.navigate({ ...opts, _fromLocation: innerLoadContext.location }),
1059
+ cause: preload ? "preload" : cause,
1060
+ route
1061
+ };
1062
+ };
1063
+ this.runLoader = async (innerLoadContext, matchId, index, route) => {
1064
+ try {
1065
+ try {
1066
+ if (!this.isServer || this.getMatch(matchId).ssr === true) {
1067
+ this.loadRouteChunk(route);
1068
+ }
1069
+ const loaderResult = route.options.loader?.(
1070
+ this.getLoaderContext(innerLoadContext, matchId, index, route)
1071
+ );
1072
+ const loaderResultIsPromise = route.options.loader && isPromise(loaderResult);
1073
+ const willLoadSomething = !!(loaderResultIsPromise || route._lazyPromise || route._componentsPromise || route.options.head || route.options.scripts || route.options.headers || this.getMatch(matchId)._nonReactive.minPendingPromise);
1074
+ if (willLoadSomething) {
1075
+ innerLoadContext.updateMatch(matchId, (prev) => ({
1076
+ ...prev,
1077
+ isFetching: "loader"
1078
+ }));
1079
+ }
1080
+ if (route.options.loader) {
1081
+ const loaderData = loaderResultIsPromise ? await loaderResult : loaderResult;
1082
+ this.handleRedirectAndNotFound(
1083
+ innerLoadContext,
1084
+ this.getMatch(matchId),
1085
+ loaderData
1086
+ );
1087
+ if (loaderData !== void 0) {
1088
+ innerLoadContext.updateMatch(matchId, (prev) => ({
1089
+ ...prev,
1090
+ loaderData
1091
+ }));
725
1092
  }
726
1093
  }
727
- match.beforeLoadPromise?.resolve();
728
- match.loaderPromise?.resolve();
729
- updateMatch(match.id, (prev) => ({
1094
+ if (route._lazyPromise) await route._lazyPromise;
1095
+ const headResult = this.executeHead(innerLoadContext, matchId, route);
1096
+ const head = headResult ? await headResult : void 0;
1097
+ const pendingPromise = this.potentialPendingMinPromise(matchId);
1098
+ if (pendingPromise) await pendingPromise;
1099
+ if (route._componentsPromise) await route._componentsPromise;
1100
+ innerLoadContext.updateMatch(matchId, (prev) => ({
730
1101
  ...prev,
731
- status: isRedirect(err) ? "redirected" : isNotFound(err) ? "notFound" : "error",
1102
+ error: void 0,
1103
+ status: "success",
732
1104
  isFetching: false,
733
- error: err,
734
- beforeLoadPromise: void 0,
735
- loaderPromise: void 0
1105
+ updatedAt: Date.now(),
1106
+ ...head
736
1107
  }));
737
- if (!err.routeId) {
738
- err.routeId = match.routeId;
739
- }
740
- match.loadPromise?.resolve();
741
- if (isRedirect(err)) {
742
- rendered = true;
743
- err.options._fromLocation = location;
744
- err.redirectHandled = true;
745
- err = this.resolveRedirect(err);
746
- throw err;
747
- } else if (isNotFound(err)) {
748
- this._handleNotFound(matches, err, {
749
- updateMatch
750
- });
751
- throw err;
1108
+ } catch (e) {
1109
+ let error = e;
1110
+ const pendingPromise = this.potentialPendingMinPromise(matchId);
1111
+ if (pendingPromise) await pendingPromise;
1112
+ this.handleRedirectAndNotFound(
1113
+ innerLoadContext,
1114
+ this.getMatch(matchId),
1115
+ e
1116
+ );
1117
+ try {
1118
+ route.options.onError?.(e);
1119
+ } catch (onErrorError) {
1120
+ error = onErrorError;
1121
+ this.handleRedirectAndNotFound(
1122
+ innerLoadContext,
1123
+ this.getMatch(matchId),
1124
+ onErrorError
1125
+ );
752
1126
  }
1127
+ const headResult = this.executeHead(innerLoadContext, matchId, route);
1128
+ const head = headResult ? await headResult : void 0;
1129
+ innerLoadContext.updateMatch(matchId, (prev) => ({
1130
+ ...prev,
1131
+ error,
1132
+ status: "error",
1133
+ isFetching: false,
1134
+ ...head
1135
+ }));
753
1136
  }
754
- };
755
- const shouldSkipLoader = (matchId) => {
1137
+ } catch (err) {
756
1138
  const match = this.getMatch(matchId);
757
- if (!this.isServer && match._dehydrated) {
758
- return true;
1139
+ if (match) {
1140
+ const headResult = this.executeHead(innerLoadContext, matchId, route);
1141
+ if (headResult) {
1142
+ const head = await headResult;
1143
+ innerLoadContext.updateMatch(matchId, (prev) => ({
1144
+ ...prev,
1145
+ ...head
1146
+ }));
1147
+ }
1148
+ match._nonReactive.loaderPromise = void 0;
759
1149
  }
1150
+ this.handleRedirectAndNotFound(innerLoadContext, match, err);
1151
+ }
1152
+ };
1153
+ this.loadRouteMatch = async (innerLoadContext, index) => {
1154
+ const { id: matchId, routeId } = innerLoadContext.matches[index];
1155
+ let loaderShouldRunAsync = false;
1156
+ let loaderIsRunningAsync = false;
1157
+ const route = this.looseRoutesById[routeId];
1158
+ const prevMatch = this.getMatch(matchId);
1159
+ if (this.shouldSkipLoader(matchId)) {
760
1160
  if (this.isServer) {
761
- if (match.ssr === false) {
762
- return true;
1161
+ const headResult = this.executeHead(innerLoadContext, matchId, route);
1162
+ if (headResult) {
1163
+ const head = await headResult;
1164
+ innerLoadContext.updateMatch(matchId, (prev) => ({
1165
+ ...prev,
1166
+ ...head
1167
+ }));
763
1168
  }
1169
+ return this.getMatch(matchId);
764
1170
  }
765
- return false;
766
- };
767
- try {
768
- await new Promise((resolveAll, rejectAll) => {
769
- ;
1171
+ } else if (prevMatch._nonReactive.loaderPromise) {
1172
+ if (prevMatch.status === "success" && !innerLoadContext.sync && !prevMatch.preload) {
1173
+ return this.getMatch(matchId);
1174
+ }
1175
+ await prevMatch._nonReactive.loaderPromise;
1176
+ const match2 = this.getMatch(matchId);
1177
+ if (match2.error) {
1178
+ this.handleRedirectAndNotFound(innerLoadContext, match2, match2.error);
1179
+ }
1180
+ } else {
1181
+ const age = Date.now() - this.getMatch(matchId).updatedAt;
1182
+ const preload = this.resolvePreload(innerLoadContext, matchId);
1183
+ const staleAge = preload ? route.options.preloadStaleTime ?? this.options.defaultPreloadStaleTime ?? 3e4 : route.options.staleTime ?? this.options.defaultStaleTime ?? 0;
1184
+ const shouldReloadOption = route.options.shouldReload;
1185
+ const shouldReload = typeof shouldReloadOption === "function" ? shouldReloadOption(
1186
+ this.getLoaderContext(innerLoadContext, matchId, index, route)
1187
+ ) : shouldReloadOption;
1188
+ const nextPreload = !!preload && !this.state.matches.some((d) => d.id === matchId);
1189
+ const match2 = this.getMatch(matchId);
1190
+ match2._nonReactive.loaderPromise = createControlledPromise();
1191
+ if (nextPreload !== match2.preload) {
1192
+ innerLoadContext.updateMatch(matchId, (prev) => ({
1193
+ ...prev,
1194
+ preload: nextPreload
1195
+ }));
1196
+ }
1197
+ const { status, invalid } = this.getMatch(matchId);
1198
+ loaderShouldRunAsync = status === "success" && (invalid || (shouldReload ?? age > staleAge));
1199
+ if (preload && route.options.preload === false) ;
1200
+ else if (loaderShouldRunAsync && !innerLoadContext.sync) {
1201
+ loaderIsRunningAsync = true;
770
1202
  (async () => {
771
1203
  try {
772
- const handleSerialError = (index, err, routerCode) => {
773
- const { id: matchId, routeId } = matches[index];
774
- const route = this.looseRoutesById[routeId];
775
- if (err instanceof Promise) {
776
- throw err;
777
- }
778
- err.routerCode = routerCode;
779
- firstBadMatchIndex = firstBadMatchIndex ?? index;
780
- handleRedirectAndNotFound(this.getMatch(matchId), err);
781
- try {
782
- route.options.onError?.(err);
783
- } catch (errorHandlerErr) {
784
- err = errorHandlerErr;
785
- handleRedirectAndNotFound(this.getMatch(matchId), err);
786
- }
787
- updateMatch(matchId, (prev) => {
788
- prev.beforeLoadPromise?.resolve();
789
- prev.loadPromise?.resolve();
790
- return {
791
- ...prev,
792
- error: err,
793
- status: "error",
794
- isFetching: false,
795
- updatedAt: Date.now(),
796
- abortController: new AbortController(),
797
- beforeLoadPromise: void 0
798
- };
799
- });
800
- };
801
- for (const [index, { id: matchId, routeId }] of matches.entries()) {
802
- const existingMatch = this.getMatch(matchId);
803
- const parentMatchId = matches[index - 1]?.id;
804
- const parentMatch = parentMatchId ? this.getMatch(parentMatchId) : void 0;
805
- const route = this.looseRoutesById[routeId];
806
- const pendingMs = route.options.pendingMs ?? this.options.defaultPendingMs;
807
- if (this.isServer) {
808
- let ssr;
809
- if (this.isShell()) {
810
- ssr = matchId === rootRouteId;
811
- } else {
812
- const defaultSsr = this.options.defaultSsr ?? true;
813
- if (parentMatch?.ssr === false) {
814
- ssr = false;
815
- } else {
816
- let tempSsr;
817
- if (route.options.ssr === void 0) {
818
- tempSsr = defaultSsr;
819
- } else if (typeof route.options.ssr === "function") {
820
- let makeMaybe = function(value, error) {
821
- if (error) {
822
- return { status: "error", error };
823
- }
824
- return { status: "success", value };
825
- };
826
- const { search, params } = this.getMatch(matchId);
827
- const ssrFnContext = {
828
- search: makeMaybe(search, existingMatch.searchError),
829
- params: makeMaybe(params, existingMatch.paramsError),
830
- location,
831
- matches: matches.map((match) => ({
832
- index: match.index,
833
- pathname: match.pathname,
834
- fullPath: match.fullPath,
835
- staticData: match.staticData,
836
- id: match.id,
837
- routeId: match.routeId,
838
- search: makeMaybe(match.search, match.searchError),
839
- params: makeMaybe(match.params, match.paramsError),
840
- ssr: match.ssr
841
- }))
842
- };
843
- tempSsr = await route.options.ssr(ssrFnContext) ?? defaultSsr;
844
- } else {
845
- tempSsr = route.options.ssr;
846
- }
847
- if (tempSsr === true && parentMatch?.ssr === "data-only") {
848
- ssr = "data-only";
849
- } else {
850
- ssr = tempSsr;
851
- }
852
- }
853
- }
854
- updateMatch(matchId, (prev) => ({
855
- ...prev,
856
- ssr
857
- }));
858
- }
859
- if (shouldSkipLoader(matchId)) {
860
- continue;
861
- }
862
- const shouldPending = !!(onReady && !this.isServer && !resolvePreload(matchId) && (route.options.loader || route.options.beforeLoad || routeNeedsPreload(route)) && typeof pendingMs === "number" && pendingMs !== Infinity && (route.options.pendingComponent ?? this.options?.defaultPendingComponent));
863
- let executeBeforeLoad = true;
864
- const setupPendingTimeout = () => {
865
- if (shouldPending && this.getMatch(matchId).pendingTimeout === void 0) {
866
- const pendingTimeout = setTimeout(() => {
867
- try {
868
- triggerOnReady();
869
- } catch {
870
- }
871
- }, pendingMs);
872
- updateMatch(matchId, (prev) => ({
873
- ...prev,
874
- pendingTimeout
875
- }));
876
- }
877
- };
878
- if (
879
- // If we are in the middle of a load, either of these will be present
880
- // (not to be confused with `loadPromise`, which is always defined)
881
- existingMatch.beforeLoadPromise || existingMatch.loaderPromise
882
- ) {
883
- setupPendingTimeout();
884
- await existingMatch.beforeLoadPromise;
885
- const match = this.getMatch(matchId);
886
- if (match.status === "error") {
887
- executeBeforeLoad = true;
888
- } else if (match.preload && (match.status === "redirected" || match.status === "notFound")) {
889
- handleRedirectAndNotFound(match, match.error);
890
- }
891
- }
892
- if (executeBeforeLoad) {
893
- try {
894
- updateMatch(matchId, (prev) => {
895
- const prevLoadPromise = prev.loadPromise;
896
- return {
897
- ...prev,
898
- loadPromise: createControlledPromise(() => {
899
- prevLoadPromise?.resolve();
900
- }),
901
- beforeLoadPromise: createControlledPromise()
902
- };
903
- });
904
- const { paramsError, searchError } = this.getMatch(matchId);
905
- if (paramsError) {
906
- handleSerialError(index, paramsError, "PARSE_PARAMS");
907
- }
908
- if (searchError) {
909
- handleSerialError(index, searchError, "VALIDATE_SEARCH");
910
- }
911
- setupPendingTimeout();
912
- const abortController = new AbortController();
913
- const parentMatchContext = parentMatch?.context ?? this.options.context ?? {};
914
- updateMatch(matchId, (prev) => ({
915
- ...prev,
916
- isFetching: "beforeLoad",
917
- fetchCount: prev.fetchCount + 1,
918
- abortController,
919
- context: {
920
- ...parentMatchContext,
921
- ...prev.__routeContext
922
- }
923
- }));
924
- const { search, params, context, cause } = this.getMatch(matchId);
925
- const preload = resolvePreload(matchId);
926
- const beforeLoadFnContext = {
927
- search,
928
- abortController,
929
- params,
930
- preload,
931
- context,
932
- location,
933
- navigate: (opts) => this.navigate({ ...opts, _fromLocation: location }),
934
- buildLocation: this.buildLocation,
935
- cause: preload ? "preload" : cause,
936
- matches
937
- };
938
- const beforeLoadContext = await route.options.beforeLoad?.(beforeLoadFnContext);
939
- if (isRedirect(beforeLoadContext) || isNotFound(beforeLoadContext)) {
940
- handleSerialError(index, beforeLoadContext, "BEFORE_LOAD");
941
- }
942
- updateMatch(matchId, (prev) => {
943
- return {
944
- ...prev,
945
- __beforeLoadContext: beforeLoadContext,
946
- context: {
947
- ...parentMatchContext,
948
- ...prev.__routeContext,
949
- ...beforeLoadContext
950
- },
951
- abortController
952
- };
953
- });
954
- } catch (err) {
955
- handleSerialError(index, err, "BEFORE_LOAD");
956
- }
957
- updateMatch(matchId, (prev) => {
958
- prev.beforeLoadPromise?.resolve();
959
- return {
960
- ...prev,
961
- beforeLoadPromise: void 0,
962
- isFetching: false
963
- };
964
- });
965
- }
966
- }
967
- const validResolvedMatches = matches.slice(0, firstBadMatchIndex);
968
- const matchPromises = [];
969
- validResolvedMatches.forEach(({ id: matchId, routeId }, index) => {
970
- matchPromises.push(
971
- (async () => {
972
- let loaderShouldRunAsync = false;
973
- let loaderIsRunningAsync = false;
974
- const route = this.looseRoutesById[routeId];
975
- const executeHead = async () => {
976
- const match = this.getMatch(matchId);
977
- if (!match) {
978
- return;
979
- }
980
- const assetContext = {
981
- matches,
982
- match,
983
- params: match.params,
984
- loaderData: match.loaderData
985
- };
986
- const headFnContent = await route.options.head?.(assetContext);
987
- const meta = headFnContent?.meta;
988
- const links = headFnContent?.links;
989
- const headScripts = headFnContent?.scripts;
990
- const styles = headFnContent?.styles;
991
- const scripts = await route.options.scripts?.(assetContext);
992
- const headers = await route.options.headers?.(assetContext);
993
- return {
994
- meta,
995
- links,
996
- headScripts,
997
- headers,
998
- scripts,
999
- styles
1000
- };
1001
- };
1002
- const potentialPendingMinPromise = async () => {
1003
- const latestMatch = this.getMatch(matchId);
1004
- if (latestMatch.minPendingPromise) {
1005
- await latestMatch.minPendingPromise;
1006
- }
1007
- };
1008
- const prevMatch = this.getMatch(matchId);
1009
- if (shouldSkipLoader(matchId)) {
1010
- if (this.isServer) {
1011
- const head = await executeHead();
1012
- updateMatch(matchId, (prev) => ({
1013
- ...prev,
1014
- ...head
1015
- }));
1016
- return this.getMatch(matchId);
1017
- }
1018
- } else if (prevMatch.loaderPromise) {
1019
- if (prevMatch.status === "success" && !sync && !prevMatch.preload) {
1020
- return this.getMatch(matchId);
1021
- }
1022
- await prevMatch.loaderPromise;
1023
- const match = this.getMatch(matchId);
1024
- if (match.error) {
1025
- handleRedirectAndNotFound(match, match.error);
1026
- }
1027
- } else {
1028
- const parentMatchPromise = matchPromises[index - 1];
1029
- const getLoaderContext = () => {
1030
- const {
1031
- params,
1032
- loaderDeps,
1033
- abortController,
1034
- context,
1035
- cause
1036
- } = this.getMatch(matchId);
1037
- const preload2 = resolvePreload(matchId);
1038
- return {
1039
- params,
1040
- deps: loaderDeps,
1041
- preload: !!preload2,
1042
- parentMatchPromise,
1043
- abortController,
1044
- context,
1045
- location,
1046
- navigate: (opts) => this.navigate({ ...opts, _fromLocation: location }),
1047
- cause: preload2 ? "preload" : cause,
1048
- route
1049
- };
1050
- };
1051
- const age = Date.now() - this.getMatch(matchId).updatedAt;
1052
- const preload = resolvePreload(matchId);
1053
- const staleAge = preload ? route.options.preloadStaleTime ?? this.options.defaultPreloadStaleTime ?? 3e4 : route.options.staleTime ?? this.options.defaultStaleTime ?? 0;
1054
- const shouldReloadOption = route.options.shouldReload;
1055
- const shouldReload = typeof shouldReloadOption === "function" ? shouldReloadOption(getLoaderContext()) : shouldReloadOption;
1056
- updateMatch(matchId, (prev) => ({
1057
- ...prev,
1058
- loaderPromise: createControlledPromise(),
1059
- preload: !!preload && !this.state.matches.some((d) => d.id === matchId)
1060
- }));
1061
- const runLoader = async () => {
1062
- try {
1063
- try {
1064
- if (!this.isServer || this.isServer && this.getMatch(matchId).ssr === true) {
1065
- this.loadRouteChunk(route);
1066
- }
1067
- updateMatch(matchId, (prev) => ({
1068
- ...prev,
1069
- isFetching: "loader"
1070
- }));
1071
- const loaderData = await route.options.loader?.(getLoaderContext());
1072
- handleRedirectAndNotFound(
1073
- this.getMatch(matchId),
1074
- loaderData
1075
- );
1076
- updateMatch(matchId, (prev) => ({
1077
- ...prev,
1078
- loaderData
1079
- }));
1080
- await route._lazyPromise;
1081
- const head = await executeHead();
1082
- await potentialPendingMinPromise();
1083
- await route._componentsPromise;
1084
- updateMatch(matchId, (prev) => ({
1085
- ...prev,
1086
- error: void 0,
1087
- status: "success",
1088
- isFetching: false,
1089
- updatedAt: Date.now(),
1090
- ...head
1091
- }));
1092
- } catch (e) {
1093
- let error = e;
1094
- await potentialPendingMinPromise();
1095
- handleRedirectAndNotFound(this.getMatch(matchId), e);
1096
- try {
1097
- route.options.onError?.(e);
1098
- } catch (onErrorError) {
1099
- error = onErrorError;
1100
- handleRedirectAndNotFound(
1101
- this.getMatch(matchId),
1102
- onErrorError
1103
- );
1104
- }
1105
- const head = await executeHead();
1106
- updateMatch(matchId, (prev) => ({
1107
- ...prev,
1108
- error,
1109
- status: "error",
1110
- isFetching: false,
1111
- ...head
1112
- }));
1113
- }
1114
- } catch (err) {
1115
- const head = await executeHead();
1116
- updateMatch(matchId, (prev) => ({
1117
- ...prev,
1118
- loaderPromise: void 0,
1119
- ...head
1120
- }));
1121
- handleRedirectAndNotFound(this.getMatch(matchId), err);
1122
- }
1123
- };
1124
- const { status, invalid } = this.getMatch(matchId);
1125
- loaderShouldRunAsync = status === "success" && (invalid || (shouldReload ?? age > staleAge));
1126
- if (preload && route.options.preload === false) {
1127
- } else if (loaderShouldRunAsync && !sync) {
1128
- loaderIsRunningAsync = true;
1129
- (async () => {
1130
- try {
1131
- await runLoader();
1132
- const { loaderPromise, loadPromise } = this.getMatch(matchId);
1133
- loaderPromise?.resolve();
1134
- loadPromise?.resolve();
1135
- updateMatch(matchId, (prev) => ({
1136
- ...prev,
1137
- loaderPromise: void 0
1138
- }));
1139
- } catch (err) {
1140
- if (isRedirect(err)) {
1141
- await this.navigate(err.options);
1142
- }
1143
- }
1144
- })();
1145
- } else if (status !== "success" || loaderShouldRunAsync && sync) {
1146
- await runLoader();
1147
- } else {
1148
- const head = await executeHead();
1149
- updateMatch(matchId, (prev) => ({
1150
- ...prev,
1151
- ...head
1152
- }));
1153
- }
1154
- }
1155
- if (!loaderIsRunningAsync) {
1156
- const { loaderPromise, loadPromise } = this.getMatch(matchId);
1157
- loaderPromise?.resolve();
1158
- loadPromise?.resolve();
1159
- }
1160
- updateMatch(matchId, (prev) => {
1161
- clearTimeout(prev.pendingTimeout);
1162
- return {
1163
- ...prev,
1164
- isFetching: loaderIsRunningAsync ? prev.isFetching : false,
1165
- loaderPromise: loaderIsRunningAsync ? prev.loaderPromise : void 0,
1166
- invalid: false,
1167
- pendingTimeout: void 0,
1168
- _dehydrated: void 0
1169
- };
1170
- });
1171
- return this.getMatch(matchId);
1172
- })()
1173
- );
1174
- });
1175
- await Promise.all(matchPromises);
1176
- resolveAll();
1204
+ await this.runLoader(innerLoadContext, matchId, index, route);
1205
+ const match3 = this.getMatch(matchId);
1206
+ match3._nonReactive.loaderPromise?.resolve();
1207
+ match3._nonReactive.loadPromise?.resolve();
1208
+ match3._nonReactive.loaderPromise = void 0;
1177
1209
  } catch (err) {
1178
- rejectAll(err);
1210
+ if (isRedirect(err)) {
1211
+ await this.navigate(err.options);
1212
+ }
1179
1213
  }
1180
1214
  })();
1181
- });
1182
- await triggerOnReady();
1183
- } catch (err) {
1184
- if (isRedirect(err) || isNotFound(err)) {
1185
- if (isNotFound(err) && !allPreload) {
1186
- await triggerOnReady();
1215
+ } else if (status !== "success" || loaderShouldRunAsync && innerLoadContext.sync) {
1216
+ await this.runLoader(innerLoadContext, matchId, index, route);
1217
+ } else {
1218
+ const headResult = this.executeHead(innerLoadContext, matchId, route);
1219
+ if (headResult) {
1220
+ const head = await headResult;
1221
+ innerLoadContext.updateMatch(matchId, (prev) => ({
1222
+ ...prev,
1223
+ ...head
1224
+ }));
1187
1225
  }
1226
+ }
1227
+ }
1228
+ const match = this.getMatch(matchId);
1229
+ if (!loaderIsRunningAsync) {
1230
+ match._nonReactive.loaderPromise?.resolve();
1231
+ match._nonReactive.loadPromise?.resolve();
1232
+ }
1233
+ clearTimeout(match._nonReactive.pendingTimeout);
1234
+ match._nonReactive.pendingTimeout = void 0;
1235
+ if (!loaderIsRunningAsync) match._nonReactive.loaderPromise = void 0;
1236
+ match._nonReactive.dehydrated = void 0;
1237
+ const nextIsFetching = loaderIsRunningAsync ? match.isFetching : false;
1238
+ if (nextIsFetching !== match.isFetching || match.invalid !== false) {
1239
+ innerLoadContext.updateMatch(matchId, (prev) => ({
1240
+ ...prev,
1241
+ isFetching: nextIsFetching,
1242
+ invalid: false
1243
+ }));
1244
+ }
1245
+ return this.getMatch(matchId);
1246
+ };
1247
+ this.loadMatches = async (baseContext) => {
1248
+ const innerLoadContext = baseContext;
1249
+ innerLoadContext.updateMatch ??= this.updateMatch;
1250
+ innerLoadContext.matchPromises = [];
1251
+ if (!this.isServer && this.state.matches.some((d) => d._forcePending)) {
1252
+ this.triggerOnReady(innerLoadContext);
1253
+ }
1254
+ try {
1255
+ for (let i = 0; i < innerLoadContext.matches.length; i++) {
1256
+ const beforeLoad = this.handleBeforeLoad(innerLoadContext, i);
1257
+ if (isPromise(beforeLoad)) await beforeLoad;
1258
+ }
1259
+ const max = innerLoadContext.firstBadMatchIndex ?? innerLoadContext.matches.length;
1260
+ for (let i = 0; i < max; i++) {
1261
+ innerLoadContext.matchPromises.push(
1262
+ this.loadRouteMatch(innerLoadContext, i)
1263
+ );
1264
+ }
1265
+ await Promise.all(innerLoadContext.matchPromises);
1266
+ const readyPromise = this.triggerOnReady(innerLoadContext);
1267
+ if (isPromise(readyPromise)) await readyPromise;
1268
+ } catch (err) {
1269
+ if (isNotFound(err) && !innerLoadContext.preload) {
1270
+ const readyPromise = this.triggerOnReady(innerLoadContext);
1271
+ if (isPromise(readyPromise)) await readyPromise;
1272
+ throw err;
1273
+ }
1274
+ if (isRedirect(err)) {
1188
1275
  throw err;
1189
1276
  }
1190
1277
  }
1191
- return matches;
1278
+ return innerLoadContext.matches;
1192
1279
  };
1193
1280
  this.invalidate = (opts) => {
1194
1281
  const invalidate = (d) => {
@@ -1196,7 +1283,7 @@ class RouterCore {
1196
1283
  return {
1197
1284
  ...d,
1198
1285
  invalid: true,
1199
- ...opts?.forcePending || d.status === "error" ? { status: "pending", error: void 0 } : {}
1286
+ ...opts?.forcePending || d.status === "error" ? { status: "pending", error: void 0 } : void 0
1200
1287
  };
1201
1288
  }
1202
1289
  return d;
@@ -1255,27 +1342,35 @@ class RouterCore {
1255
1342
  this.clearCache({ filter });
1256
1343
  };
1257
1344
  this.loadRouteChunk = (route) => {
1258
- if (route._lazyPromise === void 0) {
1345
+ if (!route._lazyLoaded && route._lazyPromise === void 0) {
1259
1346
  if (route.lazyFn) {
1260
1347
  route._lazyPromise = route.lazyFn().then((lazyRoute) => {
1261
1348
  const { id: _id, ...options2 } = lazyRoute.options;
1262
1349
  Object.assign(route.options, options2);
1350
+ route._lazyLoaded = true;
1351
+ route._lazyPromise = void 0;
1263
1352
  });
1264
1353
  } else {
1265
- route._lazyPromise = Promise.resolve();
1354
+ route._lazyLoaded = true;
1266
1355
  }
1267
1356
  }
1268
- if (route._componentsPromise === void 0) {
1269
- route._componentsPromise = route._lazyPromise.then(
1270
- () => Promise.all(
1271
- componentTypes.map(async (type) => {
1272
- const component = route.options[type];
1273
- if (component?.preload) {
1274
- await component.preload();
1275
- }
1276
- })
1277
- )
1278
- );
1357
+ if (!route._componentsLoaded && route._componentsPromise === void 0) {
1358
+ const loadComponents = () => {
1359
+ const preloads = [];
1360
+ for (const type of componentTypes) {
1361
+ const preload = route.options[type]?.preload;
1362
+ if (preload) preloads.push(preload());
1363
+ }
1364
+ if (preloads.length)
1365
+ return Promise.all(preloads).then(() => {
1366
+ route._componentsLoaded = true;
1367
+ route._componentsPromise = void 0;
1368
+ });
1369
+ route._componentsLoaded = true;
1370
+ route._componentsPromise = void 0;
1371
+ return;
1372
+ };
1373
+ route._componentsPromise = route._lazyPromise ? route._lazyPromise.then(loadComponents) : loadComponents();
1279
1374
  }
1280
1375
  return route._componentsPromise;
1281
1376
  };
@@ -1373,12 +1468,10 @@ class RouterCore {
1373
1468
  }
1374
1469
  return match;
1375
1470
  };
1376
- this._handleNotFound = (matches, err, {
1377
- updateMatch = this.updateMatch
1378
- } = {}) => {
1471
+ this._handleNotFound = (innerLoadContext, err) => {
1379
1472
  const routeCursor = this.routesById[err.routeId ?? ""] ?? this.routeTree;
1380
1473
  const matchesByRouteId = {};
1381
- for (const match of matches) {
1474
+ for (const match of innerLoadContext.matches) {
1382
1475
  matchesByRouteId[match.routeId] = match;
1383
1476
  }
1384
1477
  if (!routeCursor.options.notFoundComponent && this.options?.defaultNotFoundComponent) {
@@ -1393,7 +1486,7 @@ class RouterCore {
1393
1486
  matchForRoute,
1394
1487
  "Could not find match for route: " + routeCursor.id
1395
1488
  );
1396
- updateMatch(matchForRoute.id, (prev) => ({
1489
+ innerLoadContext.updateMatch(matchForRoute.id, (prev) => ({
1397
1490
  ...prev,
1398
1491
  status: "notFound",
1399
1492
  error: err,
@@ -1401,9 +1494,7 @@ class RouterCore {
1401
1494
  }));
1402
1495
  if (err.routerCode === "BEFORE_LOAD" && routeCursor.parentRoute) {
1403
1496
  err.routeId = routeCursor.parentRoute.id;
1404
- this._handleNotFound(matches, err, {
1405
- updateMatch
1406
- });
1497
+ this._handleNotFound(innerLoadContext, err);
1407
1498
  }
1408
1499
  };
1409
1500
  this.hasNotFoundMatch = () => {
@@ -1493,16 +1584,16 @@ class RouterCore {
1493
1584
  const matches = [];
1494
1585
  const getParentContext = (parentMatch) => {
1495
1586
  const parentMatchId = parentMatch?.id;
1496
- const parentContext = !parentMatchId ? this.options.context ?? {} : parentMatch.context ?? this.options.context ?? {};
1587
+ const parentContext = !parentMatchId ? this.options.context ?? void 0 : parentMatch.context ?? this.options.context ?? void 0;
1497
1588
  return parentContext;
1498
1589
  };
1499
1590
  matchedRoutes.forEach((route, index) => {
1500
1591
  const parentMatch = matches[index - 1];
1501
1592
  const [preMatchSearch, strictMatchSearch, searchError] = (() => {
1502
1593
  const parentSearch = parentMatch?.search ?? next.search;
1503
- const parentStrictSearch = parentMatch?._strictSearch ?? {};
1594
+ const parentStrictSearch = parentMatch?._strictSearch ?? void 0;
1504
1595
  try {
1505
- const strictSearch = validateSearch(route.options.validateSearch, { ...parentSearch }) ?? {};
1596
+ const strictSearch = validateSearch(route.options.validateSearch, { ...parentSearch }) ?? void 0;
1506
1597
  return [
1507
1598
  {
1508
1599
  ...parentSearch,
@@ -1572,7 +1663,10 @@ class RouterCore {
1572
1663
  isFetching: false,
1573
1664
  error: void 0,
1574
1665
  paramsError: parseErrors[index],
1575
- __routeContext: {},
1666
+ __routeContext: void 0,
1667
+ _nonReactive: {
1668
+ loadPromise: createControlledPromise()
1669
+ },
1576
1670
  __beforeLoadContext: void 0,
1577
1671
  context: {},
1578
1672
  abortController: new AbortController(),
@@ -1586,7 +1680,6 @@ class RouterCore {
1586
1680
  headScripts: void 0,
1587
1681
  meta: void 0,
1588
1682
  staticData: route.options.staticData || {},
1589
- loadPromise: createControlledPromise(),
1590
1683
  fullPath: route.fullPath
1591
1684
  };
1592
1685
  }
@@ -1608,19 +1701,21 @@ class RouterCore {
1608
1701
  if (!existingMatch && opts?._buildLocation !== true) {
1609
1702
  const parentMatch = matches[index - 1];
1610
1703
  const parentContext = getParentContext(parentMatch);
1611
- const contextFnContext = {
1612
- deps: match.loaderDeps,
1613
- params: match.params,
1614
- context: parentContext,
1615
- location: next,
1616
- navigate: (opts2) => this.navigate({ ...opts2, _fromLocation: next }),
1617
- buildLocation: this.buildLocation,
1618
- cause: match.cause,
1619
- abortController: match.abortController,
1620
- preload: !!match.preload,
1621
- matches
1622
- };
1623
- match.__routeContext = route.options.context?.(contextFnContext) ?? {};
1704
+ if (route.options.context) {
1705
+ const contextFnContext = {
1706
+ deps: match.loaderDeps,
1707
+ params: match.params,
1708
+ context: parentContext ?? {},
1709
+ location: next,
1710
+ navigate: (opts2) => this.navigate({ ...opts2, _fromLocation: next }),
1711
+ buildLocation: this.buildLocation,
1712
+ cause: match.cause,
1713
+ abortController: match.abortController,
1714
+ preload: !!match.preload,
1715
+ matches
1716
+ };
1717
+ match.__routeContext = route.options.context(contextFnContext) ?? void 0;
1718
+ }
1624
1719
  match.context = {
1625
1720
  ...parentContext,
1626
1721
  ...match.__routeContext,
@@ -1635,6 +1730,12 @@ class SearchParamError extends Error {
1635
1730
  }
1636
1731
  class PathParamError extends Error {
1637
1732
  }
1733
+ function makeMaybe(value, error) {
1734
+ if (error) {
1735
+ return { status: "error", error };
1736
+ }
1737
+ return { status: "success", value };
1738
+ }
1638
1739
  const normalize = (str) => str.endsWith("/") && str.length > 1 ? str.slice(0, -1) : str;
1639
1740
  function comparePaths(a, b) {
1640
1741
  return normalize(a) === normalize(b);
@@ -1923,7 +2024,7 @@ function applySearchMiddleware({
1923
2024
  try {
1924
2025
  const validatedSearch = {
1925
2026
  ...result,
1926
- ...validateSearch(route.options.validateSearch, result) ?? {}
2027
+ ...validateSearch(route.options.validateSearch, result) ?? void 0
1927
2028
  };
1928
2029
  return validatedSearch;
1929
2030
  } catch {