@tanstack/react-router 0.0.1-beta.222 → 0.0.1-beta.224

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.
@@ -779,274 +779,34 @@ function useLoaderData(opts) {
779
779
  return typeof opts.select === 'function' ? opts.select(match?.loaderData) : match?.loaderData;
780
780
  }
781
781
 
782
- // Detect if we're in the DOM
783
-
784
- function redirect(opts) {
785
- opts.isRedirect = true;
786
- return opts;
787
- }
788
- function isRedirect(obj) {
789
- return !!obj?.isRedirect;
790
- }
791
-
792
- // @ts-nocheck
793
-
794
- // 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.
795
-
796
- function encode(obj, pfx) {
797
- var k,
798
- i,
799
- tmp,
800
- str = '';
801
- for (k in obj) {
802
- if ((tmp = obj[k]) !== void 0) {
803
- if (Array.isArray(tmp)) {
804
- for (i = 0; i < tmp.length; i++) {
805
- str && (str += '&');
806
- str += encodeURIComponent(k) + '=' + encodeURIComponent(tmp[i]);
807
- }
808
- } else {
809
- str && (str += '&');
810
- str += encodeURIComponent(k) + '=' + encodeURIComponent(tmp);
811
- }
812
- }
813
- }
814
- return (pfx || '') + str;
815
- }
816
- function toValue(mix) {
817
- if (!mix) return '';
818
- var str = decodeURIComponent(mix);
819
- if (str === 'false') return false;
820
- if (str === 'true') return true;
821
- return +str * 0 === 0 && +str + '' === str ? +str : str;
822
- }
823
- function decode(str) {
824
- var tmp,
825
- k,
826
- out = {},
827
- arr = str.split('&');
828
- while (tmp = arr.shift()) {
829
- tmp = tmp.split('=');
830
- k = tmp.shift();
831
- if (out[k] !== void 0) {
832
- out[k] = [].concat(out[k], toValue(tmp.shift()));
833
- } else {
834
- out[k] = toValue(tmp.shift());
835
- }
836
- }
837
- return out;
838
- }
839
-
840
- const defaultParseSearch = parseSearchWith(JSON.parse);
841
- const defaultStringifySearch = stringifySearchWith(JSON.stringify, JSON.parse);
842
- function parseSearchWith(parser) {
843
- return searchStr => {
844
- if (searchStr.substring(0, 1) === '?') {
845
- searchStr = searchStr.substring(1);
846
- }
847
- let query = decode(searchStr);
848
-
849
- // Try to parse any query params that might be json
850
- for (let key in query) {
851
- const value = query[key];
852
- if (typeof value === 'string') {
853
- try {
854
- query[key] = parser(value);
855
- } catch (err) {
856
- //
857
- }
858
- }
859
- }
860
- return query;
861
- };
862
- }
863
- function stringifySearchWith(stringify, parser) {
864
- function stringifyValue(val) {
865
- if (typeof val === 'object' && val !== null) {
866
- try {
867
- return stringify(val);
868
- } catch (err) {
869
- // silent
870
- }
871
- } else if (typeof val === 'string' && typeof parser === 'function') {
872
- try {
873
- // Check if it's a valid parseable string.
874
- // If it is, then stringify it again.
875
- parser(val);
876
- return stringify(val);
877
- } catch (err) {
878
- // silent
879
- }
880
- }
881
- return val;
882
- }
883
- return search => {
884
- search = {
885
- ...search
886
- };
887
- if (search) {
888
- Object.keys(search).forEach(key => {
889
- const val = search[key];
890
- if (typeof val === 'undefined' || val === undefined) {
891
- delete search[key];
892
- } else {
893
- search[key] = stringifyValue(val);
894
- }
895
- });
896
- }
897
- const searchStr = encode(search).toString();
898
- return searchStr ? `?${searchStr}` : '';
899
- };
900
- }
901
-
902
- //
903
-
904
- //
905
-
906
- const componentTypes = ['component', 'errorComponent', 'pendingComponent'];
907
- class Router {
908
- // dehydratedData?: TDehydrated
909
- // resetNextScroll = false
910
- // tempLocationKey = `${Math.round(Math.random() * 10000000)}`
911
- constructor(options) {
912
- this.options = {
913
- defaultPreloadDelay: 50,
914
- context: undefined,
915
- ...options,
916
- stringifySearch: options?.stringifySearch ?? defaultStringifySearch,
917
- parseSearch: options?.parseSearch ?? defaultParseSearch
918
- };
919
- this.routeTree = this.options.routeTree;
920
- }
921
- subscribers = new Set();
922
- subscribe = (eventType, fn) => {
923
- const listener = {
924
- eventType,
925
- fn
926
- };
927
- this.subscribers.add(listener);
928
- return () => {
929
- this.subscribers.delete(listener);
930
- };
931
- };
932
- emit = routerEvent => {
933
- this.subscribers.forEach(listener => {
934
- if (listener.eventType === routerEvent.type) {
935
- listener.fn(routerEvent);
936
- }
937
- });
938
- };
939
-
940
- // dehydrate = (): DehydratedRouter => {
941
- // return {
942
- // state: {
943
- // dehydratedMatches: state.matches.map((d) =>
944
- // pick(d, ['fetchedAt', 'invalid', 'id', 'status', 'updatedAt']),
945
- // ),
946
- // },
947
- // }
948
- // }
949
-
950
- // hydrate = async (__do_not_use_server_ctx?: HydrationCtx) => {
951
- // let _ctx = __do_not_use_server_ctx
952
- // // Client hydrates from window
953
- // if (typeof document !== 'undefined') {
954
- // _ctx = window.__TSR_DEHYDRATED__
955
- // }
956
-
957
- // invariant(
958
- // _ctx,
959
- // 'Expected to find a __TSR_DEHYDRATED__ property on window... but we did not. Did you forget to render <DehydrateRouter /> in your app?',
960
- // )
961
-
962
- // const ctx = _ctx
963
- // this.dehydratedData = ctx.payload as any
964
- // this.options.hydrate?.(ctx.payload as any)
965
- // const dehydratedState = ctx.router.state
966
-
967
- // let matches = this.matchRoutes(
968
- // state.location.pathname,
969
- // state.location.search,
970
- // ).map((match) => {
971
- // const dehydratedMatch = dehydratedState.dehydratedMatches.find(
972
- // (d) => d.id === match.id,
973
- // )
974
-
975
- // invariant(
976
- // dehydratedMatch,
977
- // `Could not find a client-side match for dehydrated match with id: ${match.id}!`,
978
- // )
979
-
980
- // if (dehydratedMatch) {
981
- // return {
982
- // ...match,
983
- // ...dehydratedMatch,
984
- // }
985
- // }
986
- // return match
987
- // })
988
-
989
- // this.setState((s) => {
990
- // return {
991
- // ...s,
992
- // matches: dehydratedState.dehydratedMatches as any,
993
- // }
994
- // })
995
- // }
996
-
997
- // resolveMatchPromise = (matchId: string, key: string, value: any) => {
998
- // state.matches
999
- // .find((d) => d.id === matchId)
1000
- // ?.__promisesByKey[key]?.resolve(value)
1001
- // }
1002
-
1003
- // setRouteMatch = (
1004
- // id: string,
1005
- // pending: boolean,
1006
- // updater: NonNullableUpdater<RouteMatch<TRouteTree>>,
1007
- // ) => {
1008
- // const key = pending ? 'pendingMatches' : 'matches'
1009
-
1010
- // this.setState((prev) => {
1011
- // return {
1012
- // ...prev,
1013
- // [key]: prev[key].map((d) => {
1014
- // if (d.id === id) {
1015
- // return functionalUpdate(updater, d)
1016
- // }
1017
-
1018
- // return d
1019
- // }),
1020
- // }
1021
- // })
1022
- // }
1023
-
1024
- // setPendingRouteMatch = (
1025
- // id: string,
1026
- // updater: NonNullableUpdater<RouteMatch<TRouteTree>>,
1027
- // ) => {
1028
- // this.setRouteMatch(id, true, updater)
1029
- // }
1030
- }
1031
-
1032
- // A function that takes an import() argument which is a function and returns a new function that will
1033
- // proxy arguments from the caller to the imported function, retaining all type
1034
- // information along the way
1035
- function lazyFn(fn, key) {
1036
- return async (...args) => {
1037
- const imported = await fn();
1038
- return imported[key || 'default'](...args);
1039
- };
1040
- }
1041
-
782
+ // export type RouterContext<
783
+ // TRouteTree extends AnyRoute,
784
+ // // TDehydrated extends Record<string, any>,
785
+ // > = {
786
+ // buildLink: BuildLinkFn<TRouteTree>
787
+ // state: RouterState<TRouteTree>
788
+ // navigate: NavigateFn<TRouteTree>
789
+ // matchRoute: MatchRouteFn<TRouteTree>
790
+ // routeTree: TRouteTree
791
+ // routesById: RoutesById<TRouteTree>
792
+ // options: RouterOptions<TRouteTree>
793
+ // history: RouterHistory
794
+ // load: LoadFn
795
+ // buildLocation: BuildLocationFn<TRouteTree>
796
+ // subscribe: Router<TRouteTree>['subscribe']
797
+ // resetNextScrollRef: React.MutableRefObject<boolean>
798
+ // injectedHtmlRef: React.MutableRefObject<InjectedHtmlEntry[]>
799
+ // injectHtml: (entry: InjectedHtmlEntry) => void
800
+ // dehydrateData: <T>(
801
+ // key: any,
802
+ // getData: T | (() => Promise<T> | T),
803
+ // ) => () => void
804
+ // hydrateData: <T>(key: any) => T | undefined
805
+ // }
1042
806
  const routerContext = /*#__PURE__*/React.createContext(null);
1043
807
  if (typeof document !== 'undefined') {
1044
808
  window.__TSR_ROUTER_CONTEXT__ = routerContext;
1045
809
  }
1046
- const preloadWarning = 'Error preloading route! ☝️';
1047
- function isCtrlEvent(e) {
1048
- return !!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey);
1049
- }
1050
810
  class SearchParamError extends Error {}
1051
811
  class PathParamError extends Error {}
1052
812
  function getInitialRouterState(location) {
@@ -1063,66 +823,55 @@ function RouterProvider({
1063
823
  router,
1064
824
  ...rest
1065
825
  }) {
1066
- const options = {
826
+ // Allow the router to update options on the router instance
827
+ router.updateOptions({
1067
828
  ...router.options,
1068
829
  ...rest,
1069
830
  context: {
1070
831
  ...router.options.context,
1071
832
  ...rest?.context
1072
833
  }
1073
- };
1074
- const history = React.useState(() => options.history ?? createBrowserHistory())[0];
1075
- const tempLocationKeyRef = React.useRef(`${Math.round(Math.random() * 10000000)}`);
1076
- const resetNextScrollRef = React.useRef(true);
1077
- const navigateTimeoutRef = React.useRef(null);
1078
- const latestLoadPromiseRef = React.useRef(Promise.resolve());
1079
- const checkLatest = promise => {
1080
- return latestLoadPromiseRef.current !== promise ? latestLoadPromiseRef.current : undefined;
1081
- };
1082
- const parseLocation = useStableCallback(previousLocation => {
1083
- const parse = ({
1084
- pathname,
1085
- search,
1086
- hash,
1087
- state
1088
- }) => {
1089
- const parsedSearch = options.parseSearch(search);
1090
- return {
1091
- pathname: pathname,
1092
- searchStr: search,
1093
- search: replaceEqualDeep(previousLocation?.search, parsedSearch),
1094
- hash: hash.split('#').reverse()[0] ?? '',
1095
- href: `${pathname}${search}${hash}`,
1096
- state: replaceEqualDeep(previousLocation?.state, state)
1097
- };
1098
- };
1099
- const location = parse(history.location);
1100
- let {
1101
- __tempLocation,
1102
- __tempKey
1103
- } = location.state;
1104
- if (__tempLocation && (!__tempKey || __tempKey === tempLocationKeyRef.current)) {
1105
- // Sync up the location keys
1106
- const parsedTempLocation = parse(__tempLocation);
1107
- parsedTempLocation.state.key = location.state.key;
1108
- delete parsedTempLocation.state.__tempLocation;
1109
- return {
1110
- ...parsedTempLocation,
1111
- maskedLocation: location
1112
- };
1113
- }
1114
- return location;
1115
834
  });
1116
- const latestLocationRef = React.useRef(parseLocation());
1117
- const [preState, setState] = React.useState(() => getInitialRouterState(latestLocationRef.current));
835
+ const [preState, setState] = React.useState(() => router.state);
1118
836
  const [isTransitioning, startReactTransition] = React.useTransition();
1119
- const pendingMatchesRef = React.useRef([]);
1120
837
  const state = React.useMemo(() => ({
1121
838
  ...preState,
1122
839
  status: isTransitioning ? 'pending' : 'idle',
1123
- location: isTransitioning ? latestLocationRef.current : preState.location,
1124
- pendingMatches: pendingMatchesRef.current
840
+ location: isTransitioning ? router.latestLocation : preState.location,
841
+ pendingMatches: router.pendingMatches
1125
842
  }), [preState, isTransitioning]);
843
+ router.setState = setState;
844
+ router.state = state;
845
+ router.startReactTransition = startReactTransition;
846
+ React.useLayoutEffect(() => {
847
+ const unsub = router.history.subscribe(() => {
848
+ router.latestLocation = router.parseLocation(router.latestLocation);
849
+ if (state.location !== router.latestLocation) {
850
+ startReactTransition(() => {
851
+ try {
852
+ router.load();
853
+ } catch (err) {
854
+ console.error(err);
855
+ }
856
+ });
857
+ }
858
+ });
859
+ const nextLocation = router.buildLocation({
860
+ search: true,
861
+ params: true,
862
+ hash: true,
863
+ state: true
864
+ });
865
+ if (state.location.href !== nextLocation.href) {
866
+ router.commitLocation({
867
+ ...nextLocation,
868
+ replace: true
869
+ });
870
+ }
871
+ return () => {
872
+ unsub();
873
+ };
874
+ }, [history]);
1126
875
  React.useLayoutEffect(() => {
1127
876
  if (!isTransitioning && state.resolvedLocation !== state.location) {
1128
877
  router.emit({
@@ -1131,135 +880,592 @@ function RouterProvider({
1131
880
  toLocation: state.location,
1132
881
  pathChanged: state.location.href !== state.resolvedLocation?.href
1133
882
  });
1134
- pendingMatchesRef.current = [];
883
+ router.pendingMatches = [];
1135
884
  setState(s => ({
1136
885
  ...s,
1137
886
  resolvedLocation: s.location
1138
887
  }));
1139
888
  }
1140
889
  });
1141
- const basepath = `/${trimPath(options.basepath ?? '') ?? ''}`;
1142
- const resolvePathWithBase = useStableCallback((from, path) => {
1143
- return resolvePath(basepath, from, cleanPath(path));
1144
- });
1145
- const [routesById, routesByPath] = React.useMemo(() => {
1146
- const routesById = {};
1147
- const routesByPath = {};
1148
- const recurseRoutes = routes => {
1149
- routes.forEach((route, i) => {
1150
- route.init({
1151
- originalIndex: i
1152
- });
1153
- const existingRoute = routesById[route.id];
1154
- invariant(!existingRoute, `Duplicate routes found with id: ${String(route.id)}`);
1155
- routesById[route.id] = route;
1156
- if (!route.isRoot && route.path) {
1157
- const trimmedFullPath = trimPathRight(route.fullPath);
1158
- if (!routesByPath[trimmedFullPath] || route.fullPath.endsWith('/')) {
1159
- routesByPath[trimmedFullPath] = route;
1160
- }
1161
- }
1162
- const children = route.children;
1163
- if (children?.length) {
1164
- recurseRoutes(children);
1165
- }
1166
- });
1167
- };
1168
- recurseRoutes([router.routeTree]);
1169
- return [routesById, routesByPath];
1170
- }, []);
1171
- const looseRoutesById = routesById;
1172
- const flatRoutes = React.useMemo(() => Object.values(routesByPath).map((d, i) => {
1173
- const trimmed = trimPath(d.fullPath);
1174
- const parsed = parsePathname(trimmed);
1175
- while (parsed.length > 1 && parsed[0]?.value === '/') {
1176
- parsed.shift();
1177
- }
1178
- const score = parsed.map(d => {
1179
- if (d.type === 'param') {
1180
- return 0.5;
1181
- }
1182
- if (d.type === 'wildcard') {
1183
- return 0.25;
890
+ React.useLayoutEffect(() => {
891
+ startReactTransition(() => {
892
+ try {
893
+ router.load();
894
+ } catch (err) {
895
+ console.error(err);
1184
896
  }
1185
- return 1;
1186
897
  });
1187
- return {
1188
- child: d,
1189
- trimmed,
1190
- parsed,
1191
- index: i,
1192
- score
898
+ }, []);
899
+ return /*#__PURE__*/React.createElement(routerContext.Provider, {
900
+ value: router
901
+ }, /*#__PURE__*/React.createElement(Matches, null));
902
+ }
903
+ function getRouteMatch(state, id) {
904
+ return [...state.pendingMatches, ...state.matches].find(d => d.id === id);
905
+ }
906
+ function useRouterState(opts) {
907
+ const {
908
+ state
909
+ } = useRouter();
910
+ // return useStore(router.__store, opts?.select as any)
911
+ return opts?.select ? opts.select(state) : state;
912
+ }
913
+ function useRouter() {
914
+ const resolvedContext = window.__TSR_ROUTER_CONTEXT__ || routerContext;
915
+ const value = React.useContext(resolvedContext);
916
+ warning(value, 'useRouter must be used inside a <RouterProvider> component!');
917
+ return value;
918
+ }
919
+
920
+ function defer(_promise) {
921
+ const promise = _promise;
922
+ if (!promise.__deferredState) {
923
+ promise.__deferredState = {
924
+ uid: Math.random().toString(36).slice(2),
925
+ status: 'pending'
1193
926
  };
1194
- }).sort((a, b) => {
1195
- let isIndex = a.trimmed === '/' ? 1 : b.trimmed === '/' ? -1 : 0;
1196
- if (isIndex !== 0) return isIndex;
1197
- const length = Math.min(a.score.length, b.score.length);
1198
-
1199
- // Sort by length of score
1200
- if (a.score.length !== b.score.length) {
1201
- return b.score.length - a.score.length;
1202
- }
927
+ const state = promise.__deferredState;
928
+ promise.then(data => {
929
+ state.status = 'success';
930
+ state.data = data;
931
+ }).catch(error => {
932
+ state.status = 'error';
933
+ state.error = error;
934
+ });
935
+ }
936
+ return promise;
937
+ }
938
+ function isDehydratedDeferred(obj) {
939
+ return typeof obj === 'object' && obj !== null && !(obj instanceof Promise) && !obj.then && '__deferredState' in obj;
940
+ }
1203
941
 
1204
- // Sort by min available score
1205
- for (let i = 0; i < length; i++) {
1206
- if (a.score[i] !== b.score[i]) {
1207
- return b.score[i] - a.score[i];
1208
- }
1209
- }
942
+ function useAwaited({
943
+ promise
944
+ }) {
945
+ const router = useRouter();
946
+ let state = promise.__deferredState;
947
+ const key = `__TSR__DEFERRED__${state.uid}`;
948
+ if (isDehydratedDeferred(promise)) {
949
+ state = router.hydrateData(key);
950
+ promise = Promise.resolve(state.data);
951
+ promise.__deferredState = state;
952
+ }
953
+ if (state.status === 'pending') {
954
+ throw promise;
955
+ }
956
+ if (state.status === 'error') {
957
+ throw state.error;
958
+ }
959
+ router.dehydrateData(key, state);
960
+ return [state.data];
961
+ }
962
+ function Await(props) {
963
+ const awaited = useAwaited(props);
964
+ return props.children(...awaited);
965
+ }
1210
966
 
1211
- // Sort by min available parsed value
1212
- for (let i = 0; i < length; i++) {
1213
- if (a.parsed[i].value !== b.parsed[i].value) {
1214
- return a.parsed[i].value > b.parsed[i].value ? 1 : -1;
1215
- }
1216
- }
967
+ class FileRoute {
968
+ constructor(path) {
969
+ this.path = path;
970
+ }
971
+ createRoute = options => {
972
+ const route = new Route(options);
973
+ route.isRoot = false;
974
+ return route;
975
+ };
976
+ }
1217
977
 
1218
- // Sort by length of trimmed full path
1219
- if (a.trimmed !== b.trimmed) {
1220
- return a.trimmed > b.trimmed ? 1 : -1;
978
+ function lazyRouteComponent(importer, exportName) {
979
+ let loadPromise;
980
+ const load = () => {
981
+ if (!loadPromise) {
982
+ loadPromise = importer();
1221
983
  }
984
+ return loadPromise;
985
+ };
986
+ const lazyComp = /*#__PURE__*/React.lazy(async () => {
987
+ const moduleExports = await load();
988
+ const comp = moduleExports[exportName ?? 'default'];
989
+ return {
990
+ default: comp
991
+ };
992
+ });
993
+ lazyComp.preload = load;
994
+ return lazyComp;
995
+ }
1222
996
 
1223
- // Sort by original index
1224
- return a.index - b.index;
1225
- }).map((d, i) => {
1226
- d.child.rank = i;
1227
- return d.child;
1228
- }), [routesByPath]);
1229
- const matchRoutes = useStableCallback((pathname, locationSearch, opts) => {
1230
- let routeParams = {};
1231
- let foundRoute = flatRoutes.find(route => {
1232
- const matchedParams = matchPathname(basepath, trimPathRight(pathname), {
1233
- to: route.fullPath,
1234
- caseSensitive: route.options.caseSensitive ?? options.caseSensitive,
1235
- fuzzy: false
1236
- });
1237
- if (matchedParams) {
1238
- routeParams = matchedParams;
1239
- return true;
997
+ function _extends() {
998
+ _extends = Object.assign ? Object.assign.bind() : function (target) {
999
+ for (var i = 1; i < arguments.length; i++) {
1000
+ var source = arguments[i];
1001
+ for (var key in source) {
1002
+ if (Object.prototype.hasOwnProperty.call(source, key)) {
1003
+ target[key] = source[key];
1004
+ }
1240
1005
  }
1241
- return false;
1242
- });
1243
- let routeCursor = foundRoute || routesById['__root__'];
1244
- let matchedRoutes = [routeCursor];
1245
- // let includingLayouts = true
1246
- while (routeCursor?.parentRoute) {
1247
- routeCursor = routeCursor.parentRoute;
1248
- if (routeCursor) matchedRoutes.unshift(routeCursor);
1249
1006
  }
1007
+ return target;
1008
+ };
1009
+ return _extends.apply(this, arguments);
1010
+ }
1250
1011
 
1251
- // Existing matches are matches that are already loaded along with
1252
- // pending matches that are still loading
1012
+ function useLinkProps(options) {
1013
+ const {
1014
+ buildLink
1015
+ } = useRouter();
1016
+ const match = useMatch({
1017
+ strict: false
1018
+ });
1019
+ const {
1020
+ // custom props
1021
+ type,
1022
+ children,
1023
+ target,
1024
+ activeProps = () => ({
1025
+ className: 'active'
1026
+ }),
1027
+ inactiveProps = () => ({}),
1028
+ activeOptions,
1029
+ disabled,
1030
+ hash,
1031
+ search,
1032
+ params,
1033
+ to,
1034
+ state,
1035
+ mask,
1036
+ preload,
1037
+ preloadDelay,
1038
+ replace,
1039
+ startTransition,
1040
+ resetScroll,
1041
+ // element props
1042
+ style,
1043
+ className,
1044
+ onClick,
1045
+ onFocus,
1046
+ onMouseEnter,
1047
+ onMouseLeave,
1048
+ onTouchStart,
1049
+ ...rest
1050
+ } = options;
1051
+ const linkInfo = buildLink({
1052
+ from: options.to ? match.pathname : undefined,
1053
+ ...options
1054
+ });
1055
+ if (linkInfo.type === 'external') {
1056
+ const {
1057
+ href
1058
+ } = linkInfo;
1059
+ return {
1060
+ href
1061
+ };
1062
+ }
1063
+ const {
1064
+ handleClick,
1065
+ handleFocus,
1066
+ handleEnter,
1067
+ handleLeave,
1068
+ handleTouchStart,
1069
+ isActive,
1070
+ next
1071
+ } = linkInfo;
1072
+ const composeHandlers = handlers => e => {
1073
+ if (e.persist) e.persist();
1074
+ handlers.filter(Boolean).forEach(handler => {
1075
+ if (e.defaultPrevented) return;
1076
+ handler(e);
1077
+ });
1078
+ };
1253
1079
 
1254
- const parseErrors = matchedRoutes.map(route => {
1255
- let parsedParamsError;
1256
- if (route.options.parseParams) {
1257
- try {
1258
- const parsedParams = route.options.parseParams(routeParams);
1259
- // Add the parsed params to the accumulated params bag
1260
- Object.assign(routeParams, parsedParams);
1261
- } catch (err) {
1262
- parsedParamsError = new PathParamError(err.message, {
1080
+ // Get the active props
1081
+ const resolvedActiveProps = isActive ? functionalUpdate(activeProps, {}) ?? {} : {};
1082
+
1083
+ // Get the inactive props
1084
+ const resolvedInactiveProps = isActive ? {} : functionalUpdate(inactiveProps, {}) ?? {};
1085
+ return {
1086
+ ...resolvedActiveProps,
1087
+ ...resolvedInactiveProps,
1088
+ ...rest,
1089
+ href: disabled ? undefined : next.maskedLocation ? next.maskedLocation.href : next.href,
1090
+ onClick: composeHandlers([onClick, handleClick]),
1091
+ onFocus: composeHandlers([onFocus, handleFocus]),
1092
+ onMouseEnter: composeHandlers([onMouseEnter, handleEnter]),
1093
+ onMouseLeave: composeHandlers([onMouseLeave, handleLeave]),
1094
+ onTouchStart: composeHandlers([onTouchStart, handleTouchStart]),
1095
+ target,
1096
+ style: {
1097
+ ...style,
1098
+ ...resolvedActiveProps.style,
1099
+ ...resolvedInactiveProps.style
1100
+ },
1101
+ className: [className, resolvedActiveProps.className, resolvedInactiveProps.className].filter(Boolean).join(' ') || undefined,
1102
+ ...(disabled ? {
1103
+ role: 'link',
1104
+ 'aria-disabled': true
1105
+ } : undefined),
1106
+ ['data-status']: isActive ? 'active' : undefined
1107
+ };
1108
+ }
1109
+ const Link = /*#__PURE__*/React.forwardRef((props, ref) => {
1110
+ const linkProps = useLinkProps(props);
1111
+ return /*#__PURE__*/React.createElement("a", _extends({
1112
+ ref: ref
1113
+ }, linkProps, {
1114
+ children: typeof props.children === 'function' ? props.children({
1115
+ isActive: linkProps['data-status'] === 'active'
1116
+ }) : props.children
1117
+ }));
1118
+ });
1119
+
1120
+ // @ts-nocheck
1121
+
1122
+ // 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.
1123
+
1124
+ function encode(obj, pfx) {
1125
+ var k,
1126
+ i,
1127
+ tmp,
1128
+ str = '';
1129
+ for (k in obj) {
1130
+ if ((tmp = obj[k]) !== void 0) {
1131
+ if (Array.isArray(tmp)) {
1132
+ for (i = 0; i < tmp.length; i++) {
1133
+ str && (str += '&');
1134
+ str += encodeURIComponent(k) + '=' + encodeURIComponent(tmp[i]);
1135
+ }
1136
+ } else {
1137
+ str && (str += '&');
1138
+ str += encodeURIComponent(k) + '=' + encodeURIComponent(tmp);
1139
+ }
1140
+ }
1141
+ }
1142
+ return (pfx || '') + str;
1143
+ }
1144
+ function toValue(mix) {
1145
+ if (!mix) return '';
1146
+ var str = decodeURIComponent(mix);
1147
+ if (str === 'false') return false;
1148
+ if (str === 'true') return true;
1149
+ return +str * 0 === 0 && +str + '' === str ? +str : str;
1150
+ }
1151
+ function decode(str) {
1152
+ var tmp,
1153
+ k,
1154
+ out = {},
1155
+ arr = str.split('&');
1156
+ while (tmp = arr.shift()) {
1157
+ tmp = tmp.split('=');
1158
+ k = tmp.shift();
1159
+ if (out[k] !== void 0) {
1160
+ out[k] = [].concat(out[k], toValue(tmp.shift()));
1161
+ } else {
1162
+ out[k] = toValue(tmp.shift());
1163
+ }
1164
+ }
1165
+ return out;
1166
+ }
1167
+
1168
+ // Detect if we're in the DOM
1169
+
1170
+ function redirect(opts) {
1171
+ opts.isRedirect = true;
1172
+ return opts;
1173
+ }
1174
+ function isRedirect(obj) {
1175
+ return !!obj?.isRedirect;
1176
+ }
1177
+
1178
+ const defaultParseSearch = parseSearchWith(JSON.parse);
1179
+ const defaultStringifySearch = stringifySearchWith(JSON.stringify, JSON.parse);
1180
+ function parseSearchWith(parser) {
1181
+ return searchStr => {
1182
+ if (searchStr.substring(0, 1) === '?') {
1183
+ searchStr = searchStr.substring(1);
1184
+ }
1185
+ let query = decode(searchStr);
1186
+
1187
+ // Try to parse any query params that might be json
1188
+ for (let key in query) {
1189
+ const value = query[key];
1190
+ if (typeof value === 'string') {
1191
+ try {
1192
+ query[key] = parser(value);
1193
+ } catch (err) {
1194
+ //
1195
+ }
1196
+ }
1197
+ }
1198
+ return query;
1199
+ };
1200
+ }
1201
+ function stringifySearchWith(stringify, parser) {
1202
+ function stringifyValue(val) {
1203
+ if (typeof val === 'object' && val !== null) {
1204
+ try {
1205
+ return stringify(val);
1206
+ } catch (err) {
1207
+ // silent
1208
+ }
1209
+ } else if (typeof val === 'string' && typeof parser === 'function') {
1210
+ try {
1211
+ // Check if it's a valid parseable string.
1212
+ // If it is, then stringify it again.
1213
+ parser(val);
1214
+ return stringify(val);
1215
+ } catch (err) {
1216
+ // silent
1217
+ }
1218
+ }
1219
+ return val;
1220
+ }
1221
+ return search => {
1222
+ search = {
1223
+ ...search
1224
+ };
1225
+ if (search) {
1226
+ Object.keys(search).forEach(key => {
1227
+ const val = search[key];
1228
+ if (typeof val === 'undefined' || val === undefined) {
1229
+ delete search[key];
1230
+ } else {
1231
+ search[key] = stringifyValue(val);
1232
+ }
1233
+ });
1234
+ }
1235
+ const searchStr = encode(search).toString();
1236
+ return searchStr ? `?${searchStr}` : '';
1237
+ };
1238
+ }
1239
+
1240
+ //
1241
+
1242
+ const componentTypes = ['component', 'errorComponent', 'pendingComponent'];
1243
+ const preloadWarning = 'Error preloading route! ☝️';
1244
+ class Router {
1245
+ // Option-independent properties
1246
+ tempLocationKey = `${Math.round(Math.random() * 10000000)}`;
1247
+ resetNextScroll = true;
1248
+ navigateTimeout = null;
1249
+ latestLoadPromise = Promise.resolve();
1250
+ subscribers = new Set();
1251
+ pendingMatches = [];
1252
+ injectedHtml = [];
1253
+
1254
+ // Must build in constructor
1255
+
1256
+ constructor(options) {
1257
+ this.updateOptions({
1258
+ defaultPreloadDelay: 50,
1259
+ context: undefined,
1260
+ ...options,
1261
+ stringifySearch: options?.stringifySearch ?? defaultStringifySearch,
1262
+ parseSearch: options?.parseSearch ?? defaultParseSearch
1263
+ });
1264
+ }
1265
+ startReactTransition = () => {
1266
+ warning(false, 'startReactTransition implementation is missing. If you see this, please file an issue.');
1267
+ };
1268
+ setState = () => {
1269
+ warning(false, 'setState implementation is missing. If you see this, please file an issue.');
1270
+ };
1271
+ updateOptions = newOptions => {
1272
+ this.options;
1273
+ this.options = {
1274
+ ...this.options,
1275
+ ...newOptions
1276
+ };
1277
+ this.basepath = `/${trimPath(newOptions.basepath ?? '') ?? ''}`;
1278
+ if (!this.history || this.options.history && this.options.history !== this.history) {
1279
+ this.history = this.options.history ?? createBrowserHistory();
1280
+ this.latestLocation = this.parseLocation();
1281
+ }
1282
+ if (this.options.routeTree !== this.routeTree) {
1283
+ this.routeTree = this.options.routeTree;
1284
+ this.buildRouteTree();
1285
+ }
1286
+ if (!this.state) {
1287
+ this.state = getInitialRouterState(this.latestLocation);
1288
+ }
1289
+ };
1290
+ buildRouteTree = () => {
1291
+ this.routesById = {};
1292
+ this.routesByPath = {};
1293
+ const recurseRoutes = childRoutes => {
1294
+ childRoutes.forEach((childRoute, i) => {
1295
+ // if (typeof childRoute === 'function') {
1296
+ // childRoute = (childRoute as any)()
1297
+ // }
1298
+ childRoute.init({
1299
+ originalIndex: i
1300
+ });
1301
+ const existingRoute = this.routesById[childRoute.id];
1302
+ invariant(!existingRoute, `Duplicate routes found with id: ${String(childRoute.id)}`);
1303
+ this.routesById[childRoute.id] = childRoute;
1304
+ if (!childRoute.isRoot && childRoute.path) {
1305
+ const trimmedFullPath = trimPathRight(childRoute.fullPath);
1306
+ if (!this.routesByPath[trimmedFullPath] || childRoute.fullPath.endsWith('/')) {
1307
+ this.routesByPath[trimmedFullPath] = childRoute;
1308
+ }
1309
+ }
1310
+ const children = childRoute.children;
1311
+ if (children?.length) {
1312
+ recurseRoutes(children);
1313
+ }
1314
+ });
1315
+ };
1316
+ recurseRoutes([this.routeTree]);
1317
+ this.flatRoutes = Object.values(this.routesByPath).map((d, i) => {
1318
+ const trimmed = trimPath(d.fullPath);
1319
+ const parsed = parsePathname(trimmed);
1320
+ while (parsed.length > 1 && parsed[0]?.value === '/') {
1321
+ parsed.shift();
1322
+ }
1323
+ const score = parsed.map(d => {
1324
+ if (d.type === 'param') {
1325
+ return 0.5;
1326
+ }
1327
+ if (d.type === 'wildcard') {
1328
+ return 0.25;
1329
+ }
1330
+ return 1;
1331
+ });
1332
+ return {
1333
+ child: d,
1334
+ trimmed,
1335
+ parsed,
1336
+ index: i,
1337
+ score
1338
+ };
1339
+ }).sort((a, b) => {
1340
+ let isIndex = a.trimmed === '/' ? 1 : b.trimmed === '/' ? -1 : 0;
1341
+ if (isIndex !== 0) return isIndex;
1342
+ const length = Math.min(a.score.length, b.score.length);
1343
+
1344
+ // Sort by length of score
1345
+ if (a.score.length !== b.score.length) {
1346
+ return b.score.length - a.score.length;
1347
+ }
1348
+
1349
+ // Sort by min available score
1350
+ for (let i = 0; i < length; i++) {
1351
+ if (a.score[i] !== b.score[i]) {
1352
+ return b.score[i] - a.score[i];
1353
+ }
1354
+ }
1355
+
1356
+ // Sort by min available parsed value
1357
+ for (let i = 0; i < length; i++) {
1358
+ if (a.parsed[i].value !== b.parsed[i].value) {
1359
+ return a.parsed[i].value > b.parsed[i].value ? 1 : -1;
1360
+ }
1361
+ }
1362
+
1363
+ // Sort by length of trimmed full path
1364
+ if (a.trimmed !== b.trimmed) {
1365
+ return a.trimmed > b.trimmed ? 1 : -1;
1366
+ }
1367
+
1368
+ // Sort by original index
1369
+ return a.index - b.index;
1370
+ }).map((d, i) => {
1371
+ d.child.rank = i;
1372
+ return d.child;
1373
+ });
1374
+ };
1375
+ subscribe = (eventType, fn) => {
1376
+ const listener = {
1377
+ eventType,
1378
+ fn
1379
+ };
1380
+ this.subscribers.add(listener);
1381
+ return () => {
1382
+ this.subscribers.delete(listener);
1383
+ };
1384
+ };
1385
+ emit = routerEvent => {
1386
+ this.subscribers.forEach(listener => {
1387
+ if (listener.eventType === routerEvent.type) {
1388
+ listener.fn(routerEvent);
1389
+ }
1390
+ });
1391
+ };
1392
+ checkLatest = promise => {
1393
+ return this.latestLoadPromise !== promise ? this.latestLoadPromise : undefined;
1394
+ };
1395
+ parseLocation = previousLocation => {
1396
+ const parse = ({
1397
+ pathname,
1398
+ search,
1399
+ hash,
1400
+ state
1401
+ }) => {
1402
+ const parsedSearch = this.options.parseSearch(search);
1403
+ return {
1404
+ pathname: pathname,
1405
+ searchStr: search,
1406
+ search: replaceEqualDeep(previousLocation?.search, parsedSearch),
1407
+ hash: hash.split('#').reverse()[0] ?? '',
1408
+ href: `${pathname}${search}${hash}`,
1409
+ state: replaceEqualDeep(previousLocation?.state, state)
1410
+ };
1411
+ };
1412
+ const location = parse(this.history.location);
1413
+ let {
1414
+ __tempLocation,
1415
+ __tempKey
1416
+ } = location.state;
1417
+ if (__tempLocation && (!__tempKey || __tempKey === this.tempLocationKey)) {
1418
+ // Sync up the location keys
1419
+ const parsedTempLocation = parse(__tempLocation);
1420
+ parsedTempLocation.state.key = location.state.key;
1421
+ delete parsedTempLocation.state.__tempLocation;
1422
+ return {
1423
+ ...parsedTempLocation,
1424
+ maskedLocation: location
1425
+ };
1426
+ }
1427
+ return location;
1428
+ };
1429
+ resolvePathWithBase = (from, path) => {
1430
+ return resolvePath(this.basepath, from, cleanPath(path));
1431
+ };
1432
+ get looseRoutesById() {
1433
+ return this.routesById;
1434
+ }
1435
+ matchRoutes = (pathname, locationSearch, opts) => {
1436
+ let routeParams = {};
1437
+ let foundRoute = this.flatRoutes.find(route => {
1438
+ const matchedParams = matchPathname(this.basepath, trimPathRight(pathname), {
1439
+ to: route.fullPath,
1440
+ caseSensitive: route.options.caseSensitive ?? this.options.caseSensitive,
1441
+ fuzzy: false
1442
+ });
1443
+ if (matchedParams) {
1444
+ routeParams = matchedParams;
1445
+ return true;
1446
+ }
1447
+ return false;
1448
+ });
1449
+ let routeCursor = foundRoute || this.routesById['__root__'];
1450
+ let matchedRoutes = [routeCursor];
1451
+ // let includingLayouts = true
1452
+ while (routeCursor?.parentRoute) {
1453
+ routeCursor = routeCursor.parentRoute;
1454
+ if (routeCursor) matchedRoutes.unshift(routeCursor);
1455
+ }
1456
+
1457
+ // Existing matches are matches that are already loaded along with
1458
+ // pending matches that are still loading
1459
+
1460
+ const parseErrors = matchedRoutes.map(route => {
1461
+ let parsedParamsError;
1462
+ if (route.options.parseParams) {
1463
+ try {
1464
+ const parsedParams = route.options.parseParams(routeParams);
1465
+ // Add the parsed params to the accumulated params bag
1466
+ Object.assign(routeParams, parsedParams);
1467
+ } catch (err) {
1468
+ parsedParamsError = new PathParamError(err.message, {
1263
1469
  cause: err
1264
1470
  });
1265
1471
  if (opts?.throwOnError) {
@@ -1277,10 +1483,12 @@ function RouterProvider({
1277
1483
  // Waste not, want not. If we already have a match for this route,
1278
1484
  // reuse it. This is important for layout routes, which might stick
1279
1485
  // around between navigation actions that only change leaf routes.
1280
- const existingMatch = getRouteMatch(state, matchId);
1486
+ const existingMatch = getRouteMatch(this.state, matchId);
1487
+ const cause = this.state.matches.find(d => d.id === matchId) ? 'stay' : 'enter';
1281
1488
  if (existingMatch) {
1282
1489
  return {
1283
- ...existingMatch
1490
+ ...existingMatch,
1491
+ cause
1284
1492
  };
1285
1493
  }
1286
1494
 
@@ -1290,7 +1498,7 @@ function RouterProvider({
1290
1498
  id: matchId,
1291
1499
  routeId: route.id,
1292
1500
  params: routeParams,
1293
- pathname: joinPaths([basepath, interpolatedPath]),
1501
+ pathname: joinPaths([this.basepath, interpolatedPath]),
1294
1502
  updatedAt: Date.now(),
1295
1503
  routeSearch: {},
1296
1504
  search: {},
@@ -1304,7 +1512,8 @@ function RouterProvider({
1304
1512
  context: undefined,
1305
1513
  abortController: new AbortController(),
1306
1514
  shouldReloadDeps: undefined,
1307
- fetchedAt: 0
1515
+ fetchedAt: 0,
1516
+ cause
1308
1517
  };
1309
1518
  return routeMatch;
1310
1519
  });
@@ -1314,7 +1523,7 @@ function RouterProvider({
1314
1523
  // so that we can use the parent match's search params and context
1315
1524
  matches.forEach((match, i) => {
1316
1525
  const parentMatch = matches[i - 1];
1317
- const route = looseRoutesById[match.routeId];
1526
+ const route = this.looseRoutesById[match.routeId];
1318
1527
  const searchInfo = (() => {
1319
1528
  // Validate the search params and stabilize them
1320
1529
  const parentSearchInfo = {
@@ -1348,28 +1557,28 @@ function RouterProvider({
1348
1557
  Object.assign(match, searchInfo);
1349
1558
  });
1350
1559
  return matches;
1351
- });
1352
- const cancelMatch = useStableCallback(id => {
1353
- getRouteMatch(state, id)?.abortController?.abort();
1354
- });
1355
- const cancelMatches = useStableCallback(state => {
1356
- state.matches.forEach(match => {
1357
- cancelMatch(match.id);
1560
+ };
1561
+ cancelMatch = id => {
1562
+ getRouteMatch(this.state, id)?.abortController?.abort();
1563
+ };
1564
+ cancelMatches = () => {
1565
+ this.state.matches.forEach(match => {
1566
+ this.cancelMatch(match.id);
1358
1567
  });
1359
- });
1360
- const buildLocation = useStableCallback(opts => {
1568
+ };
1569
+ buildLocation = opts => {
1361
1570
  const build = (dest = {}, matches) => {
1362
- const from = latestLocationRef.current;
1571
+ const from = this.latestLocation;
1363
1572
  const fromPathname = dest.from ?? from.pathname;
1364
- let pathname = resolvePathWithBase(fromPathname, `${dest.to ?? ''}`);
1365
- const fromMatches = matchRoutes(fromPathname, from.search);
1573
+ let pathname = this.resolvePathWithBase(fromPathname, `${dest.to ?? ''}`);
1574
+ const fromMatches = this.matchRoutes(fromPathname, from.search);
1366
1575
  const stayingMatches = matches?.filter(d => fromMatches?.find(e => e.routeId === d.routeId));
1367
1576
  const prevParams = {
1368
1577
  ...last(fromMatches)?.params
1369
1578
  };
1370
1579
  let nextParams = (dest.params ?? true) === true ? prevParams : functionalUpdate(dest.params, prevParams);
1371
1580
  if (nextParams) {
1372
- matches?.map(d => looseRoutesById[d.routeId].options.stringifyParams).filter(Boolean).forEach(fn => {
1581
+ matches?.map(d => this.looseRoutesById[d.routeId].options.stringifyParams).filter(Boolean).forEach(fn => {
1373
1582
  nextParams = {
1374
1583
  ...nextParams,
1375
1584
  ...fn(nextParams)
@@ -1377,8 +1586,8 @@ function RouterProvider({
1377
1586
  });
1378
1587
  }
1379
1588
  pathname = interpolatePath(pathname, nextParams ?? {});
1380
- const preSearchFilters = stayingMatches?.map(match => looseRoutesById[match.routeId].options.preSearchFilters ?? []).flat().filter(Boolean) ?? [];
1381
- const postSearchFilters = stayingMatches?.map(match => looseRoutesById[match.routeId].options.postSearchFilters ?? []).flat().filter(Boolean) ?? [];
1589
+ const preSearchFilters = stayingMatches?.map(match => this.looseRoutesById[match.routeId].options.preSearchFilters ?? []).flat().filter(Boolean) ?? [];
1590
+ const postSearchFilters = stayingMatches?.map(match => this.looseRoutesById[match.routeId].options.postSearchFilters ?? []).flat().filter(Boolean) ?? [];
1382
1591
 
1383
1592
  // Pre filters first
1384
1593
  const preFilteredSearch = preSearchFilters?.length ? preSearchFilters?.reduce((prev, next) => next(prev), from.search) : from.search;
@@ -1392,7 +1601,7 @@ function RouterProvider({
1392
1601
  // Then post filters
1393
1602
  const postFilteredSearch = postSearchFilters?.length ? postSearchFilters.reduce((prev, next) => next(prev), destSearch) : destSearch;
1394
1603
  const search = replaceEqualDeep(from.search, postFilteredSearch);
1395
- const searchStr = options.stringifySearch(search);
1604
+ const searchStr = this.options.stringifySearch(search);
1396
1605
  const hash = dest.hash === true ? from.hash : dest.hash ? functionalUpdate(dest.hash, from.hash) : from.hash;
1397
1606
  const hashStr = hash ? `#${hash}` : '';
1398
1607
  let nextState = dest.state === true ? from.state : dest.state ? functionalUpdate(dest.state, from.state) : from.state;
@@ -1403,7 +1612,7 @@ function RouterProvider({
1403
1612
  searchStr,
1404
1613
  state: nextState,
1405
1614
  hash,
1406
- href: history.createHref(`${pathname}${searchStr}${hashStr}`),
1615
+ href: this.history.createHref(`${pathname}${searchStr}${hashStr}`),
1407
1616
  unmaskOnReload: dest.unmaskOnReload
1408
1617
  };
1409
1618
  };
@@ -1412,8 +1621,8 @@ function RouterProvider({
1412
1621
  let maskedNext = maskedDest ? build(maskedDest) : undefined;
1413
1622
  if (!maskedNext) {
1414
1623
  let params = {};
1415
- let foundMask = options.routeMasks?.find(d => {
1416
- const match = matchPathname(basepath, next.pathname, {
1624
+ let foundMask = this.options.routeMasks?.find(d => {
1625
+ const match = matchPathname(this.basepath, next.pathname, {
1417
1626
  to: d.from,
1418
1627
  caseSensitive: false,
1419
1628
  fuzzy: false
@@ -1433,8 +1642,8 @@ function RouterProvider({
1433
1642
  maskedNext = build(maskedDest);
1434
1643
  }
1435
1644
  }
1436
- const nextMatches = matchRoutes(next.pathname, next.search);
1437
- const maskedMatches = maskedNext ? matchRoutes(maskedNext.pathname, maskedNext.search) : undefined;
1645
+ const nextMatches = this.matchRoutes(next.pathname, next.search);
1646
+ const maskedMatches = maskedNext ? this.matchRoutes(maskedNext.pathname, maskedNext.search) : undefined;
1438
1647
  const maskedFinal = maskedNext ? build(maskedDest, maskedMatches) : undefined;
1439
1648
  const final = build(dest, nextMatches);
1440
1649
  if (maskedFinal) {
@@ -1449,13 +1658,13 @@ function RouterProvider({
1449
1658
  });
1450
1659
  }
1451
1660
  return buildWithMatches(opts);
1452
- });
1453
- const commitLocation = useStableCallback(async ({
1661
+ };
1662
+ commitLocation = async ({
1454
1663
  startTransition,
1455
1664
  ...next
1456
1665
  }) => {
1457
- if (navigateTimeoutRef.current) clearTimeout(navigateTimeoutRef.current);
1458
- const isSameUrl = latestLocationRef.current.href === next.href;
1666
+ if (this.navigateTimeout) clearTimeout(this.navigateTimeout);
1667
+ const isSameUrl = this.latestLocation.href === next.href;
1459
1668
 
1460
1669
  // If the next urls are the same and we're not replacing,
1461
1670
  // do nothing
@@ -1482,37 +1691,37 @@ function RouterProvider({
1482
1691
  }
1483
1692
  }
1484
1693
  };
1485
- if (nextHistory.unmaskOnReload ?? options.unmaskOnReload ?? false) {
1486
- nextHistory.state.__tempKey = tempLocationKeyRef.current;
1694
+ if (nextHistory.unmaskOnReload ?? this.options.unmaskOnReload ?? false) {
1695
+ nextHistory.state.__tempKey = this.tempLocationKey;
1487
1696
  }
1488
1697
  }
1489
1698
  const apply = () => {
1490
- history[next.replace ? 'replace' : 'push'](nextHistory.href, nextHistory.state);
1699
+ this.history[next.replace ? 'replace' : 'push'](nextHistory.href, nextHistory.state);
1491
1700
  };
1492
1701
  if (startTransition ?? true) {
1493
- startReactTransition(apply);
1702
+ this.startReactTransition(apply);
1494
1703
  } else {
1495
1704
  apply();
1496
1705
  }
1497
1706
  }
1498
- resetNextScrollRef.current = next.resetScroll ?? true;
1499
- return latestLoadPromiseRef.current;
1500
- });
1501
- const buildAndCommitLocation = useStableCallback(({
1707
+ this.resetNextScroll = next.resetScroll ?? true;
1708
+ return this.latestLoadPromise;
1709
+ };
1710
+ buildAndCommitLocation = ({
1502
1711
  replace,
1503
1712
  resetScroll,
1504
1713
  startTransition,
1505
1714
  ...rest
1506
1715
  } = {}) => {
1507
- const location = buildLocation(rest);
1508
- return commitLocation({
1716
+ const location = this.buildLocation(rest);
1717
+ return this.commitLocation({
1509
1718
  ...location,
1510
1719
  startTransition,
1511
1720
  replace,
1512
1721
  resetScroll
1513
1722
  });
1514
- });
1515
- const navigate = useStableCallback(({
1723
+ };
1724
+ navigate = ({
1516
1725
  from,
1517
1726
  to = '',
1518
1727
  ...rest
@@ -1530,13 +1739,13 @@ function RouterProvider({
1530
1739
  isExternal = true;
1531
1740
  } catch (e) {}
1532
1741
  invariant(!isExternal, 'Attempting to navigate to external url with this.navigate!');
1533
- return buildAndCommitLocation({
1742
+ return this.buildAndCommitLocation({
1534
1743
  ...rest,
1535
1744
  from: fromString,
1536
1745
  to: toString
1537
1746
  });
1538
- });
1539
- const loadMatches = useStableCallback(async ({
1747
+ };
1748
+ loadMatches = async ({
1540
1749
  checkLatest,
1541
1750
  matches,
1542
1751
  preload
@@ -1548,7 +1757,7 @@ function RouterProvider({
1548
1757
  try {
1549
1758
  for (let [index, match] of matches.entries()) {
1550
1759
  const parentMatch = matches[index - 1];
1551
- const route = looseRoutesById[match.routeId];
1760
+ const route = this.looseRoutesById[match.routeId];
1552
1761
  const handleError = (err, code) => {
1553
1762
  err.routerCode = code;
1554
1763
  firstBadMatchIndex = firstBadMatchIndex ?? index;
@@ -1577,19 +1786,21 @@ function RouterProvider({
1577
1786
  if (match.searchError) {
1578
1787
  handleError(match.searchError, 'VALIDATE_SEARCH');
1579
1788
  }
1580
- const parentContext = parentMatch?.context ?? options.context ?? {};
1789
+ const parentContext = parentMatch?.context ?? this.options.context ?? {};
1581
1790
  const beforeLoadContext = (await route.options.beforeLoad?.({
1582
1791
  search: match.search,
1583
1792
  abortController: match.abortController,
1584
1793
  params: match.params,
1585
1794
  preload: !!preload,
1586
1795
  context: parentContext,
1587
- location: state.location,
1588
- navigate: opts => navigate({
1796
+ location: this.state.location,
1797
+ // TOOD: just expose state and router, etc
1798
+ navigate: opts => this.navigate({
1589
1799
  ...opts,
1590
1800
  from: match.pathname
1591
1801
  }),
1592
- buildLocation
1802
+ buildLocation: this.buildLocation,
1803
+ cause: match.cause
1593
1804
  })) ?? {};
1594
1805
  const context = {
1595
1806
  ...parentContext,
@@ -1606,7 +1817,7 @@ function RouterProvider({
1606
1817
  }
1607
1818
  } catch (err) {
1608
1819
  if (isRedirect(err)) {
1609
- if (!preload) navigate(err);
1820
+ if (!preload) this.navigate(err);
1610
1821
  return matches;
1611
1822
  }
1612
1823
  throw err;
@@ -1616,11 +1827,11 @@ function RouterProvider({
1616
1827
  validResolvedMatches.forEach((match, index) => {
1617
1828
  matchPromises.push((async () => {
1618
1829
  const parentMatchPromise = matchPromises[index - 1];
1619
- const route = looseRoutesById[match.routeId];
1830
+ const route = this.looseRoutesById[match.routeId];
1620
1831
  const handleIfRedirect = err => {
1621
1832
  if (isRedirect(err)) {
1622
1833
  if (!preload) {
1623
- navigate(err);
1834
+ this.navigate(err);
1624
1835
  }
1625
1836
  return true;
1626
1837
  }
@@ -1633,9 +1844,8 @@ function RouterProvider({
1633
1844
  invalid: false
1634
1845
  };
1635
1846
  if (match.isFetching) {
1636
- loadPromise = getRouteMatch(state, match.id)?.loadPromise;
1847
+ loadPromise = getRouteMatch(this.state, match.id)?.loadPromise;
1637
1848
  } else {
1638
- const cause = state.matches.find(d => d.id === match.id) ? 'stay' : 'enter';
1639
1849
  const loaderContext = {
1640
1850
  params: match.params,
1641
1851
  search: match.search,
@@ -1643,20 +1853,20 @@ function RouterProvider({
1643
1853
  parentMatchPromise,
1644
1854
  abortController: match.abortController,
1645
1855
  context: match.context,
1646
- location: state.location,
1647
- navigate: opts => navigate({
1856
+ location: this.state.location,
1857
+ navigate: opts => this.navigate({
1648
1858
  ...opts,
1649
1859
  from: match.pathname
1650
1860
  }),
1651
- cause
1861
+ cause: match.cause
1652
1862
  };
1653
1863
 
1654
1864
  // Default to reloading the route all the time
1655
1865
  let shouldReload = true;
1656
1866
  let shouldReloadDeps = typeof route.options.shouldReload === 'function' ? route.options.shouldReload?.(loaderContext) : !!(route.options.shouldReload ?? true);
1657
- if (cause === 'enter') {
1867
+ if (match.cause === 'enter') {
1658
1868
  match.shouldReloadDeps = shouldReloadDeps;
1659
- } else if (cause === 'stay') {
1869
+ } else if (match.cause === 'stay') {
1660
1870
  if (typeof shouldReloadDeps === 'object') {
1661
1871
  // compare the deps to see if they've changed
1662
1872
  shouldReload = !deepEqual(shouldReloadDeps, match.shouldReloadDeps);
@@ -1692,7 +1902,7 @@ function RouterProvider({
1692
1902
  loadPromise
1693
1903
  };
1694
1904
  if (!preload) {
1695
- setState(s => ({
1905
+ this.setState(s => ({
1696
1906
  ...s,
1697
1907
  matches: s.matches.map(d => d.id === match.id ? match : d)
1698
1908
  }));
@@ -1727,7 +1937,7 @@ function RouterProvider({
1727
1937
  };
1728
1938
  }
1729
1939
  if (!preload) {
1730
- setState(s => ({
1940
+ this.setState(s => ({
1731
1941
  ...s,
1732
1942
  matches: s.matches.map(d => d.id === match.id ? match : d)
1733
1943
  }));
@@ -1736,17 +1946,17 @@ function RouterProvider({
1736
1946
  });
1737
1947
  await Promise.all(matchPromises);
1738
1948
  return matches;
1739
- });
1740
- const load = useStableCallback(async () => {
1949
+ };
1950
+ load = async () => {
1741
1951
  const promise = new Promise(async (resolve, reject) => {
1742
- const next = latestLocationRef.current;
1743
- const prevLocation = state.resolvedLocation;
1952
+ const next = this.latestLocation;
1953
+ const prevLocation = this.state.resolvedLocation;
1744
1954
  const pathDidChange = prevLocation.href !== next.href;
1745
1955
  let latestPromise;
1746
1956
 
1747
1957
  // Cancel any pending matches
1748
- cancelMatches(state);
1749
- router.emit({
1958
+ this.cancelMatches();
1959
+ this.emit({
1750
1960
  type: 'onBeforeLoad',
1751
1961
  fromLocation: prevLocation,
1752
1962
  toLocation: next,
@@ -1754,14 +1964,14 @@ function RouterProvider({
1754
1964
  });
1755
1965
 
1756
1966
  // Match the routes
1757
- let matches = matchRoutes(next.pathname, next.search, {
1967
+ let matches = this.matchRoutes(next.pathname, next.search, {
1758
1968
  debug: true
1759
1969
  });
1760
- pendingMatchesRef.current = matches;
1761
- const previousMatches = state.matches;
1970
+ this.pendingMatches = matches;
1971
+ const previousMatches = this.state.matches;
1762
1972
 
1763
1973
  // Ingest the new matches
1764
- setState(s => ({
1974
+ this.setState(s => ({
1765
1975
  ...s,
1766
1976
  status: 'pending',
1767
1977
  location: next,
@@ -1770,9 +1980,9 @@ function RouterProvider({
1770
1980
  try {
1771
1981
  try {
1772
1982
  // Load the matches
1773
- await loadMatches({
1983
+ await this.loadMatches({
1774
1984
  matches,
1775
- checkLatest: () => checkLatest(promise)
1985
+ checkLatest: () => this.checkLatest(promise)
1776
1986
  });
1777
1987
  } catch (err) {
1778
1988
  // swallow this error, since we'll display the
@@ -1780,12 +1990,12 @@ function RouterProvider({
1780
1990
  }
1781
1991
 
1782
1992
  // Only apply the latest transition
1783
- if (latestPromise = checkLatest(promise)) {
1993
+ if (latestPromise = this.checkLatest(promise)) {
1784
1994
  return latestPromise;
1785
1995
  }
1786
- const exitingMatchIds = previousMatches.filter(id => !pendingMatchesRef.current.includes(id));
1787
- const enteringMatchIds = pendingMatchesRef.current.filter(id => !previousMatches.includes(id));
1788
- const stayingMatchIds = previousMatches.filter(id => pendingMatchesRef.current.includes(id))
1996
+ const exitingMatchIds = previousMatches.filter(id => !this.pendingMatches.includes(id));
1997
+ const enteringMatchIds = this.pendingMatches.filter(id => !previousMatches.includes(id));
1998
+ const stayingMatchIds = previousMatches.filter(id => this.pendingMatches.includes(id))
1789
1999
 
1790
2000
  // setState((s) => ({
1791
2001
  // ...s,
@@ -1797,10 +2007,10 @@ function RouterProvider({
1797
2007
  ;
1798
2008
  [[exitingMatchIds, 'onLeave'], [enteringMatchIds, 'onEnter'], [stayingMatchIds, 'onTransition']].forEach(([matches, hook]) => {
1799
2009
  matches.forEach(match => {
1800
- looseRoutesById[match.routeId].options[hook]?.(match);
2010
+ this.looseRoutesById[match.routeId].options[hook]?.(match);
1801
2011
  });
1802
2012
  });
1803
- router.emit({
2013
+ this.emit({
1804
2014
  type: 'onLoad',
1805
2015
  fromLocation: prevLocation,
1806
2016
  toLocation: next,
@@ -1809,28 +2019,28 @@ function RouterProvider({
1809
2019
  resolve();
1810
2020
  } catch (err) {
1811
2021
  // Only apply the latest transition
1812
- if (latestPromise = checkLatest(promise)) {
2022
+ if (latestPromise = this.checkLatest(promise)) {
1813
2023
  return latestPromise;
1814
2024
  }
1815
2025
  reject(err);
1816
2026
  }
1817
2027
  });
1818
- latestLoadPromiseRef.current = promise;
1819
- return latestLoadPromiseRef.current;
1820
- });
1821
- const preloadRoute = useStableCallback(async (navigateOpts = state.location) => {
1822
- let next = buildLocation(navigateOpts);
1823
- let matches = matchRoutes(next.pathname, next.search, {
2028
+ this.latestLoadPromise = promise;
2029
+ return this.latestLoadPromise;
2030
+ };
2031
+ preloadRoute = async (navigateOpts = this.state.location) => {
2032
+ let next = this.buildLocation(navigateOpts);
2033
+ let matches = this.matchRoutes(next.pathname, next.search, {
1824
2034
  throwOnError: true
1825
2035
  });
1826
- await loadMatches({
2036
+ await this.loadMatches({
1827
2037
  matches,
1828
2038
  preload: true,
1829
2039
  checkLatest: () => undefined
1830
2040
  });
1831
2041
  return [last(matches), matches];
1832
- });
1833
- const buildLink = useStableCallback(dest => {
2042
+ };
2043
+ buildLink = dest => {
1834
2044
  // If this link simply reloads the current route,
1835
2045
  // make sure it has a new key so it will trigger a data refresh
1836
2046
 
@@ -1856,18 +2066,18 @@ function RouterProvider({
1856
2066
  };
1857
2067
  } catch (e) {}
1858
2068
  const nextOpts = dest;
1859
- const next = buildLocation(nextOpts);
1860
- const preload = userPreload ?? options.defaultPreload;
1861
- const preloadDelay = userPreloadDelay ?? options.defaultPreloadDelay ?? 0;
2069
+ const next = this.buildLocation(nextOpts);
2070
+ const preload = userPreload ?? this.options.defaultPreload;
2071
+ const preloadDelay = userPreloadDelay ?? this.options.defaultPreloadDelay ?? 0;
1862
2072
 
1863
2073
  // Compare path/hash for matches
1864
- const currentPathSplit = latestLocationRef.current.pathname.split('/');
2074
+ const currentPathSplit = this.latestLocation.pathname.split('/');
1865
2075
  const nextPathSplit = next.pathname.split('/');
1866
2076
  const pathIsFuzzyEqual = nextPathSplit.every((d, i) => d === currentPathSplit[i]);
1867
- // Combine the matches based on user options
1868
- const pathTest = activeOptions?.exact ? latestLocationRef.current.pathname === next.pathname : pathIsFuzzyEqual;
1869
- const hashTest = activeOptions?.includeHash ? latestLocationRef.current.hash === next.hash : true;
1870
- const searchTest = activeOptions?.includeSearch ?? true ? deepEqual(latestLocationRef.current.search, next.search, true) : true;
2077
+ // Combine the matches based on user this.options
2078
+ const pathTest = activeOptions?.exact ? this.latestLocation.pathname === next.pathname : pathIsFuzzyEqual;
2079
+ const hashTest = activeOptions?.includeHash ? this.latestLocation.hash === next.hash : true;
2080
+ const searchTest = activeOptions?.includeSearch ?? true ? deepEqual(this.latestLocation.search, next.search, true) : true;
1871
2081
 
1872
2082
  // The final "active" test
1873
2083
  const isActive = pathTest && hashTest && searchTest;
@@ -1878,7 +2088,7 @@ function RouterProvider({
1878
2088
  e.preventDefault();
1879
2089
 
1880
2090
  // All is well? Navigate!
1881
- commitLocation({
2091
+ this.commitLocation({
1882
2092
  ...next,
1883
2093
  replace,
1884
2094
  resetScroll,
@@ -1890,384 +2100,184 @@ function RouterProvider({
1890
2100
  // The click handler
1891
2101
  const handleFocus = e => {
1892
2102
  if (preload) {
1893
- preloadRoute(nextOpts).catch(err => {
2103
+ this.preloadRoute(nextOpts).catch(err => {
1894
2104
  console.warn(err);
1895
2105
  console.warn(preloadWarning);
1896
2106
  });
1897
2107
  }
1898
2108
  };
1899
2109
  const handleTouchStart = e => {
1900
- preloadRoute(nextOpts).catch(err => {
2110
+ this.preloadRoute(nextOpts).catch(err => {
1901
2111
  console.warn(err);
1902
2112
  console.warn(preloadWarning);
1903
2113
  });
1904
2114
  };
1905
- const handleEnter = e => {
1906
- const target = e.target || {};
1907
- if (preload) {
1908
- if (target.preloadTimeout) {
1909
- return;
1910
- }
1911
- target.preloadTimeout = setTimeout(() => {
1912
- target.preloadTimeout = null;
1913
- preloadRoute(nextOpts).catch(err => {
1914
- console.warn(err);
1915
- console.warn(preloadWarning);
1916
- });
1917
- }, preloadDelay);
1918
- }
1919
- };
1920
- const handleLeave = e => {
1921
- const target = e.target || {};
1922
- if (target.preloadTimeout) {
1923
- clearTimeout(target.preloadTimeout);
1924
- target.preloadTimeout = null;
1925
- }
1926
- };
1927
- return {
1928
- type: 'internal',
1929
- next,
1930
- handleFocus,
1931
- handleClick,
1932
- handleEnter,
1933
- handleLeave,
1934
- handleTouchStart,
1935
- isActive,
1936
- disabled
1937
- };
1938
- });
1939
- React.useLayoutEffect(() => {
1940
- const unsub = history.subscribe(() => {
1941
- latestLocationRef.current = parseLocation(latestLocationRef.current);
1942
- if (state.location !== latestLocationRef.current) {
1943
- startReactTransition(() => {
1944
- try {
1945
- load();
1946
- } catch (err) {
1947
- console.error(err);
1948
- }
1949
- });
1950
- }
1951
- });
1952
- const nextLocation = buildLocation({
1953
- search: true,
1954
- params: true,
1955
- hash: true,
1956
- state: true
1957
- });
1958
- if (state.location.href !== nextLocation.href) {
1959
- commitLocation({
1960
- ...nextLocation,
1961
- replace: true
1962
- });
1963
- }
1964
- return () => {
1965
- unsub();
1966
- };
1967
- }, [history]);
1968
- const matchRoute = useStableCallback((location, opts) => {
1969
- location = {
1970
- ...location,
1971
- to: location.to ? resolvePathWithBase(location.from || '', location.to) : undefined
1972
- };
1973
- const next = buildLocation(location);
1974
- if (opts?.pending && state.status !== 'pending') {
1975
- return false;
1976
- }
1977
- const baseLocation = opts?.pending ? latestLocationRef.current : state.resolvedLocation;
1978
-
1979
- // const baseLocation = state.resolvedLocation
1980
-
1981
- if (!baseLocation) {
1982
- return false;
1983
- }
1984
- const match = matchPathname(basepath, baseLocation.pathname, {
1985
- ...opts,
1986
- to: next.pathname
1987
- });
1988
- if (!match) {
1989
- return false;
1990
- }
1991
- if (match && (opts?.includeSearch ?? true)) {
1992
- return deepEqual(baseLocation.search, next.search, true) ? match : false;
1993
- }
1994
- return match;
1995
- });
1996
- const injectedHtmlRef = React.useRef([]);
1997
- const injectHtml = useStableCallback(async html => {
1998
- injectedHtmlRef.current.push(html);
1999
- });
2000
- const dehydrateData = useStableCallback((key, getData) => {
2001
- if (typeof document === 'undefined') {
2002
- const strKey = typeof key === 'string' ? key : JSON.stringify(key);
2003
- injectHtml(async () => {
2004
- const id = `__TSR_DEHYDRATED__${strKey}`;
2005
- const data = typeof getData === 'function' ? await getData() : getData;
2006
- return `<script id='${id}' suppressHydrationWarning>window["__TSR_DEHYDRATED__${escapeJSON(strKey)}"] = ${JSON.stringify(data)}
2007
- ;(() => {
2008
- var el = document.getElementById('${id}')
2009
- el.parentElement.removeChild(el)
2010
- })()
2011
- </script>`;
2012
- });
2013
- return () => hydrateData(key);
2014
- }
2015
- return () => undefined;
2016
- });
2017
- const hydrateData = useStableCallback(key => {
2018
- if (typeof document !== 'undefined') {
2019
- const strKey = typeof key === 'string' ? key : JSON.stringify(key);
2020
- return window[`__TSR_DEHYDRATED__${strKey}`];
2021
- }
2022
- return undefined;
2023
- });
2024
- React.useLayoutEffect(() => {
2025
- startReactTransition(() => {
2026
- try {
2027
- load();
2028
- } catch (err) {
2029
- console.error(err);
2030
- }
2031
- });
2032
- }, []);
2033
- const routerContextValue = {
2034
- routeTree: router.routeTree,
2035
- navigate,
2036
- buildLink,
2037
- state,
2038
- matchRoute,
2039
- routesById,
2040
- options,
2041
- history,
2042
- load,
2043
- buildLocation,
2044
- subscribe: router.subscribe,
2045
- resetNextScrollRef,
2046
- injectedHtmlRef,
2047
- injectHtml,
2048
- dehydrateData,
2049
- hydrateData
2050
- };
2051
- return /*#__PURE__*/React.createElement(routerContext.Provider, {
2052
- value: routerContextValue
2053
- }, /*#__PURE__*/React.createElement(Matches, null));
2054
- }
2055
- function getRouteMatch(state, id) {
2056
- return [...state.pendingMatches, ...state.matches].find(d => d.id === id);
2057
- }
2058
- function useRouterState(opts) {
2059
- const {
2060
- state
2061
- } = useRouter();
2062
- // return useStore(router.__store, opts?.select as any)
2063
- return opts?.select ? opts.select(state) : state;
2064
- }
2065
- function useRouter() {
2066
- const resolvedContext = window.__TSR_ROUTER_CONTEXT__ || routerContext;
2067
- const value = React.useContext(resolvedContext);
2068
- warning(value, 'useRouter must be used inside a <RouterProvider> component!');
2069
- return value;
2070
- }
2071
-
2072
- function defer(_promise) {
2073
- const promise = _promise;
2074
- if (!promise.__deferredState) {
2075
- promise.__deferredState = {
2076
- uid: Math.random().toString(36).slice(2),
2077
- status: 'pending'
2078
- };
2079
- const state = promise.__deferredState;
2080
- promise.then(data => {
2081
- state.status = 'success';
2082
- state.data = data;
2083
- }).catch(error => {
2084
- state.status = 'error';
2085
- state.error = error;
2086
- });
2087
- }
2088
- return promise;
2089
- }
2090
- function isDehydratedDeferred(obj) {
2091
- return typeof obj === 'object' && obj !== null && !(obj instanceof Promise) && !obj.then && '__deferredState' in obj;
2092
- }
2093
-
2094
- function useAwaited({
2095
- promise
2096
- }) {
2097
- const router = useRouter();
2098
- let state = promise.__deferredState;
2099
- const key = `__TSR__DEFERRED__${state.uid}`;
2100
- if (isDehydratedDeferred(promise)) {
2101
- state = router.hydrateData(key);
2102
- promise = Promise.resolve(state.data);
2103
- promise.__deferredState = state;
2104
- }
2105
- if (state.status === 'pending') {
2106
- throw promise;
2107
- }
2108
- if (state.status === 'error') {
2109
- throw state.error;
2110
- }
2111
- router.dehydrateData(key, state);
2112
- return [state.data];
2113
- }
2114
- function Await(props) {
2115
- const awaited = useAwaited(props);
2116
- return props.children(...awaited);
2117
- }
2118
-
2119
- class FileRoute {
2120
- constructor(path) {
2121
- this.path = path;
2122
- }
2123
- createRoute = options => {
2124
- const route = new Route(options);
2125
- route.isRoot = false;
2126
- return route;
2127
- };
2128
- }
2129
-
2130
- function lazyRouteComponent(importer, exportName) {
2131
- let loadPromise;
2132
- const load = () => {
2133
- if (!loadPromise) {
2134
- loadPromise = importer();
2135
- }
2136
- return loadPromise;
2137
- };
2138
- const lazyComp = /*#__PURE__*/React.lazy(async () => {
2139
- const moduleExports = await load();
2140
- const comp = moduleExports[exportName ?? 'default'];
2141
- return {
2142
- default: comp
2143
- };
2144
- });
2145
- lazyComp.preload = load;
2146
- return lazyComp;
2147
- }
2148
-
2149
- function _extends() {
2150
- _extends = Object.assign ? Object.assign.bind() : function (target) {
2151
- for (var i = 1; i < arguments.length; i++) {
2152
- var source = arguments[i];
2153
- for (var key in source) {
2154
- if (Object.prototype.hasOwnProperty.call(source, key)) {
2155
- target[key] = source[key];
2115
+ const handleEnter = e => {
2116
+ const target = e.target || {};
2117
+ if (preload) {
2118
+ if (target.preloadTimeout) {
2119
+ return;
2156
2120
  }
2121
+ target.preloadTimeout = setTimeout(() => {
2122
+ target.preloadTimeout = null;
2123
+ this.preloadRoute(nextOpts).catch(err => {
2124
+ console.warn(err);
2125
+ console.warn(preloadWarning);
2126
+ });
2127
+ }, preloadDelay);
2157
2128
  }
2158
- }
2159
- return target;
2160
- };
2161
- return _extends.apply(this, arguments);
2162
- }
2163
-
2164
- function useLinkProps(options) {
2165
- const {
2166
- buildLink
2167
- } = useRouter();
2168
- const match = useMatch({
2169
- strict: false
2170
- });
2171
- const {
2172
- // custom props
2173
- type,
2174
- children,
2175
- target,
2176
- activeProps = () => ({
2177
- className: 'active'
2178
- }),
2179
- inactiveProps = () => ({}),
2180
- activeOptions,
2181
- disabled,
2182
- hash,
2183
- search,
2184
- params,
2185
- to,
2186
- state,
2187
- mask,
2188
- preload,
2189
- preloadDelay,
2190
- replace,
2191
- startTransition,
2192
- resetScroll,
2193
- // element props
2194
- style,
2195
- className,
2196
- onClick,
2197
- onFocus,
2198
- onMouseEnter,
2199
- onMouseLeave,
2200
- onTouchStart,
2201
- ...rest
2202
- } = options;
2203
- const linkInfo = buildLink({
2204
- from: options.to ? match.pathname : undefined,
2205
- ...options
2206
- });
2207
- if (linkInfo.type === 'external') {
2208
- const {
2209
- href
2210
- } = linkInfo;
2129
+ };
2130
+ const handleLeave = e => {
2131
+ const target = e.target || {};
2132
+ if (target.preloadTimeout) {
2133
+ clearTimeout(target.preloadTimeout);
2134
+ target.preloadTimeout = null;
2135
+ }
2136
+ };
2211
2137
  return {
2212
- href
2138
+ type: 'internal',
2139
+ next,
2140
+ handleFocus,
2141
+ handleClick,
2142
+ handleEnter,
2143
+ handleLeave,
2144
+ handleTouchStart,
2145
+ isActive,
2146
+ disabled
2213
2147
  };
2214
- }
2215
- const {
2216
- handleClick,
2217
- handleFocus,
2218
- handleEnter,
2219
- handleLeave,
2220
- handleTouchStart,
2221
- isActive,
2222
- next
2223
- } = linkInfo;
2224
- const composeHandlers = handlers => e => {
2225
- if (e.persist) e.persist();
2226
- handlers.filter(Boolean).forEach(handler => {
2227
- if (e.defaultPrevented) return;
2228
- handler(e);
2148
+ };
2149
+ matchRoute = (location, opts) => {
2150
+ location = {
2151
+ ...location,
2152
+ to: location.to ? this.resolvePathWithBase(location.from || '', location.to) : undefined
2153
+ };
2154
+ const next = this.buildLocation(location);
2155
+ if (opts?.pending && this.state.status !== 'pending') {
2156
+ return false;
2157
+ }
2158
+ const baseLocation = opts?.pending ? this.latestLocation : this.state.resolvedLocation;
2159
+
2160
+ // const baseLocation = state.resolvedLocation
2161
+
2162
+ if (!baseLocation) {
2163
+ return false;
2164
+ }
2165
+ const match = matchPathname(this.basepath, baseLocation.pathname, {
2166
+ ...opts,
2167
+ to: next.pathname
2229
2168
  });
2169
+ if (!match) {
2170
+ return false;
2171
+ }
2172
+ if (match && (opts?.includeSearch ?? true)) {
2173
+ return deepEqual(baseLocation.search, next.search, true) ? match : false;
2174
+ }
2175
+ return match;
2176
+ };
2177
+ injectHtml = async html => {
2178
+ this.injectedHtml.push(html);
2179
+ };
2180
+ dehydrateData = (key, getData) => {
2181
+ if (typeof document === 'undefined') {
2182
+ const strKey = typeof key === 'string' ? key : JSON.stringify(key);
2183
+ this.injectHtml(async () => {
2184
+ const id = `__TSR_DEHYDRATED__${strKey}`;
2185
+ const data = typeof getData === 'function' ? await getData() : getData;
2186
+ return `<script id='${id}' suppressHydrationWarning>window["__TSR_DEHYDRATED__${escapeJSON(strKey)}"] = ${JSON.stringify(data)}
2187
+ ;(() => {
2188
+ var el = document.getElementById('${id}')
2189
+ el.parentElement.removeChild(el)
2190
+ })()
2191
+ </script>`;
2192
+ });
2193
+ return () => this.hydrateData(key);
2194
+ }
2195
+ return () => undefined;
2196
+ };
2197
+ hydrateData = key => {
2198
+ if (typeof document !== 'undefined') {
2199
+ const strKey = typeof key === 'string' ? key : JSON.stringify(key);
2200
+ return window[`__TSR_DEHYDRATED__${strKey}`];
2201
+ }
2202
+ return undefined;
2230
2203
  };
2231
2204
 
2232
- // Get the active props
2233
- const resolvedActiveProps = isActive ? functionalUpdate(activeProps, {}) ?? {} : {};
2205
+ // dehydrate = (): DehydratedRouter => {
2206
+ // return {
2207
+ // state: {
2208
+ // dehydratedMatches: this.state.matches.map((d) =>
2209
+ // pick(d, ['fetchedAt', 'invalid', 'id', 'status', 'updatedAt']),
2210
+ // ),
2211
+ // },
2212
+ // }
2213
+ // }
2234
2214
 
2235
- // Get the inactive props
2236
- const resolvedInactiveProps = isActive ? {} : functionalUpdate(inactiveProps, {}) ?? {};
2237
- return {
2238
- ...resolvedActiveProps,
2239
- ...resolvedInactiveProps,
2240
- ...rest,
2241
- href: disabled ? undefined : next.maskedLocation ? next.maskedLocation.href : next.href,
2242
- onClick: composeHandlers([onClick, handleClick]),
2243
- onFocus: composeHandlers([onFocus, handleFocus]),
2244
- onMouseEnter: composeHandlers([onMouseEnter, handleEnter]),
2245
- onMouseLeave: composeHandlers([onMouseLeave, handleLeave]),
2246
- onTouchStart: composeHandlers([onTouchStart, handleTouchStart]),
2247
- target,
2248
- style: {
2249
- ...style,
2250
- ...resolvedActiveProps.style,
2251
- ...resolvedInactiveProps.style
2252
- },
2253
- className: [className, resolvedActiveProps.className, resolvedInactiveProps.className].filter(Boolean).join(' ') || undefined,
2254
- ...(disabled ? {
2255
- role: 'link',
2256
- 'aria-disabled': true
2257
- } : undefined),
2258
- ['data-status']: isActive ? 'active' : undefined
2215
+ // hydrate = async (__do_not_use_server_ctx?: HydrationCtx) => {
2216
+ // let _ctx = __do_not_use_server_ctx
2217
+ // // Client hydrates from window
2218
+ // if (typeof document !== 'undefined') {
2219
+ // _ctx = window.__TSR_DEHYDRATED__
2220
+ // }
2221
+
2222
+ // invariant(
2223
+ // _ctx,
2224
+ // 'Expected to find a __TSR_DEHYDRATED__ property on window... but we did not. Did you forget to render <DehydrateRouter /> in your app?',
2225
+ // )
2226
+
2227
+ // const ctx = _ctx
2228
+ // this.dehydratedData = ctx.payload as any
2229
+ // this.options.hydrate?.(ctx.payload as any)
2230
+ // const dehydratedState = ctx.router.state
2231
+
2232
+ // let matches = this.matchRoutes(
2233
+ // this.state.location.pathname,
2234
+ // this.state.location.search,
2235
+ // ).map((match) => {
2236
+ // const dehydratedMatch = dehydratedState.dehydratedMatches.find(
2237
+ // (d) => d.id === match.id,
2238
+ // )
2239
+
2240
+ // invariant(
2241
+ // dehydratedMatch,
2242
+ // `Could not find a client-side match for dehydrated match with id: ${match.id}!`,
2243
+ // )
2244
+
2245
+ // if (dehydratedMatch) {
2246
+ // return {
2247
+ // ...match,
2248
+ // ...dehydratedMatch,
2249
+ // }
2250
+ // }
2251
+ // return match
2252
+ // })
2253
+
2254
+ // this.setState((s) => {
2255
+ // return {
2256
+ // ...s,
2257
+ // matches: dehydratedState.dehydratedMatches as any,
2258
+ // }
2259
+ // })
2260
+ // }
2261
+
2262
+ // resolveMatchPromise = (matchId: string, key: string, value: any) => {
2263
+ // state.matches
2264
+ // .find((d) => d.id === matchId)
2265
+ // ?.__promisesByKey[key]?.resolve(value)
2266
+ // }
2267
+ }
2268
+
2269
+ // A function that takes an import() argument which is a function and returns a new function that will
2270
+ // proxy arguments from the caller to the imported function, retaining all type
2271
+ // information along the way
2272
+ function lazyFn(fn, key) {
2273
+ return async (...args) => {
2274
+ const imported = await fn();
2275
+ return imported[key || 'default'](...args);
2259
2276
  };
2260
2277
  }
2261
- const Link = /*#__PURE__*/React.forwardRef((props, ref) => {
2262
- const linkProps = useLinkProps(props);
2263
- return /*#__PURE__*/React.createElement("a", _extends({
2264
- ref: ref
2265
- }, linkProps, {
2266
- children: typeof props.children === 'function' ? props.children({
2267
- isActive: linkProps['data-status'] === 'active'
2268
- }) : props.children
2269
- }));
2270
- });
2278
+ function isCtrlEvent(e) {
2279
+ return !!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey);
2280
+ }
2271
2281
 
2272
2282
  const useLayoutEffect = typeof window !== 'undefined' ? React.useLayoutEffect : React.useEffect;
2273
2283
  const windowKey = 'window';
@@ -2277,11 +2287,7 @@ let cache;
2277
2287
  const sessionsStorage = typeof window !== 'undefined' && window.sessionStorage;
2278
2288
  const defaultGetKey = location => location.state.key;
2279
2289
  function useScrollRestoration(options) {
2280
- const {
2281
- state,
2282
- subscribe,
2283
- resetNextScrollRef
2284
- } = useRouter();
2290
+ const router = useRouter();
2285
2291
  useLayoutEffect(() => {
2286
2292
  const getKey = options?.getKey || defaultGetKey;
2287
2293
  if (sessionsStorage) {
@@ -2337,7 +2343,7 @@ function useScrollRestoration(options) {
2337
2343
  if (typeof document !== 'undefined') {
2338
2344
  document.addEventListener('scroll', onScroll, true);
2339
2345
  }
2340
- const unsubOnBeforeLoad = subscribe('onBeforeLoad', event => {
2346
+ const unsubOnBeforeLoad = router.subscribe('onBeforeLoad', event => {
2341
2347
  if (event.pathChanged) {
2342
2348
  const restoreKey = getKey(event.fromLocation);
2343
2349
  for (const elementSelector in cache.state.next) {
@@ -2367,12 +2373,12 @@ function useScrollRestoration(options) {
2367
2373
  }
2368
2374
  }
2369
2375
  });
2370
- const unsubOnResolved = subscribe('onResolved', event => {
2376
+ const unsubOnResolved = router.subscribe('onResolved', event => {
2371
2377
  if (event.pathChanged) {
2372
- if (!resetNextScrollRef.current) {
2378
+ if (!router.resetNextScroll) {
2373
2379
  return;
2374
2380
  }
2375
- resetNextScrollRef.current = true;
2381
+ router.resetNextScroll = true;
2376
2382
  const getKey = options?.getKey || defaultGetKey;
2377
2383
  const restoreKey = getKey(event.toLocation);
2378
2384
  let windowRestored = false;