@tanstack/router-core 0.0.1-beta.184 → 0.0.1-beta.186
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/fileRoute.js.map +1 -1
- package/build/cjs/history.js +21 -19
- package/build/cjs/history.js.map +1 -1
- package/build/cjs/index.js +1 -0
- package/build/cjs/index.js.map +1 -1
- package/build/cjs/route.js +4 -0
- package/build/cjs/route.js.map +1 -1
- package/build/cjs/router.js +205 -101
- package/build/cjs/router.js.map +1 -1
- package/build/cjs/scroll-restoration.js +1 -1
- package/build/cjs/scroll-restoration.js.map +1 -1
- package/build/cjs/utils.js +15 -0
- package/build/cjs/utils.js.map +1 -1
- package/build/esm/index.js +247 -123
- package/build/esm/index.js.map +1 -1
- package/build/stats-html.html +1 -1
- package/build/stats-react.json +132 -132
- package/build/types/fileRoute.d.ts +1 -0
- package/build/types/history.d.ts +8 -3
- package/build/types/link.d.ts +23 -18
- package/build/types/route.d.ts +23 -7
- package/build/types/routeInfo.d.ts +3 -3
- package/build/types/router.d.ts +30 -20
- package/build/types/utils.d.ts +2 -11
- package/build/umd/index.development.js +247 -122
- package/build/umd/index.development.js.map +1 -1
- package/build/umd/index.production.js +2 -2
- package/build/umd/index.production.js.map +1 -1
- package/package.json +1 -1
- package/src/fileRoute.ts +3 -3
- package/src/history.ts +34 -26
- package/src/link.ts +66 -51
- package/src/route.ts +61 -16
- package/src/routeInfo.ts +3 -3
- package/src/router.ts +325 -181
- package/src/scroll-restoration.ts +1 -1
- package/src/utils.ts +21 -17
package/src/router.ts
CHANGED
|
@@ -29,6 +29,7 @@ import {
|
|
|
29
29
|
RegisteredRouteComponent,
|
|
30
30
|
RegisteredErrorRouteComponent,
|
|
31
31
|
RegisteredPendingRouteComponent,
|
|
32
|
+
RouteMask,
|
|
32
33
|
} from './route'
|
|
33
34
|
import {
|
|
34
35
|
RoutesById,
|
|
@@ -50,13 +51,15 @@ import {
|
|
|
50
51
|
Updater,
|
|
51
52
|
replaceEqualDeep,
|
|
52
53
|
partialDeepEqual,
|
|
54
|
+
NonNullableUpdater,
|
|
53
55
|
} from './utils'
|
|
54
56
|
import {
|
|
55
57
|
createBrowserHistory,
|
|
56
58
|
createMemoryHistory,
|
|
59
|
+
HistoryLocation,
|
|
60
|
+
LocationState,
|
|
57
61
|
RouterHistory,
|
|
58
62
|
} from './history'
|
|
59
|
-
import warning from 'tiny-warning'
|
|
60
63
|
|
|
61
64
|
//
|
|
62
65
|
|
|
@@ -78,25 +81,20 @@ export type RegisteredRouter = Register extends {
|
|
|
78
81
|
? TRouter
|
|
79
82
|
: AnyRouter
|
|
80
83
|
|
|
81
|
-
export interface
|
|
82
|
-
|
|
83
|
-
export interface ParsedLocation<
|
|
84
|
-
TSearchObj extends AnySearchSchema = {},
|
|
85
|
-
// TState extends LocationState = LocationState,
|
|
86
|
-
> {
|
|
84
|
+
export interface ParsedLocation<TSearchObj extends AnySearchSchema = {}> {
|
|
87
85
|
href: string
|
|
88
86
|
pathname: string
|
|
89
87
|
search: TSearchObj
|
|
90
88
|
searchStr: string
|
|
91
89
|
state: LocationState
|
|
92
90
|
hash: string
|
|
93
|
-
|
|
91
|
+
maskedLocation?: ParsedLocation<TSearchObj>
|
|
92
|
+
unmaskOnReload?: boolean
|
|
94
93
|
}
|
|
95
94
|
|
|
96
95
|
export interface FromLocation {
|
|
97
96
|
pathname: string
|
|
98
97
|
search?: unknown
|
|
99
|
-
key?: string
|
|
100
98
|
hash?: string
|
|
101
99
|
}
|
|
102
100
|
|
|
@@ -158,7 +156,7 @@ export interface RouterOptions<
|
|
|
158
156
|
parseSearch?: SearchParser
|
|
159
157
|
defaultPreload?: false | 'intent'
|
|
160
158
|
defaultPreloadDelay?: number
|
|
161
|
-
|
|
159
|
+
reloadOnWindowFocus?: boolean
|
|
162
160
|
defaultComponent?: RegisteredRouteComponent<
|
|
163
161
|
unknown,
|
|
164
162
|
AnySearchSchema,
|
|
@@ -192,12 +190,11 @@ export interface RouterOptions<
|
|
|
192
190
|
}>
|
|
193
191
|
dehydrate?: () => TDehydrated
|
|
194
192
|
hydrate?: (dehydrated: TDehydrated) => void
|
|
193
|
+
routeMasks?: RouteMask<TRouteTree>[]
|
|
194
|
+
unmaskOnReload?: boolean
|
|
195
195
|
}
|
|
196
196
|
|
|
197
|
-
export interface RouterState<
|
|
198
|
-
TRouteTree extends AnyRoute = AnyRoute,
|
|
199
|
-
// TState extends LocationState = LocationState,
|
|
200
|
-
> {
|
|
197
|
+
export interface RouterState<TRouteTree extends AnyRoute = AnyRoute> {
|
|
201
198
|
status: 'idle' | 'pending'
|
|
202
199
|
isFetching: boolean
|
|
203
200
|
matchesById: Record<string, RouteMatch<TRouteTree>>
|
|
@@ -219,11 +216,21 @@ export interface BuildNextOptions {
|
|
|
219
216
|
params?: true | Updater<unknown>
|
|
220
217
|
search?: true | Updater<unknown>
|
|
221
218
|
hash?: true | Updater<string>
|
|
222
|
-
state?: LocationState
|
|
223
|
-
|
|
219
|
+
state?: true | NonNullableUpdater<LocationState>
|
|
220
|
+
mask?: {
|
|
221
|
+
to?: string | number | null
|
|
222
|
+
params?: true | Updater<unknown>
|
|
223
|
+
search?: true | Updater<unknown>
|
|
224
|
+
hash?: true | Updater<string>
|
|
225
|
+
state?: true | NonNullableUpdater<LocationState>
|
|
226
|
+
unmaskOnReload?: boolean
|
|
227
|
+
}
|
|
224
228
|
from?: string
|
|
225
|
-
|
|
226
|
-
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export interface CommitLocationOptions {
|
|
232
|
+
replace?: boolean
|
|
233
|
+
resetScroll?: boolean
|
|
227
234
|
}
|
|
228
235
|
|
|
229
236
|
export interface MatchLocation {
|
|
@@ -231,7 +238,6 @@ export interface MatchLocation {
|
|
|
231
238
|
fuzzy?: boolean
|
|
232
239
|
caseSensitive?: boolean
|
|
233
240
|
from?: string
|
|
234
|
-
fromCurrent?: boolean
|
|
235
241
|
}
|
|
236
242
|
|
|
237
243
|
export interface MatchRouteOptions {
|
|
@@ -331,6 +337,8 @@ export class Router<
|
|
|
331
337
|
state: RouterState<TRouteTree>
|
|
332
338
|
dehydratedData?: TDehydrated
|
|
333
339
|
resetNextScroll = false
|
|
340
|
+
tempLocationKey = `${Math.round(Math.random() * 10000000)}`
|
|
341
|
+
// nextTemporaryLocation?: ParsedLocation<FullSearchSchema<TRouteTree>>
|
|
334
342
|
|
|
335
343
|
constructor(options: RouterConstructorOptions<TRouteTree, TDehydrated>) {
|
|
336
344
|
this.options = {
|
|
@@ -402,15 +410,15 @@ export class Router<
|
|
|
402
410
|
|
|
403
411
|
this.update(options)
|
|
404
412
|
|
|
405
|
-
const
|
|
406
|
-
hash: true,
|
|
407
|
-
fromCurrent: true,
|
|
413
|
+
const nextLocation = this.buildLocation({
|
|
408
414
|
search: true,
|
|
415
|
+
params: true,
|
|
416
|
+
hash: true,
|
|
409
417
|
state: true,
|
|
410
418
|
})
|
|
411
419
|
|
|
412
|
-
if (this.state.location.href !==
|
|
413
|
-
this.#commitLocation({ ...
|
|
420
|
+
if (this.state.location.href !== nextLocation.href) {
|
|
421
|
+
this.#commitLocation({ ...nextLocation, replace: true })
|
|
414
422
|
}
|
|
415
423
|
}
|
|
416
424
|
|
|
@@ -464,8 +472,10 @@ export class Router<
|
|
|
464
472
|
}
|
|
465
473
|
|
|
466
474
|
#onFocus = () => {
|
|
467
|
-
if (this.options.
|
|
468
|
-
this.invalidate(
|
|
475
|
+
if (this.options.reloadOnWindowFocus ?? true) {
|
|
476
|
+
this.invalidate({
|
|
477
|
+
__fromFocus: true,
|
|
478
|
+
})
|
|
469
479
|
}
|
|
470
480
|
}
|
|
471
481
|
|
|
@@ -517,17 +527,6 @@ export class Router<
|
|
|
517
527
|
return this
|
|
518
528
|
}
|
|
519
529
|
|
|
520
|
-
buildNext = (opts: BuildNextOptions): ParsedLocation => {
|
|
521
|
-
const next = this.#buildLocation(opts)
|
|
522
|
-
|
|
523
|
-
const __matches = this.matchRoutes(next.pathname, next.search)
|
|
524
|
-
|
|
525
|
-
return this.#buildLocation({
|
|
526
|
-
...opts,
|
|
527
|
-
__matches,
|
|
528
|
-
})
|
|
529
|
-
}
|
|
530
|
-
|
|
531
530
|
cancelMatches = () => {
|
|
532
531
|
this.state.matches.forEach((match) => {
|
|
533
532
|
this.cancelMatch(match.id)
|
|
@@ -608,7 +607,7 @@ export class Router<
|
|
|
608
607
|
try {
|
|
609
608
|
// Load the matches
|
|
610
609
|
try {
|
|
611
|
-
await this.loadMatches(pendingMatches)
|
|
610
|
+
await this.loadMatches(pendingMatches.map((d) => d.id))
|
|
612
611
|
} catch (err) {
|
|
613
612
|
// swallow this error, since we'll display the
|
|
614
613
|
// errors on the route components
|
|
@@ -683,10 +682,20 @@ export class Router<
|
|
|
683
682
|
prevMatchesById: Record<string, RouteMatch<TRouteTree>>,
|
|
684
683
|
nextMatches: AnyRouteMatch[],
|
|
685
684
|
): Record<string, RouteMatch<TRouteTree>> => {
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
685
|
+
let matchesById = { ...prevMatchesById }
|
|
686
|
+
|
|
687
|
+
nextMatches.forEach((match) => {
|
|
688
|
+
if (!matchesById[match.id]) {
|
|
689
|
+
matchesById[match.id] = match
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
matchesById[match.id] = {
|
|
693
|
+
...matchesById[match.id],
|
|
694
|
+
...match,
|
|
695
|
+
}
|
|
696
|
+
})
|
|
697
|
+
|
|
698
|
+
return matchesById
|
|
690
699
|
}
|
|
691
700
|
|
|
692
701
|
getRoute = (id: string): Route => {
|
|
@@ -702,7 +711,8 @@ export class Router<
|
|
|
702
711
|
maxAge?: number
|
|
703
712
|
} = this.state.location,
|
|
704
713
|
) => {
|
|
705
|
-
|
|
714
|
+
let next = this.buildLocation(navigateOpts)
|
|
715
|
+
|
|
706
716
|
const matches = this.matchRoutes(next.pathname, next.search, {
|
|
707
717
|
throwOnError: true,
|
|
708
718
|
})
|
|
@@ -714,12 +724,15 @@ export class Router<
|
|
|
714
724
|
}
|
|
715
725
|
})
|
|
716
726
|
|
|
717
|
-
await this.loadMatches(
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
727
|
+
await this.loadMatches(
|
|
728
|
+
matches.map((d) => d.id),
|
|
729
|
+
{
|
|
730
|
+
preload: true,
|
|
731
|
+
maxAge: navigateOpts.maxAge,
|
|
732
|
+
},
|
|
733
|
+
)
|
|
721
734
|
|
|
722
|
-
return matches
|
|
735
|
+
return [last(matches)!, matches] as const
|
|
723
736
|
}
|
|
724
737
|
|
|
725
738
|
cleanMatches = () => {
|
|
@@ -931,14 +944,13 @@ export class Router<
|
|
|
931
944
|
}
|
|
932
945
|
|
|
933
946
|
loadMatches = async (
|
|
934
|
-
|
|
947
|
+
matchIds: string[],
|
|
935
948
|
opts?: {
|
|
936
949
|
preload?: boolean
|
|
937
950
|
maxAge?: number
|
|
938
951
|
},
|
|
939
952
|
) => {
|
|
940
|
-
const getFreshMatches = () =>
|
|
941
|
-
_resolvedMatches.map((d) => this.getRouteMatch(d.id)!)
|
|
953
|
+
const getFreshMatches = () => matchIds.map((d) => this.getRouteMatch(d)!)
|
|
942
954
|
|
|
943
955
|
if (!opts?.preload) {
|
|
944
956
|
getFreshMatches().forEach((match) => {
|
|
@@ -1032,8 +1044,9 @@ export class Router<
|
|
|
1032
1044
|
}
|
|
1033
1045
|
}
|
|
1034
1046
|
} catch (err) {
|
|
1035
|
-
if (
|
|
1036
|
-
this.navigate(err as any)
|
|
1047
|
+
if (isRedirect(err)) {
|
|
1048
|
+
if (!opts?.preload) this.navigate(err as any)
|
|
1049
|
+
return
|
|
1037
1050
|
}
|
|
1038
1051
|
|
|
1039
1052
|
throw err
|
|
@@ -1158,6 +1171,8 @@ export class Router<
|
|
|
1158
1171
|
navigate = async <
|
|
1159
1172
|
TFrom extends RoutePaths<TRouteTree> = '/',
|
|
1160
1173
|
TTo extends string = '',
|
|
1174
|
+
TMaskFrom extends RoutePaths<TRouteTree> = TFrom,
|
|
1175
|
+
TMaskTo extends string = '',
|
|
1161
1176
|
>({
|
|
1162
1177
|
from,
|
|
1163
1178
|
to = '' as any,
|
|
@@ -1166,7 +1181,7 @@ export class Router<
|
|
|
1166
1181
|
replace,
|
|
1167
1182
|
params,
|
|
1168
1183
|
resetScroll,
|
|
1169
|
-
}: NavigateOptions<TRouteTree, TFrom, TTo>) => {
|
|
1184
|
+
}: NavigateOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo>) => {
|
|
1170
1185
|
// If this link simply reloads the current route,
|
|
1171
1186
|
// make sure it has a new key so it will trigger a data refresh
|
|
1172
1187
|
|
|
@@ -1186,21 +1201,22 @@ export class Router<
|
|
|
1186
1201
|
'Attempting to navigate to external url with this.navigate!',
|
|
1187
1202
|
)
|
|
1188
1203
|
|
|
1189
|
-
return this.#
|
|
1204
|
+
return this.#buildAndCommitLocation({
|
|
1190
1205
|
from: fromString,
|
|
1191
1206
|
to: toString,
|
|
1192
1207
|
search,
|
|
1193
1208
|
hash,
|
|
1194
|
-
replace,
|
|
1195
1209
|
params,
|
|
1210
|
+
replace,
|
|
1196
1211
|
resetScroll,
|
|
1197
1212
|
})
|
|
1198
1213
|
}
|
|
1199
1214
|
|
|
1200
1215
|
matchRoute = <
|
|
1216
|
+
TRouteTree extends AnyRoute = AnyRoute,
|
|
1201
1217
|
TFrom extends RoutePaths<TRouteTree> = '/',
|
|
1202
1218
|
TTo extends string = '',
|
|
1203
|
-
TResolved
|
|
1219
|
+
TResolved = ResolveRelativePath<TFrom, NoInfer<TTo>>,
|
|
1204
1220
|
>(
|
|
1205
1221
|
location: ToOptions<TRouteTree, TFrom, TTo>,
|
|
1206
1222
|
opts?: MatchRouteOptions,
|
|
@@ -1212,7 +1228,7 @@ export class Router<
|
|
|
1212
1228
|
: undefined,
|
|
1213
1229
|
} as any
|
|
1214
1230
|
|
|
1215
|
-
const next = this.
|
|
1231
|
+
const next = this.buildLocation(location)
|
|
1216
1232
|
if (opts?.pending && this.state.status !== 'pending') {
|
|
1217
1233
|
return false
|
|
1218
1234
|
}
|
|
@@ -1257,6 +1273,7 @@ export class Router<
|
|
|
1257
1273
|
preloadDelay: userPreloadDelay,
|
|
1258
1274
|
disabled,
|
|
1259
1275
|
state,
|
|
1276
|
+
mask,
|
|
1260
1277
|
resetScroll,
|
|
1261
1278
|
}: LinkOptions<TRouteTree, TFrom, TTo>): LinkInfo => {
|
|
1262
1279
|
// If this link simply reloads the current route,
|
|
@@ -1281,10 +1298,11 @@ export class Router<
|
|
|
1281
1298
|
hash,
|
|
1282
1299
|
replace,
|
|
1283
1300
|
state,
|
|
1301
|
+
mask,
|
|
1284
1302
|
resetScroll,
|
|
1285
1303
|
}
|
|
1286
1304
|
|
|
1287
|
-
const next = this.
|
|
1305
|
+
const next = this.buildLocation(nextOpts)
|
|
1288
1306
|
|
|
1289
1307
|
preload = preload ?? this.options.defaultPreload
|
|
1290
1308
|
const preloadDelay =
|
|
@@ -1323,7 +1341,7 @@ export class Router<
|
|
|
1323
1341
|
e.preventDefault()
|
|
1324
1342
|
|
|
1325
1343
|
// All is well? Navigate!
|
|
1326
|
-
this.#commitLocation(
|
|
1344
|
+
this.#commitLocation({ ...next, replace, resetScroll })
|
|
1327
1345
|
}
|
|
1328
1346
|
}
|
|
1329
1347
|
|
|
@@ -1603,154 +1621,269 @@ export class Router<
|
|
|
1603
1621
|
#parseLocation = (
|
|
1604
1622
|
previousLocation?: ParsedLocation,
|
|
1605
1623
|
): ParsedLocation<FullSearchSchema<TRouteTree>> => {
|
|
1606
|
-
|
|
1624
|
+
const parse = ({
|
|
1625
|
+
pathname,
|
|
1626
|
+
search,
|
|
1627
|
+
hash,
|
|
1628
|
+
state,
|
|
1629
|
+
}: HistoryLocation): ParsedLocation<FullSearchSchema<TRouteTree>> => {
|
|
1630
|
+
const parsedSearch = this.options.parseSearch(search)
|
|
1631
|
+
|
|
1632
|
+
return {
|
|
1633
|
+
pathname: pathname,
|
|
1634
|
+
searchStr: search,
|
|
1635
|
+
search: replaceEqualDeep(previousLocation?.search, parsedSearch) as any,
|
|
1636
|
+
hash: hash.split('#').reverse()[0] ?? '',
|
|
1637
|
+
href: `${pathname}${search}${hash}`,
|
|
1638
|
+
state: replaceEqualDeep(
|
|
1639
|
+
previousLocation?.state,
|
|
1640
|
+
state,
|
|
1641
|
+
) as LocationState,
|
|
1642
|
+
}
|
|
1643
|
+
}
|
|
1607
1644
|
|
|
1608
|
-
const
|
|
1645
|
+
const location = parse(this.history.location)
|
|
1609
1646
|
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1647
|
+
let { __tempLocation, __tempKey } = location.state
|
|
1648
|
+
|
|
1649
|
+
if (__tempLocation && (!__tempKey || __tempKey === this.tempLocationKey)) {
|
|
1650
|
+
// Sync up the location keys
|
|
1651
|
+
const parsedTempLocation = parse(__tempLocation) as any
|
|
1652
|
+
parsedTempLocation.state.key = location.state.key
|
|
1653
|
+
|
|
1654
|
+
delete parsedTempLocation.state.__tempLocation
|
|
1655
|
+
|
|
1656
|
+
return {
|
|
1657
|
+
...parsedTempLocation,
|
|
1658
|
+
maskedLocation: location,
|
|
1659
|
+
}
|
|
1618
1660
|
}
|
|
1661
|
+
|
|
1662
|
+
return location
|
|
1619
1663
|
}
|
|
1620
1664
|
|
|
1621
|
-
|
|
1622
|
-
|
|
1665
|
+
buildLocation = (opts: BuildNextOptions = {}): ParsedLocation => {
|
|
1666
|
+
const build = (
|
|
1667
|
+
dest: BuildNextOptions & {
|
|
1668
|
+
unmaskOnReload?: boolean
|
|
1669
|
+
} = {},
|
|
1670
|
+
matches?: AnyRouteMatch[],
|
|
1671
|
+
): ParsedLocation => {
|
|
1672
|
+
const from = this.state.location
|
|
1673
|
+
|
|
1674
|
+
const fromPathname = dest.from ?? from.pathname
|
|
1675
|
+
|
|
1676
|
+
let pathname = resolvePath(
|
|
1677
|
+
this.basepath ?? '/',
|
|
1678
|
+
fromPathname,
|
|
1679
|
+
`${dest.to ?? ''}`,
|
|
1680
|
+
)
|
|
1623
1681
|
|
|
1624
|
-
|
|
1625
|
-
? this.state.location.pathname
|
|
1626
|
-
: dest.from ?? this.state.location.pathname
|
|
1682
|
+
const fromMatches = this.matchRoutes(from.pathname, from.search)
|
|
1627
1683
|
|
|
1628
|
-
|
|
1629
|
-
this.basepath ?? '/',
|
|
1630
|
-
fromPathname,
|
|
1631
|
-
`${dest.to ?? ''}`,
|
|
1632
|
-
)
|
|
1684
|
+
const prevParams = { ...last(fromMatches)?.params }
|
|
1633
1685
|
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1686
|
+
let nextParams =
|
|
1687
|
+
(dest.params ?? true) === true
|
|
1688
|
+
? prevParams
|
|
1689
|
+
: functionalUpdate(dest.params!, prevParams)
|
|
1638
1690
|
|
|
1639
|
-
|
|
1691
|
+
if (nextParams) {
|
|
1692
|
+
matches
|
|
1693
|
+
?.map((d) => this.getRoute(d.routeId).options.stringifyParams)
|
|
1694
|
+
.filter(Boolean)
|
|
1695
|
+
.forEach((fn) => {
|
|
1696
|
+
nextParams = { ...nextParams!, ...fn!(nextParams!) }
|
|
1697
|
+
})
|
|
1698
|
+
}
|
|
1640
1699
|
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1700
|
+
pathname = interpolatePath(pathname, nextParams ?? {})
|
|
1701
|
+
|
|
1702
|
+
const preSearchFilters =
|
|
1703
|
+
matches
|
|
1704
|
+
?.map(
|
|
1705
|
+
(match) =>
|
|
1706
|
+
this.getRoute(match.routeId).options.preSearchFilters ?? [],
|
|
1707
|
+
)
|
|
1708
|
+
.flat()
|
|
1709
|
+
.filter(Boolean) ?? []
|
|
1710
|
+
|
|
1711
|
+
const postSearchFilters =
|
|
1712
|
+
matches
|
|
1713
|
+
?.map(
|
|
1714
|
+
(match) =>
|
|
1715
|
+
this.getRoute(match.routeId).options.postSearchFilters ?? [],
|
|
1716
|
+
)
|
|
1717
|
+
.flat()
|
|
1718
|
+
.filter(Boolean) ?? []
|
|
1719
|
+
|
|
1720
|
+
// Pre filters first
|
|
1721
|
+
const preFilteredSearch = preSearchFilters?.length
|
|
1722
|
+
? preSearchFilters?.reduce((prev, next) => next(prev), from.search)
|
|
1723
|
+
: from.search
|
|
1724
|
+
|
|
1725
|
+
// Then the link/navigate function
|
|
1726
|
+
const destSearch =
|
|
1727
|
+
dest.search === true
|
|
1728
|
+
? preFilteredSearch // Preserve resolvedFrom true
|
|
1729
|
+
: dest.search
|
|
1730
|
+
? functionalUpdate(dest.search, preFilteredSearch) ?? {} // Updater
|
|
1731
|
+
: preSearchFilters?.length
|
|
1732
|
+
? preFilteredSearch // Preserve resolvedFrom filters
|
|
1733
|
+
: {}
|
|
1734
|
+
|
|
1735
|
+
// Then post filters
|
|
1736
|
+
const postFilteredSearch = postSearchFilters?.length
|
|
1737
|
+
? postSearchFilters.reduce((prev, next) => next(prev), destSearch)
|
|
1738
|
+
: destSearch
|
|
1739
|
+
|
|
1740
|
+
const search = replaceEqualDeep(from.search, postFilteredSearch)
|
|
1741
|
+
|
|
1742
|
+
const searchStr = this.options.stringifySearch(search)
|
|
1743
|
+
|
|
1744
|
+
const hash =
|
|
1745
|
+
dest.hash === true
|
|
1746
|
+
? from.hash
|
|
1747
|
+
: dest.hash
|
|
1748
|
+
? functionalUpdate(dest.hash!, from.hash)
|
|
1749
|
+
: from.hash
|
|
1750
|
+
|
|
1751
|
+
const hashStr = hash ? `#${hash}` : ''
|
|
1752
|
+
|
|
1753
|
+
let nextState =
|
|
1754
|
+
dest.state === true
|
|
1755
|
+
? from.state
|
|
1756
|
+
: dest.state
|
|
1757
|
+
? functionalUpdate(dest.state, from.state)
|
|
1758
|
+
: from.state
|
|
1759
|
+
|
|
1760
|
+
nextState = replaceEqualDeep(from.state, nextState)
|
|
1645
1761
|
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
})
|
|
1762
|
+
return {
|
|
1763
|
+
pathname,
|
|
1764
|
+
search,
|
|
1765
|
+
searchStr,
|
|
1766
|
+
state: nextState,
|
|
1767
|
+
hash,
|
|
1768
|
+
href: this.history.createHref(`${pathname}${searchStr}${hashStr}`),
|
|
1769
|
+
unmaskOnReload: dest.unmaskOnReload,
|
|
1770
|
+
}
|
|
1653
1771
|
}
|
|
1654
1772
|
|
|
1655
|
-
|
|
1773
|
+
const buildWithMatches = (
|
|
1774
|
+
dest: BuildNextOptions = {},
|
|
1775
|
+
maskedDest?: BuildNextOptions,
|
|
1776
|
+
) => {
|
|
1777
|
+
let next = build(dest)
|
|
1778
|
+
let maskedNext = maskedDest ? build(maskedDest) : undefined
|
|
1779
|
+
|
|
1780
|
+
if (!maskedNext) {
|
|
1781
|
+
const foundMask = this.options.routeMasks?.find((d) => {
|
|
1782
|
+
const match = matchPathname(this.basepath, next.pathname, {
|
|
1783
|
+
to: d.from,
|
|
1784
|
+
fuzzy: false,
|
|
1785
|
+
})
|
|
1656
1786
|
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
(match) =>
|
|
1661
|
-
this.getRoute(match.routeId).options.preSearchFilters ?? [],
|
|
1662
|
-
)
|
|
1663
|
-
.flat()
|
|
1664
|
-
.filter(Boolean) ?? []
|
|
1665
|
-
|
|
1666
|
-
const postSearchFilters =
|
|
1667
|
-
dest.__matches
|
|
1668
|
-
?.map(
|
|
1669
|
-
(match) =>
|
|
1670
|
-
this.getRoute(match.routeId).options.postSearchFilters ?? [],
|
|
1671
|
-
)
|
|
1672
|
-
.flat()
|
|
1673
|
-
.filter(Boolean) ?? []
|
|
1787
|
+
if (match) {
|
|
1788
|
+
return match
|
|
1789
|
+
}
|
|
1674
1790
|
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
? preSearchFilters?.reduce(
|
|
1678
|
-
(prev, next) => next(prev),
|
|
1679
|
-
this.state.location.search,
|
|
1680
|
-
)
|
|
1681
|
-
: this.state.location.search
|
|
1682
|
-
|
|
1683
|
-
// Then the link/navigate function
|
|
1684
|
-
const destSearch =
|
|
1685
|
-
dest.search === true
|
|
1686
|
-
? preFilteredSearch // Preserve resolvedFrom true
|
|
1687
|
-
: dest.search
|
|
1688
|
-
? functionalUpdate(dest.search, preFilteredSearch) ?? {} // Updater
|
|
1689
|
-
: preSearchFilters?.length
|
|
1690
|
-
? preFilteredSearch // Preserve resolvedFrom filters
|
|
1691
|
-
: {}
|
|
1692
|
-
|
|
1693
|
-
// Then post filters
|
|
1694
|
-
const postFilteredSearch = postSearchFilters?.length
|
|
1695
|
-
? postSearchFilters.reduce((prev, next) => next(prev), destSearch)
|
|
1696
|
-
: destSearch
|
|
1697
|
-
|
|
1698
|
-
const search = replaceEqualDeep(
|
|
1699
|
-
this.state.location.search,
|
|
1700
|
-
postFilteredSearch,
|
|
1701
|
-
)
|
|
1791
|
+
return false
|
|
1792
|
+
})
|
|
1702
1793
|
|
|
1703
|
-
|
|
1794
|
+
if (foundMask) {
|
|
1795
|
+
maskedDest = foundMask
|
|
1796
|
+
maskedNext = build(maskedDest)
|
|
1797
|
+
}
|
|
1798
|
+
}
|
|
1704
1799
|
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
? this.
|
|
1708
|
-
:
|
|
1800
|
+
const nextMatches = this.matchRoutes(next.pathname, next.search)
|
|
1801
|
+
const maskedMatches = maskedNext
|
|
1802
|
+
? this.matchRoutes(maskedNext.pathname, maskedNext.search)
|
|
1803
|
+
: undefined
|
|
1804
|
+
const maskedFinal = maskedNext
|
|
1805
|
+
? build(maskedDest, maskedMatches)
|
|
1806
|
+
: undefined
|
|
1709
1807
|
|
|
1710
|
-
|
|
1808
|
+
const final = build(dest, nextMatches)
|
|
1711
1809
|
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
: functionalUpdate(dest.state, this.state.location.state)!
|
|
1810
|
+
if (maskedFinal) {
|
|
1811
|
+
final.maskedLocation = maskedFinal
|
|
1812
|
+
}
|
|
1716
1813
|
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1814
|
+
return final
|
|
1815
|
+
}
|
|
1816
|
+
|
|
1817
|
+
if (opts.mask) {
|
|
1818
|
+
return buildWithMatches(opts, {
|
|
1819
|
+
...pick(opts, ['from']),
|
|
1820
|
+
...opts.mask,
|
|
1821
|
+
})
|
|
1725
1822
|
}
|
|
1823
|
+
|
|
1824
|
+
return buildWithMatches(opts)
|
|
1726
1825
|
}
|
|
1727
1826
|
|
|
1728
|
-
#
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1827
|
+
#buildAndCommitLocation = ({
|
|
1828
|
+
replace,
|
|
1829
|
+
resetScroll,
|
|
1830
|
+
...rest
|
|
1831
|
+
}: BuildNextOptions & CommitLocationOptions = {}) => {
|
|
1832
|
+
const location = this.buildLocation(rest)
|
|
1833
|
+
return this.#commitLocation({
|
|
1834
|
+
...location,
|
|
1835
|
+
replace,
|
|
1836
|
+
resetScroll,
|
|
1837
|
+
})
|
|
1838
|
+
}
|
|
1732
1839
|
|
|
1840
|
+
#commitLocation = async (next: ParsedLocation & CommitLocationOptions) => {
|
|
1733
1841
|
if (this.navigateTimeout) clearTimeout(this.navigateTimeout)
|
|
1734
1842
|
|
|
1735
1843
|
let nextAction: 'push' | 'replace' = 'replace'
|
|
1736
1844
|
|
|
1737
|
-
if (!
|
|
1845
|
+
if (!next.replace) {
|
|
1738
1846
|
nextAction = 'push'
|
|
1739
1847
|
}
|
|
1740
1848
|
|
|
1741
1849
|
const isSameUrl = this.state.location.href === next.href
|
|
1742
1850
|
|
|
1743
|
-
if (isSameUrl
|
|
1851
|
+
if (isSameUrl) {
|
|
1744
1852
|
nextAction = 'replace'
|
|
1745
1853
|
}
|
|
1746
1854
|
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1855
|
+
let { maskedLocation, ...nextHistory } = next
|
|
1856
|
+
|
|
1857
|
+
if (maskedLocation) {
|
|
1858
|
+
nextHistory = {
|
|
1859
|
+
...maskedLocation,
|
|
1860
|
+
state: {
|
|
1861
|
+
...maskedLocation.state,
|
|
1862
|
+
__tempKey: undefined,
|
|
1863
|
+
__tempLocation: {
|
|
1864
|
+
...nextHistory,
|
|
1865
|
+
search: nextHistory.searchStr,
|
|
1866
|
+
state: {
|
|
1867
|
+
...nextHistory.state,
|
|
1868
|
+
__tempKey: undefined!,
|
|
1869
|
+
__tempLocation: undefined!,
|
|
1870
|
+
key: undefined!,
|
|
1871
|
+
},
|
|
1872
|
+
},
|
|
1873
|
+
},
|
|
1874
|
+
}
|
|
1875
|
+
|
|
1876
|
+
if (nextHistory.unmaskOnReload ?? this.options.unmaskOnReload ?? false) {
|
|
1877
|
+
nextHistory.state.__tempKey = this.tempLocationKey
|
|
1878
|
+
}
|
|
1879
|
+
}
|
|
1750
1880
|
|
|
1751
|
-
this.history[nextAction === 'push' ? 'push' : 'replace'](
|
|
1881
|
+
this.history[nextAction === 'push' ? 'push' : 'replace'](
|
|
1882
|
+
nextHistory.href,
|
|
1883
|
+
nextHistory.state,
|
|
1884
|
+
)
|
|
1752
1885
|
|
|
1753
|
-
this.resetNextScroll =
|
|
1886
|
+
this.resetNextScroll = next.resetScroll ?? true
|
|
1754
1887
|
|
|
1755
1888
|
return this.latestLoadPromise
|
|
1756
1889
|
}
|
|
@@ -1817,37 +1950,46 @@ export class Router<
|
|
|
1817
1950
|
invalidate = async (opts?: {
|
|
1818
1951
|
matchId?: string
|
|
1819
1952
|
reload?: boolean
|
|
1953
|
+
__fromFocus?: boolean
|
|
1820
1954
|
}): Promise<void> => {
|
|
1821
1955
|
if (opts?.matchId) {
|
|
1822
1956
|
this.setRouteMatch(opts.matchId, (s) => ({
|
|
1823
1957
|
...s,
|
|
1824
1958
|
invalid: true,
|
|
1825
1959
|
}))
|
|
1960
|
+
|
|
1826
1961
|
const matchIndex = this.state.matches.findIndex(
|
|
1827
1962
|
(d) => d.id === opts.matchId,
|
|
1828
1963
|
)
|
|
1829
1964
|
const childMatch = this.state.matches[matchIndex + 1]
|
|
1830
1965
|
|
|
1831
1966
|
if (childMatch) {
|
|
1832
|
-
return this.invalidate({
|
|
1967
|
+
return this.invalidate({
|
|
1968
|
+
matchId: childMatch.id,
|
|
1969
|
+
reload: false,
|
|
1970
|
+
__fromFocus: opts.__fromFocus,
|
|
1971
|
+
})
|
|
1833
1972
|
}
|
|
1834
1973
|
} else {
|
|
1835
1974
|
this.__store.batch(() => {
|
|
1836
1975
|
Object.values(this.state.matchesById).forEach((match) => {
|
|
1837
|
-
this.
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1976
|
+
const route = this.getRoute(match.routeId)
|
|
1977
|
+
const shouldInvalidate = opts?.__fromFocus
|
|
1978
|
+
? route.options.reloadOnWindowFocus ?? true
|
|
1979
|
+
: true
|
|
1980
|
+
|
|
1981
|
+
if (shouldInvalidate) {
|
|
1982
|
+
this.setRouteMatch(match.id, (s) => ({
|
|
1983
|
+
...s,
|
|
1984
|
+
invalid: true,
|
|
1985
|
+
}))
|
|
1986
|
+
}
|
|
1841
1987
|
})
|
|
1842
1988
|
})
|
|
1843
1989
|
}
|
|
1844
1990
|
|
|
1845
1991
|
if (opts?.reload ?? true) {
|
|
1846
|
-
return this.
|
|
1847
|
-
fromCurrent: true,
|
|
1848
|
-
replace: true,
|
|
1849
|
-
search: true,
|
|
1850
|
-
} as any)
|
|
1992
|
+
return this.load()
|
|
1851
1993
|
}
|
|
1852
1994
|
}
|
|
1853
1995
|
}
|
|
@@ -1882,7 +2024,9 @@ export type Redirect<
|
|
|
1882
2024
|
TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
|
|
1883
2025
|
TFrom extends RoutePaths<TRouteTree> = '/',
|
|
1884
2026
|
TTo extends string = '',
|
|
1885
|
-
|
|
2027
|
+
TMaskFrom extends RoutePaths<TRouteTree> = TFrom,
|
|
2028
|
+
TMaskTo extends string = '',
|
|
2029
|
+
> = NavigateOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo> & {
|
|
1886
2030
|
code?: number
|
|
1887
2031
|
}
|
|
1888
2032
|
|