@tanstack/router-core 1.124.2 → 1.125.0

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 +176 -55
  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 +176 -55
  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 +216 -63
  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
@@ -112,8 +112,7 @@ class RouterCore {
112
112
  routeTree: this.routeTree,
113
113
  initRoute: (route, i) => {
114
114
  route.init({
115
- originalIndex: i,
116
- defaultSsr: this.options.defaultSsr
115
+ originalIndex: i
117
116
  });
118
117
  }
119
118
  });
@@ -123,8 +122,7 @@ class RouterCore {
123
122
  const notFoundRoute = this.options.notFoundRoute;
124
123
  if (notFoundRoute) {
125
124
  notFoundRoute.init({
126
- originalIndex: 99999999999,
127
- defaultSsr: this.options.defaultSsr
125
+ originalIndex: 99999999999
128
126
  });
129
127
  this.routesById[notFoundRoute.id] = notFoundRoute;
130
128
  }
@@ -498,7 +496,10 @@ class RouterCore {
498
496
  throw redirect.redirect({ href: nextLocation.href });
499
497
  }
500
498
  }
501
- const pendingMatches = this.matchRoutes(this.latestLocation);
499
+ let pendingMatches = this.matchRoutes(this.latestLocation);
500
+ if (this.isShell) {
501
+ pendingMatches = pendingMatches.slice(0, 1);
502
+ }
502
503
  this.__store.setState((s) => ({
503
504
  ...s,
504
505
  status: "pending",
@@ -693,12 +694,41 @@ class RouterCore {
693
694
  const triggerOnReady = async () => {
694
695
  if (!rendered) {
695
696
  rendered = true;
697
+ if (!allPreload && !this.isServer) {
698
+ matches.forEach((match) => {
699
+ const {
700
+ id: matchId,
701
+ routeId,
702
+ _forcePending,
703
+ minPendingPromise
704
+ } = match;
705
+ const route = this.looseRoutesById[routeId];
706
+ const pendingMinMs = route.options.pendingMinMs ?? this.options.defaultPendingMinMs;
707
+ if (_forcePending && pendingMinMs && !minPendingPromise) {
708
+ const minPendingPromise2 = utils.createControlledPromise();
709
+ updateMatch(matchId, (prev) => ({
710
+ ...prev,
711
+ minPendingPromise: minPendingPromise2
712
+ }));
713
+ setTimeout(() => {
714
+ minPendingPromise2.resolve();
715
+ updateMatch(matchId, (prev) => ({
716
+ ...prev,
717
+ minPendingPromise: void 0
718
+ }));
719
+ }, pendingMinMs);
720
+ }
721
+ });
722
+ }
696
723
  await (onReady == null ? void 0 : onReady());
697
724
  }
698
725
  };
699
726
  const resolvePreload = (matchId) => {
700
727
  return !!(allPreload && !this.state.matches.find((d) => d.id === matchId));
701
728
  };
729
+ if (!this.isServer && this.state.matches.find((d) => d._forcePending)) {
730
+ triggerOnReady();
731
+ }
702
732
  const handleRedirectAndNotFound = (match, err) => {
703
733
  var _a, _b, _c, _d;
704
734
  if (redirect.isRedirect(err) || notFound.isNotFound(err)) {
@@ -741,6 +771,18 @@ class RouterCore {
741
771
  }
742
772
  }
743
773
  };
774
+ const shouldSkipLoader = (matchId) => {
775
+ const match = this.getMatch(matchId);
776
+ if (!this.isServer && match._dehydrated) {
777
+ return true;
778
+ }
779
+ if (this.isServer) {
780
+ if (match.ssr === false) {
781
+ return true;
782
+ }
783
+ }
784
+ return false;
785
+ };
744
786
  try {
745
787
  await new Promise((resolveAll, rejectAll) => {
746
788
  ;
@@ -781,8 +823,60 @@ class RouterCore {
781
823
  for (const [index, { id: matchId, routeId }] of matches.entries()) {
782
824
  const existingMatch = this.getMatch(matchId);
783
825
  const parentMatchId = (_a = matches[index - 1]) == null ? void 0 : _a.id;
826
+ const parentMatch = parentMatchId ? this.getMatch(parentMatchId) : void 0;
784
827
  const route = this.looseRoutesById[routeId];
785
828
  const pendingMs = route.options.pendingMs ?? this.options.defaultPendingMs;
829
+ if (this.isServer) {
830
+ const defaultSsr = this.options.defaultSsr ?? true;
831
+ let ssr;
832
+ if ((parentMatch == null ? void 0 : parentMatch.ssr) === false) {
833
+ ssr = false;
834
+ } else {
835
+ let tempSsr;
836
+ if (route.options.ssr === void 0) {
837
+ tempSsr = defaultSsr;
838
+ } else if (typeof route.options.ssr === "function") {
839
+ let makeMaybe = function(value, error) {
840
+ if (error) {
841
+ return { status: "error", error };
842
+ }
843
+ return { status: "success", value };
844
+ };
845
+ const { search, params } = this.getMatch(matchId);
846
+ const ssrFnContext = {
847
+ search: makeMaybe(search, existingMatch.searchError),
848
+ params: makeMaybe(params, existingMatch.paramsError),
849
+ location,
850
+ matches: matches.map((match) => ({
851
+ index: match.index,
852
+ pathname: match.pathname,
853
+ fullPath: match.fullPath,
854
+ staticData: match.staticData,
855
+ id: match.id,
856
+ routeId: match.routeId,
857
+ search: makeMaybe(match.search, match.searchError),
858
+ params: makeMaybe(match.params, match.paramsError),
859
+ ssr: match.ssr
860
+ }))
861
+ };
862
+ tempSsr = await route.options.ssr(ssrFnContext) ?? defaultSsr;
863
+ } else {
864
+ tempSsr = route.options.ssr;
865
+ }
866
+ if (tempSsr === true && (parentMatch == null ? void 0 : parentMatch.ssr) === "data-only") {
867
+ ssr = "data-only";
868
+ } else {
869
+ ssr = tempSsr;
870
+ }
871
+ }
872
+ updateMatch(matchId, (prev) => ({
873
+ ...prev,
874
+ ssr
875
+ }));
876
+ }
877
+ if (shouldSkipLoader(matchId)) {
878
+ continue;
879
+ }
786
880
  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)));
787
881
  let executeBeforeLoad = true;
788
882
  if (
@@ -835,7 +929,7 @@ class RouterCore {
835
929
  if (searchError) {
836
930
  handleSerialError(index, searchError, "VALIDATE_SEARCH");
837
931
  }
838
- const getParentMatchContext = () => parentMatchId ? this.getMatch(parentMatchId).context : this.options.context ?? {};
932
+ const parentMatchContext = (parentMatch == null ? void 0 : parentMatch.context) ?? this.options.context ?? {};
839
933
  updateMatch(matchId, (prev) => ({
840
934
  ...prev,
841
935
  isFetching: "beforeLoad",
@@ -843,7 +937,7 @@ class RouterCore {
843
937
  abortController,
844
938
  pendingTimeout,
845
939
  context: {
846
- ...getParentMatchContext(),
940
+ ...parentMatchContext,
847
941
  ...prev.__routeContext
848
942
  }
849
943
  }));
@@ -870,7 +964,7 @@ class RouterCore {
870
964
  ...prev,
871
965
  __beforeLoadContext: beforeLoadContext,
872
966
  context: {
873
- ...getParentMatchContext(),
967
+ ...parentMatchContext,
874
968
  ...prev.__routeContext,
875
969
  ...beforeLoadContext
876
970
  },
@@ -896,10 +990,61 @@ class RouterCore {
896
990
  validResolvedMatches.forEach(({ id: matchId, routeId }, index) => {
897
991
  matchPromises.push(
898
992
  (async () => {
993
+ var _a2, _b2;
899
994
  let loaderShouldRunAsync = false;
900
995
  let loaderIsRunningAsync = false;
996
+ const route = this.looseRoutesById[routeId];
997
+ const executeHead = async () => {
998
+ var _a3, _b3, _c2, _d2, _e, _f;
999
+ const match = this.getMatch(matchId);
1000
+ if (!match) {
1001
+ return;
1002
+ }
1003
+ const assetContext = {
1004
+ matches,
1005
+ match,
1006
+ params: match.params,
1007
+ loaderData: match.loaderData
1008
+ };
1009
+ const headFnContent = await ((_b3 = (_a3 = route.options).head) == null ? void 0 : _b3.call(_a3, assetContext));
1010
+ const meta = headFnContent == null ? void 0 : headFnContent.meta;
1011
+ const links = headFnContent == null ? void 0 : headFnContent.links;
1012
+ const headScripts = headFnContent == null ? void 0 : headFnContent.scripts;
1013
+ const styles = headFnContent == null ? void 0 : headFnContent.styles;
1014
+ const scripts = await ((_d2 = (_c2 = route.options).scripts) == null ? void 0 : _d2.call(_c2, assetContext));
1015
+ const headers = await ((_f = (_e = route.options).headers) == null ? void 0 : _f.call(_e, assetContext));
1016
+ return {
1017
+ meta,
1018
+ links,
1019
+ headScripts,
1020
+ headers,
1021
+ scripts,
1022
+ styles
1023
+ };
1024
+ };
1025
+ const potentialPendingMinPromise = async () => {
1026
+ const latestMatch = this.getMatch(matchId);
1027
+ if (latestMatch.minPendingPromise) {
1028
+ await latestMatch.minPendingPromise;
1029
+ }
1030
+ };
901
1031
  const prevMatch = this.getMatch(matchId);
902
- if (prevMatch.loaderPromise) {
1032
+ if (shouldSkipLoader(matchId)) {
1033
+ if (this.isServer) {
1034
+ const head = await executeHead();
1035
+ updateMatch(matchId, (prev) => ({
1036
+ ...prev,
1037
+ ...head
1038
+ }));
1039
+ (_a2 = this.serverSsr) == null ? void 0 : _a2.onMatchSettled({
1040
+ router: this,
1041
+ match: this.getMatch(matchId)
1042
+ });
1043
+ return this.getMatch(matchId);
1044
+ } else {
1045
+ await potentialPendingMinPromise();
1046
+ }
1047
+ } else if (prevMatch.loaderPromise) {
903
1048
  if (prevMatch.status === "success" && !sync && !prevMatch.preload) {
904
1049
  return this.getMatch(matchId);
905
1050
  }
@@ -910,7 +1055,6 @@ class RouterCore {
910
1055
  }
911
1056
  } else {
912
1057
  const parentMatchPromise = matchPromises[index - 1];
913
- const route = this.looseRoutesById[routeId];
914
1058
  const getLoaderContext = () => {
915
1059
  const {
916
1060
  params,
@@ -943,55 +1087,28 @@ class RouterCore {
943
1087
  loaderPromise: utils.createControlledPromise(),
944
1088
  preload: !!preload && !this.state.matches.find((d) => d.id === matchId)
945
1089
  }));
946
- const executeHead = async () => {
947
- var _a2, _b2, _c2, _d2, _e, _f;
948
- const match = this.getMatch(matchId);
949
- if (!match) {
950
- return;
951
- }
952
- const assetContext = {
953
- matches,
954
- match,
955
- params: match.params,
956
- loaderData: match.loaderData
957
- };
958
- const headFnContent = await ((_b2 = (_a2 = route.options).head) == null ? void 0 : _b2.call(_a2, assetContext));
959
- const meta = headFnContent == null ? void 0 : headFnContent.meta;
960
- const links = headFnContent == null ? void 0 : headFnContent.links;
961
- const headScripts = headFnContent == null ? void 0 : headFnContent.scripts;
962
- const styles = headFnContent == null ? void 0 : headFnContent.styles;
963
- const scripts = await ((_d2 = (_c2 = route.options).scripts) == null ? void 0 : _d2.call(_c2, assetContext));
964
- const headers = await ((_f = (_e = route.options).headers) == null ? void 0 : _f.call(_e, assetContext));
965
- return {
966
- meta,
967
- links,
968
- headScripts,
969
- headers,
970
- scripts,
971
- styles
972
- };
973
- };
974
1090
  const runLoader = async () => {
975
- var _a2, _b2, _c2, _d2, _e;
1091
+ var _a3, _b3, _c2, _d2, _e;
976
1092
  try {
977
- const potentialPendingMinPromise = async () => {
978
- const latestMatch = this.getMatch(matchId);
979
- if (latestMatch.minPendingPromise) {
980
- await latestMatch.minPendingPromise;
981
- }
982
- };
983
1093
  try {
984
- this.loadRouteChunk(route);
1094
+ if (!this.isServer || this.isServer && this.getMatch(matchId).ssr === true) {
1095
+ this.loadRouteChunk(route);
1096
+ }
985
1097
  updateMatch(matchId, (prev) => ({
986
1098
  ...prev,
987
1099
  isFetching: "loader"
988
1100
  }));
989
- const loaderData = await ((_b2 = (_a2 = route.options).loader) == null ? void 0 : _b2.call(_a2, getLoaderContext()));
1101
+ const loaderData = await ((_b3 = (_a3 = route.options).loader) == null ? void 0 : _b3.call(_a3, getLoaderContext()));
990
1102
  handleRedirectAndNotFound(
991
1103
  this.getMatch(matchId),
992
1104
  loaderData
993
1105
  );
1106
+ updateMatch(matchId, (prev) => ({
1107
+ ...prev,
1108
+ loaderData
1109
+ }));
994
1110
  await route._lazyPromise;
1111
+ const head = await executeHead();
995
1112
  await potentialPendingMinPromise();
996
1113
  await route._componentsPromise;
997
1114
  updateMatch(matchId, (prev) => ({
@@ -1000,11 +1117,6 @@ class RouterCore {
1000
1117
  status: "success",
1001
1118
  isFetching: false,
1002
1119
  updatedAt: Date.now(),
1003
- loaderData
1004
- }));
1005
- const head = await executeHead();
1006
- updateMatch(matchId, (prev) => ({
1007
- ...prev,
1008
1120
  ...head
1009
1121
  }));
1010
1122
  } catch (e) {
@@ -1043,10 +1155,10 @@ class RouterCore {
1043
1155
  handleRedirectAndNotFound(this.getMatch(matchId), err);
1044
1156
  }
1045
1157
  };
1046
- const { status, invalid } = this.getMatch(matchId);
1158
+ const { status, invalid, _forcePending } = this.getMatch(matchId);
1047
1159
  loaderShouldRunAsync = status === "success" && (invalid || (shouldReload ?? age > staleAge));
1048
1160
  if (preload && route.options.preload === false) {
1049
- } else if (loaderShouldRunAsync && !sync) {
1161
+ } else if (loaderShouldRunAsync && !sync && !_forcePending) {
1050
1162
  loaderIsRunningAsync = true;
1051
1163
  (async () => {
1052
1164
  try {
@@ -1067,11 +1179,18 @@ class RouterCore {
1067
1179
  } else if (status !== "success" || loaderShouldRunAsync && sync) {
1068
1180
  await runLoader();
1069
1181
  } else {
1182
+ if (_forcePending) {
1183
+ await potentialPendingMinPromise();
1184
+ }
1070
1185
  const head = await executeHead();
1071
1186
  updateMatch(matchId, (prev) => ({
1072
1187
  ...prev,
1073
1188
  ...head
1074
1189
  }));
1190
+ (_b2 = this.serverSsr) == null ? void 0 : _b2.onMatchSettled({
1191
+ router: this,
1192
+ match: this.getMatch(matchId)
1193
+ });
1075
1194
  }
1076
1195
  }
1077
1196
  if (!loaderIsRunningAsync) {
@@ -1083,7 +1202,9 @@ class RouterCore {
1083
1202
  ...prev,
1084
1203
  isFetching: loaderIsRunningAsync ? prev.isFetching : false,
1085
1204
  loaderPromise: loaderIsRunningAsync ? prev.loaderPromise : void 0,
1086
- invalid: false
1205
+ invalid: false,
1206
+ _dehydrated: void 0,
1207
+ _forcePending: void 0
1087
1208
  }));
1088
1209
  return this.getMatch(matchId);
1089
1210
  })()