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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -282,6 +282,11 @@ function useRouteContext(opts) {
282
282
  });
283
283
  }
284
284
  const useLayoutEffect$1 = typeof window !== 'undefined' ? React.useLayoutEffect : React.useEffect;
285
+ function escapeJSON(jsonString) {
286
+ return jsonString.replace(/\\/g, '\\\\') // Escape backslashes
287
+ .replace(/'/g, "\\'") // Escape single quotes
288
+ .replace(/"/g, '\\"'); // Escape double quotes
289
+ }
285
290
 
286
291
  function joinPaths(paths) {
287
292
  return cleanPath(paths.filter(Boolean).join('/'));
@@ -467,173 +472,480 @@ function matchByPath(basepath, from, matchLocation) {
467
472
  return isMatch ? params : undefined;
468
473
  }
469
474
 
470
- // Detect if we're in the DOM
471
-
472
- function redirect(opts) {
473
- opts.isRedirect = true;
474
- return opts;
475
+ function useParams(opts) {
476
+ return useRouterState({
477
+ select: state => {
478
+ const params = last(state.matches)?.params;
479
+ return opts?.select ? opts.select(params) : params;
480
+ }
481
+ });
475
482
  }
476
- function isRedirect(obj) {
477
- return !!obj?.isRedirect;
483
+
484
+ function useSearch(opts) {
485
+ return useMatch({
486
+ ...opts,
487
+ select: match => {
488
+ return opts?.select ? opts.select(match.search) : match.search;
489
+ }
490
+ });
478
491
  }
479
492
 
480
- // @ts-nocheck
493
+ const rootRouteId = '__root__';
481
494
 
482
- // 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.
495
+ // The parse type here allows a zod schema to be passed directly to the validator
483
496
 
484
- function encode(obj, pfx) {
485
- var k,
486
- i,
487
- tmp,
488
- str = '';
489
- for (k in obj) {
490
- if ((tmp = obj[k]) !== void 0) {
491
- if (Array.isArray(tmp)) {
492
- for (i = 0; i < tmp.length; i++) {
493
- str && (str += '&');
494
- str += encodeURIComponent(k) + '=' + encodeURIComponent(tmp[i]);
495
- }
496
- } else {
497
- str && (str += '&');
498
- str += encodeURIComponent(k) + '=' + encodeURIComponent(tmp);
499
- }
500
- }
497
+ class Route {
498
+ // Set up in this.init()
499
+
500
+ // customId!: TCustomId
501
+
502
+ // Optional
503
+
504
+ constructor(options) {
505
+ this.options = options || {};
506
+ this.isRoot = !options?.getParentRoute;
507
+ Route.__onInit(this);
501
508
  }
502
- return (pfx || '') + str;
503
- }
504
- function toValue(mix) {
505
- if (!mix) return '';
506
- var str = decodeURIComponent(mix);
507
- if (str === 'false') return false;
508
- if (str === 'true') return true;
509
- return +str * 0 === 0 && +str + '' === str ? +str : str;
510
- }
511
- function decode(str) {
512
- var tmp,
513
- k,
514
- out = {},
515
- arr = str.split('&');
516
- while (tmp = arr.shift()) {
517
- tmp = tmp.split('=');
518
- k = tmp.shift();
519
- if (out[k] !== void 0) {
520
- out[k] = [].concat(out[k], toValue(tmp.shift()));
509
+ init = opts => {
510
+ this.originalIndex = opts.originalIndex;
511
+ const options = this.options;
512
+ const isRoot = !options?.path && !options?.id;
513
+ this.parentRoute = this.options?.getParentRoute?.();
514
+ if (isRoot) {
515
+ this.path = rootRouteId;
521
516
  } else {
522
- out[k] = toValue(tmp.shift());
517
+ invariant(this.parentRoute, `Child Route instances must pass a 'getParentRoute: () => ParentRoute' option that returns a Route instance.`);
523
518
  }
524
- }
525
- return out;
526
- }
519
+ let path = isRoot ? rootRouteId : options.path;
527
520
 
528
- const defaultParseSearch = parseSearchWith(JSON.parse);
529
- const defaultStringifySearch = stringifySearchWith(JSON.stringify, JSON.parse);
530
- function parseSearchWith(parser) {
531
- return searchStr => {
532
- if (searchStr.substring(0, 1) === '?') {
533
- searchStr = searchStr.substring(1);
521
+ // If the path is anything other than an index path, trim it up
522
+ if (path && path !== '/') {
523
+ path = trimPath(path);
534
524
  }
535
- let query = decode(searchStr);
525
+ const customId = options?.id || path;
536
526
 
537
- // Try to parse any query params that might be json
538
- for (let key in query) {
539
- const value = query[key];
540
- if (typeof value === 'string') {
541
- try {
542
- query[key] = parser(value);
543
- } catch (err) {
544
- //
545
- }
546
- }
527
+ // Strip the parentId prefix from the first level of children
528
+ let id = isRoot ? rootRouteId : joinPaths([this.parentRoute.id === rootRouteId ? '' : this.parentRoute.id, customId]);
529
+ if (path === rootRouteId) {
530
+ path = '/';
547
531
  }
548
- return query;
532
+ if (id !== rootRouteId) {
533
+ id = joinPaths(['/', id]);
534
+ }
535
+ const fullPath = id === rootRouteId ? '/' : joinPaths([this.parentRoute.fullPath, path]);
536
+ this.path = path;
537
+ this.id = id;
538
+ // this.customId = customId as TCustomId
539
+ this.fullPath = fullPath;
540
+ this.to = fullPath;
541
+ };
542
+ addChildren = children => {
543
+ this.children = children;
544
+ return this;
545
+ };
546
+ update = options => {
547
+ Object.assign(this.options, options);
548
+ return this;
549
+ };
550
+ static __onInit = route => {
551
+ // This is a dummy static method that should get
552
+ // replaced by a framework specific implementation if necessary
553
+ };
554
+ useMatch = opts => {
555
+ return useMatch({
556
+ ...opts,
557
+ from: this.id
558
+ });
559
+ };
560
+ useRouteContext = opts => {
561
+ return useMatch({
562
+ ...opts,
563
+ from: this.id,
564
+ select: d => opts?.select ? opts.select(d.context) : d.context
565
+ });
566
+ };
567
+ useSearch = opts => {
568
+ return useSearch({
569
+ ...opts,
570
+ from: this.id
571
+ });
572
+ };
573
+ useParams = opts => {
574
+ return useParams({
575
+ ...opts,
576
+ from: this.id
577
+ });
578
+ };
579
+ useLoaderData = opts => {
580
+ return useLoaderData({
581
+ ...opts,
582
+ from: this.id
583
+ });
549
584
  };
550
585
  }
551
- function stringifySearchWith(stringify, parser) {
552
- function stringifyValue(val) {
553
- if (typeof val === 'object' && val !== null) {
554
- try {
555
- return stringify(val);
556
- } catch (err) {
557
- // silent
558
- }
559
- } else if (typeof val === 'string' && typeof parser === 'function') {
560
- try {
561
- // Check if it's a valid parseable string.
562
- // If it is, then stringify it again.
563
- parser(val);
564
- return stringify(val);
565
- } catch (err) {
566
- // silent
567
- }
568
- }
569
- return val;
570
- }
571
- return search => {
572
- search = {
573
- ...search
574
- };
575
- if (search) {
576
- Object.keys(search).forEach(key => {
577
- const val = search[key];
578
- if (typeof val === 'undefined' || val === undefined) {
579
- delete search[key];
580
- } else {
581
- search[key] = stringifyValue(val);
582
- }
583
- });
584
- }
585
- const searchStr = encode(search).toString();
586
- return searchStr ? `?${searchStr}` : '';
586
+ function rootRouteWithContext() {
587
+ return options => {
588
+ return new RootRoute(options);
587
589
  };
588
590
  }
589
-
590
- //
591
-
592
- //
593
-
594
- const componentTypes = ['component', 'errorComponent', 'pendingComponent'];
595
- class Router {
596
- // dehydratedData?: TDehydrated
597
- // resetNextScroll = false
598
- // tempLocationKey = `${Math.round(Math.random() * 10000000)}`
591
+ class RootRoute extends Route {
599
592
  constructor(options) {
600
- this.options = {
601
- defaultPreloadDelay: 50,
602
- context: undefined,
603
- ...options,
604
- stringifySearch: options?.stringifySearch ?? defaultStringifySearch,
605
- parseSearch: options?.parseSearch ?? defaultParseSearch
606
- };
607
- this.routeTree = this.options.routeTree;
593
+ super(options);
608
594
  }
609
- subscribers = new Set();
610
- subscribe = (eventType, fn) => {
611
- const listener = {
612
- eventType,
613
- fn
614
- };
615
- this.subscribers.add(listener);
616
- return () => {
617
- this.subscribers.delete(listener);
618
- };
619
- };
620
- emit = routerEvent => {
621
- this.subscribers.forEach(listener => {
622
- if (listener.eventType === routerEvent.type) {
623
- listener.fn(routerEvent);
624
- }
625
- });
626
- };
595
+ }
596
+ function createRouteMask(opts) {
597
+ return opts;
598
+ }
627
599
 
628
- // dehydrate = (): DehydratedRouter => {
629
- // return {
630
- // state: {
631
- // dehydratedMatches: state.matches.map((d) =>
632
- // pick(d, ['fetchedAt', 'invalid', 'id', 'status', 'updatedAt']),
633
- // ),
634
- // },
635
- // }
636
- // }
600
+ //
601
+
602
+ function Matches() {
603
+ const {
604
+ routesById,
605
+ state
606
+ } = useRouter();
607
+ const {
608
+ matches
609
+ } = state;
610
+ const locationKey = useRouterState().location.state.key;
611
+ const route = routesById[rootRouteId];
612
+ const errorComponent = React.useCallback(props => {
613
+ return /*#__PURE__*/React.createElement(ErrorComponent, {
614
+ ...props,
615
+ useMatch: route.useMatch,
616
+ useRouteContext: route.useRouteContext,
617
+ useSearch: route.useSearch,
618
+ useParams: route.useParams
619
+ });
620
+ }, [route]);
621
+ return /*#__PURE__*/React.createElement(matchesContext.Provider, {
622
+ value: matches
623
+ }, /*#__PURE__*/React.createElement(CatchBoundary, {
624
+ resetKey: locationKey,
625
+ errorComponent: errorComponent,
626
+ onCatch: () => {
627
+ warning(false, `Error in router! Consider setting an 'errorComponent' in your RootRoute! 👍`);
628
+ }
629
+ }, matches.length ? /*#__PURE__*/React.createElement(Match, {
630
+ matches: matches
631
+ }) : null));
632
+ }
633
+ const defaultPending = () => null;
634
+ function SafeFragment(props) {
635
+ return /*#__PURE__*/React.createElement(React.Fragment, null, props.children);
636
+ }
637
+ function Match({
638
+ matches
639
+ }) {
640
+ const {
641
+ options,
642
+ routesById
643
+ } = useRouter();
644
+ const match = matches[0];
645
+ const routeId = match?.routeId;
646
+ const route = routesById[routeId];
647
+ const locationKey = useRouterState().location.state?.key;
648
+ const PendingComponent = route.options.pendingComponent ?? options.defaultPendingComponent ?? defaultPending;
649
+ const routeErrorComponent = route.options.errorComponent ?? options.defaultErrorComponent ?? ErrorComponent;
650
+ const ResolvedSuspenseBoundary = route.options.wrapInSuspense ? React.Suspense : SafeFragment;
651
+ const errorComponent = routeErrorComponent ? React.useCallback(props => {
652
+ return /*#__PURE__*/React.createElement(routeErrorComponent, {
653
+ ...props,
654
+ useMatch: route.useMatch,
655
+ useRouteContext: route.useRouteContext,
656
+ useSearch: route.useSearch,
657
+ useParams: route.useParams
658
+ });
659
+ }, [route]) : undefined;
660
+ return /*#__PURE__*/React.createElement(matchesContext.Provider, {
661
+ value: matches
662
+ }, /*#__PURE__*/React.createElement(ResolvedSuspenseBoundary, {
663
+ fallback: /*#__PURE__*/React.createElement(PendingComponent, {
664
+ useMatch: route.useMatch,
665
+ useRouteContext: route.useRouteContext,
666
+ useSearch: route.useSearch,
667
+ useParams: route.useParams
668
+ })
669
+ }, errorComponent ? /*#__PURE__*/React.createElement(CatchBoundary, {
670
+ resetKey: locationKey,
671
+ errorComponent: errorComponent,
672
+ onCatch: () => {
673
+ warning(false, `Error in route match: ${match.id}`);
674
+ }
675
+ }, /*#__PURE__*/React.createElement(MatchInner, {
676
+ match: match
677
+ })) : /*#__PURE__*/React.createElement(SafeFragment, null, /*#__PURE__*/React.createElement(MatchInner, {
678
+ match: match
679
+ }))));
680
+ }
681
+ function MatchInner({
682
+ match
683
+ }) {
684
+ const {
685
+ options,
686
+ routesById
687
+ } = useRouter();
688
+ const route = routesById[match.routeId];
689
+ if (match.status === 'error') {
690
+ throw match.error;
691
+ }
692
+ if (match.status === 'pending') {
693
+ throw match.loadPromise;
694
+ }
695
+ if (match.status === 'success') {
696
+ let comp = route.options.component ?? options.defaultComponent;
697
+ if (comp) {
698
+ return /*#__PURE__*/React.createElement(comp, {
699
+ useMatch: route.useMatch,
700
+ useRouteContext: route.useRouteContext,
701
+ useSearch: route.useSearch,
702
+ useParams: route.useParams,
703
+ useLoaderData: route.useLoaderData
704
+ });
705
+ }
706
+ return /*#__PURE__*/React.createElement(Outlet, null);
707
+ }
708
+ invariant(false, 'Idle routeMatch status encountered during rendering! You should never see this. File an issue!');
709
+ }
710
+ function Outlet() {
711
+ const matches = React.useContext(matchesContext).slice(1);
712
+ if (!matches[0]) {
713
+ return null;
714
+ }
715
+ return /*#__PURE__*/React.createElement(Match, {
716
+ matches: matches
717
+ });
718
+ }
719
+ function useMatchRoute() {
720
+ const {
721
+ matchRoute
722
+ } = useRouter();
723
+ return React.useCallback(opts => {
724
+ const {
725
+ pending,
726
+ caseSensitive,
727
+ ...rest
728
+ } = opts;
729
+ return matchRoute(rest, {
730
+ pending,
731
+ caseSensitive
732
+ });
733
+ }, []);
734
+ }
735
+ function MatchRoute(props) {
736
+ const matchRoute = useMatchRoute();
737
+ const params = matchRoute(props);
738
+ if (typeof props.children === 'function') {
739
+ return props.children(params);
740
+ }
741
+ return !!params ? props.children : null;
742
+ }
743
+ function useMatch(opts) {
744
+ const nearestMatch = React.useContext(matchesContext)[0];
745
+ const nearestMatchRouteId = nearestMatch?.routeId;
746
+ const matchRouteId = useRouterState({
747
+ select: state => {
748
+ const match = opts?.from ? state.matches.find(d => d.routeId === opts?.from) : state.matches.find(d => d.id === nearestMatch.id);
749
+ return match.routeId;
750
+ }
751
+ });
752
+ if (opts?.strict ?? true) {
753
+ invariant(nearestMatchRouteId == matchRouteId, `useMatch("${matchRouteId}") is being called in a component that is meant to render the '${nearestMatchRouteId}' route. Did you mean to 'useMatch("${matchRouteId}", { strict: false })' or 'useRoute("${matchRouteId}")' instead?`);
754
+ }
755
+ const matchSelection = useRouterState({
756
+ select: state => {
757
+ const match = opts?.from ? state.matches.find(d => d.routeId === opts?.from) : state.matches.find(d => d.id === nearestMatch.id);
758
+ invariant(match, `Could not find ${opts?.from ? `an active match from "${opts.from}"` : 'a nearest match!'}`);
759
+ return opts?.select ? opts.select(match) : match;
760
+ }
761
+ });
762
+ return matchSelection;
763
+ }
764
+ const matchesContext = /*#__PURE__*/React.createContext(null);
765
+ function useMatches(opts) {
766
+ const contextMatches = React.useContext(matchesContext);
767
+ return useRouterState({
768
+ select: state => {
769
+ const matches = state.matches.slice(state.matches.findIndex(d => d.id === contextMatches[0]?.id));
770
+ return opts?.select ? opts.select(matches) : matches;
771
+ }
772
+ });
773
+ }
774
+ function useLoaderData(opts) {
775
+ const match = useMatch({
776
+ ...opts,
777
+ select: undefined
778
+ });
779
+ return typeof opts.select === 'function' ? opts.select(match?.loaderData) : match?.loaderData;
780
+ }
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
+ // }
637
949
 
638
950
  // hydrate = async (__do_not_use_server_ctx?: HydrationCtx) => {
639
951
  // let _ctx = __do_not_use_server_ctx
@@ -682,49 +994,6 @@ class Router {
682
994
  // })
683
995
  // }
684
996
 
685
- // TODO:
686
- // injectedHtml: (string | (() => Promise<string> | string))[] = []
687
-
688
- // TODO:
689
- // injectHtml = async (html: string | (() => Promise<string> | string)) => {
690
- // this.injectedHtml.push(html)
691
- // }
692
-
693
- // TODO:
694
- // dehydrateData = <T>(key: any, getData: T | (() => Promise<T> | T)) => {
695
- // if (typeof document === 'undefined') {
696
- // const strKey = typeof key === 'string' ? key : JSON.stringify(key)
697
-
698
- // this.injectHtml(async () => {
699
- // const id = `__TSR_DEHYDRATED__${strKey}`
700
- // const data =
701
- // typeof getData === 'function' ? await (getData as any)() : getData
702
- // return `<script id='${id}' suppressHydrationWarning>window["__TSR_DEHYDRATED__${escapeJSON(
703
- // strKey,
704
- // )}"] = ${JSON.stringify(data)}
705
- // ;(() => {
706
- // var el = document.getElementById('${id}')
707
- // el.parentElement.removeChild(el)
708
- // })()
709
- // </script>`
710
- // })
711
-
712
- // return () => this.hydrateData<T>(key)
713
- // }
714
-
715
- // return () => undefined
716
- // }
717
-
718
- // hydrateData = <T = unknown>(key: any) => {
719
- // if (typeof document !== 'undefined') {
720
- // const strKey = typeof key === 'string' ? key : JSON.stringify(key)
721
-
722
- // return window[`__TSR_DEHYDRATED__${strKey}` as any] as T
723
- // }
724
-
725
- // return undefined
726
- // }
727
-
728
997
  // resolveMatchPromise = (matchId: string, key: string, value: any) => {
729
998
  // state.matches
730
999
  // .find((d) => d.id === matchId)
@@ -1384,13 +1653,15 @@ function RouterProvider({
1384
1653
 
1385
1654
  // Default to reloading the route all the time
1386
1655
  let shouldReload = true;
1387
- let shouldReloadDeps = typeof route.options.shouldReload === 'function' ? route.options.shouldReload?.(loaderContext) : !!route.options.shouldReload;
1388
- if (typeof shouldReloadDeps === 'object') {
1389
- // compare the deps to see if they've changed
1390
- shouldReload = !deepEqual(shouldReloadDeps, match.shouldReloadDeps);
1391
- match.shouldReloadDeps = shouldReloadDeps;
1392
- } else {
1393
- shouldReload = !!shouldReloadDeps;
1656
+ if (cause !== 'enter') {
1657
+ let shouldReloadDeps = typeof route.options.shouldReload === 'function' ? route.options.shouldReload?.(loaderContext) : !!(route.options.shouldReload ?? true);
1658
+ if (typeof shouldReloadDeps === 'object') {
1659
+ // compare the deps to see if they've changed
1660
+ shouldReload = !deepEqual(shouldReloadDeps, match.shouldReloadDeps);
1661
+ match.shouldReloadDeps = shouldReloadDeps;
1662
+ } else {
1663
+ shouldReload = !!shouldReloadDeps;
1664
+ }
1394
1665
  }
1395
1666
 
1396
1667
  // If the user doesn't want the route to reload, just
@@ -1691,386 +1962,158 @@ function RouterProvider({
1691
1962
  return () => {
1692
1963
  unsub();
1693
1964
  };
1694
- }, [history]);
1695
- React.useLayoutEffect(() => {
1696
- startReactTransition(() => {
1697
- try {
1698
- load();
1699
- } catch (err) {
1700
- console.error(err);
1701
- }
1702
- });
1703
- }, []);
1704
- const matchRoute = useStableCallback((location, opts) => {
1705
- location = {
1706
- ...location,
1707
- to: location.to ? resolvePathWithBase(location.from || '', location.to) : undefined
1708
- };
1709
- const next = buildLocation(location);
1710
- if (opts?.pending && state.status !== 'pending') {
1711
- return false;
1712
- }
1713
- const baseLocation = opts?.pending ? latestLocationRef.current : state.resolvedLocation;
1714
-
1715
- // const baseLocation = state.resolvedLocation
1716
-
1717
- if (!baseLocation) {
1718
- return false;
1719
- }
1720
- const match = matchPathname(basepath, baseLocation.pathname, {
1721
- ...opts,
1722
- to: next.pathname
1723
- });
1724
- if (!match) {
1725
- return false;
1726
- }
1727
- if (match && (opts?.includeSearch ?? true)) {
1728
- return deepEqual(baseLocation.search, next.search, true) ? match : false;
1729
- }
1730
- return match;
1731
- });
1732
- const routerContextValue = {
1733
- routeTree: router.routeTree,
1734
- navigate,
1735
- buildLink,
1736
- state,
1737
- matchRoute,
1738
- routesById,
1739
- options,
1740
- history,
1741
- load,
1742
- buildLocation,
1743
- subscribe: router.subscribe,
1744
- resetNextScrollRef
1745
- };
1746
- return /*#__PURE__*/React.createElement(routerContext.Provider, {
1747
- value: routerContextValue
1748
- }, /*#__PURE__*/React.createElement(Matches, null));
1749
- }
1750
- function getRouteMatch(state, id) {
1751
- return [...state.pendingMatches, ...state.matches].find(d => d.id === id);
1752
- }
1753
- function useRouterState(opts) {
1754
- const {
1755
- state
1756
- } = useRouter();
1757
- // return useStore(router.__store, opts?.select as any)
1758
- return opts?.select ? opts.select(state) : state;
1759
- }
1760
- function useRouter() {
1761
- const resolvedContext = window.__TSR_ROUTER_CONTEXT__ || routerContext;
1762
- const value = React.useContext(resolvedContext);
1763
- warning(value, 'useRouter must be used inside a <RouterProvider> component!');
1764
- return value;
1765
- }
1766
-
1767
- function Matches() {
1768
- const {
1769
- routesById,
1770
- state
1771
- } = useRouter();
1772
- const {
1773
- matches
1774
- } = state;
1775
- const locationKey = useRouterState().location.state.key;
1776
- const route = routesById[rootRouteId];
1777
- const errorComponent = React.useCallback(props => {
1778
- return /*#__PURE__*/React.createElement(ErrorComponent, {
1779
- ...props,
1780
- useMatch: route.useMatch,
1781
- useRouteContext: route.useRouteContext,
1782
- useSearch: route.useSearch,
1783
- useParams: route.useParams
1784
- });
1785
- }, [route]);
1786
- return /*#__PURE__*/React.createElement(matchesContext.Provider, {
1787
- value: matches
1788
- }, /*#__PURE__*/React.createElement(CatchBoundary, {
1789
- resetKey: locationKey,
1790
- errorComponent: errorComponent,
1791
- onCatch: () => {
1792
- warning(false, `Error in router! Consider setting an 'errorComponent' in your RootRoute! 👍`);
1793
- }
1794
- }, matches.length ? /*#__PURE__*/React.createElement(Match, {
1795
- matches: matches
1796
- }) : null));
1797
- }
1798
- const defaultPending = () => null;
1799
- function SafeFragment(props) {
1800
- return /*#__PURE__*/React.createElement(React.Fragment, null, props.children);
1801
- }
1802
- function Match({
1803
- matches
1804
- }) {
1805
- const {
1806
- options,
1807
- routesById
1808
- } = useRouter();
1809
- const match = matches[0];
1810
- const routeId = match?.routeId;
1811
- const route = routesById[routeId];
1812
- const locationKey = useRouterState().location.state?.key;
1813
- const PendingComponent = route.options.pendingComponent ?? options.defaultPendingComponent ?? defaultPending;
1814
- const routeErrorComponent = route.options.errorComponent ?? options.defaultErrorComponent ?? ErrorComponent;
1815
- const ResolvedSuspenseBoundary = route.options.wrapInSuspense ? React.Suspense : SafeFragment;
1816
- const errorComponent = routeErrorComponent ? React.useCallback(props => {
1817
- return /*#__PURE__*/React.createElement(routeErrorComponent, {
1818
- ...props,
1819
- useMatch: route.useMatch,
1820
- useRouteContext: route.useRouteContext,
1821
- useSearch: route.useSearch,
1822
- useParams: route.useParams
1823
- });
1824
- }, [route]) : undefined;
1825
- return /*#__PURE__*/React.createElement(matchesContext.Provider, {
1826
- value: matches
1827
- }, /*#__PURE__*/React.createElement(ResolvedSuspenseBoundary, {
1828
- fallback: /*#__PURE__*/React.createElement(PendingComponent, {
1829
- useMatch: route.useMatch,
1830
- useRouteContext: route.useRouteContext,
1831
- useSearch: route.useSearch,
1832
- useParams: route.useParams
1833
- })
1834
- }, errorComponent ? /*#__PURE__*/React.createElement(CatchBoundary, {
1835
- resetKey: locationKey,
1836
- errorComponent: errorComponent,
1837
- onCatch: () => {
1838
- warning(false, `Error in route match: ${match.id}`);
1839
- }
1840
- }, /*#__PURE__*/React.createElement(MatchInner, {
1841
- match: match
1842
- })) : /*#__PURE__*/React.createElement(SafeFragment, null, /*#__PURE__*/React.createElement(MatchInner, {
1843
- match: match
1844
- }))));
1845
- }
1846
- function MatchInner({
1847
- match
1848
- }) {
1849
- const {
1850
- options,
1851
- routesById
1852
- } = useRouter();
1853
- const route = routesById[match.routeId];
1854
- if (match.status === 'error') {
1855
- throw match.error;
1856
- }
1857
- if (match.status === 'pending') {
1858
- throw match.loadPromise;
1859
- }
1860
- if (match.status === 'success') {
1861
- let comp = route.options.component ?? options.defaultComponent;
1862
- if (comp) {
1863
- return /*#__PURE__*/React.createElement(comp, {
1864
- useMatch: route.useMatch,
1865
- useRouteContext: route.useRouteContext,
1866
- useSearch: route.useSearch,
1867
- useParams: route.useParams,
1868
- useLoaderData: route.useLoaderData
1869
- });
1965
+ }, [history]);
1966
+ const matchRoute = useStableCallback((location, opts) => {
1967
+ location = {
1968
+ ...location,
1969
+ to: location.to ? resolvePathWithBase(location.from || '', location.to) : undefined
1970
+ };
1971
+ const next = buildLocation(location);
1972
+ if (opts?.pending && state.status !== 'pending') {
1973
+ return false;
1870
1974
  }
1871
- return /*#__PURE__*/React.createElement(Outlet, null);
1872
- }
1873
- invariant(false, 'Idle routeMatch status encountered during rendering! You should never see this. File an issue!');
1874
- }
1875
- function Outlet() {
1876
- const matches = React.useContext(matchesContext).slice(1);
1877
- if (!matches[0]) {
1878
- return null;
1879
- }
1880
- return /*#__PURE__*/React.createElement(Match, {
1881
- matches: matches
1882
- });
1883
- }
1884
- function useMatchRoute() {
1885
- const {
1886
- matchRoute
1887
- } = useRouter();
1888
- return React.useCallback(opts => {
1889
- const {
1890
- pending,
1891
- caseSensitive,
1892
- ...rest
1893
- } = opts;
1894
- return matchRoute(rest, {
1895
- pending,
1896
- caseSensitive
1975
+ const baseLocation = opts?.pending ? latestLocationRef.current : state.resolvedLocation;
1976
+
1977
+ // const baseLocation = state.resolvedLocation
1978
+
1979
+ if (!baseLocation) {
1980
+ return false;
1981
+ }
1982
+ const match = matchPathname(basepath, baseLocation.pathname, {
1983
+ ...opts,
1984
+ to: next.pathname
1897
1985
  });
1898
- }, []);
1899
- }
1900
- function MatchRoute(props) {
1901
- const matchRoute = useMatchRoute();
1902
- const params = matchRoute(props);
1903
- if (typeof props.children === 'function') {
1904
- return props.children(params);
1905
- }
1906
- return !!params ? props.children : null;
1907
- }
1908
- function useMatch(opts) {
1909
- const nearestMatch = React.useContext(matchesContext)[0];
1910
- const nearestMatchRouteId = nearestMatch?.routeId;
1911
- const matchRouteId = useRouterState({
1912
- select: state => {
1913
- const match = opts?.from ? state.matches.find(d => d.routeId === opts?.from) : state.matches.find(d => d.id === nearestMatch.id);
1914
- return match.routeId;
1986
+ if (!match) {
1987
+ return false;
1915
1988
  }
1989
+ if (match && (opts?.includeSearch ?? true)) {
1990
+ return deepEqual(baseLocation.search, next.search, true) ? match : false;
1991
+ }
1992
+ return match;
1916
1993
  });
1917
- if (opts?.strict ?? true) {
1918
- invariant(nearestMatchRouteId == matchRouteId, `useMatch("${matchRouteId}") is being called in a component that is meant to render the '${nearestMatchRouteId}' route. Did you mean to 'useMatch("${matchRouteId}", { strict: false })' or 'useRoute("${matchRouteId}")' instead?`);
1919
- }
1920
- const matchSelection = useRouterState({
1921
- select: state => {
1922
- const match = opts?.from ? state.matches.find(d => d.routeId === opts?.from) : state.matches.find(d => d.id === nearestMatch.id);
1923
- invariant(match, `Could not find ${opts?.from ? `an active match from "${opts.from}"` : 'a nearest match!'}`);
1924
- return opts?.select ? opts.select(match) : match;
1994
+ const injectedHtmlRef = React.useRef([]);
1995
+ const injectHtml = useStableCallback(async html => {
1996
+ injectedHtmlRef.current.push(html);
1997
+ });
1998
+ const dehydrateData = useStableCallback((key, getData) => {
1999
+ if (typeof document === 'undefined') {
2000
+ const strKey = typeof key === 'string' ? key : JSON.stringify(key);
2001
+ injectHtml(async () => {
2002
+ const id = `__TSR_DEHYDRATED__${strKey}`;
2003
+ const data = typeof getData === 'function' ? await getData() : getData;
2004
+ return `<script id='${id}' suppressHydrationWarning>window["__TSR_DEHYDRATED__${escapeJSON(strKey)}"] = ${JSON.stringify(data)}
2005
+ ;(() => {
2006
+ var el = document.getElementById('${id}')
2007
+ el.parentElement.removeChild(el)
2008
+ })()
2009
+ </script>`;
2010
+ });
2011
+ return () => hydrateData(key);
1925
2012
  }
2013
+ return () => undefined;
1926
2014
  });
1927
- return matchSelection;
1928
- }
1929
- const matchesContext = /*#__PURE__*/React.createContext(null);
1930
- function useMatches(opts) {
1931
- const contextMatches = React.useContext(matchesContext);
1932
- return useRouterState({
1933
- select: state => {
1934
- const matches = state.matches.slice(state.matches.findIndex(d => d.id === contextMatches[0]?.id));
1935
- return opts?.select ? opts.select(matches) : matches;
2015
+ const hydrateData = useStableCallback(key => {
2016
+ if (typeof document !== 'undefined') {
2017
+ const strKey = typeof key === 'string' ? key : JSON.stringify(key);
2018
+ return window[`__TSR_DEHYDRATED__${strKey}`];
1936
2019
  }
2020
+ return undefined;
1937
2021
  });
2022
+ React.useLayoutEffect(() => {
2023
+ startReactTransition(() => {
2024
+ try {
2025
+ load();
2026
+ } catch (err) {
2027
+ console.error(err);
2028
+ }
2029
+ });
2030
+ }, []);
2031
+ const routerContextValue = {
2032
+ routeTree: router.routeTree,
2033
+ navigate,
2034
+ buildLink,
2035
+ state,
2036
+ matchRoute,
2037
+ routesById,
2038
+ options,
2039
+ history,
2040
+ load,
2041
+ buildLocation,
2042
+ subscribe: router.subscribe,
2043
+ resetNextScrollRef,
2044
+ injectedHtmlRef,
2045
+ injectHtml,
2046
+ dehydrateData,
2047
+ hydrateData
2048
+ };
2049
+ return /*#__PURE__*/React.createElement(routerContext.Provider, {
2050
+ value: routerContextValue
2051
+ }, /*#__PURE__*/React.createElement(Matches, null));
1938
2052
  }
1939
- function useLoaderData(opts) {
1940
- const match = useMatch({
1941
- ...opts,
1942
- select: undefined
1943
- });
1944
- return typeof opts.select === 'function' ? opts.select(match?.loaderData) : match?.loaderData;
2053
+ function getRouteMatch(state, id) {
2054
+ return [...state.pendingMatches, ...state.matches].find(d => d.id === id);
1945
2055
  }
1946
-
1947
- function useParams(opts) {
1948
- return useRouterState({
1949
- select: state => {
1950
- const params = last(state.matches)?.params;
1951
- return opts?.select ? opts.select(params) : params;
1952
- }
1953
- });
2056
+ function useRouterState(opts) {
2057
+ const {
2058
+ state
2059
+ } = useRouter();
2060
+ // return useStore(router.__store, opts?.select as any)
2061
+ return opts?.select ? opts.select(state) : state;
1954
2062
  }
1955
-
1956
- function useSearch(opts) {
1957
- return useMatch({
1958
- ...opts,
1959
- select: match => {
1960
- return opts?.select ? opts.select(match.search) : match.search;
1961
- }
1962
- });
2063
+ function useRouter() {
2064
+ const resolvedContext = window.__TSR_ROUTER_CONTEXT__ || routerContext;
2065
+ const value = React.useContext(resolvedContext);
2066
+ warning(value, 'useRouter must be used inside a <RouterProvider> component!');
2067
+ return value;
1963
2068
  }
1964
2069
 
1965
- const rootRouteId = '__root__';
1966
-
1967
- // The parse type here allows a zod schema to be passed directly to the validator
1968
-
1969
- class Route {
1970
- // Set up in this.init()
1971
-
1972
- // customId!: TCustomId
1973
-
1974
- // Optional
1975
-
1976
- constructor(options) {
1977
- this.options = options || {};
1978
- this.isRoot = !options?.getParentRoute;
1979
- Route.__onInit(this);
1980
- }
1981
- init = opts => {
1982
- this.originalIndex = opts.originalIndex;
1983
- const options = this.options;
1984
- const isRoot = !options?.path && !options?.id;
1985
- this.parentRoute = this.options?.getParentRoute?.();
1986
- if (isRoot) {
1987
- this.path = rootRouteId;
1988
- } else {
1989
- invariant(this.parentRoute, `Child Route instances must pass a 'getParentRoute: () => ParentRoute' option that returns a Route instance.`);
1990
- }
1991
- let path = isRoot ? rootRouteId : options.path;
1992
-
1993
- // If the path is anything other than an index path, trim it up
1994
- if (path && path !== '/') {
1995
- path = trimPath(path);
1996
- }
1997
- const customId = options?.id || path;
1998
-
1999
- // Strip the parentId prefix from the first level of children
2000
- let id = isRoot ? rootRouteId : joinPaths([this.parentRoute.id === rootRouteId ? '' : this.parentRoute.id, customId]);
2001
- if (path === rootRouteId) {
2002
- path = '/';
2003
- }
2004
- if (id !== rootRouteId) {
2005
- id = joinPaths(['/', id]);
2006
- }
2007
- const fullPath = id === rootRouteId ? '/' : joinPaths([this.parentRoute.fullPath, path]);
2008
- this.path = path;
2009
- this.id = id;
2010
- // this.customId = customId as TCustomId
2011
- this.fullPath = fullPath;
2012
- this.to = fullPath;
2013
- };
2014
- addChildren = children => {
2015
- this.children = children;
2016
- return this;
2017
- };
2018
- update = options => {
2019
- Object.assign(this.options, options);
2020
- return this;
2021
- };
2022
- static __onInit = route => {
2023
- // This is a dummy static method that should get
2024
- // replaced by a framework specific implementation if necessary
2025
- };
2026
- useMatch = opts => {
2027
- return useMatch({
2028
- ...opts,
2029
- from: this.id
2030
- });
2031
- };
2032
- useRouteContext = opts => {
2033
- return useMatch({
2034
- ...opts,
2035
- from: this.id,
2036
- select: d => opts?.select ? opts.select(d.context) : d.context
2037
- });
2038
- };
2039
- useSearch = opts => {
2040
- return useSearch({
2041
- ...opts,
2042
- from: this.id
2043
- });
2044
- };
2045
- useParams = opts => {
2046
- return useParams({
2047
- ...opts,
2048
- from: this.id
2049
- });
2050
- };
2051
- useLoaderData = opts => {
2052
- return useLoaderData({
2053
- ...opts,
2054
- from: this.id
2070
+ function defer(_promise) {
2071
+ const promise = _promise;
2072
+ if (!promise.__deferredState) {
2073
+ promise.__deferredState = {
2074
+ uid: Math.random().toString(36).slice(2),
2075
+ status: 'pending'
2076
+ };
2077
+ const state = promise.__deferredState;
2078
+ promise.then(data => {
2079
+ state.status = 'success';
2080
+ state.data = data;
2081
+ }).catch(error => {
2082
+ state.status = 'error';
2083
+ state.error = error;
2055
2084
  });
2056
- };
2085
+ }
2086
+ return promise;
2057
2087
  }
2058
- function rootRouteWithContext() {
2059
- return options => {
2060
- return new RootRoute(options);
2061
- };
2088
+ function isDehydratedDeferred(obj) {
2089
+ return typeof obj === 'object' && obj !== null && !(obj instanceof Promise) && !obj.then && '__deferredState' in obj;
2062
2090
  }
2063
- class RootRoute extends Route {
2064
- constructor(options) {
2065
- super(options);
2091
+
2092
+ function useAwaited({
2093
+ promise
2094
+ }) {
2095
+ const router = useRouter();
2096
+ let state = promise.__deferredState;
2097
+ const key = `__TSR__DEFERRED__${state.uid}`;
2098
+ if (isDehydratedDeferred(promise)) {
2099
+ state = router.hydrateData(key);
2100
+ promise = Promise.resolve(state.data);
2101
+ promise.__deferredState = state;
2102
+ }
2103
+ if (state.status === 'pending') {
2104
+ throw promise;
2105
+ }
2106
+ if (state.status === 'error') {
2107
+ throw state.error;
2066
2108
  }
2109
+ router.dehydrateData(key, state);
2110
+ return [state.data];
2067
2111
  }
2068
- function createRouteMask(opts) {
2069
- return opts;
2112
+ function Await(props) {
2113
+ const awaited = useAwaited(props);
2114
+ return props.children(...awaited);
2070
2115
  }
2071
2116
 
2072
- //
2073
-
2074
2117
  class FileRoute {
2075
2118
  constructor(path) {
2076
2119
  this.path = path;
@@ -2428,5 +2471,5 @@ function Navigate(props) {
2428
2471
  return null;
2429
2472
  }
2430
2473
 
2431
- export { Block, CatchBoundary, CatchBoundaryImpl, ErrorComponent, FileRoute, Link, Match, MatchRoute, Matches, Navigate, Outlet, PathParamError, RootRoute, Route, Router, RouterProvider, ScrollRestoration, SearchParamError, cleanPath, componentTypes, createRouteMask, decode, deepEqual, defaultParseSearch, defaultStringifySearch, encode, functionalUpdate, getInitialRouterState, getRouteMatch, interpolatePath, isPlainObject, isRedirect, isServer, joinPaths, last, lazyFn, lazyRouteComponent, matchByPath, matchPathname, matchesContext, parsePathname, parseSearchWith, pick, redirect, replaceEqualDeep, resolvePath, rootRouteId, rootRouteWithContext, routerContext, shallow, stringifySearchWith, trimPath, trimPathLeft, trimPathRight, typedNavigate, useBlocker, useLayoutEffect$1 as useLayoutEffect, useLinkProps, useLoaderData, useMatch, useMatchRoute, useMatches, useNavigate, useParams, useRouteContext, useRouter, useRouterState, useScrollRestoration, useSearch, useStableCallback };
2474
+ export { Await, Block, CatchBoundary, CatchBoundaryImpl, ErrorComponent, FileRoute, Link, Match, MatchRoute, Matches, Navigate, Outlet, PathParamError, RootRoute, Route, Router, RouterProvider, ScrollRestoration, SearchParamError, cleanPath, componentTypes, createRouteMask, decode, deepEqual, defaultParseSearch, defaultStringifySearch, defer, encode, escapeJSON, functionalUpdate, getInitialRouterState, getRouteMatch, interpolatePath, isDehydratedDeferred, isPlainObject, isRedirect, isServer, joinPaths, last, lazyFn, lazyRouteComponent, matchByPath, matchPathname, matchesContext, parsePathname, parseSearchWith, pick, redirect, replaceEqualDeep, resolvePath, rootRouteId, rootRouteWithContext, routerContext, shallow, stringifySearchWith, trimPath, trimPathLeft, trimPathRight, typedNavigate, useAwaited, useBlocker, useLayoutEffect$1 as useLayoutEffect, useLinkProps, useLoaderData, useMatch, useMatchRoute, useMatches, useNavigate, useParams, useRouteContext, useRouter, useRouterState, useScrollRestoration, useSearch, useStableCallback };
2432
2475
  //# sourceMappingURL=index.js.map