@tanstack/router-core 0.0.1-alpha.9 → 0.0.1-beta.10
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 +44 -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 +50 -48
- package/build/cjs/packages/router-core/src/routeMatch.js.map +1 -1
- package/build/cjs/packages/router-core/src/router.js +280 -257
- 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 +379 -324
- package/build/esm/index.js.map +1 -1
- package/build/stats-html.html +1 -1
- package/build/stats-react.json +138 -144
- package/build/types/index.d.ts +140 -96
- package/build/umd/index.development.js +379 -324
- 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 +1 -1
- package/src/frameworks.ts +0 -1
- package/src/link.ts +45 -17
- package/src/qss.ts +1 -0
- package/src/route.ts +74 -28
- package/src/routeConfig.ts +27 -55
- package/src/routeMatch.ts +70 -61
- package/src/router.ts +435 -331
- package/src/utils.ts +7 -0
package/src/router.ts
CHANGED
|
@@ -23,10 +23,13 @@ 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
|
+
AnyLoaderData,
|
|
29
|
+
AnyPathParams,
|
|
28
30
|
AnyRouteConfig,
|
|
29
31
|
AnySearchSchema,
|
|
32
|
+
LoaderContext,
|
|
30
33
|
RouteConfig,
|
|
31
34
|
SearchFilter,
|
|
32
35
|
} from './routeConfig'
|
|
@@ -42,7 +45,9 @@ import { defaultParseSearch, defaultStringifySearch } from './searchParams'
|
|
|
42
45
|
import {
|
|
43
46
|
functionalUpdate,
|
|
44
47
|
last,
|
|
48
|
+
pick,
|
|
45
49
|
PickAsRequired,
|
|
50
|
+
PickRequired,
|
|
46
51
|
replaceEqualDeep,
|
|
47
52
|
Timeout,
|
|
48
53
|
Updater,
|
|
@@ -99,6 +104,9 @@ export interface RouterOptions<TRouteConfig extends AnyRouteConfig> {
|
|
|
99
104
|
basepath?: string
|
|
100
105
|
createRouter?: (router: Router<any, any>) => void
|
|
101
106
|
createRoute?: (opts: { route: AnyRoute; router: Router<any, any> }) => void
|
|
107
|
+
createElement?: (
|
|
108
|
+
element: GetFrameworkGeneric<'SyncOrAsyncElement'>,
|
|
109
|
+
) => Promise<GetFrameworkGeneric<'Element'>>
|
|
102
110
|
}
|
|
103
111
|
|
|
104
112
|
export interface Action<
|
|
@@ -106,10 +114,13 @@ export interface Action<
|
|
|
106
114
|
TResponse = unknown,
|
|
107
115
|
// TError = unknown,
|
|
108
116
|
> {
|
|
109
|
-
submit: (
|
|
117
|
+
submit: (
|
|
118
|
+
submission?: TPayload,
|
|
119
|
+
actionOpts?: { invalidate?: boolean; multi?: boolean },
|
|
120
|
+
) => Promise<TResponse>
|
|
110
121
|
current?: ActionState<TPayload, TResponse>
|
|
111
122
|
latest?: ActionState<TPayload, TResponse>
|
|
112
|
-
|
|
123
|
+
submissions: ActionState<TPayload, TResponse>[]
|
|
113
124
|
}
|
|
114
125
|
|
|
115
126
|
export interface ActionState<
|
|
@@ -120,19 +131,54 @@ export interface ActionState<
|
|
|
120
131
|
submittedAt: number
|
|
121
132
|
status: 'idle' | 'pending' | 'success' | 'error'
|
|
122
133
|
submission: TPayload
|
|
134
|
+
isMulti: boolean
|
|
123
135
|
data?: TResponse
|
|
124
136
|
error?: unknown
|
|
125
137
|
}
|
|
126
138
|
|
|
139
|
+
export interface Loader<
|
|
140
|
+
TFullSearchSchema extends AnySearchSchema = {},
|
|
141
|
+
TAllParams extends AnyPathParams = {},
|
|
142
|
+
TRouteLoaderData = AnyLoaderData,
|
|
143
|
+
> {
|
|
144
|
+
fetch: keyof PickRequired<TFullSearchSchema> extends never
|
|
145
|
+
? keyof TAllParams extends never
|
|
146
|
+
? (loaderContext: { signal?: AbortSignal }) => Promise<TRouteLoaderData>
|
|
147
|
+
: (loaderContext: {
|
|
148
|
+
params: TAllParams
|
|
149
|
+
search?: TFullSearchSchema
|
|
150
|
+
signal?: AbortSignal
|
|
151
|
+
}) => Promise<TRouteLoaderData>
|
|
152
|
+
: keyof TAllParams extends never
|
|
153
|
+
? (loaderContext: {
|
|
154
|
+
search: TFullSearchSchema
|
|
155
|
+
params: TAllParams
|
|
156
|
+
signal?: AbortSignal
|
|
157
|
+
}) => Promise<TRouteLoaderData>
|
|
158
|
+
: (loaderContext: {
|
|
159
|
+
search: TFullSearchSchema
|
|
160
|
+
signal?: AbortSignal
|
|
161
|
+
}) => Promise<TRouteLoaderData>
|
|
162
|
+
current?: LoaderState<TFullSearchSchema, TAllParams>
|
|
163
|
+
latest?: LoaderState<TFullSearchSchema, TAllParams>
|
|
164
|
+
pending: LoaderState<TFullSearchSchema, TAllParams>[]
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export interface LoaderState<
|
|
168
|
+
TFullSearchSchema = unknown,
|
|
169
|
+
TAllParams = unknown,
|
|
170
|
+
> {
|
|
171
|
+
loadedAt: number
|
|
172
|
+
loaderContext: LoaderContext<TFullSearchSchema, TAllParams>
|
|
173
|
+
}
|
|
174
|
+
|
|
127
175
|
export interface RouterState {
|
|
128
176
|
status: 'idle' | 'loading'
|
|
129
177
|
location: Location
|
|
130
178
|
matches: RouteMatch[]
|
|
131
179
|
lastUpdated: number
|
|
132
|
-
loaderData: unknown
|
|
133
|
-
currentAction?: ActionState
|
|
134
|
-
latestAction?: ActionState
|
|
135
180
|
actions: Record<string, Action>
|
|
181
|
+
loaders: Record<string, Loader>
|
|
136
182
|
pending?: PendingState
|
|
137
183
|
isFetching: boolean
|
|
138
184
|
isPreloading: boolean
|
|
@@ -143,7 +189,7 @@ export interface PendingState {
|
|
|
143
189
|
matches: RouteMatch[]
|
|
144
190
|
}
|
|
145
191
|
|
|
146
|
-
type Listener = () => void
|
|
192
|
+
type Listener = (router: Router<any, any>) => void
|
|
147
193
|
|
|
148
194
|
export type ListenerFn = () => void
|
|
149
195
|
|
|
@@ -181,10 +227,27 @@ type LinkCurrentTargetElement = {
|
|
|
181
227
|
preloadTimeout?: null | ReturnType<typeof setTimeout>
|
|
182
228
|
}
|
|
183
229
|
|
|
230
|
+
interface DehydratedRouterState
|
|
231
|
+
extends Pick<RouterState, 'status' | 'location' | 'lastUpdated'> {
|
|
232
|
+
matches: DehydratedRouteMatch[]
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
interface DehydratedRouteMatch
|
|
236
|
+
extends Pick<
|
|
237
|
+
RouteMatch<any, any>,
|
|
238
|
+
| 'matchId'
|
|
239
|
+
| 'status'
|
|
240
|
+
| 'routeLoaderData'
|
|
241
|
+
| 'loaderData'
|
|
242
|
+
| 'isInvalid'
|
|
243
|
+
| 'invalidAt'
|
|
244
|
+
> {}
|
|
245
|
+
|
|
184
246
|
export interface Router<
|
|
185
247
|
TRouteConfig extends AnyRouteConfig = RouteConfig,
|
|
186
248
|
TAllRouteInfo extends AnyAllRouteInfo = AllRouteInfo<TRouteConfig>,
|
|
187
249
|
> {
|
|
250
|
+
history: BrowserHistory | MemoryHistory | HashHistory
|
|
188
251
|
options: PickAsRequired<
|
|
189
252
|
RouterOptions<TRouteConfig>,
|
|
190
253
|
'stringifySearch' | 'parseSearch'
|
|
@@ -201,28 +264,20 @@ export interface Router<
|
|
|
201
264
|
routeTree: Route<TAllRouteInfo, RouteInfo>
|
|
202
265
|
routesById: RoutesById<TAllRouteInfo>
|
|
203
266
|
navigationPromise: Promise<void>
|
|
204
|
-
removeActionQueue: { action: Action; actionState: ActionState }[]
|
|
205
267
|
startedLoadingAt: number
|
|
206
268
|
resolveNavigation: () => void
|
|
207
269
|
subscribe: (listener: Listener) => () => void
|
|
270
|
+
reset: () => void
|
|
208
271
|
notify: () => void
|
|
209
272
|
mount: () => () => void
|
|
210
273
|
onFocus: () => void
|
|
211
274
|
update: <TRouteConfig extends RouteConfig = RouteConfig>(
|
|
212
275
|
opts?: RouterOptions<TRouteConfig>,
|
|
213
276
|
) => Router<TRouteConfig>
|
|
214
|
-
|
|
215
|
-
routeConfig: RouteConfig,
|
|
216
|
-
) => Route<TAllRouteInfo, AnyRouteInfo>
|
|
217
|
-
parseLocation: (
|
|
218
|
-
location: History['location'],
|
|
219
|
-
previousLocation?: Location,
|
|
220
|
-
) => Location
|
|
221
|
-
buildLocation: (dest: BuildNextOptions) => Location
|
|
222
|
-
commitLocation: (next: Location, replace?: boolean) => Promise<void>
|
|
277
|
+
|
|
223
278
|
buildNext: (opts: BuildNextOptions) => Location
|
|
224
279
|
cancelMatches: () => void
|
|
225
|
-
|
|
280
|
+
load: (next?: Location) => Promise<void>
|
|
226
281
|
matchCache: Record<string, MatchCacheEntry>
|
|
227
282
|
cleanMatchCache: () => void
|
|
228
283
|
getRoute: <TId extends keyof TAllRouteInfo['routeInfoById']>(
|
|
@@ -247,9 +302,6 @@ export interface Router<
|
|
|
247
302
|
invalidateRoute: (opts: MatchLocation) => void
|
|
248
303
|
reload: () => Promise<void>
|
|
249
304
|
resolvePath: (from: string, path: string) => string
|
|
250
|
-
_navigate: (
|
|
251
|
-
location: BuildNextOptions & { replace?: boolean },
|
|
252
|
-
) => Promise<void>
|
|
253
305
|
navigate: <
|
|
254
306
|
TFrom extends ValidFromPath<TAllRouteInfo> = '/',
|
|
255
307
|
TTo extends string = '.',
|
|
@@ -269,16 +321,44 @@ export interface Router<
|
|
|
269
321
|
>(
|
|
270
322
|
opts: LinkOptions<TAllRouteInfo, TFrom, TTo>,
|
|
271
323
|
) => LinkInfo
|
|
324
|
+
dehydrateState: () => DehydratedRouterState
|
|
325
|
+
hydrateState: (state: DehydratedRouterState) => void
|
|
326
|
+
__: {
|
|
327
|
+
buildRouteTree: (
|
|
328
|
+
routeConfig: RouteConfig,
|
|
329
|
+
) => Route<TAllRouteInfo, AnyRouteInfo>
|
|
330
|
+
parseLocation: (
|
|
331
|
+
location: History['location'],
|
|
332
|
+
previousLocation?: Location,
|
|
333
|
+
) => Location
|
|
334
|
+
buildLocation: (dest: BuildNextOptions) => Location
|
|
335
|
+
commitLocation: (next: Location, replace?: boolean) => Promise<void>
|
|
336
|
+
navigate: (
|
|
337
|
+
location: BuildNextOptions & { replace?: boolean },
|
|
338
|
+
) => Promise<void>
|
|
339
|
+
}
|
|
272
340
|
}
|
|
273
341
|
|
|
274
342
|
// Detect if we're in the DOM
|
|
275
|
-
const isServer =
|
|
276
|
-
typeof window === 'undefined' || !window.document?.createElement
|
|
277
|
-
)
|
|
343
|
+
const isServer =
|
|
344
|
+
typeof window === 'undefined' || !window.document?.createElement
|
|
278
345
|
|
|
279
346
|
// This is the default history object if none is defined
|
|
280
347
|
const createDefaultHistory = () =>
|
|
281
|
-
|
|
348
|
+
isServer ? createMemoryHistory() : createBrowserHistory()
|
|
349
|
+
|
|
350
|
+
function getInitialRouterState(): RouterState {
|
|
351
|
+
return {
|
|
352
|
+
status: 'idle',
|
|
353
|
+
location: null!,
|
|
354
|
+
matches: [],
|
|
355
|
+
actions: {},
|
|
356
|
+
loaders: {},
|
|
357
|
+
lastUpdated: Date.now(),
|
|
358
|
+
isFetching: false,
|
|
359
|
+
isPreloading: false,
|
|
360
|
+
}
|
|
361
|
+
}
|
|
282
362
|
|
|
283
363
|
export function createRouter<
|
|
284
364
|
TRouteConfig extends AnyRouteConfig = RouteConfig,
|
|
@@ -299,9 +379,9 @@ export function createRouter<
|
|
|
299
379
|
}
|
|
300
380
|
|
|
301
381
|
let router: Router<TRouteConfig, TAllRouteInfo> = {
|
|
382
|
+
history,
|
|
302
383
|
options: originalOptions,
|
|
303
384
|
listeners: [],
|
|
304
|
-
removeActionQueue: [],
|
|
305
385
|
// Resolved after construction
|
|
306
386
|
basepath: '',
|
|
307
387
|
routeTree: undefined!,
|
|
@@ -312,15 +392,10 @@ export function createRouter<
|
|
|
312
392
|
navigationPromise: Promise.resolve(),
|
|
313
393
|
resolveNavigation: () => {},
|
|
314
394
|
matchCache: {},
|
|
315
|
-
state:
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
actions: {},
|
|
320
|
-
loaderData: {} as any,
|
|
321
|
-
lastUpdated: Date.now(),
|
|
322
|
-
isFetching: false,
|
|
323
|
-
isPreloading: false,
|
|
395
|
+
state: getInitialRouterState(),
|
|
396
|
+
reset: () => {
|
|
397
|
+
router.state = getInitialRouterState()
|
|
398
|
+
router.notify()
|
|
324
399
|
},
|
|
325
400
|
startedLoadingAt: Date.now(),
|
|
326
401
|
subscribe: (listener: Listener): (() => void) => {
|
|
@@ -346,11 +421,51 @@ export function createRouter<
|
|
|
346
421
|
}
|
|
347
422
|
|
|
348
423
|
cascadeLoaderData(router.state.matches)
|
|
349
|
-
router.listeners.forEach((listener) => listener())
|
|
424
|
+
router.listeners.forEach((listener) => listener(router))
|
|
425
|
+
},
|
|
426
|
+
|
|
427
|
+
dehydrateState: () => {
|
|
428
|
+
return {
|
|
429
|
+
...pick(router.state, ['status', 'location', 'lastUpdated']),
|
|
430
|
+
matches: router.state.matches.map((match) =>
|
|
431
|
+
pick(match, [
|
|
432
|
+
'matchId',
|
|
433
|
+
'status',
|
|
434
|
+
'routeLoaderData',
|
|
435
|
+
'loaderData',
|
|
436
|
+
'isInvalid',
|
|
437
|
+
'invalidAt',
|
|
438
|
+
]),
|
|
439
|
+
),
|
|
440
|
+
}
|
|
441
|
+
},
|
|
442
|
+
|
|
443
|
+
hydrateState: (dehydratedState) => {
|
|
444
|
+
// Match the routes
|
|
445
|
+
const matches = router.matchRoutes(router.location.pathname, {
|
|
446
|
+
strictParseParams: true,
|
|
447
|
+
})
|
|
448
|
+
|
|
449
|
+
matches.forEach((match, index) => {
|
|
450
|
+
const dehydratedMatch = dehydratedState.matches[index]
|
|
451
|
+
invariant(
|
|
452
|
+
dehydratedMatch,
|
|
453
|
+
'Oh no! Dehydrated route matches did not match the active state of the router 😬',
|
|
454
|
+
)
|
|
455
|
+
Object.assign(match, dehydratedMatch)
|
|
456
|
+
})
|
|
457
|
+
|
|
458
|
+
router.loadMatches(matches)
|
|
459
|
+
|
|
460
|
+
router.state = {
|
|
461
|
+
...router.state,
|
|
462
|
+
...dehydratedState,
|
|
463
|
+
matches,
|
|
464
|
+
}
|
|
350
465
|
},
|
|
351
466
|
|
|
352
467
|
mount: () => {
|
|
353
|
-
const next = router.buildLocation({
|
|
468
|
+
const next = router.__.buildLocation({
|
|
354
469
|
to: '.',
|
|
355
470
|
search: true,
|
|
356
471
|
hash: true,
|
|
@@ -359,15 +474,13 @@ export function createRouter<
|
|
|
359
474
|
// If the current location isn't updated, trigger a navigation
|
|
360
475
|
// to the current location. Otherwise, load the current location.
|
|
361
476
|
if (next.href !== router.location.href) {
|
|
362
|
-
router.commitLocation(next, true)
|
|
363
|
-
} else {
|
|
364
|
-
router.loadLocation()
|
|
477
|
+
router.__.commitLocation(next, true)
|
|
365
478
|
}
|
|
366
479
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
)
|
|
480
|
+
// router.load()
|
|
481
|
+
|
|
482
|
+
const unsub = router.history.listen((event) => {
|
|
483
|
+
router.load(router.__.parseLocation(event.location, router.location))
|
|
371
484
|
})
|
|
372
485
|
|
|
373
486
|
// addEventListener does not exist in React Native, but window does
|
|
@@ -380,17 +493,28 @@ export function createRouter<
|
|
|
380
493
|
|
|
381
494
|
return () => {
|
|
382
495
|
unsub()
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
496
|
+
if (!isServer && window.removeEventListener) {
|
|
497
|
+
// Be sure to unsubscribe if a new handler is set
|
|
498
|
+
window.removeEventListener('visibilitychange', router.onFocus)
|
|
499
|
+
window.removeEventListener('focus', router.onFocus)
|
|
500
|
+
}
|
|
386
501
|
}
|
|
387
502
|
},
|
|
388
503
|
|
|
389
504
|
onFocus: () => {
|
|
390
|
-
router.
|
|
505
|
+
router.load()
|
|
391
506
|
},
|
|
392
507
|
|
|
393
508
|
update: (opts) => {
|
|
509
|
+
const newHistory = opts?.history !== router.history
|
|
510
|
+
if (!router.location || newHistory) {
|
|
511
|
+
if (opts?.history) {
|
|
512
|
+
router.history = opts.history
|
|
513
|
+
}
|
|
514
|
+
router.location = router.__.parseLocation(router.history.location)
|
|
515
|
+
router.state.location = router.location
|
|
516
|
+
}
|
|
517
|
+
|
|
394
518
|
Object.assign(router.options, opts)
|
|
395
519
|
|
|
396
520
|
const { basepath, routeConfig } = router.options
|
|
@@ -399,235 +523,12 @@ export function createRouter<
|
|
|
399
523
|
|
|
400
524
|
if (routeConfig) {
|
|
401
525
|
router.routesById = {} as any
|
|
402
|
-
router.routeTree = router.buildRouteTree(routeConfig)
|
|
526
|
+
router.routeTree = router.__.buildRouteTree(routeConfig)
|
|
403
527
|
}
|
|
404
528
|
|
|
405
529
|
return router as any
|
|
406
530
|
},
|
|
407
531
|
|
|
408
|
-
buildRouteTree: (rootRouteConfig: RouteConfig) => {
|
|
409
|
-
const recurseRoutes = (
|
|
410
|
-
routeConfigs: RouteConfig[],
|
|
411
|
-
parent?: Route<TAllRouteInfo, any>,
|
|
412
|
-
): Route<TAllRouteInfo, any>[] => {
|
|
413
|
-
return routeConfigs.map((routeConfig) => {
|
|
414
|
-
const routeOptions = routeConfig.options
|
|
415
|
-
const route = createRoute(routeConfig, routeOptions, parent, router)
|
|
416
|
-
|
|
417
|
-
// {
|
|
418
|
-
// pendingMs: routeOptions.pendingMs ?? router.defaultPendingMs,
|
|
419
|
-
// pendingMinMs: routeOptions.pendingMinMs ?? router.defaultPendingMinMs,
|
|
420
|
-
// }
|
|
421
|
-
|
|
422
|
-
const existingRoute = (router.routesById as any)[route.routeId]
|
|
423
|
-
|
|
424
|
-
if (existingRoute) {
|
|
425
|
-
if (process.env.NODE_ENV !== 'production') {
|
|
426
|
-
console.warn(
|
|
427
|
-
`Duplicate routes found with id: ${String(route.routeId)}`,
|
|
428
|
-
router.routesById,
|
|
429
|
-
route,
|
|
430
|
-
)
|
|
431
|
-
}
|
|
432
|
-
throw new Error()
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
;(router.routesById as any)[route.routeId] = route
|
|
436
|
-
|
|
437
|
-
const children = routeConfig.children as RouteConfig[]
|
|
438
|
-
|
|
439
|
-
route.childRoutes = children?.length
|
|
440
|
-
? recurseRoutes(children, route)
|
|
441
|
-
: undefined
|
|
442
|
-
|
|
443
|
-
return route
|
|
444
|
-
})
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
const routes = recurseRoutes([rootRouteConfig])
|
|
448
|
-
|
|
449
|
-
return routes[0]!
|
|
450
|
-
},
|
|
451
|
-
|
|
452
|
-
parseLocation: (
|
|
453
|
-
location: History['location'],
|
|
454
|
-
previousLocation?: Location,
|
|
455
|
-
): Location => {
|
|
456
|
-
const parsedSearch = router.options.parseSearch(location.search)
|
|
457
|
-
|
|
458
|
-
return {
|
|
459
|
-
pathname: location.pathname,
|
|
460
|
-
searchStr: location.search,
|
|
461
|
-
search: replaceEqualDeep(previousLocation?.search, parsedSearch),
|
|
462
|
-
hash: location.hash.split('#').reverse()[0] ?? '',
|
|
463
|
-
href: `${location.pathname}${location.search}${location.hash}`,
|
|
464
|
-
state: location.state as LocationState,
|
|
465
|
-
key: location.key,
|
|
466
|
-
}
|
|
467
|
-
},
|
|
468
|
-
|
|
469
|
-
buildLocation: (dest: BuildNextOptions = {}): Location => {
|
|
470
|
-
// const resolvedFrom: Location = {
|
|
471
|
-
// ...router.location,
|
|
472
|
-
const fromPathname = dest.fromCurrent
|
|
473
|
-
? router.location.pathname
|
|
474
|
-
: dest.from ?? router.location.pathname
|
|
475
|
-
|
|
476
|
-
let pathname = resolvePath(
|
|
477
|
-
router.basepath ?? '/',
|
|
478
|
-
fromPathname,
|
|
479
|
-
`${dest.to ?? '.'}`,
|
|
480
|
-
)
|
|
481
|
-
|
|
482
|
-
const fromMatches = router.matchRoutes(router.location.pathname, {
|
|
483
|
-
strictParseParams: true,
|
|
484
|
-
})
|
|
485
|
-
|
|
486
|
-
const toMatches = router.matchRoutes(pathname)
|
|
487
|
-
|
|
488
|
-
const prevParams = { ...last(fromMatches)?.params }
|
|
489
|
-
|
|
490
|
-
let nextParams =
|
|
491
|
-
(dest.params ?? true) === true
|
|
492
|
-
? prevParams
|
|
493
|
-
: functionalUpdate(dest.params!, prevParams)
|
|
494
|
-
|
|
495
|
-
if (nextParams) {
|
|
496
|
-
toMatches
|
|
497
|
-
.map((d) => d.options.stringifyParams)
|
|
498
|
-
.filter(Boolean)
|
|
499
|
-
.forEach((fn) => {
|
|
500
|
-
Object.assign({}, nextParams!, fn!(nextParams!))
|
|
501
|
-
})
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
pathname = interpolatePath(pathname, nextParams ?? {})
|
|
505
|
-
|
|
506
|
-
// Pre filters first
|
|
507
|
-
const preFilteredSearch = dest.__preSearchFilters?.length
|
|
508
|
-
? dest.__preSearchFilters.reduce(
|
|
509
|
-
(prev, next) => next(prev),
|
|
510
|
-
router.location.search,
|
|
511
|
-
)
|
|
512
|
-
: router.location.search
|
|
513
|
-
|
|
514
|
-
// Then the link/navigate function
|
|
515
|
-
const destSearch =
|
|
516
|
-
dest.search === true
|
|
517
|
-
? preFilteredSearch // Preserve resolvedFrom true
|
|
518
|
-
: dest.search
|
|
519
|
-
? functionalUpdate(dest.search, preFilteredSearch) ?? {} // Updater
|
|
520
|
-
: dest.__preSearchFilters?.length
|
|
521
|
-
? preFilteredSearch // Preserve resolvedFrom filters
|
|
522
|
-
: {}
|
|
523
|
-
|
|
524
|
-
// Then post filters
|
|
525
|
-
const postFilteredSearch = dest.__postSearchFilters?.length
|
|
526
|
-
? dest.__postSearchFilters.reduce(
|
|
527
|
-
(prev, next) => next(prev),
|
|
528
|
-
destSearch,
|
|
529
|
-
)
|
|
530
|
-
: destSearch
|
|
531
|
-
|
|
532
|
-
const search = replaceEqualDeep(
|
|
533
|
-
router.location.search,
|
|
534
|
-
postFilteredSearch,
|
|
535
|
-
)
|
|
536
|
-
|
|
537
|
-
const searchStr = router.options.stringifySearch(search)
|
|
538
|
-
let hash =
|
|
539
|
-
dest.hash === true
|
|
540
|
-
? router.location.hash
|
|
541
|
-
: functionalUpdate(dest.hash!, router.location.hash)
|
|
542
|
-
hash = hash ? `#${hash}` : ''
|
|
543
|
-
|
|
544
|
-
return {
|
|
545
|
-
pathname,
|
|
546
|
-
search,
|
|
547
|
-
searchStr,
|
|
548
|
-
state: router.location.state,
|
|
549
|
-
hash,
|
|
550
|
-
href: `${pathname}${searchStr}${hash}`,
|
|
551
|
-
key: dest.key,
|
|
552
|
-
}
|
|
553
|
-
},
|
|
554
|
-
|
|
555
|
-
commitLocation: (next: Location, replace?: boolean): Promise<void> => {
|
|
556
|
-
const id = '' + Date.now() + Math.random()
|
|
557
|
-
|
|
558
|
-
if (router.navigateTimeout) clearTimeout(router.navigateTimeout)
|
|
559
|
-
|
|
560
|
-
let nextAction: 'push' | 'replace' = 'replace'
|
|
561
|
-
|
|
562
|
-
if (!replace) {
|
|
563
|
-
nextAction = 'push'
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
const isSameUrl =
|
|
567
|
-
router.parseLocation(history.location).href === next.href
|
|
568
|
-
|
|
569
|
-
if (isSameUrl && !next.key) {
|
|
570
|
-
nextAction = 'replace'
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
if (nextAction === 'replace') {
|
|
574
|
-
history.replace(
|
|
575
|
-
{
|
|
576
|
-
pathname: next.pathname,
|
|
577
|
-
hash: next.hash,
|
|
578
|
-
search: next.searchStr,
|
|
579
|
-
},
|
|
580
|
-
{
|
|
581
|
-
id,
|
|
582
|
-
},
|
|
583
|
-
)
|
|
584
|
-
} else {
|
|
585
|
-
history.push(
|
|
586
|
-
{
|
|
587
|
-
pathname: next.pathname,
|
|
588
|
-
hash: next.hash,
|
|
589
|
-
search: next.searchStr,
|
|
590
|
-
},
|
|
591
|
-
{
|
|
592
|
-
id,
|
|
593
|
-
},
|
|
594
|
-
)
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
router.navigationPromise = new Promise((resolve) => {
|
|
598
|
-
const previousNavigationResolve = router.resolveNavigation
|
|
599
|
-
|
|
600
|
-
router.resolveNavigation = () => {
|
|
601
|
-
previousNavigationResolve()
|
|
602
|
-
resolve()
|
|
603
|
-
}
|
|
604
|
-
})
|
|
605
|
-
|
|
606
|
-
return router.navigationPromise
|
|
607
|
-
},
|
|
608
|
-
|
|
609
|
-
buildNext: (opts: BuildNextOptions) => {
|
|
610
|
-
const next = router.buildLocation(opts)
|
|
611
|
-
|
|
612
|
-
const matches = router.matchRoutes(next.pathname)
|
|
613
|
-
|
|
614
|
-
const __preSearchFilters = matches
|
|
615
|
-
.map((match) => match.options.preSearchFilters ?? [])
|
|
616
|
-
.flat()
|
|
617
|
-
.filter(Boolean)
|
|
618
|
-
|
|
619
|
-
const __postSearchFilters = matches
|
|
620
|
-
.map((match) => match.options.postSearchFilters ?? [])
|
|
621
|
-
.flat()
|
|
622
|
-
.filter(Boolean)
|
|
623
|
-
|
|
624
|
-
return router.buildLocation({
|
|
625
|
-
...opts,
|
|
626
|
-
__preSearchFilters,
|
|
627
|
-
__postSearchFilters,
|
|
628
|
-
})
|
|
629
|
-
},
|
|
630
|
-
|
|
631
532
|
cancelMatches: () => {
|
|
632
533
|
;[
|
|
633
534
|
...router.state.matches,
|
|
@@ -637,7 +538,7 @@ export function createRouter<
|
|
|
637
538
|
})
|
|
638
539
|
},
|
|
639
540
|
|
|
640
|
-
|
|
541
|
+
load: async (next?: Location) => {
|
|
641
542
|
const id = Math.random()
|
|
642
543
|
router.startedLoadingAt = id
|
|
643
544
|
|
|
@@ -646,22 +547,11 @@ export function createRouter<
|
|
|
646
547
|
router.location = next
|
|
647
548
|
}
|
|
648
549
|
|
|
649
|
-
// Clear out old actions
|
|
650
|
-
router.removeActionQueue.forEach(({ action, actionState }) => {
|
|
651
|
-
if (router.state.currentAction === actionState) {
|
|
652
|
-
router.state.currentAction = undefined
|
|
653
|
-
}
|
|
654
|
-
if (action.current === actionState) {
|
|
655
|
-
action.current = undefined
|
|
656
|
-
}
|
|
657
|
-
})
|
|
658
|
-
router.removeActionQueue = []
|
|
659
|
-
|
|
660
550
|
// Cancel any pending matches
|
|
661
551
|
router.cancelMatches()
|
|
662
552
|
|
|
663
553
|
// Match the routes
|
|
664
|
-
const matches = router.matchRoutes(location.pathname, {
|
|
554
|
+
const matches = router.matchRoutes(router.location.pathname, {
|
|
665
555
|
strictParseParams: true,
|
|
666
556
|
})
|
|
667
557
|
|
|
@@ -706,6 +596,13 @@ export function createRouter<
|
|
|
706
596
|
params: d.params,
|
|
707
597
|
search: d.routeSearch,
|
|
708
598
|
})
|
|
599
|
+
|
|
600
|
+
// // Clear actions
|
|
601
|
+
// if (d.action) {
|
|
602
|
+
// d.action.current = undefined
|
|
603
|
+
// d.action.submissions = []
|
|
604
|
+
// }
|
|
605
|
+
|
|
709
606
|
// Clear idle error states when match leaves
|
|
710
607
|
if (d.status === 'error' && !d.isFetching) {
|
|
711
608
|
d.status = 'idle'
|
|
@@ -739,19 +636,24 @@ export function createRouter<
|
|
|
739
636
|
params: d.params,
|
|
740
637
|
search: d.search,
|
|
741
638
|
})
|
|
639
|
+
delete router.matchCache[d.matchId]
|
|
742
640
|
})
|
|
743
641
|
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
await Promise.all(
|
|
747
|
-
matches.map((d) => d.__.loaderPromise || Promise.resolve()),
|
|
748
|
-
)
|
|
749
|
-
}
|
|
642
|
+
// router.notify()
|
|
643
|
+
|
|
750
644
|
if (router.startedLoadingAt !== id) {
|
|
751
645
|
// Ignore side-effects of match loading
|
|
752
646
|
return
|
|
753
647
|
}
|
|
754
648
|
|
|
649
|
+
matches.forEach((match) => {
|
|
650
|
+
// Clear actions
|
|
651
|
+
if (match.action) {
|
|
652
|
+
match.action.current = undefined
|
|
653
|
+
match.action.submissions = []
|
|
654
|
+
}
|
|
655
|
+
})
|
|
656
|
+
|
|
755
657
|
router.state = {
|
|
756
658
|
...router.state,
|
|
757
659
|
location: router.location,
|
|
@@ -922,43 +824,17 @@ export function createRouter<
|
|
|
922
824
|
},
|
|
923
825
|
|
|
924
826
|
loadMatches: async (resolvedMatches, loaderOpts) => {
|
|
925
|
-
const now = Date.now()
|
|
926
|
-
const minMaxAge = loaderOpts?.preload
|
|
927
|
-
? Math.max(loaderOpts?.maxAge, loaderOpts?.gcMaxAge)
|
|
928
|
-
: 0
|
|
929
|
-
|
|
930
827
|
const matchPromises = resolvedMatches.map(async (match) => {
|
|
931
828
|
// Validate the match (loads search params etc)
|
|
932
829
|
match.__.validate()
|
|
933
|
-
|
|
934
|
-
// If this is a preload, add it to the preload cache
|
|
935
|
-
if (loaderOpts?.preload && minMaxAge > 0) {
|
|
936
|
-
// If the match is currently active, don't preload it
|
|
937
|
-
if (router.state.matches.find((d) => d.matchId === match.matchId)) {
|
|
938
|
-
return
|
|
939
|
-
}
|
|
940
|
-
|
|
941
|
-
router.matchCache[match.matchId] = {
|
|
942
|
-
gc: now + loaderOpts.gcMaxAge,
|
|
943
|
-
match,
|
|
944
|
-
}
|
|
945
|
-
}
|
|
946
|
-
|
|
947
|
-
// If the match is invalid, errored or idle, trigger it to load
|
|
948
|
-
if (
|
|
949
|
-
(match.status === 'success' && match.getIsInvalid()) ||
|
|
950
|
-
match.status === 'error' ||
|
|
951
|
-
match.status === 'idle'
|
|
952
|
-
) {
|
|
953
|
-
const maxAge = loaderOpts?.preload ? loaderOpts?.maxAge : undefined
|
|
954
|
-
|
|
955
|
-
match.load({ maxAge })
|
|
956
|
-
}
|
|
830
|
+
match.load(loaderOpts)
|
|
957
831
|
|
|
958
832
|
if (match.status === 'loading') {
|
|
959
833
|
// If requested, start the pending timers
|
|
960
834
|
if (loaderOpts?.withPending) match.__.startPending()
|
|
835
|
+
}
|
|
961
836
|
|
|
837
|
+
if (match.__.loadPromise) {
|
|
962
838
|
// Wait for the first sign of activity from the match
|
|
963
839
|
// This might be completion, error, or a pending state
|
|
964
840
|
await match.__.loadPromise
|
|
@@ -986,7 +862,7 @@ export function createRouter<
|
|
|
986
862
|
},
|
|
987
863
|
|
|
988
864
|
reload: () =>
|
|
989
|
-
router.
|
|
865
|
+
router.__.navigate({
|
|
990
866
|
fromCurrent: true,
|
|
991
867
|
replace: true,
|
|
992
868
|
search: true,
|
|
@@ -1024,11 +900,6 @@ export function createRouter<
|
|
|
1024
900
|
})
|
|
1025
901
|
},
|
|
1026
902
|
|
|
1027
|
-
_navigate: (location: BuildNextOptions & { replace?: boolean }) => {
|
|
1028
|
-
const next = router.buildNext(location)
|
|
1029
|
-
return router.commitLocation(next, location.replace)
|
|
1030
|
-
},
|
|
1031
|
-
|
|
1032
903
|
navigate: async ({ from, to = '.', search, hash, replace, params }) => {
|
|
1033
904
|
// If this link simply reloads the current route,
|
|
1034
905
|
// make sure it has a new key so it will trigger a data refresh
|
|
@@ -1050,7 +921,7 @@ export function createRouter<
|
|
|
1050
921
|
'Attempting to navigate to external url with router.navigate!',
|
|
1051
922
|
)
|
|
1052
923
|
|
|
1053
|
-
return router.
|
|
924
|
+
return router.__.navigate({
|
|
1054
925
|
from: fromString,
|
|
1055
926
|
to: toString,
|
|
1056
927
|
search,
|
|
@@ -1134,7 +1005,7 @@ export function createRouter<
|
|
|
1134
1005
|
}
|
|
1135
1006
|
|
|
1136
1007
|
// All is well? Navigate!)
|
|
1137
|
-
router.
|
|
1008
|
+
router.__.navigate(nextOpts)
|
|
1138
1009
|
}
|
|
1139
1010
|
}
|
|
1140
1011
|
|
|
@@ -1186,10 +1057,230 @@ export function createRouter<
|
|
|
1186
1057
|
disabled,
|
|
1187
1058
|
}
|
|
1188
1059
|
},
|
|
1189
|
-
|
|
1060
|
+
buildNext: (opts: BuildNextOptions) => {
|
|
1061
|
+
const next = router.__.buildLocation(opts)
|
|
1062
|
+
|
|
1063
|
+
const matches = router.matchRoutes(next.pathname)
|
|
1064
|
+
|
|
1065
|
+
const __preSearchFilters = matches
|
|
1066
|
+
.map((match) => match.options.preSearchFilters ?? [])
|
|
1067
|
+
.flat()
|
|
1068
|
+
.filter(Boolean)
|
|
1069
|
+
|
|
1070
|
+
const __postSearchFilters = matches
|
|
1071
|
+
.map((match) => match.options.postSearchFilters ?? [])
|
|
1072
|
+
.flat()
|
|
1073
|
+
.filter(Boolean)
|
|
1074
|
+
|
|
1075
|
+
return router.__.buildLocation({
|
|
1076
|
+
...opts,
|
|
1077
|
+
__preSearchFilters,
|
|
1078
|
+
__postSearchFilters,
|
|
1079
|
+
})
|
|
1080
|
+
},
|
|
1190
1081
|
|
|
1191
|
-
|
|
1192
|
-
|
|
1082
|
+
__: {
|
|
1083
|
+
buildRouteTree: (rootRouteConfig: RouteConfig) => {
|
|
1084
|
+
const recurseRoutes = (
|
|
1085
|
+
routeConfigs: RouteConfig[],
|
|
1086
|
+
parent?: Route<TAllRouteInfo, any>,
|
|
1087
|
+
): Route<TAllRouteInfo, any>[] => {
|
|
1088
|
+
return routeConfigs.map((routeConfig) => {
|
|
1089
|
+
const routeOptions = routeConfig.options
|
|
1090
|
+
const route = createRoute(routeConfig, routeOptions, parent, router)
|
|
1091
|
+
const existingRoute = (router.routesById as any)[route.routeId]
|
|
1092
|
+
|
|
1093
|
+
if (existingRoute) {
|
|
1094
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
1095
|
+
console.warn(
|
|
1096
|
+
`Duplicate routes found with id: ${String(route.routeId)}`,
|
|
1097
|
+
router.routesById,
|
|
1098
|
+
route,
|
|
1099
|
+
)
|
|
1100
|
+
}
|
|
1101
|
+
throw new Error()
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
;(router.routesById as any)[route.routeId] = route
|
|
1105
|
+
|
|
1106
|
+
const children = routeConfig.children as RouteConfig[]
|
|
1107
|
+
|
|
1108
|
+
route.childRoutes = children?.length
|
|
1109
|
+
? recurseRoutes(children, route)
|
|
1110
|
+
: undefined
|
|
1111
|
+
|
|
1112
|
+
return route
|
|
1113
|
+
})
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
const routes = recurseRoutes([rootRouteConfig])
|
|
1117
|
+
|
|
1118
|
+
return routes[0]!
|
|
1119
|
+
},
|
|
1120
|
+
|
|
1121
|
+
parseLocation: (
|
|
1122
|
+
location: History['location'],
|
|
1123
|
+
previousLocation?: Location,
|
|
1124
|
+
): Location => {
|
|
1125
|
+
const parsedSearch = router.options.parseSearch(location.search)
|
|
1126
|
+
|
|
1127
|
+
return {
|
|
1128
|
+
pathname: location.pathname,
|
|
1129
|
+
searchStr: location.search,
|
|
1130
|
+
search: replaceEqualDeep(previousLocation?.search, parsedSearch),
|
|
1131
|
+
hash: location.hash.split('#').reverse()[0] ?? '',
|
|
1132
|
+
href: `${location.pathname}${location.search}${location.hash}`,
|
|
1133
|
+
state: location.state as LocationState,
|
|
1134
|
+
key: location.key,
|
|
1135
|
+
}
|
|
1136
|
+
},
|
|
1137
|
+
|
|
1138
|
+
navigate: (location: BuildNextOptions & { replace?: boolean }) => {
|
|
1139
|
+
const next = router.buildNext(location)
|
|
1140
|
+
return router.__.commitLocation(next, location.replace)
|
|
1141
|
+
},
|
|
1142
|
+
|
|
1143
|
+
buildLocation: (dest: BuildNextOptions = {}): Location => {
|
|
1144
|
+
// const resolvedFrom: Location = {
|
|
1145
|
+
// ...router.location,
|
|
1146
|
+
const fromPathname = dest.fromCurrent
|
|
1147
|
+
? router.location.pathname
|
|
1148
|
+
: dest.from ?? router.location.pathname
|
|
1149
|
+
|
|
1150
|
+
let pathname = resolvePath(
|
|
1151
|
+
router.basepath ?? '/',
|
|
1152
|
+
fromPathname,
|
|
1153
|
+
`${dest.to ?? '.'}`,
|
|
1154
|
+
)
|
|
1155
|
+
|
|
1156
|
+
const fromMatches = router.matchRoutes(router.location.pathname, {
|
|
1157
|
+
strictParseParams: true,
|
|
1158
|
+
})
|
|
1159
|
+
|
|
1160
|
+
const toMatches = router.matchRoutes(pathname)
|
|
1161
|
+
|
|
1162
|
+
const prevParams = { ...last(fromMatches)?.params }
|
|
1163
|
+
|
|
1164
|
+
let nextParams =
|
|
1165
|
+
(dest.params ?? true) === true
|
|
1166
|
+
? prevParams
|
|
1167
|
+
: functionalUpdate(dest.params!, prevParams)
|
|
1168
|
+
|
|
1169
|
+
if (nextParams) {
|
|
1170
|
+
toMatches
|
|
1171
|
+
.map((d) => d.options.stringifyParams)
|
|
1172
|
+
.filter(Boolean)
|
|
1173
|
+
.forEach((fn) => {
|
|
1174
|
+
Object.assign({}, nextParams!, fn!(nextParams!))
|
|
1175
|
+
})
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
pathname = interpolatePath(pathname, nextParams ?? {})
|
|
1179
|
+
|
|
1180
|
+
// Pre filters first
|
|
1181
|
+
const preFilteredSearch = dest.__preSearchFilters?.length
|
|
1182
|
+
? dest.__preSearchFilters.reduce(
|
|
1183
|
+
(prev, next) => next(prev),
|
|
1184
|
+
router.location.search,
|
|
1185
|
+
)
|
|
1186
|
+
: router.location.search
|
|
1187
|
+
|
|
1188
|
+
// Then the link/navigate function
|
|
1189
|
+
const destSearch =
|
|
1190
|
+
dest.search === true
|
|
1191
|
+
? preFilteredSearch // Preserve resolvedFrom true
|
|
1192
|
+
: dest.search
|
|
1193
|
+
? functionalUpdate(dest.search, preFilteredSearch) ?? {} // Updater
|
|
1194
|
+
: dest.__preSearchFilters?.length
|
|
1195
|
+
? preFilteredSearch // Preserve resolvedFrom filters
|
|
1196
|
+
: {}
|
|
1197
|
+
|
|
1198
|
+
// Then post filters
|
|
1199
|
+
const postFilteredSearch = dest.__postSearchFilters?.length
|
|
1200
|
+
? dest.__postSearchFilters.reduce(
|
|
1201
|
+
(prev, next) => next(prev),
|
|
1202
|
+
destSearch,
|
|
1203
|
+
)
|
|
1204
|
+
: destSearch
|
|
1205
|
+
|
|
1206
|
+
const search = replaceEqualDeep(
|
|
1207
|
+
router.location.search,
|
|
1208
|
+
postFilteredSearch,
|
|
1209
|
+
)
|
|
1210
|
+
|
|
1211
|
+
const searchStr = router.options.stringifySearch(search)
|
|
1212
|
+
let hash =
|
|
1213
|
+
dest.hash === true
|
|
1214
|
+
? router.location.hash
|
|
1215
|
+
: functionalUpdate(dest.hash!, router.location.hash)
|
|
1216
|
+
hash = hash ? `#${hash}` : ''
|
|
1217
|
+
|
|
1218
|
+
return {
|
|
1219
|
+
pathname,
|
|
1220
|
+
search,
|
|
1221
|
+
searchStr,
|
|
1222
|
+
state: router.location.state,
|
|
1223
|
+
hash,
|
|
1224
|
+
href: `${pathname}${searchStr}${hash}`,
|
|
1225
|
+
key: dest.key,
|
|
1226
|
+
}
|
|
1227
|
+
},
|
|
1228
|
+
|
|
1229
|
+
commitLocation: (next: Location, replace?: boolean): Promise<void> => {
|
|
1230
|
+
const id = '' + Date.now() + Math.random()
|
|
1231
|
+
|
|
1232
|
+
if (router.navigateTimeout) clearTimeout(router.navigateTimeout)
|
|
1233
|
+
|
|
1234
|
+
let nextAction: 'push' | 'replace' = 'replace'
|
|
1235
|
+
|
|
1236
|
+
if (!replace) {
|
|
1237
|
+
nextAction = 'push'
|
|
1238
|
+
}
|
|
1239
|
+
|
|
1240
|
+
const isSameUrl =
|
|
1241
|
+
router.__.parseLocation(history.location).href === next.href
|
|
1242
|
+
|
|
1243
|
+
if (isSameUrl && !next.key) {
|
|
1244
|
+
nextAction = 'replace'
|
|
1245
|
+
}
|
|
1246
|
+
|
|
1247
|
+
if (nextAction === 'replace') {
|
|
1248
|
+
history.replace(
|
|
1249
|
+
{
|
|
1250
|
+
pathname: next.pathname,
|
|
1251
|
+
hash: next.hash,
|
|
1252
|
+
search: next.searchStr,
|
|
1253
|
+
},
|
|
1254
|
+
{
|
|
1255
|
+
id,
|
|
1256
|
+
},
|
|
1257
|
+
)
|
|
1258
|
+
} else {
|
|
1259
|
+
history.push(
|
|
1260
|
+
{
|
|
1261
|
+
pathname: next.pathname,
|
|
1262
|
+
hash: next.hash,
|
|
1263
|
+
search: next.searchStr,
|
|
1264
|
+
},
|
|
1265
|
+
{
|
|
1266
|
+
id,
|
|
1267
|
+
},
|
|
1268
|
+
)
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
router.navigationPromise = new Promise((resolve) => {
|
|
1272
|
+
const previousNavigationResolve = router.resolveNavigation
|
|
1273
|
+
|
|
1274
|
+
router.resolveNavigation = () => {
|
|
1275
|
+
previousNavigationResolve()
|
|
1276
|
+
resolve()
|
|
1277
|
+
}
|
|
1278
|
+
})
|
|
1279
|
+
|
|
1280
|
+
return router.navigationPromise
|
|
1281
|
+
},
|
|
1282
|
+
},
|
|
1283
|
+
}
|
|
1193
1284
|
|
|
1194
1285
|
router.update(userOptions)
|
|
1195
1286
|
|
|
@@ -1202,3 +1293,16 @@ export function createRouter<
|
|
|
1202
1293
|
function isCtrlEvent(e: MouseEvent) {
|
|
1203
1294
|
return !!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey)
|
|
1204
1295
|
}
|
|
1296
|
+
|
|
1297
|
+
function cascadeLoaderData(matches: RouteMatch<any, any>[]) {
|
|
1298
|
+
matches.forEach((match, index) => {
|
|
1299
|
+
const parent = matches[index - 1]
|
|
1300
|
+
|
|
1301
|
+
if (parent) {
|
|
1302
|
+
match.loaderData = replaceEqualDeep(match.loaderData, {
|
|
1303
|
+
...parent.loaderData,
|
|
1304
|
+
...match.routeLoaderData,
|
|
1305
|
+
})
|
|
1306
|
+
}
|
|
1307
|
+
})
|
|
1308
|
+
}
|