@tanstack/router-core 1.124.2 → 1.125.1

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.
Files changed (45) hide show
  1. package/dist/cjs/Matches.cjs.map +1 -1
  2. package/dist/cjs/Matches.d.cts +29 -0
  3. package/dist/cjs/index.cjs +1 -0
  4. package/dist/cjs/index.cjs.map +1 -1
  5. package/dist/cjs/index.d.cts +1 -1
  6. package/dist/cjs/route.cjs +0 -5
  7. package/dist/cjs/route.cjs.map +1 -1
  8. package/dist/cjs/route.d.cts +20 -6
  9. package/dist/cjs/router.cjs +208 -79
  10. package/dist/cjs/router.cjs.map +1 -1
  11. package/dist/cjs/router.d.cts +6 -1
  12. package/dist/cjs/ssr/ssr-client.cjs +38 -2
  13. package/dist/cjs/ssr/ssr-client.cjs.map +1 -1
  14. package/dist/cjs/ssr/ssr-client.d.cts +1 -0
  15. package/dist/cjs/ssr/ssr-server.cjs +2 -1
  16. package/dist/cjs/ssr/ssr-server.cjs.map +1 -1
  17. package/dist/cjs/utils.cjs +5 -0
  18. package/dist/cjs/utils.cjs.map +1 -1
  19. package/dist/cjs/utils.d.cts +1 -0
  20. package/dist/esm/Matches.d.ts +29 -0
  21. package/dist/esm/Matches.js.map +1 -1
  22. package/dist/esm/index.d.ts +1 -1
  23. package/dist/esm/index.js +2 -1
  24. package/dist/esm/route.d.ts +20 -6
  25. package/dist/esm/route.js +0 -5
  26. package/dist/esm/route.js.map +1 -1
  27. package/dist/esm/router.d.ts +6 -1
  28. package/dist/esm/router.js +208 -79
  29. package/dist/esm/router.js.map +1 -1
  30. package/dist/esm/ssr/ssr-client.d.ts +1 -0
  31. package/dist/esm/ssr/ssr-client.js +38 -2
  32. package/dist/esm/ssr/ssr-client.js.map +1 -1
  33. package/dist/esm/ssr/ssr-server.js +2 -1
  34. package/dist/esm/ssr/ssr-server.js.map +1 -1
  35. package/dist/esm/utils.d.ts +1 -0
  36. package/dist/esm/utils.js +5 -0
  37. package/dist/esm/utils.js.map +1 -1
  38. package/package.json +1 -1
  39. package/src/Matches.ts +38 -0
  40. package/src/index.ts +1 -0
  41. package/src/route.ts +32 -10
  42. package/src/router.ts +259 -96
  43. package/src/ssr/ssr-client.ts +49 -3
  44. package/src/ssr/ssr-server.ts +1 -0
  45. package/src/utils.ts +12 -0
@@ -110,8 +110,7 @@ class RouterCore {
110
110
  routeTree: this.routeTree,
111
111
  initRoute: (route, i) => {
112
112
  route.init({
113
- originalIndex: i,
114
- defaultSsr: this.options.defaultSsr
113
+ originalIndex: i
115
114
  });
116
115
  }
117
116
  });
@@ -121,8 +120,7 @@ class RouterCore {
121
120
  const notFoundRoute = this.options.notFoundRoute;
122
121
  if (notFoundRoute) {
123
122
  notFoundRoute.init({
124
- originalIndex: 99999999999,
125
- defaultSsr: this.options.defaultSsr
123
+ originalIndex: 99999999999
126
124
  });
127
125
  this.routesById[notFoundRoute.id] = notFoundRoute;
128
126
  }
@@ -213,7 +211,13 @@ class RouterCore {
213
211
  const match = this.getMatch(id);
214
212
  if (!match) return;
215
213
  match.abortController.abort();
216
- clearTimeout(match.pendingTimeout);
214
+ this.updateMatch(id, (prev) => {
215
+ clearTimeout(prev.pendingTimeout);
216
+ return {
217
+ ...prev,
218
+ pendingTimeout: void 0
219
+ };
220
+ });
217
221
  };
218
222
  this.cancelMatches = () => {
219
223
  var _a;
@@ -496,7 +500,10 @@ class RouterCore {
496
500
  throw redirect({ href: nextLocation.href });
497
501
  }
498
502
  }
499
- const pendingMatches = this.matchRoutes(this.latestLocation);
503
+ let pendingMatches = this.matchRoutes(this.latestLocation);
504
+ if (this.isShell) {
505
+ pendingMatches = pendingMatches.slice(0, 1);
506
+ }
500
507
  this.__store.setState((s) => ({
501
508
  ...s,
502
509
  status: "pending",
@@ -691,12 +698,41 @@ class RouterCore {
691
698
  const triggerOnReady = async () => {
692
699
  if (!rendered) {
693
700
  rendered = true;
701
+ if (!allPreload && !this.isServer) {
702
+ matches.forEach((match) => {
703
+ const {
704
+ id: matchId,
705
+ routeId,
706
+ _forcePending,
707
+ minPendingPromise
708
+ } = match;
709
+ const route = this.looseRoutesById[routeId];
710
+ const pendingMinMs = route.options.pendingMinMs ?? this.options.defaultPendingMinMs;
711
+ if (_forcePending && pendingMinMs && !minPendingPromise) {
712
+ const minPendingPromise2 = createControlledPromise();
713
+ updateMatch(matchId, (prev) => ({
714
+ ...prev,
715
+ minPendingPromise: minPendingPromise2
716
+ }));
717
+ setTimeout(() => {
718
+ minPendingPromise2.resolve();
719
+ updateMatch(matchId, (prev) => ({
720
+ ...prev,
721
+ minPendingPromise: void 0
722
+ }));
723
+ }, pendingMinMs);
724
+ }
725
+ });
726
+ }
694
727
  await (onReady == null ? void 0 : onReady());
695
728
  }
696
729
  };
697
730
  const resolvePreload = (matchId) => {
698
731
  return !!(allPreload && !this.state.matches.find((d) => d.id === matchId));
699
732
  };
733
+ if (!this.isServer && this.state.matches.find((d) => d._forcePending)) {
734
+ triggerOnReady();
735
+ }
700
736
  const handleRedirectAndNotFound = (match, err) => {
701
737
  var _a, _b, _c, _d;
702
738
  if (isRedirect(err) || isNotFound(err)) {
@@ -739,6 +775,18 @@ class RouterCore {
739
775
  }
740
776
  }
741
777
  };
778
+ const shouldSkipLoader = (matchId) => {
779
+ const match = this.getMatch(matchId);
780
+ if (!this.isServer && match._dehydrated) {
781
+ return true;
782
+ }
783
+ if (this.isServer) {
784
+ if (match.ssr === false) {
785
+ return true;
786
+ }
787
+ }
788
+ return false;
789
+ };
742
790
  try {
743
791
  await new Promise((resolveAll, rejectAll) => {
744
792
  ;
@@ -779,23 +827,82 @@ class RouterCore {
779
827
  for (const [index, { id: matchId, routeId }] of matches.entries()) {
780
828
  const existingMatch = this.getMatch(matchId);
781
829
  const parentMatchId = (_a = matches[index - 1]) == null ? void 0 : _a.id;
830
+ const parentMatch = parentMatchId ? this.getMatch(parentMatchId) : void 0;
782
831
  const route = this.looseRoutesById[routeId];
783
832
  const pendingMs = route.options.pendingMs ?? this.options.defaultPendingMs;
833
+ if (this.isServer) {
834
+ const defaultSsr = this.options.defaultSsr ?? true;
835
+ let ssr;
836
+ if ((parentMatch == null ? void 0 : parentMatch.ssr) === false) {
837
+ ssr = false;
838
+ } else {
839
+ let tempSsr;
840
+ if (route.options.ssr === void 0) {
841
+ tempSsr = defaultSsr;
842
+ } else if (typeof route.options.ssr === "function") {
843
+ let makeMaybe = function(value, error) {
844
+ if (error) {
845
+ return { status: "error", error };
846
+ }
847
+ return { status: "success", value };
848
+ };
849
+ const { search, params } = this.getMatch(matchId);
850
+ const ssrFnContext = {
851
+ search: makeMaybe(search, existingMatch.searchError),
852
+ params: makeMaybe(params, existingMatch.paramsError),
853
+ location,
854
+ matches: matches.map((match) => ({
855
+ index: match.index,
856
+ pathname: match.pathname,
857
+ fullPath: match.fullPath,
858
+ staticData: match.staticData,
859
+ id: match.id,
860
+ routeId: match.routeId,
861
+ search: makeMaybe(match.search, match.searchError),
862
+ params: makeMaybe(match.params, match.paramsError),
863
+ ssr: match.ssr
864
+ }))
865
+ };
866
+ tempSsr = await route.options.ssr(ssrFnContext) ?? defaultSsr;
867
+ } else {
868
+ tempSsr = route.options.ssr;
869
+ }
870
+ if (tempSsr === true && (parentMatch == null ? void 0 : parentMatch.ssr) === "data-only") {
871
+ ssr = "data-only";
872
+ } else {
873
+ ssr = tempSsr;
874
+ }
875
+ }
876
+ updateMatch(matchId, (prev) => ({
877
+ ...prev,
878
+ ssr
879
+ }));
880
+ }
881
+ if (shouldSkipLoader(matchId)) {
882
+ continue;
883
+ }
784
884
  const shouldPending = !!(onReady && !this.isServer && !resolvePreload(matchId) && (route.options.loader || route.options.beforeLoad || routeNeedsPreload(route)) && typeof pendingMs === "number" && pendingMs !== Infinity && (route.options.pendingComponent ?? ((_b = this.options) == null ? void 0 : _b.defaultPendingComponent)));
785
885
  let executeBeforeLoad = true;
786
- if (
787
- // If we are in the middle of a load, either of these will be present
788
- // (not to be confused with `loadPromise`, which is always defined)
789
- existingMatch.beforeLoadPromise || existingMatch.loaderPromise
790
- ) {
791
- if (shouldPending) {
792
- setTimeout(() => {
886
+ const setupPendingTimeout = () => {
887
+ if (shouldPending && this.getMatch(matchId).pendingTimeout === void 0) {
888
+ const pendingTimeout = setTimeout(() => {
793
889
  try {
794
890
  triggerOnReady();
795
891
  } catch {
796
892
  }
797
893
  }, pendingMs);
894
+ updateMatch(matchId, (prev) => ({
895
+ ...prev,
896
+ pendingTimeout
897
+ }));
798
898
  }
899
+ };
900
+ if (
901
+ // If we are in the middle of a load, either of these will be present
902
+ // (not to be confused with `loadPromise`, which is always defined)
903
+ existingMatch.beforeLoadPromise || existingMatch.loaderPromise
904
+ ) {
905
+ setupPendingTimeout();
799
906
  await existingMatch.beforeLoadPromise;
800
907
  const match = this.getMatch(matchId);
801
908
  if (match.status === "error") {
@@ -816,16 +923,6 @@ class RouterCore {
816
923
  beforeLoadPromise: createControlledPromise()
817
924
  };
818
925
  });
819
- const abortController = new AbortController();
820
- let pendingTimeout;
821
- if (shouldPending) {
822
- pendingTimeout = setTimeout(() => {
823
- try {
824
- triggerOnReady();
825
- } catch {
826
- }
827
- }, pendingMs);
828
- }
829
926
  const { paramsError, searchError } = this.getMatch(matchId);
830
927
  if (paramsError) {
831
928
  handleSerialError(index, paramsError, "PARSE_PARAMS");
@@ -833,15 +930,16 @@ class RouterCore {
833
930
  if (searchError) {
834
931
  handleSerialError(index, searchError, "VALIDATE_SEARCH");
835
932
  }
836
- const getParentMatchContext = () => parentMatchId ? this.getMatch(parentMatchId).context : this.options.context ?? {};
933
+ setupPendingTimeout();
934
+ const abortController = new AbortController();
935
+ const parentMatchContext = (parentMatch == null ? void 0 : parentMatch.context) ?? this.options.context ?? {};
837
936
  updateMatch(matchId, (prev) => ({
838
937
  ...prev,
839
938
  isFetching: "beforeLoad",
840
939
  fetchCount: prev.fetchCount + 1,
841
940
  abortController,
842
- pendingTimeout,
843
941
  context: {
844
- ...getParentMatchContext(),
942
+ ...parentMatchContext,
845
943
  ...prev.__routeContext
846
944
  }
847
945
  }));
@@ -868,7 +966,7 @@ class RouterCore {
868
966
  ...prev,
869
967
  __beforeLoadContext: beforeLoadContext,
870
968
  context: {
871
- ...getParentMatchContext(),
969
+ ...parentMatchContext,
872
970
  ...prev.__routeContext,
873
971
  ...beforeLoadContext
874
972
  },
@@ -894,10 +992,61 @@ class RouterCore {
894
992
  validResolvedMatches.forEach(({ id: matchId, routeId }, index) => {
895
993
  matchPromises.push(
896
994
  (async () => {
995
+ var _a2, _b2;
897
996
  let loaderShouldRunAsync = false;
898
997
  let loaderIsRunningAsync = false;
998
+ const route = this.looseRoutesById[routeId];
999
+ const executeHead = async () => {
1000
+ var _a3, _b3, _c2, _d2, _e, _f;
1001
+ const match = this.getMatch(matchId);
1002
+ if (!match) {
1003
+ return;
1004
+ }
1005
+ const assetContext = {
1006
+ matches,
1007
+ match,
1008
+ params: match.params,
1009
+ loaderData: match.loaderData
1010
+ };
1011
+ const headFnContent = await ((_b3 = (_a3 = route.options).head) == null ? void 0 : _b3.call(_a3, assetContext));
1012
+ const meta = headFnContent == null ? void 0 : headFnContent.meta;
1013
+ const links = headFnContent == null ? void 0 : headFnContent.links;
1014
+ const headScripts = headFnContent == null ? void 0 : headFnContent.scripts;
1015
+ const styles = headFnContent == null ? void 0 : headFnContent.styles;
1016
+ const scripts = await ((_d2 = (_c2 = route.options).scripts) == null ? void 0 : _d2.call(_c2, assetContext));
1017
+ const headers = await ((_f = (_e = route.options).headers) == null ? void 0 : _f.call(_e, assetContext));
1018
+ return {
1019
+ meta,
1020
+ links,
1021
+ headScripts,
1022
+ headers,
1023
+ scripts,
1024
+ styles
1025
+ };
1026
+ };
1027
+ const potentialPendingMinPromise = async () => {
1028
+ const latestMatch = this.getMatch(matchId);
1029
+ if (latestMatch.minPendingPromise) {
1030
+ await latestMatch.minPendingPromise;
1031
+ }
1032
+ };
899
1033
  const prevMatch = this.getMatch(matchId);
900
- if (prevMatch.loaderPromise) {
1034
+ if (shouldSkipLoader(matchId)) {
1035
+ if (this.isServer) {
1036
+ const head = await executeHead();
1037
+ updateMatch(matchId, (prev) => ({
1038
+ ...prev,
1039
+ ...head
1040
+ }));
1041
+ (_a2 = this.serverSsr) == null ? void 0 : _a2.onMatchSettled({
1042
+ router: this,
1043
+ match: this.getMatch(matchId)
1044
+ });
1045
+ return this.getMatch(matchId);
1046
+ } else {
1047
+ await potentialPendingMinPromise();
1048
+ }
1049
+ } else if (prevMatch.loaderPromise) {
901
1050
  if (prevMatch.status === "success" && !sync && !prevMatch.preload) {
902
1051
  return this.getMatch(matchId);
903
1052
  }
@@ -908,7 +1057,6 @@ class RouterCore {
908
1057
  }
909
1058
  } else {
910
1059
  const parentMatchPromise = matchPromises[index - 1];
911
- const route = this.looseRoutesById[routeId];
912
1060
  const getLoaderContext = () => {
913
1061
  const {
914
1062
  params,
@@ -941,55 +1089,28 @@ class RouterCore {
941
1089
  loaderPromise: createControlledPromise(),
942
1090
  preload: !!preload && !this.state.matches.find((d) => d.id === matchId)
943
1091
  }));
944
- const executeHead = async () => {
945
- var _a2, _b2, _c2, _d2, _e, _f;
946
- const match = this.getMatch(matchId);
947
- if (!match) {
948
- return;
949
- }
950
- const assetContext = {
951
- matches,
952
- match,
953
- params: match.params,
954
- loaderData: match.loaderData
955
- };
956
- const headFnContent = await ((_b2 = (_a2 = route.options).head) == null ? void 0 : _b2.call(_a2, assetContext));
957
- const meta = headFnContent == null ? void 0 : headFnContent.meta;
958
- const links = headFnContent == null ? void 0 : headFnContent.links;
959
- const headScripts = headFnContent == null ? void 0 : headFnContent.scripts;
960
- const styles = headFnContent == null ? void 0 : headFnContent.styles;
961
- const scripts = await ((_d2 = (_c2 = route.options).scripts) == null ? void 0 : _d2.call(_c2, assetContext));
962
- const headers = await ((_f = (_e = route.options).headers) == null ? void 0 : _f.call(_e, assetContext));
963
- return {
964
- meta,
965
- links,
966
- headScripts,
967
- headers,
968
- scripts,
969
- styles
970
- };
971
- };
972
1092
  const runLoader = async () => {
973
- var _a2, _b2, _c2, _d2, _e;
1093
+ var _a3, _b3, _c2, _d2, _e;
974
1094
  try {
975
- const potentialPendingMinPromise = async () => {
976
- const latestMatch = this.getMatch(matchId);
977
- if (latestMatch.minPendingPromise) {
978
- await latestMatch.minPendingPromise;
979
- }
980
- };
981
1095
  try {
982
- this.loadRouteChunk(route);
1096
+ if (!this.isServer || this.isServer && this.getMatch(matchId).ssr === true) {
1097
+ this.loadRouteChunk(route);
1098
+ }
983
1099
  updateMatch(matchId, (prev) => ({
984
1100
  ...prev,
985
1101
  isFetching: "loader"
986
1102
  }));
987
- const loaderData = await ((_b2 = (_a2 = route.options).loader) == null ? void 0 : _b2.call(_a2, getLoaderContext()));
1103
+ const loaderData = await ((_b3 = (_a3 = route.options).loader) == null ? void 0 : _b3.call(_a3, getLoaderContext()));
988
1104
  handleRedirectAndNotFound(
989
1105
  this.getMatch(matchId),
990
1106
  loaderData
991
1107
  );
1108
+ updateMatch(matchId, (prev) => ({
1109
+ ...prev,
1110
+ loaderData
1111
+ }));
992
1112
  await route._lazyPromise;
1113
+ const head = await executeHead();
993
1114
  await potentialPendingMinPromise();
994
1115
  await route._componentsPromise;
995
1116
  updateMatch(matchId, (prev) => ({
@@ -998,11 +1119,6 @@ class RouterCore {
998
1119
  status: "success",
999
1120
  isFetching: false,
1000
1121
  updatedAt: Date.now(),
1001
- loaderData
1002
- }));
1003
- const head = await executeHead();
1004
- updateMatch(matchId, (prev) => ({
1005
- ...prev,
1006
1122
  ...head
1007
1123
  }));
1008
1124
  } catch (e) {
@@ -1041,10 +1157,10 @@ class RouterCore {
1041
1157
  handleRedirectAndNotFound(this.getMatch(matchId), err);
1042
1158
  }
1043
1159
  };
1044
- const { status, invalid } = this.getMatch(matchId);
1160
+ const { status, invalid, _forcePending } = this.getMatch(matchId);
1045
1161
  loaderShouldRunAsync = status === "success" && (invalid || (shouldReload ?? age > staleAge));
1046
1162
  if (preload && route.options.preload === false) {
1047
- } else if (loaderShouldRunAsync && !sync) {
1163
+ } else if (loaderShouldRunAsync && !sync && !_forcePending) {
1048
1164
  loaderIsRunningAsync = true;
1049
1165
  (async () => {
1050
1166
  try {
@@ -1065,11 +1181,18 @@ class RouterCore {
1065
1181
  } else if (status !== "success" || loaderShouldRunAsync && sync) {
1066
1182
  await runLoader();
1067
1183
  } else {
1184
+ if (_forcePending) {
1185
+ await potentialPendingMinPromise();
1186
+ }
1068
1187
  const head = await executeHead();
1069
1188
  updateMatch(matchId, (prev) => ({
1070
1189
  ...prev,
1071
1190
  ...head
1072
1191
  }));
1192
+ (_b2 = this.serverSsr) == null ? void 0 : _b2.onMatchSettled({
1193
+ router: this,
1194
+ match: this.getMatch(matchId)
1195
+ });
1073
1196
  }
1074
1197
  }
1075
1198
  if (!loaderIsRunningAsync) {
@@ -1077,12 +1200,18 @@ class RouterCore {
1077
1200
  loaderPromise == null ? void 0 : loaderPromise.resolve();
1078
1201
  loadPromise == null ? void 0 : loadPromise.resolve();
1079
1202
  }
1080
- updateMatch(matchId, (prev) => ({
1081
- ...prev,
1082
- isFetching: loaderIsRunningAsync ? prev.isFetching : false,
1083
- loaderPromise: loaderIsRunningAsync ? prev.loaderPromise : void 0,
1084
- invalid: false
1085
- }));
1203
+ updateMatch(matchId, (prev) => {
1204
+ clearTimeout(prev.pendingTimeout);
1205
+ return {
1206
+ ...prev,
1207
+ isFetching: loaderIsRunningAsync ? prev.isFetching : false,
1208
+ loaderPromise: loaderIsRunningAsync ? prev.loaderPromise : void 0,
1209
+ invalid: false,
1210
+ pendingTimeout: void 0,
1211
+ _dehydrated: void 0,
1212
+ _forcePending: void 0
1213
+ };
1214
+ });
1086
1215
  return this.getMatch(matchId);
1087
1216
  })()
1088
1217
  );