@tanstack/react-router 1.92.13 → 1.94.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.
@@ -1,4 +1,4 @@
1
- import { FromPathOption, NavigateOptions, PathParamOptions, SearchParamOptions, ToPathOption } from './link.js';
1
+ import { FromPathOption, LinkComponentProps, NavigateOptions, PathParamOptions, SearchParamOptions, ToPathOption } from './link.js';
2
2
  import { RouteIds } from './routeInfo.js';
3
3
  import { AnyRouter, RegisteredRouter } from './router.js';
4
4
  import { UseParamsOptions, UseParamsResult } from './useParams.js';
@@ -27,15 +27,19 @@ export type InferMaskTo<TOptions> = TOptions extends {
27
27
  mask: {
28
28
  to: infer TTo extends string;
29
29
  };
30
- } ? TTo : string;
30
+ } ? TTo : '';
31
31
  export type InferMaskFrom<TOptions> = TOptions extends {
32
32
  mask: {
33
33
  from: infer TFrom extends string;
34
34
  };
35
35
  } ? TFrom : string;
36
36
  export type ValidateNavigateOptions<TOptions, TRouter extends AnyRouter = RegisteredRouter> = Constrain<TOptions, NavigateOptions<TRouter, InferFrom<TOptions>, InferTo<TOptions>, InferMaskFrom<TOptions>, InferMaskTo<TOptions>>>;
37
- export type ValidateNavigateOptionsArray<TOptions extends ReadonlyArray<any>> = {
38
- [K in keyof TOptions]: ValidateNavigateOptions<TOptions[K]>;
37
+ export type ValidateNavigateOptionsArray<TOptions extends ReadonlyArray<any>, TRouter extends AnyRouter = RegisteredRouter> = {
38
+ [K in keyof TOptions]: ValidateNavigateOptions<TOptions[K], TRouter>;
39
+ };
40
+ export type ValidateLinkOptions<TOptions, TComp = 'a', TRouter extends AnyRouter = RegisteredRouter> = Constrain<TOptions, LinkComponentProps<TComp, TRouter, InferFrom<TOptions>, InferTo<TOptions>, InferMaskFrom<TOptions>, InferMaskTo<TOptions>>>;
41
+ export type ValidateLinkOptionsArray<TOptions extends ReadonlyArray<any>, TComp = 'a', TRouter extends AnyRouter = RegisteredRouter> = {
42
+ [K in keyof TOptions]: ValidateLinkOptions<TOptions[K], TComp, TRouter>;
39
43
  };
40
44
  export type ValidateId<TId extends string, TRouter extends AnyRouter = RegisteredRouter> = ConstrainLiteral<TId, RouteIds<TRouter['routeTree']>>;
41
45
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/react-router",
3
- "version": "1.92.13",
3
+ "version": "1.94.0",
4
4
  "description": "Modern and scalable routing for React applications",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
package/src/link.tsx CHANGED
@@ -46,6 +46,10 @@ import type {
46
46
  WithoutEmpty,
47
47
  } from './utils'
48
48
  import type { ReactNode } from 'react'
49
+ import type {
50
+ ValidateLinkOptions,
51
+ ValidateLinkOptionsArray,
52
+ } from './typePrimitives'
49
53
 
50
54
  export type ParsePathParams<T extends string, TAcc = never> = T &
51
55
  `${string}$${string}` extends never
@@ -1040,19 +1044,21 @@ function isCtrlEvent(e: MouseEvent) {
1040
1044
  return !!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey)
1041
1045
  }
1042
1046
 
1047
+ export type LinkOptionsFnOptions<
1048
+ TOptions,
1049
+ TComp,
1050
+ TRouter extends AnyRouter = RegisteredRouter,
1051
+ > =
1052
+ TOptions extends ReadonlyArray<any>
1053
+ ? ValidateLinkOptionsArray<TOptions, TComp, TRouter>
1054
+ : ValidateLinkOptions<TOptions, TComp, TRouter>
1055
+
1043
1056
  export type LinkOptionsFn<TComp> = <
1044
- const TProps,
1057
+ const TOptions,
1045
1058
  TRouter extends AnyRouter = RegisteredRouter,
1046
- TFrom extends string = string,
1047
- TTo extends string | undefined = undefined,
1048
- TMaskFrom extends string = TFrom,
1049
- TMaskTo extends string = '',
1050
1059
  >(
1051
- options: Constrain<
1052
- TProps,
1053
- LinkComponentProps<TComp, TRouter, TFrom, TTo, TMaskFrom, TMaskTo>
1054
- >,
1055
- ) => TProps
1060
+ options: LinkOptionsFnOptions<TOptions, TComp, TRouter>,
1061
+ ) => TOptions
1056
1062
 
1057
1063
  export const linkOptions: LinkOptionsFn<'a'> = (options) => {
1058
1064
  return options as any
package/src/router.ts CHANGED
@@ -1937,7 +1937,7 @@ export class Router<
1937
1937
 
1938
1938
  latestLoadPromise: undefined | Promise<void>
1939
1939
 
1940
- load = async (): Promise<void> => {
1940
+ load = async (opts?: { sync?: boolean }): Promise<void> => {
1941
1941
  this.latestLocation = this.parseLocation(this.latestLocation)
1942
1942
 
1943
1943
  let redirect: ResolvedRedirect | undefined
@@ -2000,6 +2000,7 @@ export class Router<
2000
2000
  })
2001
2001
 
2002
2002
  await this.loadMatches({
2003
+ sync: opts?.sync,
2003
2004
  matches: pendingMatches,
2004
2005
  location: next,
2005
2006
  // eslint-disable-next-line @typescript-eslint/require-await
@@ -2188,6 +2189,7 @@ export class Router<
2188
2189
  preload: allPreload,
2189
2190
  onReady,
2190
2191
  updateMatch = this.updateMatch,
2192
+ sync,
2191
2193
  }: {
2192
2194
  location: ParsedLocation
2193
2195
  matches: Array<AnyRouteMatch>
@@ -2198,6 +2200,7 @@ export class Router<
2198
2200
  updater: (match: AnyRouteMatch) => AnyRouteMatch,
2199
2201
  ) => void
2200
2202
  getMatch?: (matchId: string) => AnyRouteMatch | undefined
2203
+ sync?: boolean
2201
2204
  }): Promise<Array<MakeRouteMatch>> => {
2202
2205
  let firstBadMatchIndex: number | undefined
2203
2206
  let rendered = false
@@ -2484,7 +2487,8 @@ export class Router<
2484
2487
  const { loaderPromise: prevLoaderPromise } =
2485
2488
  this.getMatch(matchId)!
2486
2489
 
2487
- let loaderRunningAsync = false
2490
+ let loaderShouldRunAsync = false
2491
+ let loaderIsRunningAsync = false
2488
2492
 
2489
2493
  if (prevLoaderPromise) {
2490
2494
  await prevLoaderPromise
@@ -2665,36 +2669,50 @@ export class Router<
2665
2669
 
2666
2670
  // If the route is successful and still fresh, just resolve
2667
2671
  const { status, invalid } = this.getMatch(matchId)!
2668
- loaderRunningAsync =
2672
+ loaderShouldRunAsync =
2669
2673
  status === 'success' &&
2670
2674
  (invalid || (shouldReload ?? age > staleAge))
2671
2675
  if (preload && route.options.preload === false) {
2672
2676
  // Do nothing
2673
- } else if (loaderRunningAsync) {
2677
+ } else if (loaderShouldRunAsync && !sync) {
2678
+ loaderIsRunningAsync = true
2674
2679
  ;(async () => {
2675
2680
  try {
2676
2681
  await runLoader()
2682
+ const { loaderPromise, loadPromise } =
2683
+ this.getMatch(matchId)!
2684
+ loaderPromise?.resolve()
2685
+ loadPromise?.resolve()
2686
+ updateMatch(matchId, (prev) => ({
2687
+ ...prev,
2688
+ loaderPromise: undefined,
2689
+ }))
2677
2690
  } catch (err) {
2678
2691
  if (isResolvedRedirect(err)) {
2679
2692
  await this.navigate(err)
2680
2693
  }
2681
2694
  }
2682
2695
  })()
2683
- } else if (status !== 'success') {
2696
+ } else if (
2697
+ status !== 'success' ||
2698
+ (loaderShouldRunAsync && sync)
2699
+ ) {
2684
2700
  await runLoader()
2685
2701
  }
2686
-
2702
+ }
2703
+ if (!loaderIsRunningAsync) {
2687
2704
  const { loaderPromise, loadPromise } =
2688
2705
  this.getMatch(matchId)!
2689
-
2690
2706
  loaderPromise?.resolve()
2691
2707
  loadPromise?.resolve()
2692
2708
  }
2693
2709
 
2694
2710
  updateMatch(matchId, (prev) => ({
2695
2711
  ...prev,
2696
- isFetching: loaderRunningAsync ? prev.isFetching : false,
2697
- loaderPromise: undefined,
2712
+ isFetching: loaderIsRunningAsync ? prev.isFetching : false,
2713
+ loaderPromise: loaderIsRunningAsync
2714
+ ? prev.loaderPromise
2715
+ : undefined,
2698
2716
  invalid: false,
2699
2717
  }))
2700
2718
  return this.getMatch(matchId)!
@@ -2725,6 +2743,7 @@ export class Router<
2725
2743
 
2726
2744
  invalidate = <TRouter extends AnyRouter = typeof this>(opts?: {
2727
2745
  filter?: (d: MakeRouteMatchUnion<TRouter>) => boolean
2746
+ sync?: boolean
2728
2747
  }) => {
2729
2748
  const invalidate = (d: MakeRouteMatch<TRouteTree>) => {
2730
2749
  if (opts?.filter?.(d as MakeRouteMatchUnion<TRouter>) ?? true) {
@@ -2746,7 +2765,7 @@ export class Router<
2746
2765
  pendingMatches: s.pendingMatches?.map(invalidate),
2747
2766
  }))
2748
2767
 
2749
- return this.load()
2768
+ return this.load({ sync: opts?.sync })
2750
2769
  }
2751
2770
 
2752
2771
  resolveRedirect = (err: AnyRedirect): ResolvedRedirect => {
@@ -1,5 +1,6 @@
1
1
  import type {
2
2
  FromPathOption,
3
+ LinkComponentProps,
3
4
  NavigateOptions,
4
5
  PathParamOptions,
5
6
  SearchParamOptions,
@@ -59,7 +60,7 @@ export type InferMaskTo<TOptions> = TOptions extends {
59
60
  mask: { to: infer TTo extends string }
60
61
  }
61
62
  ? TTo
62
- : string
63
+ : ''
63
64
 
64
65
  export type InferMaskFrom<TOptions> = TOptions extends {
65
66
  mask: { from: infer TFrom extends string }
@@ -81,8 +82,34 @@ export type ValidateNavigateOptions<
81
82
  >
82
83
  >
83
84
 
84
- export type ValidateNavigateOptionsArray<TOptions extends ReadonlyArray<any>> =
85
- { [K in keyof TOptions]: ValidateNavigateOptions<TOptions[K]> }
85
+ export type ValidateNavigateOptionsArray<
86
+ TOptions extends ReadonlyArray<any>,
87
+ TRouter extends AnyRouter = RegisteredRouter,
88
+ > = { [K in keyof TOptions]: ValidateNavigateOptions<TOptions[K], TRouter> }
89
+
90
+ export type ValidateLinkOptions<
91
+ TOptions,
92
+ TComp = 'a',
93
+ TRouter extends AnyRouter = RegisteredRouter,
94
+ > = Constrain<
95
+ TOptions,
96
+ LinkComponentProps<
97
+ TComp,
98
+ TRouter,
99
+ InferFrom<TOptions>,
100
+ InferTo<TOptions>,
101
+ InferMaskFrom<TOptions>,
102
+ InferMaskTo<TOptions>
103
+ >
104
+ >
105
+
106
+ export type ValidateLinkOptionsArray<
107
+ TOptions extends ReadonlyArray<any>,
108
+ TComp = 'a',
109
+ TRouter extends AnyRouter = RegisteredRouter,
110
+ > = {
111
+ [K in keyof TOptions]: ValidateLinkOptions<TOptions[K], TComp, TRouter>
112
+ }
86
113
 
87
114
  export type ValidateId<
88
115
  TId extends string,