@tanstack/react-router 0.0.1-beta.269 → 0.0.1-beta.270

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.
@@ -301,6 +301,346 @@ function escapeJSON(jsonString) {
301
301
  .replace(/"/g, '\\"'); // Escape double quotes
302
302
  }
303
303
 
304
+ const matchContext = /*#__PURE__*/React.createContext(undefined);
305
+ function Matches() {
306
+ const router = useRouter();
307
+ const matchId = useRouterState({
308
+ select: s => {
309
+ return getRenderedMatches(s)[0]?.id;
310
+ }
311
+ });
312
+ return /*#__PURE__*/React.createElement(matchContext.Provider, {
313
+ value: matchId
314
+ }, /*#__PURE__*/React.createElement(CatchBoundary, {
315
+ getResetKey: () => router.state.resolvedLocation.state?.key,
316
+ errorComponent: ErrorComponent,
317
+ onCatch: () => {
318
+ warning(false, `Error in router! Consider setting an 'errorComponent' in your RootRoute! 👍`);
319
+ }
320
+ }, matchId ? /*#__PURE__*/React.createElement(Match, {
321
+ matchId: matchId
322
+ }) : null));
323
+ }
324
+ function SafeFragment(props) {
325
+ return /*#__PURE__*/React.createElement(React.Fragment, null, props.children);
326
+ }
327
+ function Match({
328
+ matchId
329
+ }) {
330
+ const router = useRouter();
331
+ const routeId = useRouterState({
332
+ select: s => getRenderedMatches(s).find(d => d.id === matchId)?.routeId
333
+ });
334
+ invariant(routeId, `Could not find routeId for matchId "${matchId}". Please file an issue!`);
335
+ const route = router.routesById[routeId];
336
+ const PendingComponent = route.options.pendingComponent ?? router.options.defaultPendingComponent;
337
+ const routeErrorComponent = route.options.errorComponent ?? router.options.defaultErrorComponent ?? ErrorComponent;
338
+ const ResolvedSuspenseBoundary = route.options.wrapInSuspense ?? PendingComponent ?? route.options.component?.preload ?? route.options.pendingComponent?.preload ?? route.options.errorComponent?.preload ? React.Suspense : SafeFragment;
339
+ const ResolvedCatchBoundary = routeErrorComponent ? CatchBoundary : SafeFragment;
340
+ return /*#__PURE__*/React.createElement(matchContext.Provider, {
341
+ value: matchId
342
+ }, /*#__PURE__*/React.createElement(ResolvedSuspenseBoundary, {
343
+ fallback: PendingComponent
344
+ }, /*#__PURE__*/React.createElement(ResolvedCatchBoundary, {
345
+ getResetKey: () => router.state.resolvedLocation.state?.key,
346
+ errorComponent: routeErrorComponent,
347
+ onCatch: () => {
348
+ warning(false, `Error in route match: ${matchId}`);
349
+ }
350
+ }, /*#__PURE__*/React.createElement(MatchInner, {
351
+ matchId: matchId,
352
+ pendingElement: PendingComponent
353
+ }))));
354
+ }
355
+ function MatchInner({
356
+ matchId,
357
+ pendingElement
358
+ }) {
359
+ const router = useRouter();
360
+ const routeId = useRouterState({
361
+ select: s => getRenderedMatches(s).find(d => d.id === matchId)?.routeId
362
+ });
363
+ const route = router.routesById[routeId];
364
+ const match = useRouterState({
365
+ select: s => pick(getRenderedMatches(s).find(d => d.id === matchId), ['status', 'error', 'showPending', 'loadPromise'])
366
+ });
367
+ if (match.status === 'error') {
368
+ throw match.error;
369
+ }
370
+ if (match.status === 'pending') {
371
+ if (match.showPending) {
372
+ return pendingElement || null;
373
+ }
374
+ throw match.loadPromise;
375
+ }
376
+ if (match.status === 'success') {
377
+ let Comp = route.options.component ?? router.options.defaultComponent;
378
+ if (Comp) {
379
+ return /*#__PURE__*/React.createElement(Comp, null);
380
+ }
381
+ return /*#__PURE__*/React.createElement(Outlet, null);
382
+ }
383
+ invariant(false, 'Idle routeMatch status encountered during rendering! You should never see this. File an issue!');
384
+ }
385
+ const Outlet = /*#__PURE__*/React.memo(function Outlet() {
386
+ const matchId = React.useContext(matchContext);
387
+ const childMatchId = useRouterState({
388
+ select: s => {
389
+ const matches = getRenderedMatches(s);
390
+ const index = matches.findIndex(d => d.id === matchId);
391
+ return matches[index + 1]?.id;
392
+ }
393
+ });
394
+ if (!childMatchId) {
395
+ return null;
396
+ }
397
+ return /*#__PURE__*/React.createElement(Match, {
398
+ matchId: childMatchId
399
+ });
400
+ });
401
+ function useMatchRoute() {
402
+ const {
403
+ matchRoute
404
+ } = useRouter();
405
+ return React.useCallback(opts => {
406
+ const {
407
+ pending,
408
+ caseSensitive,
409
+ ...rest
410
+ } = opts;
411
+ return matchRoute(rest, {
412
+ pending,
413
+ caseSensitive
414
+ });
415
+ }, []);
416
+ }
417
+ function MatchRoute(props) {
418
+ const matchRoute = useMatchRoute();
419
+ const params = matchRoute(props);
420
+ if (typeof props.children === 'function') {
421
+ return props.children(params);
422
+ }
423
+ return !!params ? props.children : null;
424
+ }
425
+ function getRenderedMatches(state) {
426
+ return state.pendingMatches?.some(d => d.showPending) ? state.pendingMatches : state.matches;
427
+ }
428
+ function useMatch(opts) {
429
+ const router = useRouter();
430
+ const nearestMatchId = React.useContext(matchContext);
431
+ const nearestMatchRouteId = getRenderedMatches(router.state).find(d => d.id === nearestMatchId)?.routeId;
432
+ const matchRouteId = (() => {
433
+ const matches = getRenderedMatches(router.state);
434
+ const match = opts?.from ? matches.find(d => d.routeId === opts?.from) : matches.find(d => d.id === nearestMatchId);
435
+ return match.routeId;
436
+ })();
437
+ if (opts?.strict ?? true) {
438
+ 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?`);
439
+ }
440
+ const matchSelection = useRouterState({
441
+ select: state => {
442
+ const match = getRenderedMatches(state).find(d => d.id === nearestMatchId);
443
+ invariant(match, `Could not find ${opts?.from ? `an active match from "${opts.from}"` : 'a nearest match!'}`);
444
+ return opts?.select ? opts.select(match) : match;
445
+ }
446
+ });
447
+ return matchSelection;
448
+ }
449
+ function useMatches(opts) {
450
+ return useRouterState({
451
+ select: state => {
452
+ let matches = getRenderedMatches(state);
453
+ return opts?.select ? opts.select(matches) : matches;
454
+ }
455
+ });
456
+ }
457
+ function useParentMatches(opts) {
458
+ const contextMatchId = React.useContext(matchContext);
459
+ return useMatches({
460
+ select: matches => {
461
+ matches = matches.slice(matches.findIndex(d => d.id === contextMatchId));
462
+ return opts?.select ? opts.select(matches) : matches;
463
+ }
464
+ });
465
+ }
466
+ function useLoaderData(opts) {
467
+ return useMatch({
468
+ ...opts,
469
+ select: s => {
470
+ return typeof opts.select === 'function' ? opts.select(s?.loaderData) : s?.loaderData;
471
+ }
472
+ });
473
+ }
474
+
475
+ const routerContext = /*#__PURE__*/React.createContext(null);
476
+ if (typeof document !== 'undefined') {
477
+ window.__TSR_ROUTER_CONTEXT__ = routerContext;
478
+ }
479
+ function RouterProvider({
480
+ router,
481
+ ...rest
482
+ }) {
483
+ // Allow the router to update options on the router instance
484
+ router.update({
485
+ ...router.options,
486
+ ...rest,
487
+ context: {
488
+ ...router.options.context,
489
+ ...rest?.context
490
+ }
491
+ });
492
+ const inner = /*#__PURE__*/React.createElement(routerContext.Provider, {
493
+ value: router
494
+ }, /*#__PURE__*/React.createElement(Matches, null), /*#__PURE__*/React.createElement(Transitioner, null));
495
+ if (router.options.Wrap) {
496
+ return /*#__PURE__*/React.createElement(router.options.Wrap, null, inner);
497
+ }
498
+ return inner;
499
+ }
500
+ function Transitioner() {
501
+ const router = useRouter();
502
+ const routerState = useRouterState({
503
+ select: s => pick(s, ['isLoading', 'location', 'resolvedLocation', 'isTransitioning'])
504
+ });
505
+ const [isTransitioning, startReactTransition] = React.useTransition();
506
+ router.startReactTransition = startReactTransition;
507
+ React.useEffect(() => {
508
+ if (isTransitioning) {
509
+ router.__store.setState(s => ({
510
+ ...s,
511
+ isTransitioning
512
+ }));
513
+ }
514
+ }, [isTransitioning]);
515
+ const tryLoad = () => {
516
+ const apply = cb => {
517
+ if (!routerState.isTransitioning) {
518
+ startReactTransition(() => cb());
519
+ } else {
520
+ cb();
521
+ }
522
+ };
523
+ apply(() => {
524
+ try {
525
+ router.load();
526
+ } catch (err) {
527
+ console.error(err);
528
+ }
529
+ });
530
+ };
531
+ useLayoutEffect$1(() => {
532
+ const unsub = router.history.subscribe(() => {
533
+ router.latestLocation = router.parseLocation(router.latestLocation);
534
+ if (routerState.location !== router.latestLocation) {
535
+ tryLoad();
536
+ }
537
+ });
538
+ const nextLocation = router.buildLocation({
539
+ search: true,
540
+ params: true,
541
+ hash: true,
542
+ state: true
543
+ });
544
+ if (routerState.location.href !== nextLocation.href) {
545
+ router.commitLocation({
546
+ ...nextLocation,
547
+ replace: true
548
+ });
549
+ }
550
+ return () => {
551
+ unsub();
552
+ };
553
+ }, [router.history]);
554
+ useLayoutEffect$1(() => {
555
+ if (routerState.isTransitioning && !isTransitioning && !routerState.isLoading && routerState.resolvedLocation !== routerState.location) {
556
+ router.emit({
557
+ type: 'onResolved',
558
+ fromLocation: routerState.resolvedLocation,
559
+ toLocation: routerState.location,
560
+ pathChanged: routerState.location.href !== routerState.resolvedLocation?.href
561
+ });
562
+ if (document.querySelector) {
563
+ const el = document.getElementById(routerState.location.hash);
564
+ if (el) {
565
+ el.scrollIntoView();
566
+ }
567
+ }
568
+ router.pendingMatches = [];
569
+ router.__store.setState(s => ({
570
+ ...s,
571
+ isTransitioning: false,
572
+ resolvedLocation: s.location
573
+ }));
574
+ }
575
+ }, [routerState.isTransitioning, isTransitioning, routerState.isLoading, routerState.resolvedLocation, routerState.location]);
576
+ useLayoutEffect$1(() => {
577
+ if (!window.__TSR_DEHYDRATED__) {
578
+ tryLoad();
579
+ }
580
+ }, []);
581
+ return null;
582
+ }
583
+ function getRouteMatch(state, id) {
584
+ return [...(state.pendingMatches ?? []), ...state.matches].find(d => d.id === id);
585
+ }
586
+ function useRouterState(opts) {
587
+ const router = useRouter();
588
+ return useStore(router.__store, opts?.select);
589
+ }
590
+ function useRouter() {
591
+ const resolvedContext = typeof document !== 'undefined' ? window.__TSR_ROUTER_CONTEXT__ || routerContext : routerContext;
592
+ const value = React.useContext(resolvedContext);
593
+ warning(value, 'useRouter must be used inside a <RouterProvider> component!');
594
+ return value;
595
+ }
596
+
597
+ function defer(_promise) {
598
+ const promise = _promise;
599
+ if (!promise.__deferredState) {
600
+ promise.__deferredState = {
601
+ uid: Math.random().toString(36).slice(2),
602
+ status: 'pending'
603
+ };
604
+ const state = promise.__deferredState;
605
+ promise.then(data => {
606
+ state.status = 'success';
607
+ state.data = data;
608
+ }).catch(error => {
609
+ state.status = 'error';
610
+ state.error = error;
611
+ });
612
+ }
613
+ return promise;
614
+ }
615
+ function isDehydratedDeferred(obj) {
616
+ return typeof obj === 'object' && obj !== null && !(obj instanceof Promise) && !obj.then && '__deferredState' in obj;
617
+ }
618
+
619
+ function useAwaited({
620
+ promise
621
+ }) {
622
+ const router = useRouter();
623
+ let state = promise.__deferredState;
624
+ const key = `__TSR__DEFERRED__${state.uid}`;
625
+ if (isDehydratedDeferred(promise)) {
626
+ state = router.hydrateData(key);
627
+ promise = Promise.resolve(state.data);
628
+ promise.__deferredState = state;
629
+ }
630
+ if (state.status === 'pending') {
631
+ throw new Promise(r => setTimeout(r, 1)).then(() => promise);
632
+ }
633
+ if (state.status === 'error') {
634
+ throw state.error;
635
+ }
636
+ router.dehydrateData(key, state);
637
+ return [state.data];
638
+ }
639
+ function Await(props) {
640
+ const awaited = useAwaited(props);
641
+ return props.children(...awaited);
642
+ }
643
+
304
644
  function joinPaths(paths) {
305
645
  return cleanPath(paths.filter(Boolean).join('/'));
306
646
  }
@@ -476,133 +816,47 @@ function matchByPath(basepath, from, matchLocation) {
476
816
  }
477
817
  if (baseSegment.value.charAt(0) !== '$') {
478
818
  params[routeSegment.value.substring(1)] = baseSegment.value;
479
- }
480
- }
481
- }
482
- if (!isLastBaseSegment && isLastRouteSegment) {
483
- params['**'] = joinPaths(baseSegments.slice(i + 1).map(d => d.value));
484
- return !!matchLocation.fuzzy && routeSegment?.value !== '/';
485
- }
486
- }
487
- return true;
488
- })();
489
- return isMatch ? params : undefined;
490
- }
491
-
492
- function useParams(opts) {
493
- return useRouterState({
494
- select: state => {
495
- const params = last(state.matches)?.params;
496
- return opts?.select ? opts.select(params) : params;
497
- }
498
- });
499
- }
500
-
501
- function useSearch(opts) {
502
- return useMatch({
503
- ...opts,
504
- select: match => {
505
- return opts?.select ? opts.select(match.search) : match.search;
506
- }
507
- });
508
- }
509
-
510
- const rootRouteId = '__root__';
511
-
512
- // The parse type here allows a zod schema to be passed directly to the validator
513
-
514
- class RouteApi {
515
- constructor({
516
- id
517
- }) {
518
- this.id = id;
519
- }
520
- useMatch = opts => {
521
- return useMatch({
522
- ...opts,
523
- from: this.id
524
- });
525
- };
526
- useRouteContext = opts => {
527
- return useMatch({
528
- ...opts,
529
- from: this.id,
530
- select: d => opts?.select ? opts.select(d.context) : d.context
531
- });
532
- };
533
- useSearch = opts => {
534
- return useSearch({
535
- ...opts,
536
- from: this.id
537
- });
538
- };
539
- useParams = opts => {
540
- return useParams({
541
- ...opts,
542
- from: this.id
543
- });
544
- };
545
- useLoaderData = opts => {
546
- return useLoaderData({
547
- ...opts,
548
- from: this.id
549
- });
550
- };
819
+ }
820
+ }
821
+ }
822
+ if (!isLastBaseSegment && isLastRouteSegment) {
823
+ params['**'] = joinPaths(baseSegments.slice(i + 1).map(d => d.value));
824
+ return !!matchLocation.fuzzy && routeSegment?.value !== '/';
825
+ }
826
+ }
827
+ return true;
828
+ })();
829
+ return isMatch ? params : undefined;
551
830
  }
552
- class Route {
553
- // Set up in this.init()
554
-
555
- // customId!: TCustomId
556
-
557
- // Optional
558
831
 
559
- constructor(options) {
560
- this.options = options || {};
561
- this.isRoot = !options?.getParentRoute;
562
- invariant(!(options?.id && options?.path), `Route cannot have both an 'id' and a 'path' option.`);
563
- this.$$typeof = Symbol.for('react.memo');
564
- }
565
- init = opts => {
566
- this.originalIndex = opts.originalIndex;
567
- const options = this.options;
568
- const isRoot = !options?.path && !options?.id;
569
- this.parentRoute = this.options?.getParentRoute?.();
570
- if (isRoot) {
571
- this.path = rootRouteId;
572
- } else {
573
- invariant(this.parentRoute, `Child Route instances must pass a 'getParentRoute: () => ParentRoute' option that returns a Route instance.`);
832
+ function useParams(opts) {
833
+ return useRouterState({
834
+ select: state => {
835
+ const params = last(state.matches)?.params;
836
+ return opts?.select ? opts.select(params) : params;
574
837
  }
575
- let path = isRoot ? rootRouteId : options.path;
838
+ });
839
+ }
576
840
 
577
- // If the path is anything other than an index path, trim it up
578
- if (path && path !== '/') {
579
- path = trimPath(path);
841
+ function useSearch(opts) {
842
+ return useMatch({
843
+ ...opts,
844
+ select: match => {
845
+ return opts?.select ? opts.select(match.search) : match.search;
580
846
  }
581
- const customId = options?.id || path;
847
+ });
848
+ }
582
849
 
583
- // Strip the parentId prefix from the first level of children
584
- let id = isRoot ? rootRouteId : joinPaths([this.parentRoute.id === rootRouteId ? '' : this.parentRoute.id, customId]);
585
- if (path === rootRouteId) {
586
- path = '/';
587
- }
588
- if (id !== rootRouteId) {
589
- id = joinPaths(['/', id]);
590
- }
591
- const fullPath = id === rootRouteId ? '/' : joinPaths([this.parentRoute.fullPath, path]);
592
- this.path = path;
850
+ const rootRouteId = '__root__';
851
+
852
+ // The parse type here allows a zod schema to be passed directly to the validator
853
+
854
+ class RouteApi {
855
+ constructor({
856
+ id
857
+ }) {
593
858
  this.id = id;
594
- // this.customId = customId as TCustomId
595
- this.fullPath = fullPath;
596
- this.to = fullPath;
597
- };
598
- addChildren = children => {
599
- this.children = children;
600
- return this;
601
- };
602
- update = options => {
603
- Object.assign(this.options, options);
604
- return this;
605
- };
859
+ }
606
860
  useMatch = opts => {
607
861
  return useMatch({
608
862
  ...opts,
@@ -635,393 +889,115 @@ class Route {
635
889
  });
636
890
  };
637
891
  }
638
- function rootRouteWithContext() {
639
- return options => {
640
- return new RootRoute(options);
641
- };
642
- }
643
- class RootRoute extends Route {
644
- constructor(options) {
645
- super(options);
646
- }
647
- }
648
- function createRouteMask(opts) {
649
- return opts;
650
- }
651
-
652
- //
653
-
654
- class NotFoundRoute extends Route {
655
- constructor(options) {
656
- super({
657
- ...options,
658
- id: '404'
659
- });
660
- }
661
- }
662
-
663
- const matchContext = /*#__PURE__*/React.createContext(undefined);
664
- function Matches() {
665
- const router = useRouter();
666
- const matchId = useRouterState({
667
- select: s => {
668
- return getRenderedMatches(s)[0]?.id;
669
- }
670
- });
671
- const route = router.routesById[rootRouteId];
672
- const errorComponent = React.useCallback(props => {
673
- return /*#__PURE__*/React.createElement(ErrorComponent, {
674
- ...props,
675
- useMatch: route.useMatch,
676
- useRouteContext: route.useRouteContext,
677
- useSearch: route.useSearch,
678
- useParams: route.useParams
679
- });
680
- }, [route]);
681
- return /*#__PURE__*/React.createElement(matchContext.Provider, {
682
- value: matchId
683
- }, /*#__PURE__*/React.createElement(CatchBoundary, {
684
- getResetKey: () => router.state.resolvedLocation.state?.key,
685
- errorComponent: errorComponent,
686
- onCatch: () => {
687
- warning(false, `Error in router! Consider setting an 'errorComponent' in your RootRoute! 👍`);
688
- }
689
- }, matchId ? /*#__PURE__*/React.createElement(Match, {
690
- matchId: matchId
691
- }) : null));
692
- }
693
- function SafeFragment(props) {
694
- return /*#__PURE__*/React.createElement(React.Fragment, null, props.children);
695
- }
696
- function Match({
697
- matchId
698
- }) {
699
- const router = useRouter();
700
- const routeId = useRouterState({
701
- select: s => getRenderedMatches(s).find(d => d.id === matchId)?.routeId
702
- });
703
- invariant(routeId, `Could not find routeId for matchId "${matchId}". Please file an issue!`);
704
- const route = router.routesById[routeId];
705
- const PendingComponent = route.options.pendingComponent ?? router.options.defaultPendingComponent;
706
- const pendingElement = PendingComponent ? /*#__PURE__*/React.createElement(PendingComponent, {
707
- useMatch: route.useMatch,
708
- useRouteContext: route.useRouteContext,
709
- useSearch: route.useSearch,
710
- useParams: route.useParams
711
- }) : undefined;
712
- const routeErrorComponent = route.options.errorComponent ?? router.options.defaultErrorComponent ?? ErrorComponent;
713
- const ResolvedSuspenseBoundary = route.options.wrapInSuspense ?? pendingElement ? React.Suspense : SafeFragment;
714
- const errorComponent = routeErrorComponent ? React.useCallback(props => {
715
- return /*#__PURE__*/React.createElement(routeErrorComponent, {
716
- ...props,
717
- useMatch: route.useMatch,
718
- useRouteContext: route.useRouteContext,
719
- useSearch: route.useSearch,
720
- useParams: route.useParams
721
- });
722
- }, [route]) : undefined;
723
- const ResolvedCatchBoundary = errorComponent ? CatchBoundary : SafeFragment;
724
- return /*#__PURE__*/React.createElement(matchContext.Provider, {
725
- value: matchId
726
- }, /*#__PURE__*/React.createElement(ResolvedSuspenseBoundary, {
727
- fallback: pendingElement
728
- }, /*#__PURE__*/React.createElement(ResolvedCatchBoundary, {
729
- getResetKey: () => router.state.resolvedLocation.state?.key,
730
- errorComponent: errorComponent,
731
- onCatch: () => {
732
- warning(false, `Error in route match: ${matchId}`);
733
- }
734
- }, /*#__PURE__*/React.createElement(MatchInner, {
735
- matchId: matchId,
736
- pendingElement: pendingElement
737
- }))));
738
- }
739
- function MatchInner({
740
- matchId,
741
- pendingElement
742
- }) {
743
- const router = useRouter();
744
- const routeId = useRouterState({
745
- select: s => getRenderedMatches(s).find(d => d.id === matchId)?.routeId
746
- });
747
- const route = router.routesById[routeId];
748
- const match = useRouterState({
749
- select: s => pick(getRenderedMatches(s).find(d => d.id === matchId), ['status', 'error', 'showPending', 'loadPromise'])
750
- });
751
- if (match.status === 'error') {
752
- throw match.error;
753
- }
754
- if (match.status === 'pending') {
755
- if (match.showPending) {
756
- return pendingElement || null;
757
- }
758
- throw match.loadPromise;
759
- }
760
- if (match.status === 'success') {
761
- let comp = route.options.component ?? router.options.defaultComponent;
762
- if (comp) {
763
- return /*#__PURE__*/React.createElement(comp, {
764
- useMatch: route.useMatch,
765
- useRouteContext: route.useRouteContext,
766
- useSearch: route.useSearch,
767
- useParams: route.useParams,
768
- useLoaderData: route.useLoaderData
769
- });
770
- }
771
- return /*#__PURE__*/React.createElement(Outlet, null);
772
- }
773
- invariant(false, 'Idle routeMatch status encountered during rendering! You should never see this. File an issue!');
774
- }
775
- const Outlet = /*#__PURE__*/React.memo(function Outlet() {
776
- const matchId = React.useContext(matchContext);
777
- const childMatchId = useRouterState({
778
- select: s => {
779
- const matches = getRenderedMatches(s);
780
- const index = matches.findIndex(d => d.id === matchId);
781
- return matches[index + 1]?.id;
782
- }
783
- });
784
- if (!childMatchId) {
785
- return null;
786
- }
787
- return /*#__PURE__*/React.createElement(Match, {
788
- matchId: childMatchId
789
- });
790
- });
791
- function useMatchRoute() {
792
- const {
793
- matchRoute
794
- } = useRouter();
795
- return React.useCallback(opts => {
796
- const {
797
- pending,
798
- caseSensitive,
799
- ...rest
800
- } = opts;
801
- return matchRoute(rest, {
802
- pending,
803
- caseSensitive
804
- });
805
- }, []);
806
- }
807
- function MatchRoute(props) {
808
- const matchRoute = useMatchRoute();
809
- const params = matchRoute(props);
810
- if (typeof props.children === 'function') {
811
- return props.children(params);
812
- }
813
- return !!params ? props.children : null;
814
- }
815
- function getRenderedMatches(state) {
816
- return state.pendingMatches?.some(d => d.showPending) ? state.pendingMatches : state.matches;
817
- }
818
- function useMatch(opts) {
819
- const router = useRouter();
820
- const nearestMatchId = React.useContext(matchContext);
821
- const nearestMatchRouteId = getRenderedMatches(router.state).find(d => d.id === nearestMatchId)?.routeId;
822
- const matchRouteId = (() => {
823
- const matches = getRenderedMatches(router.state);
824
- const match = opts?.from ? matches.find(d => d.routeId === opts?.from) : matches.find(d => d.id === nearestMatchId);
825
- return match.routeId;
826
- })();
827
- if (opts?.strict ?? true) {
828
- 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?`);
829
- }
830
- const matchSelection = useRouterState({
831
- select: state => {
832
- const match = getRenderedMatches(state).find(d => d.id === nearestMatchId);
833
- invariant(match, `Could not find ${opts?.from ? `an active match from "${opts.from}"` : 'a nearest match!'}`);
834
- return opts?.select ? opts.select(match) : match;
835
- }
836
- });
837
- return matchSelection;
838
- }
839
- function useMatches(opts) {
840
- const contextMatchId = React.useContext(matchContext);
841
- return useRouterState({
842
- select: state => {
843
- let matches = getRenderedMatches(state);
844
- matches = matches.slice(matches.findIndex(d => d.id === contextMatchId));
845
- return opts?.select ? opts.select(matches) : matches;
846
- }
847
- });
848
- }
849
- function useLoaderData(opts) {
850
- return useMatch({
851
- ...opts,
852
- select: s => {
853
- return typeof opts.select === 'function' ? opts.select(s?.loaderData) : s?.loaderData;
892
+ class Route {
893
+ // Set up in this.init()
894
+
895
+ // customId!: TCustomId
896
+
897
+ // Optional
898
+
899
+ constructor(options) {
900
+ this.options = options || {};
901
+ this.isRoot = !options?.getParentRoute;
902
+ invariant(!(options?.id && options?.path), `Route cannot have both an 'id' and a 'path' option.`);
903
+ this.$$typeof = Symbol.for('react.memo');
904
+ }
905
+ init = opts => {
906
+ this.originalIndex = opts.originalIndex;
907
+ const options = this.options;
908
+ const isRoot = !options?.path && !options?.id;
909
+ this.parentRoute = this.options?.getParentRoute?.();
910
+ if (isRoot) {
911
+ this.path = rootRouteId;
912
+ } else {
913
+ invariant(this.parentRoute, `Child Route instances must pass a 'getParentRoute: () => ParentRoute' option that returns a Route instance.`);
854
914
  }
855
- });
856
- }
915
+ let path = isRoot ? rootRouteId : options.path;
857
916
 
858
- const routerContext = /*#__PURE__*/React.createContext(null);
859
- if (typeof document !== 'undefined') {
860
- window.__TSR_ROUTER_CONTEXT__ = routerContext;
861
- }
862
- function RouterProvider({
863
- router,
864
- ...rest
865
- }) {
866
- // Allow the router to update options on the router instance
867
- router.update({
868
- ...router.options,
869
- ...rest,
870
- context: {
871
- ...router.options.context,
872
- ...rest?.context
917
+ // If the path is anything other than an index path, trim it up
918
+ if (path && path !== '/') {
919
+ path = trimPath(path);
873
920
  }
874
- });
875
- const inner = /*#__PURE__*/React.createElement(routerContext.Provider, {
876
- value: router
877
- }, /*#__PURE__*/React.createElement(Matches, null), /*#__PURE__*/React.createElement(Transitioner, null));
878
- if (router.options.Wrap) {
879
- return /*#__PURE__*/React.createElement(router.options.Wrap, null, inner);
880
- }
881
- return inner;
882
- }
883
- function Transitioner() {
884
- const router = useRouter();
885
- const routerState = useRouterState({
886
- select: s => pick(s, ['isLoading', 'location', 'resolvedLocation', 'isTransitioning'])
887
- });
888
- const [isTransitioning, startReactTransition] = React.useTransition();
889
- router.startReactTransition = startReactTransition;
890
- React.useEffect(() => {
891
- if (isTransitioning) {
892
- router.__store.setState(s => ({
893
- ...s,
894
- isTransitioning
895
- }));
921
+ const customId = options?.id || path;
922
+
923
+ // Strip the parentId prefix from the first level of children
924
+ let id = isRoot ? rootRouteId : joinPaths([this.parentRoute.id === rootRouteId ? '' : this.parentRoute.id, customId]);
925
+ if (path === rootRouteId) {
926
+ path = '/';
896
927
  }
897
- }, [isTransitioning]);
898
- const tryLoad = () => {
899
- const apply = cb => {
900
- if (!routerState.isTransitioning) {
901
- startReactTransition(() => cb());
902
- } else {
903
- cb();
904
- }
905
- };
906
- apply(() => {
907
- try {
908
- router.load();
909
- } catch (err) {
910
- console.error(err);
911
- }
928
+ if (id !== rootRouteId) {
929
+ id = joinPaths(['/', id]);
930
+ }
931
+ const fullPath = id === rootRouteId ? '/' : joinPaths([this.parentRoute.fullPath, path]);
932
+ this.path = path;
933
+ this.id = id;
934
+ // this.customId = customId as TCustomId
935
+ this.fullPath = fullPath;
936
+ this.to = fullPath;
937
+ };
938
+ addChildren = children => {
939
+ this.children = children;
940
+ return this;
941
+ };
942
+ update = options => {
943
+ Object.assign(this.options, options);
944
+ return this;
945
+ };
946
+ useMatch = opts => {
947
+ return useMatch({
948
+ ...opts,
949
+ from: this.id
912
950
  });
913
951
  };
914
- useLayoutEffect$1(() => {
915
- const unsub = router.history.subscribe(() => {
916
- router.latestLocation = router.parseLocation(router.latestLocation);
917
- if (routerState.location !== router.latestLocation) {
918
- tryLoad();
919
- }
952
+ useRouteContext = opts => {
953
+ return useMatch({
954
+ ...opts,
955
+ from: this.id,
956
+ select: d => opts?.select ? opts.select(d.context) : d.context
920
957
  });
921
- const nextLocation = router.buildLocation({
922
- search: true,
923
- params: true,
924
- hash: true,
925
- state: true
958
+ };
959
+ useSearch = opts => {
960
+ return useSearch({
961
+ ...opts,
962
+ from: this.id
926
963
  });
927
- if (routerState.location.href !== nextLocation.href) {
928
- router.commitLocation({
929
- ...nextLocation,
930
- replace: true
931
- });
932
- }
933
- return () => {
934
- unsub();
935
- };
936
- }, [router.history]);
937
- useLayoutEffect$1(() => {
938
- if (routerState.isTransitioning && !isTransitioning && !routerState.isLoading && routerState.resolvedLocation !== routerState.location) {
939
- router.emit({
940
- type: 'onResolved',
941
- fromLocation: routerState.resolvedLocation,
942
- toLocation: routerState.location,
943
- pathChanged: routerState.location.href !== routerState.resolvedLocation?.href
944
- });
945
- if (document.querySelector) {
946
- const el = document.getElementById(routerState.location.hash);
947
- if (el) {
948
- el.scrollIntoView();
949
- }
950
- }
951
- router.pendingMatches = [];
952
- router.__store.setState(s => ({
953
- ...s,
954
- isTransitioning: false,
955
- resolvedLocation: s.location
956
- }));
957
- }
958
- }, [routerState.isTransitioning, isTransitioning, routerState.isLoading, routerState.resolvedLocation, routerState.location]);
959
- useLayoutEffect$1(() => {
960
- if (!window.__TSR_DEHYDRATED__) {
961
- tryLoad();
962
- }
963
- }, []);
964
- return null;
965
- }
966
- function getRouteMatch(state, id) {
967
- return [...(state.pendingMatches ?? []), ...state.matches].find(d => d.id === id);
968
- }
969
- function useRouterState(opts) {
970
- const router = useRouter();
971
- return useStore(router.__store, opts?.select);
964
+ };
965
+ useParams = opts => {
966
+ return useParams({
967
+ ...opts,
968
+ from: this.id
969
+ });
970
+ };
971
+ useLoaderData = opts => {
972
+ return useLoaderData({
973
+ ...opts,
974
+ from: this.id
975
+ });
976
+ };
972
977
  }
973
- function useRouter() {
974
- const resolvedContext = typeof document !== 'undefined' ? window.__TSR_ROUTER_CONTEXT__ || routerContext : routerContext;
975
- const value = React.useContext(resolvedContext);
976
- warning(value, 'useRouter must be used inside a <RouterProvider> component!');
977
- return value;
978
+ function rootRouteWithContext() {
979
+ return options => {
980
+ return new RootRoute(options);
981
+ };
978
982
  }
979
-
980
- function defer(_promise) {
981
- const promise = _promise;
982
- if (!promise.__deferredState) {
983
- promise.__deferredState = {
984
- uid: Math.random().toString(36).slice(2),
985
- status: 'pending'
986
- };
987
- const state = promise.__deferredState;
988
- promise.then(data => {
989
- state.status = 'success';
990
- state.data = data;
991
- }).catch(error => {
992
- state.status = 'error';
993
- state.error = error;
994
- });
983
+ class RootRoute extends Route {
984
+ constructor(options) {
985
+ super(options);
995
986
  }
996
- return promise;
997
987
  }
998
- function isDehydratedDeferred(obj) {
999
- return typeof obj === 'object' && obj !== null && !(obj instanceof Promise) && !obj.then && '__deferredState' in obj;
988
+ function createRouteMask(opts) {
989
+ return opts;
1000
990
  }
1001
991
 
1002
- function useAwaited({
1003
- promise
1004
- }) {
1005
- const router = useRouter();
1006
- let state = promise.__deferredState;
1007
- const key = `__TSR__DEFERRED__${state.uid}`;
1008
- if (isDehydratedDeferred(promise)) {
1009
- state = router.hydrateData(key);
1010
- promise = Promise.resolve(state.data);
1011
- promise.__deferredState = state;
1012
- }
1013
- if (state.status === 'pending') {
1014
- throw new Promise(r => setTimeout(r, 1)).then(() => promise);
1015
- }
1016
- if (state.status === 'error') {
1017
- throw state.error;
992
+ //
993
+
994
+ class NotFoundRoute extends Route {
995
+ constructor(options) {
996
+ super({
997
+ ...options,
998
+ id: '404'
999
+ });
1018
1000
  }
1019
- router.dehydrateData(key, state);
1020
- return [state.data];
1021
- }
1022
- function Await(props) {
1023
- const awaited = useAwaited(props);
1024
- return props.children(...awaited);
1025
1001
  }
1026
1002
 
1027
1003
  class FileRoute {
@@ -1702,7 +1678,6 @@ class Router {
1702
1678
  status: hasLoaders ? 'pending' : 'success',
1703
1679
  showPending: false,
1704
1680
  isFetching: false,
1705
- invalid: false,
1706
1681
  error: undefined,
1707
1682
  paramsError: parseErrors[index],
1708
1683
  loadPromise: Promise.resolve(),
@@ -2021,7 +1996,6 @@ class Router {
2021
1996
  matches[index] = match = {
2022
1997
  ...match,
2023
1998
  fetchedAt: Date.now(),
2024
- invalid: false,
2025
1999
  showPending: false
2026
2000
  };
2027
2001
  const pendingMs = route.options.pendingMs ?? this.options.defaultPendingMs;
@@ -2224,7 +2198,7 @@ class Router {
2224
2198
 
2225
2199
  //
2226
2200
  ;
2227
- [[exitingMatchIds, 'onLeave'], [enteringMatchIds, 'onEnter'], [stayingMatchIds, 'onTransition']].forEach(([matches, hook]) => {
2201
+ [[exitingMatchIds, 'onLeave'], [enteringMatchIds, 'onEnter'], [stayingMatchIds, 'onStay']].forEach(([matches, hook]) => {
2228
2202
  matches.forEach(match => {
2229
2203
  this.looseRoutesById[match.routeId].options[hook]?.(match);
2230
2204
  });
@@ -2314,7 +2288,7 @@ class Router {
2314
2288
  dehydrate = () => {
2315
2289
  return {
2316
2290
  state: {
2317
- dehydratedMatches: this.state.matches.map(d => pick(d, ['fetchedAt', 'invalid', 'id', 'status', 'updatedAt', 'loaderData']))
2291
+ dehydratedMatches: this.state.matches.map(d => pick(d, ['fetchedAt', 'id', 'status', 'updatedAt', 'loaderData']))
2318
2292
  }
2319
2293
  };
2320
2294
  };
@@ -2578,9 +2552,21 @@ function useNavigate(defaultOpts) {
2578
2552
  });
2579
2553
  }, []);
2580
2554
  }
2581
- function typedNavigate(navigate) {
2582
- return navigate;
2583
- } //
2555
+
2556
+ // NOTE: I don't know of anyone using this. It's undocumented, so let's wait until someone needs it
2557
+ // export function typedNavigate<
2558
+ // TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
2559
+ // TDefaultFrom extends RoutePaths<TRouteTree> = '/',
2560
+ // >(navigate: (opts: NavigateOptions<any>) => Promise<void>) {
2561
+ // return navigate as <
2562
+ // TFrom extends RoutePaths<TRouteTree> = TDefaultFrom,
2563
+ // TTo extends string = '',
2564
+ // TMaskFrom extends RoutePaths<TRouteTree> = '/',
2565
+ // TMaskTo extends string = '',
2566
+ // >(
2567
+ // opts?: NavigateOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo>,
2568
+ // ) => Promise<void>
2569
+ // } //
2584
2570
 
2585
2571
  function Navigate(props) {
2586
2572
  const {
@@ -2598,5 +2584,5 @@ function Navigate(props) {
2598
2584
  return null;
2599
2585
  }
2600
2586
 
2601
- export { Await, Block, CatchBoundary, CatchBoundaryImpl, ErrorComponent, FileRoute, Link, Match, MatchRoute, Matches, Navigate, NotFoundRoute, Outlet, PathParamError, RootRoute, Route, RouteApi, 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, matchContext, matchPathname, parsePathname, parseSearchWith, pick, redirect, removeBasepath, replaceEqualDeep, resolvePath, rootRouteId, rootRouteWithContext, routerContext, shallow, stringifySearchWith, trimPath, trimPathLeft, trimPathRight, typedNavigate, useAwaited, useBlocker, useElementScrollRestoration, useLayoutEffect$1 as useLayoutEffect, useLinkProps, useLoaderData, useMatch, useMatchRoute, useMatches, useNavigate, useParams, useRouteContext, useRouter, useRouterState, useScrollRestoration, useSearch, useStableCallback };
2587
+ export { Await, Block, CatchBoundary, CatchBoundaryImpl, ErrorComponent, FileRoute, Link, Match, MatchRoute, Matches, Navigate, NotFoundRoute, Outlet, PathParamError, RootRoute, Route, RouteApi, 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, matchContext, matchPathname, parsePathname, parseSearchWith, pick, redirect, removeBasepath, replaceEqualDeep, resolvePath, rootRouteId, rootRouteWithContext, routerContext, shallow, stringifySearchWith, trimPath, trimPathLeft, trimPathRight, useAwaited, useBlocker, useElementScrollRestoration, useLayoutEffect$1 as useLayoutEffect, useLinkProps, useLoaderData, useMatch, useMatchRoute, useMatches, useNavigate, useParams, useParentMatches, useRouteContext, useRouter, useRouterState, useScrollRestoration, useSearch, useStableCallback };
2602
2588
  //# sourceMappingURL=index.js.map