@tanstack/react-router 0.0.1-beta.223 → 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,8 +1483,8 @@ 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);
1281
- const cause = state.matches.find(d => d.id === matchId) ? 'stay' : 'enter';
1486
+ const existingMatch = getRouteMatch(this.state, matchId);
1487
+ const cause = this.state.matches.find(d => d.id === matchId) ? 'stay' : 'enter';
1282
1488
  if (existingMatch) {
1283
1489
  return {
1284
1490
  ...existingMatch,
@@ -1292,7 +1498,7 @@ function RouterProvider({
1292
1498
  id: matchId,
1293
1499
  routeId: route.id,
1294
1500
  params: routeParams,
1295
- pathname: joinPaths([basepath, interpolatedPath]),
1501
+ pathname: joinPaths([this.basepath, interpolatedPath]),
1296
1502
  updatedAt: Date.now(),
1297
1503
  routeSearch: {},
1298
1504
  search: {},
@@ -1317,7 +1523,7 @@ function RouterProvider({
1317
1523
  // so that we can use the parent match's search params and context
1318
1524
  matches.forEach((match, i) => {
1319
1525
  const parentMatch = matches[i - 1];
1320
- const route = looseRoutesById[match.routeId];
1526
+ const route = this.looseRoutesById[match.routeId];
1321
1527
  const searchInfo = (() => {
1322
1528
  // Validate the search params and stabilize them
1323
1529
  const parentSearchInfo = {
@@ -1351,28 +1557,28 @@ function RouterProvider({
1351
1557
  Object.assign(match, searchInfo);
1352
1558
  });
1353
1559
  return matches;
1354
- });
1355
- const cancelMatch = useStableCallback(id => {
1356
- getRouteMatch(state, id)?.abortController?.abort();
1357
- });
1358
- const cancelMatches = useStableCallback(state => {
1359
- state.matches.forEach(match => {
1360
- 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);
1361
1567
  });
1362
- });
1363
- const buildLocation = useStableCallback(opts => {
1568
+ };
1569
+ buildLocation = opts => {
1364
1570
  const build = (dest = {}, matches) => {
1365
- const from = latestLocationRef.current;
1571
+ const from = this.latestLocation;
1366
1572
  const fromPathname = dest.from ?? from.pathname;
1367
- let pathname = resolvePathWithBase(fromPathname, `${dest.to ?? ''}`);
1368
- const fromMatches = matchRoutes(fromPathname, from.search);
1573
+ let pathname = this.resolvePathWithBase(fromPathname, `${dest.to ?? ''}`);
1574
+ const fromMatches = this.matchRoutes(fromPathname, from.search);
1369
1575
  const stayingMatches = matches?.filter(d => fromMatches?.find(e => e.routeId === d.routeId));
1370
1576
  const prevParams = {
1371
1577
  ...last(fromMatches)?.params
1372
1578
  };
1373
1579
  let nextParams = (dest.params ?? true) === true ? prevParams : functionalUpdate(dest.params, prevParams);
1374
1580
  if (nextParams) {
1375
- 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 => {
1376
1582
  nextParams = {
1377
1583
  ...nextParams,
1378
1584
  ...fn(nextParams)
@@ -1380,8 +1586,8 @@ function RouterProvider({
1380
1586
  });
1381
1587
  }
1382
1588
  pathname = interpolatePath(pathname, nextParams ?? {});
1383
- const preSearchFilters = stayingMatches?.map(match => looseRoutesById[match.routeId].options.preSearchFilters ?? []).flat().filter(Boolean) ?? [];
1384
- 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) ?? [];
1385
1591
 
1386
1592
  // Pre filters first
1387
1593
  const preFilteredSearch = preSearchFilters?.length ? preSearchFilters?.reduce((prev, next) => next(prev), from.search) : from.search;
@@ -1395,7 +1601,7 @@ function RouterProvider({
1395
1601
  // Then post filters
1396
1602
  const postFilteredSearch = postSearchFilters?.length ? postSearchFilters.reduce((prev, next) => next(prev), destSearch) : destSearch;
1397
1603
  const search = replaceEqualDeep(from.search, postFilteredSearch);
1398
- const searchStr = options.stringifySearch(search);
1604
+ const searchStr = this.options.stringifySearch(search);
1399
1605
  const hash = dest.hash === true ? from.hash : dest.hash ? functionalUpdate(dest.hash, from.hash) : from.hash;
1400
1606
  const hashStr = hash ? `#${hash}` : '';
1401
1607
  let nextState = dest.state === true ? from.state : dest.state ? functionalUpdate(dest.state, from.state) : from.state;
@@ -1406,7 +1612,7 @@ function RouterProvider({
1406
1612
  searchStr,
1407
1613
  state: nextState,
1408
1614
  hash,
1409
- href: history.createHref(`${pathname}${searchStr}${hashStr}`),
1615
+ href: this.history.createHref(`${pathname}${searchStr}${hashStr}`),
1410
1616
  unmaskOnReload: dest.unmaskOnReload
1411
1617
  };
1412
1618
  };
@@ -1415,8 +1621,8 @@ function RouterProvider({
1415
1621
  let maskedNext = maskedDest ? build(maskedDest) : undefined;
1416
1622
  if (!maskedNext) {
1417
1623
  let params = {};
1418
- let foundMask = options.routeMasks?.find(d => {
1419
- const match = matchPathname(basepath, next.pathname, {
1624
+ let foundMask = this.options.routeMasks?.find(d => {
1625
+ const match = matchPathname(this.basepath, next.pathname, {
1420
1626
  to: d.from,
1421
1627
  caseSensitive: false,
1422
1628
  fuzzy: false
@@ -1436,8 +1642,8 @@ function RouterProvider({
1436
1642
  maskedNext = build(maskedDest);
1437
1643
  }
1438
1644
  }
1439
- const nextMatches = matchRoutes(next.pathname, next.search);
1440
- 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;
1441
1647
  const maskedFinal = maskedNext ? build(maskedDest, maskedMatches) : undefined;
1442
1648
  const final = build(dest, nextMatches);
1443
1649
  if (maskedFinal) {
@@ -1452,13 +1658,13 @@ function RouterProvider({
1452
1658
  });
1453
1659
  }
1454
1660
  return buildWithMatches(opts);
1455
- });
1456
- const commitLocation = useStableCallback(async ({
1661
+ };
1662
+ commitLocation = async ({
1457
1663
  startTransition,
1458
1664
  ...next
1459
1665
  }) => {
1460
- if (navigateTimeoutRef.current) clearTimeout(navigateTimeoutRef.current);
1461
- const isSameUrl = latestLocationRef.current.href === next.href;
1666
+ if (this.navigateTimeout) clearTimeout(this.navigateTimeout);
1667
+ const isSameUrl = this.latestLocation.href === next.href;
1462
1668
 
1463
1669
  // If the next urls are the same and we're not replacing,
1464
1670
  // do nothing
@@ -1485,37 +1691,37 @@ function RouterProvider({
1485
1691
  }
1486
1692
  }
1487
1693
  };
1488
- if (nextHistory.unmaskOnReload ?? options.unmaskOnReload ?? false) {
1489
- nextHistory.state.__tempKey = tempLocationKeyRef.current;
1694
+ if (nextHistory.unmaskOnReload ?? this.options.unmaskOnReload ?? false) {
1695
+ nextHistory.state.__tempKey = this.tempLocationKey;
1490
1696
  }
1491
1697
  }
1492
1698
  const apply = () => {
1493
- history[next.replace ? 'replace' : 'push'](nextHistory.href, nextHistory.state);
1699
+ this.history[next.replace ? 'replace' : 'push'](nextHistory.href, nextHistory.state);
1494
1700
  };
1495
1701
  if (startTransition ?? true) {
1496
- startReactTransition(apply);
1702
+ this.startReactTransition(apply);
1497
1703
  } else {
1498
1704
  apply();
1499
1705
  }
1500
1706
  }
1501
- resetNextScrollRef.current = next.resetScroll ?? true;
1502
- return latestLoadPromiseRef.current;
1503
- });
1504
- const buildAndCommitLocation = useStableCallback(({
1707
+ this.resetNextScroll = next.resetScroll ?? true;
1708
+ return this.latestLoadPromise;
1709
+ };
1710
+ buildAndCommitLocation = ({
1505
1711
  replace,
1506
1712
  resetScroll,
1507
1713
  startTransition,
1508
1714
  ...rest
1509
1715
  } = {}) => {
1510
- const location = buildLocation(rest);
1511
- return commitLocation({
1716
+ const location = this.buildLocation(rest);
1717
+ return this.commitLocation({
1512
1718
  ...location,
1513
1719
  startTransition,
1514
1720
  replace,
1515
1721
  resetScroll
1516
1722
  });
1517
- });
1518
- const navigate = useStableCallback(({
1723
+ };
1724
+ navigate = ({
1519
1725
  from,
1520
1726
  to = '',
1521
1727
  ...rest
@@ -1533,13 +1739,13 @@ function RouterProvider({
1533
1739
  isExternal = true;
1534
1740
  } catch (e) {}
1535
1741
  invariant(!isExternal, 'Attempting to navigate to external url with this.navigate!');
1536
- return buildAndCommitLocation({
1742
+ return this.buildAndCommitLocation({
1537
1743
  ...rest,
1538
1744
  from: fromString,
1539
1745
  to: toString
1540
1746
  });
1541
- });
1542
- const loadMatches = useStableCallback(async ({
1747
+ };
1748
+ loadMatches = async ({
1543
1749
  checkLatest,
1544
1750
  matches,
1545
1751
  preload
@@ -1551,7 +1757,7 @@ function RouterProvider({
1551
1757
  try {
1552
1758
  for (let [index, match] of matches.entries()) {
1553
1759
  const parentMatch = matches[index - 1];
1554
- const route = looseRoutesById[match.routeId];
1760
+ const route = this.looseRoutesById[match.routeId];
1555
1761
  const handleError = (err, code) => {
1556
1762
  err.routerCode = code;
1557
1763
  firstBadMatchIndex = firstBadMatchIndex ?? index;
@@ -1580,19 +1786,20 @@ function RouterProvider({
1580
1786
  if (match.searchError) {
1581
1787
  handleError(match.searchError, 'VALIDATE_SEARCH');
1582
1788
  }
1583
- const parentContext = parentMatch?.context ?? options.context ?? {};
1789
+ const parentContext = parentMatch?.context ?? this.options.context ?? {};
1584
1790
  const beforeLoadContext = (await route.options.beforeLoad?.({
1585
1791
  search: match.search,
1586
1792
  abortController: match.abortController,
1587
1793
  params: match.params,
1588
1794
  preload: !!preload,
1589
1795
  context: parentContext,
1590
- location: state.location,
1591
- navigate: opts => navigate({
1796
+ location: this.state.location,
1797
+ // TOOD: just expose state and router, etc
1798
+ navigate: opts => this.navigate({
1592
1799
  ...opts,
1593
1800
  from: match.pathname
1594
1801
  }),
1595
- buildLocation,
1802
+ buildLocation: this.buildLocation,
1596
1803
  cause: match.cause
1597
1804
  })) ?? {};
1598
1805
  const context = {
@@ -1610,7 +1817,7 @@ function RouterProvider({
1610
1817
  }
1611
1818
  } catch (err) {
1612
1819
  if (isRedirect(err)) {
1613
- if (!preload) navigate(err);
1820
+ if (!preload) this.navigate(err);
1614
1821
  return matches;
1615
1822
  }
1616
1823
  throw err;
@@ -1620,11 +1827,11 @@ function RouterProvider({
1620
1827
  validResolvedMatches.forEach((match, index) => {
1621
1828
  matchPromises.push((async () => {
1622
1829
  const parentMatchPromise = matchPromises[index - 1];
1623
- const route = looseRoutesById[match.routeId];
1830
+ const route = this.looseRoutesById[match.routeId];
1624
1831
  const handleIfRedirect = err => {
1625
1832
  if (isRedirect(err)) {
1626
1833
  if (!preload) {
1627
- navigate(err);
1834
+ this.navigate(err);
1628
1835
  }
1629
1836
  return true;
1630
1837
  }
@@ -1637,7 +1844,7 @@ function RouterProvider({
1637
1844
  invalid: false
1638
1845
  };
1639
1846
  if (match.isFetching) {
1640
- loadPromise = getRouteMatch(state, match.id)?.loadPromise;
1847
+ loadPromise = getRouteMatch(this.state, match.id)?.loadPromise;
1641
1848
  } else {
1642
1849
  const loaderContext = {
1643
1850
  params: match.params,
@@ -1646,8 +1853,8 @@ function RouterProvider({
1646
1853
  parentMatchPromise,
1647
1854
  abortController: match.abortController,
1648
1855
  context: match.context,
1649
- location: state.location,
1650
- navigate: opts => navigate({
1856
+ location: this.state.location,
1857
+ navigate: opts => this.navigate({
1651
1858
  ...opts,
1652
1859
  from: match.pathname
1653
1860
  }),
@@ -1695,7 +1902,7 @@ function RouterProvider({
1695
1902
  loadPromise
1696
1903
  };
1697
1904
  if (!preload) {
1698
- setState(s => ({
1905
+ this.setState(s => ({
1699
1906
  ...s,
1700
1907
  matches: s.matches.map(d => d.id === match.id ? match : d)
1701
1908
  }));
@@ -1730,7 +1937,7 @@ function RouterProvider({
1730
1937
  };
1731
1938
  }
1732
1939
  if (!preload) {
1733
- setState(s => ({
1940
+ this.setState(s => ({
1734
1941
  ...s,
1735
1942
  matches: s.matches.map(d => d.id === match.id ? match : d)
1736
1943
  }));
@@ -1739,17 +1946,17 @@ function RouterProvider({
1739
1946
  });
1740
1947
  await Promise.all(matchPromises);
1741
1948
  return matches;
1742
- });
1743
- const load = useStableCallback(async () => {
1949
+ };
1950
+ load = async () => {
1744
1951
  const promise = new Promise(async (resolve, reject) => {
1745
- const next = latestLocationRef.current;
1746
- const prevLocation = state.resolvedLocation;
1952
+ const next = this.latestLocation;
1953
+ const prevLocation = this.state.resolvedLocation;
1747
1954
  const pathDidChange = prevLocation.href !== next.href;
1748
1955
  let latestPromise;
1749
1956
 
1750
1957
  // Cancel any pending matches
1751
- cancelMatches(state);
1752
- router.emit({
1958
+ this.cancelMatches();
1959
+ this.emit({
1753
1960
  type: 'onBeforeLoad',
1754
1961
  fromLocation: prevLocation,
1755
1962
  toLocation: next,
@@ -1757,14 +1964,14 @@ function RouterProvider({
1757
1964
  });
1758
1965
 
1759
1966
  // Match the routes
1760
- let matches = matchRoutes(next.pathname, next.search, {
1967
+ let matches = this.matchRoutes(next.pathname, next.search, {
1761
1968
  debug: true
1762
1969
  });
1763
- pendingMatchesRef.current = matches;
1764
- const previousMatches = state.matches;
1970
+ this.pendingMatches = matches;
1971
+ const previousMatches = this.state.matches;
1765
1972
 
1766
1973
  // Ingest the new matches
1767
- setState(s => ({
1974
+ this.setState(s => ({
1768
1975
  ...s,
1769
1976
  status: 'pending',
1770
1977
  location: next,
@@ -1773,9 +1980,9 @@ function RouterProvider({
1773
1980
  try {
1774
1981
  try {
1775
1982
  // Load the matches
1776
- await loadMatches({
1983
+ await this.loadMatches({
1777
1984
  matches,
1778
- checkLatest: () => checkLatest(promise)
1985
+ checkLatest: () => this.checkLatest(promise)
1779
1986
  });
1780
1987
  } catch (err) {
1781
1988
  // swallow this error, since we'll display the
@@ -1783,12 +1990,12 @@ function RouterProvider({
1783
1990
  }
1784
1991
 
1785
1992
  // Only apply the latest transition
1786
- if (latestPromise = checkLatest(promise)) {
1993
+ if (latestPromise = this.checkLatest(promise)) {
1787
1994
  return latestPromise;
1788
1995
  }
1789
- const exitingMatchIds = previousMatches.filter(id => !pendingMatchesRef.current.includes(id));
1790
- const enteringMatchIds = pendingMatchesRef.current.filter(id => !previousMatches.includes(id));
1791
- 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))
1792
1999
 
1793
2000
  // setState((s) => ({
1794
2001
  // ...s,
@@ -1800,10 +2007,10 @@ function RouterProvider({
1800
2007
  ;
1801
2008
  [[exitingMatchIds, 'onLeave'], [enteringMatchIds, 'onEnter'], [stayingMatchIds, 'onTransition']].forEach(([matches, hook]) => {
1802
2009
  matches.forEach(match => {
1803
- looseRoutesById[match.routeId].options[hook]?.(match);
2010
+ this.looseRoutesById[match.routeId].options[hook]?.(match);
1804
2011
  });
1805
2012
  });
1806
- router.emit({
2013
+ this.emit({
1807
2014
  type: 'onLoad',
1808
2015
  fromLocation: prevLocation,
1809
2016
  toLocation: next,
@@ -1812,28 +2019,28 @@ function RouterProvider({
1812
2019
  resolve();
1813
2020
  } catch (err) {
1814
2021
  // Only apply the latest transition
1815
- if (latestPromise = checkLatest(promise)) {
2022
+ if (latestPromise = this.checkLatest(promise)) {
1816
2023
  return latestPromise;
1817
2024
  }
1818
2025
  reject(err);
1819
2026
  }
1820
2027
  });
1821
- latestLoadPromiseRef.current = promise;
1822
- return latestLoadPromiseRef.current;
1823
- });
1824
- const preloadRoute = useStableCallback(async (navigateOpts = state.location) => {
1825
- let next = buildLocation(navigateOpts);
1826
- 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, {
1827
2034
  throwOnError: true
1828
2035
  });
1829
- await loadMatches({
2036
+ await this.loadMatches({
1830
2037
  matches,
1831
2038
  preload: true,
1832
2039
  checkLatest: () => undefined
1833
2040
  });
1834
2041
  return [last(matches), matches];
1835
- });
1836
- const buildLink = useStableCallback(dest => {
2042
+ };
2043
+ buildLink = dest => {
1837
2044
  // If this link simply reloads the current route,
1838
2045
  // make sure it has a new key so it will trigger a data refresh
1839
2046
 
@@ -1859,18 +2066,18 @@ function RouterProvider({
1859
2066
  };
1860
2067
  } catch (e) {}
1861
2068
  const nextOpts = dest;
1862
- const next = buildLocation(nextOpts);
1863
- const preload = userPreload ?? options.defaultPreload;
1864
- 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;
1865
2072
 
1866
2073
  // Compare path/hash for matches
1867
- const currentPathSplit = latestLocationRef.current.pathname.split('/');
2074
+ const currentPathSplit = this.latestLocation.pathname.split('/');
1868
2075
  const nextPathSplit = next.pathname.split('/');
1869
2076
  const pathIsFuzzyEqual = nextPathSplit.every((d, i) => d === currentPathSplit[i]);
1870
- // Combine the matches based on user options
1871
- const pathTest = activeOptions?.exact ? latestLocationRef.current.pathname === next.pathname : pathIsFuzzyEqual;
1872
- const hashTest = activeOptions?.includeHash ? latestLocationRef.current.hash === next.hash : true;
1873
- 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;
1874
2081
 
1875
2082
  // The final "active" test
1876
2083
  const isActive = pathTest && hashTest && searchTest;
@@ -1881,7 +2088,7 @@ function RouterProvider({
1881
2088
  e.preventDefault();
1882
2089
 
1883
2090
  // All is well? Navigate!
1884
- commitLocation({
2091
+ this.commitLocation({
1885
2092
  ...next,
1886
2093
  replace,
1887
2094
  resetScroll,
@@ -1893,384 +2100,184 @@ function RouterProvider({
1893
2100
  // The click handler
1894
2101
  const handleFocus = e => {
1895
2102
  if (preload) {
1896
- preloadRoute(nextOpts).catch(err => {
2103
+ this.preloadRoute(nextOpts).catch(err => {
1897
2104
  console.warn(err);
1898
2105
  console.warn(preloadWarning);
1899
2106
  });
1900
2107
  }
1901
2108
  };
1902
2109
  const handleTouchStart = e => {
1903
- preloadRoute(nextOpts).catch(err => {
2110
+ this.preloadRoute(nextOpts).catch(err => {
1904
2111
  console.warn(err);
1905
2112
  console.warn(preloadWarning);
1906
2113
  });
1907
2114
  };
1908
- const handleEnter = e => {
1909
- const target = e.target || {};
1910
- if (preload) {
1911
- if (target.preloadTimeout) {
1912
- return;
1913
- }
1914
- target.preloadTimeout = setTimeout(() => {
1915
- target.preloadTimeout = null;
1916
- preloadRoute(nextOpts).catch(err => {
1917
- console.warn(err);
1918
- console.warn(preloadWarning);
1919
- });
1920
- }, preloadDelay);
1921
- }
1922
- };
1923
- const handleLeave = e => {
1924
- const target = e.target || {};
1925
- if (target.preloadTimeout) {
1926
- clearTimeout(target.preloadTimeout);
1927
- target.preloadTimeout = null;
1928
- }
1929
- };
1930
- return {
1931
- type: 'internal',
1932
- next,
1933
- handleFocus,
1934
- handleClick,
1935
- handleEnter,
1936
- handleLeave,
1937
- handleTouchStart,
1938
- isActive,
1939
- disabled
1940
- };
1941
- });
1942
- React.useLayoutEffect(() => {
1943
- const unsub = history.subscribe(() => {
1944
- latestLocationRef.current = parseLocation(latestLocationRef.current);
1945
- if (state.location !== latestLocationRef.current) {
1946
- startReactTransition(() => {
1947
- try {
1948
- load();
1949
- } catch (err) {
1950
- console.error(err);
1951
- }
1952
- });
1953
- }
1954
- });
1955
- const nextLocation = buildLocation({
1956
- search: true,
1957
- params: true,
1958
- hash: true,
1959
- state: true
1960
- });
1961
- if (state.location.href !== nextLocation.href) {
1962
- commitLocation({
1963
- ...nextLocation,
1964
- replace: true
1965
- });
1966
- }
1967
- return () => {
1968
- unsub();
1969
- };
1970
- }, [history]);
1971
- const matchRoute = useStableCallback((location, opts) => {
1972
- location = {
1973
- ...location,
1974
- to: location.to ? resolvePathWithBase(location.from || '', location.to) : undefined
1975
- };
1976
- const next = buildLocation(location);
1977
- if (opts?.pending && state.status !== 'pending') {
1978
- return false;
1979
- }
1980
- const baseLocation = opts?.pending ? latestLocationRef.current : state.resolvedLocation;
1981
-
1982
- // const baseLocation = state.resolvedLocation
1983
-
1984
- if (!baseLocation) {
1985
- return false;
1986
- }
1987
- const match = matchPathname(basepath, baseLocation.pathname, {
1988
- ...opts,
1989
- to: next.pathname
1990
- });
1991
- if (!match) {
1992
- return false;
1993
- }
1994
- if (match && (opts?.includeSearch ?? true)) {
1995
- return deepEqual(baseLocation.search, next.search, true) ? match : false;
1996
- }
1997
- return match;
1998
- });
1999
- const injectedHtmlRef = React.useRef([]);
2000
- const injectHtml = useStableCallback(async html => {
2001
- injectedHtmlRef.current.push(html);
2002
- });
2003
- const dehydrateData = useStableCallback((key, getData) => {
2004
- if (typeof document === 'undefined') {
2005
- const strKey = typeof key === 'string' ? key : JSON.stringify(key);
2006
- injectHtml(async () => {
2007
- const id = `__TSR_DEHYDRATED__${strKey}`;
2008
- const data = typeof getData === 'function' ? await getData() : getData;
2009
- return `<script id='${id}' suppressHydrationWarning>window["__TSR_DEHYDRATED__${escapeJSON(strKey)}"] = ${JSON.stringify(data)}
2010
- ;(() => {
2011
- var el = document.getElementById('${id}')
2012
- el.parentElement.removeChild(el)
2013
- })()
2014
- </script>`;
2015
- });
2016
- return () => hydrateData(key);
2017
- }
2018
- return () => undefined;
2019
- });
2020
- const hydrateData = useStableCallback(key => {
2021
- if (typeof document !== 'undefined') {
2022
- const strKey = typeof key === 'string' ? key : JSON.stringify(key);
2023
- return window[`__TSR_DEHYDRATED__${strKey}`];
2024
- }
2025
- return undefined;
2026
- });
2027
- React.useLayoutEffect(() => {
2028
- startReactTransition(() => {
2029
- try {
2030
- load();
2031
- } catch (err) {
2032
- console.error(err);
2033
- }
2034
- });
2035
- }, []);
2036
- const routerContextValue = {
2037
- routeTree: router.routeTree,
2038
- navigate,
2039
- buildLink,
2040
- state,
2041
- matchRoute,
2042
- routesById,
2043
- options,
2044
- history,
2045
- load,
2046
- buildLocation,
2047
- subscribe: router.subscribe,
2048
- resetNextScrollRef,
2049
- injectedHtmlRef,
2050
- injectHtml,
2051
- dehydrateData,
2052
- hydrateData
2053
- };
2054
- return /*#__PURE__*/React.createElement(routerContext.Provider, {
2055
- value: routerContextValue
2056
- }, /*#__PURE__*/React.createElement(Matches, null));
2057
- }
2058
- function getRouteMatch(state, id) {
2059
- return [...state.pendingMatches, ...state.matches].find(d => d.id === id);
2060
- }
2061
- function useRouterState(opts) {
2062
- const {
2063
- state
2064
- } = useRouter();
2065
- // return useStore(router.__store, opts?.select as any)
2066
- return opts?.select ? opts.select(state) : state;
2067
- }
2068
- function useRouter() {
2069
- const resolvedContext = window.__TSR_ROUTER_CONTEXT__ || routerContext;
2070
- const value = React.useContext(resolvedContext);
2071
- warning(value, 'useRouter must be used inside a <RouterProvider> component!');
2072
- return value;
2073
- }
2074
-
2075
- function defer(_promise) {
2076
- const promise = _promise;
2077
- if (!promise.__deferredState) {
2078
- promise.__deferredState = {
2079
- uid: Math.random().toString(36).slice(2),
2080
- status: 'pending'
2081
- };
2082
- const state = promise.__deferredState;
2083
- promise.then(data => {
2084
- state.status = 'success';
2085
- state.data = data;
2086
- }).catch(error => {
2087
- state.status = 'error';
2088
- state.error = error;
2089
- });
2090
- }
2091
- return promise;
2092
- }
2093
- function isDehydratedDeferred(obj) {
2094
- return typeof obj === 'object' && obj !== null && !(obj instanceof Promise) && !obj.then && '__deferredState' in obj;
2095
- }
2096
-
2097
- function useAwaited({
2098
- promise
2099
- }) {
2100
- const router = useRouter();
2101
- let state = promise.__deferredState;
2102
- const key = `__TSR__DEFERRED__${state.uid}`;
2103
- if (isDehydratedDeferred(promise)) {
2104
- state = router.hydrateData(key);
2105
- promise = Promise.resolve(state.data);
2106
- promise.__deferredState = state;
2107
- }
2108
- if (state.status === 'pending') {
2109
- throw promise;
2110
- }
2111
- if (state.status === 'error') {
2112
- throw state.error;
2113
- }
2114
- router.dehydrateData(key, state);
2115
- return [state.data];
2116
- }
2117
- function Await(props) {
2118
- const awaited = useAwaited(props);
2119
- return props.children(...awaited);
2120
- }
2121
-
2122
- class FileRoute {
2123
- constructor(path) {
2124
- this.path = path;
2125
- }
2126
- createRoute = options => {
2127
- const route = new Route(options);
2128
- route.isRoot = false;
2129
- return route;
2130
- };
2131
- }
2132
-
2133
- function lazyRouteComponent(importer, exportName) {
2134
- let loadPromise;
2135
- const load = () => {
2136
- if (!loadPromise) {
2137
- loadPromise = importer();
2138
- }
2139
- return loadPromise;
2140
- };
2141
- const lazyComp = /*#__PURE__*/React.lazy(async () => {
2142
- const moduleExports = await load();
2143
- const comp = moduleExports[exportName ?? 'default'];
2144
- return {
2145
- default: comp
2146
- };
2147
- });
2148
- lazyComp.preload = load;
2149
- return lazyComp;
2150
- }
2151
-
2152
- function _extends() {
2153
- _extends = Object.assign ? Object.assign.bind() : function (target) {
2154
- for (var i = 1; i < arguments.length; i++) {
2155
- var source = arguments[i];
2156
- for (var key in source) {
2157
- if (Object.prototype.hasOwnProperty.call(source, key)) {
2158
- target[key] = source[key];
2115
+ const handleEnter = e => {
2116
+ const target = e.target || {};
2117
+ if (preload) {
2118
+ if (target.preloadTimeout) {
2119
+ return;
2159
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);
2160
2128
  }
2161
- }
2162
- return target;
2163
- };
2164
- return _extends.apply(this, arguments);
2165
- }
2166
-
2167
- function useLinkProps(options) {
2168
- const {
2169
- buildLink
2170
- } = useRouter();
2171
- const match = useMatch({
2172
- strict: false
2173
- });
2174
- const {
2175
- // custom props
2176
- type,
2177
- children,
2178
- target,
2179
- activeProps = () => ({
2180
- className: 'active'
2181
- }),
2182
- inactiveProps = () => ({}),
2183
- activeOptions,
2184
- disabled,
2185
- hash,
2186
- search,
2187
- params,
2188
- to,
2189
- state,
2190
- mask,
2191
- preload,
2192
- preloadDelay,
2193
- replace,
2194
- startTransition,
2195
- resetScroll,
2196
- // element props
2197
- style,
2198
- className,
2199
- onClick,
2200
- onFocus,
2201
- onMouseEnter,
2202
- onMouseLeave,
2203
- onTouchStart,
2204
- ...rest
2205
- } = options;
2206
- const linkInfo = buildLink({
2207
- from: options.to ? match.pathname : undefined,
2208
- ...options
2209
- });
2210
- if (linkInfo.type === 'external') {
2211
- const {
2212
- href
2213
- } = 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
+ };
2214
2137
  return {
2215
- href
2138
+ type: 'internal',
2139
+ next,
2140
+ handleFocus,
2141
+ handleClick,
2142
+ handleEnter,
2143
+ handleLeave,
2144
+ handleTouchStart,
2145
+ isActive,
2146
+ disabled
2216
2147
  };
2217
- }
2218
- const {
2219
- handleClick,
2220
- handleFocus,
2221
- handleEnter,
2222
- handleLeave,
2223
- handleTouchStart,
2224
- isActive,
2225
- next
2226
- } = linkInfo;
2227
- const composeHandlers = handlers => e => {
2228
- if (e.persist) e.persist();
2229
- handlers.filter(Boolean).forEach(handler => {
2230
- if (e.defaultPrevented) return;
2231
- 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
2232
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;
2233
2203
  };
2234
2204
 
2235
- // Get the active props
2236
- 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
+ // }
2237
2214
 
2238
- // Get the inactive props
2239
- const resolvedInactiveProps = isActive ? {} : functionalUpdate(inactiveProps, {}) ?? {};
2240
- return {
2241
- ...resolvedActiveProps,
2242
- ...resolvedInactiveProps,
2243
- ...rest,
2244
- href: disabled ? undefined : next.maskedLocation ? next.maskedLocation.href : next.href,
2245
- onClick: composeHandlers([onClick, handleClick]),
2246
- onFocus: composeHandlers([onFocus, handleFocus]),
2247
- onMouseEnter: composeHandlers([onMouseEnter, handleEnter]),
2248
- onMouseLeave: composeHandlers([onMouseLeave, handleLeave]),
2249
- onTouchStart: composeHandlers([onTouchStart, handleTouchStart]),
2250
- target,
2251
- style: {
2252
- ...style,
2253
- ...resolvedActiveProps.style,
2254
- ...resolvedInactiveProps.style
2255
- },
2256
- className: [className, resolvedActiveProps.className, resolvedInactiveProps.className].filter(Boolean).join(' ') || undefined,
2257
- ...(disabled ? {
2258
- role: 'link',
2259
- 'aria-disabled': true
2260
- } : undefined),
2261
- ['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);
2262
2276
  };
2263
2277
  }
2264
- const Link = /*#__PURE__*/React.forwardRef((props, ref) => {
2265
- const linkProps = useLinkProps(props);
2266
- return /*#__PURE__*/React.createElement("a", _extends({
2267
- ref: ref
2268
- }, linkProps, {
2269
- children: typeof props.children === 'function' ? props.children({
2270
- isActive: linkProps['data-status'] === 'active'
2271
- }) : props.children
2272
- }));
2273
- });
2278
+ function isCtrlEvent(e) {
2279
+ return !!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey);
2280
+ }
2274
2281
 
2275
2282
  const useLayoutEffect = typeof window !== 'undefined' ? React.useLayoutEffect : React.useEffect;
2276
2283
  const windowKey = 'window';
@@ -2280,11 +2287,7 @@ let cache;
2280
2287
  const sessionsStorage = typeof window !== 'undefined' && window.sessionStorage;
2281
2288
  const defaultGetKey = location => location.state.key;
2282
2289
  function useScrollRestoration(options) {
2283
- const {
2284
- state,
2285
- subscribe,
2286
- resetNextScrollRef
2287
- } = useRouter();
2290
+ const router = useRouter();
2288
2291
  useLayoutEffect(() => {
2289
2292
  const getKey = options?.getKey || defaultGetKey;
2290
2293
  if (sessionsStorage) {
@@ -2340,7 +2343,7 @@ function useScrollRestoration(options) {
2340
2343
  if (typeof document !== 'undefined') {
2341
2344
  document.addEventListener('scroll', onScroll, true);
2342
2345
  }
2343
- const unsubOnBeforeLoad = subscribe('onBeforeLoad', event => {
2346
+ const unsubOnBeforeLoad = router.subscribe('onBeforeLoad', event => {
2344
2347
  if (event.pathChanged) {
2345
2348
  const restoreKey = getKey(event.fromLocation);
2346
2349
  for (const elementSelector in cache.state.next) {
@@ -2370,12 +2373,12 @@ function useScrollRestoration(options) {
2370
2373
  }
2371
2374
  }
2372
2375
  });
2373
- const unsubOnResolved = subscribe('onResolved', event => {
2376
+ const unsubOnResolved = router.subscribe('onResolved', event => {
2374
2377
  if (event.pathChanged) {
2375
- if (!resetNextScrollRef.current) {
2378
+ if (!router.resetNextScroll) {
2376
2379
  return;
2377
2380
  }
2378
- resetNextScrollRef.current = true;
2381
+ router.resetNextScroll = true;
2379
2382
  const getKey = options?.getKey || defaultGetKey;
2380
2383
  const restoreKey = getKey(event.toLocation);
2381
2384
  let windowRestored = false;