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