@tanstack/react-router 1.4.7 → 1.4.9

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tanstack/react-router",
3
3
  "author": "Tanner Linsley",
4
- "version": "1.4.7",
4
+ "version": "1.4.9",
5
5
  "license": "MIT",
6
6
  "repository": "tanstack/router",
7
7
  "homepage": "https://tanstack.com/router",
@@ -44,7 +44,7 @@
44
44
  "@tanstack/store": "^0.1.3",
45
45
  "tiny-invariant": "^1.3.1",
46
46
  "tiny-warning": "^1.0.3",
47
- "@tanstack/history": "1.4.7"
47
+ "@tanstack/history": "1.4.9"
48
48
  },
49
49
  "scripts": {
50
50
  "build": "rollup --config rollup.config.js"
package/src/Matches.tsx CHANGED
@@ -4,9 +4,8 @@ import warning from 'tiny-warning'
4
4
  import { CatchBoundary, ErrorComponent } from './CatchBoundary'
5
5
  import { useRouter, useRouterState } from './RouterProvider'
6
6
  import { ResolveRelativePath, ToOptions } from './link'
7
- import { AnyRoute, ReactNode, rootRouteId } from './route'
7
+ import { AnyRoute, ReactNode } from './route'
8
8
  import {
9
- FullSearchSchema,
10
9
  ParseRoute,
11
10
  RouteById,
12
11
  RouteByPath,
@@ -14,7 +13,7 @@ import {
14
13
  RoutePaths,
15
14
  } from './routeInfo'
16
15
  import { RegisteredRouter, RouterState } from './router'
17
- import { NoInfer, StrictOrFrom, pick } from './utils'
16
+ import { GetTFrom, NoInfer, StrictOrFrom, pick } from './utils'
18
17
 
19
18
  export const matchContext = React.createContext<string | undefined>(undefined)
20
19
 
@@ -37,8 +36,7 @@ export interface RouteMatch<
37
36
  loaderData?: RouteById<TRouteTree, TRouteId>['types']['loaderData']
38
37
  routeContext: RouteById<TRouteTree, TRouteId>['types']['routeContext']
39
38
  context: RouteById<TRouteTree, TRouteId>['types']['allContext']
40
- search: FullSearchSchema<TRouteTree> &
41
- RouteById<TRouteTree, TRouteId>['types']['fullSearchSchema']
39
+ search: RouteById<TRouteTree, TRouteId>['types']['fullSearchSchema']
42
40
  fetchCount: number
43
41
  abortController: AbortController
44
42
  cause: 'preload' | 'enter' | 'stay'
@@ -293,16 +291,17 @@ export function getRenderedMatches(state: RouterState) {
293
291
  }
294
292
 
295
293
  export function useMatch<
294
+ TOpts extends StrictOrFrom<TFrom>,
296
295
  TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
297
296
  TFrom extends RouteIds<TRouteTree> = RouteIds<TRouteTree>,
298
- TStrict extends boolean = true,
299
- TRouteMatchState = RouteMatch<TRouteTree, TFrom>,
297
+ TFromInferred extends RouteIds<TRouteTree> = GetTFrom<TOpts, TRouteTree>,
298
+ TRouteMatchState = RouteMatch<TRouteTree, TFromInferred>,
300
299
  TSelected = TRouteMatchState,
301
300
  >(
302
- opts: StrictOrFrom<TFrom> & {
301
+ opts: TOpts & {
303
302
  select?: (match: TRouteMatchState) => TSelected
304
303
  },
305
- ): TStrict extends true ? TSelected : TSelected | undefined {
304
+ ): TSelected {
306
305
  const router = useRouter()
307
306
  const nearestMatchId = React.useContext(matchContext)
308
307
 
@@ -378,19 +377,20 @@ export function useParentMatches<T = RouteMatch[]>(opts?: {
378
377
  }
379
378
 
380
379
  export function useLoaderDeps<
380
+ TOpts extends StrictOrFrom<TFrom>,
381
381
  TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
382
382
  TFrom extends RouteIds<TRouteTree> = RouteIds<TRouteTree>,
383
- TStrict extends boolean = true,
384
- TRouteMatch extends RouteMatch<TRouteTree, TFrom> = RouteMatch<
383
+ TFromInferred extends RouteIds<TRouteTree> = GetTFrom<TOpts, TRouteTree>,
384
+ TRouteMatch extends RouteMatch<TRouteTree, TFromInferred> = RouteMatch<
385
385
  TRouteTree,
386
- TFrom
386
+ TFromInferred
387
387
  >,
388
388
  TSelected = Required<TRouteMatch>['loaderDeps'],
389
389
  >(
390
- opts: StrictOrFrom<TFrom> & {
390
+ opts: TOpts & {
391
391
  select?: (match: TRouteMatch) => TSelected
392
392
  },
393
- ): TStrict extends true ? TSelected : TSelected | undefined {
393
+ ): TSelected {
394
394
  return useMatch({
395
395
  ...opts,
396
396
  select: (s) => {
@@ -398,23 +398,24 @@ export function useLoaderDeps<
398
398
  ? opts.select(s?.loaderDeps)
399
399
  : s?.loaderDeps
400
400
  },
401
- })!
401
+ })
402
402
  }
403
403
 
404
404
  export function useLoaderData<
405
+ TOpts extends StrictOrFrom<TFrom>,
405
406
  TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
406
407
  TFrom extends RouteIds<TRouteTree> = RouteIds<TRouteTree>,
407
- TStrict extends boolean = true,
408
- TRouteMatch extends RouteMatch<TRouteTree, TFrom> = RouteMatch<
408
+ TFromInferred extends RouteIds<TRouteTree> = GetTFrom<TOpts, TRouteTree>,
409
+ TRouteMatch extends RouteMatch<TRouteTree, TFromInferred> = RouteMatch<
409
410
  TRouteTree,
410
- TFrom
411
+ TFromInferred
411
412
  >,
412
413
  TSelected = Required<TRouteMatch>['loaderData'],
413
414
  >(
414
- opts: StrictOrFrom<TFrom> & {
415
+ opts: TOpts & {
415
416
  select?: (match: TRouteMatch) => TSelected
416
417
  },
417
- ): TStrict extends true ? TSelected : TSelected | undefined {
418
+ ): TSelected {
418
419
  return useMatch({
419
420
  ...opts,
420
421
  select: (s) => {
@@ -422,5 +423,5 @@ export function useLoaderData<
422
423
  ? opts.select(s?.loaderData)
423
424
  : s?.loaderData
424
425
  },
425
- })!
426
+ })
426
427
  }
package/src/link.tsx CHANGED
@@ -15,6 +15,7 @@ import {
15
15
  NoInfer,
16
16
  NonNullableUpdater,
17
17
  PickRequired,
18
+ StringLiteral,
18
19
  Updater,
19
20
  WithoutEmpty,
20
21
  deepEqual,
@@ -114,7 +115,7 @@ export type RelativeToPathAutoComplete<
114
115
 
115
116
  export type NavigateOptions<
116
117
  TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
117
- TFrom extends RoutePaths<TRouteTree> | string = string,
118
+ TFrom extends RoutePaths<TRouteTree> | string = RoutePaths<TRouteTree>,
118
119
  TTo extends string = '',
119
120
  TMaskFrom extends RoutePaths<TRouteTree> | string = TFrom,
120
121
  TMaskTo extends string = '',
@@ -128,7 +129,7 @@ export type NavigateOptions<
128
129
 
129
130
  export type ToOptions<
130
131
  TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
131
- TFrom extends RoutePaths<TRouteTree> | string = string,
132
+ TFrom extends RoutePaths<TRouteTree> | string = RoutePaths<TRouteTree>,
132
133
  TTo extends string = '',
133
134
  TMaskFrom extends RoutePaths<TRouteTree> | string = TFrom,
134
135
  TMaskTo extends string = '',
@@ -138,7 +139,7 @@ export type ToOptions<
138
139
 
139
140
  export type ToMaskOptions<
140
141
  TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
141
- TMaskFrom extends RoutePaths<TRouteTree> | string = string,
142
+ TMaskFrom extends RoutePaths<TRouteTree> | string = RoutePaths<TRouteTree>,
142
143
  TMaskTo extends string = '',
143
144
  > = ToSubOptions<TRouteTree, TMaskFrom, TMaskTo> & {
144
145
  unmaskOnReload?: boolean
@@ -146,7 +147,7 @@ export type ToMaskOptions<
146
147
 
147
148
  export type ToSubOptions<
148
149
  TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
149
- TFrom extends RoutePaths<TRouteTree> | string = string,
150
+ TFrom extends RoutePaths<TRouteTree> | string = RoutePaths<TRouteTree>,
150
151
  TTo extends string = '',
151
152
  TResolved = ResolveRelativePath<TFrom, NoInfer<TTo>>,
152
153
  > = {
@@ -156,7 +157,7 @@ export type ToSubOptions<
156
157
  // State to pass to the history stack
157
158
  state?: true | NonNullableUpdater<HistoryState>
158
159
  // The source route path. This is automatically set when using route-level APIs, but for type-safe relative routing on the router itself, this is required
159
- from?: TFrom
160
+ from?: StringLiteral<TFrom>
160
161
  // // When using relative route paths, this option forces resolution from the current path, instead of the route API's path or `from` path
161
162
  } & CheckPath<TRouteTree, NoInfer<TResolved>, {}> &
162
163
  SearchParamOptions<TRouteTree, TFrom, TTo, TResolved> &
package/src/route.ts CHANGED
@@ -485,41 +485,40 @@ export class RouteApi<
485
485
  useMatch = <TSelected = TAllContext>(opts?: {
486
486
  select?: (s: TAllContext) => TSelected
487
487
  }): TSelected => {
488
- return useMatch({ ...opts, from: this.id }) as any
488
+ return useMatch({ select: opts?.select, from: this.id })
489
489
  }
490
490
 
491
491
  useRouteContext = <TSelected = TAllContext>(opts?: {
492
492
  select?: (s: TAllContext) => TSelected
493
493
  }): TSelected => {
494
494
  return useMatch({
495
- ...opts,
496
495
  from: this.id,
497
496
  select: (d: any) => (opts?.select ? opts.select(d.context) : d.context),
498
- } as any)
497
+ })
499
498
  }
500
499
 
501
500
  useSearch = <TSelected = TFullSearchSchema>(opts?: {
502
501
  select?: (s: TFullSearchSchema) => TSelected
503
502
  }): TSelected => {
504
- return useSearch({ ...opts, from: this.id } as any)
503
+ return useSearch({ ...opts, from: this.id })
505
504
  }
506
505
 
507
506
  useParams = <TSelected = TAllParams>(opts?: {
508
507
  select?: (s: TAllParams) => TSelected
509
508
  }): TSelected => {
510
- return useParams({ ...opts, from: this.id } as any)
509
+ return useParams({ ...opts, from: this.id })
511
510
  }
512
511
 
513
512
  useLoaderDeps = <TSelected = TLoaderDeps>(opts?: {
514
513
  select?: (s: TLoaderDeps) => TSelected
515
514
  }): TSelected => {
516
- return useLoaderDeps({ ...opts, from: this.id } as any) as any
515
+ return useLoaderDeps({ ...opts, from: this.id } as any)
517
516
  }
518
517
 
519
518
  useLoaderData = <TSelected = TLoaderData>(opts?: {
520
519
  select?: (s: TLoaderData) => TSelected
521
520
  }): TSelected => {
522
- return useLoaderData({ ...opts, from: this.id } as any) as any
521
+ return useLoaderData({ ...opts, from: this.id } as any)
523
522
  }
524
523
  }
525
524
 
@@ -810,7 +809,7 @@ export class Route<
810
809
  useMatch = <TSelected = TAllContext>(opts?: {
811
810
  select?: (search: TAllContext) => TSelected
812
811
  }): TSelected => {
813
- return useMatch({ ...opts, from: this.id }) as any
812
+ return useMatch({ ...opts, from: this.id })
814
813
  }
815
814
 
816
815
  useRouteContext = <TSelected = TAllContext>(opts?: {
@@ -820,31 +819,31 @@ export class Route<
820
819
  ...opts,
821
820
  from: this.id,
822
821
  select: (d: any) => (opts?.select ? opts.select(d.context) : d.context),
823
- } as any)
822
+ })
824
823
  }
825
824
 
826
825
  useSearch = <TSelected = TFullSearchSchema>(opts?: {
827
826
  select?: (search: TFullSearchSchema) => TSelected
828
827
  }): TSelected => {
829
- return useSearch({ ...opts, from: this.id } as any)
828
+ return useSearch({ ...opts, from: this.id })
830
829
  }
831
830
 
832
831
  useParams = <TSelected = TAllParams>(opts?: {
833
832
  select?: (search: TAllParams) => TSelected
834
833
  }): TSelected => {
835
- return useParams({ ...opts, from: this.id } as any)
834
+ return useParams({ ...opts, from: this.id })
836
835
  }
837
836
 
838
837
  useLoaderDeps = <TSelected = TLoaderDeps>(opts?: {
839
838
  select?: (s: TLoaderDeps) => TSelected
840
839
  }): TSelected => {
841
- return useLoaderDeps({ ...opts, from: this.id } as any) as any
840
+ return useLoaderDeps({ ...opts, from: this.id } as any)
842
841
  }
843
842
 
844
843
  useLoaderData = <TSelected = TLoaderData>(opts?: {
845
844
  select?: (search: TLoaderData) => TSelected
846
845
  }): TSelected => {
847
- return useLoaderData({ ...opts, from: this.id } as any) as any
846
+ return useLoaderData({ ...opts, from: this.id } as any)
848
847
  }
849
848
  }
850
849
 
package/src/router.ts CHANGED
@@ -1060,9 +1060,9 @@ export class Router<
1060
1060
 
1061
1061
  const pendingMs =
1062
1062
  route.options.pendingMs ?? this.options.defaultPendingMs
1063
- const pendingPromise = new Promise<void>((r) =>
1064
- setTimeout(r, pendingMs),
1065
- )
1063
+ const pendingPromise = typeof pendingMs === 'number' && pendingMs <= 0
1064
+ ? Promise.resolve()
1065
+ : new Promise<void>((r) => setTimeout(r, pendingMs))
1066
1066
 
1067
1067
  const beforeLoadContext =
1068
1068
  (await route.options.beforeLoad?.({
@@ -1145,7 +1145,7 @@ export class Router<
1145
1145
  route.options.pendingMinMs ?? this.options.defaultPendingMinMs
1146
1146
  const shouldPending =
1147
1147
  !preload &&
1148
- pendingMs &&
1148
+ typeof pendingMs === 'number' &&
1149
1149
  (route.options.pendingComponent ??
1150
1150
  this.options.defaultPendingComponent)
1151
1151
 
@@ -5,12 +5,13 @@ import { LinkOptions, NavigateOptions } from './link'
5
5
  import { AnyRoute } from './route'
6
6
  import { RoutePaths } from './routeInfo'
7
7
  import { RegisteredRouter } from './router'
8
+ import { StringLiteral } from './utils'
8
9
 
9
10
  export function useNavigate<
10
11
  TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
11
- TDefaultFrom extends RoutePaths<TRouteTree> | string = string,
12
- >(_defaultOpts?: { from?: TDefaultFrom }) {
13
- const { navigate, buildLocation } = useRouter()
12
+ TDefaultFrom extends RoutePaths<TRouteTree> | string = RoutePaths<TRouteTree>,
13
+ >(_defaultOpts?: { from?: StringLiteral<TDefaultFrom> }) {
14
+ const { navigate } = useRouter()
14
15
 
15
16
  const matchPathname = useMatch({
16
17
  strict: false,
package/src/useParams.tsx CHANGED
@@ -3,18 +3,19 @@ import { RouteIds, RouteById, AllParams } from './routeInfo'
3
3
  import { RegisteredRouter } from './router'
4
4
  import { last } from './utils'
5
5
  import { useRouterState } from './RouterProvider'
6
- import { StrictOrFrom } from './utils'
6
+ import { StrictOrFrom, GetTFrom } from './utils'
7
7
  import { getRenderedMatches } from './Matches'
8
8
 
9
9
  export function useParams<
10
+ TOpts extends StrictOrFrom<TFrom>,
10
11
  TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
11
12
  TFrom extends RouteIds<TRouteTree> = RouteIds<TRouteTree>,
12
- TDefaultSelected = AllParams<TRouteTree> &
13
- RouteById<TRouteTree, TFrom>['types']['allParams'],
14
- TSelected = TDefaultSelected,
13
+ TFromInferred = GetTFrom<TOpts, TRouteTree>,
14
+ TParams = RouteById<TRouteTree, TFromInferred>['types']['allParams'],
15
+ TSelected = TParams,
15
16
  >(
16
- opts: StrictOrFrom<TFrom> & {
17
- select?: (search: TDefaultSelected) => TSelected
17
+ opts: TOpts & {
18
+ select?: (params: TParams) => TSelected
18
19
  },
19
20
  ): TSelected {
20
21
  return useRouterState({
package/src/useSearch.tsx CHANGED
@@ -3,21 +3,22 @@ import { RouteIds, RouteById } from './routeInfo'
3
3
  import { RegisteredRouter } from './router'
4
4
  import { RouteMatch } from './Matches'
5
5
  import { useMatch } from './Matches'
6
- import { StrictOrFrom } from './utils'
6
+ import { StrictOrFrom, GetTFrom } from './utils'
7
7
 
8
8
  export function useSearch<
9
+ TOpts extends StrictOrFrom<TFrom>,
9
10
  TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
10
11
  TFrom extends RouteIds<TRouteTree> = RouteIds<TRouteTree>,
11
- TStrict extends boolean = true,
12
- TSearch = RouteById<TRouteTree, TFrom>['types']['fullSearchSchema'],
12
+ TFromInferred = GetTFrom<TOpts, TRouteTree>,
13
+ TSearch = RouteById<TRouteTree, TFromInferred>['types']['fullSearchSchema'],
13
14
  TSelected = TSearch,
14
15
  >(
15
- opts: StrictOrFrom<TFrom> & {
16
+ opts: TOpts & {
16
17
  select?: (search: TSearch) => TSelected
17
18
  },
18
- ): TStrict extends true ? TSelected : TSelected | undefined {
19
+ ) : TSelected {
19
20
  return useMatch({
20
- ...(opts as any),
21
+ ...opts,
21
22
  select: (match: RouteMatch) => {
22
23
  return opts?.select ? opts.select(match.search as TSearch) : match.search
23
24
  },
package/src/utils.ts CHANGED
@@ -156,7 +156,8 @@ export function replaceEqualDeep<T>(prev: any, _next: T): T {
156
156
  const array = isPlainArray(prev) && isPlainArray(next)
157
157
 
158
158
  if (array || (isPlainObject(prev) && isPlainObject(next))) {
159
- const prevSize = array ? prev.length : Object.keys(prev).length
159
+ const prevItems = array ? prev : Object.keys(prev)
160
+ const prevSize = prevItems.length
160
161
  const nextItems = array ? next : Object.keys(next)
161
162
  const nextSize = nextItems.length
162
163
  const copy: any = array ? [] : {}
@@ -165,9 +166,19 @@ export function replaceEqualDeep<T>(prev: any, _next: T): T {
165
166
 
166
167
  for (let i = 0; i < nextSize; i++) {
167
168
  const key = array ? i : nextItems[i]
168
- copy[key] = replaceEqualDeep(prev[key], next[key])
169
- if (copy[key] === prev[key] && prev[key] !== undefined) {
169
+ if (
170
+ !array &&
171
+ prev[key] === undefined &&
172
+ next[key] === undefined &&
173
+ prevItems.includes(key)
174
+ ) {
175
+ copy[key] = undefined
170
176
  equalItems++
177
+ } else {
178
+ copy[key] = replaceEqualDeep(prev[key], next[key])
179
+ if (copy[key] === prev[key] && prev[key] !== undefined) {
180
+ equalItems++
181
+ }
171
182
  }
172
183
  }
173
184
 
@@ -279,9 +290,15 @@ export function shallow<T>(objA: T, objB: T) {
279
290
  return true
280
291
  }
281
292
 
293
+ export type StringLiteral<T> = T extends string
294
+ ? string extends T
295
+ ? string
296
+ : T
297
+ : never
298
+
282
299
  export type StrictOrFrom<TFrom> =
283
300
  | {
284
- from: TFrom
301
+ from: StringLiteral<TFrom> | TFrom
285
302
  strict?: true
286
303
  }
287
304
  | {
@@ -289,17 +306,22 @@ export type StrictOrFrom<TFrom> =
289
306
  strict: false
290
307
  }
291
308
 
309
+ export type GetTFrom<T, TRouteTree extends AnyRoute> = T extends StrictOrFrom<
310
+ infer TFrom extends RouteIds<TRouteTree>
311
+ >
312
+ ? TFrom
313
+ : never
314
+
292
315
  export function useRouteContext<
293
316
  TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
294
317
  TFrom extends RouteIds<TRouteTree> = RouteIds<TRouteTree>,
295
- TStrict extends boolean = true,
296
318
  TRouteContext = RouteById<TRouteTree, TFrom>['types']['allContext'],
297
319
  TSelected = TRouteContext,
298
320
  >(
299
321
  opts: StrictOrFrom<TFrom> & {
300
322
  select?: (search: TRouteContext) => TSelected
301
323
  },
302
- ): TStrict extends true ? TSelected : TSelected | undefined {
324
+ ): TSelected {
303
325
  return useMatch({
304
326
  ...(opts as any),
305
327
  select: (match: RouteMatch) =>