@tanstack/react-router 1.28.1 → 1.28.3
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/dist/cjs/Matches.cjs +45 -23
- package/dist/cjs/Matches.cjs.map +1 -1
- package/dist/cjs/Matches.d.cts +5 -6
- package/dist/cjs/RouterProvider.cjs +2 -2
- package/dist/cjs/RouterProvider.cjs.map +1 -1
- package/dist/cjs/fileRoute.d.cts +4 -4
- package/dist/cjs/index.cjs +0 -1
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +1 -1
- package/dist/cjs/link.cjs.map +1 -1
- package/dist/cjs/link.d.cts +2 -0
- package/dist/cjs/redirects.cjs.map +1 -1
- package/dist/cjs/redirects.d.cts +3 -1
- package/dist/cjs/route.cjs.map +1 -1
- package/dist/cjs/route.d.cts +2 -2
- package/dist/cjs/router.cjs +374 -327
- package/dist/cjs/router.cjs.map +1 -1
- package/dist/cjs/router.d.cts +2 -2
- package/dist/cjs/utils.cjs +20 -2
- package/dist/cjs/utils.cjs.map +1 -1
- package/dist/cjs/utils.d.cts +6 -1
- package/dist/esm/Matches.d.ts +5 -6
- package/dist/esm/Matches.js +46 -24
- package/dist/esm/Matches.js.map +1 -1
- package/dist/esm/RouterProvider.js +2 -2
- package/dist/esm/RouterProvider.js.map +1 -1
- package/dist/esm/fileRoute.d.ts +4 -4
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/index.js +1 -2
- package/dist/esm/link.d.ts +2 -0
- package/dist/esm/link.js.map +1 -1
- package/dist/esm/redirects.d.ts +3 -1
- package/dist/esm/redirects.js.map +1 -1
- package/dist/esm/route.d.ts +2 -2
- package/dist/esm/route.js.map +1 -1
- package/dist/esm/router.d.ts +2 -2
- package/dist/esm/router.js +375 -328
- package/dist/esm/router.js.map +1 -1
- package/dist/esm/utils.d.ts +6 -1
- package/dist/esm/utils.js +20 -2
- package/dist/esm/utils.js.map +1 -1
- package/package.json +4 -2
- package/src/Matches.tsx +73 -35
- package/src/RouterProvider.tsx +2 -0
- package/src/index.tsx +0 -1
- package/src/link.tsx +2 -0
- package/src/redirects.ts +4 -2
- package/src/route.ts +2 -7
- package/src/router.ts +498 -426
- package/src/utils.ts +31 -2
package/src/Matches.tsx
CHANGED
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
import * as React from 'react'
|
|
2
2
|
import invariant from 'tiny-invariant'
|
|
3
3
|
import warning from 'tiny-warning'
|
|
4
|
+
import { set } from 'zod'
|
|
4
5
|
import { CatchBoundary, ErrorComponent } from './CatchBoundary'
|
|
5
6
|
import { useRouterState } from './useRouterState'
|
|
6
7
|
import { useRouter } from './useRouter'
|
|
7
|
-
import {
|
|
8
|
+
import { createControlledPromise, pick } from './utils'
|
|
8
9
|
import { CatchNotFound, DefaultGlobalNotFound, isNotFound } from './not-found'
|
|
9
10
|
import { isRedirect } from './redirects'
|
|
11
|
+
import {
|
|
12
|
+
type AnyRouter,
|
|
13
|
+
type RegisteredRouter,
|
|
14
|
+
type RouterState,
|
|
15
|
+
} from './router'
|
|
10
16
|
import type { ResolveRelativePath, ToOptions } from './link'
|
|
11
17
|
import type {
|
|
12
18
|
AnyRoute,
|
|
@@ -23,8 +29,13 @@ import type {
|
|
|
23
29
|
RouteIds,
|
|
24
30
|
RoutePaths,
|
|
25
31
|
} from './routeInfo'
|
|
26
|
-
import type {
|
|
27
|
-
|
|
32
|
+
import type {
|
|
33
|
+
ControlledPromise,
|
|
34
|
+
DeepPartial,
|
|
35
|
+
Expand,
|
|
36
|
+
NoInfer,
|
|
37
|
+
StrictOrFrom,
|
|
38
|
+
} from './utils'
|
|
28
39
|
|
|
29
40
|
export const matchContext = React.createContext<string | undefined>(undefined)
|
|
30
41
|
|
|
@@ -41,12 +52,12 @@ export interface RouteMatch<
|
|
|
41
52
|
: Expand<Partial<AllParams<TRouteTree>>>
|
|
42
53
|
status: 'pending' | 'success' | 'error' | 'redirected' | 'notFound'
|
|
43
54
|
isFetching: boolean
|
|
44
|
-
showPending: boolean
|
|
45
55
|
error: unknown
|
|
46
56
|
paramsError: unknown
|
|
47
57
|
searchError: unknown
|
|
48
58
|
updatedAt: number
|
|
49
|
-
loadPromise
|
|
59
|
+
loadPromise: ControlledPromise<void>
|
|
60
|
+
loaderPromise: Promise<RouteById<TRouteTree, TRouteId>['types']['loaderData']>
|
|
50
61
|
loaderData?: RouteById<TRouteTree, TRouteId>['types']['loaderData']
|
|
51
62
|
routeContext: RouteById<TRouteTree, TRouteId>['types']['routeContext']
|
|
52
63
|
context: RouteById<TRouteTree, TRouteId>['types']['allContext']
|
|
@@ -64,22 +75,21 @@ export interface RouteMatch<
|
|
|
64
75
|
loaderDeps: RouteById<TRouteTree, TRouteId>['types']['loaderDeps']
|
|
65
76
|
preload: boolean
|
|
66
77
|
invalid: boolean
|
|
67
|
-
pendingPromise?: Promise<void>
|
|
68
78
|
meta?: Array<JSX.IntrinsicElements['meta']>
|
|
69
79
|
links?: Array<JSX.IntrinsicElements['link']>
|
|
70
80
|
scripts?: Array<JSX.IntrinsicElements['script']>
|
|
71
81
|
headers?: Record<string, string>
|
|
72
82
|
globalNotFound?: boolean
|
|
73
83
|
staticData: StaticDataRouteOption
|
|
84
|
+
minPendingPromise?: ControlledPromise<void>
|
|
74
85
|
}
|
|
75
86
|
|
|
76
87
|
export type AnyRouteMatch = RouteMatch<any, any>
|
|
77
88
|
|
|
78
89
|
export function Matches() {
|
|
79
|
-
const router = useRouter()
|
|
80
90
|
const matchId = useRouterState({
|
|
81
91
|
select: (s) => {
|
|
82
|
-
return
|
|
92
|
+
return s.matches[0]?.id
|
|
83
93
|
},
|
|
84
94
|
})
|
|
85
95
|
|
|
@@ -113,8 +123,7 @@ function SafeFragment(props: any) {
|
|
|
113
123
|
export function Match({ matchId }: { matchId: string }) {
|
|
114
124
|
const router = useRouter()
|
|
115
125
|
const routeId = useRouterState({
|
|
116
|
-
select: (s) =>
|
|
117
|
-
getRenderedMatches(s).find((d) => d.id === matchId)?.routeId as string,
|
|
126
|
+
select: (s) => s.matches.find((d) => d.id === matchId)?.routeId as string,
|
|
118
127
|
})
|
|
119
128
|
|
|
120
129
|
invariant(
|
|
@@ -186,7 +195,7 @@ export function Match({ matchId }: { matchId: string }) {
|
|
|
186
195
|
return React.createElement(routeNotFoundComponent, error as any)
|
|
187
196
|
}}
|
|
188
197
|
>
|
|
189
|
-
<MatchInner matchId={matchId}
|
|
198
|
+
<MatchInner matchId={matchId} />
|
|
190
199
|
</ResolvedNotFoundBoundary>
|
|
191
200
|
</ResolvedCatchBoundary>
|
|
192
201
|
</ResolvedSuspenseBoundary>
|
|
@@ -196,26 +205,26 @@ export function Match({ matchId }: { matchId: string }) {
|
|
|
196
205
|
|
|
197
206
|
function MatchInner({
|
|
198
207
|
matchId,
|
|
199
|
-
pendingElement,
|
|
208
|
+
// pendingElement,
|
|
200
209
|
}: {
|
|
201
210
|
matchId: string
|
|
202
|
-
pendingElement: any
|
|
211
|
+
// pendingElement: any
|
|
203
212
|
}): any {
|
|
204
213
|
const router = useRouter()
|
|
205
214
|
const routeId = useRouterState({
|
|
206
|
-
select: (s) =>
|
|
207
|
-
getRenderedMatches(s).find((d) => d.id === matchId)?.routeId as string,
|
|
215
|
+
select: (s) => s.matches.find((d) => d.id === matchId)?.routeId as string,
|
|
208
216
|
})
|
|
209
217
|
|
|
210
218
|
const route = router.routesById[routeId]!
|
|
211
219
|
|
|
212
220
|
const match = useRouterState({
|
|
213
221
|
select: (s) =>
|
|
214
|
-
pick(
|
|
222
|
+
pick(s.matches.find((d) => d.id === matchId)!, [
|
|
223
|
+
'id',
|
|
215
224
|
'status',
|
|
216
225
|
'error',
|
|
217
|
-
'showPending',
|
|
218
226
|
'loadPromise',
|
|
227
|
+
'minPendingPromise',
|
|
219
228
|
]),
|
|
220
229
|
})
|
|
221
230
|
|
|
@@ -258,7 +267,7 @@ function MatchInner({
|
|
|
258
267
|
// of a suspense boundary. This is the only way to get
|
|
259
268
|
// renderToPipeableStream to not hang indefinitely.
|
|
260
269
|
// We'll serialize the error and rethrow it on the client.
|
|
261
|
-
if (isServer) {
|
|
270
|
+
if (router.isServer) {
|
|
262
271
|
return (
|
|
263
272
|
<RouteErrorComponent
|
|
264
273
|
error={match.error}
|
|
@@ -279,9 +288,47 @@ function MatchInner({
|
|
|
279
288
|
}
|
|
280
289
|
|
|
281
290
|
if (match.status === 'pending') {
|
|
282
|
-
if
|
|
283
|
-
|
|
291
|
+
// We're pending, and if we have a minPendingMs, we need to wait for it
|
|
292
|
+
const pendingMinMs =
|
|
293
|
+
route.options.pendingMinMs ?? router.options.defaultPendingMinMs
|
|
294
|
+
|
|
295
|
+
if (pendingMinMs && !match.minPendingPromise) {
|
|
296
|
+
// Create a promise that will resolve after the minPendingMs
|
|
297
|
+
|
|
298
|
+
match.minPendingPromise = createControlledPromise()
|
|
299
|
+
|
|
300
|
+
if (!router.isServer) {
|
|
301
|
+
Promise.resolve().then(() => {
|
|
302
|
+
router.__store.setState((s) => ({
|
|
303
|
+
...s,
|
|
304
|
+
matches: s.matches.map((d) =>
|
|
305
|
+
d.id === match.id
|
|
306
|
+
? { ...d, minPendingPromise: createControlledPromise() }
|
|
307
|
+
: d,
|
|
308
|
+
),
|
|
309
|
+
}))
|
|
310
|
+
})
|
|
311
|
+
|
|
312
|
+
setTimeout(() => {
|
|
313
|
+
// We've handled the minPendingPromise, so we can delete it
|
|
314
|
+
router.__store.setState((s) => {
|
|
315
|
+
return {
|
|
316
|
+
...s,
|
|
317
|
+
matches: s.matches.map((d) =>
|
|
318
|
+
d.id === match.id
|
|
319
|
+
? {
|
|
320
|
+
...d,
|
|
321
|
+
minPendingPromise:
|
|
322
|
+
(d.minPendingPromise?.resolve(), undefined),
|
|
323
|
+
}
|
|
324
|
+
: d,
|
|
325
|
+
),
|
|
326
|
+
}
|
|
327
|
+
})
|
|
328
|
+
}, pendingMinMs)
|
|
329
|
+
}
|
|
284
330
|
}
|
|
331
|
+
|
|
285
332
|
throw match.loadPromise
|
|
286
333
|
}
|
|
287
334
|
|
|
@@ -306,15 +353,14 @@ export const Outlet = React.memo(function Outlet() {
|
|
|
306
353
|
const router = useRouter()
|
|
307
354
|
const matchId = React.useContext(matchContext)
|
|
308
355
|
const routeId = useRouterState({
|
|
309
|
-
select: (s) =>
|
|
310
|
-
getRenderedMatches(s).find((d) => d.id === matchId)?.routeId as string,
|
|
356
|
+
select: (s) => s.matches.find((d) => d.id === matchId)?.routeId as string,
|
|
311
357
|
})
|
|
312
358
|
|
|
313
359
|
const route = router.routesById[routeId]!
|
|
314
360
|
|
|
315
361
|
const { parentGlobalNotFound } = useRouterState({
|
|
316
362
|
select: (s) => {
|
|
317
|
-
const matches =
|
|
363
|
+
const matches = s.matches
|
|
318
364
|
const parentMatch = matches.find((d) => d.id === matchId)
|
|
319
365
|
invariant(
|
|
320
366
|
parentMatch,
|
|
@@ -328,7 +374,7 @@ export const Outlet = React.memo(function Outlet() {
|
|
|
328
374
|
|
|
329
375
|
const childMatchId = useRouterState({
|
|
330
376
|
select: (s) => {
|
|
331
|
-
const matches =
|
|
377
|
+
const matches = s.matches
|
|
332
378
|
const index = matches.findIndex((d) => d.id === matchId)
|
|
333
379
|
return matches[index + 1]?.id
|
|
334
380
|
},
|
|
@@ -453,14 +499,6 @@ export function MatchRoute<
|
|
|
453
499
|
return params ? props.children : null
|
|
454
500
|
}
|
|
455
501
|
|
|
456
|
-
export function getRenderedMatches<
|
|
457
|
-
TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
|
|
458
|
-
>(state: RouterState<TRouteTree>) {
|
|
459
|
-
return state.pendingMatches?.some((d) => d.showPending)
|
|
460
|
-
? state.pendingMatches
|
|
461
|
-
: state.matches
|
|
462
|
-
}
|
|
463
|
-
|
|
464
502
|
export function useMatch<
|
|
465
503
|
TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
|
|
466
504
|
TFrom extends RouteIds<TRouteTree> = RouteIds<TRouteTree>,
|
|
@@ -476,7 +514,7 @@ export function useMatch<
|
|
|
476
514
|
|
|
477
515
|
const matchSelection = useRouterState({
|
|
478
516
|
select: (state) => {
|
|
479
|
-
const match =
|
|
517
|
+
const match = state.matches.find((d) =>
|
|
480
518
|
opts.from ? opts.from === d.routeId : d.id === nearestMatchId,
|
|
481
519
|
)
|
|
482
520
|
|
|
@@ -491,7 +529,7 @@ export function useMatch<
|
|
|
491
529
|
},
|
|
492
530
|
})
|
|
493
531
|
|
|
494
|
-
return matchSelection as
|
|
532
|
+
return matchSelection as TSelected
|
|
495
533
|
}
|
|
496
534
|
|
|
497
535
|
export function useMatches<
|
|
@@ -506,7 +544,7 @@ export function useMatches<
|
|
|
506
544
|
}): T {
|
|
507
545
|
return useRouterState({
|
|
508
546
|
select: (state) => {
|
|
509
|
-
const matches =
|
|
547
|
+
const matches = state.matches
|
|
510
548
|
return opts?.select
|
|
511
549
|
? opts.select(matches as Array<TRouteMatch>)
|
|
512
550
|
: (matches as T)
|
package/src/RouterProvider.tsx
CHANGED
|
@@ -88,10 +88,12 @@ export function RouterProvider<
|
|
|
88
88
|
const routerContext = getRouterContext()
|
|
89
89
|
|
|
90
90
|
const provider = (
|
|
91
|
+
<React.Suspense fallback={null}>
|
|
91
92
|
<routerContext.Provider value={router}>
|
|
92
93
|
{matches}
|
|
93
94
|
<Transitioner />
|
|
94
95
|
</routerContext.Provider>
|
|
96
|
+
</React.Suspense>
|
|
95
97
|
)
|
|
96
98
|
|
|
97
99
|
if (router.options.Wrap) {
|
package/src/index.tsx
CHANGED
package/src/link.tsx
CHANGED
|
@@ -3,6 +3,7 @@ import { useMatch } from './Matches'
|
|
|
3
3
|
import { useRouterState } from './useRouterState'
|
|
4
4
|
import { useRouter } from './useRouter'
|
|
5
5
|
import { deepEqual, exactPathTest, functionalUpdate } from './utils'
|
|
6
|
+
import type { ParsedLocation } from '.'
|
|
6
7
|
import type { HistoryState } from '@tanstack/history'
|
|
7
8
|
import type { Trim } from './fileRoute'
|
|
8
9
|
import type { AnyRoute, RootSearchSchema } from './route'
|
|
@@ -178,6 +179,7 @@ export type ToOptions<
|
|
|
178
179
|
TMaskFrom extends RoutePaths<TRouteTree> | string = TFrom,
|
|
179
180
|
TMaskTo extends string = '',
|
|
180
181
|
> = ToSubOptions<TRouteTree, TFrom, TTo> & {
|
|
182
|
+
_fromLocation?: ParsedLocation
|
|
181
183
|
mask?: ToMaskOptions<TRouteTree, TMaskFrom, TMaskTo>
|
|
182
184
|
}
|
|
183
185
|
|
package/src/redirects.ts
CHANGED
|
@@ -31,8 +31,10 @@ export type ResolvedRedirect<
|
|
|
31
31
|
TMaskTo extends string = '',
|
|
32
32
|
> = PickAsRequired<
|
|
33
33
|
Redirect<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo>,
|
|
34
|
-
'code' | 'statusCode' | '
|
|
35
|
-
>
|
|
34
|
+
'code' | 'statusCode' | 'headers'
|
|
35
|
+
> & {
|
|
36
|
+
href: string
|
|
37
|
+
}
|
|
36
38
|
|
|
37
39
|
export function redirect<
|
|
38
40
|
TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
|
package/src/route.ts
CHANGED
|
@@ -254,14 +254,10 @@ export type UpdatableRouteOptions<TAllParams, TFullSearchSchema, TLoaderData> =
|
|
|
254
254
|
meta?: (ctx: {
|
|
255
255
|
params: TAllParams
|
|
256
256
|
loaderData: TLoaderData
|
|
257
|
-
}) =>
|
|
258
|
-
| Array<JSX.IntrinsicElements['meta']>
|
|
259
|
-
| Promise<Array<JSX.IntrinsicElements['meta']>>
|
|
257
|
+
}) => Array<JSX.IntrinsicElements['meta']>
|
|
260
258
|
links?: () => Array<JSX.IntrinsicElements['link']>
|
|
261
259
|
scripts?: () => Array<JSX.IntrinsicElements['script']>
|
|
262
|
-
headers?: (ctx: {
|
|
263
|
-
loaderData: TLoaderData
|
|
264
|
-
}) => Promise<Record<string, string>> | Record<string, string>
|
|
260
|
+
headers?: (ctx: { loaderData: TLoaderData }) => Record<string, string>
|
|
265
261
|
} & UpdatableStaticRouteOption
|
|
266
262
|
|
|
267
263
|
export type UpdatableStaticRouteOption =
|
|
@@ -822,7 +818,6 @@ export class Route<
|
|
|
822
818
|
}
|
|
823
819
|
|
|
824
820
|
useMatch = <
|
|
825
|
-
// eslint-disable-next-line no-shadow
|
|
826
821
|
TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
|
|
827
822
|
TRouteMatchState = RouteMatch<TRouteTree, TId>,
|
|
828
823
|
TSelected = TRouteMatchState,
|