@tanstack/react-router 1.78.3 → 1.81.0

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.
Files changed (123) hide show
  1. package/dist/cjs/Match.cjs +10 -11
  2. package/dist/cjs/Match.cjs.map +1 -1
  3. package/dist/cjs/Matches.cjs +8 -4
  4. package/dist/cjs/Matches.cjs.map +1 -1
  5. package/dist/cjs/Matches.d.cts +8 -9
  6. package/dist/cjs/RouterProvider.cjs.map +1 -1
  7. package/dist/cjs/RouterProvider.d.cts +3 -3
  8. package/dist/cjs/Transitioner.cjs +9 -8
  9. package/dist/cjs/Transitioner.cjs.map +1 -1
  10. package/dist/cjs/fileRoute.cjs +15 -3
  11. package/dist/cjs/fileRoute.cjs.map +1 -1
  12. package/dist/cjs/fileRoute.d.cts +12 -19
  13. package/dist/cjs/link.cjs +4 -1
  14. package/dist/cjs/link.cjs.map +1 -1
  15. package/dist/cjs/link.d.cts +1 -1
  16. package/dist/cjs/path.cjs +12 -2
  17. package/dist/cjs/path.cjs.map +1 -1
  18. package/dist/cjs/path.d.cts +2 -1
  19. package/dist/cjs/route.cjs +33 -9
  20. package/dist/cjs/route.cjs.map +1 -1
  21. package/dist/cjs/route.d.cts +20 -38
  22. package/dist/cjs/router.cjs +15 -4
  23. package/dist/cjs/router.cjs.map +1 -1
  24. package/dist/cjs/router.d.cts +25 -11
  25. package/dist/cjs/routerContext.cjs.map +1 -1
  26. package/dist/cjs/routerContext.d.cts +1 -1
  27. package/dist/cjs/structuralSharing.d.cts +12 -0
  28. package/dist/cjs/useLoaderData.cjs +4 -2
  29. package/dist/cjs/useLoaderData.cjs.map +1 -1
  30. package/dist/cjs/useLoaderData.d.cts +12 -9
  31. package/dist/cjs/useLoaderDeps.cjs +3 -2
  32. package/dist/cjs/useLoaderDeps.cjs.map +1 -1
  33. package/dist/cjs/useLoaderDeps.d.cts +12 -8
  34. package/dist/cjs/useLocation.cjs.map +1 -1
  35. package/dist/cjs/useLocation.d.cts +6 -3
  36. package/dist/cjs/useMatch.cjs +2 -1
  37. package/dist/cjs/useMatch.cjs.map +1 -1
  38. package/dist/cjs/useMatch.d.cts +10 -8
  39. package/dist/cjs/useParams.cjs +3 -1
  40. package/dist/cjs/useParams.cjs.map +1 -1
  41. package/dist/cjs/useParams.d.cts +12 -8
  42. package/dist/cjs/useRouteContext.cjs.map +1 -1
  43. package/dist/cjs/useRouteContext.d.cts +11 -8
  44. package/dist/cjs/useRouterState.cjs +18 -1
  45. package/dist/cjs/useRouterState.cjs.map +1 -1
  46. package/dist/cjs/useRouterState.d.cts +6 -3
  47. package/dist/cjs/useSearch.cjs +3 -1
  48. package/dist/cjs/useSearch.cjs.map +1 -1
  49. package/dist/cjs/useSearch.d.cts +12 -8
  50. package/dist/cjs/utils.cjs.map +1 -1
  51. package/dist/cjs/utils.d.cts +10 -4
  52. package/dist/esm/Match.js +10 -11
  53. package/dist/esm/Match.js.map +1 -1
  54. package/dist/esm/Matches.d.ts +8 -9
  55. package/dist/esm/Matches.js +8 -4
  56. package/dist/esm/Matches.js.map +1 -1
  57. package/dist/esm/RouterProvider.d.ts +3 -3
  58. package/dist/esm/RouterProvider.js.map +1 -1
  59. package/dist/esm/Transitioner.js +10 -9
  60. package/dist/esm/Transitioner.js.map +1 -1
  61. package/dist/esm/fileRoute.d.ts +12 -19
  62. package/dist/esm/fileRoute.js +15 -3
  63. package/dist/esm/fileRoute.js.map +1 -1
  64. package/dist/esm/link.d.ts +1 -1
  65. package/dist/esm/link.js +4 -1
  66. package/dist/esm/link.js.map +1 -1
  67. package/dist/esm/path.d.ts +2 -1
  68. package/dist/esm/path.js +12 -2
  69. package/dist/esm/path.js.map +1 -1
  70. package/dist/esm/route.d.ts +20 -38
  71. package/dist/esm/route.js +33 -9
  72. package/dist/esm/route.js.map +1 -1
  73. package/dist/esm/router.d.ts +25 -11
  74. package/dist/esm/router.js +15 -4
  75. package/dist/esm/router.js.map +1 -1
  76. package/dist/esm/routerContext.d.ts +1 -1
  77. package/dist/esm/routerContext.js.map +1 -1
  78. package/dist/esm/structuralSharing.d.ts +12 -0
  79. package/dist/esm/useLoaderData.d.ts +12 -9
  80. package/dist/esm/useLoaderData.js +4 -2
  81. package/dist/esm/useLoaderData.js.map +1 -1
  82. package/dist/esm/useLoaderDeps.d.ts +12 -8
  83. package/dist/esm/useLoaderDeps.js +3 -2
  84. package/dist/esm/useLoaderDeps.js.map +1 -1
  85. package/dist/esm/useLocation.d.ts +6 -3
  86. package/dist/esm/useLocation.js.map +1 -1
  87. package/dist/esm/useMatch.d.ts +10 -8
  88. package/dist/esm/useMatch.js +2 -1
  89. package/dist/esm/useMatch.js.map +1 -1
  90. package/dist/esm/useParams.d.ts +12 -8
  91. package/dist/esm/useParams.js +3 -1
  92. package/dist/esm/useParams.js.map +1 -1
  93. package/dist/esm/useRouteContext.d.ts +11 -8
  94. package/dist/esm/useRouteContext.js.map +1 -1
  95. package/dist/esm/useRouterState.d.ts +6 -3
  96. package/dist/esm/useRouterState.js +18 -1
  97. package/dist/esm/useRouterState.js.map +1 -1
  98. package/dist/esm/useSearch.d.ts +12 -8
  99. package/dist/esm/useSearch.js +3 -1
  100. package/dist/esm/useSearch.js.map +1 -1
  101. package/dist/esm/utils.d.ts +10 -4
  102. package/dist/esm/utils.js.map +1 -1
  103. package/package.json +3 -3
  104. package/src/Match.tsx +6 -7
  105. package/src/Matches.tsx +52 -24
  106. package/src/RouterProvider.tsx +4 -1
  107. package/src/Transitioner.tsx +9 -10
  108. package/src/fileRoute.ts +29 -29
  109. package/src/link.tsx +6 -3
  110. package/src/path.ts +16 -1
  111. package/src/route.ts +57 -101
  112. package/src/router.ts +60 -4
  113. package/src/routerContext.tsx +1 -1
  114. package/src/structuralSharing.ts +49 -0
  115. package/src/useLoaderData.tsx +76 -28
  116. package/src/useLoaderDeps.tsx +55 -20
  117. package/src/useLocation.tsx +30 -8
  118. package/src/useMatch.tsx +71 -21
  119. package/src/useParams.tsx +70 -21
  120. package/src/useRouteContext.ts +45 -23
  121. package/src/useRouterState.tsx +45 -6
  122. package/src/useSearch.tsx +69 -20
  123. package/src/utils.ts +16 -3
package/src/router.ts CHANGED
@@ -103,7 +103,7 @@ declare global {
103
103
  cleanScripts: () => void
104
104
  dehydrated?: any
105
105
  }
106
- __TSR_ROUTER_CONTEXT__?: React.Context<Router<any, any>>
106
+ __TSR_ROUTER_CONTEXT__?: React.Context<Router<any, any, any>>
107
107
  }
108
108
  }
109
109
 
@@ -111,7 +111,7 @@ export interface Register {
111
111
  // router: Router
112
112
  }
113
113
 
114
- export type AnyRouter = Router<any, any, any, any>
114
+ export type AnyRouter = Router<any, any, any, any, any>
115
115
 
116
116
  export type AnyRouterWithContext<TContext> = Router<
117
117
  AnyRouteWithContext<TContext>,
@@ -173,6 +173,7 @@ export type TrailingSlashOption = 'always' | 'never' | 'preserve'
173
173
  export interface RouterOptions<
174
174
  TRouteTree extends AnyRoute,
175
175
  TTrailingSlashOption extends TrailingSlashOption,
176
+ TDefaultStructuralSharingOption extends boolean = false,
176
177
  TDehydrated extends Record<string, any> = Record<string, any>,
177
178
  TSerializedError extends Record<string, any> = Record<string, any>,
178
179
  > {
@@ -464,6 +465,23 @@ export interface RouterOptions<
464
465
  */
465
466
  strict?: boolean
466
467
  }
468
+
469
+ /**
470
+ * Configures whether structural sharing is enabled by default for fine-grained selectors.
471
+ *
472
+ * @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RouterOptionsType#defaultstructuralsharing-property)
473
+ */
474
+ defaultStructuralSharing?: TDefaultStructuralSharingOption
475
+
476
+ /**
477
+ * Configures which URI characters are allowed in path params that would ordinarily be escaped by encodeURIComponent.
478
+ *
479
+ * @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RouterOptionsType#pathparamsallowedcharacters-property)
480
+ * @link [Guide](https://tanstack.com/router/latest/docs/framework/react/guide/path-params#allowed-characters)
481
+ */
482
+ pathParamsAllowedCharacters?: Array<
483
+ ';' | ':' | '@' | '&' | '=' | '+' | '$' | ','
484
+ >
467
485
  }
468
486
 
469
487
  export interface RouterErrorSerializer<TSerializedError> {
@@ -530,12 +548,14 @@ export interface DehydratedRouter {
530
548
  export type RouterConstructorOptions<
531
549
  TRouteTree extends AnyRoute,
532
550
  TTrailingSlashOption extends TrailingSlashOption,
551
+ TDefaultStructuralSharingOption extends boolean,
533
552
  TDehydrated extends Record<string, any>,
534
553
  TSerializedError extends Record<string, any>,
535
554
  > = Omit<
536
555
  RouterOptions<
537
556
  TRouteTree,
538
557
  TTrailingSlashOption,
558
+ TDefaultStructuralSharingOption,
539
559
  TDehydrated,
540
560
  TSerializedError
541
561
  >,
@@ -627,6 +647,7 @@ export type RouterListener<TRouterEvent extends RouterEvent> = {
627
647
  export function createRouter<
628
648
  TRouteTree extends AnyRoute,
629
649
  TTrailingSlashOption extends TrailingSlashOption,
650
+ TDefaultStructuralSharingOption extends boolean,
630
651
  TDehydrated extends Record<string, any> = Record<string, any>,
631
652
  TSerializedError extends Record<string, any> = Record<string, any>,
632
653
  >(
@@ -635,6 +656,7 @@ export function createRouter<
635
656
  : RouterConstructorOptions<
636
657
  TRouteTree,
637
658
  TTrailingSlashOption,
659
+ TDefaultStructuralSharingOption,
638
660
  TDehydrated,
639
661
  TSerializedError
640
662
  >,
@@ -642,6 +664,7 @@ export function createRouter<
642
664
  return new Router<
643
665
  TRouteTree,
644
666
  TTrailingSlashOption,
667
+ TDefaultStructuralSharingOption,
645
668
  TDehydrated,
646
669
  TSerializedError
647
670
  >(options)
@@ -657,6 +680,7 @@ type MatchRoutesOpts = {
657
680
  export class Router<
658
681
  in out TRouteTree extends AnyRoute,
659
682
  in out TTrailingSlashOption extends TrailingSlashOption,
683
+ in out TDefaultStructuralSharingOption extends boolean,
660
684
  in out TDehydrated extends Record<string, any> = Record<string, any>,
661
685
  in out TSerializedError extends Record<string, any> = Record<string, any>,
662
686
  > {
@@ -694,6 +718,7 @@ export class Router<
694
718
  RouterOptions<
695
719
  TRouteTree,
696
720
  TTrailingSlashOption,
721
+ TDefaultStructuralSharingOption,
697
722
  TDehydrated,
698
723
  TSerializedError
699
724
  >,
@@ -711,6 +736,7 @@ export class Router<
711
736
  routesByPath!: RoutesByPath<TRouteTree>
712
737
  flatRoutes!: Array<AnyRoute>
713
738
  isServer!: boolean
739
+ pathParamsDecodeCharMap?: Map<string, string>
714
740
 
715
741
  /**
716
742
  * @deprecated Use the `createRouter` function instead
@@ -719,6 +745,7 @@ export class Router<
719
745
  options: RouterConstructorOptions<
720
746
  TRouteTree,
721
747
  TTrailingSlashOption,
748
+ TDefaultStructuralSharingOption,
722
749
  TDehydrated,
723
750
  TSerializedError
724
751
  >,
@@ -750,6 +777,7 @@ export class Router<
750
777
  newOptions: RouterConstructorOptions<
751
778
  TRouteTree,
752
779
  TTrailingSlashOption,
780
+ TDefaultStructuralSharingOption,
753
781
  TDehydrated,
754
782
  TSerializedError
755
783
  >,
@@ -768,6 +796,15 @@ export class Router<
768
796
 
769
797
  this.isServer = this.options.isServer ?? typeof document === 'undefined'
770
798
 
799
+ this.pathParamsDecodeCharMap = this.options.pathParamsAllowedCharacters
800
+ ? new Map(
801
+ this.options.pathParamsAllowedCharacters.map((char) => [
802
+ encodeURIComponent(char),
803
+ char,
804
+ ]),
805
+ )
806
+ : undefined
807
+
771
808
  if (
772
809
  !this.basepath ||
773
810
  (newOptions.basepath && newOptions.basepath !== previousOptions.basepath)
@@ -1187,6 +1224,7 @@ export class Router<
1187
1224
  const interpolatedPath = interpolatePath({
1188
1225
  path: route.fullPath,
1189
1226
  params: routeParams,
1227
+ decodeCharMap: this.pathParamsDecodeCharMap,
1190
1228
  })
1191
1229
 
1192
1230
  const matchId =
@@ -1194,6 +1232,7 @@ export class Router<
1194
1232
  path: route.id,
1195
1233
  params: routeParams,
1196
1234
  leaveWildcards: true,
1235
+ decodeCharMap: this.pathParamsDecodeCharMap,
1197
1236
  }) + loaderDepsHash
1198
1237
 
1199
1238
  // Waste not, want not. If we already have a match for this route,
@@ -1431,6 +1470,7 @@ export class Router<
1431
1470
  const interpolatedPath = interpolatePath({
1432
1471
  path: route.fullPath,
1433
1472
  params: matchedRoutesResult?.routeParams ?? {},
1473
+ decodeCharMap: this.pathParamsDecodeCharMap,
1434
1474
  })
1435
1475
  const pathname = joinPaths([this.basepath, interpolatedPath])
1436
1476
  return pathname === fromPath
@@ -1467,6 +1507,7 @@ export class Router<
1467
1507
  params: nextParams ?? {},
1468
1508
  leaveWildcards: false,
1469
1509
  leaveParams: opts.leaveParams,
1510
+ decodeCharMap: this.pathParamsDecodeCharMap,
1470
1511
  })
1471
1512
 
1472
1513
  let search = fromSearch
@@ -1637,6 +1678,7 @@ export class Router<
1637
1678
  }
1638
1679
  maskedNext = build(maskedDest)
1639
1680
  }
1681
+ // console.log('buildWithMatches', {foundMask, dest, maskedDest, maskedNext})
1640
1682
  }
1641
1683
 
1642
1684
  const nextMatches = this.getMatchedRoutes(next, dest)
@@ -2186,6 +2228,8 @@ export class Router<
2186
2228
  let pendingTimeout: ReturnType<typeof setTimeout>
2187
2229
 
2188
2230
  if (shouldPending) {
2231
+ console.log('### 2186 setting timeout', pendingMs)
2232
+
2189
2233
  // If we might show a pending component, we need to wait for the
2190
2234
  // pending promise to resolve before we start showing that state
2191
2235
  pendingTimeout = setTimeout(() => {
@@ -2649,7 +2693,13 @@ export class Router<
2649
2693
  TMaskTo extends string = '',
2650
2694
  >(
2651
2695
  opts: NavigateOptions<
2652
- Router<TRouteTree, TTrailingSlashOption, TDehydrated, TSerializedError>,
2696
+ Router<
2697
+ TRouteTree,
2698
+ TTrailingSlashOption,
2699
+ TDefaultStructuralSharingOption,
2700
+ TDehydrated,
2701
+ TSerializedError
2702
+ >,
2653
2703
  TFrom,
2654
2704
  TTo,
2655
2705
  TMaskFrom,
@@ -2723,7 +2773,13 @@ export class Router<
2723
2773
  TResolved = ResolveRelativePath<TFrom, NoInfer<TTo>>,
2724
2774
  >(
2725
2775
  location: ToOptions<
2726
- Router<TRouteTree, TTrailingSlashOption, TDehydrated, TSerializedError>,
2776
+ Router<
2777
+ TRouteTree,
2778
+ TTrailingSlashOption,
2779
+ TDefaultStructuralSharingOption,
2780
+ TDehydrated,
2781
+ TSerializedError
2782
+ >,
2727
2783
  TFrom,
2728
2784
  TTo
2729
2785
  >,
@@ -1,7 +1,7 @@
1
1
  import * as React from 'react'
2
2
  import type { Router } from './router'
3
3
 
4
- const routerContext = React.createContext<Router<any, any>>(null!)
4
+ const routerContext = React.createContext<Router<any, any, any>>(null!)
5
5
 
6
6
  export function getRouterContext() {
7
7
  if (typeof document === 'undefined') {
@@ -0,0 +1,49 @@
1
+ import type { Constrain, ValidateJSON } from './utils'
2
+ import type { AnyRouter } from './router'
3
+
4
+ export type DefaultStructuralSharingEnabled<TRouter extends AnyRouter> =
5
+ boolean extends TRouter['options']['defaultStructuralSharing']
6
+ ? // for now, default to false.
7
+ // TODO in V2: default to true
8
+ false
9
+ : NonNullable<TRouter['options']['defaultStructuralSharing']>
10
+
11
+ export interface OptionalStructuralSharing<TStructuralSharing, TConstraint> {
12
+ readonly structuralSharing?:
13
+ | Constrain<TStructuralSharing, TConstraint>
14
+ | undefined
15
+ }
16
+
17
+ export interface RequiredStructuralSharing<TStructuralSharing, TConstraint> {
18
+ readonly structuralSharing: Constrain<TStructuralSharing, TConstraint>
19
+ }
20
+
21
+ export type StructuralSharingOption<
22
+ TRouter extends AnyRouter,
23
+ TSelected,
24
+ TStructuralSharing,
25
+ > = unknown extends TSelected
26
+ ? OptionalStructuralSharing<TStructuralSharing, boolean>
27
+ : unknown extends TRouter['routeTree']
28
+ ? OptionalStructuralSharing<TStructuralSharing, boolean>
29
+ : TSelected extends ValidateJSON<TSelected>
30
+ ? OptionalStructuralSharing<TStructuralSharing, boolean>
31
+ : DefaultStructuralSharingEnabled<TRouter> extends true
32
+ ? RequiredStructuralSharing<TStructuralSharing, false>
33
+ : OptionalStructuralSharing<TStructuralSharing, false>
34
+
35
+ export type StructuralSharingEnabled<
36
+ TRouter extends AnyRouter,
37
+ TStructuralSharing,
38
+ > = boolean extends TStructuralSharing
39
+ ? DefaultStructuralSharingEnabled<TRouter>
40
+ : TStructuralSharing
41
+
42
+ export type ValidateSelected<
43
+ TRouter extends AnyRouter,
44
+ TSelected,
45
+ TStructuralSharing,
46
+ > =
47
+ StructuralSharingEnabled<TRouter, TStructuralSharing> extends true
48
+ ? ValidateJSON<TSelected>
49
+ : TSelected
@@ -1,45 +1,93 @@
1
1
  import { useMatch } from './useMatch'
2
- import type { RegisteredRouter } from './router'
3
- import type { AnyRoute } from './route'
4
- import type { MakeRouteMatch } from './Matches'
5
- import type { RouteIds } from './routeInfo'
6
- import type { Constrain, StrictOrFrom } from './utils'
2
+ import type {
3
+ StructuralSharingOption,
4
+ ValidateSelected,
5
+ } from './structuralSharing'
6
+ import type { AnyRouter, RegisteredRouter } from './router'
7
+ import type { AllLoaderData, RouteById } from './routeInfo'
8
+ import type { Expand, StrictOrFrom } from './utils'
7
9
 
8
- export type UseLoaderDataOptions<
9
- TRouteTree extends AnyRoute,
10
+ export interface UseLoaderDataBaseOptions<
11
+ TRouter extends AnyRouter,
10
12
  TFrom,
11
13
  TStrict extends boolean,
12
- TRouteMatch extends MakeRouteMatch<TRouteTree, TFrom, TStrict>,
13
14
  TSelected,
14
- > = StrictOrFrom<Constrain<TFrom, RouteIds<TRouteTree>>, TStrict> & {
15
- select?: (match: Required<TRouteMatch>['loaderData']) => TSelected
15
+ TStructuralSharing,
16
+ > {
17
+ select?: (
18
+ match: ResolveLoaderData<TRouter, TFrom, TStrict>,
19
+ ) => ValidateSelected<TRouter, TSelected, TStructuralSharing>
16
20
  }
17
21
 
22
+ export type UseLoaderDataOptions<
23
+ TRouter extends AnyRouter,
24
+ TFrom extends string | undefined,
25
+ TStrict extends boolean,
26
+ TSelected,
27
+ TStructuralSharing,
28
+ > = StrictOrFrom<TRouter, TFrom, TStrict> &
29
+ UseLoaderDataBaseOptions<
30
+ TRouter,
31
+ TFrom,
32
+ TStrict,
33
+ TSelected,
34
+ TStructuralSharing
35
+ > &
36
+ StructuralSharingOption<TRouter, TSelected, TStructuralSharing>
37
+
38
+ export type ResolveLoaderData<
39
+ TRouter extends AnyRouter,
40
+ TFrom,
41
+ TStrict extends boolean,
42
+ > = TStrict extends false
43
+ ? AllLoaderData<TRouter['routeTree']>
44
+ : Expand<RouteById<TRouter['routeTree'], TFrom>['types']['loaderData']>
45
+
46
+ export type UseLoaderDataResult<
47
+ TRouter extends AnyRouter,
48
+ TFrom,
49
+ TStrict extends boolean,
50
+ TSelected,
51
+ > = unknown extends TSelected
52
+ ? ResolveLoaderData<TRouter, TFrom, TStrict>
53
+ : TSelected
54
+
55
+ export type UseLoaderDataRoute<out TId> = <
56
+ TRouter extends AnyRouter = RegisteredRouter,
57
+ TSelected = unknown,
58
+ TStructuralSharing extends boolean = boolean,
59
+ >(
60
+ opts?: UseLoaderDataBaseOptions<
61
+ TRouter,
62
+ TId,
63
+ true,
64
+ TSelected,
65
+ TStructuralSharing
66
+ > &
67
+ StructuralSharingOption<TRouter, TSelected, TStructuralSharing>,
68
+ ) => UseLoaderDataResult<TRouter, TId, true, TSelected>
69
+
18
70
  export function useLoaderData<
19
- TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
71
+ TRouter extends AnyRouter = RegisteredRouter,
20
72
  TFrom extends string | undefined = undefined,
21
73
  TStrict extends boolean = true,
22
- TRouteMatch extends MakeRouteMatch<
23
- TRouteTree,
24
- TFrom,
25
- TStrict
26
- > = MakeRouteMatch<TRouteTree, TFrom, TStrict>,
27
- TSelected = Required<TRouteMatch>['loaderData'],
74
+ TSelected = unknown,
75
+ TStructuralSharing extends boolean = boolean,
28
76
  >(
29
77
  opts: UseLoaderDataOptions<
30
- TRouteTree,
78
+ TRouter,
31
79
  TFrom,
32
80
  TStrict,
33
- TRouteMatch,
34
- TSelected
81
+ TSelected,
82
+ TStructuralSharing
35
83
  >,
36
- ): TSelected {
37
- return useMatch<TRouteTree, TFrom, TStrict, TRouteMatch, TSelected>({
38
- ...opts,
39
- select: (s) => {
40
- return typeof opts.select === 'function'
41
- ? opts.select(s.loaderData)
42
- : (s.loaderData as TSelected)
84
+ ): UseLoaderDataResult<TRouter, TFrom, TStrict, TSelected> {
85
+ return useMatch({
86
+ from: opts.from!,
87
+ strict: opts.strict,
88
+ structuralSharing: opts.structuralSharing,
89
+ select: (s: any) => {
90
+ return opts.select ? opts.select(s.loaderData) : s.loaderData
43
91
  },
44
- })
92
+ } as any) as UseLoaderDataResult<TRouter, TFrom, TStrict, TSelected>
45
93
  }
@@ -1,29 +1,64 @@
1
1
  import { useMatch } from './useMatch'
2
- import type { RegisteredRouter } from './router'
3
- import type { AnyRoute } from './route'
4
- import type { MakeRouteMatch } from './Matches'
5
- import type { RouteIds } from './routeInfo'
6
- import type { Constrain, StrictOrFrom } from './utils'
2
+ import type {
3
+ StructuralSharingOption,
4
+ ValidateSelected,
5
+ } from './structuralSharing'
6
+ import type { AnyRouter, RegisteredRouter } from './router'
7
+ import type { RouteById } from './routeInfo'
8
+ import type { Expand, StrictOrFrom } from './utils'
9
+
10
+ export interface UseLoaderDepsBaseOptions<
11
+ TRouter extends AnyRouter,
12
+ TFrom,
13
+ TSelected,
14
+ TStructuralSharing,
15
+ > {
16
+ select?: (
17
+ deps: ResolveLoaderDeps<TRouter, TFrom>,
18
+ ) => ValidateSelected<TRouter, TSelected, TStructuralSharing>
19
+ }
20
+
21
+ export type UseLoaderDepsOptions<
22
+ TRouter extends AnyRouter,
23
+ TFrom extends string | undefined,
24
+ TSelected,
25
+ TStructuralSharing,
26
+ > = StrictOrFrom<TRouter, TFrom> &
27
+ UseLoaderDepsBaseOptions<TRouter, TFrom, TSelected, TStructuralSharing> &
28
+ StructuralSharingOption<TRouter, TSelected, TStructuralSharing>
29
+
30
+ export type ResolveLoaderDeps<TRouter extends AnyRouter, TFrom> = Expand<
31
+ RouteById<TRouter['routeTree'], TFrom>['types']['loaderDeps']
32
+ >
33
+
34
+ export type UseLoaderDepsResult<
35
+ TRouter extends AnyRouter,
36
+ TFrom,
37
+ TSelected,
38
+ > = unknown extends TSelected ? ResolveLoaderDeps<TRouter, TFrom> : TSelected
39
+
40
+ export type UseLoaderDepsRoute<out TId> = <
41
+ TRouter extends AnyRouter = RegisteredRouter,
42
+ TSelected = unknown,
43
+ TStructuralSharing extends boolean = boolean,
44
+ >(
45
+ opts?: UseLoaderDepsBaseOptions<TRouter, TId, TSelected, TStructuralSharing> &
46
+ StructuralSharingOption<TRouter, TSelected, false>,
47
+ ) => UseLoaderDepsResult<TRouter, TId, TSelected>
7
48
 
8
49
  export function useLoaderDeps<
9
- TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
50
+ TRouter extends AnyRouter = RegisteredRouter,
10
51
  TFrom extends string | undefined = undefined,
11
- TRouteMatch extends MakeRouteMatch<TRouteTree, TFrom> = MakeRouteMatch<
12
- TRouteTree,
13
- TFrom
14
- >,
15
- TSelected = Required<TRouteMatch>['loaderDeps'],
52
+ TSelected = unknown,
53
+ TStructuralSharing extends boolean = boolean,
16
54
  >(
17
- opts: StrictOrFrom<Constrain<TFrom, RouteIds<TRouteTree>>> & {
18
- select?: (match: TRouteMatch) => TSelected
19
- },
20
- ): TSelected {
55
+ opts: UseLoaderDepsOptions<TRouter, TFrom, TSelected, TStructuralSharing>,
56
+ ): UseLoaderDepsResult<TRouter, TFrom, TSelected> {
57
+ const { select, ...rest } = opts
21
58
  return useMatch({
22
- ...opts,
59
+ ...rest,
23
60
  select: (s) => {
24
- return typeof opts.select === 'function'
25
- ? opts.select(s.loaderDeps)
26
- : s.loaderDeps
61
+ return select ? select(s.loaderDeps) : s.loaderDeps
27
62
  },
28
- })
63
+ }) as UseLoaderDepsResult<TRouter, TFrom, TSelected>
29
64
  }
@@ -1,15 +1,37 @@
1
1
  import { useRouterState } from './useRouterState'
2
+ import type {
3
+ StructuralSharingOption,
4
+ ValidateSelected,
5
+ } from './structuralSharing'
2
6
  import type { AnyRouter, RegisteredRouter, RouterState } from './router'
3
7
 
8
+ export interface UseLocationBaseOptions<
9
+ TRouter extends AnyRouter,
10
+ TSelected,
11
+ TStructuralSharing extends boolean = boolean,
12
+ > {
13
+ select?: (
14
+ state: RouterState<TRouter['routeTree']>['location'],
15
+ ) => ValidateSelected<TRouter, TSelected, TStructuralSharing>
16
+ }
17
+
18
+ export type UseLocationResult<
19
+ TRouter extends AnyRouter,
20
+ TSelected,
21
+ > = unknown extends TSelected
22
+ ? RouterState<TRouter['routeTree']>['location']
23
+ : TSelected
24
+
4
25
  export function useLocation<
5
26
  TRouter extends AnyRouter = RegisteredRouter,
6
- TLocationState = RouterState<TRouter['routeTree']>['location'],
7
- TSelected = TLocationState,
8
- >(opts?: { select?: (state: TLocationState) => TSelected }): TSelected {
27
+ TSelected = unknown,
28
+ TStructuralSharing extends boolean = boolean,
29
+ >(
30
+ opts?: UseLocationBaseOptions<TRouter, TSelected, TStructuralSharing> &
31
+ StructuralSharingOption<TRouter, TSelected, TStructuralSharing>,
32
+ ): UseLocationResult<TRouter, TSelected> {
9
33
  return useRouterState({
10
- select: (state) =>
11
- opts?.select
12
- ? opts.select(state.location as TLocationState)
13
- : (state.location as TSelected),
14
- })
34
+ select: (state: any) =>
35
+ opts?.select ? opts.select(state.location) : state.location,
36
+ } as any) as UseLocationResult<TRouter, TSelected>
15
37
  }
package/src/useMatch.tsx CHANGED
@@ -2,44 +2,93 @@ import * as React from 'react'
2
2
  import invariant from 'tiny-invariant'
3
3
  import { useRouterState } from './useRouterState'
4
4
  import { matchContext } from './matchContext'
5
- import type { RegisteredRouter } from './router'
6
- import type { AnyRoute } from './route'
5
+ import type {
6
+ StructuralSharingOption,
7
+ ValidateSelected,
8
+ } from './structuralSharing'
9
+ import type { AnyRouter, RegisteredRouter } from './router'
7
10
  import type { MakeRouteMatch } from './Matches'
8
- import type { RouteIds } from './routeInfo'
9
- import type { Constrain, StrictOrFrom } from './utils'
11
+ import type { StrictOrFrom, ThrowOrOptional } from './utils'
10
12
 
11
- export type UseMatchOptions<
13
+ export interface UseMatchBaseOptions<
14
+ TRouter extends AnyRouter,
12
15
  TFrom,
13
16
  TStrict extends boolean,
14
- TRouteMatch,
17
+ TThrow,
15
18
  TSelected,
16
- TThrow extends boolean,
17
- > = StrictOrFrom<TFrom, TStrict> & {
18
- select?: (match: TRouteMatch) => TSelected
19
+ TStructuralSharing extends boolean,
20
+ > {
21
+ select?: (
22
+ match: MakeRouteMatch<TRouter['routeTree'], TFrom, TStrict>,
23
+ ) => ValidateSelected<TRouter, TSelected, TStructuralSharing>
19
24
  shouldThrow?: TThrow
20
25
  }
21
26
 
27
+ export type UseMatchRoute<out TFrom> = <
28
+ TRouter extends AnyRouter = RegisteredRouter,
29
+ TSelected = unknown,
30
+ TStructuralSharing extends boolean = boolean,
31
+ >(
32
+ opts?: UseMatchBaseOptions<
33
+ TRouter,
34
+ TFrom,
35
+ true,
36
+ true,
37
+ TSelected,
38
+ TStructuralSharing
39
+ > &
40
+ StructuralSharingOption<TRouter, TSelected, TStructuralSharing>,
41
+ ) => UseMatchResult<TRouter, TFrom, true, TSelected>
42
+
43
+ export type UseMatchOptions<
44
+ TRouter extends AnyRouter,
45
+ TFrom extends string | undefined,
46
+ TStrict extends boolean,
47
+ TSelected,
48
+ TThrow extends boolean,
49
+ TStructuralSharing extends boolean,
50
+ > = StrictOrFrom<TRouter, TFrom, TStrict> &
51
+ UseMatchBaseOptions<
52
+ TRouter,
53
+ TFrom,
54
+ TStrict,
55
+ TThrow,
56
+ TSelected,
57
+ TStructuralSharing
58
+ > &
59
+ StructuralSharingOption<TRouter, TSelected, TStructuralSharing>
60
+
61
+ export type UseMatchResult<
62
+ TRouter extends AnyRouter,
63
+ TFrom,
64
+ TStrict extends boolean,
65
+ TSelected,
66
+ > = unknown extends TSelected
67
+ ? MakeRouteMatch<TRouter['routeTree'], TFrom, TStrict>
68
+ : TSelected
69
+
22
70
  export function useMatch<
23
- TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
71
+ TRouter extends AnyRouter = RegisteredRouter,
24
72
  TFrom extends string | undefined = undefined,
25
73
  TStrict extends boolean = true,
26
- TRouteMatch = MakeRouteMatch<TRouteTree, TFrom, TStrict>,
27
- TSelected = TRouteMatch,
28
74
  TThrow extends boolean = true,
75
+ TSelected = unknown,
76
+ TStructuralSharing extends boolean = boolean,
29
77
  >(
30
78
  opts: UseMatchOptions<
31
- Constrain<TFrom, RouteIds<TRouteTree>>,
79
+ TRouter,
80
+ TFrom,
32
81
  TStrict,
33
- TRouteMatch,
34
82
  TSelected,
35
- TThrow
83
+ TThrow,
84
+ TStructuralSharing
36
85
  >,
37
- ): TThrow extends true ? TSelected : TSelected | undefined {
86
+ ): ThrowOrOptional<UseMatchResult<TRouter, TFrom, TStrict, TSelected>, TThrow> {
38
87
  const nearestMatchId = React.useContext(matchContext)
39
88
 
40
89
  const matchSelection = useRouterState({
41
- select: (state) => {
42
- const match = state.matches.find((d) =>
90
+ select: (state: any) => {
91
+ const match = state.matches.find((d: any) =>
43
92
  opts.from ? opts.from === d.routeId : d.id === nearestMatchId,
44
93
  )
45
94
  invariant(
@@ -51,9 +100,10 @@ export function useMatch<
51
100
  return undefined
52
101
  }
53
102
 
54
- return opts.select ? opts.select(match as any) : match
103
+ return opts.select ? opts.select(match) : match
55
104
  },
56
- })
105
+ structuralSharing: opts.structuralSharing,
106
+ } as any)
57
107
 
58
- return matchSelection as TSelected
108
+ return matchSelection as any
59
109
  }