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