@tanstack/router-core 0.0.1-beta.2 → 0.0.1-beta.23
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/packages/router-core/src/index.js +1 -1
- package/build/cjs/packages/router-core/src/path.js +1 -4
- package/build/cjs/packages/router-core/src/path.js.map +1 -1
- package/build/cjs/packages/router-core/src/qss.js +1 -0
- package/build/cjs/packages/router-core/src/qss.js.map +1 -1
- package/build/cjs/packages/router-core/src/route.js +9 -23
- package/build/cjs/packages/router-core/src/route.js.map +1 -1
- package/build/cjs/packages/router-core/src/routeConfig.js.map +1 -1
- package/build/cjs/packages/router-core/src/routeMatch.js +75 -121
- package/build/cjs/packages/router-core/src/routeMatch.js.map +1 -1
- package/build/cjs/packages/router-core/src/router.js +183 -89
- package/build/cjs/packages/router-core/src/router.js.map +1 -1
- package/build/cjs/packages/router-core/src/utils.js +7 -6
- package/build/cjs/packages/router-core/src/utils.js.map +1 -1
- package/build/esm/index.js +271 -237
- package/build/esm/index.js.map +1 -1
- package/build/stats-html.html +1 -1
- package/build/stats-react.json +139 -152
- package/build/types/index.d.ts +199 -191
- package/build/umd/index.development.js +271 -237
- 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 +2 -1
- package/src/frameworks.ts +1 -2
- package/src/index.ts +0 -1
- package/src/link.ts +3 -1
- package/src/path.ts +0 -4
- package/src/qss.ts +1 -0
- package/src/route.ts +10 -26
- package/src/routeConfig.ts +30 -21
- package/src/routeInfo.ts +13 -3
- package/src/routeMatch.ts +94 -156
- package/src/router.ts +269 -109
- package/src/utils.ts +12 -5
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,
|
|
@@ -89,23 +90,23 @@ export interface RouterOptions<TRouteConfig extends AnyRouteConfig> {
|
|
|
89
90
|
defaultPreloadMaxAge?: number
|
|
90
91
|
defaultPreloadGcMaxAge?: number
|
|
91
92
|
defaultPreloadDelay?: number
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
defaultCatchElement?: GetFrameworkGeneric<'Element'>
|
|
96
|
-
defaultPendingElement?: GetFrameworkGeneric<'Element'>
|
|
97
|
-
defaultPendingMs?: number
|
|
98
|
-
defaultPendingMinMs?: number
|
|
93
|
+
defaultComponent?: GetFrameworkGeneric<'Component'>
|
|
94
|
+
defaultErrorComponent?: GetFrameworkGeneric<'Component'>
|
|
95
|
+
defaultPendingComponent?: GetFrameworkGeneric<'Component'>
|
|
99
96
|
defaultLoaderMaxAge?: number
|
|
100
97
|
defaultLoaderGcMaxAge?: number
|
|
101
98
|
caseSensitive?: boolean
|
|
102
99
|
routeConfig?: TRouteConfig
|
|
103
100
|
basepath?: string
|
|
101
|
+
useServerData?: boolean
|
|
104
102
|
createRouter?: (router: Router<any, any>) => void
|
|
105
103
|
createRoute?: (opts: { route: AnyRoute; router: Router<any, any> }) => void
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
) => Promise<GetFrameworkGeneric<'
|
|
104
|
+
loadComponent?: (
|
|
105
|
+
component: GetFrameworkGeneric<'Component'>,
|
|
106
|
+
) => Promise<GetFrameworkGeneric<'Component'>>
|
|
107
|
+
// renderComponent?: (
|
|
108
|
+
// component: GetFrameworkGeneric<'Component'>,
|
|
109
|
+
// ) => GetFrameworkGeneric<'Element'>
|
|
109
110
|
}
|
|
110
111
|
|
|
111
112
|
export interface Action<
|
|
@@ -113,10 +114,13 @@ export interface Action<
|
|
|
113
114
|
TResponse = unknown,
|
|
114
115
|
// TError = unknown,
|
|
115
116
|
> {
|
|
116
|
-
submit: (
|
|
117
|
+
submit: (
|
|
118
|
+
submission?: TPayload,
|
|
119
|
+
actionOpts?: { invalidate?: boolean; multi?: boolean },
|
|
120
|
+
) => Promise<TResponse>
|
|
117
121
|
current?: ActionState<TPayload, TResponse>
|
|
118
122
|
latest?: ActionState<TPayload, TResponse>
|
|
119
|
-
|
|
123
|
+
submissions: ActionState<TPayload, TResponse>[]
|
|
120
124
|
}
|
|
121
125
|
|
|
122
126
|
export interface ActionState<
|
|
@@ -127,6 +131,7 @@ export interface ActionState<
|
|
|
127
131
|
submittedAt: number
|
|
128
132
|
status: 'idle' | 'pending' | 'success' | 'error'
|
|
129
133
|
submission: TPayload
|
|
134
|
+
isMulti: boolean
|
|
130
135
|
data?: TResponse
|
|
131
136
|
error?: unknown
|
|
132
137
|
}
|
|
@@ -160,21 +165,21 @@ export interface Loader<
|
|
|
160
165
|
}
|
|
161
166
|
|
|
162
167
|
export interface LoaderState<
|
|
163
|
-
TFullSearchSchema =
|
|
164
|
-
TAllParams =
|
|
168
|
+
TFullSearchSchema extends AnySearchSchema = {},
|
|
169
|
+
TAllParams extends AnyPathParams = {},
|
|
165
170
|
> {
|
|
166
171
|
loadedAt: number
|
|
167
172
|
loaderContext: LoaderContext<TFullSearchSchema, TAllParams>
|
|
168
173
|
}
|
|
169
174
|
|
|
170
|
-
export interface RouterState
|
|
175
|
+
export interface RouterState<
|
|
176
|
+
TSearchObj extends AnySearchSchema = {},
|
|
177
|
+
TState extends LocationState = LocationState,
|
|
178
|
+
> {
|
|
171
179
|
status: 'idle' | 'loading'
|
|
172
|
-
location: Location
|
|
180
|
+
location: Location<TSearchObj, TState>
|
|
173
181
|
matches: RouteMatch[]
|
|
174
182
|
lastUpdated: number
|
|
175
|
-
loaderData: unknown
|
|
176
|
-
currentAction?: ActionState
|
|
177
|
-
latestAction?: ActionState
|
|
178
183
|
actions: Record<string, Action>
|
|
179
184
|
loaders: Record<string, Loader>
|
|
180
185
|
pending?: PendingState
|
|
@@ -196,6 +201,7 @@ export interface BuildNextOptions {
|
|
|
196
201
|
params?: true | Updater<Record<string, any>>
|
|
197
202
|
search?: true | Updater<unknown>
|
|
198
203
|
hash?: true | Updater<string>
|
|
204
|
+
state?: LocationState
|
|
199
205
|
key?: string
|
|
200
206
|
from?: string
|
|
201
207
|
fromCurrent?: boolean
|
|
@@ -225,10 +231,35 @@ type LinkCurrentTargetElement = {
|
|
|
225
231
|
preloadTimeout?: null | ReturnType<typeof setTimeout>
|
|
226
232
|
}
|
|
227
233
|
|
|
234
|
+
interface DehydratedRouterState
|
|
235
|
+
extends Pick<RouterState, 'status' | 'location' | 'lastUpdated'> {
|
|
236
|
+
matches: DehydratedRouteMatch[]
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
interface DehydratedRouteMatch
|
|
240
|
+
extends Pick<
|
|
241
|
+
RouteMatch<any, any>,
|
|
242
|
+
| 'matchId'
|
|
243
|
+
| 'status'
|
|
244
|
+
| 'routeLoaderData'
|
|
245
|
+
| 'loaderData'
|
|
246
|
+
| 'isInvalid'
|
|
247
|
+
| 'invalidAt'
|
|
248
|
+
> {}
|
|
249
|
+
|
|
250
|
+
export interface RouterContext {}
|
|
251
|
+
|
|
228
252
|
export interface Router<
|
|
229
253
|
TRouteConfig extends AnyRouteConfig = RouteConfig,
|
|
230
254
|
TAllRouteInfo extends AnyAllRouteInfo = AllRouteInfo<TRouteConfig>,
|
|
231
255
|
> {
|
|
256
|
+
types: {
|
|
257
|
+
// Super secret internal stuff
|
|
258
|
+
RouteConfig: TRouteConfig
|
|
259
|
+
AllRouteInfo: TAllRouteInfo
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Public API
|
|
232
263
|
history: BrowserHistory | MemoryHistory | HashHistory
|
|
233
264
|
options: PickAsRequired<
|
|
234
265
|
RouterOptions<TRouteConfig>,
|
|
@@ -237,19 +268,19 @@ export interface Router<
|
|
|
237
268
|
// Computed in this.update()
|
|
238
269
|
basepath: string
|
|
239
270
|
// Internal:
|
|
240
|
-
|
|
271
|
+
context: RouterContext
|
|
241
272
|
listeners: Listener[]
|
|
242
|
-
location: Location
|
|
273
|
+
location: Location<TAllRouteInfo['fullSearchSchema']>
|
|
243
274
|
navigateTimeout?: Timeout
|
|
244
275
|
nextAction?: 'push' | 'replace'
|
|
245
|
-
state: RouterState
|
|
276
|
+
state: RouterState<TAllRouteInfo['fullSearchSchema']>
|
|
246
277
|
routeTree: Route<TAllRouteInfo, RouteInfo>
|
|
247
278
|
routesById: RoutesById<TAllRouteInfo>
|
|
248
279
|
navigationPromise: Promise<void>
|
|
249
|
-
removeActionQueue: { action: Action; actionState: ActionState }[]
|
|
250
280
|
startedLoadingAt: number
|
|
251
281
|
resolveNavigation: () => void
|
|
252
282
|
subscribe: (listener: Listener) => () => void
|
|
283
|
+
reset: () => void
|
|
253
284
|
notify: () => void
|
|
254
285
|
mount: () => () => void
|
|
255
286
|
onFocus: () => void
|
|
@@ -259,7 +290,7 @@ export interface Router<
|
|
|
259
290
|
|
|
260
291
|
buildNext: (opts: BuildNextOptions) => Location
|
|
261
292
|
cancelMatches: () => void
|
|
262
|
-
|
|
293
|
+
load: (next?: Location) => Promise<void>
|
|
263
294
|
matchCache: Record<string, MatchCacheEntry>
|
|
264
295
|
cleanMatchCache: () => void
|
|
265
296
|
getRoute: <TId extends keyof TAllRouteInfo['routeInfoById']>(
|
|
@@ -276,11 +307,13 @@ export interface Router<
|
|
|
276
307
|
) => RouteMatch[]
|
|
277
308
|
loadMatches: (
|
|
278
309
|
resolvedMatches: RouteMatch[],
|
|
279
|
-
loaderOpts?:
|
|
310
|
+
loaderOpts?:
|
|
280
311
|
| { preload: true; maxAge: number; gcMaxAge: number }
|
|
281
|
-
| { preload?: false; maxAge?: never; gcMaxAge?: never }
|
|
282
|
-
),
|
|
312
|
+
| { preload?: false; maxAge?: never; gcMaxAge?: never },
|
|
283
313
|
) => Promise<void>
|
|
314
|
+
loadMatchData: (
|
|
315
|
+
routeMatch: RouteMatch<any, any>,
|
|
316
|
+
) => Promise<Record<string, unknown>>
|
|
284
317
|
invalidateRoute: (opts: MatchLocation) => void
|
|
285
318
|
reload: () => Promise<void>
|
|
286
319
|
resolvePath: (from: string, path: string) => string
|
|
@@ -303,6 +336,8 @@ export interface Router<
|
|
|
303
336
|
>(
|
|
304
337
|
opts: LinkOptions<TAllRouteInfo, TFrom, TTo>,
|
|
305
338
|
) => LinkInfo
|
|
339
|
+
dehydrateState: () => DehydratedRouterState
|
|
340
|
+
hydrateState: (state: DehydratedRouterState) => void
|
|
306
341
|
__: {
|
|
307
342
|
buildRouteTree: (
|
|
308
343
|
routeConfig: RouteConfig,
|
|
@@ -320,13 +355,25 @@ export interface Router<
|
|
|
320
355
|
}
|
|
321
356
|
|
|
322
357
|
// Detect if we're in the DOM
|
|
323
|
-
const isServer =
|
|
324
|
-
typeof window === 'undefined' || !window.document?.createElement
|
|
325
|
-
)
|
|
358
|
+
const isServer =
|
|
359
|
+
typeof window === 'undefined' || !window.document?.createElement
|
|
326
360
|
|
|
327
361
|
// This is the default history object if none is defined
|
|
328
362
|
const createDefaultHistory = () =>
|
|
329
|
-
|
|
363
|
+
isServer ? createMemoryHistory() : createBrowserHistory()
|
|
364
|
+
|
|
365
|
+
function getInitialRouterState(): RouterState {
|
|
366
|
+
return {
|
|
367
|
+
status: 'idle',
|
|
368
|
+
location: null!,
|
|
369
|
+
matches: [],
|
|
370
|
+
actions: {},
|
|
371
|
+
loaders: {},
|
|
372
|
+
lastUpdated: Date.now(),
|
|
373
|
+
isFetching: false,
|
|
374
|
+
isPreloading: false,
|
|
375
|
+
}
|
|
376
|
+
}
|
|
330
377
|
|
|
331
378
|
export function createRouter<
|
|
332
379
|
TRouteConfig extends AnyRouteConfig = RouteConfig,
|
|
@@ -347,30 +394,26 @@ export function createRouter<
|
|
|
347
394
|
}
|
|
348
395
|
|
|
349
396
|
let router: Router<TRouteConfig, TAllRouteInfo> = {
|
|
397
|
+
types: undefined!,
|
|
398
|
+
|
|
399
|
+
// public api
|
|
350
400
|
history,
|
|
351
401
|
options: originalOptions,
|
|
352
402
|
listeners: [],
|
|
353
|
-
removeActionQueue: [],
|
|
354
403
|
// Resolved after construction
|
|
404
|
+
context: {},
|
|
355
405
|
basepath: '',
|
|
356
406
|
routeTree: undefined!,
|
|
357
407
|
routesById: {} as any,
|
|
358
408
|
location: undefined!,
|
|
359
|
-
allRouteInfo: undefined!,
|
|
360
409
|
//
|
|
361
410
|
navigationPromise: Promise.resolve(),
|
|
362
411
|
resolveNavigation: () => {},
|
|
363
412
|
matchCache: {},
|
|
364
|
-
state:
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
actions: {},
|
|
369
|
-
loaders: {},
|
|
370
|
-
loaderData: {} as any,
|
|
371
|
-
lastUpdated: Date.now(),
|
|
372
|
-
isFetching: false,
|
|
373
|
-
isPreloading: false,
|
|
413
|
+
state: getInitialRouterState(),
|
|
414
|
+
reset: () => {
|
|
415
|
+
router.state = getInitialRouterState()
|
|
416
|
+
router.notify()
|
|
374
417
|
},
|
|
375
418
|
startedLoadingAt: Date.now(),
|
|
376
419
|
subscribe: (listener: Listener): (() => void) => {
|
|
@@ -383,22 +426,71 @@ export function createRouter<
|
|
|
383
426
|
return router.routesById[id]
|
|
384
427
|
},
|
|
385
428
|
notify: (): void => {
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
isFetching
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
429
|
+
const isFetching =
|
|
430
|
+
router.state.status === 'loading' ||
|
|
431
|
+
router.state.matches.some((d) => d.isFetching)
|
|
432
|
+
|
|
433
|
+
const isPreloading = Object.values(router.matchCache).some(
|
|
434
|
+
(d) =>
|
|
435
|
+
d.match.isFetching &&
|
|
436
|
+
!router.state.matches.find((dd) => dd.matchId === d.match.matchId),
|
|
437
|
+
)
|
|
438
|
+
|
|
439
|
+
if (
|
|
440
|
+
router.state.isFetching !== isFetching ||
|
|
441
|
+
router.state.isPreloading !== isPreloading
|
|
442
|
+
) {
|
|
443
|
+
router.state = {
|
|
444
|
+
...router.state,
|
|
445
|
+
isFetching,
|
|
446
|
+
isPreloading,
|
|
447
|
+
}
|
|
396
448
|
}
|
|
397
449
|
|
|
398
450
|
cascadeLoaderData(router.state.matches)
|
|
399
451
|
router.listeners.forEach((listener) => listener(router))
|
|
400
452
|
},
|
|
401
453
|
|
|
454
|
+
dehydrateState: () => {
|
|
455
|
+
return {
|
|
456
|
+
...pick(router.state, ['status', 'location', 'lastUpdated']),
|
|
457
|
+
matches: router.state.matches.map((match) =>
|
|
458
|
+
pick(match, [
|
|
459
|
+
'matchId',
|
|
460
|
+
'status',
|
|
461
|
+
'routeLoaderData',
|
|
462
|
+
'loaderData',
|
|
463
|
+
'isInvalid',
|
|
464
|
+
'invalidAt',
|
|
465
|
+
]),
|
|
466
|
+
),
|
|
467
|
+
}
|
|
468
|
+
},
|
|
469
|
+
|
|
470
|
+
hydrateState: (dehydratedState) => {
|
|
471
|
+
// Match the routes
|
|
472
|
+
const matches = router.matchRoutes(router.location.pathname, {
|
|
473
|
+
strictParseParams: true,
|
|
474
|
+
})
|
|
475
|
+
|
|
476
|
+
matches.forEach((match, index) => {
|
|
477
|
+
const dehydratedMatch = dehydratedState.matches[index]
|
|
478
|
+
invariant(
|
|
479
|
+
dehydratedMatch,
|
|
480
|
+
'Oh no! Dehydrated route matches did not match the active state of the router 😬',
|
|
481
|
+
)
|
|
482
|
+
Object.assign(match, dehydratedMatch)
|
|
483
|
+
})
|
|
484
|
+
|
|
485
|
+
matches.forEach((match) => match.__.validate())
|
|
486
|
+
|
|
487
|
+
router.state = {
|
|
488
|
+
...router.state,
|
|
489
|
+
...dehydratedState,
|
|
490
|
+
matches,
|
|
491
|
+
}
|
|
492
|
+
},
|
|
493
|
+
|
|
402
494
|
mount: () => {
|
|
403
495
|
const next = router.__.buildLocation({
|
|
404
496
|
to: '.',
|
|
@@ -410,14 +502,14 @@ export function createRouter<
|
|
|
410
502
|
// to the current location. Otherwise, load the current location.
|
|
411
503
|
if (next.href !== router.location.href) {
|
|
412
504
|
router.__.commitLocation(next, true)
|
|
413
|
-
} else {
|
|
414
|
-
router.loadLocation()
|
|
415
505
|
}
|
|
416
506
|
|
|
417
|
-
|
|
418
|
-
router.
|
|
419
|
-
|
|
420
|
-
|
|
507
|
+
if (!router.state.matches.length) {
|
|
508
|
+
router.load()
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
const unsub = router.history.listen((event) => {
|
|
512
|
+
router.load(router.__.parseLocation(event.location, router.location))
|
|
421
513
|
})
|
|
422
514
|
|
|
423
515
|
// addEventListener does not exist in React Native, but window does
|
|
@@ -430,17 +522,28 @@ export function createRouter<
|
|
|
430
522
|
|
|
431
523
|
return () => {
|
|
432
524
|
unsub()
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
525
|
+
if (!isServer && window.removeEventListener) {
|
|
526
|
+
// Be sure to unsubscribe if a new handler is set
|
|
527
|
+
window.removeEventListener('visibilitychange', router.onFocus)
|
|
528
|
+
window.removeEventListener('focus', router.onFocus)
|
|
529
|
+
}
|
|
436
530
|
}
|
|
437
531
|
},
|
|
438
532
|
|
|
439
533
|
onFocus: () => {
|
|
440
|
-
router.
|
|
534
|
+
router.load()
|
|
441
535
|
},
|
|
442
536
|
|
|
443
537
|
update: (opts) => {
|
|
538
|
+
const newHistory = opts?.history !== router.history
|
|
539
|
+
if (!router.location || newHistory) {
|
|
540
|
+
if (opts?.history) {
|
|
541
|
+
router.history = opts.history
|
|
542
|
+
}
|
|
543
|
+
router.location = router.__.parseLocation(router.history.location)
|
|
544
|
+
router.state.location = router.location
|
|
545
|
+
}
|
|
546
|
+
|
|
444
547
|
Object.assign(router.options, opts)
|
|
445
548
|
|
|
446
549
|
const { basepath, routeConfig } = router.options
|
|
@@ -464,7 +567,7 @@ export function createRouter<
|
|
|
464
567
|
})
|
|
465
568
|
},
|
|
466
569
|
|
|
467
|
-
|
|
570
|
+
load: async (next?: Location) => {
|
|
468
571
|
const id = Math.random()
|
|
469
572
|
router.startedLoadingAt = id
|
|
470
573
|
|
|
@@ -473,40 +576,52 @@ export function createRouter<
|
|
|
473
576
|
router.location = next
|
|
474
577
|
}
|
|
475
578
|
|
|
476
|
-
// Clear out old actions
|
|
477
|
-
router.removeActionQueue.forEach(({ action, actionState }) => {
|
|
478
|
-
if (router.state.currentAction === actionState) {
|
|
479
|
-
router.state.currentAction = undefined
|
|
480
|
-
}
|
|
481
|
-
if (action.current === actionState) {
|
|
482
|
-
action.current = undefined
|
|
483
|
-
}
|
|
484
|
-
})
|
|
485
|
-
router.removeActionQueue = []
|
|
486
|
-
|
|
487
579
|
// Cancel any pending matches
|
|
488
580
|
router.cancelMatches()
|
|
489
581
|
|
|
490
582
|
// Match the routes
|
|
491
|
-
const matches = router.matchRoutes(location.pathname, {
|
|
583
|
+
const matches = router.matchRoutes(router.location.pathname, {
|
|
492
584
|
strictParseParams: true,
|
|
493
585
|
})
|
|
494
586
|
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
587
|
+
// Check if each match middleware to see if the route can be accessed
|
|
588
|
+
try {
|
|
589
|
+
await Promise.all(
|
|
590
|
+
matches.map((match) =>
|
|
591
|
+
match.options.beforeLoad?.({
|
|
592
|
+
context: router.context,
|
|
593
|
+
}),
|
|
594
|
+
),
|
|
595
|
+
)
|
|
596
|
+
} catch (err: any) {
|
|
597
|
+
if (err?.then) {
|
|
598
|
+
await new Promise(() => {})
|
|
599
|
+
}
|
|
600
|
+
throw err
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
if (typeof document !== 'undefined') {
|
|
604
|
+
router.state = {
|
|
605
|
+
...router.state,
|
|
606
|
+
pending: {
|
|
607
|
+
matches: matches,
|
|
608
|
+
location: router.location,
|
|
609
|
+
},
|
|
610
|
+
status: 'loading',
|
|
611
|
+
}
|
|
612
|
+
} else {
|
|
613
|
+
router.state = {
|
|
614
|
+
...router.state,
|
|
498
615
|
matches: matches,
|
|
499
616
|
location: router.location,
|
|
500
|
-
|
|
501
|
-
|
|
617
|
+
status: 'loading',
|
|
618
|
+
}
|
|
502
619
|
}
|
|
503
620
|
|
|
504
621
|
router.notify()
|
|
505
622
|
|
|
506
623
|
// Load the matches
|
|
507
|
-
await router.loadMatches(matches
|
|
508
|
-
withPending: true,
|
|
509
|
-
})
|
|
624
|
+
await router.loadMatches(matches)
|
|
510
625
|
|
|
511
626
|
if (router.startedLoadingAt !== id) {
|
|
512
627
|
// Ignore side-effects of match loading
|
|
@@ -526,6 +641,10 @@ export function createRouter<
|
|
|
526
641
|
}
|
|
527
642
|
})
|
|
528
643
|
|
|
644
|
+
const entering = matches.filter((d) => {
|
|
645
|
+
return !previousMatches.find((dd) => dd.matchId === d.matchId)
|
|
646
|
+
})
|
|
647
|
+
|
|
529
648
|
const now = Date.now()
|
|
530
649
|
|
|
531
650
|
exiting.forEach((d) => {
|
|
@@ -533,6 +652,7 @@ export function createRouter<
|
|
|
533
652
|
params: d.params,
|
|
534
653
|
search: d.routeSearch,
|
|
535
654
|
})
|
|
655
|
+
|
|
536
656
|
// Clear idle error states when match leaves
|
|
537
657
|
if (d.status === 'error' && !d.isFetching) {
|
|
538
658
|
d.status = 'idle'
|
|
@@ -557,29 +677,27 @@ export function createRouter<
|
|
|
557
677
|
})
|
|
558
678
|
})
|
|
559
679
|
|
|
560
|
-
const entering = matches.filter((d) => {
|
|
561
|
-
return !previousMatches.find((dd) => dd.matchId === d.matchId)
|
|
562
|
-
})
|
|
563
|
-
|
|
564
680
|
entering.forEach((d) => {
|
|
565
|
-
d.__.onExit = d.options.
|
|
681
|
+
d.__.onExit = d.options.onLoaded?.({
|
|
566
682
|
params: d.params,
|
|
567
683
|
search: d.search,
|
|
568
684
|
})
|
|
569
685
|
delete router.matchCache[d.matchId]
|
|
570
686
|
})
|
|
571
687
|
|
|
572
|
-
if (matches.some((d) => d.status === 'loading')) {
|
|
573
|
-
router.notify()
|
|
574
|
-
await Promise.all(
|
|
575
|
-
matches.map((d) => d.__.loaderPromise || Promise.resolve()),
|
|
576
|
-
)
|
|
577
|
-
}
|
|
578
688
|
if (router.startedLoadingAt !== id) {
|
|
579
689
|
// Ignore side-effects of match loading
|
|
580
690
|
return
|
|
581
691
|
}
|
|
582
692
|
|
|
693
|
+
matches.forEach((match) => {
|
|
694
|
+
// Clear actions
|
|
695
|
+
if (match.action) {
|
|
696
|
+
match.action.current = undefined
|
|
697
|
+
match.action.submissions = []
|
|
698
|
+
}
|
|
699
|
+
})
|
|
700
|
+
|
|
583
701
|
router.state = {
|
|
584
702
|
...router.state,
|
|
585
703
|
location: router.location,
|
|
@@ -727,6 +845,7 @@ export function createRouter<
|
|
|
727
845
|
existingMatches.find((d) => d.matchId === matchId) ||
|
|
728
846
|
router.matchCache[matchId]?.match ||
|
|
729
847
|
createRouteMatch(router, foundRoute, {
|
|
848
|
+
parentMatch,
|
|
730
849
|
matchId,
|
|
731
850
|
params,
|
|
732
851
|
pathname: joinPaths([pathname, interpolatedPath]),
|
|
@@ -755,12 +874,14 @@ export function createRouter<
|
|
|
755
874
|
match.__.validate()
|
|
756
875
|
match.load(loaderOpts)
|
|
757
876
|
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
877
|
+
const search = match.search as { __data?: any }
|
|
878
|
+
|
|
879
|
+
if (search.__data && search.__data.matchId !== match.matchId) {
|
|
880
|
+
return
|
|
881
|
+
}
|
|
761
882
|
|
|
883
|
+
if (match.__.loadPromise) {
|
|
762
884
|
// Wait for the first sign of activity from the match
|
|
763
|
-
// This might be completion, error, or a pending state
|
|
764
885
|
await match.__.loadPromise
|
|
765
886
|
}
|
|
766
887
|
})
|
|
@@ -770,6 +891,40 @@ export function createRouter<
|
|
|
770
891
|
await Promise.all(matchPromises)
|
|
771
892
|
},
|
|
772
893
|
|
|
894
|
+
loadMatchData: async (routeMatch) => {
|
|
895
|
+
if (isServer || !router.options.useServerData) {
|
|
896
|
+
return (
|
|
897
|
+
(await routeMatch.options.loader?.({
|
|
898
|
+
// parentLoaderPromise: routeMatch.parentMatch?.__.dataPromise,
|
|
899
|
+
params: routeMatch.params,
|
|
900
|
+
search: routeMatch.routeSearch,
|
|
901
|
+
signal: routeMatch.__.abortController.signal,
|
|
902
|
+
})) ?? {}
|
|
903
|
+
)
|
|
904
|
+
} else {
|
|
905
|
+
const next = router.buildNext({
|
|
906
|
+
to: '.',
|
|
907
|
+
search: (d: any) => ({
|
|
908
|
+
...(d ?? {}),
|
|
909
|
+
__data: {
|
|
910
|
+
matchId: routeMatch.matchId,
|
|
911
|
+
},
|
|
912
|
+
}),
|
|
913
|
+
})
|
|
914
|
+
|
|
915
|
+
const res = await fetch(next.href, {
|
|
916
|
+
method: 'GET',
|
|
917
|
+
// signal: routeMatch.__.abortController.signal,
|
|
918
|
+
})
|
|
919
|
+
|
|
920
|
+
if (res.ok) {
|
|
921
|
+
return res.json()
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
throw new Error('Failed to fetch match data')
|
|
925
|
+
}
|
|
926
|
+
},
|
|
927
|
+
|
|
773
928
|
invalidateRoute: (opts: MatchLocation) => {
|
|
774
929
|
const next = router.buildNext(opts)
|
|
775
930
|
const unloadedMatchIds = router
|
|
@@ -1012,12 +1167,6 @@ export function createRouter<
|
|
|
1012
1167
|
return routeConfigs.map((routeConfig) => {
|
|
1013
1168
|
const routeOptions = routeConfig.options
|
|
1014
1169
|
const route = createRoute(routeConfig, routeOptions, parent, router)
|
|
1015
|
-
|
|
1016
|
-
// {
|
|
1017
|
-
// pendingMs: routeOptions.pendingMs ?? router.defaultPendingMs,
|
|
1018
|
-
// pendingMinMs: routeOptions.pendingMinMs ?? router.defaultPendingMinMs,
|
|
1019
|
-
// }
|
|
1020
|
-
|
|
1021
1170
|
const existingRoute = (router.routesById as any)[route.routeId]
|
|
1022
1171
|
|
|
1023
1172
|
if (existingRoute) {
|
|
@@ -1183,6 +1332,7 @@ export function createRouter<
|
|
|
1183
1332
|
},
|
|
1184
1333
|
{
|
|
1185
1334
|
id,
|
|
1335
|
+
...next.state,
|
|
1186
1336
|
},
|
|
1187
1337
|
)
|
|
1188
1338
|
} else {
|
|
@@ -1212,9 +1362,6 @@ export function createRouter<
|
|
|
1212
1362
|
},
|
|
1213
1363
|
}
|
|
1214
1364
|
|
|
1215
|
-
router.location = router.__.parseLocation(history.location)
|
|
1216
|
-
router.state.location = router.location
|
|
1217
|
-
|
|
1218
1365
|
router.update(userOptions)
|
|
1219
1366
|
|
|
1220
1367
|
// Allow frameworks to hook into the router creation
|
|
@@ -1226,3 +1373,16 @@ export function createRouter<
|
|
|
1226
1373
|
function isCtrlEvent(e: MouseEvent) {
|
|
1227
1374
|
return !!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey)
|
|
1228
1375
|
}
|
|
1376
|
+
|
|
1377
|
+
function cascadeLoaderData(matches: RouteMatch<any, any>[]) {
|
|
1378
|
+
matches.forEach((match, index) => {
|
|
1379
|
+
const parent = matches[index - 1]
|
|
1380
|
+
|
|
1381
|
+
if (parent) {
|
|
1382
|
+
match.loaderData = replaceEqualDeep(match.loaderData, {
|
|
1383
|
+
...parent.loaderData,
|
|
1384
|
+
...match.routeLoaderData,
|
|
1385
|
+
})
|
|
1386
|
+
}
|
|
1387
|
+
})
|
|
1388
|
+
}
|
package/src/utils.ts
CHANGED
|
@@ -24,11 +24,11 @@ export type Expand<T> = T extends object
|
|
|
24
24
|
: never
|
|
25
25
|
: T
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
27
|
+
export type UnionToIntersection<U> = (
|
|
28
|
+
U extends any ? (k: U) => void : never
|
|
29
|
+
) extends (k: infer I) => any
|
|
30
|
+
? I
|
|
31
|
+
: never
|
|
32
32
|
|
|
33
33
|
export type Values<O> = O[ValueKeys<O>]
|
|
34
34
|
export type ValueKeys<O> = Extract<keyof O, PropertyKey>
|
|
@@ -155,3 +155,10 @@ export function functionalUpdate<TResult>(
|
|
|
155
155
|
|
|
156
156
|
return updater
|
|
157
157
|
}
|
|
158
|
+
|
|
159
|
+
export function pick<T, K extends keyof T>(parent: T, keys: K[]): Pick<T, K> {
|
|
160
|
+
return keys.reduce((obj: any, key: K) => {
|
|
161
|
+
obj[key] = parent[key]
|
|
162
|
+
return obj
|
|
163
|
+
}, {} as any)
|
|
164
|
+
}
|