@tanstack/react-router 1.19.2 → 1.19.7

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.
package/src/link.tsx CHANGED
@@ -6,7 +6,6 @@ import { Trim } from './fileRoute'
6
6
  import { AnyRoute, ReactNode, RootSearchSchema } from './route'
7
7
  import { RouteByPath, RoutePaths, RoutePathsAutoComplete } from './routeInfo'
8
8
  import { RegisteredRouter } from './router'
9
- import { LinkProps, UseLinkPropsOptions } from './useNavigate'
10
9
  import {
11
10
  Expand,
12
11
  IsUnion,
@@ -580,32 +579,97 @@ export function useLinkProps<
580
579
  }
581
580
  }
582
581
 
583
- export interface LinkComponent<TProps extends Record<string, any> = {}> {
584
- <
585
- TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
586
- TFrom extends RoutePaths<TRouteTree> | string = string,
587
- TTo extends string = '',
588
- TMaskFrom extends RoutePaths<TRouteTree> | string = TFrom,
589
- TMaskTo extends string = '',
590
- >(
591
- props: LinkProps<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo> &
592
- TProps &
593
- React.RefAttributes<HTMLAnchorElement>,
594
- ): ReactNode
582
+ export type UseLinkPropsOptions<
583
+ TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
584
+ TFrom extends RoutePaths<TRouteTree> | string = string,
585
+ TTo extends string = '',
586
+ TMaskFrom extends RoutePaths<TRouteTree> | string = TFrom,
587
+ TMaskTo extends string = '',
588
+ > = ActiveLinkOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo> &
589
+ React.AnchorHTMLAttributes<HTMLAnchorElement>
590
+
591
+ export type ActiveLinkOptions<
592
+ TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
593
+ TFrom extends RoutePaths<TRouteTree> | string = string,
594
+ TTo extends string = '',
595
+ TMaskFrom extends RoutePaths<TRouteTree> | string = TFrom,
596
+ TMaskTo extends string = '',
597
+ > = LinkOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo> & {
598
+ // A function that returns additional props for the `active` state of this link. These props override other props passed to the link (`style`'s are merged, `className`'s are concatenated)
599
+ activeProps?:
600
+ | React.AnchorHTMLAttributes<HTMLAnchorElement>
601
+ | (() => React.AnchorHTMLAttributes<HTMLAnchorElement>)
602
+ // A function that returns additional props for the `inactive` state of this link. These props override other props passed to the link (`style`'s are merged, `className`'s are concatenated)
603
+ inactiveProps?:
604
+ | React.AnchorHTMLAttributes<HTMLAnchorElement>
605
+ | (() => React.AnchorHTMLAttributes<HTMLAnchorElement>)
606
+ }
607
+
608
+ export type LinkProps<
609
+ TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
610
+ TFrom extends RoutePaths<TRouteTree> | string = string,
611
+ TTo extends string = '',
612
+ TMaskFrom extends RoutePaths<TRouteTree> | string = TFrom,
613
+ TMaskTo extends string = '',
614
+ > = ActiveLinkOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo> & {
615
+ // If a function is passed as a child, it will be given the `isActive` boolean to aid in further styling on the element it returns
616
+ children?:
617
+ | React.ReactNode
618
+ | ((state: { isActive: boolean }) => React.ReactNode)
595
619
  }
596
620
 
597
- export const Link: LinkComponent = React.forwardRef((props: any, ref) => {
598
- const { type, ...linkProps } = useLinkProps(props)
621
+ type LinkComponent<TComp> = <
622
+ TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
623
+ TFrom extends RoutePaths<TRouteTree> = string,
624
+ TTo extends string = '',
625
+ TMaskFrom extends RoutePaths<TRouteTree> = TFrom,
626
+ TMaskTo extends string = '',
627
+ >(
628
+ props: React.PropsWithoutRef<
629
+ LinkProps<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo> &
630
+ (TComp extends React.FC<infer TProps> | React.Component<infer TProps>
631
+ ? TProps
632
+ : TComp extends keyof JSX.IntrinsicElements
633
+ ? React.HTMLProps<TComp>
634
+ : never)
635
+ > &
636
+ React.RefAttributes<
637
+ TComp extends
638
+ | React.FC<{ ref: infer TRef }>
639
+ | React.Component<{ ref: infer TRef }>
640
+ ? TRef
641
+ : TComp extends keyof JSX.IntrinsicElements
642
+ ? React.ComponentRef<TComp>
643
+ : never
644
+ >,
645
+ ) => React.ReactElement
646
+
647
+ export function createLink<const TComp>(Comp: TComp): LinkComponent<TComp> {
648
+ return React.forwardRef(function Link(props, ref) {
649
+ return <Link {...(props as any)} _asChild={Comp} ref={ref} />
650
+ }) as any
651
+ }
652
+
653
+ export const Link: LinkComponent<'a'> = React.forwardRef((props: any, ref) => {
654
+ const { _asChild, ...rest } = props
655
+ const { type, ...linkProps } = useLinkProps(rest as any)
599
656
 
600
657
  const children =
601
- typeof props.children === 'function'
602
- ? props.children({
658
+ typeof rest.children === 'function'
659
+ ? rest.children({
603
660
  isActive: (linkProps as any)['data-status'] === 'active',
604
661
  })
605
- : props.children
662
+ : rest.children
606
663
 
607
- return <a {...linkProps} ref={ref} children={children} />
608
- })
664
+ return React.createElement(
665
+ _asChild ? _asChild : 'a',
666
+ {
667
+ ...linkProps,
668
+ ref,
669
+ },
670
+ children as any,
671
+ )
672
+ }) as any
609
673
 
610
674
  function isCtrlEvent(e: MouseEvent) {
611
675
  return !!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey)
package/src/redirects.ts CHANGED
@@ -1,10 +1,9 @@
1
+ import { PickAsRequired } from '.'
1
2
  import { NavigateOptions } from './link'
2
3
  import { AnyRoute } from './route'
3
4
  import { RoutePaths } from './routeInfo'
4
5
  import { RegisteredRouter } from './router'
5
6
 
6
- // Detect if we're in the DOM
7
-
8
7
  export type AnyRedirect = Redirect<any, any, any, any, any>
9
8
 
10
9
  export type Redirect<
@@ -14,11 +13,27 @@ export type Redirect<
14
13
  TMaskFrom extends RoutePaths<TRouteTree> = TFrom,
15
14
  TMaskTo extends string = '',
16
15
  > = {
16
+ /**
17
+ * @deprecated Use `statusCode` instead
18
+ **/
17
19
  code?: number
20
+ statusCode?: number
18
21
  throw?: any
19
- href?: string
20
22
  } & NavigateOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo>
21
23
 
24
+ export type ResolvedRedirect<
25
+ TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
26
+ TFrom extends RoutePaths<TRouteTree> = '/',
27
+ TTo extends string = '',
28
+ TMaskFrom extends RoutePaths<TRouteTree> = TFrom,
29
+ TMaskTo extends string = '',
30
+ > = PickAsRequired<
31
+ Redirect<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo>,
32
+ 'code' | 'statusCode'
33
+ > & {
34
+ href: string
35
+ }
36
+
22
37
  export function redirect<
23
38
  TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
24
39
  TFrom extends RoutePaths<TRouteTree> = '/',
@@ -29,6 +44,7 @@ export function redirect<
29
44
  opts: Redirect<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo>,
30
45
  ): Redirect<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo> {
31
46
  ;(opts as any).isRedirect = true
47
+ opts.statusCode = opts.statusCode || opts.code || 301
32
48
  if (opts.throw ?? true) {
33
49
  throw opts
34
50
  }
package/src/route.ts CHANGED
@@ -86,8 +86,7 @@ export type RouteOptions<
86
86
  TRouterContext,
87
87
  TAllContext,
88
88
  TLoaderDeps,
89
- TLoaderDataReturn,
90
- TLoaderData
89
+ TLoaderDataReturn
91
90
  > &
92
91
  UpdatableRouteOptions<
93
92
  NoInfer<TAllParams>,
@@ -100,14 +99,11 @@ export type ParamsFallback<
100
99
  TParams,
101
100
  > = unknown extends TParams ? Record<ParsePathParams<TPath>, string> : TParams
102
101
 
103
- export type BaseRouteOptions<
102
+ export type FileBaseRouteOptions<
104
103
  TParentRoute extends AnyRoute = AnyRoute,
105
- TCustomId extends string = string,
106
104
  TPath extends string = string,
107
105
  TSearchSchemaInput extends Record<string, any> = {},
108
106
  TSearchSchema extends Record<string, any> = {},
109
- TSearchSchemaUsed extends Record<string, any> = {},
110
- TFullSearchSchemaInput extends Record<string, any> = TSearchSchemaUsed,
111
107
  TFullSearchSchema extends Record<string, any> = TSearchSchema,
112
108
  TParams extends AnyPathParams = {},
113
109
  TAllParams = ParamsFallback<TPath, TParams>,
@@ -117,11 +113,7 @@ export type BaseRouteOptions<
117
113
  TAllContext extends Record<string, any> = AnyContext,
118
114
  TLoaderDeps extends Record<string, any> = {},
119
115
  TLoaderDataReturn extends any = unknown,
120
- TLoaderData extends any = [TLoaderDataReturn] extends [never]
121
- ? undefined
122
- : TLoaderDataReturn,
123
- > = RoutePathOptions<TCustomId, TPath> & {
124
- getParentRoute: () => TParentRoute
116
+ > = {
125
117
  validateSearch?: SearchSchemaValidator<TSearchSchemaInput, TSearchSchema>
126
118
  shouldReload?:
127
119
  | boolean
@@ -133,7 +125,6 @@ export type BaseRouteOptions<
133
125
  TRouteContext
134
126
  >,
135
127
  ) => any)
136
- } & {
137
128
  // This async function is called before a route is loaded.
138
129
  // If an error is thrown here, the route's loader will not be called.
139
130
  // If thrown during a navigation, the navigation will be cancelled and the error will be passed to the `onError` function.
@@ -145,7 +136,6 @@ export type BaseRouteOptions<
145
136
  TRouteContextReturn,
146
137
  TRouterContext
147
138
  >
148
- } & {
149
139
  loaderDeps?: (opts: { search: TFullSearchSchema }) => TLoaderDeps
150
140
  loader?: RouteLoaderFn<
151
141
  TAllParams,
@@ -155,22 +145,58 @@ export type BaseRouteOptions<
155
145
  TLoaderDataReturn
156
146
  >
157
147
  } & (
158
- | {
159
- // Both or none
160
- parseParams?: (
161
- rawParams: IsAny<TPath, any, Record<ParsePathParams<TPath>, string>>,
162
- ) => TParams extends Record<ParsePathParams<TPath>, any>
163
- ? TParams
164
- : 'parseParams must return an object'
165
- stringifyParams?: (
166
- params: NoInfer<ParamsFallback<TPath, TParams>>,
167
- ) => Record<ParsePathParams<TPath>, string>
168
- }
169
- | {
170
- stringifyParams?: never
171
- parseParams?: never
172
- }
173
- )
148
+ | {
149
+ // Both or none
150
+ parseParams?: (
151
+ rawParams: IsAny<TPath, any, Record<ParsePathParams<TPath>, string>>,
152
+ ) => TParams extends Record<ParsePathParams<TPath>, any>
153
+ ? TParams
154
+ : 'parseParams must return an object'
155
+ stringifyParams?: (
156
+ params: NoInfer<ParamsFallback<TPath, TParams>>,
157
+ ) => Record<ParsePathParams<TPath>, string>
158
+ }
159
+ | {
160
+ stringifyParams?: never
161
+ parseParams?: never
162
+ }
163
+ )
164
+
165
+ export type BaseRouteOptions<
166
+ TParentRoute extends AnyRoute = AnyRoute,
167
+ TCustomId extends string = string,
168
+ TPath extends string = string,
169
+ TSearchSchemaInput extends Record<string, any> = {},
170
+ TSearchSchema extends Record<string, any> = {},
171
+ TSearchSchemaUsed extends Record<string, any> = {},
172
+ TFullSearchSchemaInput extends Record<string, any> = TSearchSchemaUsed,
173
+ TFullSearchSchema extends Record<string, any> = TSearchSchema,
174
+ TParams extends AnyPathParams = {},
175
+ TAllParams = ParamsFallback<TPath, TParams>,
176
+ TRouteContextReturn extends RouteContext = RouteContext,
177
+ TRouteContext extends RouteContext = RouteContext,
178
+ TRouterContext extends RouteConstraints['TRouterContext'] = AnyContext,
179
+ TAllContext extends Record<string, any> = AnyContext,
180
+ TLoaderDeps extends Record<string, any> = {},
181
+ TLoaderDataReturn extends any = unknown,
182
+ > = RoutePathOptions<TCustomId, TPath> &
183
+ FileBaseRouteOptions<
184
+ TParentRoute,
185
+ TPath,
186
+ TSearchSchemaInput,
187
+ TSearchSchema,
188
+ TFullSearchSchema,
189
+ TParams,
190
+ TAllParams,
191
+ TRouteContextReturn,
192
+ TRouteContext,
193
+ TRouterContext,
194
+ TAllContext,
195
+ TLoaderDeps,
196
+ TLoaderDataReturn
197
+ > & {
198
+ getParentRoute: () => TParentRoute
199
+ }
174
200
 
175
201
  type BeforeLoadFn<
176
202
  TFullSearchSchema extends Record<string, any>,
package/src/router.ts CHANGED
@@ -65,7 +65,7 @@ import {
65
65
  trimPathRight,
66
66
  } from './path'
67
67
  import invariant from 'tiny-invariant'
68
- import { AnyRedirect, isRedirect } from './redirects'
68
+ import { AnyRedirect, ResolvedRedirect, isRedirect } from './redirects'
69
69
  import { NotFoundError, isNotFound } from './not-found'
70
70
  import { NavigateOptions, ResolveRelativePath, ToOptions } from './link'
71
71
  import { NoInfer } from '@tanstack/react-store'
@@ -168,6 +168,7 @@ export interface RouterState<TRouteTree extends AnyRoute = AnyRoute> {
168
168
  resolvedLocation: ParsedLocation<FullSearchSchema<TRouteTree>>
169
169
  lastUpdated: number
170
170
  statusCode: number
171
+ redirect?: ResolvedRedirect
171
172
  }
172
173
 
173
174
  export type ListenerFn<TEvent extends RouterEvent> = (event: TEvent) => void
@@ -1476,9 +1477,14 @@ export class Router<
1476
1477
  handleError(err)
1477
1478
 
1478
1479
  // If the route is still active, redirect
1479
- if (isActive) {
1480
- this.handleRedirect(err)
1481
- }
1480
+ // TODO: Do we really need this?
1481
+ invariant(
1482
+ false,
1483
+ 'You need to redirect from a background fetch? This is not supported yet. File an issue.',
1484
+ )
1485
+ // if (isActive) {
1486
+ // this.handleRedirect(err)
1487
+ // }
1482
1488
  }
1483
1489
  }
1484
1490
  })()
@@ -1583,7 +1589,7 @@ export class Router<
1583
1589
  })
1584
1590
 
1585
1591
  try {
1586
- let redirected: AnyRedirect
1592
+ let redirect: ResolvedRedirect
1587
1593
  let notFound: NotFoundError
1588
1594
 
1589
1595
  try {
@@ -1595,8 +1601,7 @@ export class Router<
1595
1601
  })
1596
1602
  } catch (err) {
1597
1603
  if (isRedirect(err)) {
1598
- redirected = err
1599
- this.handleRedirect(err)
1604
+ redirect = this.resolveRedirect(err)
1600
1605
  } else if (isNotFound(err)) {
1601
1606
  notFound = err
1602
1607
  this.handleNotFound(pendingMatches, err)
@@ -1635,11 +1640,12 @@ export class Router<
1635
1640
  ...exitingMatches.filter((d) => d.status !== 'error'),
1636
1641
  ],
1637
1642
  statusCode:
1638
- redirected?.code || notFound
1643
+ redirect?.statusCode || notFound
1639
1644
  ? 404
1640
1645
  : s.matches.some((d) => d.status === 'error')
1641
1646
  ? 500
1642
1647
  : 200,
1648
+ redirect,
1643
1649
  }))
1644
1650
  this.cleanCache()
1645
1651
  })
@@ -1682,13 +1688,14 @@ export class Router<
1682
1688
  return this.latestLoadPromise
1683
1689
  }
1684
1690
 
1685
- handleRedirect = (err: AnyRedirect) => {
1686
- if (!err.href) {
1687
- err.href = this.buildLocation(err as any).href
1688
- }
1689
- if (!isServer) {
1690
- this.navigate({ ...(err as any), replace: true })
1691
+ resolveRedirect = (err: AnyRedirect): ResolvedRedirect => {
1692
+ let redirect = err as ResolvedRedirect
1693
+
1694
+ if (!redirect.href) {
1695
+ redirect.href = this.buildLocation(redirect as any).href
1691
1696
  }
1697
+
1698
+ return redirect
1692
1699
  }
1693
1700
 
1694
1701
  cleanCache = () => {
@@ -73,43 +73,3 @@ export function Navigate<
73
73
 
74
74
  return null
75
75
  }
76
-
77
- export type UseLinkPropsOptions<
78
- TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
79
- TFrom extends RoutePaths<TRouteTree> | string = string,
80
- TTo extends string = '',
81
- TMaskFrom extends RoutePaths<TRouteTree> | string = TFrom,
82
- TMaskTo extends string = '',
83
- > = ActiveLinkOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo> &
84
- React.AnchorHTMLAttributes<HTMLAnchorElement>
85
-
86
- export type LinkProps<
87
- TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
88
- TFrom extends RoutePaths<TRouteTree> | string = string,
89
- TTo extends string = '',
90
- TMaskFrom extends RoutePaths<TRouteTree> | string = TFrom,
91
- TMaskTo extends string = '',
92
- > = ActiveLinkOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo> &
93
- Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, 'children'> & {
94
- // If a function is passed as a child, it will be given the `isActive` boolean to aid in further styling on the element it returns
95
- children?:
96
- | React.ReactNode
97
- | ((state: { isActive: boolean }) => React.ReactNode)
98
- }
99
-
100
- export type ActiveLinkOptions<
101
- TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
102
- TFrom extends RoutePaths<TRouteTree> | string = string,
103
- TTo extends string = '',
104
- TMaskFrom extends RoutePaths<TRouteTree> | string = TFrom,
105
- TMaskTo extends string = '',
106
- > = LinkOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo> & {
107
- // A function that returns additional props for the `active` state of this link. These props override other props passed to the link (`style`'s are merged, `className`'s are concatenated)
108
- activeProps?:
109
- | React.AnchorHTMLAttributes<HTMLAnchorElement>
110
- | (() => React.AnchorHTMLAttributes<HTMLAnchorElement>)
111
- // A function that returns additional props for the `inactive` state of this link. These props override other props passed to the link (`style`'s are merged, `className`'s are concatenated)
112
- inactiveProps?:
113
- | React.AnchorHTMLAttributes<HTMLAnchorElement>
114
- | (() => React.AnchorHTMLAttributes<HTMLAnchorElement>)
115
- }