@tanstack/router-core 0.0.1-beta.2 → 0.0.1-beta.21
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/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 +170 -89
- package/build/cjs/packages/router-core/src/router.js.map +1 -1
- package/build/cjs/packages/router-core/src/utils.js +7 -0
- package/build/cjs/packages/router-core/src/utils.js.map +1 -1
- package/build/esm/index.js +257 -227
- package/build/esm/index.js.map +1 -1
- package/build/stats-html.html +1 -1
- package/build/stats-react.json +137 -150
- package/build/types/index.d.ts +184 -186
- package/build/umd/index.development.js +257 -227
- 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/qss.ts +1 -0
- package/src/route.ts +10 -26
- package/src/routeConfig.ts +25 -20
- package/src/routeInfo.ts +9 -2
- package/src/routeMatch.ts +94 -156
- package/src/router.ts +241 -104
- package/src/utils.ts +7 -0
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,8 +165,8 @@ 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>
|
|
@@ -172,9 +177,6 @@ export interface RouterState {
|
|
|
172
177
|
location: Location
|
|
173
178
|
matches: RouteMatch[]
|
|
174
179
|
lastUpdated: number
|
|
175
|
-
loaderData: unknown
|
|
176
|
-
currentAction?: ActionState
|
|
177
|
-
latestAction?: ActionState
|
|
178
180
|
actions: Record<string, Action>
|
|
179
181
|
loaders: Record<string, Loader>
|
|
180
182
|
pending?: PendingState
|
|
@@ -196,6 +198,7 @@ export interface BuildNextOptions {
|
|
|
196
198
|
params?: true | Updater<Record<string, any>>
|
|
197
199
|
search?: true | Updater<unknown>
|
|
198
200
|
hash?: true | Updater<string>
|
|
201
|
+
state?: LocationState
|
|
199
202
|
key?: string
|
|
200
203
|
from?: string
|
|
201
204
|
fromCurrent?: boolean
|
|
@@ -225,10 +228,33 @@ type LinkCurrentTargetElement = {
|
|
|
225
228
|
preloadTimeout?: null | ReturnType<typeof setTimeout>
|
|
226
229
|
}
|
|
227
230
|
|
|
231
|
+
interface DehydratedRouterState
|
|
232
|
+
extends Pick<RouterState, 'status' | 'location' | 'lastUpdated'> {
|
|
233
|
+
matches: DehydratedRouteMatch[]
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
interface DehydratedRouteMatch
|
|
237
|
+
extends Pick<
|
|
238
|
+
RouteMatch<any, any>,
|
|
239
|
+
| 'matchId'
|
|
240
|
+
| 'status'
|
|
241
|
+
| 'routeLoaderData'
|
|
242
|
+
| 'loaderData'
|
|
243
|
+
| 'isInvalid'
|
|
244
|
+
| 'invalidAt'
|
|
245
|
+
> {}
|
|
246
|
+
|
|
228
247
|
export interface Router<
|
|
229
248
|
TRouteConfig extends AnyRouteConfig = RouteConfig,
|
|
230
249
|
TAllRouteInfo extends AnyAllRouteInfo = AllRouteInfo<TRouteConfig>,
|
|
231
250
|
> {
|
|
251
|
+
types: {
|
|
252
|
+
// Super secret internal stuff
|
|
253
|
+
RouteConfig: TRouteConfig
|
|
254
|
+
AllRouteInfo: TAllRouteInfo
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Public API
|
|
232
258
|
history: BrowserHistory | MemoryHistory | HashHistory
|
|
233
259
|
options: PickAsRequired<
|
|
234
260
|
RouterOptions<TRouteConfig>,
|
|
@@ -237,7 +263,6 @@ export interface Router<
|
|
|
237
263
|
// Computed in this.update()
|
|
238
264
|
basepath: string
|
|
239
265
|
// Internal:
|
|
240
|
-
allRouteInfo: TAllRouteInfo
|
|
241
266
|
listeners: Listener[]
|
|
242
267
|
location: Location
|
|
243
268
|
navigateTimeout?: Timeout
|
|
@@ -246,10 +271,10 @@ export interface Router<
|
|
|
246
271
|
routeTree: Route<TAllRouteInfo, RouteInfo>
|
|
247
272
|
routesById: RoutesById<TAllRouteInfo>
|
|
248
273
|
navigationPromise: Promise<void>
|
|
249
|
-
removeActionQueue: { action: Action; actionState: ActionState }[]
|
|
250
274
|
startedLoadingAt: number
|
|
251
275
|
resolveNavigation: () => void
|
|
252
276
|
subscribe: (listener: Listener) => () => void
|
|
277
|
+
reset: () => void
|
|
253
278
|
notify: () => void
|
|
254
279
|
mount: () => () => void
|
|
255
280
|
onFocus: () => void
|
|
@@ -259,7 +284,7 @@ export interface Router<
|
|
|
259
284
|
|
|
260
285
|
buildNext: (opts: BuildNextOptions) => Location
|
|
261
286
|
cancelMatches: () => void
|
|
262
|
-
|
|
287
|
+
load: (next?: Location) => Promise<void>
|
|
263
288
|
matchCache: Record<string, MatchCacheEntry>
|
|
264
289
|
cleanMatchCache: () => void
|
|
265
290
|
getRoute: <TId extends keyof TAllRouteInfo['routeInfoById']>(
|
|
@@ -276,11 +301,13 @@ export interface Router<
|
|
|
276
301
|
) => RouteMatch[]
|
|
277
302
|
loadMatches: (
|
|
278
303
|
resolvedMatches: RouteMatch[],
|
|
279
|
-
loaderOpts?:
|
|
304
|
+
loaderOpts?:
|
|
280
305
|
| { preload: true; maxAge: number; gcMaxAge: number }
|
|
281
|
-
| { preload?: false; maxAge?: never; gcMaxAge?: never }
|
|
282
|
-
),
|
|
306
|
+
| { preload?: false; maxAge?: never; gcMaxAge?: never },
|
|
283
307
|
) => Promise<void>
|
|
308
|
+
loadMatchData: (
|
|
309
|
+
routeMatch: RouteMatch<any, any>,
|
|
310
|
+
) => Promise<Record<string, unknown>>
|
|
284
311
|
invalidateRoute: (opts: MatchLocation) => void
|
|
285
312
|
reload: () => Promise<void>
|
|
286
313
|
resolvePath: (from: string, path: string) => string
|
|
@@ -303,6 +330,8 @@ export interface Router<
|
|
|
303
330
|
>(
|
|
304
331
|
opts: LinkOptions<TAllRouteInfo, TFrom, TTo>,
|
|
305
332
|
) => LinkInfo
|
|
333
|
+
dehydrateState: () => DehydratedRouterState
|
|
334
|
+
hydrateState: (state: DehydratedRouterState) => void
|
|
306
335
|
__: {
|
|
307
336
|
buildRouteTree: (
|
|
308
337
|
routeConfig: RouteConfig,
|
|
@@ -320,13 +349,25 @@ export interface Router<
|
|
|
320
349
|
}
|
|
321
350
|
|
|
322
351
|
// Detect if we're in the DOM
|
|
323
|
-
const isServer =
|
|
324
|
-
typeof window === 'undefined' || !window.document?.createElement
|
|
325
|
-
)
|
|
352
|
+
const isServer =
|
|
353
|
+
typeof window === 'undefined' || !window.document?.createElement
|
|
326
354
|
|
|
327
355
|
// This is the default history object if none is defined
|
|
328
356
|
const createDefaultHistory = () =>
|
|
329
|
-
|
|
357
|
+
isServer ? createMemoryHistory() : createBrowserHistory()
|
|
358
|
+
|
|
359
|
+
function getInitialRouterState(): RouterState {
|
|
360
|
+
return {
|
|
361
|
+
status: 'idle',
|
|
362
|
+
location: null!,
|
|
363
|
+
matches: [],
|
|
364
|
+
actions: {},
|
|
365
|
+
loaders: {},
|
|
366
|
+
lastUpdated: Date.now(),
|
|
367
|
+
isFetching: false,
|
|
368
|
+
isPreloading: false,
|
|
369
|
+
}
|
|
370
|
+
}
|
|
330
371
|
|
|
331
372
|
export function createRouter<
|
|
332
373
|
TRouteConfig extends AnyRouteConfig = RouteConfig,
|
|
@@ -347,30 +388,25 @@ export function createRouter<
|
|
|
347
388
|
}
|
|
348
389
|
|
|
349
390
|
let router: Router<TRouteConfig, TAllRouteInfo> = {
|
|
391
|
+
types: undefined!,
|
|
392
|
+
|
|
393
|
+
// public api
|
|
350
394
|
history,
|
|
351
395
|
options: originalOptions,
|
|
352
396
|
listeners: [],
|
|
353
|
-
removeActionQueue: [],
|
|
354
397
|
// Resolved after construction
|
|
355
398
|
basepath: '',
|
|
356
399
|
routeTree: undefined!,
|
|
357
400
|
routesById: {} as any,
|
|
358
401
|
location: undefined!,
|
|
359
|
-
allRouteInfo: undefined!,
|
|
360
402
|
//
|
|
361
403
|
navigationPromise: Promise.resolve(),
|
|
362
404
|
resolveNavigation: () => {},
|
|
363
405
|
matchCache: {},
|
|
364
|
-
state:
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
actions: {},
|
|
369
|
-
loaders: {},
|
|
370
|
-
loaderData: {} as any,
|
|
371
|
-
lastUpdated: Date.now(),
|
|
372
|
-
isFetching: false,
|
|
373
|
-
isPreloading: false,
|
|
406
|
+
state: getInitialRouterState(),
|
|
407
|
+
reset: () => {
|
|
408
|
+
router.state = getInitialRouterState()
|
|
409
|
+
router.notify()
|
|
374
410
|
},
|
|
375
411
|
startedLoadingAt: Date.now(),
|
|
376
412
|
subscribe: (listener: Listener): (() => void) => {
|
|
@@ -383,22 +419,71 @@ export function createRouter<
|
|
|
383
419
|
return router.routesById[id]
|
|
384
420
|
},
|
|
385
421
|
notify: (): void => {
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
isFetching
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
422
|
+
const isFetching =
|
|
423
|
+
router.state.status === 'loading' ||
|
|
424
|
+
router.state.matches.some((d) => d.isFetching)
|
|
425
|
+
|
|
426
|
+
const isPreloading = Object.values(router.matchCache).some(
|
|
427
|
+
(d) =>
|
|
428
|
+
d.match.isFetching &&
|
|
429
|
+
!router.state.matches.find((dd) => dd.matchId === d.match.matchId),
|
|
430
|
+
)
|
|
431
|
+
|
|
432
|
+
if (
|
|
433
|
+
router.state.isFetching !== isFetching ||
|
|
434
|
+
router.state.isPreloading !== isPreloading
|
|
435
|
+
) {
|
|
436
|
+
router.state = {
|
|
437
|
+
...router.state,
|
|
438
|
+
isFetching,
|
|
439
|
+
isPreloading,
|
|
440
|
+
}
|
|
396
441
|
}
|
|
397
442
|
|
|
398
443
|
cascadeLoaderData(router.state.matches)
|
|
399
444
|
router.listeners.forEach((listener) => listener(router))
|
|
400
445
|
},
|
|
401
446
|
|
|
447
|
+
dehydrateState: () => {
|
|
448
|
+
return {
|
|
449
|
+
...pick(router.state, ['status', 'location', 'lastUpdated']),
|
|
450
|
+
matches: router.state.matches.map((match) =>
|
|
451
|
+
pick(match, [
|
|
452
|
+
'matchId',
|
|
453
|
+
'status',
|
|
454
|
+
'routeLoaderData',
|
|
455
|
+
'loaderData',
|
|
456
|
+
'isInvalid',
|
|
457
|
+
'invalidAt',
|
|
458
|
+
]),
|
|
459
|
+
),
|
|
460
|
+
}
|
|
461
|
+
},
|
|
462
|
+
|
|
463
|
+
hydrateState: (dehydratedState) => {
|
|
464
|
+
// Match the routes
|
|
465
|
+
const matches = router.matchRoutes(router.location.pathname, {
|
|
466
|
+
strictParseParams: true,
|
|
467
|
+
})
|
|
468
|
+
|
|
469
|
+
matches.forEach((match, index) => {
|
|
470
|
+
const dehydratedMatch = dehydratedState.matches[index]
|
|
471
|
+
invariant(
|
|
472
|
+
dehydratedMatch,
|
|
473
|
+
'Oh no! Dehydrated route matches did not match the active state of the router 😬',
|
|
474
|
+
)
|
|
475
|
+
Object.assign(match, dehydratedMatch)
|
|
476
|
+
})
|
|
477
|
+
|
|
478
|
+
matches.forEach((match) => match.__.validate())
|
|
479
|
+
|
|
480
|
+
router.state = {
|
|
481
|
+
...router.state,
|
|
482
|
+
...dehydratedState,
|
|
483
|
+
matches,
|
|
484
|
+
}
|
|
485
|
+
},
|
|
486
|
+
|
|
402
487
|
mount: () => {
|
|
403
488
|
const next = router.__.buildLocation({
|
|
404
489
|
to: '.',
|
|
@@ -410,14 +495,14 @@ export function createRouter<
|
|
|
410
495
|
// to the current location. Otherwise, load the current location.
|
|
411
496
|
if (next.href !== router.location.href) {
|
|
412
497
|
router.__.commitLocation(next, true)
|
|
413
|
-
} else {
|
|
414
|
-
router.loadLocation()
|
|
415
498
|
}
|
|
416
499
|
|
|
417
|
-
|
|
418
|
-
router.
|
|
419
|
-
|
|
420
|
-
|
|
500
|
+
if (!router.state.matches.length) {
|
|
501
|
+
router.load()
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
const unsub = router.history.listen((event) => {
|
|
505
|
+
router.load(router.__.parseLocation(event.location, router.location))
|
|
421
506
|
})
|
|
422
507
|
|
|
423
508
|
// addEventListener does not exist in React Native, but window does
|
|
@@ -430,17 +515,28 @@ export function createRouter<
|
|
|
430
515
|
|
|
431
516
|
return () => {
|
|
432
517
|
unsub()
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
518
|
+
if (!isServer && window.removeEventListener) {
|
|
519
|
+
// Be sure to unsubscribe if a new handler is set
|
|
520
|
+
window.removeEventListener('visibilitychange', router.onFocus)
|
|
521
|
+
window.removeEventListener('focus', router.onFocus)
|
|
522
|
+
}
|
|
436
523
|
}
|
|
437
524
|
},
|
|
438
525
|
|
|
439
526
|
onFocus: () => {
|
|
440
|
-
router.
|
|
527
|
+
router.load()
|
|
441
528
|
},
|
|
442
529
|
|
|
443
530
|
update: (opts) => {
|
|
531
|
+
const newHistory = opts?.history !== router.history
|
|
532
|
+
if (!router.location || newHistory) {
|
|
533
|
+
if (opts?.history) {
|
|
534
|
+
router.history = opts.history
|
|
535
|
+
}
|
|
536
|
+
router.location = router.__.parseLocation(router.history.location)
|
|
537
|
+
router.state.location = router.location
|
|
538
|
+
}
|
|
539
|
+
|
|
444
540
|
Object.assign(router.options, opts)
|
|
445
541
|
|
|
446
542
|
const { basepath, routeConfig } = router.options
|
|
@@ -464,7 +560,7 @@ export function createRouter<
|
|
|
464
560
|
})
|
|
465
561
|
},
|
|
466
562
|
|
|
467
|
-
|
|
563
|
+
load: async (next?: Location) => {
|
|
468
564
|
const id = Math.random()
|
|
469
565
|
router.startedLoadingAt = id
|
|
470
566
|
|
|
@@ -473,40 +569,36 @@ export function createRouter<
|
|
|
473
569
|
router.location = next
|
|
474
570
|
}
|
|
475
571
|
|
|
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
572
|
// Cancel any pending matches
|
|
488
573
|
router.cancelMatches()
|
|
489
574
|
|
|
490
575
|
// Match the routes
|
|
491
|
-
const matches = router.matchRoutes(location.pathname, {
|
|
576
|
+
const matches = router.matchRoutes(router.location.pathname, {
|
|
492
577
|
strictParseParams: true,
|
|
493
578
|
})
|
|
494
579
|
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
580
|
+
if (typeof document !== 'undefined') {
|
|
581
|
+
router.state = {
|
|
582
|
+
...router.state,
|
|
583
|
+
pending: {
|
|
584
|
+
matches: matches,
|
|
585
|
+
location: router.location,
|
|
586
|
+
},
|
|
587
|
+
status: 'loading',
|
|
588
|
+
}
|
|
589
|
+
} else {
|
|
590
|
+
router.state = {
|
|
591
|
+
...router.state,
|
|
498
592
|
matches: matches,
|
|
499
593
|
location: router.location,
|
|
500
|
-
|
|
501
|
-
|
|
594
|
+
status: 'loading',
|
|
595
|
+
}
|
|
502
596
|
}
|
|
503
597
|
|
|
504
598
|
router.notify()
|
|
505
599
|
|
|
506
600
|
// Load the matches
|
|
507
|
-
await router.loadMatches(matches
|
|
508
|
-
withPending: true,
|
|
509
|
-
})
|
|
601
|
+
await router.loadMatches(matches)
|
|
510
602
|
|
|
511
603
|
if (router.startedLoadingAt !== id) {
|
|
512
604
|
// Ignore side-effects of match loading
|
|
@@ -526,6 +618,10 @@ export function createRouter<
|
|
|
526
618
|
}
|
|
527
619
|
})
|
|
528
620
|
|
|
621
|
+
const entering = matches.filter((d) => {
|
|
622
|
+
return !previousMatches.find((dd) => dd.matchId === d.matchId)
|
|
623
|
+
})
|
|
624
|
+
|
|
529
625
|
const now = Date.now()
|
|
530
626
|
|
|
531
627
|
exiting.forEach((d) => {
|
|
@@ -533,6 +629,7 @@ export function createRouter<
|
|
|
533
629
|
params: d.params,
|
|
534
630
|
search: d.routeSearch,
|
|
535
631
|
})
|
|
632
|
+
|
|
536
633
|
// Clear idle error states when match leaves
|
|
537
634
|
if (d.status === 'error' && !d.isFetching) {
|
|
538
635
|
d.status = 'idle'
|
|
@@ -557,10 +654,6 @@ export function createRouter<
|
|
|
557
654
|
})
|
|
558
655
|
})
|
|
559
656
|
|
|
560
|
-
const entering = matches.filter((d) => {
|
|
561
|
-
return !previousMatches.find((dd) => dd.matchId === d.matchId)
|
|
562
|
-
})
|
|
563
|
-
|
|
564
657
|
entering.forEach((d) => {
|
|
565
658
|
d.__.onExit = d.options.onMatch?.({
|
|
566
659
|
params: d.params,
|
|
@@ -569,17 +662,19 @@ export function createRouter<
|
|
|
569
662
|
delete router.matchCache[d.matchId]
|
|
570
663
|
})
|
|
571
664
|
|
|
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
665
|
if (router.startedLoadingAt !== id) {
|
|
579
666
|
// Ignore side-effects of match loading
|
|
580
667
|
return
|
|
581
668
|
}
|
|
582
669
|
|
|
670
|
+
matches.forEach((match) => {
|
|
671
|
+
// Clear actions
|
|
672
|
+
if (match.action) {
|
|
673
|
+
match.action.current = undefined
|
|
674
|
+
match.action.submissions = []
|
|
675
|
+
}
|
|
676
|
+
})
|
|
677
|
+
|
|
583
678
|
router.state = {
|
|
584
679
|
...router.state,
|
|
585
680
|
location: router.location,
|
|
@@ -727,6 +822,7 @@ export function createRouter<
|
|
|
727
822
|
existingMatches.find((d) => d.matchId === matchId) ||
|
|
728
823
|
router.matchCache[matchId]?.match ||
|
|
729
824
|
createRouteMatch(router, foundRoute, {
|
|
825
|
+
parentMatch,
|
|
730
826
|
matchId,
|
|
731
827
|
params,
|
|
732
828
|
pathname: joinPaths([pathname, interpolatedPath]),
|
|
@@ -755,12 +851,14 @@ export function createRouter<
|
|
|
755
851
|
match.__.validate()
|
|
756
852
|
match.load(loaderOpts)
|
|
757
853
|
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
854
|
+
const search = match.search as { __data?: any }
|
|
855
|
+
|
|
856
|
+
if (search.__data && search.__data.matchId !== match.matchId) {
|
|
857
|
+
return
|
|
858
|
+
}
|
|
761
859
|
|
|
860
|
+
if (match.__.loadPromise) {
|
|
762
861
|
// Wait for the first sign of activity from the match
|
|
763
|
-
// This might be completion, error, or a pending state
|
|
764
862
|
await match.__.loadPromise
|
|
765
863
|
}
|
|
766
864
|
})
|
|
@@ -770,6 +868,40 @@ export function createRouter<
|
|
|
770
868
|
await Promise.all(matchPromises)
|
|
771
869
|
},
|
|
772
870
|
|
|
871
|
+
loadMatchData: async (routeMatch) => {
|
|
872
|
+
if (isServer || !router.options.useServerData) {
|
|
873
|
+
return (
|
|
874
|
+
(await routeMatch.options.loader?.({
|
|
875
|
+
// parentLoaderPromise: routeMatch.parentMatch?.__.dataPromise,
|
|
876
|
+
params: routeMatch.params,
|
|
877
|
+
search: routeMatch.routeSearch,
|
|
878
|
+
signal: routeMatch.__.abortController.signal,
|
|
879
|
+
})) ?? {}
|
|
880
|
+
)
|
|
881
|
+
} else {
|
|
882
|
+
const next = router.buildNext({
|
|
883
|
+
to: '.',
|
|
884
|
+
search: (d: any) => ({
|
|
885
|
+
...(d ?? {}),
|
|
886
|
+
__data: {
|
|
887
|
+
matchId: routeMatch.matchId,
|
|
888
|
+
},
|
|
889
|
+
}),
|
|
890
|
+
})
|
|
891
|
+
|
|
892
|
+
const res = await fetch(next.href, {
|
|
893
|
+
method: 'GET',
|
|
894
|
+
// signal: routeMatch.__.abortController.signal,
|
|
895
|
+
})
|
|
896
|
+
|
|
897
|
+
if (res.ok) {
|
|
898
|
+
return res.json()
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
throw new Error('Failed to fetch match data')
|
|
902
|
+
}
|
|
903
|
+
},
|
|
904
|
+
|
|
773
905
|
invalidateRoute: (opts: MatchLocation) => {
|
|
774
906
|
const next = router.buildNext(opts)
|
|
775
907
|
const unloadedMatchIds = router
|
|
@@ -1012,12 +1144,6 @@ export function createRouter<
|
|
|
1012
1144
|
return routeConfigs.map((routeConfig) => {
|
|
1013
1145
|
const routeOptions = routeConfig.options
|
|
1014
1146
|
const route = createRoute(routeConfig, routeOptions, parent, router)
|
|
1015
|
-
|
|
1016
|
-
// {
|
|
1017
|
-
// pendingMs: routeOptions.pendingMs ?? router.defaultPendingMs,
|
|
1018
|
-
// pendingMinMs: routeOptions.pendingMinMs ?? router.defaultPendingMinMs,
|
|
1019
|
-
// }
|
|
1020
|
-
|
|
1021
1147
|
const existingRoute = (router.routesById as any)[route.routeId]
|
|
1022
1148
|
|
|
1023
1149
|
if (existingRoute) {
|
|
@@ -1183,6 +1309,7 @@ export function createRouter<
|
|
|
1183
1309
|
},
|
|
1184
1310
|
{
|
|
1185
1311
|
id,
|
|
1312
|
+
...next.state,
|
|
1186
1313
|
},
|
|
1187
1314
|
)
|
|
1188
1315
|
} else {
|
|
@@ -1212,9 +1339,6 @@ export function createRouter<
|
|
|
1212
1339
|
},
|
|
1213
1340
|
}
|
|
1214
1341
|
|
|
1215
|
-
router.location = router.__.parseLocation(history.location)
|
|
1216
|
-
router.state.location = router.location
|
|
1217
|
-
|
|
1218
1342
|
router.update(userOptions)
|
|
1219
1343
|
|
|
1220
1344
|
// Allow frameworks to hook into the router creation
|
|
@@ -1226,3 +1350,16 @@ export function createRouter<
|
|
|
1226
1350
|
function isCtrlEvent(e: MouseEvent) {
|
|
1227
1351
|
return !!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey)
|
|
1228
1352
|
}
|
|
1353
|
+
|
|
1354
|
+
function cascadeLoaderData(matches: RouteMatch<any, any>[]) {
|
|
1355
|
+
matches.forEach((match, index) => {
|
|
1356
|
+
const parent = matches[index - 1]
|
|
1357
|
+
|
|
1358
|
+
if (parent) {
|
|
1359
|
+
match.loaderData = replaceEqualDeep(match.loaderData, {
|
|
1360
|
+
...parent.loaderData,
|
|
1361
|
+
...match.routeLoaderData,
|
|
1362
|
+
})
|
|
1363
|
+
}
|
|
1364
|
+
})
|
|
1365
|
+
}
|
package/src/utils.ts
CHANGED
|
@@ -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
|
+
}
|