@tanstack/react-router 1.29.2 → 1.31.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 (63) hide show
  1. package/dist/cjs/Matches.cjs.map +1 -1
  2. package/dist/cjs/Matches.d.cts +9 -9
  3. package/dist/cjs/RouterProvider.cjs.map +1 -1
  4. package/dist/cjs/RouterProvider.d.cts +7 -7
  5. package/dist/cjs/fileRoute.d.cts +1 -1
  6. package/dist/cjs/link.cjs.map +1 -1
  7. package/dist/cjs/link.d.cts +42 -42
  8. package/dist/cjs/path.cjs +1 -1
  9. package/dist/cjs/path.cjs.map +1 -1
  10. package/dist/cjs/redirects.cjs.map +1 -1
  11. package/dist/cjs/redirects.d.cts +5 -6
  12. package/dist/cjs/route.cjs.map +1 -1
  13. package/dist/cjs/route.d.cts +6 -6
  14. package/dist/cjs/routeInfo.d.cts +19 -2
  15. package/dist/cjs/router.cjs +5 -1
  16. package/dist/cjs/router.cjs.map +1 -1
  17. package/dist/cjs/router.d.cts +13 -12
  18. package/dist/cjs/routerContext.cjs.map +1 -1
  19. package/dist/cjs/routerContext.d.cts +1 -1
  20. package/dist/cjs/useNavigate.cjs.map +1 -1
  21. package/dist/cjs/useNavigate.d.cts +3 -4
  22. package/dist/cjs/useRouter.cjs.map +1 -1
  23. package/dist/cjs/useRouter.d.cts +3 -4
  24. package/dist/cjs/useRouterState.cjs.map +1 -1
  25. package/dist/cjs/useRouterState.d.cts +3 -4
  26. package/dist/esm/Matches.d.ts +9 -9
  27. package/dist/esm/Matches.js.map +1 -1
  28. package/dist/esm/RouterProvider.d.ts +7 -7
  29. package/dist/esm/RouterProvider.js.map +1 -1
  30. package/dist/esm/fileRoute.d.ts +1 -1
  31. package/dist/esm/link.d.ts +42 -42
  32. package/dist/esm/link.js.map +1 -1
  33. package/dist/esm/path.js +1 -1
  34. package/dist/esm/path.js.map +1 -1
  35. package/dist/esm/redirects.d.ts +5 -6
  36. package/dist/esm/redirects.js.map +1 -1
  37. package/dist/esm/route.d.ts +6 -6
  38. package/dist/esm/route.js.map +1 -1
  39. package/dist/esm/routeInfo.d.ts +19 -2
  40. package/dist/esm/router.d.ts +13 -12
  41. package/dist/esm/router.js +5 -1
  42. package/dist/esm/router.js.map +1 -1
  43. package/dist/esm/routerContext.d.ts +1 -1
  44. package/dist/esm/routerContext.js.map +1 -1
  45. package/dist/esm/useNavigate.d.ts +3 -4
  46. package/dist/esm/useNavigate.js.map +1 -1
  47. package/dist/esm/useRouter.d.ts +3 -4
  48. package/dist/esm/useRouter.js.map +1 -1
  49. package/dist/esm/useRouterState.d.ts +3 -4
  50. package/dist/esm/useRouterState.js.map +1 -1
  51. package/package.json +1 -1
  52. package/src/Matches.tsx +39 -21
  53. package/src/RouterProvider.tsx +34 -11
  54. package/src/link.tsx +124 -139
  55. package/src/path.ts +1 -1
  56. package/src/redirects.ts +14 -14
  57. package/src/route.ts +3 -3
  58. package/src/routeInfo.ts +72 -4
  59. package/src/router.ts +59 -13
  60. package/src/routerContext.tsx +1 -1
  61. package/src/useNavigate.tsx +9 -10
  62. package/src/useRouter.tsx +4 -5
  63. package/src/useRouterState.tsx +5 -6
package/src/routeInfo.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type { AnyRoute } from './route'
2
+ import type { AnyRouter, Router, TrailingSlashOption } from './router'
2
3
  import type { UnionToIntersection, UnionToTuple } from './utils'
3
4
 
4
5
  export type ParseRoute<TRouteTree, TAcc = TRouteTree> = TRouteTree extends {
@@ -9,12 +10,14 @@ export type ParseRoute<TRouteTree, TAcc = TRouteTree> = TRouteTree extends {
9
10
  : TAcc
10
11
  : TAcc
11
12
 
12
- export type RouteLeaves<TRouteTree> =
13
+ export type ParseRouteWithoutBranches<TRouteTree> =
13
14
  ParseRoute<TRouteTree> extends infer TRoute extends AnyRoute
14
15
  ? TRoute extends any
15
16
  ? TRoute['types']['children'] extends ReadonlyArray<any>
16
- ? never
17
- : TRoute['fullPath']
17
+ ? '/' extends TRoute['types']['children'][number]['path']
18
+ ? never
19
+ : TRoute
20
+ : TRoute
18
21
  : never
19
22
  : never
20
23
 
@@ -29,9 +32,14 @@ export type RouteById<TRouteTree extends AnyRoute, TId> = Extract<
29
32
 
30
33
  export type RouteIds<TRouteTree extends AnyRoute> = ParseRoute<TRouteTree>['id']
31
34
 
35
+ export type CatchAllPaths<TRouteTree extends AnyRoute> = Record<
36
+ '.' | '..' | '',
37
+ ParseRoute<TRouteTree>
38
+ >
39
+
32
40
  export type RoutesByPath<TRouteTree extends AnyRoute> = {
33
41
  [K in ParseRoute<TRouteTree> as K['fullPath']]: K
34
- } & Record<'.' | '..', ParseRoute<TRouteTree>>
42
+ } & CatchAllPaths<TRouteTree>
35
43
 
36
44
  export type RouteByPath<TRouteTree extends AnyRoute, TPath> = Extract<
37
45
  string extends TPath
@@ -44,6 +52,66 @@ export type RoutePaths<TRouteTree extends AnyRoute> =
44
52
  | ParseRoute<TRouteTree>['fullPath']
45
53
  | '/'
46
54
 
55
+ export type RouteToPathAlwaysTrailingSlash<TRoute extends AnyRoute> =
56
+ TRoute['path'] extends '/'
57
+ ? TRoute['fullPath']
58
+ : TRoute['fullPath'] extends '/'
59
+ ? TRoute['fullPath']
60
+ : `${TRoute['fullPath']}/`
61
+
62
+ export type RouteToPathNeverTrailingSlash<TRoute extends AnyRoute> =
63
+ TRoute['path'] extends '/'
64
+ ? TRoute['fullPath'] extends '/'
65
+ ? TRoute['fullPath']
66
+ : TRoute['fullPath'] extends `${infer TRest}/`
67
+ ? TRest
68
+ : TRoute['fullPath']
69
+ : TRoute['fullPath']
70
+
71
+ export type RouteToPathPreserveTrailingSlash<TRoute extends AnyRoute> =
72
+ | RouteToPathNeverTrailingSlash<TRoute>
73
+ | RouteToPathAlwaysTrailingSlash<TRoute>
74
+
75
+ export type RouteToPathByTrailingSlashOption<TRoute extends AnyRoute> = {
76
+ always: RouteToPathAlwaysTrailingSlash<TRoute>
77
+ preserve: RouteToPathPreserveTrailingSlash<TRoute>
78
+ never: RouteToPathNeverTrailingSlash<TRoute>
79
+ }
80
+
81
+ export type TrailingSlashOptionByRouter<TRouter extends AnyRouter> =
82
+ TrailingSlashOption extends TRouter['options']['trailingSlash']
83
+ ? 'never'
84
+ : NonNullable<TRouter['options']['trailingSlash']>
85
+
86
+ export type RouteToByRouter<
87
+ TRouter extends AnyRouter,
88
+ TRoute extends AnyRoute,
89
+ > = RouteToPathByTrailingSlashOption<TRoute>[TrailingSlashOptionByRouter<TRouter>]
90
+
91
+ export type RouteToPath<
92
+ TRouter extends AnyRouter,
93
+ TRouteTree extends AnyRoute,
94
+ > =
95
+ ParseRouteWithoutBranches<TRouteTree> extends infer TRoute extends AnyRoute
96
+ ? TRoute extends any
97
+ ? RouteToByRouter<TRouter, TRoute>
98
+ : never
99
+ : never
100
+
101
+ export type RoutesByToPath<TRouter extends AnyRouter> = {
102
+ [TRoute in ParseRouteWithoutBranches<TRouter['routeTree']> as RouteToByRouter<
103
+ TRouter,
104
+ TRoute
105
+ >]: TRoute
106
+ } & CatchAllPaths<TRouter['routeTree']>
107
+
108
+ export type RouteByToPath<TRouter extends AnyRouter, TTo> = Extract<
109
+ string extends TTo
110
+ ? ParseRouteWithoutBranches<TRouter['routeTree']>
111
+ : RoutesByToPath<TRouter>[TTo],
112
+ AnyRoute
113
+ >
114
+
47
115
  export type RoutePathsAutoComplete<TRouteTree extends AnyRoute, T> =
48
116
  | (string extends T ? T & {} : T)
49
117
  | RoutePaths<TRouteTree>
package/src/router.ts CHANGED
@@ -87,7 +87,7 @@ import type { DeferredPromiseState } from './defer'
87
87
  declare global {
88
88
  interface Window {
89
89
  __TSR_DEHYDRATED__?: { data: string }
90
- __TSR_ROUTER_CONTEXT__?: React.Context<Router<any>>
90
+ __TSR_ROUTER_CONTEXT__?: React.Context<Router<any, any>>
91
91
  }
92
92
  }
93
93
 
@@ -95,7 +95,7 @@ export interface Register {
95
95
  // router: Router
96
96
  }
97
97
 
98
- export type AnyRouter = Router<AnyRoute, any, any>
98
+ export type AnyRouter = Router<any, any, any, any>
99
99
 
100
100
  export type RegisteredRouter = Register extends {
101
101
  router: infer TRouter extends AnyRouter
@@ -117,8 +117,11 @@ export type RouterContextOptions<TRouteTree extends AnyRoute> =
117
117
  context: TRouteTree['types']['routerContext']
118
118
  }
119
119
 
120
+ export type TrailingSlashOption = 'always' | 'never' | 'preserve'
121
+
120
122
  export interface RouterOptions<
121
123
  TRouteTree extends AnyRoute,
124
+ TTrailingSlashOption extends TrailingSlashOption,
122
125
  TDehydrated extends Record<string, any> = Record<string, any>,
123
126
  TSerializedError extends Record<string, any> = Record<string, any>,
124
127
  > {
@@ -157,7 +160,7 @@ export interface RouterOptions<
157
160
  defaultNotFoundComponent?: NotFoundRouteComponent
158
161
  transformer?: RouterTransformer
159
162
  errorSerializer?: RouterErrorSerializer<TSerializedError>
160
- trailingSlash?: 'always' | 'never' | 'preserve'
163
+ trailingSlash?: TTrailingSlashOption
161
164
  }
162
165
 
163
166
  export interface RouterTransformer {
@@ -220,9 +223,18 @@ export interface DehydratedRouter {
220
223
 
221
224
  export type RouterConstructorOptions<
222
225
  TRouteTree extends AnyRoute,
226
+ TTrailingSlashOption extends TrailingSlashOption,
223
227
  TDehydrated extends Record<string, any>,
224
228
  TSerializedError extends Record<string, any>,
225
- > = Omit<RouterOptions<TRouteTree, TDehydrated, TSerializedError>, 'context'> &
229
+ > = Omit<
230
+ RouterOptions<
231
+ TRouteTree,
232
+ TTrailingSlashOption,
233
+ TDehydrated,
234
+ TSerializedError
235
+ >,
236
+ 'context'
237
+ > &
226
238
  RouterContextOptions<TRouteTree>
227
239
 
228
240
  export const componentTypes = [
@@ -261,17 +273,29 @@ export type RouterListener<TRouterEvent extends RouterEvent> = {
261
273
  }
262
274
 
263
275
  export function createRouter<
264
- TRouteTree extends AnyRoute = AnyRoute,
276
+ TRouteTree extends AnyRoute,
277
+ TTrailingSlashOption extends TrailingSlashOption,
265
278
  TDehydrated extends Record<string, any> = Record<string, any>,
266
279
  TSerializedError extends Record<string, any> = Record<string, any>,
267
280
  >(
268
- options: RouterConstructorOptions<TRouteTree, TDehydrated, TSerializedError>,
281
+ options: RouterConstructorOptions<
282
+ TRouteTree,
283
+ TTrailingSlashOption,
284
+ TDehydrated,
285
+ TSerializedError
286
+ >,
269
287
  ) {
270
- return new Router<TRouteTree, TDehydrated, TSerializedError>(options)
288
+ return new Router<
289
+ TRouteTree,
290
+ TTrailingSlashOption,
291
+ TDehydrated,
292
+ TSerializedError
293
+ >(options)
271
294
  }
272
295
 
273
296
  export class Router<
274
- in out TRouteTree extends AnyRoute = AnyRoute,
297
+ in out TRouteTree extends AnyRoute,
298
+ in out TTrailingSlashOption extends TrailingSlashOption,
275
299
  in out TDehydrated extends Record<string, any> = Record<string, any>,
276
300
  in out TSerializedError extends Record<string, any> = Record<string, any>,
277
301
  > {
@@ -292,7 +316,12 @@ export class Router<
292
316
  __store!: Store<RouterState<TRouteTree>>
293
317
  options!: PickAsRequired<
294
318
  Omit<
295
- RouterOptions<TRouteTree, TDehydrated, TSerializedError>,
319
+ RouterOptions<
320
+ TRouteTree,
321
+ TTrailingSlashOption,
322
+ TDehydrated,
323
+ TSerializedError
324
+ >,
296
325
  'transformer'
297
326
  > & {
298
327
  transformer: RouterTransformer
@@ -313,6 +342,7 @@ export class Router<
313
342
  constructor(
314
343
  options: RouterConstructorOptions<
315
344
  TRouteTree,
345
+ TTrailingSlashOption,
316
346
  TDehydrated,
317
347
  TSerializedError
318
348
  >,
@@ -343,6 +373,7 @@ export class Router<
343
373
  update = (
344
374
  newOptions: RouterConstructorOptions<
345
375
  TRouteTree,
376
+ TTrailingSlashOption,
346
377
  TDehydrated,
347
378
  TSerializedError
348
379
  >,
@@ -925,9 +956,14 @@ export class Router<
925
956
  })
926
957
  }
927
958
 
928
- // encodeURI all params so the generated href is valid and stable
959
+ // encode all path params so the generated href is valid and stable
929
960
  Object.keys(nextParams).forEach((key) => {
930
- nextParams[key] = encodeURI(nextParams[key])
961
+ if (['*', '_splat'].includes(key)) {
962
+ // the splat/catch-all routes shouldn't have the '/' encoded out
963
+ nextParams[key] = encodeURI(nextParams[key])
964
+ } else {
965
+ nextParams[key] = encodeURIComponent(nextParams[key])
966
+ }
931
967
  })
932
968
 
933
969
  pathname = interpolatePath({
@@ -1843,7 +1879,13 @@ export class Router<
1843
1879
  TMaskFrom extends RoutePaths<TRouteTree> | string = TFrom,
1844
1880
  TMaskTo extends string = '',
1845
1881
  >(
1846
- opts: NavigateOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo>,
1882
+ opts: NavigateOptions<
1883
+ Router<TRouteTree, TTrailingSlashOption, TDehydrated, TSerializedError>,
1884
+ TFrom,
1885
+ TTo,
1886
+ TMaskFrom,
1887
+ TMaskTo
1888
+ >,
1847
1889
  ): Promise<Array<AnyRouteMatch> | undefined> => {
1848
1890
  const next = this.buildLocation(opts as any)
1849
1891
 
@@ -1916,7 +1958,11 @@ export class Router<
1916
1958
  TTo extends string = '',
1917
1959
  TResolved = ResolveRelativePath<TFrom, NoInfer<TTo>>,
1918
1960
  >(
1919
- location: ToOptions<TRouteTree, TFrom, TTo>,
1961
+ location: ToOptions<
1962
+ Router<TRouteTree, TTrailingSlashOption, TDehydrated, TSerializedError>,
1963
+ TFrom,
1964
+ TTo
1965
+ >,
1920
1966
  opts?: MatchRouteOptions,
1921
1967
  ): false | RouteById<TRouteTree, TResolved>['types']['allParams'] => {
1922
1968
  const matchLocation = {
@@ -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>>(null!)
4
+ const routerContext = React.createContext<Router<any, any>>(null!)
5
5
 
6
6
  export function getRouterContext() {
7
7
  if (typeof document === 'undefined') {
@@ -3,20 +3,19 @@ import { useMatch } from './Matches'
3
3
  import { useRouter } from './useRouter'
4
4
 
5
5
  import type { NavigateOptions } from './link'
6
- import type { AnyRoute } from './route'
7
6
  import type { RoutePaths, RoutePathsAutoComplete } from './routeInfo'
8
- import type { RegisteredRouter } from './router'
7
+ import type { AnyRouter, RegisteredRouter } from './router'
9
8
 
10
9
  export type UseNavigateResult<TDefaultFrom extends string> = <
11
10
  TTo extends string,
12
- TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
13
- TFrom extends RoutePaths<TRouteTree> | string = TDefaultFrom,
14
- TMaskFrom extends RoutePaths<TRouteTree> | string = TFrom,
11
+ TRouter extends AnyRouter = RegisteredRouter,
12
+ TFrom extends RoutePaths<TRouter['routeTree']> | string = TDefaultFrom,
13
+ TMaskFrom extends RoutePaths<TRouter['routeTree']> | string = TFrom,
15
14
  TMaskTo extends string = '',
16
15
  >({
17
16
  from,
18
17
  ...rest
19
- }: NavigateOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo>) => Promise<void>
18
+ }: NavigateOptions<TRouter, TFrom, TTo, TMaskFrom, TMaskTo>) => Promise<void>
20
19
 
21
20
  export function useNavigate<
22
21
  TDefaultFrom extends string = string,
@@ -52,12 +51,12 @@ export function useNavigate<
52
51
  // } //
53
52
 
54
53
  export function Navigate<
55
- TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
56
- TFrom extends RoutePaths<TRouteTree> | string = string,
54
+ TRouter extends AnyRouter = RegisteredRouter,
55
+ TFrom extends RoutePaths<TRouter['routeTree']> | string = string,
57
56
  TTo extends string = '',
58
- TMaskFrom extends RoutePaths<TRouteTree> | string = TFrom,
57
+ TMaskFrom extends RoutePaths<TRouter['routeTree']> | string = TFrom,
59
58
  TMaskTo extends string = '',
60
- >(props: NavigateOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo>): null {
59
+ >(props: NavigateOptions<TRouter, TFrom, TTo, TMaskFrom, TMaskTo>): null {
61
60
  const { navigate } = useRouter()
62
61
  const match = useMatch({ strict: false })
63
62
 
package/src/useRouter.tsx CHANGED
@@ -1,12 +1,11 @@
1
1
  import * as React from 'react'
2
2
  import warning from 'tiny-warning'
3
3
  import { getRouterContext } from './routerContext'
4
- import type { AnyRoute } from './route'
5
- import type { RegisteredRouter, Router } from './router'
4
+ import type { AnyRouter, RegisteredRouter, Router } from './router'
6
5
 
7
- export function useRouter<
8
- TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
9
- >(opts?: { warn?: boolean }): Router<TRouteTree> {
6
+ export function useRouter<TRouter extends AnyRouter = RegisteredRouter>(opts?: {
7
+ warn?: boolean
8
+ }): TRouter {
10
9
  const value = React.useContext(getRouterContext())
11
10
  warning(
12
11
  !((opts?.warn ?? true) && !value),
@@ -1,16 +1,15 @@
1
1
  import { useStore } from '@tanstack/react-store'
2
2
  import { useRouter } from './useRouter'
3
- import type { AnyRoute } from './route'
4
- import type { RegisteredRouter, Router, RouterState } from './router'
3
+ import type { AnyRouter, RegisteredRouter, Router, RouterState } from './router'
5
4
 
6
5
  export function useRouterState<
7
- TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
8
- TSelected = RouterState<TRouteTree>,
6
+ TRouter extends AnyRouter = RegisteredRouter,
7
+ TSelected = RouterState<TRouter['routeTree']>,
9
8
  >(opts?: {
10
- router?: Router<TRouteTree>
9
+ router?: TRouter
11
10
  select: (state: RouterState<RegisteredRouter['routeTree']>) => TSelected
12
11
  }): TSelected {
13
- const contextRouter = useRouter<TRouteTree>({
12
+ const contextRouter = useRouter<TRouter>({
14
13
  warn: opts?.router === undefined,
15
14
  })
16
15
  return useStore((opts?.router || contextRouter).__store, opts?.select as any)