@tanstack/react-router 1.30.1 → 1.31.1
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.map +1 -1
- package/dist/cjs/Matches.d.cts +10 -10
- package/dist/cjs/RouterProvider.cjs.map +1 -1
- package/dist/cjs/RouterProvider.d.cts +7 -7
- package/dist/cjs/fileRoute.d.cts +1 -1
- package/dist/cjs/link.cjs.map +1 -1
- package/dist/cjs/link.d.cts +42 -42
- package/dist/cjs/redirects.cjs.map +1 -1
- package/dist/cjs/redirects.d.cts +5 -6
- package/dist/cjs/route.cjs.map +1 -1
- package/dist/cjs/route.d.cts +6 -6
- package/dist/cjs/routeInfo.d.cts +19 -2
- package/dist/cjs/router.cjs +69 -52
- package/dist/cjs/router.cjs.map +1 -1
- package/dist/cjs/router.d.cts +15 -15
- package/dist/cjs/routerContext.cjs.map +1 -1
- package/dist/cjs/routerContext.d.cts +1 -1
- package/dist/cjs/useNavigate.cjs.map +1 -1
- package/dist/cjs/useNavigate.d.cts +3 -4
- package/dist/cjs/useRouter.cjs.map +1 -1
- package/dist/cjs/useRouter.d.cts +3 -4
- package/dist/cjs/useRouterState.cjs.map +1 -1
- package/dist/cjs/useRouterState.d.cts +3 -4
- package/dist/esm/Matches.d.ts +10 -10
- package/dist/esm/Matches.js.map +1 -1
- package/dist/esm/RouterProvider.d.ts +7 -7
- package/dist/esm/RouterProvider.js.map +1 -1
- package/dist/esm/fileRoute.d.ts +1 -1
- package/dist/esm/link.d.ts +42 -42
- package/dist/esm/link.js.map +1 -1
- package/dist/esm/redirects.d.ts +5 -6
- package/dist/esm/redirects.js.map +1 -1
- package/dist/esm/route.d.ts +6 -6
- package/dist/esm/route.js.map +1 -1
- package/dist/esm/routeInfo.d.ts +19 -2
- package/dist/esm/router.d.ts +15 -15
- package/dist/esm/router.js +69 -52
- package/dist/esm/router.js.map +1 -1
- package/dist/esm/routerContext.d.ts +1 -1
- package/dist/esm/routerContext.js.map +1 -1
- package/dist/esm/useNavigate.d.ts +3 -4
- package/dist/esm/useNavigate.js.map +1 -1
- package/dist/esm/useRouter.d.ts +3 -4
- package/dist/esm/useRouter.js.map +1 -1
- package/dist/esm/useRouterState.d.ts +3 -4
- package/dist/esm/useRouterState.js.map +1 -1
- package/package.json +1 -1
- package/src/Matches.tsx +40 -22
- package/src/RouterProvider.tsx +34 -11
- package/src/link.tsx +124 -139
- package/src/redirects.ts +14 -14
- package/src/route.ts +3 -3
- package/src/routeInfo.ts +72 -4
- package/src/router.ts +132 -68
- package/src/routerContext.tsx +1 -1
- package/src/useNavigate.tsx +9 -10
- package/src/useRouter.tsx +4 -5
- 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
|
|
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
|
-
?
|
|
17
|
-
|
|
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
|
-
} &
|
|
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<
|
|
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?:
|
|
163
|
+
trailingSlash?: TTrailingSlashOption
|
|
161
164
|
}
|
|
162
165
|
|
|
163
166
|
export interface RouterTransformer {
|
|
@@ -202,7 +205,7 @@ export interface BuildNextOptions {
|
|
|
202
205
|
unmaskOnReload?: boolean
|
|
203
206
|
}
|
|
204
207
|
from?: string
|
|
205
|
-
|
|
208
|
+
fromSearch?: unknown
|
|
206
209
|
}
|
|
207
210
|
|
|
208
211
|
export interface DehydratedRouterState {
|
|
@@ -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<
|
|
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
|
|
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<
|
|
281
|
+
options: RouterConstructorOptions<
|
|
282
|
+
TRouteTree,
|
|
283
|
+
TTrailingSlashOption,
|
|
284
|
+
TDehydrated,
|
|
285
|
+
TSerializedError
|
|
286
|
+
>,
|
|
269
287
|
) {
|
|
270
|
-
return new Router<
|
|
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
|
|
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
|
> {
|
|
@@ -281,7 +305,6 @@ export class Router<
|
|
|
281
305
|
)}`
|
|
282
306
|
resetNextScroll = true
|
|
283
307
|
shouldViewTransition?: true = undefined
|
|
284
|
-
navigateTimeout: Timeout | null = null
|
|
285
308
|
latestLoadPromise: Promise<void> = Promise.resolve()
|
|
286
309
|
subscribers = new Set<RouterListener<RouterEvent>>()
|
|
287
310
|
injectedHtml: Array<InjectedHtmlEntry> = []
|
|
@@ -292,7 +315,12 @@ export class Router<
|
|
|
292
315
|
__store!: Store<RouterState<TRouteTree>>
|
|
293
316
|
options!: PickAsRequired<
|
|
294
317
|
Omit<
|
|
295
|
-
RouterOptions<
|
|
318
|
+
RouterOptions<
|
|
319
|
+
TRouteTree,
|
|
320
|
+
TTrailingSlashOption,
|
|
321
|
+
TDehydrated,
|
|
322
|
+
TSerializedError
|
|
323
|
+
>,
|
|
296
324
|
'transformer'
|
|
297
325
|
> & {
|
|
298
326
|
transformer: RouterTransformer
|
|
@@ -313,6 +341,7 @@ export class Router<
|
|
|
313
341
|
constructor(
|
|
314
342
|
options: RouterConstructorOptions<
|
|
315
343
|
TRouteTree,
|
|
344
|
+
TTrailingSlashOption,
|
|
316
345
|
TDehydrated,
|
|
317
346
|
TSerializedError
|
|
318
347
|
>,
|
|
@@ -343,6 +372,7 @@ export class Router<
|
|
|
343
372
|
update = (
|
|
344
373
|
newOptions: RouterConstructorOptions<
|
|
345
374
|
TRouteTree,
|
|
375
|
+
TTrailingSlashOption,
|
|
346
376
|
TDehydrated,
|
|
347
377
|
TSerializedError
|
|
348
378
|
>,
|
|
@@ -889,25 +919,25 @@ export class Router<
|
|
|
889
919
|
} = {},
|
|
890
920
|
matches?: Array<MakeRouteMatch<TRouteTree>>,
|
|
891
921
|
): ParsedLocation => {
|
|
892
|
-
|
|
893
|
-
let fromSearch = dest.
|
|
922
|
+
let fromPath = this.latestLocation.pathname
|
|
923
|
+
let fromSearch = dest.fromSearch || this.latestLocation.search
|
|
894
924
|
|
|
895
|
-
const fromMatches = this.matchRoutes(
|
|
925
|
+
const fromMatches = this.matchRoutes(
|
|
926
|
+
this.latestLocation.pathname,
|
|
927
|
+
fromSearch,
|
|
928
|
+
)
|
|
896
929
|
|
|
930
|
+
fromPath =
|
|
931
|
+
fromMatches.find((d) => d.id === dest.from)?.pathname || fromPath
|
|
897
932
|
fromSearch = last(fromMatches)?.search || this.latestLocation.search
|
|
898
933
|
|
|
899
934
|
const stayingMatches = matches?.filter((d) =>
|
|
900
935
|
fromMatches.find((e) => e.routeId === d.routeId),
|
|
901
936
|
)
|
|
902
937
|
|
|
903
|
-
const fromRoute = this.looseRoutesById[last(fromMatches)?.routeId]
|
|
904
|
-
|
|
905
938
|
let pathname = dest.to
|
|
906
|
-
? this.resolvePathWithBase(
|
|
907
|
-
|
|
908
|
-
`${dest.to}`,
|
|
909
|
-
)
|
|
910
|
-
: this.resolvePathWithBase(fromRoute?.fullPath, fromRoute?.fullPath)
|
|
939
|
+
? this.resolvePathWithBase(fromPath, `${dest.to}`)
|
|
940
|
+
: this.resolvePathWithBase(fromPath, fromPath)
|
|
911
941
|
|
|
912
942
|
const prevParams = { ...last(fromMatches)?.params }
|
|
913
943
|
|
|
@@ -1082,8 +1112,6 @@ export class Router<
|
|
|
1082
1112
|
viewTransition,
|
|
1083
1113
|
...next
|
|
1084
1114
|
}: ParsedLocation & CommitLocationOptions) => {
|
|
1085
|
-
if (this.navigateTimeout) clearTimeout(this.navigateTimeout)
|
|
1086
|
-
|
|
1087
1115
|
const isSameUrl = this.latestLocation.href === next.href
|
|
1088
1116
|
|
|
1089
1117
|
// If the next urls are the same and we're not replacing,
|
|
@@ -1244,15 +1272,10 @@ export class Router<
|
|
|
1244
1272
|
}
|
|
1245
1273
|
|
|
1246
1274
|
if (isRedirect(err)) {
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
if (!preload && !this.isServer) {
|
|
1250
|
-
this.navigate({ ...(redirect as any), replace: true })
|
|
1251
|
-
}
|
|
1252
|
-
|
|
1253
|
-
throw redirect
|
|
1275
|
+
err = this.resolveRedirect(err)
|
|
1276
|
+
throw err
|
|
1254
1277
|
} else if (isNotFound(err)) {
|
|
1255
|
-
|
|
1278
|
+
this.handleNotFound(matches, err)
|
|
1256
1279
|
throw err
|
|
1257
1280
|
}
|
|
1258
1281
|
}
|
|
@@ -1264,6 +1287,25 @@ export class Router<
|
|
|
1264
1287
|
const parentMatch = matches[index - 1]
|
|
1265
1288
|
const route = this.looseRoutesById[match.routeId]!
|
|
1266
1289
|
const abortController = new AbortController()
|
|
1290
|
+
let loadPromise = match.loadPromise
|
|
1291
|
+
|
|
1292
|
+
if (match.isFetching) {
|
|
1293
|
+
continue
|
|
1294
|
+
}
|
|
1295
|
+
|
|
1296
|
+
const previousResolve = loadPromise.resolve
|
|
1297
|
+
// Create a new one
|
|
1298
|
+
loadPromise = createControlledPromise<void>(
|
|
1299
|
+
// Resolve the old when we we resolve the new one
|
|
1300
|
+
previousResolve,
|
|
1301
|
+
)
|
|
1302
|
+
|
|
1303
|
+
// Otherwise, load the route
|
|
1304
|
+
matches[index] = match = updateMatch(match.id, (prev) => ({
|
|
1305
|
+
...prev,
|
|
1306
|
+
isFetching: 'beforeLoad',
|
|
1307
|
+
loadPromise,
|
|
1308
|
+
}))
|
|
1267
1309
|
|
|
1268
1310
|
const handleSerialError = (err: any, routerCode: string) => {
|
|
1269
1311
|
err.routerCode = routerCode
|
|
@@ -1344,6 +1386,8 @@ export class Router<
|
|
|
1344
1386
|
cause: preload ? 'preload' : match.cause,
|
|
1345
1387
|
})) ?? ({} as any)
|
|
1346
1388
|
|
|
1389
|
+
if ((latestPromise = checkLatest())) return latestPromise
|
|
1390
|
+
|
|
1347
1391
|
if (
|
|
1348
1392
|
isRedirect(beforeLoadContext) ||
|
|
1349
1393
|
isNotFound(beforeLoadContext)
|
|
@@ -1373,6 +1417,8 @@ export class Router<
|
|
|
1373
1417
|
}
|
|
1374
1418
|
}
|
|
1375
1419
|
|
|
1420
|
+
if ((latestPromise = checkLatest())) return latestPromise
|
|
1421
|
+
|
|
1376
1422
|
const validResolvedMatches = matches.slice(0, firstBadMatchIndex)
|
|
1377
1423
|
const matchPromises: Array<Promise<any>> = []
|
|
1378
1424
|
|
|
@@ -1400,7 +1446,6 @@ export class Router<
|
|
|
1400
1446
|
let lazyPromise = Promise.resolve()
|
|
1401
1447
|
let componentsPromise = Promise.resolve() as Promise<any>
|
|
1402
1448
|
let loaderPromise = existing.loaderPromise
|
|
1403
|
-
let loadPromise = existing.loadPromise
|
|
1404
1449
|
|
|
1405
1450
|
// If the Matches component rendered
|
|
1406
1451
|
// the pending component and needs to show it for
|
|
@@ -1424,7 +1469,7 @@ export class Router<
|
|
|
1424
1469
|
}
|
|
1425
1470
|
|
|
1426
1471
|
try {
|
|
1427
|
-
if (
|
|
1472
|
+
if (match.isFetching === 'beforeLoad') {
|
|
1428
1473
|
// If the user doesn't want the route to reload, just
|
|
1429
1474
|
// resolve with the existing loader data
|
|
1430
1475
|
|
|
@@ -1433,11 +1478,14 @@ export class Router<
|
|
|
1433
1478
|
// }
|
|
1434
1479
|
|
|
1435
1480
|
// Otherwise, load the route
|
|
1436
|
-
matches[index] = match =
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1481
|
+
matches[index] = match = updateMatch(
|
|
1482
|
+
match.id,
|
|
1483
|
+
(prev) => ({
|
|
1484
|
+
...prev,
|
|
1485
|
+
isFetching: 'loader',
|
|
1486
|
+
fetchCount: match.fetchCount + 1,
|
|
1487
|
+
}),
|
|
1488
|
+
)
|
|
1441
1489
|
|
|
1442
1490
|
lazyPromise =
|
|
1443
1491
|
route.lazyFn?.().then((lazyRoute) => {
|
|
@@ -1470,20 +1518,15 @@ export class Router<
|
|
|
1470
1518
|
// Kick off the loader!
|
|
1471
1519
|
loaderPromise = route.options.loader?.(loaderContext)
|
|
1472
1520
|
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1521
|
+
matches[index] = match = updateMatch(
|
|
1522
|
+
match.id,
|
|
1523
|
+
(prev) => ({
|
|
1524
|
+
...prev,
|
|
1525
|
+
loaderPromise,
|
|
1526
|
+
}),
|
|
1478
1527
|
)
|
|
1479
1528
|
}
|
|
1480
1529
|
|
|
1481
|
-
matches[index] = match = updateMatch(match.id, (prev) => ({
|
|
1482
|
-
...prev,
|
|
1483
|
-
loaderPromise,
|
|
1484
|
-
loadPromise,
|
|
1485
|
-
}))
|
|
1486
|
-
|
|
1487
1530
|
const loaderData = await loaderPromise
|
|
1488
1531
|
if ((latestPromise = checkLatest()))
|
|
1489
1532
|
return await latestPromise
|
|
@@ -1549,7 +1592,7 @@ export class Router<
|
|
|
1549
1592
|
if ((latestPromise = checkLatest()))
|
|
1550
1593
|
return await latestPromise
|
|
1551
1594
|
|
|
1552
|
-
loadPromise.resolve()
|
|
1595
|
+
match.loadPromise.resolve()
|
|
1553
1596
|
}
|
|
1554
1597
|
|
|
1555
1598
|
// This is where all of the stale-while-revalidate magic happens
|
|
@@ -1694,27 +1737,38 @@ export class Router<
|
|
|
1694
1737
|
let redirect: ResolvedRedirect | undefined
|
|
1695
1738
|
let notFound: NotFoundError | undefined
|
|
1696
1739
|
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
const loadMatchesPromise = this.loadMatches({
|
|
1740
|
+
const loadMatches = () =>
|
|
1741
|
+
this.loadMatches({
|
|
1700
1742
|
matches: pendingMatches,
|
|
1701
1743
|
location: next,
|
|
1702
1744
|
checkLatest: () => this.checkLatest(promise),
|
|
1703
1745
|
})
|
|
1704
1746
|
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1747
|
+
// If we are on the server or non-first load on the client, await
|
|
1748
|
+
// the loadMatches before transitioning
|
|
1749
|
+
if (previousMatches.length || this.isServer) {
|
|
1750
|
+
try {
|
|
1751
|
+
await loadMatches()
|
|
1752
|
+
} catch (err) {
|
|
1753
|
+
if (isRedirect(err)) {
|
|
1754
|
+
redirect = err as ResolvedRedirect
|
|
1755
|
+
} else if (isNotFound(err)) {
|
|
1756
|
+
notFound = err
|
|
1757
|
+
}
|
|
1713
1758
|
}
|
|
1714
|
-
|
|
1715
|
-
//
|
|
1716
|
-
//
|
|
1717
|
-
|
|
1759
|
+
} else {
|
|
1760
|
+
// For client-only first loads, we need to start the transition
|
|
1761
|
+
// immediately and load the matches in the background
|
|
1762
|
+
loadMatches().catch((err) => {
|
|
1763
|
+
// This also means that we need to handle any redirects
|
|
1764
|
+
// that might happen during the load/transition
|
|
1765
|
+
if (isRedirect(err)) {
|
|
1766
|
+
this.navigate({ ...err, replace: true })
|
|
1767
|
+
}
|
|
1768
|
+
// Because our history listener isn't guaranteed to be mounted
|
|
1769
|
+
// on the first load, we need to manually call load again
|
|
1770
|
+
this.load()
|
|
1771
|
+
})
|
|
1718
1772
|
}
|
|
1719
1773
|
|
|
1720
1774
|
// Only apply the latest transition
|
|
@@ -1848,7 +1902,13 @@ export class Router<
|
|
|
1848
1902
|
TMaskFrom extends RoutePaths<TRouteTree> | string = TFrom,
|
|
1849
1903
|
TMaskTo extends string = '',
|
|
1850
1904
|
>(
|
|
1851
|
-
opts: NavigateOptions<
|
|
1905
|
+
opts: NavigateOptions<
|
|
1906
|
+
Router<TRouteTree, TTrailingSlashOption, TDehydrated, TSerializedError>,
|
|
1907
|
+
TFrom,
|
|
1908
|
+
TTo,
|
|
1909
|
+
TMaskFrom,
|
|
1910
|
+
TMaskTo
|
|
1911
|
+
>,
|
|
1852
1912
|
): Promise<Array<AnyRouteMatch> | undefined> => {
|
|
1853
1913
|
const next = this.buildLocation(opts as any)
|
|
1854
1914
|
|
|
@@ -1905,7 +1965,7 @@ export class Router<
|
|
|
1905
1965
|
} catch (err) {
|
|
1906
1966
|
if (isRedirect(err)) {
|
|
1907
1967
|
return await this.preloadRoute({
|
|
1908
|
-
|
|
1968
|
+
fromSearch: next.search,
|
|
1909
1969
|
from: next.pathname,
|
|
1910
1970
|
...(err as any),
|
|
1911
1971
|
})
|
|
@@ -1921,7 +1981,11 @@ export class Router<
|
|
|
1921
1981
|
TTo extends string = '',
|
|
1922
1982
|
TResolved = ResolveRelativePath<TFrom, NoInfer<TTo>>,
|
|
1923
1983
|
>(
|
|
1924
|
-
location: ToOptions<
|
|
1984
|
+
location: ToOptions<
|
|
1985
|
+
Router<TRouteTree, TTrailingSlashOption, TDehydrated, TSerializedError>,
|
|
1986
|
+
TFrom,
|
|
1987
|
+
TTo
|
|
1988
|
+
>,
|
|
1925
1989
|
opts?: MatchRouteOptions,
|
|
1926
1990
|
): false | RouteById<TRouteTree, TResolved>['types']['allParams'] => {
|
|
1927
1991
|
const matchLocation = {
|
package/src/routerContext.tsx
CHANGED
|
@@ -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') {
|
package/src/useNavigate.tsx
CHANGED
|
@@ -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
|
-
|
|
13
|
-
TFrom extends RoutePaths<
|
|
14
|
-
TMaskFrom extends RoutePaths<
|
|
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<
|
|
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
|
-
|
|
56
|
-
TFrom extends RoutePaths<
|
|
54
|
+
TRouter extends AnyRouter = RegisteredRouter,
|
|
55
|
+
TFrom extends RoutePaths<TRouter['routeTree']> | string = string,
|
|
57
56
|
TTo extends string = '',
|
|
58
|
-
TMaskFrom extends RoutePaths<
|
|
57
|
+
TMaskFrom extends RoutePaths<TRouter['routeTree']> | string = TFrom,
|
|
59
58
|
TMaskTo extends string = '',
|
|
60
|
-
>(props: NavigateOptions<
|
|
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 {
|
|
5
|
-
import type { RegisteredRouter, Router } from './router'
|
|
4
|
+
import type { AnyRouter, RegisteredRouter, Router } from './router'
|
|
6
5
|
|
|
7
|
-
export function useRouter<
|
|
8
|
-
|
|
9
|
-
|
|
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),
|
package/src/useRouterState.tsx
CHANGED
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
import { useStore } from '@tanstack/react-store'
|
|
2
2
|
import { useRouter } from './useRouter'
|
|
3
|
-
import type {
|
|
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
|
-
|
|
8
|
-
TSelected = RouterState<
|
|
6
|
+
TRouter extends AnyRouter = RegisteredRouter,
|
|
7
|
+
TSelected = RouterState<TRouter['routeTree']>,
|
|
9
8
|
>(opts?: {
|
|
10
|
-
router?:
|
|
9
|
+
router?: TRouter
|
|
11
10
|
select: (state: RouterState<RegisteredRouter['routeTree']>) => TSelected
|
|
12
11
|
}): TSelected {
|
|
13
|
-
const contextRouter = useRouter<
|
|
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)
|