@tanstack/router-core 1.121.0-alpha.1 → 1.121.0-alpha.11
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/index.d.cts +1 -1
- package/dist/cjs/link.cjs.map +1 -1
- package/dist/cjs/link.d.cts +1 -1
- package/dist/cjs/redirect.cjs +7 -0
- package/dist/cjs/redirect.cjs.map +1 -1
- package/dist/cjs/route.cjs.map +1 -1
- package/dist/cjs/route.d.cts +5 -5
- package/dist/cjs/router.cjs +172 -188
- package/dist/cjs/router.cjs.map +1 -1
- package/dist/cjs/router.d.cts +2 -5
- package/dist/cjs/utils.cjs.map +1 -1
- package/dist/cjs/utils.d.cts +1 -0
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/link.d.ts +1 -1
- package/dist/esm/link.js.map +1 -1
- package/dist/esm/redirect.js +7 -0
- package/dist/esm/redirect.js.map +1 -1
- package/dist/esm/route.d.ts +5 -5
- package/dist/esm/route.js.map +1 -1
- package/dist/esm/router.d.ts +2 -5
- package/dist/esm/router.js +172 -188
- package/dist/esm/router.js.map +1 -1
- package/dist/esm/utils.d.ts +1 -0
- package/dist/esm/utils.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +0 -1
- package/src/link.ts +1 -1
- package/src/redirect.ts +8 -0
- package/src/route.ts +5 -4
- package/src/router.ts +245 -233
- package/src/utils.ts +1 -0
package/src/router.ts
CHANGED
|
@@ -433,8 +433,9 @@ export interface BuildNextOptions {
|
|
|
433
433
|
unmaskOnReload?: boolean
|
|
434
434
|
}
|
|
435
435
|
from?: string
|
|
436
|
-
_fromLocation?: ParsedLocation
|
|
437
436
|
href?: string
|
|
437
|
+
_fromLocation?: ParsedLocation
|
|
438
|
+
unsafeRelative?: 'path'
|
|
438
439
|
}
|
|
439
440
|
|
|
440
441
|
type NavigationEventInfo = {
|
|
@@ -521,11 +522,6 @@ export interface RouterErrorSerializer<TSerializedError> {
|
|
|
521
522
|
deserialize: (err: TSerializedError) => unknown
|
|
522
523
|
}
|
|
523
524
|
|
|
524
|
-
export interface MatchedRoutesResult {
|
|
525
|
-
matchedRoutes: Array<AnyRoute>
|
|
526
|
-
routeParams: Record<string, string>
|
|
527
|
-
}
|
|
528
|
-
|
|
529
525
|
export type PreloadRouteFn<
|
|
530
526
|
TRouteTree extends AnyRoute,
|
|
531
527
|
TTrailingSlashOption extends TrailingSlashOption,
|
|
@@ -1071,9 +1067,9 @@ export class RouterCore<
|
|
|
1071
1067
|
} as ParsedLocation,
|
|
1072
1068
|
opts,
|
|
1073
1069
|
)
|
|
1074
|
-
} else {
|
|
1075
|
-
return this.matchRoutesInternal(pathnameOrNext, locationSearchOrOpts)
|
|
1076
1070
|
}
|
|
1071
|
+
|
|
1072
|
+
return this.matchRoutesInternal(pathnameOrNext, locationSearchOrOpts)
|
|
1077
1073
|
}
|
|
1078
1074
|
|
|
1079
1075
|
private matchRoutesInternal(
|
|
@@ -1334,7 +1330,8 @@ export class RouterCore<
|
|
|
1334
1330
|
const route = this.looseRoutesById[match.routeId]!
|
|
1335
1331
|
const existingMatch = this.getMatch(match.id)
|
|
1336
1332
|
|
|
1337
|
-
// only execute `context` if we are not
|
|
1333
|
+
// only execute `context` if we are not calling from router.buildLocation
|
|
1334
|
+
|
|
1338
1335
|
if (!existingMatch && opts?._buildLocation !== true) {
|
|
1339
1336
|
const parentMatch = matches[index - 1]
|
|
1340
1337
|
const parentContext = getParentContext(parentMatch)
|
|
@@ -1403,75 +1400,67 @@ export class RouterCore<
|
|
|
1403
1400
|
dest: BuildNextOptions & {
|
|
1404
1401
|
unmaskOnReload?: boolean
|
|
1405
1402
|
} = {},
|
|
1406
|
-
matchedRoutesResult?: MatchedRoutesResult,
|
|
1407
1403
|
): ParsedLocation => {
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
: this.state.matches
|
|
1411
|
-
|
|
1412
|
-
const fromMatch =
|
|
1413
|
-
dest.from != null
|
|
1414
|
-
? fromMatches.find((d) =>
|
|
1415
|
-
matchPathname(this.basepath, trimPathRight(d.pathname), {
|
|
1416
|
-
to: dest.from,
|
|
1417
|
-
caseSensitive: false,
|
|
1418
|
-
fuzzy: false,
|
|
1419
|
-
}),
|
|
1420
|
-
)
|
|
1421
|
-
: undefined
|
|
1422
|
-
|
|
1423
|
-
const fromPath = fromMatch?.pathname || this.latestLocation.pathname
|
|
1404
|
+
// We allow the caller to override the current location
|
|
1405
|
+
const currentLocation = dest._fromLocation || this.latestLocation
|
|
1424
1406
|
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
)
|
|
1407
|
+
const allFromMatches = this.matchRoutes(currentLocation, {
|
|
1408
|
+
_buildLocation: true,
|
|
1409
|
+
})
|
|
1429
1410
|
|
|
1430
|
-
const
|
|
1431
|
-
|
|
1432
|
-
|
|
1411
|
+
const lastMatch = last(allFromMatches)!
|
|
1412
|
+
|
|
1413
|
+
// First let's find the starting pathname
|
|
1414
|
+
// By default, start with the current location
|
|
1415
|
+
let fromPath = lastMatch.fullPath
|
|
1416
|
+
|
|
1417
|
+
// If there is a to, it means we are changing the path in some way
|
|
1418
|
+
// So we need to find the relative fromPath
|
|
1419
|
+
if (dest.unsafeRelative === 'path') {
|
|
1420
|
+
fromPath = currentLocation.pathname
|
|
1421
|
+
} else if (dest.to && dest.from) {
|
|
1422
|
+
fromPath = dest.from
|
|
1423
|
+
const existingFrom = [...allFromMatches].reverse().find((d) => {
|
|
1424
|
+
return (
|
|
1425
|
+
d.fullPath === fromPath || d.fullPath === joinPaths([fromPath, '/'])
|
|
1426
|
+
)
|
|
1427
|
+
})
|
|
1433
1428
|
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
let pathname: string
|
|
1438
|
-
if (dest.to) {
|
|
1439
|
-
const resolvePathTo =
|
|
1440
|
-
fromMatch?.fullPath ||
|
|
1441
|
-
last(fromMatches)?.fullPath ||
|
|
1442
|
-
this.latestLocation.pathname
|
|
1443
|
-
pathname = this.resolvePathWithBase(resolvePathTo, `${dest.to}`)
|
|
1444
|
-
} else {
|
|
1445
|
-
const fromRouteByFromPathRouteId =
|
|
1446
|
-
this.routesById[
|
|
1447
|
-
stayingMatches?.find((route) => {
|
|
1448
|
-
const interpolatedPath = interpolatePath({
|
|
1449
|
-
path: route.fullPath,
|
|
1450
|
-
params: matchedRoutesResult?.routeParams ?? {},
|
|
1451
|
-
decodeCharMap: this.pathParamsDecodeCharMap,
|
|
1452
|
-
}).interpolatedPath
|
|
1453
|
-
const pathname = joinPaths([this.basepath, interpolatedPath])
|
|
1454
|
-
return pathname === fromPath
|
|
1455
|
-
})?.id as keyof this['routesById']
|
|
1456
|
-
]
|
|
1457
|
-
pathname = this.resolvePathWithBase(
|
|
1458
|
-
fromPath,
|
|
1459
|
-
fromRouteByFromPathRouteId?.to ?? fromPath,
|
|
1460
|
-
)
|
|
1429
|
+
if (!existingFrom) {
|
|
1430
|
+
console.warn(`Could not find match for from: ${dest.from}`)
|
|
1431
|
+
}
|
|
1461
1432
|
}
|
|
1462
1433
|
|
|
1463
|
-
|
|
1434
|
+
// From search should always use the current location
|
|
1435
|
+
const fromSearch = lastMatch.search
|
|
1436
|
+
// Same with params. It can't hurt to provide as many as possible
|
|
1437
|
+
const fromParams = { ...lastMatch.params }
|
|
1464
1438
|
|
|
1439
|
+
// Resolve the next to
|
|
1440
|
+
const nextTo = dest.to
|
|
1441
|
+
? this.resolvePathWithBase(fromPath, `${dest.to}`)
|
|
1442
|
+
: fromPath
|
|
1443
|
+
|
|
1444
|
+
// Resolve the next params
|
|
1465
1445
|
let nextParams =
|
|
1466
1446
|
(dest.params ?? true) === true
|
|
1467
|
-
?
|
|
1447
|
+
? fromParams
|
|
1468
1448
|
: {
|
|
1469
|
-
...
|
|
1470
|
-
...functionalUpdate(dest.params as any,
|
|
1449
|
+
...fromParams,
|
|
1450
|
+
...functionalUpdate(dest.params as any, fromParams),
|
|
1471
1451
|
}
|
|
1472
1452
|
|
|
1453
|
+
const destRoutes = this.matchRoutes(
|
|
1454
|
+
nextTo,
|
|
1455
|
+
{},
|
|
1456
|
+
{
|
|
1457
|
+
_buildLocation: true,
|
|
1458
|
+
},
|
|
1459
|
+
).map((d) => this.looseRoutesById[d.routeId]!)
|
|
1460
|
+
|
|
1461
|
+
// If there are any params, we need to stringify them
|
|
1473
1462
|
if (Object.keys(nextParams).length > 0) {
|
|
1474
|
-
|
|
1463
|
+
destRoutes
|
|
1475
1464
|
.map((route) => {
|
|
1476
1465
|
return (
|
|
1477
1466
|
route.options.params?.stringify ?? route.options.stringifyParams
|
|
@@ -1483,25 +1472,27 @@ export class RouterCore<
|
|
|
1483
1472
|
})
|
|
1484
1473
|
}
|
|
1485
1474
|
|
|
1486
|
-
|
|
1487
|
-
|
|
1475
|
+
// Interpolate the next to into the next pathname
|
|
1476
|
+
const nextPathname = interpolatePath({
|
|
1477
|
+
path: nextTo,
|
|
1488
1478
|
params: nextParams ?? {},
|
|
1489
1479
|
leaveWildcards: false,
|
|
1490
1480
|
leaveParams: opts.leaveParams,
|
|
1491
1481
|
decodeCharMap: this.pathParamsDecodeCharMap,
|
|
1492
1482
|
}).interpolatedPath
|
|
1493
1483
|
|
|
1494
|
-
|
|
1484
|
+
// Resolve the next search
|
|
1485
|
+
let nextSearch = fromSearch
|
|
1495
1486
|
if (opts._includeValidateSearch && this.options.search?.strict) {
|
|
1496
1487
|
let validatedSearch = {}
|
|
1497
|
-
|
|
1488
|
+
destRoutes.forEach((route) => {
|
|
1498
1489
|
try {
|
|
1499
1490
|
if (route.options.validateSearch) {
|
|
1500
1491
|
validatedSearch = {
|
|
1501
1492
|
...validatedSearch,
|
|
1502
1493
|
...(validateSearch(route.options.validateSearch, {
|
|
1503
1494
|
...validatedSearch,
|
|
1504
|
-
...
|
|
1495
|
+
...nextSearch,
|
|
1505
1496
|
}) ?? {}),
|
|
1506
1497
|
}
|
|
1507
1498
|
}
|
|
@@ -1509,137 +1500,52 @@ export class RouterCore<
|
|
|
1509
1500
|
// ignore errors here because they are already handled in matchRoutes
|
|
1510
1501
|
}
|
|
1511
1502
|
})
|
|
1512
|
-
|
|
1503
|
+
nextSearch = validatedSearch
|
|
1513
1504
|
}
|
|
1514
1505
|
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
if (route.options.search?.middlewares) {
|
|
1522
|
-
middlewares.push(...route.options.search.middlewares)
|
|
1523
|
-
}
|
|
1524
|
-
}
|
|
1525
|
-
// TODO remove preSearchFilters and postSearchFilters in v2
|
|
1526
|
-
else if (
|
|
1527
|
-
route.options.preSearchFilters ||
|
|
1528
|
-
route.options.postSearchFilters
|
|
1529
|
-
) {
|
|
1530
|
-
const legacyMiddleware: SearchMiddleware<any> = ({
|
|
1531
|
-
search,
|
|
1532
|
-
next,
|
|
1533
|
-
}) => {
|
|
1534
|
-
let nextSearch = search
|
|
1535
|
-
if (
|
|
1536
|
-
'preSearchFilters' in route.options &&
|
|
1537
|
-
route.options.preSearchFilters
|
|
1538
|
-
) {
|
|
1539
|
-
nextSearch = route.options.preSearchFilters.reduce(
|
|
1540
|
-
(prev, next) => next(prev),
|
|
1541
|
-
search,
|
|
1542
|
-
)
|
|
1543
|
-
}
|
|
1544
|
-
const result = next(nextSearch)
|
|
1545
|
-
if (
|
|
1546
|
-
'postSearchFilters' in route.options &&
|
|
1547
|
-
route.options.postSearchFilters
|
|
1548
|
-
) {
|
|
1549
|
-
return route.options.postSearchFilters.reduce(
|
|
1550
|
-
(prev, next) => next(prev),
|
|
1551
|
-
result,
|
|
1552
|
-
)
|
|
1553
|
-
}
|
|
1554
|
-
return result
|
|
1555
|
-
}
|
|
1556
|
-
middlewares.push(legacyMiddleware)
|
|
1557
|
-
}
|
|
1558
|
-
if (opts._includeValidateSearch && route.options.validateSearch) {
|
|
1559
|
-
const validate: SearchMiddleware<any> = ({ search, next }) => {
|
|
1560
|
-
const result = next(search)
|
|
1561
|
-
try {
|
|
1562
|
-
const validatedSearch = {
|
|
1563
|
-
...result,
|
|
1564
|
-
...(validateSearch(
|
|
1565
|
-
route.options.validateSearch,
|
|
1566
|
-
result,
|
|
1567
|
-
) ?? {}),
|
|
1568
|
-
}
|
|
1569
|
-
return validatedSearch
|
|
1570
|
-
} catch {
|
|
1571
|
-
// ignore errors here because they are already handled in matchRoutes
|
|
1572
|
-
return result
|
|
1573
|
-
}
|
|
1574
|
-
}
|
|
1575
|
-
middlewares.push(validate)
|
|
1576
|
-
}
|
|
1577
|
-
return acc.concat(middlewares)
|
|
1578
|
-
},
|
|
1579
|
-
[] as Array<SearchMiddleware<any>>,
|
|
1580
|
-
) ?? []
|
|
1581
|
-
|
|
1582
|
-
// the chain ends here since `next` is not called
|
|
1583
|
-
const final: SearchMiddleware<any> = ({ search }) => {
|
|
1584
|
-
if (!dest.search) {
|
|
1585
|
-
return {}
|
|
1586
|
-
}
|
|
1587
|
-
if (dest.search === true) {
|
|
1588
|
-
return search
|
|
1589
|
-
}
|
|
1590
|
-
return functionalUpdate(dest.search, search)
|
|
1591
|
-
}
|
|
1592
|
-
allMiddlewares.push(final)
|
|
1593
|
-
|
|
1594
|
-
const applyNext = (index: number, currentSearch: any): any => {
|
|
1595
|
-
// no more middlewares left, return the current search
|
|
1596
|
-
if (index >= allMiddlewares.length) {
|
|
1597
|
-
return currentSearch
|
|
1598
|
-
}
|
|
1599
|
-
|
|
1600
|
-
const middleware = allMiddlewares[index]!
|
|
1601
|
-
|
|
1602
|
-
const next = (newSearch: any): any => {
|
|
1603
|
-
return applyNext(index + 1, newSearch)
|
|
1604
|
-
}
|
|
1605
|
-
|
|
1606
|
-
return middleware({ search: currentSearch, next })
|
|
1607
|
-
}
|
|
1608
|
-
|
|
1609
|
-
// Start applying middlewares
|
|
1610
|
-
return applyNext(0, search)
|
|
1611
|
-
}
|
|
1506
|
+
nextSearch = applySearchMiddleware({
|
|
1507
|
+
search: nextSearch,
|
|
1508
|
+
dest,
|
|
1509
|
+
destRoutes,
|
|
1510
|
+
_includeValidateSearch: opts._includeValidateSearch,
|
|
1511
|
+
})
|
|
1612
1512
|
|
|
1613
|
-
|
|
1513
|
+
// Replace the equal deep
|
|
1514
|
+
nextSearch = replaceEqualDeep(fromSearch, nextSearch)
|
|
1614
1515
|
|
|
1615
|
-
|
|
1616
|
-
const searchStr = this.options.stringifySearch(
|
|
1516
|
+
// Stringify the next search
|
|
1517
|
+
const searchStr = this.options.stringifySearch(nextSearch)
|
|
1617
1518
|
|
|
1519
|
+
// Resolve the next hash
|
|
1618
1520
|
const hash =
|
|
1619
1521
|
dest.hash === true
|
|
1620
|
-
?
|
|
1522
|
+
? currentLocation.hash
|
|
1621
1523
|
: dest.hash
|
|
1622
|
-
? functionalUpdate(dest.hash,
|
|
1524
|
+
? functionalUpdate(dest.hash, currentLocation.hash)
|
|
1623
1525
|
: undefined
|
|
1624
1526
|
|
|
1527
|
+
// Resolve the next hash string
|
|
1625
1528
|
const hashStr = hash ? `#${hash}` : ''
|
|
1626
1529
|
|
|
1530
|
+
// Resolve the next state
|
|
1627
1531
|
let nextState =
|
|
1628
1532
|
dest.state === true
|
|
1629
|
-
?
|
|
1533
|
+
? currentLocation.state
|
|
1630
1534
|
: dest.state
|
|
1631
|
-
? functionalUpdate(dest.state,
|
|
1535
|
+
? functionalUpdate(dest.state, currentLocation.state)
|
|
1632
1536
|
: {}
|
|
1633
1537
|
|
|
1634
|
-
|
|
1538
|
+
// Replace the equal deep
|
|
1539
|
+
nextState = replaceEqualDeep(currentLocation.state, nextState)
|
|
1635
1540
|
|
|
1541
|
+
// Return the next location
|
|
1636
1542
|
return {
|
|
1637
|
-
pathname,
|
|
1638
|
-
search,
|
|
1543
|
+
pathname: nextPathname,
|
|
1544
|
+
search: nextSearch,
|
|
1639
1545
|
searchStr,
|
|
1640
1546
|
state: nextState as any,
|
|
1641
1547
|
hash: hash ?? '',
|
|
1642
|
-
href: `${
|
|
1548
|
+
href: `${nextPathname}${searchStr}${hashStr}`,
|
|
1643
1549
|
unmaskOnReload: dest.unmaskOnReload,
|
|
1644
1550
|
}
|
|
1645
1551
|
}
|
|
@@ -1649,6 +1555,7 @@ export class RouterCore<
|
|
|
1649
1555
|
maskedDest?: BuildNextOptions,
|
|
1650
1556
|
) => {
|
|
1651
1557
|
const next = build(dest)
|
|
1558
|
+
|
|
1652
1559
|
let maskedNext = maskedDest ? build(maskedDest) : undefined
|
|
1653
1560
|
|
|
1654
1561
|
if (!maskedNext) {
|
|
@@ -1680,22 +1587,12 @@ export class RouterCore<
|
|
|
1680
1587
|
}
|
|
1681
1588
|
}
|
|
1682
1589
|
|
|
1683
|
-
const nextMatches = this.getMatchedRoutes(
|
|
1684
|
-
next.pathname,
|
|
1685
|
-
dest.to as string,
|
|
1686
|
-
)
|
|
1687
|
-
const final = build(dest, nextMatches)
|
|
1688
|
-
|
|
1689
1590
|
if (maskedNext) {
|
|
1690
|
-
const
|
|
1691
|
-
|
|
1692
|
-
maskedDest?.to as string,
|
|
1693
|
-
)
|
|
1694
|
-
const maskedFinal = build(maskedDest, maskedMatches)
|
|
1695
|
-
final.maskedLocation = maskedFinal
|
|
1591
|
+
const maskedFinal = build(maskedDest)
|
|
1592
|
+
next.maskedLocation = maskedFinal
|
|
1696
1593
|
}
|
|
1697
1594
|
|
|
1698
|
-
return
|
|
1595
|
+
return next
|
|
1699
1596
|
}
|
|
1700
1597
|
|
|
1701
1598
|
if (opts.mask) {
|
|
@@ -2488,7 +2385,7 @@ export class RouterCore<
|
|
|
2488
2385
|
!this.state.matches.find((d) => d.id === matchId),
|
|
2489
2386
|
}))
|
|
2490
2387
|
|
|
2491
|
-
const executeHead = () => {
|
|
2388
|
+
const executeHead = async () => {
|
|
2492
2389
|
const match = this.getMatch(matchId)
|
|
2493
2390
|
// in case of a redirecting match during preload, the match does not exist
|
|
2494
2391
|
if (!match) {
|
|
@@ -2500,21 +2397,17 @@ export class RouterCore<
|
|
|
2500
2397
|
params: match.params,
|
|
2501
2398
|
loaderData: match.loaderData,
|
|
2502
2399
|
}
|
|
2503
|
-
const headFnContent =
|
|
2400
|
+
const headFnContent =
|
|
2401
|
+
await route.options.head?.(assetContext)
|
|
2504
2402
|
const meta = headFnContent?.meta
|
|
2505
2403
|
const links = headFnContent?.links
|
|
2506
2404
|
const headScripts = headFnContent?.scripts
|
|
2507
2405
|
|
|
2508
|
-
const scripts =
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
links,
|
|
2514
|
-
headScripts,
|
|
2515
|
-
headers,
|
|
2516
|
-
scripts,
|
|
2517
|
-
}))
|
|
2406
|
+
const scripts =
|
|
2407
|
+
await route.options.scripts?.(assetContext)
|
|
2408
|
+
const headers =
|
|
2409
|
+
await route.options.headers?.(assetContext)
|
|
2410
|
+
return { meta, links, headScripts, headers, scripts }
|
|
2518
2411
|
}
|
|
2519
2412
|
|
|
2520
2413
|
const runLoader = async () => {
|
|
@@ -2561,17 +2454,19 @@ export class RouterCore<
|
|
|
2561
2454
|
// to be preloaded before we resolve the match
|
|
2562
2455
|
await route._componentsPromise
|
|
2563
2456
|
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2457
|
+
updateMatch(matchId, (prev) => ({
|
|
2458
|
+
...prev,
|
|
2459
|
+
error: undefined,
|
|
2460
|
+
status: 'success',
|
|
2461
|
+
isFetching: false,
|
|
2462
|
+
updatedAt: Date.now(),
|
|
2463
|
+
loaderData,
|
|
2464
|
+
}))
|
|
2465
|
+
const head = await executeHead()
|
|
2466
|
+
updateMatch(matchId, (prev) => ({
|
|
2467
|
+
...prev,
|
|
2468
|
+
...head,
|
|
2469
|
+
}))
|
|
2575
2470
|
} catch (e) {
|
|
2576
2471
|
let error = e
|
|
2577
2472
|
|
|
@@ -2588,16 +2483,14 @@ export class RouterCore<
|
|
|
2588
2483
|
onErrorError,
|
|
2589
2484
|
)
|
|
2590
2485
|
}
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
executeHead()
|
|
2600
|
-
})
|
|
2486
|
+
const head = await executeHead()
|
|
2487
|
+
updateMatch(matchId, (prev) => ({
|
|
2488
|
+
...prev,
|
|
2489
|
+
error,
|
|
2490
|
+
status: 'error',
|
|
2491
|
+
isFetching: false,
|
|
2492
|
+
...head,
|
|
2493
|
+
}))
|
|
2601
2494
|
}
|
|
2602
2495
|
|
|
2603
2496
|
this.serverSsr?.onMatchSettled({
|
|
@@ -2605,13 +2498,13 @@ export class RouterCore<
|
|
|
2605
2498
|
match: this.getMatch(matchId)!,
|
|
2606
2499
|
})
|
|
2607
2500
|
} catch (err) {
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
})
|
|
2501
|
+
const head = await executeHead()
|
|
2502
|
+
|
|
2503
|
+
updateMatch(matchId, (prev) => ({
|
|
2504
|
+
...prev,
|
|
2505
|
+
loaderPromise: undefined,
|
|
2506
|
+
...head,
|
|
2507
|
+
}))
|
|
2615
2508
|
handleRedirectAndNotFound(this.getMatch(matchId)!, err)
|
|
2616
2509
|
}
|
|
2617
2510
|
}
|
|
@@ -2651,7 +2544,11 @@ export class RouterCore<
|
|
|
2651
2544
|
// if the loader did not run, still update head.
|
|
2652
2545
|
// reason: parent's beforeLoad may have changed the route context
|
|
2653
2546
|
// and only now do we know the route context (and that the loader would not run)
|
|
2654
|
-
executeHead()
|
|
2547
|
+
const head = await executeHead()
|
|
2548
|
+
updateMatch(matchId, (prev) => ({
|
|
2549
|
+
...prev,
|
|
2550
|
+
...head,
|
|
2551
|
+
}))
|
|
2655
2552
|
}
|
|
2656
2553
|
}
|
|
2657
2554
|
if (!loaderIsRunningAsync) {
|
|
@@ -2873,6 +2770,7 @@ export class RouterCore<
|
|
|
2873
2770
|
if (err.options.reloadDocument) {
|
|
2874
2771
|
return undefined
|
|
2875
2772
|
}
|
|
2773
|
+
|
|
2876
2774
|
return await this.preloadRoute({
|
|
2877
2775
|
...err.options,
|
|
2878
2776
|
_fromLocation: next,
|
|
@@ -3329,3 +3227,117 @@ export function getMatchedRoutes<TRouteLike extends RouteLike>({
|
|
|
3329
3227
|
|
|
3330
3228
|
return { matchedRoutes, routeParams, foundRoute }
|
|
3331
3229
|
}
|
|
3230
|
+
|
|
3231
|
+
function applySearchMiddleware({
|
|
3232
|
+
search,
|
|
3233
|
+
dest,
|
|
3234
|
+
destRoutes,
|
|
3235
|
+
_includeValidateSearch,
|
|
3236
|
+
}: {
|
|
3237
|
+
search: any
|
|
3238
|
+
dest: BuildNextOptions
|
|
3239
|
+
destRoutes: Array<AnyRoute>
|
|
3240
|
+
_includeValidateSearch: boolean | undefined
|
|
3241
|
+
}) {
|
|
3242
|
+
const allMiddlewares =
|
|
3243
|
+
destRoutes.reduce(
|
|
3244
|
+
(acc, route) => {
|
|
3245
|
+
const middlewares: Array<SearchMiddleware<any>> = []
|
|
3246
|
+
|
|
3247
|
+
if ('search' in route.options) {
|
|
3248
|
+
if (route.options.search?.middlewares) {
|
|
3249
|
+
middlewares.push(...route.options.search.middlewares)
|
|
3250
|
+
}
|
|
3251
|
+
}
|
|
3252
|
+
// TODO remove preSearchFilters and postSearchFilters in v2
|
|
3253
|
+
else if (
|
|
3254
|
+
route.options.preSearchFilters ||
|
|
3255
|
+
route.options.postSearchFilters
|
|
3256
|
+
) {
|
|
3257
|
+
const legacyMiddleware: SearchMiddleware<any> = ({
|
|
3258
|
+
search,
|
|
3259
|
+
next,
|
|
3260
|
+
}) => {
|
|
3261
|
+
let nextSearch = search
|
|
3262
|
+
|
|
3263
|
+
if (
|
|
3264
|
+
'preSearchFilters' in route.options &&
|
|
3265
|
+
route.options.preSearchFilters
|
|
3266
|
+
) {
|
|
3267
|
+
nextSearch = route.options.preSearchFilters.reduce(
|
|
3268
|
+
(prev, next) => next(prev),
|
|
3269
|
+
search,
|
|
3270
|
+
)
|
|
3271
|
+
}
|
|
3272
|
+
|
|
3273
|
+
const result = next(nextSearch)
|
|
3274
|
+
|
|
3275
|
+
if (
|
|
3276
|
+
'postSearchFilters' in route.options &&
|
|
3277
|
+
route.options.postSearchFilters
|
|
3278
|
+
) {
|
|
3279
|
+
return route.options.postSearchFilters.reduce(
|
|
3280
|
+
(prev, next) => next(prev),
|
|
3281
|
+
result,
|
|
3282
|
+
)
|
|
3283
|
+
}
|
|
3284
|
+
|
|
3285
|
+
return result
|
|
3286
|
+
}
|
|
3287
|
+
middlewares.push(legacyMiddleware)
|
|
3288
|
+
}
|
|
3289
|
+
|
|
3290
|
+
if (_includeValidateSearch && route.options.validateSearch) {
|
|
3291
|
+
const validate: SearchMiddleware<any> = ({ search, next }) => {
|
|
3292
|
+
const result = next(search)
|
|
3293
|
+
try {
|
|
3294
|
+
const validatedSearch = {
|
|
3295
|
+
...result,
|
|
3296
|
+
...(validateSearch(route.options.validateSearch, result) ?? {}),
|
|
3297
|
+
}
|
|
3298
|
+
return validatedSearch
|
|
3299
|
+
} catch {
|
|
3300
|
+
// ignore errors here because they are already handled in matchRoutes
|
|
3301
|
+
return result
|
|
3302
|
+
}
|
|
3303
|
+
}
|
|
3304
|
+
|
|
3305
|
+
middlewares.push(validate)
|
|
3306
|
+
}
|
|
3307
|
+
|
|
3308
|
+
return acc.concat(middlewares)
|
|
3309
|
+
},
|
|
3310
|
+
[] as Array<SearchMiddleware<any>>,
|
|
3311
|
+
) ?? []
|
|
3312
|
+
|
|
3313
|
+
// the chain ends here since `next` is not called
|
|
3314
|
+
const final: SearchMiddleware<any> = ({ search }) => {
|
|
3315
|
+
if (!dest.search) {
|
|
3316
|
+
return {}
|
|
3317
|
+
}
|
|
3318
|
+
if (dest.search === true) {
|
|
3319
|
+
return search
|
|
3320
|
+
}
|
|
3321
|
+
return functionalUpdate(dest.search, search)
|
|
3322
|
+
}
|
|
3323
|
+
|
|
3324
|
+
allMiddlewares.push(final)
|
|
3325
|
+
|
|
3326
|
+
const applyNext = (index: number, currentSearch: any): any => {
|
|
3327
|
+
// no more middlewares left, return the current search
|
|
3328
|
+
if (index >= allMiddlewares.length) {
|
|
3329
|
+
return currentSearch
|
|
3330
|
+
}
|
|
3331
|
+
|
|
3332
|
+
const middleware = allMiddlewares[index]!
|
|
3333
|
+
|
|
3334
|
+
const next = (newSearch: any): any => {
|
|
3335
|
+
return applyNext(index + 1, newSearch)
|
|
3336
|
+
}
|
|
3337
|
+
|
|
3338
|
+
return middleware({ search: currentSearch, next })
|
|
3339
|
+
}
|
|
3340
|
+
|
|
3341
|
+
// Start applying middlewares
|
|
3342
|
+
return applyNext(0, search)
|
|
3343
|
+
}
|
package/src/utils.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { RouteIds } from './routeInfo'
|
|
2
2
|
import type { AnyRouter } from './router'
|
|
3
3
|
|
|
4
|
+
export type Awaitable<T> = T | Promise<T>
|
|
4
5
|
export type NoInfer<T> = [T][T extends any ? 0 : never]
|
|
5
6
|
export type IsAny<TValue, TYesResult, TNoResult = TValue> = 1 extends 0 & TValue
|
|
6
7
|
? TYesResult
|