@tanstack/router-core 0.0.1-alpha.9 → 0.0.1-beta.2
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/route.js +35 -0
- 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 +28 -23
- package/build/cjs/packages/router-core/src/routeMatch.js.map +1 -1
- package/build/cjs/packages/router-core/src/router.js +180 -194
- package/build/cjs/packages/router-core/src/router.js.map +1 -1
- package/build/esm/index.js +243 -217
- package/build/esm/index.js.map +1 -1
- package/build/stats-html.html +1 -1
- package/build/stats-react.json +136 -136
- package/build/types/index.d.ts +73 -34
- package/build/umd/index.development.js +243 -217
- 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/route.ts +64 -2
- package/src/routeConfig.ts +27 -55
- package/src/routeMatch.ts +45 -32
- package/src/router.ts +303 -279
package/src/router.ts
CHANGED
|
@@ -25,8 +25,11 @@ import {
|
|
|
25
25
|
} from './path'
|
|
26
26
|
import { AnyRoute, cascadeLoaderData, 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'
|
|
@@ -43,6 +46,7 @@ import {
|
|
|
43
46
|
functionalUpdate,
|
|
44
47
|
last,
|
|
45
48
|
PickAsRequired,
|
|
49
|
+
PickRequired,
|
|
46
50
|
replaceEqualDeep,
|
|
47
51
|
Timeout,
|
|
48
52
|
Updater,
|
|
@@ -99,6 +103,9 @@ export interface RouterOptions<TRouteConfig extends AnyRouteConfig> {
|
|
|
99
103
|
basepath?: string
|
|
100
104
|
createRouter?: (router: Router<any, any>) => void
|
|
101
105
|
createRoute?: (opts: { route: AnyRoute; router: Router<any, any> }) => void
|
|
106
|
+
createElement?: (
|
|
107
|
+
element: GetFrameworkGeneric<'SyncOrAsyncElement'>,
|
|
108
|
+
) => Promise<GetFrameworkGeneric<'Element'>>
|
|
102
109
|
}
|
|
103
110
|
|
|
104
111
|
export interface Action<
|
|
@@ -124,6 +131,42 @@ export interface ActionState<
|
|
|
124
131
|
error?: unknown
|
|
125
132
|
}
|
|
126
133
|
|
|
134
|
+
export interface Loader<
|
|
135
|
+
TFullSearchSchema extends AnySearchSchema = {},
|
|
136
|
+
TAllParams extends AnyPathParams = {},
|
|
137
|
+
TRouteLoaderData = AnyLoaderData,
|
|
138
|
+
> {
|
|
139
|
+
fetch: keyof PickRequired<TFullSearchSchema> extends never
|
|
140
|
+
? keyof TAllParams extends never
|
|
141
|
+
? (loaderContext: { signal?: AbortSignal }) => Promise<TRouteLoaderData>
|
|
142
|
+
: (loaderContext: {
|
|
143
|
+
params: TAllParams
|
|
144
|
+
search?: TFullSearchSchema
|
|
145
|
+
signal?: AbortSignal
|
|
146
|
+
}) => Promise<TRouteLoaderData>
|
|
147
|
+
: keyof TAllParams extends never
|
|
148
|
+
? (loaderContext: {
|
|
149
|
+
search: TFullSearchSchema
|
|
150
|
+
params: TAllParams
|
|
151
|
+
signal?: AbortSignal
|
|
152
|
+
}) => Promise<TRouteLoaderData>
|
|
153
|
+
: (loaderContext: {
|
|
154
|
+
search: TFullSearchSchema
|
|
155
|
+
signal?: AbortSignal
|
|
156
|
+
}) => Promise<TRouteLoaderData>
|
|
157
|
+
current?: LoaderState<TFullSearchSchema, TAllParams>
|
|
158
|
+
latest?: LoaderState<TFullSearchSchema, TAllParams>
|
|
159
|
+
pending: LoaderState<TFullSearchSchema, TAllParams>[]
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export interface LoaderState<
|
|
163
|
+
TFullSearchSchema = unknown,
|
|
164
|
+
TAllParams = unknown,
|
|
165
|
+
> {
|
|
166
|
+
loadedAt: number
|
|
167
|
+
loaderContext: LoaderContext<TFullSearchSchema, TAllParams>
|
|
168
|
+
}
|
|
169
|
+
|
|
127
170
|
export interface RouterState {
|
|
128
171
|
status: 'idle' | 'loading'
|
|
129
172
|
location: Location
|
|
@@ -133,6 +176,7 @@ export interface RouterState {
|
|
|
133
176
|
currentAction?: ActionState
|
|
134
177
|
latestAction?: ActionState
|
|
135
178
|
actions: Record<string, Action>
|
|
179
|
+
loaders: Record<string, Loader>
|
|
136
180
|
pending?: PendingState
|
|
137
181
|
isFetching: boolean
|
|
138
182
|
isPreloading: boolean
|
|
@@ -143,7 +187,7 @@ export interface PendingState {
|
|
|
143
187
|
matches: RouteMatch[]
|
|
144
188
|
}
|
|
145
189
|
|
|
146
|
-
type Listener = () => void
|
|
190
|
+
type Listener = (router: Router<any, any>) => void
|
|
147
191
|
|
|
148
192
|
export type ListenerFn = () => void
|
|
149
193
|
|
|
@@ -185,6 +229,7 @@ export interface Router<
|
|
|
185
229
|
TRouteConfig extends AnyRouteConfig = RouteConfig,
|
|
186
230
|
TAllRouteInfo extends AnyAllRouteInfo = AllRouteInfo<TRouteConfig>,
|
|
187
231
|
> {
|
|
232
|
+
history: BrowserHistory | MemoryHistory | HashHistory
|
|
188
233
|
options: PickAsRequired<
|
|
189
234
|
RouterOptions<TRouteConfig>,
|
|
190
235
|
'stringifySearch' | 'parseSearch'
|
|
@@ -211,15 +256,7 @@ export interface Router<
|
|
|
211
256
|
update: <TRouteConfig extends RouteConfig = RouteConfig>(
|
|
212
257
|
opts?: RouterOptions<TRouteConfig>,
|
|
213
258
|
) => 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>
|
|
259
|
+
|
|
223
260
|
buildNext: (opts: BuildNextOptions) => Location
|
|
224
261
|
cancelMatches: () => void
|
|
225
262
|
loadLocation: (next?: Location) => Promise<void>
|
|
@@ -247,9 +284,6 @@ export interface Router<
|
|
|
247
284
|
invalidateRoute: (opts: MatchLocation) => void
|
|
248
285
|
reload: () => Promise<void>
|
|
249
286
|
resolvePath: (from: string, path: string) => string
|
|
250
|
-
_navigate: (
|
|
251
|
-
location: BuildNextOptions & { replace?: boolean },
|
|
252
|
-
) => Promise<void>
|
|
253
287
|
navigate: <
|
|
254
288
|
TFrom extends ValidFromPath<TAllRouteInfo> = '/',
|
|
255
289
|
TTo extends string = '.',
|
|
@@ -269,6 +303,20 @@ export interface Router<
|
|
|
269
303
|
>(
|
|
270
304
|
opts: LinkOptions<TAllRouteInfo, TFrom, TTo>,
|
|
271
305
|
) => LinkInfo
|
|
306
|
+
__: {
|
|
307
|
+
buildRouteTree: (
|
|
308
|
+
routeConfig: RouteConfig,
|
|
309
|
+
) => Route<TAllRouteInfo, AnyRouteInfo>
|
|
310
|
+
parseLocation: (
|
|
311
|
+
location: History['location'],
|
|
312
|
+
previousLocation?: Location,
|
|
313
|
+
) => Location
|
|
314
|
+
buildLocation: (dest: BuildNextOptions) => Location
|
|
315
|
+
commitLocation: (next: Location, replace?: boolean) => Promise<void>
|
|
316
|
+
navigate: (
|
|
317
|
+
location: BuildNextOptions & { replace?: boolean },
|
|
318
|
+
) => Promise<void>
|
|
319
|
+
}
|
|
272
320
|
}
|
|
273
321
|
|
|
274
322
|
// Detect if we're in the DOM
|
|
@@ -299,6 +347,7 @@ export function createRouter<
|
|
|
299
347
|
}
|
|
300
348
|
|
|
301
349
|
let router: Router<TRouteConfig, TAllRouteInfo> = {
|
|
350
|
+
history,
|
|
302
351
|
options: originalOptions,
|
|
303
352
|
listeners: [],
|
|
304
353
|
removeActionQueue: [],
|
|
@@ -317,6 +366,7 @@ export function createRouter<
|
|
|
317
366
|
location: null!,
|
|
318
367
|
matches: [],
|
|
319
368
|
actions: {},
|
|
369
|
+
loaders: {},
|
|
320
370
|
loaderData: {} as any,
|
|
321
371
|
lastUpdated: Date.now(),
|
|
322
372
|
isFetching: false,
|
|
@@ -346,11 +396,11 @@ export function createRouter<
|
|
|
346
396
|
}
|
|
347
397
|
|
|
348
398
|
cascadeLoaderData(router.state.matches)
|
|
349
|
-
router.listeners.forEach((listener) => listener())
|
|
399
|
+
router.listeners.forEach((listener) => listener(router))
|
|
350
400
|
},
|
|
351
401
|
|
|
352
402
|
mount: () => {
|
|
353
|
-
const next = router.buildLocation({
|
|
403
|
+
const next = router.__.buildLocation({
|
|
354
404
|
to: '.',
|
|
355
405
|
search: true,
|
|
356
406
|
hash: true,
|
|
@@ -359,14 +409,14 @@ export function createRouter<
|
|
|
359
409
|
// If the current location isn't updated, trigger a navigation
|
|
360
410
|
// to the current location. Otherwise, load the current location.
|
|
361
411
|
if (next.href !== router.location.href) {
|
|
362
|
-
router.commitLocation(next, true)
|
|
412
|
+
router.__.commitLocation(next, true)
|
|
363
413
|
} else {
|
|
364
414
|
router.loadLocation()
|
|
365
415
|
}
|
|
366
416
|
|
|
367
417
|
const unsub = history.listen((event) => {
|
|
368
418
|
router.loadLocation(
|
|
369
|
-
router.parseLocation(event.location, router.location),
|
|
419
|
+
router.__.parseLocation(event.location, router.location),
|
|
370
420
|
)
|
|
371
421
|
})
|
|
372
422
|
|
|
@@ -399,235 +449,12 @@ export function createRouter<
|
|
|
399
449
|
|
|
400
450
|
if (routeConfig) {
|
|
401
451
|
router.routesById = {} as any
|
|
402
|
-
router.routeTree = router.buildRouteTree(routeConfig)
|
|
452
|
+
router.routeTree = router.__.buildRouteTree(routeConfig)
|
|
403
453
|
}
|
|
404
454
|
|
|
405
455
|
return router as any
|
|
406
456
|
},
|
|
407
457
|
|
|
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
458
|
cancelMatches: () => {
|
|
632
459
|
;[
|
|
633
460
|
...router.state.matches,
|
|
@@ -739,6 +566,7 @@ export function createRouter<
|
|
|
739
566
|
params: d.params,
|
|
740
567
|
search: d.search,
|
|
741
568
|
})
|
|
569
|
+
delete router.matchCache[d.matchId]
|
|
742
570
|
})
|
|
743
571
|
|
|
744
572
|
if (matches.some((d) => d.status === 'loading')) {
|
|
@@ -922,38 +750,10 @@ export function createRouter<
|
|
|
922
750
|
},
|
|
923
751
|
|
|
924
752
|
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
753
|
const matchPromises = resolvedMatches.map(async (match) => {
|
|
931
754
|
// Validate the match (loads search params etc)
|
|
932
755
|
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
|
-
}
|
|
756
|
+
match.load(loaderOpts)
|
|
957
757
|
|
|
958
758
|
if (match.status === 'loading') {
|
|
959
759
|
// If requested, start the pending timers
|
|
@@ -986,7 +786,7 @@ export function createRouter<
|
|
|
986
786
|
},
|
|
987
787
|
|
|
988
788
|
reload: () =>
|
|
989
|
-
router.
|
|
789
|
+
router.__.navigate({
|
|
990
790
|
fromCurrent: true,
|
|
991
791
|
replace: true,
|
|
992
792
|
search: true,
|
|
@@ -1024,11 +824,6 @@ export function createRouter<
|
|
|
1024
824
|
})
|
|
1025
825
|
},
|
|
1026
826
|
|
|
1027
|
-
_navigate: (location: BuildNextOptions & { replace?: boolean }) => {
|
|
1028
|
-
const next = router.buildNext(location)
|
|
1029
|
-
return router.commitLocation(next, location.replace)
|
|
1030
|
-
},
|
|
1031
|
-
|
|
1032
827
|
navigate: async ({ from, to = '.', search, hash, replace, params }) => {
|
|
1033
828
|
// If this link simply reloads the current route,
|
|
1034
829
|
// make sure it has a new key so it will trigger a data refresh
|
|
@@ -1050,7 +845,7 @@ export function createRouter<
|
|
|
1050
845
|
'Attempting to navigate to external url with router.navigate!',
|
|
1051
846
|
)
|
|
1052
847
|
|
|
1053
|
-
return router.
|
|
848
|
+
return router.__.navigate({
|
|
1054
849
|
from: fromString,
|
|
1055
850
|
to: toString,
|
|
1056
851
|
search,
|
|
@@ -1134,7 +929,7 @@ export function createRouter<
|
|
|
1134
929
|
}
|
|
1135
930
|
|
|
1136
931
|
// All is well? Navigate!)
|
|
1137
|
-
router.
|
|
932
|
+
router.__.navigate(nextOpts)
|
|
1138
933
|
}
|
|
1139
934
|
}
|
|
1140
935
|
|
|
@@ -1186,9 +981,238 @@ export function createRouter<
|
|
|
1186
981
|
disabled,
|
|
1187
982
|
}
|
|
1188
983
|
},
|
|
984
|
+
buildNext: (opts: BuildNextOptions) => {
|
|
985
|
+
const next = router.__.buildLocation(opts)
|
|
986
|
+
|
|
987
|
+
const matches = router.matchRoutes(next.pathname)
|
|
988
|
+
|
|
989
|
+
const __preSearchFilters = matches
|
|
990
|
+
.map((match) => match.options.preSearchFilters ?? [])
|
|
991
|
+
.flat()
|
|
992
|
+
.filter(Boolean)
|
|
993
|
+
|
|
994
|
+
const __postSearchFilters = matches
|
|
995
|
+
.map((match) => match.options.postSearchFilters ?? [])
|
|
996
|
+
.flat()
|
|
997
|
+
.filter(Boolean)
|
|
998
|
+
|
|
999
|
+
return router.__.buildLocation({
|
|
1000
|
+
...opts,
|
|
1001
|
+
__preSearchFilters,
|
|
1002
|
+
__postSearchFilters,
|
|
1003
|
+
})
|
|
1004
|
+
},
|
|
1005
|
+
|
|
1006
|
+
__: {
|
|
1007
|
+
buildRouteTree: (rootRouteConfig: RouteConfig) => {
|
|
1008
|
+
const recurseRoutes = (
|
|
1009
|
+
routeConfigs: RouteConfig[],
|
|
1010
|
+
parent?: Route<TAllRouteInfo, any>,
|
|
1011
|
+
): Route<TAllRouteInfo, any>[] => {
|
|
1012
|
+
return routeConfigs.map((routeConfig) => {
|
|
1013
|
+
const routeOptions = routeConfig.options
|
|
1014
|
+
const route = createRoute(routeConfig, routeOptions, parent, router)
|
|
1015
|
+
|
|
1016
|
+
// {
|
|
1017
|
+
// pendingMs: routeOptions.pendingMs ?? router.defaultPendingMs,
|
|
1018
|
+
// pendingMinMs: routeOptions.pendingMinMs ?? router.defaultPendingMinMs,
|
|
1019
|
+
// }
|
|
1020
|
+
|
|
1021
|
+
const existingRoute = (router.routesById as any)[route.routeId]
|
|
1022
|
+
|
|
1023
|
+
if (existingRoute) {
|
|
1024
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
1025
|
+
console.warn(
|
|
1026
|
+
`Duplicate routes found with id: ${String(route.routeId)}`,
|
|
1027
|
+
router.routesById,
|
|
1028
|
+
route,
|
|
1029
|
+
)
|
|
1030
|
+
}
|
|
1031
|
+
throw new Error()
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
;(router.routesById as any)[route.routeId] = route
|
|
1035
|
+
|
|
1036
|
+
const children = routeConfig.children as RouteConfig[]
|
|
1037
|
+
|
|
1038
|
+
route.childRoutes = children?.length
|
|
1039
|
+
? recurseRoutes(children, route)
|
|
1040
|
+
: undefined
|
|
1041
|
+
|
|
1042
|
+
return route
|
|
1043
|
+
})
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
const routes = recurseRoutes([rootRouteConfig])
|
|
1047
|
+
|
|
1048
|
+
return routes[0]!
|
|
1049
|
+
},
|
|
1050
|
+
|
|
1051
|
+
parseLocation: (
|
|
1052
|
+
location: History['location'],
|
|
1053
|
+
previousLocation?: Location,
|
|
1054
|
+
): Location => {
|
|
1055
|
+
const parsedSearch = router.options.parseSearch(location.search)
|
|
1056
|
+
|
|
1057
|
+
return {
|
|
1058
|
+
pathname: location.pathname,
|
|
1059
|
+
searchStr: location.search,
|
|
1060
|
+
search: replaceEqualDeep(previousLocation?.search, parsedSearch),
|
|
1061
|
+
hash: location.hash.split('#').reverse()[0] ?? '',
|
|
1062
|
+
href: `${location.pathname}${location.search}${location.hash}`,
|
|
1063
|
+
state: location.state as LocationState,
|
|
1064
|
+
key: location.key,
|
|
1065
|
+
}
|
|
1066
|
+
},
|
|
1067
|
+
|
|
1068
|
+
navigate: (location: BuildNextOptions & { replace?: boolean }) => {
|
|
1069
|
+
const next = router.buildNext(location)
|
|
1070
|
+
return router.__.commitLocation(next, location.replace)
|
|
1071
|
+
},
|
|
1072
|
+
|
|
1073
|
+
buildLocation: (dest: BuildNextOptions = {}): Location => {
|
|
1074
|
+
// const resolvedFrom: Location = {
|
|
1075
|
+
// ...router.location,
|
|
1076
|
+
const fromPathname = dest.fromCurrent
|
|
1077
|
+
? router.location.pathname
|
|
1078
|
+
: dest.from ?? router.location.pathname
|
|
1079
|
+
|
|
1080
|
+
let pathname = resolvePath(
|
|
1081
|
+
router.basepath ?? '/',
|
|
1082
|
+
fromPathname,
|
|
1083
|
+
`${dest.to ?? '.'}`,
|
|
1084
|
+
)
|
|
1085
|
+
|
|
1086
|
+
const fromMatches = router.matchRoutes(router.location.pathname, {
|
|
1087
|
+
strictParseParams: true,
|
|
1088
|
+
})
|
|
1089
|
+
|
|
1090
|
+
const toMatches = router.matchRoutes(pathname)
|
|
1091
|
+
|
|
1092
|
+
const prevParams = { ...last(fromMatches)?.params }
|
|
1093
|
+
|
|
1094
|
+
let nextParams =
|
|
1095
|
+
(dest.params ?? true) === true
|
|
1096
|
+
? prevParams
|
|
1097
|
+
: functionalUpdate(dest.params!, prevParams)
|
|
1098
|
+
|
|
1099
|
+
if (nextParams) {
|
|
1100
|
+
toMatches
|
|
1101
|
+
.map((d) => d.options.stringifyParams)
|
|
1102
|
+
.filter(Boolean)
|
|
1103
|
+
.forEach((fn) => {
|
|
1104
|
+
Object.assign({}, nextParams!, fn!(nextParams!))
|
|
1105
|
+
})
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1108
|
+
pathname = interpolatePath(pathname, nextParams ?? {})
|
|
1109
|
+
|
|
1110
|
+
// Pre filters first
|
|
1111
|
+
const preFilteredSearch = dest.__preSearchFilters?.length
|
|
1112
|
+
? dest.__preSearchFilters.reduce(
|
|
1113
|
+
(prev, next) => next(prev),
|
|
1114
|
+
router.location.search,
|
|
1115
|
+
)
|
|
1116
|
+
: router.location.search
|
|
1117
|
+
|
|
1118
|
+
// Then the link/navigate function
|
|
1119
|
+
const destSearch =
|
|
1120
|
+
dest.search === true
|
|
1121
|
+
? preFilteredSearch // Preserve resolvedFrom true
|
|
1122
|
+
: dest.search
|
|
1123
|
+
? functionalUpdate(dest.search, preFilteredSearch) ?? {} // Updater
|
|
1124
|
+
: dest.__preSearchFilters?.length
|
|
1125
|
+
? preFilteredSearch // Preserve resolvedFrom filters
|
|
1126
|
+
: {}
|
|
1127
|
+
|
|
1128
|
+
// Then post filters
|
|
1129
|
+
const postFilteredSearch = dest.__postSearchFilters?.length
|
|
1130
|
+
? dest.__postSearchFilters.reduce(
|
|
1131
|
+
(prev, next) => next(prev),
|
|
1132
|
+
destSearch,
|
|
1133
|
+
)
|
|
1134
|
+
: destSearch
|
|
1135
|
+
|
|
1136
|
+
const search = replaceEqualDeep(
|
|
1137
|
+
router.location.search,
|
|
1138
|
+
postFilteredSearch,
|
|
1139
|
+
)
|
|
1140
|
+
|
|
1141
|
+
const searchStr = router.options.stringifySearch(search)
|
|
1142
|
+
let hash =
|
|
1143
|
+
dest.hash === true
|
|
1144
|
+
? router.location.hash
|
|
1145
|
+
: functionalUpdate(dest.hash!, router.location.hash)
|
|
1146
|
+
hash = hash ? `#${hash}` : ''
|
|
1147
|
+
|
|
1148
|
+
return {
|
|
1149
|
+
pathname,
|
|
1150
|
+
search,
|
|
1151
|
+
searchStr,
|
|
1152
|
+
state: router.location.state,
|
|
1153
|
+
hash,
|
|
1154
|
+
href: `${pathname}${searchStr}${hash}`,
|
|
1155
|
+
key: dest.key,
|
|
1156
|
+
}
|
|
1157
|
+
},
|
|
1158
|
+
|
|
1159
|
+
commitLocation: (next: Location, replace?: boolean): Promise<void> => {
|
|
1160
|
+
const id = '' + Date.now() + Math.random()
|
|
1161
|
+
|
|
1162
|
+
if (router.navigateTimeout) clearTimeout(router.navigateTimeout)
|
|
1163
|
+
|
|
1164
|
+
let nextAction: 'push' | 'replace' = 'replace'
|
|
1165
|
+
|
|
1166
|
+
if (!replace) {
|
|
1167
|
+
nextAction = 'push'
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
const isSameUrl =
|
|
1171
|
+
router.__.parseLocation(history.location).href === next.href
|
|
1172
|
+
|
|
1173
|
+
if (isSameUrl && !next.key) {
|
|
1174
|
+
nextAction = 'replace'
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
if (nextAction === 'replace') {
|
|
1178
|
+
history.replace(
|
|
1179
|
+
{
|
|
1180
|
+
pathname: next.pathname,
|
|
1181
|
+
hash: next.hash,
|
|
1182
|
+
search: next.searchStr,
|
|
1183
|
+
},
|
|
1184
|
+
{
|
|
1185
|
+
id,
|
|
1186
|
+
},
|
|
1187
|
+
)
|
|
1188
|
+
} else {
|
|
1189
|
+
history.push(
|
|
1190
|
+
{
|
|
1191
|
+
pathname: next.pathname,
|
|
1192
|
+
hash: next.hash,
|
|
1193
|
+
search: next.searchStr,
|
|
1194
|
+
},
|
|
1195
|
+
{
|
|
1196
|
+
id,
|
|
1197
|
+
},
|
|
1198
|
+
)
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
router.navigationPromise = new Promise((resolve) => {
|
|
1202
|
+
const previousNavigationResolve = router.resolveNavigation
|
|
1203
|
+
|
|
1204
|
+
router.resolveNavigation = () => {
|
|
1205
|
+
previousNavigationResolve()
|
|
1206
|
+
resolve()
|
|
1207
|
+
}
|
|
1208
|
+
})
|
|
1209
|
+
|
|
1210
|
+
return router.navigationPromise
|
|
1211
|
+
},
|
|
1212
|
+
},
|
|
1189
1213
|
}
|
|
1190
1214
|
|
|
1191
|
-
router.location = router.parseLocation(history.location)
|
|
1215
|
+
router.location = router.__.parseLocation(history.location)
|
|
1192
1216
|
router.state.location = router.location
|
|
1193
1217
|
|
|
1194
1218
|
router.update(userOptions)
|