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