@tanstack/react-router 0.0.1-beta.284 → 0.0.1-beta.286

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.
@@ -163,6 +163,8 @@ function ErrorComponent({
163
163
  // // Using DeepMerge to merge TypeA and TypeB
164
164
  // type MergedType = Expand<AssignAll<[TypeA, TypeB, TypeC]>>
165
165
 
166
+ // from https://github.com/type-challenges/type-challenges/issues/737
167
+
166
168
  //
167
169
 
168
170
  const isServer = typeof document === 'undefined';
@@ -301,468 +303,110 @@ function escapeJSON(jsonString) {
301
303
  .replace(/"/g, '\\"'); // Escape double quotes
302
304
  }
303
305
 
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));
306
+ function joinPaths(paths) {
307
+ return cleanPath(paths.filter(Boolean).join('/'));
323
308
  }
324
- function SafeFragment(props) {
325
- return /*#__PURE__*/React.createElement(React.Fragment, null, props.children);
309
+ function cleanPath(path) {
310
+ // remove double slashes
311
+ return path.replace(/\/{2,}/g, '/');
326
312
  }
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 pendingElement = PendingComponent ? /*#__PURE__*/React.createElement(PendingComponent, null) : null;
338
- const routeErrorComponent = route.options.errorComponent ?? router.options.defaultErrorComponent ?? ErrorComponent;
339
- const ResolvedSuspenseBoundary = route.options.wrapInSuspense ?? PendingComponent ?? route.options.component?.preload ?? route.options.pendingComponent?.preload ?? route.options.errorComponent?.preload ? React.Suspense : SafeFragment;
340
- const ResolvedCatchBoundary = routeErrorComponent ? CatchBoundary : SafeFragment;
341
- return /*#__PURE__*/React.createElement(matchContext.Provider, {
342
- value: matchId
343
- }, /*#__PURE__*/React.createElement(ResolvedSuspenseBoundary, {
344
- fallback: pendingElement
345
- }, /*#__PURE__*/React.createElement(ResolvedCatchBoundary, {
346
- getResetKey: () => router.state.resolvedLocation.state?.key,
347
- errorComponent: routeErrorComponent,
348
- onCatch: () => {
349
- warning(false, `Error in route match: ${matchId}`);
350
- }
351
- }, /*#__PURE__*/React.createElement(MatchInner, {
352
- matchId: matchId,
353
- pendingElement: pendingElement
354
- }))));
313
+ function trimPathLeft(path) {
314
+ return path === '/' ? path : path.replace(/^\/{1,}/, '');
355
315
  }
356
- function MatchInner({
357
- matchId,
358
- pendingElement
359
- }) {
360
- const router = useRouter();
361
- const routeId = useRouterState({
362
- select: s => getRenderedMatches(s).find(d => d.id === matchId)?.routeId
363
- });
364
- const route = router.routesById[routeId];
365
- const match = useRouterState({
366
- select: s => pick(getRenderedMatches(s).find(d => d.id === matchId), ['status', 'error', 'showPending', 'loadPromise'])
367
- });
368
- if (match.status === 'error') {
369
- throw match.error;
370
- }
371
- if (match.status === 'pending') {
372
- if (match.showPending) {
373
- return pendingElement;
374
- }
375
- throw match.loadPromise;
376
- }
377
- if (match.status === 'success') {
378
- let Comp = route.options.component ?? router.options.defaultComponent;
379
- if (Comp) {
380
- return /*#__PURE__*/React.createElement(Comp, null);
381
- }
382
- return /*#__PURE__*/React.createElement(Outlet, null);
383
- }
384
- invariant(false, 'Idle routeMatch status encountered during rendering! You should never see this. File an issue!');
316
+ function trimPathRight(path) {
317
+ return path === '/' ? path : path.replace(/\/{1,}$/, '');
385
318
  }
386
- const Outlet = /*#__PURE__*/React.memo(function Outlet() {
387
- const matchId = React.useContext(matchContext);
388
- const childMatchId = useRouterState({
389
- select: s => {
390
- const matches = getRenderedMatches(s);
391
- const index = matches.findIndex(d => d.id === matchId);
392
- return matches[index + 1]?.id;
319
+ function trimPath(path) {
320
+ return trimPathRight(trimPathLeft(path));
321
+ }
322
+ function resolvePath(basepath, base, to) {
323
+ base = base.replace(new RegExp(`^${basepath}`), '/');
324
+ to = to.replace(new RegExp(`^${basepath}`), '/');
325
+ let baseSegments = parsePathname(base);
326
+ const toSegments = parsePathname(to);
327
+ toSegments.forEach((toSegment, index) => {
328
+ if (toSegment.value === '/') {
329
+ if (!index) {
330
+ // Leading slash
331
+ baseSegments = [toSegment];
332
+ } else if (index === toSegments.length - 1) {
333
+ // Trailing Slash
334
+ baseSegments.push(toSegment);
335
+ } else ;
336
+ } else if (toSegment.value === '..') {
337
+ // Extra trailing slash? pop it off
338
+ if (baseSegments.length > 1 && last(baseSegments)?.value === '/') {
339
+ baseSegments.pop();
340
+ }
341
+ baseSegments.pop();
342
+ } else if (toSegment.value === '.') {
343
+ return;
344
+ } else {
345
+ baseSegments.push(toSegment);
393
346
  }
394
347
  });
395
- if (!childMatchId) {
396
- return null;
348
+ const joined = joinPaths([basepath, ...baseSegments.map(d => d.value)]);
349
+ return cleanPath(joined);
350
+ }
351
+ function parsePathname(pathname) {
352
+ if (!pathname) {
353
+ return [];
397
354
  }
398
- return /*#__PURE__*/React.createElement(Match, {
399
- matchId: childMatchId
400
- });
401
- });
402
- function useMatchRoute() {
403
- useRouterState({
404
- select: s => [s.location, s.resolvedLocation]
405
- });
406
- const {
407
- matchRoute
408
- } = useRouter();
409
- return React.useCallback(opts => {
410
- const {
411
- pending,
412
- caseSensitive,
413
- ...rest
414
- } = opts;
415
- return matchRoute(rest, {
416
- pending,
417
- caseSensitive
355
+ pathname = cleanPath(pathname);
356
+ const segments = [];
357
+ if (pathname.slice(0, 1) === '/') {
358
+ pathname = pathname.substring(1);
359
+ segments.push({
360
+ type: 'pathname',
361
+ value: '/'
418
362
  });
419
- }, []);
420
- }
421
- function MatchRoute(props) {
422
- const matchRoute = useMatchRoute();
423
- const params = matchRoute(props);
424
- if (typeof props.children === 'function') {
425
- return props.children(params);
426
363
  }
427
- return !!params ? props.children : null;
428
- }
429
- function getRenderedMatches(state) {
430
- return state.pendingMatches?.some(d => d.showPending) ? state.pendingMatches : state.matches;
431
- }
432
- function useMatch(opts) {
433
- const router = useRouter();
434
- const nearestMatchId = React.useContext(matchContext);
435
- const nearestMatchRouteId = getRenderedMatches(router.state).find(d => d.id === nearestMatchId)?.routeId;
436
- const matchRouteId = (() => {
437
- const matches = getRenderedMatches(router.state);
438
- const match = opts?.from ? matches.find(d => d.routeId === opts?.from) : matches.find(d => d.id === nearestMatchId);
439
- return match.routeId;
440
- })();
441
- if (opts?.strict ?? true) {
442
- 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?`);
364
+ if (!pathname) {
365
+ return segments;
443
366
  }
444
- const matchSelection = useRouterState({
445
- select: state => {
446
- const match = getRenderedMatches(state).find(d => d.id === nearestMatchId);
447
- invariant(match, `Could not find ${opts?.from ? `an active match from "${opts.from}"` : 'a nearest match!'}`);
448
- return opts?.select ? opts.select(match) : match;
449
- }
450
- });
451
- return matchSelection;
452
- }
453
- function useMatches(opts) {
454
- return useRouterState({
455
- select: state => {
456
- let matches = getRenderedMatches(state);
457
- return opts?.select ? opts.select(matches) : matches;
367
+
368
+ // Remove empty segments and '.' segments
369
+ const split = pathname.split('/').filter(Boolean);
370
+ segments.push(...split.map(part => {
371
+ if (part === '$' || part === '*') {
372
+ return {
373
+ type: 'wildcard',
374
+ value: part
375
+ };
458
376
  }
459
- });
460
- }
461
- function useParentMatches(opts) {
462
- const contextMatchId = React.useContext(matchContext);
463
- return useMatches({
464
- select: matches => {
465
- matches = matches.slice(matches.findIndex(d => d.id === contextMatchId));
466
- return opts?.select ? opts.select(matches) : matches;
377
+ if (part.charAt(0) === '$') {
378
+ return {
379
+ type: 'param',
380
+ value: part
381
+ };
467
382
  }
468
- });
383
+ return {
384
+ type: 'pathname',
385
+ value: part
386
+ };
387
+ }));
388
+ if (pathname.slice(-1) === '/') {
389
+ pathname = pathname.substring(1);
390
+ segments.push({
391
+ type: 'pathname',
392
+ value: '/'
393
+ });
394
+ }
395
+ return segments;
469
396
  }
470
- function useLoaderDeps(opts) {
471
- return useMatch({
472
- ...opts,
473
- select: s => {
474
- return typeof opts.select === 'function' ? opts.select(s?.loaderDeps) : s?.loaderDeps;
397
+ function interpolatePath(path, params, leaveWildcards = false) {
398
+ const interpolatedPathSegments = parsePathname(path);
399
+ return joinPaths(interpolatedPathSegments.map(segment => {
400
+ if (segment.type === 'wildcard') {
401
+ const value = params[segment.value];
402
+ if (leaveWildcards) return `${segment.value}${value ?? ''}`;
403
+ return value;
475
404
  }
476
- });
477
- }
478
- function useLoaderData(opts) {
479
- return useMatch({
480
- ...opts,
481
- select: s => {
482
- return typeof opts.select === 'function' ? opts.select(s?.loaderData) : s?.loaderData;
405
+ if (segment.type === 'param') {
406
+ return params[segment.value.substring(1)] ?? 'undefined';
483
407
  }
484
- });
485
- }
486
-
487
- let routerContext = /*#__PURE__*/React.createContext(null);
488
- if (typeof document !== 'undefined') {
489
- if (window.__TSR_ROUTER_CONTEXT__) {
490
- routerContext = window.__TSR_ROUTER_CONTEXT__;
491
- } else {
492
- window.__TSR_ROUTER_CONTEXT__ = routerContext;
493
- }
494
- }
495
- function RouterProvider({
496
- router,
497
- ...rest
498
- }) {
499
- // Allow the router to update options on the router instance
500
- router.update({
501
- ...router.options,
502
- ...rest,
503
- context: {
504
- ...router.options.context,
505
- ...rest?.context
506
- }
507
- });
508
- const matches = router.options.InnerWrap ? /*#__PURE__*/React.createElement(router.options.InnerWrap, null, /*#__PURE__*/React.createElement(Matches, null)) : /*#__PURE__*/React.createElement(Matches, null);
509
- const provider = /*#__PURE__*/React.createElement(routerContext.Provider, {
510
- value: router
511
- }, matches, /*#__PURE__*/React.createElement(Transitioner, null));
512
- if (router.options.Wrap) {
513
- return /*#__PURE__*/React.createElement(router.options.Wrap, null, provider);
514
- }
515
- return provider;
516
- }
517
- function Transitioner() {
518
- const router = useRouter();
519
- const routerState = useRouterState({
520
- select: s => pick(s, ['isLoading', 'location', 'resolvedLocation', 'isTransitioning'])
521
- });
522
- const [isTransitioning, startReactTransition] = React.useTransition();
523
- router.startReactTransition = startReactTransition;
524
- React.useEffect(() => {
525
- if (isTransitioning) {
526
- router.__store.setState(s => ({
527
- ...s,
528
- isTransitioning
529
- }));
530
- }
531
- }, [isTransitioning]);
532
- const tryLoad = () => {
533
- const apply = cb => {
534
- if (!routerState.isTransitioning) {
535
- startReactTransition(() => cb());
536
- } else {
537
- cb();
538
- }
539
- };
540
- apply(() => {
541
- try {
542
- router.load();
543
- } catch (err) {
544
- console.error(err);
545
- }
546
- });
547
- };
548
- useLayoutEffect$1(() => {
549
- const unsub = router.history.subscribe(() => {
550
- router.latestLocation = router.parseLocation(router.latestLocation);
551
- if (routerState.location !== router.latestLocation) {
552
- tryLoad();
553
- }
554
- });
555
- const nextLocation = router.buildLocation({
556
- search: true,
557
- params: true,
558
- hash: true,
559
- state: true
560
- });
561
- if (routerState.location.href !== nextLocation.href) {
562
- router.commitLocation({
563
- ...nextLocation,
564
- replace: true
565
- });
566
- }
567
- return () => {
568
- unsub();
569
- };
570
- }, [router.history]);
571
- useLayoutEffect$1(() => {
572
- if (routerState.isTransitioning && !isTransitioning && !routerState.isLoading && routerState.resolvedLocation !== routerState.location) {
573
- router.emit({
574
- type: 'onResolved',
575
- fromLocation: routerState.resolvedLocation,
576
- toLocation: routerState.location,
577
- pathChanged: routerState.location.href !== routerState.resolvedLocation?.href
578
- });
579
- if (document.querySelector) {
580
- if (routerState.location.hash !== '') {
581
- const el = document.getElementById(routerState.location.hash);
582
- if (el) {
583
- el.scrollIntoView();
584
- }
585
- }
586
- }
587
- router.__store.setState(s => ({
588
- ...s,
589
- isTransitioning: false,
590
- resolvedLocation: s.location
591
- }));
592
- }
593
- }, [routerState.isTransitioning, isTransitioning, routerState.isLoading, routerState.resolvedLocation, routerState.location]);
594
- useLayoutEffect$1(() => {
595
- if (!window.__TSR_DEHYDRATED__) {
596
- tryLoad();
597
- }
598
- }, []);
599
- return null;
600
- }
601
- function getRouteMatch(state, id) {
602
- return [...state.cachedMatches, ...(state.pendingMatches ?? []), ...state.matches].find(d => d.id === id);
603
- }
604
- function useRouterState(opts) {
605
- const router = useRouter();
606
- return useStore(router.__store, opts?.select);
607
- }
608
- function useRouter() {
609
- const resolvedContext = typeof document !== 'undefined' ? window.__TSR_ROUTER_CONTEXT__ || routerContext : routerContext;
610
- const value = React.useContext(resolvedContext);
611
- warning(value, 'useRouter must be used inside a <RouterProvider> component!');
612
- return value;
613
- }
614
-
615
- function defer(_promise) {
616
- const promise = _promise;
617
- if (!promise.__deferredState) {
618
- promise.__deferredState = {
619
- uid: Math.random().toString(36).slice(2),
620
- status: 'pending'
621
- };
622
- const state = promise.__deferredState;
623
- promise.then(data => {
624
- state.status = 'success';
625
- state.data = data;
626
- }).catch(error => {
627
- state.status = 'error';
628
- state.error = error;
629
- });
630
- }
631
- return promise;
632
- }
633
- function isDehydratedDeferred(obj) {
634
- return typeof obj === 'object' && obj !== null && !(obj instanceof Promise) && !obj.then && '__deferredState' in obj;
635
- }
636
-
637
- function useAwaited({
638
- promise
639
- }) {
640
- const router = useRouter();
641
- let state = promise.__deferredState;
642
- const key = `__TSR__DEFERRED__${state.uid}`;
643
- if (isDehydratedDeferred(promise)) {
644
- state = router.hydrateData(key);
645
- promise = Promise.resolve(state.data);
646
- promise.__deferredState = state;
647
- }
648
- if (state.status === 'pending') {
649
- throw new Promise(r => setTimeout(r, 1)).then(() => promise);
650
- }
651
- if (state.status === 'error') {
652
- throw state.error;
653
- }
654
- router.dehydrateData(key, state);
655
- return [state.data];
656
- }
657
- function Await(props) {
658
- const awaited = useAwaited(props);
659
- return props.children(...awaited);
660
- }
661
-
662
- function joinPaths(paths) {
663
- return cleanPath(paths.filter(Boolean).join('/'));
664
- }
665
- function cleanPath(path) {
666
- // remove double slashes
667
- return path.replace(/\/{2,}/g, '/');
668
- }
669
- function trimPathLeft(path) {
670
- return path === '/' ? path : path.replace(/^\/{1,}/, '');
671
- }
672
- function trimPathRight(path) {
673
- return path === '/' ? path : path.replace(/\/{1,}$/, '');
674
- }
675
- function trimPath(path) {
676
- return trimPathRight(trimPathLeft(path));
677
- }
678
- function resolvePath(basepath, base, to) {
679
- base = base.replace(new RegExp(`^${basepath}`), '/');
680
- to = to.replace(new RegExp(`^${basepath}`), '/');
681
- let baseSegments = parsePathname(base);
682
- const toSegments = parsePathname(to);
683
- toSegments.forEach((toSegment, index) => {
684
- if (toSegment.value === '/') {
685
- if (!index) {
686
- // Leading slash
687
- baseSegments = [toSegment];
688
- } else if (index === toSegments.length - 1) {
689
- // Trailing Slash
690
- baseSegments.push(toSegment);
691
- } else ;
692
- } else if (toSegment.value === '..') {
693
- // Extra trailing slash? pop it off
694
- if (baseSegments.length > 1 && last(baseSegments)?.value === '/') {
695
- baseSegments.pop();
696
- }
697
- baseSegments.pop();
698
- } else if (toSegment.value === '.') {
699
- return;
700
- } else {
701
- baseSegments.push(toSegment);
702
- }
703
- });
704
- const joined = joinPaths([basepath, ...baseSegments.map(d => d.value)]);
705
- return cleanPath(joined);
706
- }
707
- function parsePathname(pathname) {
708
- if (!pathname) {
709
- return [];
710
- }
711
- pathname = cleanPath(pathname);
712
- const segments = [];
713
- if (pathname.slice(0, 1) === '/') {
714
- pathname = pathname.substring(1);
715
- segments.push({
716
- type: 'pathname',
717
- value: '/'
718
- });
719
- }
720
- if (!pathname) {
721
- return segments;
722
- }
723
-
724
- // Remove empty segments and '.' segments
725
- const split = pathname.split('/').filter(Boolean);
726
- segments.push(...split.map(part => {
727
- if (part === '$' || part === '*') {
728
- return {
729
- type: 'wildcard',
730
- value: part
731
- };
732
- }
733
- if (part.charAt(0) === '$') {
734
- return {
735
- type: 'param',
736
- value: part
737
- };
738
- }
739
- return {
740
- type: 'pathname',
741
- value: part
742
- };
743
- }));
744
- if (pathname.slice(-1) === '/') {
745
- pathname = pathname.substring(1);
746
- segments.push({
747
- type: 'pathname',
748
- value: '/'
749
- });
750
- }
751
- return segments;
752
- }
753
- function interpolatePath(path, params, leaveWildcards = false) {
754
- const interpolatedPathSegments = parsePathname(path);
755
- return joinPaths(interpolatedPathSegments.map(segment => {
756
- if (segment.type === 'wildcard') {
757
- const value = params[segment.value];
758
- if (leaveWildcards) return `${segment.value}${value ?? ''}`;
759
- return value;
760
- }
761
- if (segment.type === 'param') {
762
- return params[segment.value.substring(1)] ?? 'undefined';
763
- }
764
- return segment.value;
765
- }));
408
+ return segment.value;
409
+ }));
766
410
  }
767
411
  function matchPathname(basepath, currentPathname, matchLocation) {
768
412
  const pathParams = matchByPath(basepath, currentPathname, matchLocation);
@@ -936,98 +580,463 @@ class Route {
936
580
  } else {
937
581
  invariant(this.parentRoute, `Child Route instances must pass a 'getParentRoute: () => ParentRoute' option that returns a Route instance.`);
938
582
  }
939
- let path = isRoot ? rootRouteId : options.path;
940
-
941
- // If the path is anything other than an index path, trim it up
942
- if (path && path !== '/') {
943
- path = trimPath(path);
583
+ let path = isRoot ? rootRouteId : options.path;
584
+
585
+ // If the path is anything other than an index path, trim it up
586
+ if (path && path !== '/') {
587
+ path = trimPath(path);
588
+ }
589
+ const customId = options?.id || path;
590
+
591
+ // Strip the parentId prefix from the first level of children
592
+ let id = isRoot ? rootRouteId : joinPaths([this.parentRoute.id === rootRouteId ? '' : this.parentRoute.id, customId]);
593
+ if (path === rootRouteId) {
594
+ path = '/';
595
+ }
596
+ if (id !== rootRouteId) {
597
+ id = joinPaths(['/', id]);
598
+ }
599
+ const fullPath = id === rootRouteId ? '/' : joinPaths([this.parentRoute.fullPath, path]);
600
+ this.path = path;
601
+ this.id = id;
602
+ // this.customId = customId as TCustomId
603
+ this.fullPath = fullPath;
604
+ this.to = fullPath;
605
+ };
606
+ addChildren = children => {
607
+ this.children = children;
608
+ return this;
609
+ };
610
+ update = options => {
611
+ Object.assign(this.options, options);
612
+ return this;
613
+ };
614
+ useMatch = opts => {
615
+ return useMatch({
616
+ ...opts,
617
+ from: this.id
618
+ });
619
+ };
620
+ useRouteContext = opts => {
621
+ return useMatch({
622
+ ...opts,
623
+ from: this.id,
624
+ select: d => opts?.select ? opts.select(d.context) : d.context
625
+ });
626
+ };
627
+ useSearch = opts => {
628
+ return useSearch({
629
+ ...opts,
630
+ from: this.id
631
+ });
632
+ };
633
+ useParams = opts => {
634
+ return useParams({
635
+ ...opts,
636
+ from: this.id
637
+ });
638
+ };
639
+ useLoaderDeps = opts => {
640
+ return useLoaderDeps({
641
+ ...opts,
642
+ from: this.id
643
+ });
644
+ };
645
+ useLoaderData = opts => {
646
+ return useLoaderData({
647
+ ...opts,
648
+ from: this.id
649
+ });
650
+ };
651
+ }
652
+ function rootRouteWithContext() {
653
+ return options => {
654
+ return new RootRoute(options);
655
+ };
656
+ }
657
+ class RootRoute extends Route {
658
+ constructor(options) {
659
+ super(options);
660
+ }
661
+ }
662
+ function createRouteMask(opts) {
663
+ return opts;
664
+ }
665
+
666
+ //
667
+
668
+ class NotFoundRoute extends Route {
669
+ constructor(options) {
670
+ super({
671
+ ...options,
672
+ id: '404'
673
+ });
674
+ }
675
+ }
676
+
677
+ const matchContext = /*#__PURE__*/React.createContext(undefined);
678
+ function Matches() {
679
+ const router = useRouter();
680
+ const matchId = useRouterState({
681
+ select: s => {
682
+ return getRenderedMatches(s)[0]?.id;
683
+ }
684
+ });
685
+ return /*#__PURE__*/React.createElement(matchContext.Provider, {
686
+ value: matchId
687
+ }, /*#__PURE__*/React.createElement(CatchBoundary, {
688
+ getResetKey: () => router.state.resolvedLocation.state?.key,
689
+ errorComponent: ErrorComponent,
690
+ onCatch: () => {
691
+ warning(false, `Error in router! Consider setting an 'errorComponent' in your RootRoute! 👍`);
692
+ }
693
+ }, matchId ? /*#__PURE__*/React.createElement(Match, {
694
+ matchId: matchId
695
+ }) : null));
696
+ }
697
+ function SafeFragment(props) {
698
+ return /*#__PURE__*/React.createElement(React.Fragment, null, props.children);
699
+ }
700
+ function Match({
701
+ matchId
702
+ }) {
703
+ const router = useRouter();
704
+ const routeId = useRouterState({
705
+ select: s => getRenderedMatches(s).find(d => d.id === matchId)?.routeId
706
+ });
707
+ invariant(routeId, `Could not find routeId for matchId "${matchId}". Please file an issue!`);
708
+ const route = router.routesById[routeId];
709
+ const PendingComponent = route.options.pendingComponent ?? router.options.defaultPendingComponent;
710
+ const pendingElement = PendingComponent ? /*#__PURE__*/React.createElement(PendingComponent, null) : null;
711
+ const routeErrorComponent = route.options.errorComponent ?? router.options.defaultErrorComponent ?? ErrorComponent;
712
+ const ResolvedSuspenseBoundary = route.options.wrapInSuspense ?? PendingComponent ?? route.options.component?.preload ?? route.options.pendingComponent?.preload ?? route.options.errorComponent?.preload ? React.Suspense : SafeFragment;
713
+ const ResolvedCatchBoundary = routeErrorComponent ? CatchBoundary : SafeFragment;
714
+ return /*#__PURE__*/React.createElement(matchContext.Provider, {
715
+ value: matchId
716
+ }, /*#__PURE__*/React.createElement(ResolvedSuspenseBoundary, {
717
+ fallback: pendingElement
718
+ }, /*#__PURE__*/React.createElement(ResolvedCatchBoundary, {
719
+ getResetKey: () => router.state.resolvedLocation.state?.key,
720
+ errorComponent: routeErrorComponent,
721
+ onCatch: () => {
722
+ warning(false, `Error in route match: ${matchId}`);
723
+ }
724
+ }, /*#__PURE__*/React.createElement(MatchInner, {
725
+ matchId: matchId,
726
+ pendingElement: pendingElement
727
+ }))));
728
+ }
729
+ function MatchInner({
730
+ matchId,
731
+ pendingElement
732
+ }) {
733
+ const router = useRouter();
734
+ const routeId = useRouterState({
735
+ select: s => getRenderedMatches(s).find(d => d.id === matchId)?.routeId
736
+ });
737
+ const route = router.routesById[routeId];
738
+ const match = useRouterState({
739
+ select: s => pick(getRenderedMatches(s).find(d => d.id === matchId), ['status', 'error', 'showPending', 'loadPromise'])
740
+ });
741
+ if (match.status === 'error') {
742
+ throw match.error;
743
+ }
744
+ if (match.status === 'pending') {
745
+ if (match.showPending) {
746
+ return pendingElement;
747
+ }
748
+ throw match.loadPromise;
749
+ }
750
+ if (match.status === 'success') {
751
+ let Comp = route.options.component ?? router.options.defaultComponent;
752
+ if (Comp) {
753
+ return /*#__PURE__*/React.createElement(Comp, null);
754
+ }
755
+ return /*#__PURE__*/React.createElement(Outlet, null);
756
+ }
757
+ invariant(false, 'Idle routeMatch status encountered during rendering! You should never see this. File an issue!');
758
+ }
759
+ const Outlet = /*#__PURE__*/React.memo(function Outlet() {
760
+ const matchId = React.useContext(matchContext);
761
+ const childMatchId = useRouterState({
762
+ select: s => {
763
+ const matches = getRenderedMatches(s);
764
+ const index = matches.findIndex(d => d.id === matchId);
765
+ return matches[index + 1]?.id;
766
+ }
767
+ });
768
+ if (!childMatchId) {
769
+ return null;
770
+ }
771
+ return /*#__PURE__*/React.createElement(Match, {
772
+ matchId: childMatchId
773
+ });
774
+ });
775
+ function useMatchRoute() {
776
+ useRouterState({
777
+ select: s => [s.location, s.resolvedLocation]
778
+ });
779
+ const {
780
+ matchRoute
781
+ } = useRouter();
782
+ return React.useCallback(opts => {
783
+ const {
784
+ pending,
785
+ caseSensitive,
786
+ ...rest
787
+ } = opts;
788
+ return matchRoute(rest, {
789
+ pending,
790
+ caseSensitive
791
+ });
792
+ }, []);
793
+ }
794
+ function MatchRoute(props) {
795
+ const matchRoute = useMatchRoute();
796
+ const params = matchRoute(props);
797
+ if (typeof props.children === 'function') {
798
+ return props.children(params);
799
+ }
800
+ return !!params ? props.children : null;
801
+ }
802
+ function getRenderedMatches(state) {
803
+ return state.pendingMatches?.some(d => d.showPending) ? state.pendingMatches : state.matches;
804
+ }
805
+ function removeUnderscores(s) {
806
+ if (s === rootRouteId) return s;
807
+ return s?.replace(/(^_|_$)/, '').replace(/(\/_|_\/)/, '/');
808
+ }
809
+ function useMatch(opts) {
810
+ const router = useRouter();
811
+ const nearestMatchId = React.useContext(matchContext);
812
+ const nearestMatchRouteId = getRenderedMatches(router.state).find(d => d.id === nearestMatchId)?.routeId;
813
+ const matchRouteId = (() => {
814
+ const matches = getRenderedMatches(router.state);
815
+ if (!opts.from) {
816
+ return matches.find(d => d.id === nearestMatchId);
817
+ }
818
+ const from = removeUnderscores(opts.from);
819
+ return matches.find(d => d.routeId === from) ?? matches.find(d => d.routeId === from?.replace(/\/$/, ''));
820
+ })()?.routeId;
821
+ if (opts?.strict ?? true) {
822
+ 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?`);
823
+ }
824
+ const matchSelection = useRouterState({
825
+ select: state => {
826
+ const match = getRenderedMatches(state).find(d => d.id === nearestMatchId);
827
+ invariant(match, `Could not find ${opts?.from ? `an active match from "${opts.from}"` : 'a nearest match!'}`);
828
+ return opts?.select ? opts.select(match) : match;
829
+ }
830
+ });
831
+ return matchSelection;
832
+ }
833
+ function useMatches(opts) {
834
+ return useRouterState({
835
+ select: state => {
836
+ let matches = getRenderedMatches(state);
837
+ return opts?.select ? opts.select(matches) : matches;
838
+ }
839
+ });
840
+ }
841
+ function useParentMatches(opts) {
842
+ const contextMatchId = React.useContext(matchContext);
843
+ return useMatches({
844
+ select: matches => {
845
+ matches = matches.slice(matches.findIndex(d => d.id === contextMatchId));
846
+ return opts?.select ? opts.select(matches) : matches;
847
+ }
848
+ });
849
+ }
850
+ function useLoaderDeps(opts) {
851
+ return useMatch({
852
+ ...opts,
853
+ select: s => {
854
+ return typeof opts.select === 'function' ? opts.select(s?.loaderDeps) : s?.loaderDeps;
944
855
  }
945
- const customId = options?.id || path;
856
+ });
857
+ }
858
+ function useLoaderData(opts) {
859
+ return useMatch({
860
+ ...opts,
861
+ select: s => {
862
+ return typeof opts.select === 'function' ? opts.select(s?.loaderData) : s?.loaderData;
863
+ }
864
+ });
865
+ }
946
866
 
947
- // Strip the parentId prefix from the first level of children
948
- let id = isRoot ? rootRouteId : joinPaths([this.parentRoute.id === rootRouteId ? '' : this.parentRoute.id, customId]);
949
- if (path === rootRouteId) {
950
- path = '/';
867
+ let routerContext = /*#__PURE__*/React.createContext(null);
868
+ if (typeof document !== 'undefined') {
869
+ if (window.__TSR_ROUTER_CONTEXT__) {
870
+ routerContext = window.__TSR_ROUTER_CONTEXT__;
871
+ } else {
872
+ window.__TSR_ROUTER_CONTEXT__ = routerContext;
873
+ }
874
+ }
875
+ function RouterProvider({
876
+ router,
877
+ ...rest
878
+ }) {
879
+ // Allow the router to update options on the router instance
880
+ router.update({
881
+ ...router.options,
882
+ ...rest,
883
+ context: {
884
+ ...router.options.context,
885
+ ...rest?.context
951
886
  }
952
- if (id !== rootRouteId) {
953
- id = joinPaths(['/', id]);
887
+ });
888
+ const matches = router.options.InnerWrap ? /*#__PURE__*/React.createElement(router.options.InnerWrap, null, /*#__PURE__*/React.createElement(Matches, null)) : /*#__PURE__*/React.createElement(Matches, null);
889
+ const provider = /*#__PURE__*/React.createElement(routerContext.Provider, {
890
+ value: router
891
+ }, matches, /*#__PURE__*/React.createElement(Transitioner, null));
892
+ if (router.options.Wrap) {
893
+ return /*#__PURE__*/React.createElement(router.options.Wrap, null, provider);
894
+ }
895
+ return provider;
896
+ }
897
+ function Transitioner() {
898
+ const router = useRouter();
899
+ const routerState = useRouterState({
900
+ select: s => pick(s, ['isLoading', 'location', 'resolvedLocation', 'isTransitioning'])
901
+ });
902
+ const [isTransitioning, startReactTransition] = React.useTransition();
903
+ router.startReactTransition = startReactTransition;
904
+ React.useEffect(() => {
905
+ if (isTransitioning) {
906
+ router.__store.setState(s => ({
907
+ ...s,
908
+ isTransitioning
909
+ }));
954
910
  }
955
- const fullPath = id === rootRouteId ? '/' : joinPaths([this.parentRoute.fullPath, path]);
956
- this.path = path;
957
- this.id = id;
958
- // this.customId = customId as TCustomId
959
- this.fullPath = fullPath;
960
- this.to = fullPath;
961
- };
962
- addChildren = children => {
963
- this.children = children;
964
- return this;
965
- };
966
- update = options => {
967
- Object.assign(this.options, options);
968
- return this;
969
- };
970
- useMatch = opts => {
971
- return useMatch({
972
- ...opts,
973
- from: this.id
974
- });
975
- };
976
- useRouteContext = opts => {
977
- return useMatch({
978
- ...opts,
979
- from: this.id,
980
- select: d => opts?.select ? opts.select(d.context) : d.context
981
- });
982
- };
983
- useSearch = opts => {
984
- return useSearch({
985
- ...opts,
986
- from: this.id
987
- });
988
- };
989
- useParams = opts => {
990
- return useParams({
991
- ...opts,
992
- from: this.id
911
+ }, [isTransitioning]);
912
+ const tryLoad = () => {
913
+ const apply = cb => {
914
+ if (!routerState.isTransitioning) {
915
+ startReactTransition(() => cb());
916
+ } else {
917
+ cb();
918
+ }
919
+ };
920
+ apply(() => {
921
+ try {
922
+ router.load();
923
+ } catch (err) {
924
+ console.error(err);
925
+ }
993
926
  });
994
927
  };
995
- useLoaderDeps = opts => {
996
- return useLoaderDeps({
997
- ...opts,
998
- from: this.id
928
+ useLayoutEffect$1(() => {
929
+ const unsub = router.history.subscribe(() => {
930
+ router.latestLocation = router.parseLocation(router.latestLocation);
931
+ if (routerState.location !== router.latestLocation) {
932
+ tryLoad();
933
+ }
999
934
  });
1000
- };
1001
- useLoaderData = opts => {
1002
- return useLoaderData({
1003
- ...opts,
1004
- from: this.id
935
+ const nextLocation = router.buildLocation({
936
+ search: true,
937
+ params: true,
938
+ hash: true,
939
+ state: true
1005
940
  });
1006
- };
941
+ if (routerState.location.href !== nextLocation.href) {
942
+ router.commitLocation({
943
+ ...nextLocation,
944
+ replace: true
945
+ });
946
+ }
947
+ return () => {
948
+ unsub();
949
+ };
950
+ }, [router.history]);
951
+ useLayoutEffect$1(() => {
952
+ if (routerState.isTransitioning && !isTransitioning && !routerState.isLoading && routerState.resolvedLocation !== routerState.location) {
953
+ router.emit({
954
+ type: 'onResolved',
955
+ fromLocation: routerState.resolvedLocation,
956
+ toLocation: routerState.location,
957
+ pathChanged: routerState.location.href !== routerState.resolvedLocation?.href
958
+ });
959
+ if (document.querySelector) {
960
+ if (routerState.location.hash !== '') {
961
+ const el = document.getElementById(routerState.location.hash);
962
+ if (el) {
963
+ el.scrollIntoView();
964
+ }
965
+ }
966
+ }
967
+ router.__store.setState(s => ({
968
+ ...s,
969
+ isTransitioning: false,
970
+ resolvedLocation: s.location
971
+ }));
972
+ }
973
+ }, [routerState.isTransitioning, isTransitioning, routerState.isLoading, routerState.resolvedLocation, routerState.location]);
974
+ useLayoutEffect$1(() => {
975
+ if (!window.__TSR_DEHYDRATED__) {
976
+ tryLoad();
977
+ }
978
+ }, []);
979
+ return null;
1007
980
  }
1008
- function rootRouteWithContext() {
1009
- return options => {
1010
- return new RootRoute(options);
1011
- };
981
+ function getRouteMatch(state, id) {
982
+ return [...state.cachedMatches, ...(state.pendingMatches ?? []), ...state.matches].find(d => d.id === id);
1012
983
  }
1013
- class RootRoute extends Route {
1014
- constructor(options) {
1015
- super(options);
1016
- }
984
+ function useRouterState(opts) {
985
+ const router = useRouter();
986
+ return useStore(router.__store, opts?.select);
1017
987
  }
1018
- function createRouteMask(opts) {
1019
- return opts;
988
+ function useRouter() {
989
+ const resolvedContext = typeof document !== 'undefined' ? window.__TSR_ROUTER_CONTEXT__ || routerContext : routerContext;
990
+ const value = React.useContext(resolvedContext);
991
+ warning(value, 'useRouter must be used inside a <RouterProvider> component!');
992
+ return value;
1020
993
  }
1021
994
 
1022
- //
1023
-
1024
- class NotFoundRoute extends Route {
1025
- constructor(options) {
1026
- super({
1027
- ...options,
1028
- id: '404'
995
+ function defer(_promise) {
996
+ const promise = _promise;
997
+ if (!promise.__deferredState) {
998
+ promise.__deferredState = {
999
+ uid: Math.random().toString(36).slice(2),
1000
+ status: 'pending'
1001
+ };
1002
+ const state = promise.__deferredState;
1003
+ promise.then(data => {
1004
+ state.status = 'success';
1005
+ state.data = data;
1006
+ }).catch(error => {
1007
+ state.status = 'error';
1008
+ state.error = error;
1029
1009
  });
1030
1010
  }
1011
+ return promise;
1012
+ }
1013
+ function isDehydratedDeferred(obj) {
1014
+ return typeof obj === 'object' && obj !== null && !(obj instanceof Promise) && !obj.then && '__deferredState' in obj;
1015
+ }
1016
+
1017
+ function useAwaited({
1018
+ promise
1019
+ }) {
1020
+ const router = useRouter();
1021
+ let state = promise.__deferredState;
1022
+ const key = `__TSR__DEFERRED__${state.uid}`;
1023
+ if (isDehydratedDeferred(promise)) {
1024
+ state = router.hydrateData(key);
1025
+ promise = Promise.resolve(state.data);
1026
+ promise.__deferredState = state;
1027
+ }
1028
+ if (state.status === 'pending') {
1029
+ throw new Promise(r => setTimeout(r, 1)).then(() => promise);
1030
+ }
1031
+ if (state.status === 'error') {
1032
+ throw state.error;
1033
+ }
1034
+ router.dehydrateData(key, state);
1035
+ return [state.data];
1036
+ }
1037
+ function Await(props) {
1038
+ const awaited = useAwaited(props);
1039
+ return props.children(...awaited);
1031
1040
  }
1032
1041
 
1033
1042
  class FileRoute {