@tanstack/router-core 0.0.1-beta.3 → 0.0.1-beta.30
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/build/cjs/_virtual/_rollupPluginBabelHelpers.js +0 -2
- package/build/cjs/_virtual/_rollupPluginBabelHelpers.js.map +1 -1
- package/build/cjs/{packages/router-core/src/index.js → index.js} +23 -7
- package/build/cjs/{packages/router-core/src/index.js.map → index.js.map} +1 -1
- package/build/cjs/{packages/router-core/src/path.js → path.js} +7 -34
- package/build/cjs/path.js.map +1 -0
- package/build/cjs/{packages/router-core/src/qss.js → qss.js} +9 -13
- package/build/cjs/qss.js.map +1 -0
- package/build/cjs/{packages/router-core/src/route.js → route.js} +15 -37
- package/build/cjs/route.js.map +1 -0
- package/build/cjs/{packages/router-core/src/routeConfig.js → routeConfig.js} +13 -12
- package/build/cjs/routeConfig.js.map +1 -0
- package/build/cjs/routeMatch.js +200 -0
- package/build/cjs/routeMatch.js.map +1 -0
- package/build/cjs/{packages/router-core/src/router.js → router.js} +246 -208
- package/build/cjs/router.js.map +1 -0
- package/build/cjs/{packages/router-core/src/searchParams.js → searchParams.js} +7 -10
- package/build/cjs/searchParams.js.map +1 -0
- package/build/cjs/{packages/router-core/src/utils.js → utils.js} +17 -30
- package/build/cjs/utils.js.map +1 -0
- package/build/esm/index.js +382 -1307
- package/build/esm/index.js.map +1 -1
- package/build/stats-html.html +59 -49
- package/build/stats-react.json +161 -168
- package/build/types/index.d.ts +228 -206
- package/build/umd/index.development.js +373 -481
- package/build/umd/index.development.js.map +1 -1
- package/build/umd/index.production.js +1 -1
- package/build/umd/index.production.js.map +1 -1
- package/package.json +3 -2
- package/src/frameworks.ts +2 -2
- package/src/index.ts +0 -1
- package/src/link.ts +8 -5
- package/src/path.ts +2 -6
- package/src/qss.ts +1 -0
- package/src/route.ts +24 -32
- package/src/routeConfig.ts +100 -77
- package/src/routeInfo.ts +22 -7
- package/src/routeMatch.ts +95 -157
- package/src/router.ts +346 -122
- package/src/utils.ts +14 -7
- package/build/cjs/node_modules/@babel/runtime/helpers/esm/extends.js +0 -33
- package/build/cjs/node_modules/@babel/runtime/helpers/esm/extends.js.map +0 -1
- package/build/cjs/node_modules/history/index.js +0 -815
- package/build/cjs/node_modules/history/index.js.map +0 -1
- package/build/cjs/node_modules/tiny-invariant/dist/esm/tiny-invariant.js +0 -30
- package/build/cjs/node_modules/tiny-invariant/dist/esm/tiny-invariant.js.map +0 -1
- package/build/cjs/packages/router-core/src/path.js.map +0 -1
- package/build/cjs/packages/router-core/src/qss.js.map +0 -1
- package/build/cjs/packages/router-core/src/route.js.map +0 -1
- package/build/cjs/packages/router-core/src/routeConfig.js.map +0 -1
- package/build/cjs/packages/router-core/src/routeMatch.js +0 -266
- package/build/cjs/packages/router-core/src/routeMatch.js.map +0 -1
- package/build/cjs/packages/router-core/src/router.js.map +0 -1
- package/build/cjs/packages/router-core/src/searchParams.js.map +0 -1
- package/build/cjs/packages/router-core/src/utils.js.map +0 -1
package/src/router.ts
CHANGED
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
matchPathname,
|
|
24
24
|
resolvePath,
|
|
25
25
|
} from './path'
|
|
26
|
-
import { AnyRoute,
|
|
26
|
+
import { AnyRoute, createRoute, Route } from './route'
|
|
27
27
|
import {
|
|
28
28
|
AnyLoaderData,
|
|
29
29
|
AnyPathParams,
|
|
@@ -45,6 +45,7 @@ import { defaultParseSearch, defaultStringifySearch } from './searchParams'
|
|
|
45
45
|
import {
|
|
46
46
|
functionalUpdate,
|
|
47
47
|
last,
|
|
48
|
+
pick,
|
|
48
49
|
PickAsRequired,
|
|
49
50
|
PickRequired,
|
|
50
51
|
replaceEqualDeep,
|
|
@@ -52,6 +53,22 @@ import {
|
|
|
52
53
|
Updater,
|
|
53
54
|
} from './utils'
|
|
54
55
|
|
|
56
|
+
export interface RegisterRouter {
|
|
57
|
+
// router: Router
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export type RegisteredRouter = RegisterRouter extends {
|
|
61
|
+
router: Router<infer TRouteConfig, infer TAllRouteInfo, infer TRouterContext>
|
|
62
|
+
}
|
|
63
|
+
? Router<TRouteConfig, TAllRouteInfo, TRouterContext>
|
|
64
|
+
: Router
|
|
65
|
+
|
|
66
|
+
export type RegisteredAllRouteInfo = RegisterRouter extends {
|
|
67
|
+
router: Router<infer TRouteConfig, infer TAllRouteInfo, infer TRouterContext>
|
|
68
|
+
}
|
|
69
|
+
? TAllRouteInfo
|
|
70
|
+
: AnyAllRouteInfo
|
|
71
|
+
|
|
55
72
|
export interface LocationState {}
|
|
56
73
|
|
|
57
74
|
export interface Location<
|
|
@@ -80,7 +97,10 @@ export type FilterRoutesFn = <TRoute extends Route<any, RouteInfo>>(
|
|
|
80
97
|
routeConfigs: TRoute[],
|
|
81
98
|
) => TRoute[]
|
|
82
99
|
|
|
83
|
-
export interface RouterOptions<
|
|
100
|
+
export interface RouterOptions<
|
|
101
|
+
TRouteConfig extends AnyRouteConfig,
|
|
102
|
+
TRouterContext,
|
|
103
|
+
> {
|
|
84
104
|
history?: BrowserHistory | MemoryHistory | HashHistory
|
|
85
105
|
stringifySearch?: SearchSerializer
|
|
86
106
|
parseSearch?: SearchParser
|
|
@@ -89,23 +109,27 @@ export interface RouterOptions<TRouteConfig extends AnyRouteConfig> {
|
|
|
89
109
|
defaultPreloadMaxAge?: number
|
|
90
110
|
defaultPreloadGcMaxAge?: number
|
|
91
111
|
defaultPreloadDelay?: number
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
defaultCatchElement?: GetFrameworkGeneric<'Element'>
|
|
96
|
-
defaultPendingElement?: GetFrameworkGeneric<'Element'>
|
|
97
|
-
defaultPendingMs?: number
|
|
98
|
-
defaultPendingMinMs?: number
|
|
112
|
+
defaultComponent?: GetFrameworkGeneric<'Component'>
|
|
113
|
+
defaultErrorComponent?: GetFrameworkGeneric<'ErrorComponent'>
|
|
114
|
+
defaultPendingComponent?: GetFrameworkGeneric<'Component'>
|
|
99
115
|
defaultLoaderMaxAge?: number
|
|
100
116
|
defaultLoaderGcMaxAge?: number
|
|
101
117
|
caseSensitive?: boolean
|
|
102
118
|
routeConfig?: TRouteConfig
|
|
103
119
|
basepath?: string
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
120
|
+
useServerData?: boolean
|
|
121
|
+
createRouter?: (router: Router<any, any, any>) => void
|
|
122
|
+
createRoute?: (opts: {
|
|
123
|
+
route: AnyRoute
|
|
124
|
+
router: Router<any, any, any>
|
|
125
|
+
}) => void
|
|
126
|
+
context?: TRouterContext
|
|
127
|
+
loadComponent?: (
|
|
128
|
+
component: GetFrameworkGeneric<'Component'>,
|
|
129
|
+
) => Promise<GetFrameworkGeneric<'Component'>>
|
|
130
|
+
// renderComponent?: (
|
|
131
|
+
// component: GetFrameworkGeneric<'Component'>,
|
|
132
|
+
// ) => GetFrameworkGeneric<'Element'>
|
|
109
133
|
}
|
|
110
134
|
|
|
111
135
|
export interface Action<
|
|
@@ -113,10 +137,13 @@ export interface Action<
|
|
|
113
137
|
TResponse = unknown,
|
|
114
138
|
// TError = unknown,
|
|
115
139
|
> {
|
|
116
|
-
submit: (
|
|
140
|
+
submit: (
|
|
141
|
+
submission?: TPayload,
|
|
142
|
+
actionOpts?: { invalidate?: boolean; multi?: boolean },
|
|
143
|
+
) => Promise<TResponse>
|
|
117
144
|
current?: ActionState<TPayload, TResponse>
|
|
118
145
|
latest?: ActionState<TPayload, TResponse>
|
|
119
|
-
|
|
146
|
+
submissions: ActionState<TPayload, TResponse>[]
|
|
120
147
|
}
|
|
121
148
|
|
|
122
149
|
export interface ActionState<
|
|
@@ -127,6 +154,7 @@ export interface ActionState<
|
|
|
127
154
|
submittedAt: number
|
|
128
155
|
status: 'idle' | 'pending' | 'success' | 'error'
|
|
129
156
|
submission: TPayload
|
|
157
|
+
isMulti: boolean
|
|
130
158
|
data?: TResponse
|
|
131
159
|
error?: unknown
|
|
132
160
|
}
|
|
@@ -160,21 +188,21 @@ export interface Loader<
|
|
|
160
188
|
}
|
|
161
189
|
|
|
162
190
|
export interface LoaderState<
|
|
163
|
-
TFullSearchSchema =
|
|
164
|
-
TAllParams =
|
|
191
|
+
TFullSearchSchema extends AnySearchSchema = {},
|
|
192
|
+
TAllParams extends AnyPathParams = {},
|
|
165
193
|
> {
|
|
166
194
|
loadedAt: number
|
|
167
195
|
loaderContext: LoaderContext<TFullSearchSchema, TAllParams>
|
|
168
196
|
}
|
|
169
197
|
|
|
170
|
-
export interface RouterState
|
|
198
|
+
export interface RouterState<
|
|
199
|
+
TSearchObj extends AnySearchSchema = {},
|
|
200
|
+
TState extends LocationState = LocationState,
|
|
201
|
+
> {
|
|
171
202
|
status: 'idle' | 'loading'
|
|
172
|
-
location: Location
|
|
203
|
+
location: Location<TSearchObj, TState>
|
|
173
204
|
matches: RouteMatch[]
|
|
174
205
|
lastUpdated: number
|
|
175
|
-
loaderData: unknown
|
|
176
|
-
currentAction?: ActionState
|
|
177
|
-
latestAction?: ActionState
|
|
178
206
|
actions: Record<string, Action>
|
|
179
207
|
loaders: Record<string, Loader>
|
|
180
208
|
pending?: PendingState
|
|
@@ -187,7 +215,7 @@ export interface PendingState {
|
|
|
187
215
|
matches: RouteMatch[]
|
|
188
216
|
}
|
|
189
217
|
|
|
190
|
-
type Listener = (router: Router<any, any>) => void
|
|
218
|
+
type Listener = (router: Router<any, any, any>) => void
|
|
191
219
|
|
|
192
220
|
export type ListenerFn = () => void
|
|
193
221
|
|
|
@@ -196,6 +224,7 @@ export interface BuildNextOptions {
|
|
|
196
224
|
params?: true | Updater<Record<string, any>>
|
|
197
225
|
search?: true | Updater<unknown>
|
|
198
226
|
hash?: true | Updater<string>
|
|
227
|
+
state?: LocationState
|
|
199
228
|
key?: string
|
|
200
229
|
from?: string
|
|
201
230
|
fromCurrent?: boolean
|
|
@@ -225,41 +254,79 @@ type LinkCurrentTargetElement = {
|
|
|
225
254
|
preloadTimeout?: null | ReturnType<typeof setTimeout>
|
|
226
255
|
}
|
|
227
256
|
|
|
257
|
+
export interface DehydratedRouterState
|
|
258
|
+
extends Pick<
|
|
259
|
+
RouterState,
|
|
260
|
+
'status' | 'location' | 'lastUpdated' | 'location'
|
|
261
|
+
> {
|
|
262
|
+
matches: DehydratedRouteMatch[]
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
export interface DehydratedRouter<TRouterContext = unknown> {
|
|
266
|
+
location: Router['location']
|
|
267
|
+
state: DehydratedRouterState
|
|
268
|
+
context: TRouterContext
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
interface DehydratedRouteMatch
|
|
272
|
+
extends Pick<
|
|
273
|
+
RouteMatch<any, any>,
|
|
274
|
+
| 'matchId'
|
|
275
|
+
| 'status'
|
|
276
|
+
| 'routeLoaderData'
|
|
277
|
+
| 'loaderData'
|
|
278
|
+
| 'isInvalid'
|
|
279
|
+
| 'invalidAt'
|
|
280
|
+
> {}
|
|
281
|
+
|
|
282
|
+
export interface RouterContext {}
|
|
283
|
+
|
|
228
284
|
export interface Router<
|
|
229
285
|
TRouteConfig extends AnyRouteConfig = RouteConfig,
|
|
230
286
|
TAllRouteInfo extends AnyAllRouteInfo = AllRouteInfo<TRouteConfig>,
|
|
287
|
+
TRouterContext = unknown,
|
|
231
288
|
> {
|
|
289
|
+
types: {
|
|
290
|
+
// Super secret internal stuff
|
|
291
|
+
RouteConfig: TRouteConfig
|
|
292
|
+
AllRouteInfo: TAllRouteInfo
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Public API
|
|
232
296
|
history: BrowserHistory | MemoryHistory | HashHistory
|
|
233
297
|
options: PickAsRequired<
|
|
234
|
-
RouterOptions<TRouteConfig>,
|
|
235
|
-
'stringifySearch' | 'parseSearch'
|
|
298
|
+
RouterOptions<TRouteConfig, TRouterContext>,
|
|
299
|
+
'stringifySearch' | 'parseSearch' | 'context'
|
|
236
300
|
>
|
|
237
301
|
// Computed in this.update()
|
|
238
302
|
basepath: string
|
|
239
303
|
// Internal:
|
|
240
|
-
allRouteInfo: TAllRouteInfo
|
|
241
304
|
listeners: Listener[]
|
|
242
|
-
location: Location
|
|
305
|
+
location: Location<TAllRouteInfo['fullSearchSchema']>
|
|
243
306
|
navigateTimeout?: Timeout
|
|
244
307
|
nextAction?: 'push' | 'replace'
|
|
245
|
-
state: RouterState
|
|
308
|
+
state: RouterState<TAllRouteInfo['fullSearchSchema']>
|
|
246
309
|
routeTree: Route<TAllRouteInfo, RouteInfo>
|
|
247
310
|
routesById: RoutesById<TAllRouteInfo>
|
|
248
|
-
navigationPromise
|
|
249
|
-
removeActionQueue: { action: Action; actionState: ActionState }[]
|
|
311
|
+
navigationPromise?: Promise<void>
|
|
250
312
|
startedLoadingAt: number
|
|
251
313
|
resolveNavigation: () => void
|
|
252
314
|
subscribe: (listener: Listener) => () => void
|
|
315
|
+
reset: () => void
|
|
253
316
|
notify: () => void
|
|
254
317
|
mount: () => () => void
|
|
255
318
|
onFocus: () => void
|
|
256
|
-
update: <
|
|
257
|
-
|
|
258
|
-
|
|
319
|
+
update: <
|
|
320
|
+
TRouteConfig extends RouteConfig = RouteConfig,
|
|
321
|
+
TAllRouteInfo extends AnyAllRouteInfo = AllRouteInfo<TRouteConfig>,
|
|
322
|
+
TRouterContext = unknown,
|
|
323
|
+
>(
|
|
324
|
+
opts?: RouterOptions<TRouteConfig, TRouterContext>,
|
|
325
|
+
) => Router<TRouteConfig, TAllRouteInfo, TRouterContext>
|
|
259
326
|
|
|
260
327
|
buildNext: (opts: BuildNextOptions) => Location
|
|
261
328
|
cancelMatches: () => void
|
|
262
|
-
|
|
329
|
+
load: (next?: Location) => Promise<void>
|
|
263
330
|
matchCache: Record<string, MatchCacheEntry>
|
|
264
331
|
cleanMatchCache: () => void
|
|
265
332
|
getRoute: <TId extends keyof TAllRouteInfo['routeInfoById']>(
|
|
@@ -276,11 +343,13 @@ export interface Router<
|
|
|
276
343
|
) => RouteMatch[]
|
|
277
344
|
loadMatches: (
|
|
278
345
|
resolvedMatches: RouteMatch[],
|
|
279
|
-
loaderOpts?:
|
|
346
|
+
loaderOpts?:
|
|
280
347
|
| { preload: true; maxAge: number; gcMaxAge: number }
|
|
281
|
-
| { preload?: false; maxAge?: never; gcMaxAge?: never }
|
|
282
|
-
),
|
|
348
|
+
| { preload?: false; maxAge?: never; gcMaxAge?: never },
|
|
283
349
|
) => Promise<void>
|
|
350
|
+
loadMatchData: (
|
|
351
|
+
routeMatch: RouteMatch<any, any>,
|
|
352
|
+
) => Promise<Record<string, unknown>>
|
|
284
353
|
invalidateRoute: (opts: MatchLocation) => void
|
|
285
354
|
reload: () => Promise<void>
|
|
286
355
|
resolvePath: (from: string, path: string) => string
|
|
@@ -303,6 +372,8 @@ export interface Router<
|
|
|
303
372
|
>(
|
|
304
373
|
opts: LinkOptions<TAllRouteInfo, TFrom, TTo>,
|
|
305
374
|
) => LinkInfo
|
|
375
|
+
dehydrate: () => DehydratedRouter<TRouterContext>
|
|
376
|
+
hydrate: (dehydratedRouter: DehydratedRouter<TRouterContext>) => void
|
|
306
377
|
__: {
|
|
307
378
|
buildRouteTree: (
|
|
308
379
|
routeConfig: RouteConfig,
|
|
@@ -327,12 +398,26 @@ const isServer =
|
|
|
327
398
|
const createDefaultHistory = () =>
|
|
328
399
|
isServer ? createMemoryHistory() : createBrowserHistory()
|
|
329
400
|
|
|
401
|
+
function getInitialRouterState(): RouterState {
|
|
402
|
+
return {
|
|
403
|
+
status: 'idle',
|
|
404
|
+
location: null!,
|
|
405
|
+
matches: [],
|
|
406
|
+
actions: {},
|
|
407
|
+
loaders: {},
|
|
408
|
+
lastUpdated: Date.now(),
|
|
409
|
+
isFetching: false,
|
|
410
|
+
isPreloading: false,
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
330
414
|
export function createRouter<
|
|
331
415
|
TRouteConfig extends AnyRouteConfig = RouteConfig,
|
|
332
416
|
TAllRouteInfo extends AnyAllRouteInfo = AllRouteInfo<TRouteConfig>,
|
|
417
|
+
TRouterContext = unknown,
|
|
333
418
|
>(
|
|
334
|
-
userOptions?: RouterOptions<TRouteConfig>,
|
|
335
|
-
): Router<TRouteConfig, TAllRouteInfo> {
|
|
419
|
+
userOptions?: RouterOptions<TRouteConfig, TRouterContext>,
|
|
420
|
+
): Router<TRouteConfig, TAllRouteInfo, TRouterContext> {
|
|
336
421
|
const history = userOptions?.history || createDefaultHistory()
|
|
337
422
|
|
|
338
423
|
const originalOptions = {
|
|
@@ -340,36 +425,31 @@ export function createRouter<
|
|
|
340
425
|
defaultLoaderMaxAge: 0,
|
|
341
426
|
defaultPreloadMaxAge: 2000,
|
|
342
427
|
defaultPreloadDelay: 50,
|
|
428
|
+
context: undefined!,
|
|
343
429
|
...userOptions,
|
|
344
430
|
stringifySearch: userOptions?.stringifySearch ?? defaultStringifySearch,
|
|
345
431
|
parseSearch: userOptions?.parseSearch ?? defaultParseSearch,
|
|
346
432
|
}
|
|
347
433
|
|
|
348
|
-
let router: Router<TRouteConfig, TAllRouteInfo> = {
|
|
434
|
+
let router: Router<TRouteConfig, TAllRouteInfo, TRouterContext> = {
|
|
435
|
+
types: undefined!,
|
|
436
|
+
|
|
437
|
+
// public api
|
|
349
438
|
history,
|
|
350
439
|
options: originalOptions,
|
|
351
440
|
listeners: [],
|
|
352
|
-
removeActionQueue: [],
|
|
353
441
|
// Resolved after construction
|
|
354
442
|
basepath: '',
|
|
355
443
|
routeTree: undefined!,
|
|
356
444
|
routesById: {} as any,
|
|
357
445
|
location: undefined!,
|
|
358
|
-
allRouteInfo: undefined!,
|
|
359
446
|
//
|
|
360
|
-
navigationPromise: Promise.resolve(),
|
|
361
447
|
resolveNavigation: () => {},
|
|
362
448
|
matchCache: {},
|
|
363
|
-
state:
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
actions: {},
|
|
368
|
-
loaders: {},
|
|
369
|
-
loaderData: {} as any,
|
|
370
|
-
lastUpdated: Date.now(),
|
|
371
|
-
isFetching: false,
|
|
372
|
-
isPreloading: false,
|
|
449
|
+
state: getInitialRouterState(),
|
|
450
|
+
reset: () => {
|
|
451
|
+
router.state = getInitialRouterState()
|
|
452
|
+
router.notify()
|
|
373
453
|
},
|
|
374
454
|
startedLoadingAt: Date.now(),
|
|
375
455
|
subscribe: (listener: Listener): (() => void) => {
|
|
@@ -382,22 +462,86 @@ export function createRouter<
|
|
|
382
462
|
return router.routesById[id]
|
|
383
463
|
},
|
|
384
464
|
notify: (): void => {
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
isFetching
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
465
|
+
const isFetching =
|
|
466
|
+
router.state.status === 'loading' ||
|
|
467
|
+
router.state.matches.some((d) => d.isFetching)
|
|
468
|
+
|
|
469
|
+
const isPreloading = Object.values(router.matchCache).some(
|
|
470
|
+
(d) =>
|
|
471
|
+
d.match.isFetching &&
|
|
472
|
+
!router.state.matches.find((dd) => dd.matchId === d.match.matchId),
|
|
473
|
+
)
|
|
474
|
+
|
|
475
|
+
if (
|
|
476
|
+
router.state.isFetching !== isFetching ||
|
|
477
|
+
router.state.isPreloading !== isPreloading
|
|
478
|
+
) {
|
|
479
|
+
router.state = {
|
|
480
|
+
...router.state,
|
|
481
|
+
isFetching,
|
|
482
|
+
isPreloading,
|
|
483
|
+
}
|
|
395
484
|
}
|
|
396
485
|
|
|
397
486
|
cascadeLoaderData(router.state.matches)
|
|
398
487
|
router.listeners.forEach((listener) => listener(router))
|
|
399
488
|
},
|
|
400
489
|
|
|
490
|
+
dehydrate: () => {
|
|
491
|
+
return {
|
|
492
|
+
location: router.location,
|
|
493
|
+
state: {
|
|
494
|
+
...pick(router.state, [
|
|
495
|
+
'status',
|
|
496
|
+
'location',
|
|
497
|
+
'lastUpdated',
|
|
498
|
+
'location',
|
|
499
|
+
]),
|
|
500
|
+
matches: router.state.matches.map((match) =>
|
|
501
|
+
pick(match, [
|
|
502
|
+
'matchId',
|
|
503
|
+
'status',
|
|
504
|
+
'routeLoaderData',
|
|
505
|
+
'loaderData',
|
|
506
|
+
'isInvalid',
|
|
507
|
+
'invalidAt',
|
|
508
|
+
]),
|
|
509
|
+
),
|
|
510
|
+
},
|
|
511
|
+
context: router.options.context as TRouterContext,
|
|
512
|
+
}
|
|
513
|
+
},
|
|
514
|
+
|
|
515
|
+
hydrate: (dehydratedState) => {
|
|
516
|
+
// Update the location
|
|
517
|
+
router.location = dehydratedState.location
|
|
518
|
+
|
|
519
|
+
// Update the context
|
|
520
|
+
router.options.context = dehydratedState.context
|
|
521
|
+
|
|
522
|
+
// Match the routes
|
|
523
|
+
const matches = router.matchRoutes(router.location.pathname, {
|
|
524
|
+
strictParseParams: true,
|
|
525
|
+
})
|
|
526
|
+
|
|
527
|
+
matches.forEach((match, index) => {
|
|
528
|
+
const dehydratedMatch = dehydratedState.state.matches[index]
|
|
529
|
+
invariant(
|
|
530
|
+
dehydratedMatch,
|
|
531
|
+
'Oh no! Dehydrated route matches did not match the active state of the router 😬',
|
|
532
|
+
)
|
|
533
|
+
Object.assign(match, dehydratedMatch)
|
|
534
|
+
})
|
|
535
|
+
|
|
536
|
+
matches.forEach((match) => match.__.validate())
|
|
537
|
+
|
|
538
|
+
router.state = {
|
|
539
|
+
...router.state,
|
|
540
|
+
...dehydratedState,
|
|
541
|
+
matches,
|
|
542
|
+
}
|
|
543
|
+
},
|
|
544
|
+
|
|
401
545
|
mount: () => {
|
|
402
546
|
const next = router.__.buildLocation({
|
|
403
547
|
to: '.',
|
|
@@ -409,14 +553,14 @@ export function createRouter<
|
|
|
409
553
|
// to the current location. Otherwise, load the current location.
|
|
410
554
|
if (next.href !== router.location.href) {
|
|
411
555
|
router.__.commitLocation(next, true)
|
|
412
|
-
} else {
|
|
413
|
-
router.loadLocation()
|
|
414
556
|
}
|
|
415
557
|
|
|
416
|
-
|
|
417
|
-
router.
|
|
418
|
-
|
|
419
|
-
|
|
558
|
+
if (!router.state.matches.length) {
|
|
559
|
+
router.load()
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
const unsub = router.history.listen((event) => {
|
|
563
|
+
router.load(router.__.parseLocation(event.location, router.location))
|
|
420
564
|
})
|
|
421
565
|
|
|
422
566
|
// addEventListener does not exist in React Native, but window does
|
|
@@ -429,17 +573,28 @@ export function createRouter<
|
|
|
429
573
|
|
|
430
574
|
return () => {
|
|
431
575
|
unsub()
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
576
|
+
if (!isServer && window.removeEventListener) {
|
|
577
|
+
// Be sure to unsubscribe if a new handler is set
|
|
578
|
+
window.removeEventListener('visibilitychange', router.onFocus)
|
|
579
|
+
window.removeEventListener('focus', router.onFocus)
|
|
580
|
+
}
|
|
435
581
|
}
|
|
436
582
|
},
|
|
437
583
|
|
|
438
584
|
onFocus: () => {
|
|
439
|
-
router.
|
|
585
|
+
router.load()
|
|
440
586
|
},
|
|
441
587
|
|
|
442
588
|
update: (opts) => {
|
|
589
|
+
const newHistory = opts?.history !== router.history
|
|
590
|
+
if (!router.location || newHistory) {
|
|
591
|
+
if (opts?.history) {
|
|
592
|
+
router.history = opts.history
|
|
593
|
+
}
|
|
594
|
+
router.location = router.__.parseLocation(router.history.location)
|
|
595
|
+
router.state.location = router.location
|
|
596
|
+
}
|
|
597
|
+
|
|
443
598
|
Object.assign(router.options, opts)
|
|
444
599
|
|
|
445
600
|
const { basepath, routeConfig } = router.options
|
|
@@ -463,7 +618,7 @@ export function createRouter<
|
|
|
463
618
|
})
|
|
464
619
|
},
|
|
465
620
|
|
|
466
|
-
|
|
621
|
+
load: async (next?: Location) => {
|
|
467
622
|
const id = Math.random()
|
|
468
623
|
router.startedLoadingAt = id
|
|
469
624
|
|
|
@@ -472,40 +627,53 @@ export function createRouter<
|
|
|
472
627
|
router.location = next
|
|
473
628
|
}
|
|
474
629
|
|
|
475
|
-
// Clear out old actions
|
|
476
|
-
router.removeActionQueue.forEach(({ action, actionState }) => {
|
|
477
|
-
if (router.state.currentAction === actionState) {
|
|
478
|
-
router.state.currentAction = undefined
|
|
479
|
-
}
|
|
480
|
-
if (action.current === actionState) {
|
|
481
|
-
action.current = undefined
|
|
482
|
-
}
|
|
483
|
-
})
|
|
484
|
-
router.removeActionQueue = []
|
|
485
|
-
|
|
486
630
|
// Cancel any pending matches
|
|
487
631
|
router.cancelMatches()
|
|
488
632
|
|
|
489
633
|
// Match the routes
|
|
490
|
-
const matches = router.matchRoutes(location.pathname, {
|
|
634
|
+
const matches = router.matchRoutes(router.location.pathname, {
|
|
491
635
|
strictParseParams: true,
|
|
492
636
|
})
|
|
493
637
|
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
638
|
+
if (typeof document !== 'undefined') {
|
|
639
|
+
router.state = {
|
|
640
|
+
...router.state,
|
|
641
|
+
pending: {
|
|
642
|
+
matches: matches,
|
|
643
|
+
location: router.location,
|
|
644
|
+
},
|
|
645
|
+
status: 'loading',
|
|
646
|
+
}
|
|
647
|
+
} else {
|
|
648
|
+
router.state = {
|
|
649
|
+
...router.state,
|
|
497
650
|
matches: matches,
|
|
498
651
|
location: router.location,
|
|
499
|
-
|
|
500
|
-
|
|
652
|
+
status: 'loading',
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
// Check if each match middleware to see if the route can be accessed
|
|
657
|
+
try {
|
|
658
|
+
await Promise.all(
|
|
659
|
+
matches.map((match) =>
|
|
660
|
+
match.options.beforeLoad?.({
|
|
661
|
+
router: router as any,
|
|
662
|
+
match,
|
|
663
|
+
}),
|
|
664
|
+
),
|
|
665
|
+
)
|
|
666
|
+
} catch (err: any) {
|
|
667
|
+
if (err?.then) {
|
|
668
|
+
await new Promise(() => {})
|
|
669
|
+
}
|
|
670
|
+
throw err
|
|
501
671
|
}
|
|
502
672
|
|
|
503
673
|
router.notify()
|
|
504
674
|
|
|
505
675
|
// Load the matches
|
|
506
|
-
await router.loadMatches(matches
|
|
507
|
-
withPending: true,
|
|
508
|
-
})
|
|
676
|
+
await router.loadMatches(matches)
|
|
509
677
|
|
|
510
678
|
if (router.startedLoadingAt !== id) {
|
|
511
679
|
// Ignore side-effects of match loading
|
|
@@ -525,6 +693,10 @@ export function createRouter<
|
|
|
525
693
|
}
|
|
526
694
|
})
|
|
527
695
|
|
|
696
|
+
const entering = matches.filter((d) => {
|
|
697
|
+
return !previousMatches.find((dd) => dd.matchId === d.matchId)
|
|
698
|
+
})
|
|
699
|
+
|
|
528
700
|
const now = Date.now()
|
|
529
701
|
|
|
530
702
|
exiting.forEach((d) => {
|
|
@@ -532,6 +704,7 @@ export function createRouter<
|
|
|
532
704
|
params: d.params,
|
|
533
705
|
search: d.routeSearch,
|
|
534
706
|
})
|
|
707
|
+
|
|
535
708
|
// Clear idle error states when match leaves
|
|
536
709
|
if (d.status === 'error' && !d.isFetching) {
|
|
537
710
|
d.status = 'idle'
|
|
@@ -556,29 +729,27 @@ export function createRouter<
|
|
|
556
729
|
})
|
|
557
730
|
})
|
|
558
731
|
|
|
559
|
-
const entering = matches.filter((d) => {
|
|
560
|
-
return !previousMatches.find((dd) => dd.matchId === d.matchId)
|
|
561
|
-
})
|
|
562
|
-
|
|
563
732
|
entering.forEach((d) => {
|
|
564
|
-
d.__.onExit = d.options.
|
|
733
|
+
d.__.onExit = d.options.onLoaded?.({
|
|
565
734
|
params: d.params,
|
|
566
735
|
search: d.search,
|
|
567
736
|
})
|
|
568
737
|
delete router.matchCache[d.matchId]
|
|
569
738
|
})
|
|
570
739
|
|
|
571
|
-
if (matches.some((d) => d.status === 'loading')) {
|
|
572
|
-
router.notify()
|
|
573
|
-
await Promise.all(
|
|
574
|
-
matches.map((d) => d.__.loaderPromise || Promise.resolve()),
|
|
575
|
-
)
|
|
576
|
-
}
|
|
577
740
|
if (router.startedLoadingAt !== id) {
|
|
578
741
|
// Ignore side-effects of match loading
|
|
579
742
|
return
|
|
580
743
|
}
|
|
581
744
|
|
|
745
|
+
matches.forEach((match) => {
|
|
746
|
+
// Clear actions
|
|
747
|
+
if (match.action) {
|
|
748
|
+
match.action.current = undefined
|
|
749
|
+
match.action.submissions = []
|
|
750
|
+
}
|
|
751
|
+
})
|
|
752
|
+
|
|
582
753
|
router.state = {
|
|
583
754
|
...router.state,
|
|
584
755
|
location: router.location,
|
|
@@ -726,9 +897,10 @@ export function createRouter<
|
|
|
726
897
|
existingMatches.find((d) => d.matchId === matchId) ||
|
|
727
898
|
router.matchCache[matchId]?.match ||
|
|
728
899
|
createRouteMatch(router, foundRoute, {
|
|
900
|
+
parentMatch,
|
|
729
901
|
matchId,
|
|
730
902
|
params,
|
|
731
|
-
pathname: joinPaths([
|
|
903
|
+
pathname: joinPaths([router.basepath, interpolatedPath]),
|
|
732
904
|
})
|
|
733
905
|
|
|
734
906
|
matches.push(match)
|
|
@@ -754,12 +926,14 @@ export function createRouter<
|
|
|
754
926
|
match.__.validate()
|
|
755
927
|
match.load(loaderOpts)
|
|
756
928
|
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
929
|
+
const search = match.search as { __data?: any }
|
|
930
|
+
|
|
931
|
+
if (search.__data && search.__data.matchId !== match.matchId) {
|
|
932
|
+
return
|
|
933
|
+
}
|
|
760
934
|
|
|
935
|
+
if (match.__.loadPromise) {
|
|
761
936
|
// Wait for the first sign of activity from the match
|
|
762
|
-
// This might be completion, error, or a pending state
|
|
763
937
|
await match.__.loadPromise
|
|
764
938
|
}
|
|
765
939
|
})
|
|
@@ -769,6 +943,50 @@ export function createRouter<
|
|
|
769
943
|
await Promise.all(matchPromises)
|
|
770
944
|
},
|
|
771
945
|
|
|
946
|
+
loadMatchData: async (routeMatch) => {
|
|
947
|
+
if (isServer || !router.options.useServerData) {
|
|
948
|
+
return (
|
|
949
|
+
(await routeMatch.options.loader?.({
|
|
950
|
+
// parentLoaderPromise: routeMatch.parentMatch?.__.dataPromise,
|
|
951
|
+
params: routeMatch.params,
|
|
952
|
+
search: routeMatch.routeSearch,
|
|
953
|
+
signal: routeMatch.__.abortController.signal,
|
|
954
|
+
})) ?? {}
|
|
955
|
+
)
|
|
956
|
+
} else {
|
|
957
|
+
const next = router.buildNext({
|
|
958
|
+
to: '.',
|
|
959
|
+
search: (d: any) => ({
|
|
960
|
+
...(d ?? {}),
|
|
961
|
+
__data: {
|
|
962
|
+
matchId: routeMatch.matchId,
|
|
963
|
+
},
|
|
964
|
+
}),
|
|
965
|
+
})
|
|
966
|
+
|
|
967
|
+
// Refresh:
|
|
968
|
+
// '/dashboard'
|
|
969
|
+
// '/dashboard/invoices/'
|
|
970
|
+
// '/dashboard/invoices/123'
|
|
971
|
+
|
|
972
|
+
// New:
|
|
973
|
+
// '/dashboard/invoices/456'
|
|
974
|
+
|
|
975
|
+
// TODO: batch requests when possible
|
|
976
|
+
|
|
977
|
+
const res = await fetch(next.href, {
|
|
978
|
+
method: 'GET',
|
|
979
|
+
// signal: routeMatch.__.abortController.signal,
|
|
980
|
+
})
|
|
981
|
+
|
|
982
|
+
if (res.ok) {
|
|
983
|
+
return res.json()
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
throw new Error('Failed to fetch match data')
|
|
987
|
+
}
|
|
988
|
+
},
|
|
989
|
+
|
|
772
990
|
invalidateRoute: (opts: MatchLocation) => {
|
|
773
991
|
const next = router.buildNext(opts)
|
|
774
992
|
const unloadedMatchIds = router
|
|
@@ -1006,17 +1224,11 @@ export function createRouter<
|
|
|
1006
1224
|
buildRouteTree: (rootRouteConfig: RouteConfig) => {
|
|
1007
1225
|
const recurseRoutes = (
|
|
1008
1226
|
routeConfigs: RouteConfig[],
|
|
1009
|
-
parent?: Route<TAllRouteInfo, any>,
|
|
1010
|
-
): Route<TAllRouteInfo, any>[] => {
|
|
1227
|
+
parent?: Route<TAllRouteInfo, any, any>,
|
|
1228
|
+
): Route<TAllRouteInfo, any, any>[] => {
|
|
1011
1229
|
return routeConfigs.map((routeConfig) => {
|
|
1012
1230
|
const routeOptions = routeConfig.options
|
|
1013
1231
|
const route = createRoute(routeConfig, routeOptions, parent, router)
|
|
1014
|
-
|
|
1015
|
-
// {
|
|
1016
|
-
// pendingMs: routeOptions.pendingMs ?? router.defaultPendingMs,
|
|
1017
|
-
// pendingMinMs: routeOptions.pendingMinMs ?? router.defaultPendingMinMs,
|
|
1018
|
-
// }
|
|
1019
|
-
|
|
1020
1232
|
const existingRoute = (router.routesById as any)[route.routeId]
|
|
1021
1233
|
|
|
1022
1234
|
if (existingRoute) {
|
|
@@ -1182,6 +1394,7 @@ export function createRouter<
|
|
|
1182
1394
|
},
|
|
1183
1395
|
{
|
|
1184
1396
|
id,
|
|
1397
|
+
...next.state,
|
|
1185
1398
|
},
|
|
1186
1399
|
)
|
|
1187
1400
|
} else {
|
|
@@ -1203,6 +1416,7 @@ export function createRouter<
|
|
|
1203
1416
|
router.resolveNavigation = () => {
|
|
1204
1417
|
previousNavigationResolve()
|
|
1205
1418
|
resolve()
|
|
1419
|
+
delete router.navigationPromise
|
|
1206
1420
|
}
|
|
1207
1421
|
})
|
|
1208
1422
|
|
|
@@ -1211,9 +1425,6 @@ export function createRouter<
|
|
|
1211
1425
|
},
|
|
1212
1426
|
}
|
|
1213
1427
|
|
|
1214
|
-
router.location = router.__.parseLocation(history.location)
|
|
1215
|
-
router.state.location = router.location
|
|
1216
|
-
|
|
1217
1428
|
router.update(userOptions)
|
|
1218
1429
|
|
|
1219
1430
|
// Allow frameworks to hook into the router creation
|
|
@@ -1225,3 +1436,16 @@ export function createRouter<
|
|
|
1225
1436
|
function isCtrlEvent(e: MouseEvent) {
|
|
1226
1437
|
return !!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey)
|
|
1227
1438
|
}
|
|
1439
|
+
|
|
1440
|
+
function cascadeLoaderData(matches: RouteMatch<any, any>[]) {
|
|
1441
|
+
matches.forEach((match, index) => {
|
|
1442
|
+
const parent = matches[index - 1]
|
|
1443
|
+
|
|
1444
|
+
if (parent) {
|
|
1445
|
+
match.loaderData = replaceEqualDeep(match.loaderData, {
|
|
1446
|
+
...parent.loaderData,
|
|
1447
|
+
...match.routeLoaderData,
|
|
1448
|
+
})
|
|
1449
|
+
}
|
|
1450
|
+
})
|
|
1451
|
+
}
|