@tanstack/router-core 1.132.0-alpha.4 → 1.132.0
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/dist/cjs/Matches.cjs +2 -1
- package/dist/cjs/Matches.cjs.map +1 -1
- package/dist/cjs/Matches.d.cts +2 -2
- package/dist/cjs/config.cjs +10 -0
- package/dist/cjs/config.cjs.map +1 -0
- package/dist/cjs/config.d.cts +17 -0
- package/dist/cjs/fileRoute.d.cts +3 -2
- package/dist/cjs/index.cjs +9 -2
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +10 -5
- package/dist/cjs/load-matches.cjs +5 -3
- package/dist/cjs/load-matches.cjs.map +1 -1
- package/dist/cjs/location.d.cts +38 -0
- package/dist/cjs/path.cjs +27 -64
- package/dist/cjs/path.cjs.map +1 -1
- package/dist/cjs/path.d.cts +6 -7
- package/dist/cjs/process-route-tree.cjs +144 -0
- package/dist/cjs/process-route-tree.cjs.map +1 -0
- package/dist/cjs/process-route-tree.d.cts +10 -0
- package/dist/cjs/redirect.cjs +1 -1
- package/dist/cjs/redirect.cjs.map +1 -1
- package/dist/cjs/rewrite.cjs +63 -0
- package/dist/cjs/rewrite.cjs.map +1 -0
- package/dist/cjs/rewrite.d.cts +22 -0
- package/dist/cjs/route.cjs.map +1 -1
- package/dist/cjs/route.d.cts +62 -44
- package/dist/cjs/router.cjs +102 -210
- package/dist/cjs/router.cjs.map +1 -1
- package/dist/cjs/router.d.cts +81 -44
- package/dist/cjs/scroll-restoration.cjs.map +1 -1
- package/dist/cjs/scroll-restoration.d.cts +9 -0
- package/dist/cjs/ssr/createRequestHandler.cjs +4 -1
- package/dist/cjs/ssr/createRequestHandler.cjs.map +1 -1
- package/dist/cjs/ssr/serializer/transformer.cjs +14 -12
- package/dist/cjs/ssr/serializer/transformer.cjs.map +1 -1
- package/dist/cjs/ssr/serializer/transformer.d.cts +55 -15
- package/dist/cjs/ssr/ssr-client.cjs.map +1 -1
- package/dist/cjs/ssr/ssr-server.cjs +5 -2
- package/dist/cjs/ssr/ssr-server.cjs.map +1 -1
- package/dist/cjs/ssr/ssr-server.d.cts +4 -1
- package/dist/cjs/utils.cjs +68 -46
- package/dist/cjs/utils.cjs.map +1 -1
- package/dist/esm/Matches.d.ts +2 -2
- package/dist/esm/Matches.js +2 -1
- package/dist/esm/Matches.js.map +1 -1
- package/dist/esm/config.d.ts +17 -0
- package/dist/esm/config.js +10 -0
- package/dist/esm/config.js.map +1 -0
- package/dist/esm/fileRoute.d.ts +3 -2
- package/dist/esm/index.d.ts +10 -5
- package/dist/esm/index.js +10 -3
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/load-matches.js +5 -3
- package/dist/esm/load-matches.js.map +1 -1
- package/dist/esm/location.d.ts +38 -0
- package/dist/esm/path.d.ts +6 -7
- package/dist/esm/path.js +27 -64
- package/dist/esm/path.js.map +1 -1
- package/dist/esm/process-route-tree.d.ts +10 -0
- package/dist/esm/process-route-tree.js +144 -0
- package/dist/esm/process-route-tree.js.map +1 -0
- package/dist/esm/redirect.js +1 -1
- package/dist/esm/redirect.js.map +1 -1
- package/dist/esm/rewrite.d.ts +22 -0
- package/dist/esm/rewrite.js +63 -0
- package/dist/esm/rewrite.js.map +1 -0
- package/dist/esm/route.d.ts +62 -44
- package/dist/esm/route.js.map +1 -1
- package/dist/esm/router.d.ts +81 -44
- package/dist/esm/router.js +104 -212
- package/dist/esm/router.js.map +1 -1
- package/dist/esm/scroll-restoration.d.ts +9 -0
- package/dist/esm/scroll-restoration.js.map +1 -1
- package/dist/esm/ssr/createRequestHandler.js +4 -1
- package/dist/esm/ssr/createRequestHandler.js.map +1 -1
- package/dist/esm/ssr/serializer/transformer.d.ts +55 -15
- package/dist/esm/ssr/serializer/transformer.js +14 -12
- package/dist/esm/ssr/serializer/transformer.js.map +1 -1
- package/dist/esm/ssr/ssr-client.js.map +1 -1
- package/dist/esm/ssr/ssr-server.d.ts +4 -1
- package/dist/esm/ssr/ssr-server.js +5 -2
- package/dist/esm/ssr/ssr-server.js.map +1 -1
- package/dist/esm/utils.js +68 -46
- package/dist/esm/utils.js.map +1 -1
- package/package.json +2 -2
- package/src/Matches.ts +4 -3
- package/src/config.ts +42 -0
- package/src/fileRoute.ts +25 -3
- package/src/index.ts +23 -6
- package/src/load-matches.ts +31 -21
- package/src/location.ts +38 -0
- package/src/path.ts +44 -82
- package/src/process-route-tree.ts +233 -0
- package/src/redirect.ts +1 -1
- package/src/rewrite.ts +70 -0
- package/src/route.ts +311 -74
- package/src/router.ts +263 -389
- package/src/scroll-restoration.ts +1 -1
- package/src/ssr/createRequestHandler.ts +4 -1
- package/src/ssr/serializer/transformer.ts +168 -31
- package/src/ssr/server.ts +6 -0
- package/src/ssr/ssr-client.ts +2 -2
- package/src/ssr/ssr-server.ts +10 -7
- package/src/utils.ts +83 -61
package/src/router.ts
CHANGED
|
@@ -1,10 +1,5 @@
|
|
|
1
1
|
import { Store, batch } from '@tanstack/store'
|
|
2
|
-
import {
|
|
3
|
-
createBrowserHistory,
|
|
4
|
-
createMemoryHistory,
|
|
5
|
-
parseHref,
|
|
6
|
-
} from '@tanstack/history'
|
|
7
|
-
import invariant from 'tiny-invariant'
|
|
2
|
+
import { createBrowserHistory, parseHref } from '@tanstack/history'
|
|
8
3
|
import {
|
|
9
4
|
createControlledPromise,
|
|
10
5
|
deepEqual,
|
|
@@ -13,19 +8,13 @@ import {
|
|
|
13
8
|
last,
|
|
14
9
|
replaceEqualDeep,
|
|
15
10
|
} from './utils'
|
|
11
|
+
import { processRouteTree } from './process-route-tree'
|
|
16
12
|
import {
|
|
17
|
-
SEGMENT_TYPE_OPTIONAL_PARAM,
|
|
18
|
-
SEGMENT_TYPE_PARAM,
|
|
19
|
-
SEGMENT_TYPE_PATHNAME,
|
|
20
|
-
SEGMENT_TYPE_WILDCARD,
|
|
21
13
|
cleanPath,
|
|
22
14
|
interpolatePath,
|
|
23
|
-
joinPaths,
|
|
24
15
|
matchPathname,
|
|
25
|
-
parsePathname,
|
|
26
16
|
resolvePath,
|
|
27
17
|
trimPath,
|
|
28
|
-
trimPathLeft,
|
|
29
18
|
trimPathRight,
|
|
30
19
|
} from './path'
|
|
31
20
|
import { isNotFound } from './not-found'
|
|
@@ -35,7 +24,13 @@ import { rootRouteId } from './root'
|
|
|
35
24
|
import { isRedirect, redirect } from './redirect'
|
|
36
25
|
import { createLRUCache } from './lru-cache'
|
|
37
26
|
import { loadMatches, loadRouteChunk, routeNeedsPreload } from './load-matches'
|
|
38
|
-
import
|
|
27
|
+
import {
|
|
28
|
+
composeRewrites,
|
|
29
|
+
executeRewriteInput,
|
|
30
|
+
executeRewriteOutput,
|
|
31
|
+
rewriteBasepath,
|
|
32
|
+
} from './rewrite'
|
|
33
|
+
import type { ParsePathnameCache } from './path'
|
|
39
34
|
import type { SearchParser, SearchSerializer } from './searchParams'
|
|
40
35
|
import type { AnyRedirect, ResolvedRedirect } from './redirect'
|
|
41
36
|
import type {
|
|
@@ -46,6 +41,7 @@ import type {
|
|
|
46
41
|
} from '@tanstack/history'
|
|
47
42
|
import type {
|
|
48
43
|
Awaitable,
|
|
44
|
+
Constrain,
|
|
49
45
|
ControlledPromise,
|
|
50
46
|
NoInfer,
|
|
51
47
|
NonNullableUpdater,
|
|
@@ -59,6 +55,7 @@ import type {
|
|
|
59
55
|
AnyRouteWithContext,
|
|
60
56
|
MakeRemountDepsOptionsUnion,
|
|
61
57
|
RouteContextOptions,
|
|
58
|
+
RouteLike,
|
|
62
59
|
RouteMask,
|
|
63
60
|
SearchMiddleware,
|
|
64
61
|
} from './route'
|
|
@@ -84,6 +81,11 @@ import type { Manifest } from './manifest'
|
|
|
84
81
|
import type { AnySchema, AnyValidator } from './validators'
|
|
85
82
|
import type { NavigateOptions, ResolveRelativePath, ToOptions } from './link'
|
|
86
83
|
import type { NotFoundError } from './not-found'
|
|
84
|
+
import type {
|
|
85
|
+
AnySerializationAdapter,
|
|
86
|
+
ValidateSerializableInput,
|
|
87
|
+
} from './ssr/serializer/transformer'
|
|
88
|
+
// import type { AnyRouterConfig } from './config'
|
|
87
89
|
|
|
88
90
|
export type ControllablePromise<T = any> = Promise<T> & {
|
|
89
91
|
resolve: (value: T) => void
|
|
@@ -92,15 +94,30 @@ export type ControllablePromise<T = any> = Promise<T> & {
|
|
|
92
94
|
|
|
93
95
|
export type InjectedHtmlEntry = Promise<string>
|
|
94
96
|
|
|
95
|
-
export interface
|
|
96
|
-
|
|
97
|
+
export interface Register {
|
|
98
|
+
// Lots of things on here like...
|
|
99
|
+
// router
|
|
100
|
+
// config
|
|
101
|
+
// ssr
|
|
97
102
|
}
|
|
98
103
|
|
|
99
|
-
export
|
|
100
|
-
|
|
104
|
+
export type RegisteredRouter<TRegister = Register> = TRegister extends {
|
|
105
|
+
router: infer TRouter
|
|
101
106
|
}
|
|
107
|
+
? TRouter
|
|
108
|
+
: AnyRouter
|
|
102
109
|
|
|
103
|
-
export type
|
|
110
|
+
export type RegisteredConfigType<TRegister, TKey> = TRegister extends {
|
|
111
|
+
config: infer TConfig
|
|
112
|
+
}
|
|
113
|
+
? TConfig extends {
|
|
114
|
+
'~types': infer TTypes
|
|
115
|
+
}
|
|
116
|
+
? TKey extends keyof TTypes
|
|
117
|
+
? TTypes[TKey]
|
|
118
|
+
: unknown
|
|
119
|
+
: unknown
|
|
120
|
+
: unknown
|
|
104
121
|
|
|
105
122
|
export type DefaultRemountDepsFn<TRouteTree extends AnyRoute> = (
|
|
106
123
|
opts: MakeRemountDepsOptionsUnion<TRouteTree>,
|
|
@@ -111,13 +128,14 @@ export interface DefaultRouterOptionsExtensions {}
|
|
|
111
128
|
export interface RouterOptionsExtensions
|
|
112
129
|
extends DefaultRouterOptionsExtensions {}
|
|
113
130
|
|
|
131
|
+
export type SSROption = boolean | 'data-only'
|
|
132
|
+
|
|
114
133
|
export interface RouterOptions<
|
|
115
134
|
TRouteTree extends AnyRoute,
|
|
116
135
|
TTrailingSlashOption extends TrailingSlashOption,
|
|
117
136
|
TDefaultStructuralSharingOption extends boolean = false,
|
|
118
137
|
TRouterHistory extends RouterHistory = RouterHistory,
|
|
119
|
-
TDehydrated
|
|
120
|
-
TTransformerConfig = any,
|
|
138
|
+
TDehydrated = undefined,
|
|
121
139
|
> extends RouterOptionsExtensions {
|
|
122
140
|
/**
|
|
123
141
|
* The history object that will be used to manage the browser history.
|
|
@@ -262,6 +280,18 @@ export interface RouterOptions<
|
|
|
262
280
|
/**
|
|
263
281
|
* The basepath for then entire router. This is useful for mounting a router instance at a subpath.
|
|
264
282
|
*
|
|
283
|
+
* @deprecated - use `rewrite.input` with the new `rewriteBasepath` utility instead:
|
|
284
|
+
* ```ts
|
|
285
|
+
* const router = createRouter({
|
|
286
|
+
* routeTree,
|
|
287
|
+
* rewrite: rewriteBasepath('/basepath')
|
|
288
|
+
* // Or wrap existing rewrite functionality
|
|
289
|
+
* rewrite: rewriteBasepath('/basepath', {
|
|
290
|
+
* output: ({ url }) => {...},
|
|
291
|
+
* input: ({ url }) => {...},
|
|
292
|
+
* })
|
|
293
|
+
* })
|
|
294
|
+
* ```
|
|
265
295
|
* @default '/'
|
|
266
296
|
* @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RouterOptionsType#basepath-property)
|
|
267
297
|
*/
|
|
@@ -277,6 +307,9 @@ export interface RouterOptions<
|
|
|
277
307
|
* @link [Guide](https://tanstack.com/router/latest/docs/framework/react/guide/router-context)
|
|
278
308
|
*/
|
|
279
309
|
context?: InferRouterContext<TRouteTree>
|
|
310
|
+
|
|
311
|
+
additionalContext?: any
|
|
312
|
+
|
|
280
313
|
/**
|
|
281
314
|
* A function that will be called when the router is dehydrated.
|
|
282
315
|
*
|
|
@@ -285,7 +318,10 @@ export interface RouterOptions<
|
|
|
285
318
|
* @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RouterOptionsType#dehydrate-method)
|
|
286
319
|
* @link [Guide](https://tanstack.com/router/latest/docs/framework/react/guide/external-data-loading#critical-dehydrationhydration)
|
|
287
320
|
*/
|
|
288
|
-
dehydrate?: () =>
|
|
321
|
+
dehydrate?: () => Constrain<
|
|
322
|
+
TDehydrated,
|
|
323
|
+
ValidateSerializableInput<Register, TDehydrated>
|
|
324
|
+
>
|
|
289
325
|
/**
|
|
290
326
|
* A function that will be called when the router is hydrated.
|
|
291
327
|
*
|
|
@@ -355,7 +391,7 @@ export interface RouterOptions<
|
|
|
355
391
|
*
|
|
356
392
|
* @default true
|
|
357
393
|
*/
|
|
358
|
-
defaultSsr?:
|
|
394
|
+
defaultSsr?: SSROption
|
|
359
395
|
|
|
360
396
|
search?: {
|
|
361
397
|
/**
|
|
@@ -425,9 +461,50 @@ export interface RouterOptions<
|
|
|
425
461
|
*/
|
|
426
462
|
disableGlobalCatchBoundary?: boolean
|
|
427
463
|
|
|
428
|
-
serializationAdapters?:
|
|
464
|
+
serializationAdapters?: ReadonlyArray<AnySerializationAdapter>
|
|
465
|
+
/**
|
|
466
|
+
* Configures how the router will rewrite the location between the actual href and the internal href of the router.
|
|
467
|
+
*
|
|
468
|
+
* @default undefined
|
|
469
|
+
* @description You can provide a custom rewrite pair (in/out) or use the utilities like `rewriteBasepath` as a convenience for common use cases, or even do both!
|
|
470
|
+
* This is useful for basepath rewriting, shifting data from the origin to the path (for things like )
|
|
471
|
+
*/
|
|
472
|
+
rewrite?: LocationRewrite
|
|
473
|
+
origin?: string
|
|
474
|
+
ssr?: {
|
|
475
|
+
nonce?: string
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
export type LocationRewrite = {
|
|
480
|
+
/**
|
|
481
|
+
* A function that will be called to rewrite the URL before it is interpreted by the router from the history instance.
|
|
482
|
+
* Utilities like `rewriteBasepath` are provided as a convenience for common use cases.
|
|
483
|
+
*
|
|
484
|
+
* @default undefined
|
|
485
|
+
*/
|
|
486
|
+
input?: LocationRewriteFunction
|
|
487
|
+
/**
|
|
488
|
+
* A function that will be called to rewrite the URL before it is committed to the actual history instance from the router.
|
|
489
|
+
* Utilities like `rewriteBasepath` are provided as a convenience for common use cases.
|
|
490
|
+
*
|
|
491
|
+
* @default undefined
|
|
492
|
+
*/
|
|
493
|
+
output?: LocationRewriteFunction
|
|
429
494
|
}
|
|
430
495
|
|
|
496
|
+
/**
|
|
497
|
+
* A function that will be called to rewrite the URL.
|
|
498
|
+
*
|
|
499
|
+
* @param url The URL to rewrite.
|
|
500
|
+
* @returns The rewritten URL (as a URL instance or full href string) or undefined if no rewrite is needed.
|
|
501
|
+
*/
|
|
502
|
+
export type LocationRewriteFunction = ({
|
|
503
|
+
url,
|
|
504
|
+
}: {
|
|
505
|
+
url: URL
|
|
506
|
+
}) => undefined | string | URL
|
|
507
|
+
|
|
431
508
|
export interface RouterState<
|
|
432
509
|
in out TRouteTree extends AnyRoute = AnyRoute,
|
|
433
510
|
in out TRouteMatch = MakeRouteMatchUnion,
|
|
@@ -534,17 +611,15 @@ export type RouterConstructorOptions<
|
|
|
534
611
|
TDefaultStructuralSharingOption extends boolean,
|
|
535
612
|
TRouterHistory extends RouterHistory,
|
|
536
613
|
TDehydrated extends Record<string, any>,
|
|
537
|
-
TTransformerConfig,
|
|
538
614
|
> = Omit<
|
|
539
615
|
RouterOptions<
|
|
540
616
|
TRouteTree,
|
|
541
617
|
TTrailingSlashOption,
|
|
542
618
|
TDefaultStructuralSharingOption,
|
|
543
619
|
TRouterHistory,
|
|
544
|
-
TDehydrated
|
|
545
|
-
TTransformerConfig
|
|
620
|
+
TDehydrated
|
|
546
621
|
>,
|
|
547
|
-
'context'
|
|
622
|
+
'context' | 'serializationAdapters' | 'defaultSsr'
|
|
548
623
|
> &
|
|
549
624
|
RouterContextOptions<TRouteTree>
|
|
550
625
|
|
|
@@ -602,15 +677,13 @@ export type UpdateFn<
|
|
|
602
677
|
TDefaultStructuralSharingOption extends boolean,
|
|
603
678
|
TRouterHistory extends RouterHistory,
|
|
604
679
|
TDehydrated extends Record<string, any>,
|
|
605
|
-
TTransformerConfig extends any,
|
|
606
680
|
> = (
|
|
607
681
|
newOptions: RouterConstructorOptions<
|
|
608
682
|
TRouteTree,
|
|
609
683
|
TTrailingSlashOption,
|
|
610
684
|
TDefaultStructuralSharingOption,
|
|
611
685
|
TRouterHistory,
|
|
612
|
-
TDehydrated
|
|
613
|
-
TTransformerConfig
|
|
686
|
+
TDehydrated
|
|
614
687
|
>,
|
|
615
688
|
) => void
|
|
616
689
|
|
|
@@ -621,8 +694,8 @@ export type InvalidateFn<TRouter extends AnyRouter> = (opts?: {
|
|
|
621
694
|
}) => Promise<void>
|
|
622
695
|
|
|
623
696
|
export type ParseLocationFn<TRouteTree extends AnyRoute> = (
|
|
697
|
+
locationToParse: HistoryLocation,
|
|
624
698
|
previousLocation?: ParsedLocation<FullSearchSchema<TRouteTree>>,
|
|
625
|
-
locationToParse?: HistoryLocation,
|
|
626
699
|
) => ParsedLocation<FullSearchSchema<TRouteTree>>
|
|
627
700
|
|
|
628
701
|
export type GetMatchRoutesFn = (
|
|
@@ -695,11 +768,10 @@ export type AnyRouterWithContext<TContext> = RouterCore<
|
|
|
695
768
|
any,
|
|
696
769
|
any,
|
|
697
770
|
any,
|
|
698
|
-
any,
|
|
699
771
|
any
|
|
700
772
|
>
|
|
701
773
|
|
|
702
|
-
export type AnyRouter = RouterCore<any, any, any, any, any
|
|
774
|
+
export type AnyRouter = RouterCore<any, any, any, any, any>
|
|
703
775
|
|
|
704
776
|
export interface ViewTransitionOptions {
|
|
705
777
|
types:
|
|
@@ -753,7 +825,6 @@ export type CreateRouterFn = <
|
|
|
753
825
|
TDefaultStructuralSharingOption extends boolean = false,
|
|
754
826
|
TRouterHistory extends RouterHistory = RouterHistory,
|
|
755
827
|
TDehydrated extends Record<string, any> = Record<string, any>,
|
|
756
|
-
TTransformerConfig = any,
|
|
757
828
|
>(
|
|
758
829
|
options: undefined extends number
|
|
759
830
|
? 'strictNullChecks must be enabled in tsconfig.json'
|
|
@@ -762,16 +833,14 @@ export type CreateRouterFn = <
|
|
|
762
833
|
TTrailingSlashOption,
|
|
763
834
|
TDefaultStructuralSharingOption,
|
|
764
835
|
TRouterHistory,
|
|
765
|
-
TDehydrated
|
|
766
|
-
TTransformerConfig
|
|
836
|
+
TDehydrated
|
|
767
837
|
>,
|
|
768
838
|
) => RouterCore<
|
|
769
839
|
TRouteTree,
|
|
770
840
|
TTrailingSlashOption,
|
|
771
841
|
TDefaultStructuralSharingOption,
|
|
772
842
|
TRouterHistory,
|
|
773
|
-
TDehydrated
|
|
774
|
-
TTransformerConfig
|
|
843
|
+
TDehydrated
|
|
775
844
|
>
|
|
776
845
|
|
|
777
846
|
export class RouterCore<
|
|
@@ -780,7 +849,6 @@ export class RouterCore<
|
|
|
780
849
|
in out TDefaultStructuralSharingOption extends boolean,
|
|
781
850
|
in out TRouterHistory extends RouterHistory = RouterHistory,
|
|
782
851
|
in out TDehydrated extends Record<string, any> = Record<string, any>,
|
|
783
|
-
in out TTransformerConfig = any,
|
|
784
852
|
> {
|
|
785
853
|
// Option-independent properties
|
|
786
854
|
tempLocationKey: string | undefined = `${Math.round(
|
|
@@ -802,13 +870,15 @@ export class RouterCore<
|
|
|
802
870
|
TTrailingSlashOption,
|
|
803
871
|
TDefaultStructuralSharingOption,
|
|
804
872
|
TRouterHistory,
|
|
805
|
-
TDehydrated
|
|
806
|
-
TTransformerConfig
|
|
873
|
+
TDehydrated
|
|
807
874
|
>,
|
|
808
875
|
'stringifySearch' | 'parseSearch' | 'context'
|
|
809
876
|
>
|
|
810
877
|
history!: TRouterHistory
|
|
878
|
+
rewrite?: LocationRewrite
|
|
879
|
+
origin?: string
|
|
811
880
|
latestLocation!: ParsedLocation<FullSearchSchema<TRouteTree>>
|
|
881
|
+
// @deprecated - basepath functionality is now implemented via the `rewrite` option
|
|
812
882
|
basepath!: string
|
|
813
883
|
routeTree!: TRouteTree
|
|
814
884
|
routesById!: RoutesById<TRouteTree>
|
|
@@ -826,8 +896,7 @@ export class RouterCore<
|
|
|
826
896
|
TTrailingSlashOption,
|
|
827
897
|
TDefaultStructuralSharingOption,
|
|
828
898
|
TRouterHistory,
|
|
829
|
-
TDehydrated
|
|
830
|
-
TTransformerConfig
|
|
899
|
+
TDehydrated
|
|
831
900
|
>,
|
|
832
901
|
) {
|
|
833
902
|
this.update({
|
|
@@ -865,8 +934,7 @@ export class RouterCore<
|
|
|
865
934
|
TTrailingSlashOption,
|
|
866
935
|
TDefaultStructuralSharingOption,
|
|
867
936
|
TRouterHistory,
|
|
868
|
-
TDehydrated
|
|
869
|
-
TTransformerConfig
|
|
937
|
+
TDehydrated
|
|
870
938
|
> = (newOptions) => {
|
|
871
939
|
if (newOptions.notFoundRoute) {
|
|
872
940
|
console.warn(
|
|
@@ -874,7 +942,6 @@ export class RouterCore<
|
|
|
874
942
|
)
|
|
875
943
|
}
|
|
876
944
|
|
|
877
|
-
const previousOptions = this.options
|
|
878
945
|
this.options = {
|
|
879
946
|
...this.options,
|
|
880
947
|
...newOptions,
|
|
@@ -892,32 +959,42 @@ export class RouterCore<
|
|
|
892
959
|
: undefined
|
|
893
960
|
|
|
894
961
|
if (
|
|
895
|
-
!this.
|
|
896
|
-
(
|
|
962
|
+
!this.history ||
|
|
963
|
+
(this.options.history && this.options.history !== this.history)
|
|
897
964
|
) {
|
|
898
|
-
if (
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
this.
|
|
965
|
+
if (!this.options.history) {
|
|
966
|
+
if (!this.isServer) {
|
|
967
|
+
this.history = createBrowserHistory() as TRouterHistory
|
|
968
|
+
}
|
|
969
|
+
} else {
|
|
970
|
+
this.history = this.options.history
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
// For backwards compatibility, we support a basepath option, which we now implement as a rewrite
|
|
974
|
+
if (this.options.basepath) {
|
|
975
|
+
const basepathRewrite = rewriteBasepath({
|
|
976
|
+
basepath: this.options.basepath,
|
|
977
|
+
})
|
|
978
|
+
if (this.options.rewrite) {
|
|
979
|
+
this.rewrite = composeRewrites([basepathRewrite, this.options.rewrite])
|
|
904
980
|
} else {
|
|
905
|
-
this.
|
|
981
|
+
this.rewrite = basepathRewrite
|
|
906
982
|
}
|
|
983
|
+
} else {
|
|
984
|
+
this.rewrite = this.options.rewrite
|
|
907
985
|
}
|
|
908
986
|
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
(this.
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
this.latestLocation = this.parseLocation()
|
|
987
|
+
this.origin = this.options.origin
|
|
988
|
+
if (!this.origin) {
|
|
989
|
+
if (!this.isServer) {
|
|
990
|
+
this.origin = window.origin
|
|
991
|
+
} else {
|
|
992
|
+
// fallback for the server, can be overridden by calling router.update({origin}) on the server
|
|
993
|
+
this.origin = 'http://localhost'
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
if (this.history) {
|
|
997
|
+
this.updateLatestLocation()
|
|
921
998
|
}
|
|
922
999
|
|
|
923
1000
|
if (this.options.routeTree !== this.routeTree) {
|
|
@@ -925,7 +1002,7 @@ export class RouterCore<
|
|
|
925
1002
|
this.buildRouteTree()
|
|
926
1003
|
}
|
|
927
1004
|
|
|
928
|
-
if (!this.__store) {
|
|
1005
|
+
if (!this.__store && this.latestLocation) {
|
|
929
1006
|
this.__store = new Store(getInitialRouterState(this.latestLocation), {
|
|
930
1007
|
onUpdate: () => {
|
|
931
1008
|
this.__store.state = {
|
|
@@ -955,6 +1032,13 @@ export class RouterCore<
|
|
|
955
1032
|
return this.__store.state
|
|
956
1033
|
}
|
|
957
1034
|
|
|
1035
|
+
updateLatestLocation = () => {
|
|
1036
|
+
this.latestLocation = this.parseLocation(
|
|
1037
|
+
this.history.location,
|
|
1038
|
+
this.latestLocation,
|
|
1039
|
+
)
|
|
1040
|
+
}
|
|
1041
|
+
|
|
958
1042
|
buildRouteTree = () => {
|
|
959
1043
|
const { routesById, routesByPath, flatRoutes } = processRouteTree({
|
|
960
1044
|
routeTree: this.routeTree,
|
|
@@ -1001,29 +1085,41 @@ export class RouterCore<
|
|
|
1001
1085
|
}
|
|
1002
1086
|
|
|
1003
1087
|
parseLocation: ParseLocationFn<TRouteTree> = (
|
|
1004
|
-
previousLocation,
|
|
1005
1088
|
locationToParse,
|
|
1089
|
+
previousLocation,
|
|
1006
1090
|
) => {
|
|
1007
1091
|
const parse = ({
|
|
1008
|
-
|
|
1009
|
-
search,
|
|
1010
|
-
hash,
|
|
1092
|
+
href,
|
|
1011
1093
|
state,
|
|
1012
1094
|
}: HistoryLocation): ParsedLocation<FullSearchSchema<TRouteTree>> => {
|
|
1013
|
-
|
|
1095
|
+
// Before we do any processing, we need to allow rewrites to modify the URL
|
|
1096
|
+
// build up the full URL by combining the href from history with the router's origin
|
|
1097
|
+
const fullUrl = new URL(href, this.origin)
|
|
1098
|
+
const url = executeRewriteInput(this.rewrite, fullUrl)
|
|
1099
|
+
|
|
1100
|
+
const parsedSearch = this.options.parseSearch(url.search)
|
|
1014
1101
|
const searchStr = this.options.stringifySearch(parsedSearch)
|
|
1102
|
+
// Make sure our final url uses the re-stringified pathname, search, and has for consistency
|
|
1103
|
+
// (We were already doing this, so just keeping it for now)
|
|
1104
|
+
url.search = searchStr
|
|
1105
|
+
|
|
1106
|
+
const fullPath = url.href.replace(url.origin, '')
|
|
1107
|
+
|
|
1108
|
+
const { pathname, hash } = url
|
|
1015
1109
|
|
|
1016
1110
|
return {
|
|
1111
|
+
href: fullPath,
|
|
1112
|
+
publicHref: href,
|
|
1113
|
+
url: url.href,
|
|
1017
1114
|
pathname,
|
|
1018
1115
|
searchStr,
|
|
1019
1116
|
search: replaceEqualDeep(previousLocation?.search, parsedSearch) as any,
|
|
1020
1117
|
hash: hash.split('#').reverse()[0] ?? '',
|
|
1021
|
-
href: `${pathname}${searchStr}${hash}`,
|
|
1022
1118
|
state: replaceEqualDeep(previousLocation?.state, state),
|
|
1023
1119
|
}
|
|
1024
1120
|
}
|
|
1025
1121
|
|
|
1026
|
-
const location = parse(locationToParse
|
|
1122
|
+
const location = parse(locationToParse)
|
|
1027
1123
|
|
|
1028
1124
|
const { __tempLocation, __tempKey } = location.state
|
|
1029
1125
|
|
|
@@ -1046,11 +1142,9 @@ export class RouterCore<
|
|
|
1046
1142
|
|
|
1047
1143
|
resolvePathWithBase = (from: string, path: string) => {
|
|
1048
1144
|
const resolvedPath = resolvePath({
|
|
1049
|
-
basepath: this.basepath,
|
|
1050
1145
|
base: from,
|
|
1051
1146
|
to: cleanPath(path),
|
|
1052
1147
|
trailingSlash: this.options.trailingSlash,
|
|
1053
|
-
caseSensitive: this.options.caseSensitive,
|
|
1054
1148
|
parseCache: this.parsePathnameCache,
|
|
1055
1149
|
})
|
|
1056
1150
|
return resolvedPath
|
|
@@ -1122,33 +1216,6 @@ export class RouterCore<
|
|
|
1122
1216
|
return rootRouteId
|
|
1123
1217
|
})()
|
|
1124
1218
|
|
|
1125
|
-
const parseErrors = matchedRoutes.map((route) => {
|
|
1126
|
-
let parsedParamsError
|
|
1127
|
-
|
|
1128
|
-
const parseParams =
|
|
1129
|
-
route.options.params?.parse ?? route.options.parseParams
|
|
1130
|
-
|
|
1131
|
-
if (parseParams) {
|
|
1132
|
-
try {
|
|
1133
|
-
const parsedParams = parseParams(routeParams)
|
|
1134
|
-
// Add the parsed params to the accumulated params bag
|
|
1135
|
-
Object.assign(routeParams, parsedParams)
|
|
1136
|
-
} catch (err: any) {
|
|
1137
|
-
parsedParamsError = new PathParamError(err.message, {
|
|
1138
|
-
cause: err,
|
|
1139
|
-
})
|
|
1140
|
-
|
|
1141
|
-
if (opts?.throwOnError) {
|
|
1142
|
-
throw parsedParamsError
|
|
1143
|
-
}
|
|
1144
|
-
|
|
1145
|
-
return parsedParamsError
|
|
1146
|
-
}
|
|
1147
|
-
}
|
|
1148
|
-
|
|
1149
|
-
return
|
|
1150
|
-
})
|
|
1151
|
-
|
|
1152
1219
|
const matches: Array<AnyRouteMatch> = []
|
|
1153
1220
|
|
|
1154
1221
|
const getParentContext = (parentMatch?: AnyRouteMatch) => {
|
|
@@ -1221,12 +1288,18 @@ export class RouterCore<
|
|
|
1221
1288
|
|
|
1222
1289
|
const loaderDepsHash = loaderDeps ? JSON.stringify(loaderDeps) : ''
|
|
1223
1290
|
|
|
1224
|
-
const {
|
|
1291
|
+
const { interpolatedPath, usedParams } = interpolatePath({
|
|
1225
1292
|
path: route.fullPath,
|
|
1226
1293
|
params: routeParams,
|
|
1227
1294
|
decodeCharMap: this.pathParamsDecodeCharMap,
|
|
1228
1295
|
})
|
|
1229
1296
|
|
|
1297
|
+
// Waste not, want not. If we already have a match for this route,
|
|
1298
|
+
// reuse it. This is important for layout routes, which might stick
|
|
1299
|
+
// around between navigation actions that only change leaf routes.
|
|
1300
|
+
|
|
1301
|
+
// Existing matches are matches that are already loaded along with
|
|
1302
|
+
// pending matches that are still loading
|
|
1230
1303
|
const matchId =
|
|
1231
1304
|
interpolatePath({
|
|
1232
1305
|
path: route.id,
|
|
@@ -1236,18 +1309,40 @@ export class RouterCore<
|
|
|
1236
1309
|
parseCache: this.parsePathnameCache,
|
|
1237
1310
|
}).interpolatedPath + loaderDepsHash
|
|
1238
1311
|
|
|
1239
|
-
// Waste not, want not. If we already have a match for this route,
|
|
1240
|
-
// reuse it. This is important for layout routes, which might stick
|
|
1241
|
-
// around between navigation actions that only change leaf routes.
|
|
1242
|
-
|
|
1243
|
-
// Existing matches are matches that are already loaded along with
|
|
1244
|
-
// pending matches that are still loading
|
|
1245
1312
|
const existingMatch = this.getMatch(matchId)
|
|
1246
1313
|
|
|
1247
1314
|
const previousMatch = this.state.matches.find(
|
|
1248
1315
|
(d) => d.routeId === route.id,
|
|
1249
1316
|
)
|
|
1250
1317
|
|
|
1318
|
+
const strictParams = existingMatch?._strictParams ?? usedParams
|
|
1319
|
+
|
|
1320
|
+
let paramsError: PathParamError | undefined = undefined
|
|
1321
|
+
|
|
1322
|
+
if (!existingMatch) {
|
|
1323
|
+
const strictParseParams =
|
|
1324
|
+
route.options.params?.parse ?? route.options.parseParams
|
|
1325
|
+
|
|
1326
|
+
if (strictParseParams) {
|
|
1327
|
+
try {
|
|
1328
|
+
Object.assign(
|
|
1329
|
+
strictParams,
|
|
1330
|
+
strictParseParams(strictParams as Record<string, string>),
|
|
1331
|
+
)
|
|
1332
|
+
} catch (err: any) {
|
|
1333
|
+
paramsError = new PathParamError(err.message, {
|
|
1334
|
+
cause: err,
|
|
1335
|
+
})
|
|
1336
|
+
|
|
1337
|
+
if (opts?.throwOnError) {
|
|
1338
|
+
throw paramsError
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1344
|
+
Object.assign(routeParams, strictParams)
|
|
1345
|
+
|
|
1251
1346
|
const cause = previousMatch ? 'stay' : 'enter'
|
|
1252
1347
|
|
|
1253
1348
|
let match: AnyRouteMatch
|
|
@@ -1259,7 +1354,7 @@ export class RouterCore<
|
|
|
1259
1354
|
params: previousMatch
|
|
1260
1355
|
? replaceEqualDeep(previousMatch.params, routeParams)
|
|
1261
1356
|
: routeParams,
|
|
1262
|
-
_strictParams:
|
|
1357
|
+
_strictParams: strictParams,
|
|
1263
1358
|
search: previousMatch
|
|
1264
1359
|
? replaceEqualDeep(previousMatch.search, preMatchSearch)
|
|
1265
1360
|
: replaceEqualDeep(existingMatch.search, preMatchSearch),
|
|
@@ -1281,8 +1376,8 @@ export class RouterCore<
|
|
|
1281
1376
|
params: previousMatch
|
|
1282
1377
|
? replaceEqualDeep(previousMatch.params, routeParams)
|
|
1283
1378
|
: routeParams,
|
|
1284
|
-
_strictParams:
|
|
1285
|
-
pathname:
|
|
1379
|
+
_strictParams: strictParams,
|
|
1380
|
+
pathname: interpolatedPath,
|
|
1286
1381
|
updatedAt: Date.now(),
|
|
1287
1382
|
search: previousMatch
|
|
1288
1383
|
? replaceEqualDeep(previousMatch.search, preMatchSearch)
|
|
@@ -1292,7 +1387,7 @@ export class RouterCore<
|
|
|
1292
1387
|
status,
|
|
1293
1388
|
isFetching: false,
|
|
1294
1389
|
error: undefined,
|
|
1295
|
-
paramsError
|
|
1390
|
+
paramsError,
|
|
1296
1391
|
__routeContext: undefined,
|
|
1297
1392
|
_nonReactive: {
|
|
1298
1393
|
loadPromise: createControlledPromise(),
|
|
@@ -1387,7 +1482,6 @@ export class RouterCore<
|
|
|
1387
1482
|
return getMatchedRoutes({
|
|
1388
1483
|
pathname,
|
|
1389
1484
|
routePathname,
|
|
1390
|
-
basepath: this.basepath,
|
|
1391
1485
|
caseSensitive: this.options.caseSensitive,
|
|
1392
1486
|
routesByPath: this.routesByPath,
|
|
1393
1487
|
routesById: this.routesById,
|
|
@@ -1425,50 +1519,44 @@ export class RouterCore<
|
|
|
1425
1519
|
_buildLocation: true,
|
|
1426
1520
|
})
|
|
1427
1521
|
|
|
1522
|
+
// Now let's find the starting pathname
|
|
1523
|
+
// This should default to the current location if no from is provided
|
|
1428
1524
|
const lastMatch = last(allCurrentLocationMatches)!
|
|
1429
1525
|
|
|
1430
|
-
//
|
|
1431
|
-
//
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
// If the route is changing we need to find the relative fromPath
|
|
1443
|
-
if (dest.unsafeRelative === 'path') {
|
|
1444
|
-
fromPath = currentLocation.pathname
|
|
1445
|
-
} else if (routeIsChanging && dest.from) {
|
|
1446
|
-
fromPath = dest.from
|
|
1447
|
-
|
|
1448
|
-
// do this check only on navigations during test or development
|
|
1449
|
-
if (process.env.NODE_ENV !== 'production' && dest._isNavigate) {
|
|
1450
|
-
const allFromMatches = this.getMatchedRoutes(
|
|
1451
|
-
dest.from,
|
|
1452
|
-
undefined,
|
|
1453
|
-
).matchedRoutes
|
|
1526
|
+
// check that from path exists in the current route tree
|
|
1527
|
+
// do this check only on navigations during test or development
|
|
1528
|
+
if (
|
|
1529
|
+
dest.from &&
|
|
1530
|
+
process.env.NODE_ENV !== 'production' &&
|
|
1531
|
+
dest._isNavigate
|
|
1532
|
+
) {
|
|
1533
|
+
const allFromMatches = this.getMatchedRoutes(
|
|
1534
|
+
dest.from,
|
|
1535
|
+
undefined,
|
|
1536
|
+
).matchedRoutes
|
|
1454
1537
|
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1538
|
+
const matchedFrom = findLast(allCurrentLocationMatches, (d) => {
|
|
1539
|
+
return comparePaths(d.fullPath, dest.from!)
|
|
1540
|
+
})
|
|
1458
1541
|
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1542
|
+
const matchedCurrent = findLast(allFromMatches, (d) => {
|
|
1543
|
+
return comparePaths(d.fullPath, lastMatch.fullPath)
|
|
1544
|
+
})
|
|
1462
1545
|
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
}
|
|
1546
|
+
// for from to be invalid it shouldn't just be unmatched to currentLocation
|
|
1547
|
+
// but the currentLocation should also be unmatched to from
|
|
1548
|
+
if (!matchedFrom && !matchedCurrent) {
|
|
1549
|
+
console.warn(`Could not find match for from: ${dest.from}`)
|
|
1468
1550
|
}
|
|
1469
1551
|
}
|
|
1470
1552
|
|
|
1471
|
-
|
|
1553
|
+
const defaultedFromPath =
|
|
1554
|
+
dest.unsafeRelative === 'path'
|
|
1555
|
+
? currentLocation.pathname
|
|
1556
|
+
: (dest.from ?? lastMatch.fullPath)
|
|
1557
|
+
|
|
1558
|
+
// ensure this includes the basePath if set
|
|
1559
|
+
const fromPath = this.resolvePathWithBase(defaultedFromPath, '.')
|
|
1472
1560
|
|
|
1473
1561
|
// From search should always use the current location
|
|
1474
1562
|
const fromSearch = lastMatch.search
|
|
@@ -1476,6 +1564,7 @@ export class RouterCore<
|
|
|
1476
1564
|
const fromParams = { ...lastMatch.params }
|
|
1477
1565
|
|
|
1478
1566
|
// Resolve the next to
|
|
1567
|
+
// ensure this includes the basePath if set
|
|
1479
1568
|
const nextTo = dest.to
|
|
1480
1569
|
? this.resolvePathWithBase(fromPath, `${dest.to}`)
|
|
1481
1570
|
: this.resolvePathWithBase(fromPath, '.')
|
|
@@ -1581,14 +1670,25 @@ export class RouterCore<
|
|
|
1581
1670
|
// Replace the equal deep
|
|
1582
1671
|
nextState = replaceEqualDeep(currentLocation.state, nextState)
|
|
1583
1672
|
|
|
1584
|
-
//
|
|
1673
|
+
// Create the full path of the location
|
|
1674
|
+
const fullPath = `${nextPathname}${searchStr}${hashStr}`
|
|
1675
|
+
|
|
1676
|
+
// Create the new href with full origin
|
|
1677
|
+
const url = new URL(fullPath, this.origin)
|
|
1678
|
+
|
|
1679
|
+
// If a rewrite function is provided, use it to rewrite the URL
|
|
1680
|
+
const rewrittenUrl = executeRewriteOutput(this.rewrite, url)
|
|
1681
|
+
|
|
1585
1682
|
return {
|
|
1683
|
+
publicHref:
|
|
1684
|
+
rewrittenUrl.pathname + rewrittenUrl.search + rewrittenUrl.hash,
|
|
1685
|
+
href: fullPath,
|
|
1686
|
+
url: rewrittenUrl.href,
|
|
1586
1687
|
pathname: nextPathname,
|
|
1587
1688
|
search: nextSearch,
|
|
1588
1689
|
searchStr,
|
|
1589
1690
|
state: nextState as any,
|
|
1590
1691
|
hash: hash ?? '',
|
|
1591
|
-
href: `${nextPathname}${searchStr}${hashStr}`,
|
|
1592
1692
|
unmaskOnReload: dest.unmaskOnReload,
|
|
1593
1693
|
}
|
|
1594
1694
|
}
|
|
@@ -1606,7 +1706,6 @@ export class RouterCore<
|
|
|
1606
1706
|
|
|
1607
1707
|
const foundMask = this.options.routeMasks?.find((d) => {
|
|
1608
1708
|
const match = matchPathname(
|
|
1609
|
-
this.basepath,
|
|
1610
1709
|
next.pathname,
|
|
1611
1710
|
{
|
|
1612
1711
|
to: d.from,
|
|
@@ -1636,8 +1735,7 @@ export class RouterCore<
|
|
|
1636
1735
|
}
|
|
1637
1736
|
|
|
1638
1737
|
if (maskedNext) {
|
|
1639
|
-
|
|
1640
|
-
next.maskedLocation = maskedFinal
|
|
1738
|
+
next.maskedLocation = maskedNext
|
|
1641
1739
|
}
|
|
1642
1740
|
|
|
1643
1741
|
return next
|
|
@@ -1680,7 +1778,8 @@ export class RouterCore<
|
|
|
1680
1778
|
return isEqual
|
|
1681
1779
|
}
|
|
1682
1780
|
|
|
1683
|
-
const isSameUrl =
|
|
1781
|
+
const isSameUrl =
|
|
1782
|
+
trimPathRight(this.latestLocation.href) === trimPathRight(next.href)
|
|
1684
1783
|
|
|
1685
1784
|
const previousCommitPromise = this.commitLocationPromise
|
|
1686
1785
|
this.commitLocationPromise = createControlledPromise<void>(() => {
|
|
@@ -1729,7 +1828,7 @@ export class RouterCore<
|
|
|
1729
1828
|
this.shouldViewTransition = viewTransition
|
|
1730
1829
|
|
|
1731
1830
|
this.history[next.replace ? 'replace' : 'push'](
|
|
1732
|
-
nextHistory.
|
|
1831
|
+
nextHistory.publicHref,
|
|
1733
1832
|
nextHistory.state,
|
|
1734
1833
|
{ ignoreBlocker },
|
|
1735
1834
|
)
|
|
@@ -1791,7 +1890,7 @@ export class RouterCore<
|
|
|
1791
1890
|
if (reloadDocument) {
|
|
1792
1891
|
if (!href) {
|
|
1793
1892
|
const location = this.buildLocation({ to, ...rest } as any)
|
|
1794
|
-
href =
|
|
1893
|
+
href = location.href
|
|
1795
1894
|
}
|
|
1796
1895
|
if (rest.replace) {
|
|
1797
1896
|
window.location.replace(href)
|
|
@@ -1814,7 +1913,7 @@ export class RouterCore<
|
|
|
1814
1913
|
beforeLoad = () => {
|
|
1815
1914
|
// Cancel any pending matches
|
|
1816
1915
|
this.cancelMatches()
|
|
1817
|
-
this.
|
|
1916
|
+
this.updateLatestLocation()
|
|
1818
1917
|
|
|
1819
1918
|
if (this.isServer) {
|
|
1820
1919
|
// for SPAs on the initial load, this is handled by the Transitioner
|
|
@@ -1844,6 +1943,7 @@ export class RouterCore<
|
|
|
1844
1943
|
throw redirect({ href: nextLocation.href })
|
|
1845
1944
|
}
|
|
1846
1945
|
}
|
|
1946
|
+
|
|
1847
1947
|
// Match the routes
|
|
1848
1948
|
const pendingMatches = this.matchRoutes(this.latestLocation)
|
|
1849
1949
|
|
|
@@ -1989,6 +2089,7 @@ export class RouterCore<
|
|
|
1989
2089
|
this.latestLoadPromise = undefined
|
|
1990
2090
|
this.commitLocationPromise = undefined
|
|
1991
2091
|
}
|
|
2092
|
+
|
|
1992
2093
|
resolve()
|
|
1993
2094
|
})
|
|
1994
2095
|
})
|
|
@@ -2287,7 +2388,6 @@ export class RouterCore<
|
|
|
2287
2388
|
: this.state.resolvedLocation || this.state.location
|
|
2288
2389
|
|
|
2289
2390
|
const match = matchPathname(
|
|
2290
|
-
this.basepath,
|
|
2291
2391
|
baseLocation.pathname,
|
|
2292
2392
|
{
|
|
2293
2393
|
...opts,
|
|
@@ -2397,233 +2497,9 @@ function validateSearch(validateSearch: AnyValidator, input: unknown): unknown {
|
|
|
2397
2497
|
return {}
|
|
2398
2498
|
}
|
|
2399
2499
|
|
|
2400
|
-
interface RouteLike {
|
|
2401
|
-
id: string
|
|
2402
|
-
isRoot?: boolean
|
|
2403
|
-
path?: string
|
|
2404
|
-
fullPath: string
|
|
2405
|
-
rank?: number
|
|
2406
|
-
parentRoute?: RouteLike
|
|
2407
|
-
children?: Array<RouteLike>
|
|
2408
|
-
options?: {
|
|
2409
|
-
caseSensitive?: boolean
|
|
2410
|
-
}
|
|
2411
|
-
}
|
|
2412
|
-
|
|
2413
|
-
export type ProcessRouteTreeResult<TRouteLike extends RouteLike> = {
|
|
2414
|
-
routesById: Record<string, TRouteLike>
|
|
2415
|
-
routesByPath: Record<string, TRouteLike>
|
|
2416
|
-
flatRoutes: Array<TRouteLike>
|
|
2417
|
-
}
|
|
2418
|
-
|
|
2419
|
-
const REQUIRED_PARAM_BASE_SCORE = 0.5
|
|
2420
|
-
const OPTIONAL_PARAM_BASE_SCORE = 0.4
|
|
2421
|
-
const WILDCARD_PARAM_BASE_SCORE = 0.25
|
|
2422
|
-
const BOTH_PRESENCE_BASE_SCORE = 0.05
|
|
2423
|
-
const PREFIX_PRESENCE_BASE_SCORE = 0.02
|
|
2424
|
-
const SUFFIX_PRESENCE_BASE_SCORE = 0.01
|
|
2425
|
-
const PREFIX_LENGTH_SCORE_MULTIPLIER = 0.0002
|
|
2426
|
-
const SUFFIX_LENGTH_SCORE_MULTIPLIER = 0.0001
|
|
2427
|
-
|
|
2428
|
-
function handleParam(segment: Segment, baseScore: number) {
|
|
2429
|
-
if (segment.prefixSegment && segment.suffixSegment) {
|
|
2430
|
-
return (
|
|
2431
|
-
baseScore +
|
|
2432
|
-
BOTH_PRESENCE_BASE_SCORE +
|
|
2433
|
-
PREFIX_LENGTH_SCORE_MULTIPLIER * segment.prefixSegment.length +
|
|
2434
|
-
SUFFIX_LENGTH_SCORE_MULTIPLIER * segment.suffixSegment.length
|
|
2435
|
-
)
|
|
2436
|
-
}
|
|
2437
|
-
|
|
2438
|
-
if (segment.prefixSegment) {
|
|
2439
|
-
return (
|
|
2440
|
-
baseScore +
|
|
2441
|
-
PREFIX_PRESENCE_BASE_SCORE +
|
|
2442
|
-
PREFIX_LENGTH_SCORE_MULTIPLIER * segment.prefixSegment.length
|
|
2443
|
-
)
|
|
2444
|
-
}
|
|
2445
|
-
|
|
2446
|
-
if (segment.suffixSegment) {
|
|
2447
|
-
return (
|
|
2448
|
-
baseScore +
|
|
2449
|
-
SUFFIX_PRESENCE_BASE_SCORE +
|
|
2450
|
-
SUFFIX_LENGTH_SCORE_MULTIPLIER * segment.suffixSegment.length
|
|
2451
|
-
)
|
|
2452
|
-
}
|
|
2453
|
-
|
|
2454
|
-
return baseScore
|
|
2455
|
-
}
|
|
2456
|
-
|
|
2457
|
-
export function processRouteTree<TRouteLike extends RouteLike>({
|
|
2458
|
-
routeTree,
|
|
2459
|
-
initRoute,
|
|
2460
|
-
}: {
|
|
2461
|
-
routeTree: TRouteLike
|
|
2462
|
-
initRoute?: (route: TRouteLike, index: number) => void
|
|
2463
|
-
}): ProcessRouteTreeResult<TRouteLike> {
|
|
2464
|
-
const routesById = {} as Record<string, TRouteLike>
|
|
2465
|
-
const routesByPath = {} as Record<string, TRouteLike>
|
|
2466
|
-
|
|
2467
|
-
const recurseRoutes = (childRoutes: Array<TRouteLike>) => {
|
|
2468
|
-
childRoutes.forEach((childRoute, i) => {
|
|
2469
|
-
initRoute?.(childRoute, i)
|
|
2470
|
-
|
|
2471
|
-
const existingRoute = routesById[childRoute.id]
|
|
2472
|
-
|
|
2473
|
-
invariant(
|
|
2474
|
-
!existingRoute,
|
|
2475
|
-
`Duplicate routes found with id: ${String(childRoute.id)}`,
|
|
2476
|
-
)
|
|
2477
|
-
|
|
2478
|
-
routesById[childRoute.id] = childRoute
|
|
2479
|
-
|
|
2480
|
-
if (!childRoute.isRoot && childRoute.path) {
|
|
2481
|
-
const trimmedFullPath = trimPathRight(childRoute.fullPath)
|
|
2482
|
-
if (
|
|
2483
|
-
!routesByPath[trimmedFullPath] ||
|
|
2484
|
-
childRoute.fullPath.endsWith('/')
|
|
2485
|
-
) {
|
|
2486
|
-
routesByPath[trimmedFullPath] = childRoute
|
|
2487
|
-
}
|
|
2488
|
-
}
|
|
2489
|
-
|
|
2490
|
-
const children = childRoute.children as Array<TRouteLike>
|
|
2491
|
-
|
|
2492
|
-
if (children?.length) {
|
|
2493
|
-
recurseRoutes(children)
|
|
2494
|
-
}
|
|
2495
|
-
})
|
|
2496
|
-
}
|
|
2497
|
-
|
|
2498
|
-
recurseRoutes([routeTree])
|
|
2499
|
-
|
|
2500
|
-
const scoredRoutes: Array<{
|
|
2501
|
-
child: TRouteLike
|
|
2502
|
-
trimmed: string
|
|
2503
|
-
parsed: ReadonlyArray<Segment>
|
|
2504
|
-
index: number
|
|
2505
|
-
scores: Array<number>
|
|
2506
|
-
hasStaticAfter: boolean
|
|
2507
|
-
optionalParamCount: number
|
|
2508
|
-
}> = []
|
|
2509
|
-
|
|
2510
|
-
const routes: Array<TRouteLike> = Object.values(routesById)
|
|
2511
|
-
|
|
2512
|
-
routes.forEach((d, i) => {
|
|
2513
|
-
if (d.isRoot || !d.path) {
|
|
2514
|
-
return
|
|
2515
|
-
}
|
|
2516
|
-
|
|
2517
|
-
const trimmed = trimPathLeft(d.fullPath)
|
|
2518
|
-
let parsed = parsePathname(trimmed)
|
|
2519
|
-
|
|
2520
|
-
// Removes the leading slash if it is not the only remaining segment
|
|
2521
|
-
let skip = 0
|
|
2522
|
-
while (parsed.length > skip + 1 && parsed[skip]?.value === '/') {
|
|
2523
|
-
skip++
|
|
2524
|
-
}
|
|
2525
|
-
if (skip > 0) parsed = parsed.slice(skip)
|
|
2526
|
-
|
|
2527
|
-
let optionalParamCount = 0
|
|
2528
|
-
let hasStaticAfter = false
|
|
2529
|
-
const scores = parsed.map((segment, index) => {
|
|
2530
|
-
if (segment.value === '/') {
|
|
2531
|
-
return 0.75
|
|
2532
|
-
}
|
|
2533
|
-
|
|
2534
|
-
let baseScore: number | undefined = undefined
|
|
2535
|
-
if (segment.type === SEGMENT_TYPE_PARAM) {
|
|
2536
|
-
baseScore = REQUIRED_PARAM_BASE_SCORE
|
|
2537
|
-
} else if (segment.type === SEGMENT_TYPE_OPTIONAL_PARAM) {
|
|
2538
|
-
baseScore = OPTIONAL_PARAM_BASE_SCORE
|
|
2539
|
-
optionalParamCount++
|
|
2540
|
-
} else if (segment.type === SEGMENT_TYPE_WILDCARD) {
|
|
2541
|
-
baseScore = WILDCARD_PARAM_BASE_SCORE
|
|
2542
|
-
}
|
|
2543
|
-
|
|
2544
|
-
if (baseScore) {
|
|
2545
|
-
// if there is any static segment (that is not an index) after a required / optional param,
|
|
2546
|
-
// we will boost this param so it ranks higher than a required/optional param without a static segment after it
|
|
2547
|
-
// JUST FOR SORTING, NOT FOR MATCHING
|
|
2548
|
-
for (let i = index + 1; i < parsed.length; i++) {
|
|
2549
|
-
const nextSegment = parsed[i]!
|
|
2550
|
-
if (
|
|
2551
|
-
nextSegment.type === SEGMENT_TYPE_PATHNAME &&
|
|
2552
|
-
nextSegment.value !== '/'
|
|
2553
|
-
) {
|
|
2554
|
-
hasStaticAfter = true
|
|
2555
|
-
return handleParam(segment, baseScore + 0.2)
|
|
2556
|
-
}
|
|
2557
|
-
}
|
|
2558
|
-
|
|
2559
|
-
return handleParam(segment, baseScore)
|
|
2560
|
-
}
|
|
2561
|
-
|
|
2562
|
-
return 1
|
|
2563
|
-
})
|
|
2564
|
-
|
|
2565
|
-
scoredRoutes.push({
|
|
2566
|
-
child: d,
|
|
2567
|
-
trimmed,
|
|
2568
|
-
parsed,
|
|
2569
|
-
index: i,
|
|
2570
|
-
scores,
|
|
2571
|
-
optionalParamCount,
|
|
2572
|
-
hasStaticAfter,
|
|
2573
|
-
})
|
|
2574
|
-
})
|
|
2575
|
-
|
|
2576
|
-
const flatRoutes = scoredRoutes
|
|
2577
|
-
.sort((a, b) => {
|
|
2578
|
-
const minLength = Math.min(a.scores.length, b.scores.length)
|
|
2579
|
-
|
|
2580
|
-
// Sort by segment-by-segment score comparison ONLY for the common prefix
|
|
2581
|
-
for (let i = 0; i < minLength; i++) {
|
|
2582
|
-
if (a.scores[i] !== b.scores[i]) {
|
|
2583
|
-
return b.scores[i]! - a.scores[i]!
|
|
2584
|
-
}
|
|
2585
|
-
}
|
|
2586
|
-
|
|
2587
|
-
// If all common segments have equal scores, then consider length and specificity
|
|
2588
|
-
if (a.scores.length !== b.scores.length) {
|
|
2589
|
-
// If different number of optional parameters, fewer optional parameters wins (more specific)
|
|
2590
|
-
// only if both or none of the routes has static segments after the params
|
|
2591
|
-
if (a.optionalParamCount !== b.optionalParamCount) {
|
|
2592
|
-
if (a.hasStaticAfter === b.hasStaticAfter) {
|
|
2593
|
-
return a.optionalParamCount - b.optionalParamCount
|
|
2594
|
-
} else if (a.hasStaticAfter && !b.hasStaticAfter) {
|
|
2595
|
-
return -1
|
|
2596
|
-
} else if (!a.hasStaticAfter && b.hasStaticAfter) {
|
|
2597
|
-
return 1
|
|
2598
|
-
}
|
|
2599
|
-
}
|
|
2600
|
-
|
|
2601
|
-
// If same number of optional parameters, longer path wins (for static segments)
|
|
2602
|
-
return b.scores.length - a.scores.length
|
|
2603
|
-
}
|
|
2604
|
-
|
|
2605
|
-
// Sort by min available parsed value for alphabetical ordering
|
|
2606
|
-
for (let i = 0; i < minLength; i++) {
|
|
2607
|
-
if (a.parsed[i]!.value !== b.parsed[i]!.value) {
|
|
2608
|
-
return a.parsed[i]!.value > b.parsed[i]!.value ? 1 : -1
|
|
2609
|
-
}
|
|
2610
|
-
}
|
|
2611
|
-
|
|
2612
|
-
// Sort by original index
|
|
2613
|
-
return a.index - b.index
|
|
2614
|
-
})
|
|
2615
|
-
.map((d, i) => {
|
|
2616
|
-
d.child.rank = i
|
|
2617
|
-
return d.child
|
|
2618
|
-
})
|
|
2619
|
-
|
|
2620
|
-
return { routesById, routesByPath, flatRoutes }
|
|
2621
|
-
}
|
|
2622
|
-
|
|
2623
2500
|
export function getMatchedRoutes<TRouteLike extends RouteLike>({
|
|
2624
2501
|
pathname,
|
|
2625
2502
|
routePathname,
|
|
2626
|
-
basepath,
|
|
2627
2503
|
caseSensitive,
|
|
2628
2504
|
routesByPath,
|
|
2629
2505
|
routesById,
|
|
@@ -2632,7 +2508,6 @@ export function getMatchedRoutes<TRouteLike extends RouteLike>({
|
|
|
2632
2508
|
}: {
|
|
2633
2509
|
pathname: string
|
|
2634
2510
|
routePathname?: string
|
|
2635
|
-
basepath: string
|
|
2636
2511
|
caseSensitive?: boolean
|
|
2637
2512
|
routesByPath: Record<string, TRouteLike>
|
|
2638
2513
|
routesById: Record<string, TRouteLike>
|
|
@@ -2643,7 +2518,6 @@ export function getMatchedRoutes<TRouteLike extends RouteLike>({
|
|
|
2643
2518
|
const trimmedPath = trimPathRight(pathname)
|
|
2644
2519
|
const getMatchedParams = (route: TRouteLike) => {
|
|
2645
2520
|
const result = matchPathname(
|
|
2646
|
-
basepath,
|
|
2647
2521
|
trimmedPath,
|
|
2648
2522
|
{
|
|
2649
2523
|
to: route.fullPath,
|