@tanstack/router-core 0.0.1-alpha.8 → 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 +33 -27
- package/build/cjs/packages/router-core/src/routeMatch.js.map +1 -1
- package/build/cjs/packages/router-core/src/router.js +214 -211
- package/build/cjs/packages/router-core/src/router.js.map +1 -1
- package/build/esm/index.js +282 -238
- 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 +85 -37
- package/build/umd/index.development.js +282 -238
- 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 +48 -18
- package/src/route.ts +64 -2
- package/src/routeConfig.ts +27 -55
- package/src/routeMatch.ts +54 -37
- package/src/router.ts +347 -301
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,
|
|
@@ -81,9 +85,10 @@ export interface RouterOptions<TRouteConfig extends AnyRouteConfig> {
|
|
|
81
85
|
stringifySearch?: SearchSerializer
|
|
82
86
|
parseSearch?: SearchParser
|
|
83
87
|
filterRoutes?: FilterRoutesFn
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
88
|
+
defaultPreload?: false | 'intent'
|
|
89
|
+
defaultPreloadMaxAge?: number
|
|
90
|
+
defaultPreloadGcMaxAge?: number
|
|
91
|
+
defaultPreloadDelay?: number
|
|
87
92
|
useErrorBoundary?: boolean
|
|
88
93
|
defaultElement?: GetFrameworkGeneric<'Element'>
|
|
89
94
|
defaultErrorElement?: GetFrameworkGeneric<'Element'>
|
|
@@ -98,6 +103,11 @@ export interface RouterOptions<TRouteConfig extends AnyRouteConfig> {
|
|
|
98
103
|
basepath?: string
|
|
99
104
|
createRouter?: (router: Router<any, any>) => void
|
|
100
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'>>
|
|
101
111
|
}
|
|
102
112
|
|
|
103
113
|
export interface Action<
|
|
@@ -123,6 +133,42 @@ export interface ActionState<
|
|
|
123
133
|
error?: unknown
|
|
124
134
|
}
|
|
125
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
|
+
|
|
126
172
|
export interface RouterState {
|
|
127
173
|
status: 'idle' | 'loading'
|
|
128
174
|
location: Location
|
|
@@ -132,6 +178,7 @@ export interface RouterState {
|
|
|
132
178
|
currentAction?: ActionState
|
|
133
179
|
latestAction?: ActionState
|
|
134
180
|
actions: Record<string, Action>
|
|
181
|
+
loaders: Record<string, Loader>
|
|
135
182
|
pending?: PendingState
|
|
136
183
|
isFetching: boolean
|
|
137
184
|
isPreloading: boolean
|
|
@@ -184,6 +231,7 @@ export interface Router<
|
|
|
184
231
|
TRouteConfig extends AnyRouteConfig = RouteConfig,
|
|
185
232
|
TAllRouteInfo extends AnyAllRouteInfo = AllRouteInfo<TRouteConfig>,
|
|
186
233
|
> {
|
|
234
|
+
history: BrowserHistory | MemoryHistory | HashHistory
|
|
187
235
|
options: PickAsRequired<
|
|
188
236
|
RouterOptions<TRouteConfig>,
|
|
189
237
|
'stringifySearch' | 'parseSearch'
|
|
@@ -210,15 +258,7 @@ export interface Router<
|
|
|
210
258
|
update: <TRouteConfig extends RouteConfig = RouteConfig>(
|
|
211
259
|
opts?: RouterOptions<TRouteConfig>,
|
|
212
260
|
) => Router<TRouteConfig>
|
|
213
|
-
|
|
214
|
-
routeConfig: RouteConfig,
|
|
215
|
-
) => Route<TAllRouteInfo, AnyRouteInfo>
|
|
216
|
-
parseLocation: (
|
|
217
|
-
location: History['location'],
|
|
218
|
-
previousLocation?: Location,
|
|
219
|
-
) => Location
|
|
220
|
-
buildLocation: (dest: BuildNextOptions) => Location
|
|
221
|
-
commitLocation: (next: Location, replace?: boolean) => Promise<void>
|
|
261
|
+
|
|
222
262
|
buildNext: (opts: BuildNextOptions) => Location
|
|
223
263
|
cancelMatches: () => void
|
|
224
264
|
loadLocation: (next?: Location) => Promise<void>
|
|
@@ -227,9 +267,10 @@ export interface Router<
|
|
|
227
267
|
getRoute: <TId extends keyof TAllRouteInfo['routeInfoById']>(
|
|
228
268
|
id: TId,
|
|
229
269
|
) => Route<TAllRouteInfo, TAllRouteInfo['routeInfoById'][TId]>
|
|
230
|
-
loadRoute: (
|
|
270
|
+
loadRoute: (navigateOpts: BuildNextOptions) => Promise<RouteMatch[]>
|
|
271
|
+
preloadRoute: (
|
|
231
272
|
navigateOpts: BuildNextOptions,
|
|
232
|
-
loaderOpts: { maxAge
|
|
273
|
+
loaderOpts: { maxAge?: number; gcMaxAge?: number },
|
|
233
274
|
) => Promise<RouteMatch[]>
|
|
234
275
|
matchRoutes: (
|
|
235
276
|
pathname: string,
|
|
@@ -238,16 +279,13 @@ export interface Router<
|
|
|
238
279
|
loadMatches: (
|
|
239
280
|
resolvedMatches: RouteMatch[],
|
|
240
281
|
loaderOpts?: { withPending?: boolean } & (
|
|
241
|
-
| { preload: true; maxAge: number }
|
|
242
|
-
| { preload?: false; maxAge?: never }
|
|
282
|
+
| { preload: true; maxAge: number; gcMaxAge: number }
|
|
283
|
+
| { preload?: false; maxAge?: never; gcMaxAge?: never }
|
|
243
284
|
),
|
|
244
285
|
) => Promise<void>
|
|
245
286
|
invalidateRoute: (opts: MatchLocation) => void
|
|
246
287
|
reload: () => Promise<void>
|
|
247
288
|
resolvePath: (from: string, path: string) => string
|
|
248
|
-
_navigate: (
|
|
249
|
-
location: BuildNextOptions & { replace?: boolean },
|
|
250
|
-
) => Promise<void>
|
|
251
289
|
navigate: <
|
|
252
290
|
TFrom extends ValidFromPath<TAllRouteInfo> = '/',
|
|
253
291
|
TTo extends string = '.',
|
|
@@ -267,6 +305,20 @@ export interface Router<
|
|
|
267
305
|
>(
|
|
268
306
|
opts: LinkOptions<TAllRouteInfo, TFrom, TTo>,
|
|
269
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
|
+
}
|
|
270
322
|
}
|
|
271
323
|
|
|
272
324
|
// Detect if we're in the DOM
|
|
@@ -289,13 +341,15 @@ export function createRouter<
|
|
|
289
341
|
const originalOptions = {
|
|
290
342
|
defaultLoaderGcMaxAge: 5 * 60 * 1000,
|
|
291
343
|
defaultLoaderMaxAge: 0,
|
|
292
|
-
|
|
344
|
+
defaultPreloadMaxAge: 2000,
|
|
345
|
+
defaultPreloadDelay: 50,
|
|
293
346
|
...userOptions,
|
|
294
347
|
stringifySearch: userOptions?.stringifySearch ?? defaultStringifySearch,
|
|
295
348
|
parseSearch: userOptions?.parseSearch ?? defaultParseSearch,
|
|
296
349
|
}
|
|
297
350
|
|
|
298
351
|
let router: Router<TRouteConfig, TAllRouteInfo> = {
|
|
352
|
+
history,
|
|
299
353
|
options: originalOptions,
|
|
300
354
|
listeners: [],
|
|
301
355
|
removeActionQueue: [],
|
|
@@ -314,6 +368,7 @@ export function createRouter<
|
|
|
314
368
|
location: null!,
|
|
315
369
|
matches: [],
|
|
316
370
|
actions: {},
|
|
371
|
+
loaders: {},
|
|
317
372
|
loaderData: {} as any,
|
|
318
373
|
lastUpdated: Date.now(),
|
|
319
374
|
isFetching: false,
|
|
@@ -347,7 +402,7 @@ export function createRouter<
|
|
|
347
402
|
},
|
|
348
403
|
|
|
349
404
|
mount: () => {
|
|
350
|
-
const next = router.buildLocation({
|
|
405
|
+
const next = router.__.buildLocation({
|
|
351
406
|
to: '.',
|
|
352
407
|
search: true,
|
|
353
408
|
hash: true,
|
|
@@ -356,14 +411,14 @@ export function createRouter<
|
|
|
356
411
|
// If the current location isn't updated, trigger a navigation
|
|
357
412
|
// to the current location. Otherwise, load the current location.
|
|
358
413
|
if (next.href !== router.location.href) {
|
|
359
|
-
router.commitLocation(next, true)
|
|
414
|
+
router.__.commitLocation(next, true)
|
|
360
415
|
} else {
|
|
361
416
|
router.loadLocation()
|
|
362
417
|
}
|
|
363
418
|
|
|
364
419
|
const unsub = history.listen((event) => {
|
|
365
420
|
router.loadLocation(
|
|
366
|
-
router.parseLocation(event.location, router.location),
|
|
421
|
+
router.__.parseLocation(event.location, router.location),
|
|
367
422
|
)
|
|
368
423
|
})
|
|
369
424
|
|
|
@@ -396,235 +451,12 @@ export function createRouter<
|
|
|
396
451
|
|
|
397
452
|
if (routeConfig) {
|
|
398
453
|
router.routesById = {} as any
|
|
399
|
-
router.routeTree = router.buildRouteTree(routeConfig)
|
|
454
|
+
router.routeTree = router.__.buildRouteTree(routeConfig)
|
|
400
455
|
}
|
|
401
456
|
|
|
402
457
|
return router as any
|
|
403
458
|
},
|
|
404
459
|
|
|
405
|
-
buildRouteTree: (rootRouteConfig: RouteConfig) => {
|
|
406
|
-
const recurseRoutes = (
|
|
407
|
-
routeConfigs: RouteConfig[],
|
|
408
|
-
parent?: Route<TAllRouteInfo, any>,
|
|
409
|
-
): Route<TAllRouteInfo, any>[] => {
|
|
410
|
-
return routeConfigs.map((routeConfig) => {
|
|
411
|
-
const routeOptions = routeConfig.options
|
|
412
|
-
const route = createRoute(routeConfig, routeOptions, parent, router)
|
|
413
|
-
|
|
414
|
-
// {
|
|
415
|
-
// pendingMs: routeOptions.pendingMs ?? router.defaultPendingMs,
|
|
416
|
-
// pendingMinMs: routeOptions.pendingMinMs ?? router.defaultPendingMinMs,
|
|
417
|
-
// }
|
|
418
|
-
|
|
419
|
-
const existingRoute = (router.routesById as any)[route.routeId]
|
|
420
|
-
|
|
421
|
-
if (existingRoute) {
|
|
422
|
-
if (process.env.NODE_ENV !== 'production') {
|
|
423
|
-
console.warn(
|
|
424
|
-
`Duplicate routes found with id: ${String(route.routeId)}`,
|
|
425
|
-
router.routesById,
|
|
426
|
-
route,
|
|
427
|
-
)
|
|
428
|
-
}
|
|
429
|
-
throw new Error()
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
;(router.routesById as any)[route.routeId] = route
|
|
433
|
-
|
|
434
|
-
const children = routeConfig.children as RouteConfig[]
|
|
435
|
-
|
|
436
|
-
route.childRoutes = children?.length
|
|
437
|
-
? recurseRoutes(children, route)
|
|
438
|
-
: undefined
|
|
439
|
-
|
|
440
|
-
return route
|
|
441
|
-
})
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
const routes = recurseRoutes([rootRouteConfig])
|
|
445
|
-
|
|
446
|
-
return routes[0]!
|
|
447
|
-
},
|
|
448
|
-
|
|
449
|
-
parseLocation: (
|
|
450
|
-
location: History['location'],
|
|
451
|
-
previousLocation?: Location,
|
|
452
|
-
): Location => {
|
|
453
|
-
const parsedSearch = router.options.parseSearch(location.search)
|
|
454
|
-
|
|
455
|
-
return {
|
|
456
|
-
pathname: location.pathname,
|
|
457
|
-
searchStr: location.search,
|
|
458
|
-
search: replaceEqualDeep(previousLocation?.search, parsedSearch),
|
|
459
|
-
hash: location.hash.split('#').reverse()[0] ?? '',
|
|
460
|
-
href: `${location.pathname}${location.search}${location.hash}`,
|
|
461
|
-
state: location.state as LocationState,
|
|
462
|
-
key: location.key,
|
|
463
|
-
}
|
|
464
|
-
},
|
|
465
|
-
|
|
466
|
-
buildLocation: (dest: BuildNextOptions = {}): Location => {
|
|
467
|
-
// const resolvedFrom: Location = {
|
|
468
|
-
// ...router.location,
|
|
469
|
-
const fromPathname = dest.fromCurrent
|
|
470
|
-
? router.location.pathname
|
|
471
|
-
: dest.from ?? router.location.pathname
|
|
472
|
-
|
|
473
|
-
let pathname = resolvePath(
|
|
474
|
-
router.basepath ?? '/',
|
|
475
|
-
fromPathname,
|
|
476
|
-
`${dest.to ?? '.'}`,
|
|
477
|
-
)
|
|
478
|
-
|
|
479
|
-
const fromMatches = router.matchRoutes(router.location.pathname, {
|
|
480
|
-
strictParseParams: true,
|
|
481
|
-
})
|
|
482
|
-
|
|
483
|
-
const toMatches = router.matchRoutes(pathname)
|
|
484
|
-
|
|
485
|
-
const prevParams = { ...last(fromMatches)?.params }
|
|
486
|
-
|
|
487
|
-
let nextParams =
|
|
488
|
-
(dest.params ?? true) === true
|
|
489
|
-
? prevParams
|
|
490
|
-
: functionalUpdate(dest.params!, prevParams)
|
|
491
|
-
|
|
492
|
-
if (nextParams) {
|
|
493
|
-
toMatches
|
|
494
|
-
.map((d) => d.options.stringifyParams)
|
|
495
|
-
.filter(Boolean)
|
|
496
|
-
.forEach((fn) => {
|
|
497
|
-
Object.assign({}, nextParams!, fn!(nextParams!))
|
|
498
|
-
})
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
pathname = interpolatePath(pathname, nextParams ?? {})
|
|
502
|
-
|
|
503
|
-
// Pre filters first
|
|
504
|
-
const preFilteredSearch = dest.__preSearchFilters?.length
|
|
505
|
-
? dest.__preSearchFilters.reduce(
|
|
506
|
-
(prev, next) => next(prev),
|
|
507
|
-
router.location.search,
|
|
508
|
-
)
|
|
509
|
-
: router.location.search
|
|
510
|
-
|
|
511
|
-
// Then the link/navigate function
|
|
512
|
-
const destSearch =
|
|
513
|
-
dest.search === true
|
|
514
|
-
? preFilteredSearch // Preserve resolvedFrom true
|
|
515
|
-
: dest.search
|
|
516
|
-
? functionalUpdate(dest.search, preFilteredSearch) ?? {} // Updater
|
|
517
|
-
: dest.__preSearchFilters?.length
|
|
518
|
-
? preFilteredSearch // Preserve resolvedFrom filters
|
|
519
|
-
: {}
|
|
520
|
-
|
|
521
|
-
// Then post filters
|
|
522
|
-
const postFilteredSearch = dest.__postSearchFilters?.length
|
|
523
|
-
? dest.__postSearchFilters.reduce(
|
|
524
|
-
(prev, next) => next(prev),
|
|
525
|
-
destSearch,
|
|
526
|
-
)
|
|
527
|
-
: destSearch
|
|
528
|
-
|
|
529
|
-
const search = replaceEqualDeep(
|
|
530
|
-
router.location.search,
|
|
531
|
-
postFilteredSearch,
|
|
532
|
-
)
|
|
533
|
-
|
|
534
|
-
const searchStr = router.options.stringifySearch(search)
|
|
535
|
-
let hash =
|
|
536
|
-
dest.hash === true
|
|
537
|
-
? router.location.hash
|
|
538
|
-
: functionalUpdate(dest.hash!, router.location.hash)
|
|
539
|
-
hash = hash ? `#${hash}` : ''
|
|
540
|
-
|
|
541
|
-
return {
|
|
542
|
-
pathname,
|
|
543
|
-
search,
|
|
544
|
-
searchStr,
|
|
545
|
-
state: router.location.state,
|
|
546
|
-
hash,
|
|
547
|
-
href: `${pathname}${searchStr}${hash}`,
|
|
548
|
-
key: dest.key,
|
|
549
|
-
}
|
|
550
|
-
},
|
|
551
|
-
|
|
552
|
-
commitLocation: (next: Location, replace?: boolean): Promise<void> => {
|
|
553
|
-
const id = '' + Date.now() + Math.random()
|
|
554
|
-
|
|
555
|
-
if (router.navigateTimeout) clearTimeout(router.navigateTimeout)
|
|
556
|
-
|
|
557
|
-
let nextAction: 'push' | 'replace' = 'replace'
|
|
558
|
-
|
|
559
|
-
if (!replace) {
|
|
560
|
-
nextAction = 'push'
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
const isSameUrl =
|
|
564
|
-
router.parseLocation(history.location).href === next.href
|
|
565
|
-
|
|
566
|
-
if (isSameUrl && !next.key) {
|
|
567
|
-
nextAction = 'replace'
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
if (nextAction === 'replace') {
|
|
571
|
-
history.replace(
|
|
572
|
-
{
|
|
573
|
-
pathname: next.pathname,
|
|
574
|
-
hash: next.hash,
|
|
575
|
-
search: next.searchStr,
|
|
576
|
-
},
|
|
577
|
-
{
|
|
578
|
-
id,
|
|
579
|
-
},
|
|
580
|
-
)
|
|
581
|
-
} else {
|
|
582
|
-
history.push(
|
|
583
|
-
{
|
|
584
|
-
pathname: next.pathname,
|
|
585
|
-
hash: next.hash,
|
|
586
|
-
search: next.searchStr,
|
|
587
|
-
},
|
|
588
|
-
{
|
|
589
|
-
id,
|
|
590
|
-
},
|
|
591
|
-
)
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
router.navigationPromise = new Promise((resolve) => {
|
|
595
|
-
const previousNavigationResolve = router.resolveNavigation
|
|
596
|
-
|
|
597
|
-
router.resolveNavigation = () => {
|
|
598
|
-
previousNavigationResolve()
|
|
599
|
-
resolve()
|
|
600
|
-
}
|
|
601
|
-
})
|
|
602
|
-
|
|
603
|
-
return router.navigationPromise
|
|
604
|
-
},
|
|
605
|
-
|
|
606
|
-
buildNext: (opts: BuildNextOptions) => {
|
|
607
|
-
const next = router.buildLocation(opts)
|
|
608
|
-
|
|
609
|
-
const matches = router.matchRoutes(next.pathname)
|
|
610
|
-
|
|
611
|
-
const __preSearchFilters = matches
|
|
612
|
-
.map((match) => match.options.preSearchFilters ?? [])
|
|
613
|
-
.flat()
|
|
614
|
-
.filter(Boolean)
|
|
615
|
-
|
|
616
|
-
const __postSearchFilters = matches
|
|
617
|
-
.map((match) => match.options.postSearchFilters ?? [])
|
|
618
|
-
.flat()
|
|
619
|
-
.filter(Boolean)
|
|
620
|
-
|
|
621
|
-
return router.buildLocation({
|
|
622
|
-
...opts,
|
|
623
|
-
__preSearchFilters,
|
|
624
|
-
__postSearchFilters,
|
|
625
|
-
})
|
|
626
|
-
},
|
|
627
|
-
|
|
628
460
|
cancelMatches: () => {
|
|
629
461
|
;[
|
|
630
462
|
...router.state.matches,
|
|
@@ -736,6 +568,7 @@ export function createRouter<
|
|
|
736
568
|
params: d.params,
|
|
737
569
|
search: d.search,
|
|
738
570
|
})
|
|
571
|
+
delete router.matchCache[d.matchId]
|
|
739
572
|
})
|
|
740
573
|
|
|
741
574
|
if (matches.some((d) => d.status === 'loading')) {
|
|
@@ -782,17 +615,32 @@ export function createRouter<
|
|
|
782
615
|
})
|
|
783
616
|
},
|
|
784
617
|
|
|
785
|
-
loadRoute: async (
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
618
|
+
loadRoute: async (navigateOpts = router.location) => {
|
|
619
|
+
const next = router.buildNext(navigateOpts)
|
|
620
|
+
const matches = router.matchRoutes(next.pathname, {
|
|
621
|
+
strictParseParams: true,
|
|
622
|
+
})
|
|
623
|
+
await router.loadMatches(matches)
|
|
624
|
+
return matches
|
|
625
|
+
},
|
|
626
|
+
|
|
627
|
+
preloadRoute: async (navigateOpts = router.location, loaderOpts) => {
|
|
789
628
|
const next = router.buildNext(navigateOpts)
|
|
790
629
|
const matches = router.matchRoutes(next.pathname, {
|
|
791
630
|
strictParseParams: true,
|
|
792
631
|
})
|
|
793
632
|
await router.loadMatches(matches, {
|
|
794
633
|
preload: true,
|
|
795
|
-
maxAge:
|
|
634
|
+
maxAge:
|
|
635
|
+
loaderOpts.maxAge ??
|
|
636
|
+
router.options.defaultPreloadMaxAge ??
|
|
637
|
+
router.options.defaultLoaderMaxAge ??
|
|
638
|
+
0,
|
|
639
|
+
gcMaxAge:
|
|
640
|
+
loaderOpts.gcMaxAge ??
|
|
641
|
+
router.options.defaultPreloadGcMaxAge ??
|
|
642
|
+
router.options.defaultLoaderGcMaxAge ??
|
|
643
|
+
0,
|
|
796
644
|
})
|
|
797
645
|
return matches
|
|
798
646
|
},
|
|
@@ -904,38 +752,10 @@ export function createRouter<
|
|
|
904
752
|
},
|
|
905
753
|
|
|
906
754
|
loadMatches: async (resolvedMatches, loaderOpts) => {
|
|
907
|
-
const now = Date.now()
|
|
908
|
-
|
|
909
755
|
const matchPromises = resolvedMatches.map(async (match) => {
|
|
910
756
|
// Validate the match (loads search params etc)
|
|
911
757
|
match.__.validate()
|
|
912
|
-
|
|
913
|
-
// // If the match doesn't have a loader, don't attempt to load it
|
|
914
|
-
// if (!match.hasLoaders()) {
|
|
915
|
-
// return
|
|
916
|
-
// }
|
|
917
|
-
|
|
918
|
-
// If this is a preload, add it to the preload cache
|
|
919
|
-
if (loaderOpts?.preload && loaderOpts?.maxAge > 0) {
|
|
920
|
-
// If the match is currently active, don't preload it
|
|
921
|
-
if (router.state.matches.find((d) => d.matchId === match.matchId)) {
|
|
922
|
-
return
|
|
923
|
-
}
|
|
924
|
-
|
|
925
|
-
router.matchCache[match.matchId] = {
|
|
926
|
-
gc: now + loaderOpts.maxAge, // TODO: Should this use the route's maxAge?
|
|
927
|
-
match,
|
|
928
|
-
}
|
|
929
|
-
}
|
|
930
|
-
|
|
931
|
-
// If the match is invalid, errored or idle, trigger it to load
|
|
932
|
-
if (
|
|
933
|
-
(match.status === 'success' && match.getIsInvalid()) ||
|
|
934
|
-
match.status === 'error' ||
|
|
935
|
-
match.status === 'idle'
|
|
936
|
-
) {
|
|
937
|
-
match.load()
|
|
938
|
-
}
|
|
758
|
+
match.load(loaderOpts)
|
|
939
759
|
|
|
940
760
|
if (match.status === 'loading') {
|
|
941
761
|
// If requested, start the pending timers
|
|
@@ -968,7 +788,7 @@ export function createRouter<
|
|
|
968
788
|
},
|
|
969
789
|
|
|
970
790
|
reload: () =>
|
|
971
|
-
router.
|
|
791
|
+
router.__.navigate({
|
|
972
792
|
fromCurrent: true,
|
|
973
793
|
replace: true,
|
|
974
794
|
search: true,
|
|
@@ -1006,11 +826,6 @@ export function createRouter<
|
|
|
1006
826
|
})
|
|
1007
827
|
},
|
|
1008
828
|
|
|
1009
|
-
_navigate: (location: BuildNextOptions & { replace?: boolean }) => {
|
|
1010
|
-
const next = router.buildNext(location)
|
|
1011
|
-
return router.commitLocation(next, location.replace)
|
|
1012
|
-
},
|
|
1013
|
-
|
|
1014
829
|
navigate: async ({ from, to = '.', search, hash, replace, params }) => {
|
|
1015
830
|
// If this link simply reloads the current route,
|
|
1016
831
|
// make sure it has a new key so it will trigger a data refresh
|
|
@@ -1032,7 +847,7 @@ export function createRouter<
|
|
|
1032
847
|
'Attempting to navigate to external url with router.navigate!',
|
|
1033
848
|
)
|
|
1034
849
|
|
|
1035
|
-
return router.
|
|
850
|
+
return router.__.navigate({
|
|
1036
851
|
from: fromString,
|
|
1037
852
|
to: toString,
|
|
1038
853
|
search,
|
|
@@ -1053,6 +868,7 @@ export function createRouter<
|
|
|
1053
868
|
activeOptions,
|
|
1054
869
|
preload,
|
|
1055
870
|
preloadMaxAge: userPreloadMaxAge,
|
|
871
|
+
preloadGcMaxAge: userPreloadGcMaxAge,
|
|
1056
872
|
preloadDelay: userPreloadDelay,
|
|
1057
873
|
disabled,
|
|
1058
874
|
}) => {
|
|
@@ -1081,14 +897,9 @@ export function createRouter<
|
|
|
1081
897
|
|
|
1082
898
|
const next = router.buildNext(nextOpts)
|
|
1083
899
|
|
|
1084
|
-
preload = preload ?? router.options.
|
|
1085
|
-
const preloadMaxAge =
|
|
1086
|
-
userPreloadMaxAge ??
|
|
1087
|
-
router.options.defaultLinkPreloadMaxAge ??
|
|
1088
|
-
router.options.defaultLoaderGcMaxAge ??
|
|
1089
|
-
0
|
|
900
|
+
preload = preload ?? router.options.defaultPreload
|
|
1090
901
|
const preloadDelay =
|
|
1091
|
-
userPreloadDelay ?? router.options.
|
|
902
|
+
userPreloadDelay ?? router.options.defaultPreloadDelay ?? 0
|
|
1092
903
|
|
|
1093
904
|
// Compare path/hash for matches
|
|
1094
905
|
const pathIsEqual = router.state.location.pathname === next.pathname
|
|
@@ -1120,28 +931,34 @@ export function createRouter<
|
|
|
1120
931
|
}
|
|
1121
932
|
|
|
1122
933
|
// All is well? Navigate!)
|
|
1123
|
-
router.
|
|
934
|
+
router.__.navigate(nextOpts)
|
|
1124
935
|
}
|
|
1125
936
|
}
|
|
1126
937
|
|
|
1127
938
|
// The click handler
|
|
1128
939
|
const handleFocus = (e: MouseEvent) => {
|
|
1129
|
-
if (preload
|
|
1130
|
-
router.
|
|
940
|
+
if (preload) {
|
|
941
|
+
router.preloadRoute(nextOpts, {
|
|
942
|
+
maxAge: userPreloadMaxAge,
|
|
943
|
+
gcMaxAge: userPreloadGcMaxAge,
|
|
944
|
+
})
|
|
1131
945
|
}
|
|
1132
946
|
}
|
|
1133
947
|
|
|
1134
948
|
const handleEnter = (e: MouseEvent) => {
|
|
1135
949
|
const target = (e.target || {}) as LinkCurrentTargetElement
|
|
1136
950
|
|
|
1137
|
-
if (preload
|
|
951
|
+
if (preload) {
|
|
1138
952
|
if (target.preloadTimeout) {
|
|
1139
953
|
return
|
|
1140
954
|
}
|
|
1141
955
|
|
|
1142
956
|
target.preloadTimeout = setTimeout(() => {
|
|
1143
957
|
target.preloadTimeout = null
|
|
1144
|
-
router.
|
|
958
|
+
router.preloadRoute(nextOpts, {
|
|
959
|
+
maxAge: userPreloadMaxAge,
|
|
960
|
+
gcMaxAge: userPreloadGcMaxAge,
|
|
961
|
+
})
|
|
1145
962
|
}, preloadDelay)
|
|
1146
963
|
}
|
|
1147
964
|
}
|
|
@@ -1166,9 +983,238 @@ export function createRouter<
|
|
|
1166
983
|
disabled,
|
|
1167
984
|
}
|
|
1168
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
|
+
},
|
|
1169
1215
|
}
|
|
1170
1216
|
|
|
1171
|
-
router.location = router.parseLocation(history.location)
|
|
1217
|
+
router.location = router.__.parseLocation(history.location)
|
|
1172
1218
|
router.state.location = router.location
|
|
1173
1219
|
|
|
1174
1220
|
router.update(userOptions)
|