@tanstack/router-core 1.124.0 → 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 +178 -56
  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 +178 -56
  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 +225 -66
  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
  }
@@ -232,9 +230,10 @@ class RouterCore {
232
230
  });
233
231
  const lastMatch = utils.last(allFromMatches);
234
232
  let fromPath = lastMatch.fullPath;
233
+ const routeIsChanging = !!dest.to && dest.to !== fromPath && this.resolvePathWithBase(fromPath, `${dest.to}`) !== fromPath;
235
234
  if (dest.unsafeRelative === "path") {
236
235
  fromPath = currentLocation.pathname;
237
- } else if (dest.to && dest.from) {
236
+ } else if (routeIsChanging && dest.from) {
238
237
  fromPath = dest.from;
239
238
  const existingFrom = [...allFromMatches].reverse().find((d) => {
240
239
  return d.fullPath === fromPath || d.fullPath === path.joinPaths([fromPath, "/"]);
@@ -497,7 +496,10 @@ class RouterCore {
497
496
  throw redirect.redirect({ href: nextLocation.href });
498
497
  }
499
498
  }
500
- const pendingMatches = this.matchRoutes(this.latestLocation);
499
+ let pendingMatches = this.matchRoutes(this.latestLocation);
500
+ if (this.isShell) {
501
+ pendingMatches = pendingMatches.slice(0, 1);
502
+ }
501
503
  this.__store.setState((s) => ({
502
504
  ...s,
503
505
  status: "pending",
@@ -692,12 +694,41 @@ class RouterCore {
692
694
  const triggerOnReady = async () => {
693
695
  if (!rendered) {
694
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
+ }
695
723
  await (onReady == null ? void 0 : onReady());
696
724
  }
697
725
  };
698
726
  const resolvePreload = (matchId) => {
699
727
  return !!(allPreload && !this.state.matches.find((d) => d.id === matchId));
700
728
  };
729
+ if (!this.isServer && this.state.matches.find((d) => d._forcePending)) {
730
+ triggerOnReady();
731
+ }
701
732
  const handleRedirectAndNotFound = (match, err) => {
702
733
  var _a, _b, _c, _d;
703
734
  if (redirect.isRedirect(err) || notFound.isNotFound(err)) {
@@ -740,6 +771,18 @@ class RouterCore {
740
771
  }
741
772
  }
742
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
+ };
743
786
  try {
744
787
  await new Promise((resolveAll, rejectAll) => {
745
788
  ;
@@ -780,8 +823,60 @@ class RouterCore {
780
823
  for (const [index, { id: matchId, routeId }] of matches.entries()) {
781
824
  const existingMatch = this.getMatch(matchId);
782
825
  const parentMatchId = (_a = matches[index - 1]) == null ? void 0 : _a.id;
826
+ const parentMatch = parentMatchId ? this.getMatch(parentMatchId) : void 0;
783
827
  const route = this.looseRoutesById[routeId];
784
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
+ }
785
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)));
786
881
  let executeBeforeLoad = true;
787
882
  if (
@@ -834,7 +929,7 @@ class RouterCore {
834
929
  if (searchError) {
835
930
  handleSerialError(index, searchError, "VALIDATE_SEARCH");
836
931
  }
837
- const getParentMatchContext = () => parentMatchId ? this.getMatch(parentMatchId).context : this.options.context ?? {};
932
+ const parentMatchContext = (parentMatch == null ? void 0 : parentMatch.context) ?? this.options.context ?? {};
838
933
  updateMatch(matchId, (prev) => ({
839
934
  ...prev,
840
935
  isFetching: "beforeLoad",
@@ -842,7 +937,7 @@ class RouterCore {
842
937
  abortController,
843
938
  pendingTimeout,
844
939
  context: {
845
- ...getParentMatchContext(),
940
+ ...parentMatchContext,
846
941
  ...prev.__routeContext
847
942
  }
848
943
  }));
@@ -869,7 +964,7 @@ class RouterCore {
869
964
  ...prev,
870
965
  __beforeLoadContext: beforeLoadContext,
871
966
  context: {
872
- ...getParentMatchContext(),
967
+ ...parentMatchContext,
873
968
  ...prev.__routeContext,
874
969
  ...beforeLoadContext
875
970
  },
@@ -895,10 +990,61 @@ class RouterCore {
895
990
  validResolvedMatches.forEach(({ id: matchId, routeId }, index) => {
896
991
  matchPromises.push(
897
992
  (async () => {
993
+ var _a2, _b2;
898
994
  let loaderShouldRunAsync = false;
899
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
+ };
900
1031
  const prevMatch = this.getMatch(matchId);
901
- 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) {
902
1048
  if (prevMatch.status === "success" && !sync && !prevMatch.preload) {
903
1049
  return this.getMatch(matchId);
904
1050
  }
@@ -909,7 +1055,6 @@ class RouterCore {
909
1055
  }
910
1056
  } else {
911
1057
  const parentMatchPromise = matchPromises[index - 1];
912
- const route = this.looseRoutesById[routeId];
913
1058
  const getLoaderContext = () => {
914
1059
  const {
915
1060
  params,
@@ -942,55 +1087,28 @@ class RouterCore {
942
1087
  loaderPromise: utils.createControlledPromise(),
943
1088
  preload: !!preload && !this.state.matches.find((d) => d.id === matchId)
944
1089
  }));
945
- const executeHead = async () => {
946
- var _a2, _b2, _c2, _d2, _e, _f;
947
- const match = this.getMatch(matchId);
948
- if (!match) {
949
- return;
950
- }
951
- const assetContext = {
952
- matches,
953
- match,
954
- params: match.params,
955
- loaderData: match.loaderData
956
- };
957
- const headFnContent = await ((_b2 = (_a2 = route.options).head) == null ? void 0 : _b2.call(_a2, assetContext));
958
- const meta = headFnContent == null ? void 0 : headFnContent.meta;
959
- const links = headFnContent == null ? void 0 : headFnContent.links;
960
- const headScripts = headFnContent == null ? void 0 : headFnContent.scripts;
961
- const styles = headFnContent == null ? void 0 : headFnContent.styles;
962
- const scripts = await ((_d2 = (_c2 = route.options).scripts) == null ? void 0 : _d2.call(_c2, assetContext));
963
- const headers = await ((_f = (_e = route.options).headers) == null ? void 0 : _f.call(_e, assetContext));
964
- return {
965
- meta,
966
- links,
967
- headScripts,
968
- headers,
969
- scripts,
970
- styles
971
- };
972
- };
973
1090
  const runLoader = async () => {
974
- var _a2, _b2, _c2, _d2, _e;
1091
+ var _a3, _b3, _c2, _d2, _e;
975
1092
  try {
976
- const potentialPendingMinPromise = async () => {
977
- const latestMatch = this.getMatch(matchId);
978
- if (latestMatch.minPendingPromise) {
979
- await latestMatch.minPendingPromise;
980
- }
981
- };
982
1093
  try {
983
- this.loadRouteChunk(route);
1094
+ if (!this.isServer || this.isServer && this.getMatch(matchId).ssr === true) {
1095
+ this.loadRouteChunk(route);
1096
+ }
984
1097
  updateMatch(matchId, (prev) => ({
985
1098
  ...prev,
986
1099
  isFetching: "loader"
987
1100
  }));
988
- 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()));
989
1102
  handleRedirectAndNotFound(
990
1103
  this.getMatch(matchId),
991
1104
  loaderData
992
1105
  );
1106
+ updateMatch(matchId, (prev) => ({
1107
+ ...prev,
1108
+ loaderData
1109
+ }));
993
1110
  await route._lazyPromise;
1111
+ const head = await executeHead();
994
1112
  await potentialPendingMinPromise();
995
1113
  await route._componentsPromise;
996
1114
  updateMatch(matchId, (prev) => ({
@@ -999,11 +1117,6 @@ class RouterCore {
999
1117
  status: "success",
1000
1118
  isFetching: false,
1001
1119
  updatedAt: Date.now(),
1002
- loaderData
1003
- }));
1004
- const head = await executeHead();
1005
- updateMatch(matchId, (prev) => ({
1006
- ...prev,
1007
1120
  ...head
1008
1121
  }));
1009
1122
  } catch (e) {
@@ -1042,10 +1155,10 @@ class RouterCore {
1042
1155
  handleRedirectAndNotFound(this.getMatch(matchId), err);
1043
1156
  }
1044
1157
  };
1045
- const { status, invalid } = this.getMatch(matchId);
1158
+ const { status, invalid, _forcePending } = this.getMatch(matchId);
1046
1159
  loaderShouldRunAsync = status === "success" && (invalid || (shouldReload ?? age > staleAge));
1047
1160
  if (preload && route.options.preload === false) {
1048
- } else if (loaderShouldRunAsync && !sync) {
1161
+ } else if (loaderShouldRunAsync && !sync && !_forcePending) {
1049
1162
  loaderIsRunningAsync = true;
1050
1163
  (async () => {
1051
1164
  try {
@@ -1066,11 +1179,18 @@ class RouterCore {
1066
1179
  } else if (status !== "success" || loaderShouldRunAsync && sync) {
1067
1180
  await runLoader();
1068
1181
  } else {
1182
+ if (_forcePending) {
1183
+ await potentialPendingMinPromise();
1184
+ }
1069
1185
  const head = await executeHead();
1070
1186
  updateMatch(matchId, (prev) => ({
1071
1187
  ...prev,
1072
1188
  ...head
1073
1189
  }));
1190
+ (_b2 = this.serverSsr) == null ? void 0 : _b2.onMatchSettled({
1191
+ router: this,
1192
+ match: this.getMatch(matchId)
1193
+ });
1074
1194
  }
1075
1195
  }
1076
1196
  if (!loaderIsRunningAsync) {
@@ -1082,7 +1202,9 @@ class RouterCore {
1082
1202
  ...prev,
1083
1203
  isFetching: loaderIsRunningAsync ? prev.isFetching : false,
1084
1204
  loaderPromise: loaderIsRunningAsync ? prev.loaderPromise : void 0,
1085
- invalid: false
1205
+ invalid: false,
1206
+ _dehydrated: void 0,
1207
+ _forcePending: void 0
1086
1208
  }));
1087
1209
  return this.getMatch(matchId);
1088
1210
  })()