@tanstack/react-router 0.0.1-beta.219 → 0.0.1-beta.220

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.
@@ -609,6 +609,11 @@
609
609
  });
610
610
  }
611
611
  const useLayoutEffect$1 = typeof window !== 'undefined' ? React__namespace.useLayoutEffect : React__namespace.useEffect;
612
+ function escapeJSON(jsonString) {
613
+ return jsonString.replace(/\\/g, '\\\\') // Escape backslashes
614
+ .replace(/'/g, "\\'") // Escape single quotes
615
+ .replace(/"/g, '\\"'); // Escape double quotes
616
+ }
612
617
 
613
618
  function joinPaths(paths) {
614
619
  return cleanPath(paths.filter(Boolean).join('/'));
@@ -794,173 +799,480 @@
794
799
  return isMatch ? params : undefined;
795
800
  }
796
801
 
797
- // Detect if we're in the DOM
798
-
799
- function redirect(opts) {
800
- opts.isRedirect = true;
801
- return opts;
802
+ function useParams(opts) {
803
+ return useRouterState({
804
+ select: state => {
805
+ const params = last(state.matches)?.params;
806
+ return opts?.select ? opts.select(params) : params;
807
+ }
808
+ });
802
809
  }
803
- function isRedirect(obj) {
804
- return !!obj?.isRedirect;
810
+
811
+ function useSearch(opts) {
812
+ return useMatch({
813
+ ...opts,
814
+ select: match => {
815
+ return opts?.select ? opts.select(match.search) : match.search;
816
+ }
817
+ });
805
818
  }
806
819
 
807
- // @ts-nocheck
820
+ const rootRouteId = '__root__';
808
821
 
809
- // qss has been slightly modified and inlined here for our use cases (and compression's sake). We've included it as a hard dependency for MIT license attribution.
822
+ // The parse type here allows a zod schema to be passed directly to the validator
810
823
 
811
- function encode(obj, pfx) {
812
- var k,
813
- i,
814
- tmp,
815
- str = '';
816
- for (k in obj) {
817
- if ((tmp = obj[k]) !== void 0) {
818
- if (Array.isArray(tmp)) {
819
- for (i = 0; i < tmp.length; i++) {
820
- str && (str += '&');
821
- str += encodeURIComponent(k) + '=' + encodeURIComponent(tmp[i]);
822
- }
823
- } else {
824
- str && (str += '&');
825
- str += encodeURIComponent(k) + '=' + encodeURIComponent(tmp);
826
- }
827
- }
824
+ class Route {
825
+ // Set up in this.init()
826
+
827
+ // customId!: TCustomId
828
+
829
+ // Optional
830
+
831
+ constructor(options) {
832
+ this.options = options || {};
833
+ this.isRoot = !options?.getParentRoute;
834
+ Route.__onInit(this);
828
835
  }
829
- return (pfx || '') + str;
830
- }
831
- function toValue(mix) {
832
- if (!mix) return '';
833
- var str = decodeURIComponent(mix);
834
- if (str === 'false') return false;
835
- if (str === 'true') return true;
836
- return +str * 0 === 0 && +str + '' === str ? +str : str;
837
- }
838
- function decode(str) {
839
- var tmp,
840
- k,
841
- out = {},
842
- arr = str.split('&');
843
- while (tmp = arr.shift()) {
844
- tmp = tmp.split('=');
845
- k = tmp.shift();
846
- if (out[k] !== void 0) {
847
- out[k] = [].concat(out[k], toValue(tmp.shift()));
836
+ init = opts => {
837
+ this.originalIndex = opts.originalIndex;
838
+ const options = this.options;
839
+ const isRoot = !options?.path && !options?.id;
840
+ this.parentRoute = this.options?.getParentRoute?.();
841
+ if (isRoot) {
842
+ this.path = rootRouteId;
848
843
  } else {
849
- out[k] = toValue(tmp.shift());
844
+ invariant(this.parentRoute, `Child Route instances must pass a 'getParentRoute: () => ParentRoute' option that returns a Route instance.`);
850
845
  }
851
- }
852
- return out;
853
- }
846
+ let path = isRoot ? rootRouteId : options.path;
854
847
 
855
- const defaultParseSearch = parseSearchWith(JSON.parse);
856
- const defaultStringifySearch = stringifySearchWith(JSON.stringify, JSON.parse);
857
- function parseSearchWith(parser) {
858
- return searchStr => {
859
- if (searchStr.substring(0, 1) === '?') {
860
- searchStr = searchStr.substring(1);
848
+ // If the path is anything other than an index path, trim it up
849
+ if (path && path !== '/') {
850
+ path = trimPath(path);
861
851
  }
862
- let query = decode(searchStr);
852
+ const customId = options?.id || path;
863
853
 
864
- // Try to parse any query params that might be json
865
- for (let key in query) {
866
- const value = query[key];
867
- if (typeof value === 'string') {
868
- try {
869
- query[key] = parser(value);
870
- } catch (err) {
871
- //
872
- }
873
- }
854
+ // Strip the parentId prefix from the first level of children
855
+ let id = isRoot ? rootRouteId : joinPaths([this.parentRoute.id === rootRouteId ? '' : this.parentRoute.id, customId]);
856
+ if (path === rootRouteId) {
857
+ path = '/';
874
858
  }
875
- return query;
859
+ if (id !== rootRouteId) {
860
+ id = joinPaths(['/', id]);
861
+ }
862
+ const fullPath = id === rootRouteId ? '/' : joinPaths([this.parentRoute.fullPath, path]);
863
+ this.path = path;
864
+ this.id = id;
865
+ // this.customId = customId as TCustomId
866
+ this.fullPath = fullPath;
867
+ this.to = fullPath;
868
+ };
869
+ addChildren = children => {
870
+ this.children = children;
871
+ return this;
872
+ };
873
+ update = options => {
874
+ Object.assign(this.options, options);
875
+ return this;
876
+ };
877
+ static __onInit = route => {
878
+ // This is a dummy static method that should get
879
+ // replaced by a framework specific implementation if necessary
880
+ };
881
+ useMatch = opts => {
882
+ return useMatch({
883
+ ...opts,
884
+ from: this.id
885
+ });
886
+ };
887
+ useRouteContext = opts => {
888
+ return useMatch({
889
+ ...opts,
890
+ from: this.id,
891
+ select: d => opts?.select ? opts.select(d.context) : d.context
892
+ });
893
+ };
894
+ useSearch = opts => {
895
+ return useSearch({
896
+ ...opts,
897
+ from: this.id
898
+ });
899
+ };
900
+ useParams = opts => {
901
+ return useParams({
902
+ ...opts,
903
+ from: this.id
904
+ });
905
+ };
906
+ useLoaderData = opts => {
907
+ return useLoaderData({
908
+ ...opts,
909
+ from: this.id
910
+ });
876
911
  };
877
912
  }
878
- function stringifySearchWith(stringify, parser) {
879
- function stringifyValue(val) {
880
- if (typeof val === 'object' && val !== null) {
881
- try {
882
- return stringify(val);
883
- } catch (err) {
884
- // silent
885
- }
886
- } else if (typeof val === 'string' && typeof parser === 'function') {
887
- try {
888
- // Check if it's a valid parseable string.
889
- // If it is, then stringify it again.
890
- parser(val);
891
- return stringify(val);
892
- } catch (err) {
893
- // silent
894
- }
895
- }
896
- return val;
897
- }
898
- return search => {
899
- search = {
900
- ...search
901
- };
902
- if (search) {
903
- Object.keys(search).forEach(key => {
904
- const val = search[key];
905
- if (typeof val === 'undefined' || val === undefined) {
906
- delete search[key];
907
- } else {
908
- search[key] = stringifyValue(val);
909
- }
910
- });
911
- }
912
- const searchStr = encode(search).toString();
913
- return searchStr ? `?${searchStr}` : '';
913
+ function rootRouteWithContext() {
914
+ return options => {
915
+ return new RootRoute(options);
914
916
  };
915
917
  }
916
-
917
- //
918
-
919
- //
920
-
921
- const componentTypes = ['component', 'errorComponent', 'pendingComponent'];
922
- class Router {
923
- // dehydratedData?: TDehydrated
924
- // resetNextScroll = false
925
- // tempLocationKey = `${Math.round(Math.random() * 10000000)}`
918
+ class RootRoute extends Route {
926
919
  constructor(options) {
927
- this.options = {
928
- defaultPreloadDelay: 50,
929
- context: undefined,
930
- ...options,
931
- stringifySearch: options?.stringifySearch ?? defaultStringifySearch,
932
- parseSearch: options?.parseSearch ?? defaultParseSearch
933
- };
934
- this.routeTree = this.options.routeTree;
920
+ super(options);
935
921
  }
936
- subscribers = new Set();
937
- subscribe = (eventType, fn) => {
938
- const listener = {
939
- eventType,
940
- fn
941
- };
942
- this.subscribers.add(listener);
943
- return () => {
944
- this.subscribers.delete(listener);
945
- };
946
- };
947
- emit = routerEvent => {
948
- this.subscribers.forEach(listener => {
949
- if (listener.eventType === routerEvent.type) {
950
- listener.fn(routerEvent);
951
- }
952
- });
953
- };
922
+ }
923
+ function createRouteMask(opts) {
924
+ return opts;
925
+ }
954
926
 
955
- // dehydrate = (): DehydratedRouter => {
956
- // return {
957
- // state: {
958
- // dehydratedMatches: state.matches.map((d) =>
959
- // pick(d, ['fetchedAt', 'invalid', 'id', 'status', 'updatedAt']),
960
- // ),
961
- // },
962
- // }
963
- // }
927
+ //
928
+
929
+ function Matches() {
930
+ const {
931
+ routesById,
932
+ state
933
+ } = useRouter();
934
+ const {
935
+ matches
936
+ } = state;
937
+ const locationKey = useRouterState().location.state.key;
938
+ const route = routesById[rootRouteId];
939
+ const errorComponent = React__namespace.useCallback(props => {
940
+ return /*#__PURE__*/React__namespace.createElement(ErrorComponent, {
941
+ ...props,
942
+ useMatch: route.useMatch,
943
+ useRouteContext: route.useRouteContext,
944
+ useSearch: route.useSearch,
945
+ useParams: route.useParams
946
+ });
947
+ }, [route]);
948
+ return /*#__PURE__*/React__namespace.createElement(matchesContext.Provider, {
949
+ value: matches
950
+ }, /*#__PURE__*/React__namespace.createElement(CatchBoundary, {
951
+ resetKey: locationKey,
952
+ errorComponent: errorComponent,
953
+ onCatch: () => {
954
+ warning(false, `Error in router! Consider setting an 'errorComponent' in your RootRoute! 👍`);
955
+ }
956
+ }, matches.length ? /*#__PURE__*/React__namespace.createElement(Match, {
957
+ matches: matches
958
+ }) : null));
959
+ }
960
+ const defaultPending = () => null;
961
+ function SafeFragment(props) {
962
+ return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, props.children);
963
+ }
964
+ function Match({
965
+ matches
966
+ }) {
967
+ const {
968
+ options,
969
+ routesById
970
+ } = useRouter();
971
+ const match = matches[0];
972
+ const routeId = match?.routeId;
973
+ const route = routesById[routeId];
974
+ const locationKey = useRouterState().location.state?.key;
975
+ const PendingComponent = route.options.pendingComponent ?? options.defaultPendingComponent ?? defaultPending;
976
+ const routeErrorComponent = route.options.errorComponent ?? options.defaultErrorComponent ?? ErrorComponent;
977
+ const ResolvedSuspenseBoundary = route.options.wrapInSuspense ? React__namespace.Suspense : SafeFragment;
978
+ const errorComponent = routeErrorComponent ? React__namespace.useCallback(props => {
979
+ return /*#__PURE__*/React__namespace.createElement(routeErrorComponent, {
980
+ ...props,
981
+ useMatch: route.useMatch,
982
+ useRouteContext: route.useRouteContext,
983
+ useSearch: route.useSearch,
984
+ useParams: route.useParams
985
+ });
986
+ }, [route]) : undefined;
987
+ return /*#__PURE__*/React__namespace.createElement(matchesContext.Provider, {
988
+ value: matches
989
+ }, /*#__PURE__*/React__namespace.createElement(ResolvedSuspenseBoundary, {
990
+ fallback: /*#__PURE__*/React__namespace.createElement(PendingComponent, {
991
+ useMatch: route.useMatch,
992
+ useRouteContext: route.useRouteContext,
993
+ useSearch: route.useSearch,
994
+ useParams: route.useParams
995
+ })
996
+ }, errorComponent ? /*#__PURE__*/React__namespace.createElement(CatchBoundary, {
997
+ resetKey: locationKey,
998
+ errorComponent: errorComponent,
999
+ onCatch: () => {
1000
+ warning(false, `Error in route match: ${match.id}`);
1001
+ }
1002
+ }, /*#__PURE__*/React__namespace.createElement(MatchInner, {
1003
+ match: match
1004
+ })) : /*#__PURE__*/React__namespace.createElement(SafeFragment, null, /*#__PURE__*/React__namespace.createElement(MatchInner, {
1005
+ match: match
1006
+ }))));
1007
+ }
1008
+ function MatchInner({
1009
+ match
1010
+ }) {
1011
+ const {
1012
+ options,
1013
+ routesById
1014
+ } = useRouter();
1015
+ const route = routesById[match.routeId];
1016
+ if (match.status === 'error') {
1017
+ throw match.error;
1018
+ }
1019
+ if (match.status === 'pending') {
1020
+ throw match.loadPromise;
1021
+ }
1022
+ if (match.status === 'success') {
1023
+ let comp = route.options.component ?? options.defaultComponent;
1024
+ if (comp) {
1025
+ return /*#__PURE__*/React__namespace.createElement(comp, {
1026
+ useMatch: route.useMatch,
1027
+ useRouteContext: route.useRouteContext,
1028
+ useSearch: route.useSearch,
1029
+ useParams: route.useParams,
1030
+ useLoaderData: route.useLoaderData
1031
+ });
1032
+ }
1033
+ return /*#__PURE__*/React__namespace.createElement(Outlet, null);
1034
+ }
1035
+ invariant(false, 'Idle routeMatch status encountered during rendering! You should never see this. File an issue!');
1036
+ }
1037
+ function Outlet() {
1038
+ const matches = React__namespace.useContext(matchesContext).slice(1);
1039
+ if (!matches[0]) {
1040
+ return null;
1041
+ }
1042
+ return /*#__PURE__*/React__namespace.createElement(Match, {
1043
+ matches: matches
1044
+ });
1045
+ }
1046
+ function useMatchRoute() {
1047
+ const {
1048
+ matchRoute
1049
+ } = useRouter();
1050
+ return React__namespace.useCallback(opts => {
1051
+ const {
1052
+ pending,
1053
+ caseSensitive,
1054
+ ...rest
1055
+ } = opts;
1056
+ return matchRoute(rest, {
1057
+ pending,
1058
+ caseSensitive
1059
+ });
1060
+ }, []);
1061
+ }
1062
+ function MatchRoute(props) {
1063
+ const matchRoute = useMatchRoute();
1064
+ const params = matchRoute(props);
1065
+ if (typeof props.children === 'function') {
1066
+ return props.children(params);
1067
+ }
1068
+ return !!params ? props.children : null;
1069
+ }
1070
+ function useMatch(opts) {
1071
+ const nearestMatch = React__namespace.useContext(matchesContext)[0];
1072
+ const nearestMatchRouteId = nearestMatch?.routeId;
1073
+ const matchRouteId = useRouterState({
1074
+ select: state => {
1075
+ const match = opts?.from ? state.matches.find(d => d.routeId === opts?.from) : state.matches.find(d => d.id === nearestMatch.id);
1076
+ return match.routeId;
1077
+ }
1078
+ });
1079
+ if (opts?.strict ?? true) {
1080
+ invariant(nearestMatchRouteId == matchRouteId, `useMatch("${matchRouteId}") is being called in a component that is meant to render the '${nearestMatchRouteId}' route. Did you mean to 'useMatch("${matchRouteId}", { strict: false })' or 'useRoute("${matchRouteId}")' instead?`);
1081
+ }
1082
+ const matchSelection = useRouterState({
1083
+ select: state => {
1084
+ const match = opts?.from ? state.matches.find(d => d.routeId === opts?.from) : state.matches.find(d => d.id === nearestMatch.id);
1085
+ invariant(match, `Could not find ${opts?.from ? `an active match from "${opts.from}"` : 'a nearest match!'}`);
1086
+ return opts?.select ? opts.select(match) : match;
1087
+ }
1088
+ });
1089
+ return matchSelection;
1090
+ }
1091
+ const matchesContext = /*#__PURE__*/React__namespace.createContext(null);
1092
+ function useMatches(opts) {
1093
+ const contextMatches = React__namespace.useContext(matchesContext);
1094
+ return useRouterState({
1095
+ select: state => {
1096
+ const matches = state.matches.slice(state.matches.findIndex(d => d.id === contextMatches[0]?.id));
1097
+ return opts?.select ? opts.select(matches) : matches;
1098
+ }
1099
+ });
1100
+ }
1101
+ function useLoaderData(opts) {
1102
+ const match = useMatch({
1103
+ ...opts,
1104
+ select: undefined
1105
+ });
1106
+ return typeof opts.select === 'function' ? opts.select(match?.loaderData) : match?.loaderData;
1107
+ }
1108
+
1109
+ // Detect if we're in the DOM
1110
+
1111
+ function redirect(opts) {
1112
+ opts.isRedirect = true;
1113
+ return opts;
1114
+ }
1115
+ function isRedirect(obj) {
1116
+ return !!obj?.isRedirect;
1117
+ }
1118
+
1119
+ // @ts-nocheck
1120
+
1121
+ // qss has been slightly modified and inlined here for our use cases (and compression's sake). We've included it as a hard dependency for MIT license attribution.
1122
+
1123
+ function encode(obj, pfx) {
1124
+ var k,
1125
+ i,
1126
+ tmp,
1127
+ str = '';
1128
+ for (k in obj) {
1129
+ if ((tmp = obj[k]) !== void 0) {
1130
+ if (Array.isArray(tmp)) {
1131
+ for (i = 0; i < tmp.length; i++) {
1132
+ str && (str += '&');
1133
+ str += encodeURIComponent(k) + '=' + encodeURIComponent(tmp[i]);
1134
+ }
1135
+ } else {
1136
+ str && (str += '&');
1137
+ str += encodeURIComponent(k) + '=' + encodeURIComponent(tmp);
1138
+ }
1139
+ }
1140
+ }
1141
+ return (pfx || '') + str;
1142
+ }
1143
+ function toValue(mix) {
1144
+ if (!mix) return '';
1145
+ var str = decodeURIComponent(mix);
1146
+ if (str === 'false') return false;
1147
+ if (str === 'true') return true;
1148
+ return +str * 0 === 0 && +str + '' === str ? +str : str;
1149
+ }
1150
+ function decode(str) {
1151
+ var tmp,
1152
+ k,
1153
+ out = {},
1154
+ arr = str.split('&');
1155
+ while (tmp = arr.shift()) {
1156
+ tmp = tmp.split('=');
1157
+ k = tmp.shift();
1158
+ if (out[k] !== void 0) {
1159
+ out[k] = [].concat(out[k], toValue(tmp.shift()));
1160
+ } else {
1161
+ out[k] = toValue(tmp.shift());
1162
+ }
1163
+ }
1164
+ return out;
1165
+ }
1166
+
1167
+ const defaultParseSearch = parseSearchWith(JSON.parse);
1168
+ const defaultStringifySearch = stringifySearchWith(JSON.stringify, JSON.parse);
1169
+ function parseSearchWith(parser) {
1170
+ return searchStr => {
1171
+ if (searchStr.substring(0, 1) === '?') {
1172
+ searchStr = searchStr.substring(1);
1173
+ }
1174
+ let query = decode(searchStr);
1175
+
1176
+ // Try to parse any query params that might be json
1177
+ for (let key in query) {
1178
+ const value = query[key];
1179
+ if (typeof value === 'string') {
1180
+ try {
1181
+ query[key] = parser(value);
1182
+ } catch (err) {
1183
+ //
1184
+ }
1185
+ }
1186
+ }
1187
+ return query;
1188
+ };
1189
+ }
1190
+ function stringifySearchWith(stringify, parser) {
1191
+ function stringifyValue(val) {
1192
+ if (typeof val === 'object' && val !== null) {
1193
+ try {
1194
+ return stringify(val);
1195
+ } catch (err) {
1196
+ // silent
1197
+ }
1198
+ } else if (typeof val === 'string' && typeof parser === 'function') {
1199
+ try {
1200
+ // Check if it's a valid parseable string.
1201
+ // If it is, then stringify it again.
1202
+ parser(val);
1203
+ return stringify(val);
1204
+ } catch (err) {
1205
+ // silent
1206
+ }
1207
+ }
1208
+ return val;
1209
+ }
1210
+ return search => {
1211
+ search = {
1212
+ ...search
1213
+ };
1214
+ if (search) {
1215
+ Object.keys(search).forEach(key => {
1216
+ const val = search[key];
1217
+ if (typeof val === 'undefined' || val === undefined) {
1218
+ delete search[key];
1219
+ } else {
1220
+ search[key] = stringifyValue(val);
1221
+ }
1222
+ });
1223
+ }
1224
+ const searchStr = encode(search).toString();
1225
+ return searchStr ? `?${searchStr}` : '';
1226
+ };
1227
+ }
1228
+
1229
+ //
1230
+
1231
+ //
1232
+
1233
+ const componentTypes = ['component', 'errorComponent', 'pendingComponent'];
1234
+ class Router {
1235
+ // dehydratedData?: TDehydrated
1236
+ // resetNextScroll = false
1237
+ // tempLocationKey = `${Math.round(Math.random() * 10000000)}`
1238
+ constructor(options) {
1239
+ this.options = {
1240
+ defaultPreloadDelay: 50,
1241
+ context: undefined,
1242
+ ...options,
1243
+ stringifySearch: options?.stringifySearch ?? defaultStringifySearch,
1244
+ parseSearch: options?.parseSearch ?? defaultParseSearch
1245
+ };
1246
+ this.routeTree = this.options.routeTree;
1247
+ }
1248
+ subscribers = new Set();
1249
+ subscribe = (eventType, fn) => {
1250
+ const listener = {
1251
+ eventType,
1252
+ fn
1253
+ };
1254
+ this.subscribers.add(listener);
1255
+ return () => {
1256
+ this.subscribers.delete(listener);
1257
+ };
1258
+ };
1259
+ emit = routerEvent => {
1260
+ this.subscribers.forEach(listener => {
1261
+ if (listener.eventType === routerEvent.type) {
1262
+ listener.fn(routerEvent);
1263
+ }
1264
+ });
1265
+ };
1266
+
1267
+ // dehydrate = (): DehydratedRouter => {
1268
+ // return {
1269
+ // state: {
1270
+ // dehydratedMatches: state.matches.map((d) =>
1271
+ // pick(d, ['fetchedAt', 'invalid', 'id', 'status', 'updatedAt']),
1272
+ // ),
1273
+ // },
1274
+ // }
1275
+ // }
964
1276
 
965
1277
  // hydrate = async (__do_not_use_server_ctx?: HydrationCtx) => {
966
1278
  // let _ctx = __do_not_use_server_ctx
@@ -1009,49 +1321,6 @@
1009
1321
  // })
1010
1322
  // }
1011
1323
 
1012
- // TODO:
1013
- // injectedHtml: (string | (() => Promise<string> | string))[] = []
1014
-
1015
- // TODO:
1016
- // injectHtml = async (html: string | (() => Promise<string> | string)) => {
1017
- // this.injectedHtml.push(html)
1018
- // }
1019
-
1020
- // TODO:
1021
- // dehydrateData = <T>(key: any, getData: T | (() => Promise<T> | T)) => {
1022
- // if (typeof document === 'undefined') {
1023
- // const strKey = typeof key === 'string' ? key : JSON.stringify(key)
1024
-
1025
- // this.injectHtml(async () => {
1026
- // const id = `__TSR_DEHYDRATED__${strKey}`
1027
- // const data =
1028
- // typeof getData === 'function' ? await (getData as any)() : getData
1029
- // return `<script id='${id}' suppressHydrationWarning>window["__TSR_DEHYDRATED__${escapeJSON(
1030
- // strKey,
1031
- // )}"] = ${JSON.stringify(data)}
1032
- // ;(() => {
1033
- // var el = document.getElementById('${id}')
1034
- // el.parentElement.removeChild(el)
1035
- // })()
1036
- // </script>`
1037
- // })
1038
-
1039
- // return () => this.hydrateData<T>(key)
1040
- // }
1041
-
1042
- // return () => undefined
1043
- // }
1044
-
1045
- // hydrateData = <T = unknown>(key: any) => {
1046
- // if (typeof document !== 'undefined') {
1047
- // const strKey = typeof key === 'string' ? key : JSON.stringify(key)
1048
-
1049
- // return window[`__TSR_DEHYDRATED__${strKey}` as any] as T
1050
- // }
1051
-
1052
- // return undefined
1053
- // }
1054
-
1055
1324
  // resolveMatchPromise = (matchId: string, key: string, value: any) => {
1056
1325
  // state.matches
1057
1326
  // .find((d) => d.id === matchId)
@@ -1711,13 +1980,15 @@
1711
1980
 
1712
1981
  // Default to reloading the route all the time
1713
1982
  let shouldReload = true;
1714
- let shouldReloadDeps = typeof route.options.shouldReload === 'function' ? route.options.shouldReload?.(loaderContext) : !!route.options.shouldReload;
1715
- if (typeof shouldReloadDeps === 'object') {
1716
- // compare the deps to see if they've changed
1717
- shouldReload = !deepEqual(shouldReloadDeps, match.shouldReloadDeps);
1718
- match.shouldReloadDeps = shouldReloadDeps;
1719
- } else {
1720
- shouldReload = !!shouldReloadDeps;
1983
+ if (cause !== 'enter') {
1984
+ let shouldReloadDeps = typeof route.options.shouldReload === 'function' ? route.options.shouldReload?.(loaderContext) : !!(route.options.shouldReload ?? true);
1985
+ if (typeof shouldReloadDeps === 'object') {
1986
+ // compare the deps to see if they've changed
1987
+ shouldReload = !deepEqual(shouldReloadDeps, match.shouldReloadDeps);
1988
+ match.shouldReloadDeps = shouldReloadDeps;
1989
+ } else {
1990
+ shouldReload = !!shouldReloadDeps;
1991
+ }
1721
1992
  }
1722
1993
 
1723
1994
  // If the user doesn't want the route to reload, just
@@ -2018,386 +2289,158 @@
2018
2289
  return () => {
2019
2290
  unsub();
2020
2291
  };
2021
- }, [history]);
2022
- React__namespace.useLayoutEffect(() => {
2023
- startReactTransition(() => {
2024
- try {
2025
- load();
2026
- } catch (err) {
2027
- console.error(err);
2028
- }
2029
- });
2030
- }, []);
2031
- const matchRoute = useStableCallback((location, opts) => {
2032
- location = {
2033
- ...location,
2034
- to: location.to ? resolvePathWithBase(location.from || '', location.to) : undefined
2035
- };
2036
- const next = buildLocation(location);
2037
- if (opts?.pending && state.status !== 'pending') {
2038
- return false;
2039
- }
2040
- const baseLocation = opts?.pending ? latestLocationRef.current : state.resolvedLocation;
2041
-
2042
- // const baseLocation = state.resolvedLocation
2043
-
2044
- if (!baseLocation) {
2045
- return false;
2046
- }
2047
- const match = matchPathname(basepath, baseLocation.pathname, {
2048
- ...opts,
2049
- to: next.pathname
2050
- });
2051
- if (!match) {
2052
- return false;
2053
- }
2054
- if (match && (opts?.includeSearch ?? true)) {
2055
- return deepEqual(baseLocation.search, next.search, true) ? match : false;
2056
- }
2057
- return match;
2058
- });
2059
- const routerContextValue = {
2060
- routeTree: router.routeTree,
2061
- navigate,
2062
- buildLink,
2063
- state,
2064
- matchRoute,
2065
- routesById,
2066
- options,
2067
- history,
2068
- load,
2069
- buildLocation,
2070
- subscribe: router.subscribe,
2071
- resetNextScrollRef
2072
- };
2073
- return /*#__PURE__*/React__namespace.createElement(routerContext.Provider, {
2074
- value: routerContextValue
2075
- }, /*#__PURE__*/React__namespace.createElement(Matches, null));
2076
- }
2077
- function getRouteMatch(state, id) {
2078
- return [...state.pendingMatches, ...state.matches].find(d => d.id === id);
2079
- }
2080
- function useRouterState(opts) {
2081
- const {
2082
- state
2083
- } = useRouter();
2084
- // return useStore(router.__store, opts?.select as any)
2085
- return opts?.select ? opts.select(state) : state;
2086
- }
2087
- function useRouter() {
2088
- const resolvedContext = window.__TSR_ROUTER_CONTEXT__ || routerContext;
2089
- const value = React__namespace.useContext(resolvedContext);
2090
- warning(value, 'useRouter must be used inside a <RouterProvider> component!');
2091
- return value;
2092
- }
2093
-
2094
- function Matches() {
2095
- const {
2096
- routesById,
2097
- state
2098
- } = useRouter();
2099
- const {
2100
- matches
2101
- } = state;
2102
- const locationKey = useRouterState().location.state.key;
2103
- const route = routesById[rootRouteId];
2104
- const errorComponent = React__namespace.useCallback(props => {
2105
- return /*#__PURE__*/React__namespace.createElement(ErrorComponent, {
2106
- ...props,
2107
- useMatch: route.useMatch,
2108
- useRouteContext: route.useRouteContext,
2109
- useSearch: route.useSearch,
2110
- useParams: route.useParams
2111
- });
2112
- }, [route]);
2113
- return /*#__PURE__*/React__namespace.createElement(matchesContext.Provider, {
2114
- value: matches
2115
- }, /*#__PURE__*/React__namespace.createElement(CatchBoundary, {
2116
- resetKey: locationKey,
2117
- errorComponent: errorComponent,
2118
- onCatch: () => {
2119
- warning(false, `Error in router! Consider setting an 'errorComponent' in your RootRoute! 👍`);
2120
- }
2121
- }, matches.length ? /*#__PURE__*/React__namespace.createElement(Match, {
2122
- matches: matches
2123
- }) : null));
2124
- }
2125
- const defaultPending = () => null;
2126
- function SafeFragment(props) {
2127
- return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, props.children);
2128
- }
2129
- function Match({
2130
- matches
2131
- }) {
2132
- const {
2133
- options,
2134
- routesById
2135
- } = useRouter();
2136
- const match = matches[0];
2137
- const routeId = match?.routeId;
2138
- const route = routesById[routeId];
2139
- const locationKey = useRouterState().location.state?.key;
2140
- const PendingComponent = route.options.pendingComponent ?? options.defaultPendingComponent ?? defaultPending;
2141
- const routeErrorComponent = route.options.errorComponent ?? options.defaultErrorComponent ?? ErrorComponent;
2142
- const ResolvedSuspenseBoundary = route.options.wrapInSuspense ? React__namespace.Suspense : SafeFragment;
2143
- const errorComponent = routeErrorComponent ? React__namespace.useCallback(props => {
2144
- return /*#__PURE__*/React__namespace.createElement(routeErrorComponent, {
2145
- ...props,
2146
- useMatch: route.useMatch,
2147
- useRouteContext: route.useRouteContext,
2148
- useSearch: route.useSearch,
2149
- useParams: route.useParams
2150
- });
2151
- }, [route]) : undefined;
2152
- return /*#__PURE__*/React__namespace.createElement(matchesContext.Provider, {
2153
- value: matches
2154
- }, /*#__PURE__*/React__namespace.createElement(ResolvedSuspenseBoundary, {
2155
- fallback: /*#__PURE__*/React__namespace.createElement(PendingComponent, {
2156
- useMatch: route.useMatch,
2157
- useRouteContext: route.useRouteContext,
2158
- useSearch: route.useSearch,
2159
- useParams: route.useParams
2160
- })
2161
- }, errorComponent ? /*#__PURE__*/React__namespace.createElement(CatchBoundary, {
2162
- resetKey: locationKey,
2163
- errorComponent: errorComponent,
2164
- onCatch: () => {
2165
- warning(false, `Error in route match: ${match.id}`);
2166
- }
2167
- }, /*#__PURE__*/React__namespace.createElement(MatchInner, {
2168
- match: match
2169
- })) : /*#__PURE__*/React__namespace.createElement(SafeFragment, null, /*#__PURE__*/React__namespace.createElement(MatchInner, {
2170
- match: match
2171
- }))));
2172
- }
2173
- function MatchInner({
2174
- match
2175
- }) {
2176
- const {
2177
- options,
2178
- routesById
2179
- } = useRouter();
2180
- const route = routesById[match.routeId];
2181
- if (match.status === 'error') {
2182
- throw match.error;
2183
- }
2184
- if (match.status === 'pending') {
2185
- throw match.loadPromise;
2186
- }
2187
- if (match.status === 'success') {
2188
- let comp = route.options.component ?? options.defaultComponent;
2189
- if (comp) {
2190
- return /*#__PURE__*/React__namespace.createElement(comp, {
2191
- useMatch: route.useMatch,
2192
- useRouteContext: route.useRouteContext,
2193
- useSearch: route.useSearch,
2194
- useParams: route.useParams,
2195
- useLoaderData: route.useLoaderData
2196
- });
2292
+ }, [history]);
2293
+ const matchRoute = useStableCallback((location, opts) => {
2294
+ location = {
2295
+ ...location,
2296
+ to: location.to ? resolvePathWithBase(location.from || '', location.to) : undefined
2297
+ };
2298
+ const next = buildLocation(location);
2299
+ if (opts?.pending && state.status !== 'pending') {
2300
+ return false;
2197
2301
  }
2198
- return /*#__PURE__*/React__namespace.createElement(Outlet, null);
2199
- }
2200
- invariant(false, 'Idle routeMatch status encountered during rendering! You should never see this. File an issue!');
2201
- }
2202
- function Outlet() {
2203
- const matches = React__namespace.useContext(matchesContext).slice(1);
2204
- if (!matches[0]) {
2205
- return null;
2206
- }
2207
- return /*#__PURE__*/React__namespace.createElement(Match, {
2208
- matches: matches
2209
- });
2210
- }
2211
- function useMatchRoute() {
2212
- const {
2213
- matchRoute
2214
- } = useRouter();
2215
- return React__namespace.useCallback(opts => {
2216
- const {
2217
- pending,
2218
- caseSensitive,
2219
- ...rest
2220
- } = opts;
2221
- return matchRoute(rest, {
2222
- pending,
2223
- caseSensitive
2302
+ const baseLocation = opts?.pending ? latestLocationRef.current : state.resolvedLocation;
2303
+
2304
+ // const baseLocation = state.resolvedLocation
2305
+
2306
+ if (!baseLocation) {
2307
+ return false;
2308
+ }
2309
+ const match = matchPathname(basepath, baseLocation.pathname, {
2310
+ ...opts,
2311
+ to: next.pathname
2224
2312
  });
2225
- }, []);
2226
- }
2227
- function MatchRoute(props) {
2228
- const matchRoute = useMatchRoute();
2229
- const params = matchRoute(props);
2230
- if (typeof props.children === 'function') {
2231
- return props.children(params);
2232
- }
2233
- return !!params ? props.children : null;
2234
- }
2235
- function useMatch(opts) {
2236
- const nearestMatch = React__namespace.useContext(matchesContext)[0];
2237
- const nearestMatchRouteId = nearestMatch?.routeId;
2238
- const matchRouteId = useRouterState({
2239
- select: state => {
2240
- const match = opts?.from ? state.matches.find(d => d.routeId === opts?.from) : state.matches.find(d => d.id === nearestMatch.id);
2241
- return match.routeId;
2313
+ if (!match) {
2314
+ return false;
2242
2315
  }
2316
+ if (match && (opts?.includeSearch ?? true)) {
2317
+ return deepEqual(baseLocation.search, next.search, true) ? match : false;
2318
+ }
2319
+ return match;
2243
2320
  });
2244
- if (opts?.strict ?? true) {
2245
- invariant(nearestMatchRouteId == matchRouteId, `useMatch("${matchRouteId}") is being called in a component that is meant to render the '${nearestMatchRouteId}' route. Did you mean to 'useMatch("${matchRouteId}", { strict: false })' or 'useRoute("${matchRouteId}")' instead?`);
2246
- }
2247
- const matchSelection = useRouterState({
2248
- select: state => {
2249
- const match = opts?.from ? state.matches.find(d => d.routeId === opts?.from) : state.matches.find(d => d.id === nearestMatch.id);
2250
- invariant(match, `Could not find ${opts?.from ? `an active match from "${opts.from}"` : 'a nearest match!'}`);
2251
- return opts?.select ? opts.select(match) : match;
2321
+ const injectedHtmlRef = React__namespace.useRef([]);
2322
+ const injectHtml = useStableCallback(async html => {
2323
+ injectedHtmlRef.current.push(html);
2324
+ });
2325
+ const dehydrateData = useStableCallback((key, getData) => {
2326
+ if (typeof document === 'undefined') {
2327
+ const strKey = typeof key === 'string' ? key : JSON.stringify(key);
2328
+ injectHtml(async () => {
2329
+ const id = `__TSR_DEHYDRATED__${strKey}`;
2330
+ const data = typeof getData === 'function' ? await getData() : getData;
2331
+ return `<script id='${id}' suppressHydrationWarning>window["__TSR_DEHYDRATED__${escapeJSON(strKey)}"] = ${JSON.stringify(data)}
2332
+ ;(() => {
2333
+ var el = document.getElementById('${id}')
2334
+ el.parentElement.removeChild(el)
2335
+ })()
2336
+ </script>`;
2337
+ });
2338
+ return () => hydrateData(key);
2252
2339
  }
2340
+ return () => undefined;
2253
2341
  });
2254
- return matchSelection;
2255
- }
2256
- const matchesContext = /*#__PURE__*/React__namespace.createContext(null);
2257
- function useMatches(opts) {
2258
- const contextMatches = React__namespace.useContext(matchesContext);
2259
- return useRouterState({
2260
- select: state => {
2261
- const matches = state.matches.slice(state.matches.findIndex(d => d.id === contextMatches[0]?.id));
2262
- return opts?.select ? opts.select(matches) : matches;
2342
+ const hydrateData = useStableCallback(key => {
2343
+ if (typeof document !== 'undefined') {
2344
+ const strKey = typeof key === 'string' ? key : JSON.stringify(key);
2345
+ return window[`__TSR_DEHYDRATED__${strKey}`];
2263
2346
  }
2347
+ return undefined;
2264
2348
  });
2349
+ React__namespace.useLayoutEffect(() => {
2350
+ startReactTransition(() => {
2351
+ try {
2352
+ load();
2353
+ } catch (err) {
2354
+ console.error(err);
2355
+ }
2356
+ });
2357
+ }, []);
2358
+ const routerContextValue = {
2359
+ routeTree: router.routeTree,
2360
+ navigate,
2361
+ buildLink,
2362
+ state,
2363
+ matchRoute,
2364
+ routesById,
2365
+ options,
2366
+ history,
2367
+ load,
2368
+ buildLocation,
2369
+ subscribe: router.subscribe,
2370
+ resetNextScrollRef,
2371
+ injectedHtmlRef,
2372
+ injectHtml,
2373
+ dehydrateData,
2374
+ hydrateData
2375
+ };
2376
+ return /*#__PURE__*/React__namespace.createElement(routerContext.Provider, {
2377
+ value: routerContextValue
2378
+ }, /*#__PURE__*/React__namespace.createElement(Matches, null));
2265
2379
  }
2266
- function useLoaderData(opts) {
2267
- const match = useMatch({
2268
- ...opts,
2269
- select: undefined
2270
- });
2271
- return typeof opts.select === 'function' ? opts.select(match?.loaderData) : match?.loaderData;
2380
+ function getRouteMatch(state, id) {
2381
+ return [...state.pendingMatches, ...state.matches].find(d => d.id === id);
2272
2382
  }
2273
-
2274
- function useParams(opts) {
2275
- return useRouterState({
2276
- select: state => {
2277
- const params = last(state.matches)?.params;
2278
- return opts?.select ? opts.select(params) : params;
2279
- }
2280
- });
2383
+ function useRouterState(opts) {
2384
+ const {
2385
+ state
2386
+ } = useRouter();
2387
+ // return useStore(router.__store, opts?.select as any)
2388
+ return opts?.select ? opts.select(state) : state;
2281
2389
  }
2282
-
2283
- function useSearch(opts) {
2284
- return useMatch({
2285
- ...opts,
2286
- select: match => {
2287
- return opts?.select ? opts.select(match.search) : match.search;
2288
- }
2289
- });
2390
+ function useRouter() {
2391
+ const resolvedContext = window.__TSR_ROUTER_CONTEXT__ || routerContext;
2392
+ const value = React__namespace.useContext(resolvedContext);
2393
+ warning(value, 'useRouter must be used inside a <RouterProvider> component!');
2394
+ return value;
2290
2395
  }
2291
2396
 
2292
- const rootRouteId = '__root__';
2293
-
2294
- // The parse type here allows a zod schema to be passed directly to the validator
2295
-
2296
- class Route {
2297
- // Set up in this.init()
2298
-
2299
- // customId!: TCustomId
2300
-
2301
- // Optional
2302
-
2303
- constructor(options) {
2304
- this.options = options || {};
2305
- this.isRoot = !options?.getParentRoute;
2306
- Route.__onInit(this);
2307
- }
2308
- init = opts => {
2309
- this.originalIndex = opts.originalIndex;
2310
- const options = this.options;
2311
- const isRoot = !options?.path && !options?.id;
2312
- this.parentRoute = this.options?.getParentRoute?.();
2313
- if (isRoot) {
2314
- this.path = rootRouteId;
2315
- } else {
2316
- invariant(this.parentRoute, `Child Route instances must pass a 'getParentRoute: () => ParentRoute' option that returns a Route instance.`);
2317
- }
2318
- let path = isRoot ? rootRouteId : options.path;
2319
-
2320
- // If the path is anything other than an index path, trim it up
2321
- if (path && path !== '/') {
2322
- path = trimPath(path);
2323
- }
2324
- const customId = options?.id || path;
2325
-
2326
- // Strip the parentId prefix from the first level of children
2327
- let id = isRoot ? rootRouteId : joinPaths([this.parentRoute.id === rootRouteId ? '' : this.parentRoute.id, customId]);
2328
- if (path === rootRouteId) {
2329
- path = '/';
2330
- }
2331
- if (id !== rootRouteId) {
2332
- id = joinPaths(['/', id]);
2333
- }
2334
- const fullPath = id === rootRouteId ? '/' : joinPaths([this.parentRoute.fullPath, path]);
2335
- this.path = path;
2336
- this.id = id;
2337
- // this.customId = customId as TCustomId
2338
- this.fullPath = fullPath;
2339
- this.to = fullPath;
2340
- };
2341
- addChildren = children => {
2342
- this.children = children;
2343
- return this;
2344
- };
2345
- update = options => {
2346
- Object.assign(this.options, options);
2347
- return this;
2348
- };
2349
- static __onInit = route => {
2350
- // This is a dummy static method that should get
2351
- // replaced by a framework specific implementation if necessary
2352
- };
2353
- useMatch = opts => {
2354
- return useMatch({
2355
- ...opts,
2356
- from: this.id
2357
- });
2358
- };
2359
- useRouteContext = opts => {
2360
- return useMatch({
2361
- ...opts,
2362
- from: this.id,
2363
- select: d => opts?.select ? opts.select(d.context) : d.context
2364
- });
2365
- };
2366
- useSearch = opts => {
2367
- return useSearch({
2368
- ...opts,
2369
- from: this.id
2370
- });
2371
- };
2372
- useParams = opts => {
2373
- return useParams({
2374
- ...opts,
2375
- from: this.id
2376
- });
2377
- };
2378
- useLoaderData = opts => {
2379
- return useLoaderData({
2380
- ...opts,
2381
- from: this.id
2397
+ function defer(_promise) {
2398
+ const promise = _promise;
2399
+ if (!promise.__deferredState) {
2400
+ promise.__deferredState = {
2401
+ uid: Math.random().toString(36).slice(2),
2402
+ status: 'pending'
2403
+ };
2404
+ const state = promise.__deferredState;
2405
+ promise.then(data => {
2406
+ state.status = 'success';
2407
+ state.data = data;
2408
+ }).catch(error => {
2409
+ state.status = 'error';
2410
+ state.error = error;
2382
2411
  });
2383
- };
2412
+ }
2413
+ return promise;
2384
2414
  }
2385
- function rootRouteWithContext() {
2386
- return options => {
2387
- return new RootRoute(options);
2388
- };
2415
+ function isDehydratedDeferred(obj) {
2416
+ return typeof obj === 'object' && obj !== null && !(obj instanceof Promise) && !obj.then && '__deferredState' in obj;
2389
2417
  }
2390
- class RootRoute extends Route {
2391
- constructor(options) {
2392
- super(options);
2418
+
2419
+ function useAwaited({
2420
+ promise
2421
+ }) {
2422
+ const router = useRouter();
2423
+ let state = promise.__deferredState;
2424
+ const key = `__TSR__DEFERRED__${state.uid}`;
2425
+ if (isDehydratedDeferred(promise)) {
2426
+ state = router.hydrateData(key);
2427
+ promise = Promise.resolve(state.data);
2428
+ promise.__deferredState = state;
2429
+ }
2430
+ if (state.status === 'pending') {
2431
+ throw promise;
2432
+ }
2433
+ if (state.status === 'error') {
2434
+ throw state.error;
2393
2435
  }
2436
+ router.dehydrateData(key, state);
2437
+ return [state.data];
2394
2438
  }
2395
- function createRouteMask(opts) {
2396
- return opts;
2439
+ function Await(props) {
2440
+ const awaited = useAwaited(props);
2441
+ return props.children(...awaited);
2397
2442
  }
2398
2443
 
2399
- //
2400
-
2401
2444
  class FileRoute {
2402
2445
  constructor(path) {
2403
2446
  this.path = path;
@@ -2755,6 +2798,7 @@
2755
2798
  return null;
2756
2799
  }
2757
2800
 
2801
+ exports.Await = Await;
2758
2802
  exports.Block = Block;
2759
2803
  exports.CatchBoundary = CatchBoundary;
2760
2804
  exports.CatchBoundaryImpl = CatchBoundaryImpl;
@@ -2783,12 +2827,15 @@
2783
2827
  exports.deepEqual = deepEqual;
2784
2828
  exports.defaultParseSearch = defaultParseSearch;
2785
2829
  exports.defaultStringifySearch = defaultStringifySearch;
2830
+ exports.defer = defer;
2786
2831
  exports.encode = encode;
2832
+ exports.escapeJSON = escapeJSON;
2787
2833
  exports.functionalUpdate = functionalUpdate;
2788
2834
  exports.getInitialRouterState = getInitialRouterState;
2789
2835
  exports.getRouteMatch = getRouteMatch;
2790
2836
  exports.interpolatePath = interpolatePath;
2791
2837
  exports.invariant = invariant;
2838
+ exports.isDehydratedDeferred = isDehydratedDeferred;
2792
2839
  exports.isPlainObject = isPlainObject;
2793
2840
  exports.isRedirect = isRedirect;
2794
2841
  exports.isServer = isServer;
@@ -2814,6 +2861,7 @@
2814
2861
  exports.trimPathLeft = trimPathLeft;
2815
2862
  exports.trimPathRight = trimPathRight;
2816
2863
  exports.typedNavigate = typedNavigate;
2864
+ exports.useAwaited = useAwaited;
2817
2865
  exports.useBlocker = useBlocker;
2818
2866
  exports.useLayoutEffect = useLayoutEffect$1;
2819
2867
  exports.useLinkProps = useLinkProps;