@tanstack/react-router 1.31.0 → 1.31.2

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/src/router.ts CHANGED
@@ -25,7 +25,7 @@ import {
25
25
  trimPathLeft,
26
26
  trimPathRight,
27
27
  } from './path'
28
- import { isRedirect } from './redirects'
28
+ import { isRedirect, isResolvedRedirect } from './redirects'
29
29
  import { isNotFound } from './not-found'
30
30
  import type * as React from 'react'
31
31
  import type {
@@ -205,7 +205,7 @@ export interface BuildNextOptions {
205
205
  unmaskOnReload?: boolean
206
206
  }
207
207
  from?: string
208
- _fromLocation?: ParsedLocation
208
+ fromSearch?: unknown
209
209
  }
210
210
 
211
211
  export interface DehydratedRouterState {
@@ -305,7 +305,6 @@ export class Router<
305
305
  )}`
306
306
  resetNextScroll = true
307
307
  shouldViewTransition?: true = undefined
308
- navigateTimeout: Timeout | null = null
309
308
  latestLoadPromise: Promise<void> = Promise.resolve()
310
309
  subscribers = new Set<RouterListener<RouterEvent>>()
311
310
  injectedHtml: Array<InjectedHtmlEntry> = []
@@ -584,10 +583,10 @@ export class Router<
584
583
  })
585
584
  }
586
585
 
587
- checkLatest = (promise: Promise<void>): undefined | Promise<void> => {
588
- return this.latestLoadPromise !== promise
589
- ? this.latestLoadPromise
590
- : undefined
586
+ checkLatest = (promise: Promise<void>): void => {
587
+ if (this.latestLoadPromise !== promise) {
588
+ throw this.latestLoadPromise
589
+ }
591
590
  }
592
591
 
593
592
  parseLocation = (
@@ -920,25 +919,25 @@ export class Router<
920
919
  } = {},
921
920
  matches?: Array<MakeRouteMatch<TRouteTree>>,
922
921
  ): ParsedLocation => {
923
- const fromPath = dest.from || this.latestLocation.pathname
924
- let fromSearch = dest._fromLocation?.search || this.latestLocation.search
922
+ let fromPath = this.latestLocation.pathname
923
+ let fromSearch = dest.fromSearch || this.latestLocation.search
925
924
 
926
- const fromMatches = this.matchRoutes(fromPath, fromSearch)
925
+ const fromMatches = this.matchRoutes(
926
+ this.latestLocation.pathname,
927
+ fromSearch,
928
+ )
927
929
 
930
+ fromPath =
931
+ fromMatches.find((d) => d.id === dest.from)?.pathname || fromPath
928
932
  fromSearch = last(fromMatches)?.search || this.latestLocation.search
929
933
 
930
934
  const stayingMatches = matches?.filter((d) =>
931
935
  fromMatches.find((e) => e.routeId === d.routeId),
932
936
  )
933
937
 
934
- const fromRoute = this.looseRoutesById[last(fromMatches)?.routeId]
935
-
936
938
  let pathname = dest.to
937
- ? this.resolvePathWithBase(
938
- dest.from ?? this.latestLocation.pathname,
939
- `${dest.to}`,
940
- )
941
- : this.resolvePathWithBase(fromRoute?.fullPath, fromRoute?.fullPath)
939
+ ? this.resolvePathWithBase(fromPath, `${dest.to}`)
940
+ : this.resolvePathWithBase(fromPath, fromPath)
942
941
 
943
942
  const prevParams = { ...last(fromMatches)?.params }
944
943
 
@@ -1113,8 +1112,6 @@ export class Router<
1113
1112
  viewTransition,
1114
1113
  ...next
1115
1114
  }: ParsedLocation & CommitLocationOptions) => {
1116
- if (this.navigateTimeout) clearTimeout(this.navigateTimeout)
1117
-
1118
1115
  const isSameUrl = this.latestLocation.href === next.href
1119
1116
 
1120
1117
  // If the next urls are the same and we're not replacing,
@@ -1209,19 +1206,180 @@ export class Router<
1209
1206
  })
1210
1207
  }
1211
1208
 
1209
+ load = async (): Promise<void> => {
1210
+ const promise = createControlledPromise<void>()
1211
+ this.latestLoadPromise = promise
1212
+ let redirect: ResolvedRedirect | undefined
1213
+ let notFound: NotFoundError | undefined
1214
+
1215
+ this.startReactTransition(async () => {
1216
+ try {
1217
+ const next = this.latestLocation
1218
+ const prevLocation = this.state.resolvedLocation
1219
+ const pathDidChange = prevLocation.href !== next.href
1220
+
1221
+ // Cancel any pending matches
1222
+ this.cancelMatches()
1223
+
1224
+ this.emit({
1225
+ type: 'onBeforeLoad',
1226
+ fromLocation: prevLocation,
1227
+ toLocation: next,
1228
+ pathChanged: pathDidChange,
1229
+ })
1230
+
1231
+ let pendingMatches!: Array<AnyRouteMatch>
1232
+
1233
+ this.__store.batch(() => {
1234
+ this.cleanCache()
1235
+
1236
+ // Match the routes
1237
+ pendingMatches = this.matchRoutes(next.pathname, next.search)
1238
+
1239
+ // Ingest the new matches
1240
+ this.__store.setState((s) => ({
1241
+ ...s,
1242
+ status: 'pending',
1243
+ isLoading: true,
1244
+ location: next,
1245
+ pendingMatches,
1246
+ // If a cached moved to pendingMatches, remove it from cachedMatches
1247
+ cachedMatches: s.cachedMatches.filter((d) => {
1248
+ return !pendingMatches.find((e) => e.id === d.id)
1249
+ }),
1250
+ }))
1251
+ })
1252
+
1253
+ await this.loadMatches({
1254
+ matches: pendingMatches,
1255
+ location: next,
1256
+ checkLatest: () => this.checkLatest(promise),
1257
+ onReady: async () => {
1258
+ await this.startViewTransition(async () => {
1259
+ // this.viewTransitionPromise = createControlledPromise<true>()
1260
+
1261
+ // Commit the pending matches. If a previous match was
1262
+ // removed, place it in the cachedMatches
1263
+ let exitingMatches!: Array<AnyRouteMatch>
1264
+ let enteringMatches!: Array<AnyRouteMatch>
1265
+ let stayingMatches!: Array<AnyRouteMatch>
1266
+
1267
+ this.__store.batch(() => {
1268
+ this.__store.setState((s) => {
1269
+ const previousMatches = s.matches
1270
+ const newMatches = s.pendingMatches || s.matches
1271
+
1272
+ exitingMatches = previousMatches.filter(
1273
+ (match) => !newMatches.find((d) => d.id === match.id),
1274
+ )
1275
+ enteringMatches = newMatches.filter(
1276
+ (match) => !previousMatches.find((d) => d.id === match.id),
1277
+ )
1278
+ stayingMatches = previousMatches.filter((match) =>
1279
+ newMatches.find((d) => d.id === match.id),
1280
+ )
1281
+
1282
+ return {
1283
+ ...s,
1284
+ isLoading: false,
1285
+ matches: newMatches,
1286
+ pendingMatches: undefined,
1287
+ cachedMatches: [
1288
+ ...s.cachedMatches,
1289
+ ...exitingMatches.filter((d) => d.status !== 'error'),
1290
+ ],
1291
+ }
1292
+ })
1293
+ this.cleanCache()
1294
+ })
1295
+
1296
+ //
1297
+ ;(
1298
+ [
1299
+ [exitingMatches, 'onLeave'],
1300
+ [enteringMatches, 'onEnter'],
1301
+ [stayingMatches, 'onStay'],
1302
+ ] as const
1303
+ ).forEach(([matches, hook]) => {
1304
+ matches.forEach((match) => {
1305
+ this.looseRoutesById[match.routeId]!.options[hook]?.(match)
1306
+ })
1307
+ })
1308
+ })
1309
+ },
1310
+ })
1311
+ } catch (err) {
1312
+ if (isResolvedRedirect(err)) {
1313
+ redirect = err
1314
+ if (!this.isServer) {
1315
+ this.navigate({ ...err, replace: true })
1316
+ this.load()
1317
+ }
1318
+ } else if (isNotFound(err)) {
1319
+ notFound = err
1320
+ }
1321
+
1322
+ this.__store.setState((s) => ({
1323
+ ...s,
1324
+ statusCode:
1325
+ redirect?.statusCode || notFound
1326
+ ? 404
1327
+ : s.matches.some((d) => d.status === 'error')
1328
+ ? 500
1329
+ : 200,
1330
+ redirect,
1331
+ }))
1332
+ }
1333
+
1334
+ promise.resolve()
1335
+ })
1336
+
1337
+ return this.latestLoadPromise
1338
+ }
1339
+
1340
+ startViewTransition = async (fn: () => Promise<void>) => {
1341
+ // Determine if we should start a view transition from the navigation
1342
+ // or from the router default
1343
+ const shouldViewTransition =
1344
+ this.shouldViewTransition ?? this.options.defaultViewTransition
1345
+
1346
+ // Reset the view transition flag
1347
+ delete this.shouldViewTransition
1348
+ // Attempt to start a view transition (or just apply the changes if we can't)
1349
+ ;(shouldViewTransition && typeof document !== 'undefined'
1350
+ ? document
1351
+ : undefined
1352
+ )
1353
+ // @ts-expect-error
1354
+ ?.startViewTransition?.(fn) || fn()
1355
+ }
1356
+
1212
1357
  loadMatches = async ({
1213
1358
  checkLatest,
1214
1359
  location,
1215
1360
  matches,
1216
1361
  preload,
1362
+ onReady,
1217
1363
  }: {
1218
- checkLatest: () => Promise<void> | undefined
1364
+ checkLatest: () => void
1219
1365
  location: ParsedLocation
1220
1366
  matches: Array<AnyRouteMatch>
1221
1367
  preload?: boolean
1368
+ onReady?: () => Promise<void>
1222
1369
  }): Promise<Array<MakeRouteMatch>> => {
1223
- let latestPromise
1224
1370
  let firstBadMatchIndex: number | undefined
1371
+ let rendered = false
1372
+
1373
+ const triggerOnReady = async () => {
1374
+ if (!rendered) {
1375
+ rendered = true
1376
+ await onReady?.()
1377
+ }
1378
+ }
1379
+
1380
+ if (!this.isServer && !this.state.matches.length) {
1381
+ triggerOnReady()
1382
+ }
1225
1383
 
1226
1384
  const updateMatch = (
1227
1385
  id: string,
@@ -1250,51 +1408,95 @@ export class Router<
1250
1408
  return updated
1251
1409
  }
1252
1410
 
1253
- try {
1254
- await new Promise<void>((resolveAll, rejectAll) => {
1255
- ;(async () => {
1256
- try {
1257
- const handleRedirectAndNotFound = (
1258
- match: AnyRouteMatch,
1259
- err: any,
1260
- ) => {
1261
- if (isRedirect(err) || isNotFound(err)) {
1262
- updateMatch(match.id, (prev) => ({
1263
- ...prev,
1264
- status: isRedirect(err)
1265
- ? 'redirected'
1266
- : isNotFound(err)
1267
- ? 'notFound'
1268
- : 'error',
1269
- isFetching: false,
1270
- error: err,
1271
- }))
1411
+ const handleRedirectAndNotFound = (match: AnyRouteMatch, err: any) => {
1412
+ if (isResolvedRedirect(err)) throw err
1272
1413
 
1273
- if (!(err as any).routeId) {
1274
- ;(err as any).routeId = match.routeId
1275
- }
1414
+ if (isRedirect(err) || isNotFound(err)) {
1415
+ // if (!rendered) {
1416
+ updateMatch(match.id, (prev) => ({
1417
+ ...prev,
1418
+ status: isRedirect(err)
1419
+ ? 'redirected'
1420
+ : isNotFound(err)
1421
+ ? 'notFound'
1422
+ : 'error',
1423
+ isFetching: false,
1424
+ error: err,
1425
+ }))
1426
+ // }
1276
1427
 
1277
- if (isRedirect(err)) {
1278
- const redirect = this.resolveRedirect(err)
1428
+ rendered = true
1279
1429
 
1280
- if (!preload && !this.isServer) {
1281
- this.navigate({ ...(redirect as any), replace: true })
1282
- }
1430
+ if (!(err as any).routeId) {
1431
+ ;(err as any).routeId = match.routeId
1432
+ }
1283
1433
 
1284
- throw redirect
1285
- } else if (isNotFound(err)) {
1286
- if (!preload) this.handleNotFound(matches, err)
1287
- throw err
1288
- }
1289
- }
1290
- }
1434
+ if (isRedirect(err)) {
1435
+ err = this.resolveRedirect(err)
1436
+ throw err
1437
+ } else if (isNotFound(err)) {
1438
+ this.handleNotFound(matches, err)
1439
+ throw err
1440
+ }
1441
+ }
1442
+ }
1291
1443
 
1444
+ try {
1445
+ await new Promise<void>((resolveAll, rejectAll) => {
1446
+ ;(async () => {
1447
+ try {
1292
1448
  // Check each match middleware to see if the route can be accessed
1293
1449
  // eslint-disable-next-line prefer-const
1294
1450
  for (let [index, match] of matches.entries()) {
1295
1451
  const parentMatch = matches[index - 1]
1296
1452
  const route = this.looseRoutesById[match.routeId]!
1297
1453
  const abortController = new AbortController()
1454
+ let loadPromise = match.loadPromise
1455
+
1456
+ const pendingMs =
1457
+ route.options.pendingMs ?? this.options.defaultPendingMs
1458
+
1459
+ const shouldPending = !!(
1460
+ onReady &&
1461
+ !this.isServer &&
1462
+ !preload &&
1463
+ (route.options.loader || route.options.beforeLoad) &&
1464
+ typeof pendingMs === 'number' &&
1465
+ pendingMs !== Infinity &&
1466
+ (route.options.pendingComponent ??
1467
+ this.options.defaultPendingComponent)
1468
+ )
1469
+
1470
+ if (shouldPending) {
1471
+ // If we might show a pending component, we need to wait for the
1472
+ // pending promise to resolve before we start showing that state
1473
+ setTimeout(() => {
1474
+ try {
1475
+ checkLatest()
1476
+ // Update the match and prematurely resolve the loadMatches promise so that
1477
+ // the pending component can start rendering
1478
+ triggerOnReady()
1479
+ } catch {}
1480
+ }, pendingMs)
1481
+ }
1482
+
1483
+ if (match.isFetching) {
1484
+ continue
1485
+ }
1486
+
1487
+ const previousResolve = loadPromise.resolve
1488
+ // Create a new one
1489
+ loadPromise = createControlledPromise<void>(
1490
+ // Resolve the old when we we resolve the new one
1491
+ previousResolve,
1492
+ )
1493
+
1494
+ // Otherwise, load the route
1495
+ matches[index] = match = updateMatch(match.id, (prev) => ({
1496
+ ...prev,
1497
+ isFetching: 'beforeLoad',
1498
+ loadPromise,
1499
+ }))
1298
1500
 
1299
1501
  const handleSerialError = (err: any, routerCode: string) => {
1300
1502
  err.routerCode = routerCode
@@ -1325,42 +1527,10 @@ export class Router<
1325
1527
  handleSerialError(match.searchError, 'VALIDATE_SEARCH')
1326
1528
  }
1327
1529
 
1328
- // if (match.globalNotFound && !preload) {
1329
- // handleSerialError(notFound({ _global: true }), 'NOT_FOUND')
1330
- // }
1331
-
1332
1530
  try {
1333
1531
  const parentContext =
1334
1532
  parentMatch?.context ?? this.options.context ?? {}
1335
1533
 
1336
- const pendingMs =
1337
- route.options.pendingMs ?? this.options.defaultPendingMs
1338
- const pendingPromise =
1339
- typeof pendingMs !== 'number' || pendingMs <= 0
1340
- ? Promise.resolve()
1341
- : new Promise<void>((r) => {
1342
- if (pendingMs !== Infinity) setTimeout(r, pendingMs)
1343
- })
1344
-
1345
- const shouldPending =
1346
- !this.isServer &&
1347
- !preload &&
1348
- (route.options.loader || route.options.beforeLoad) &&
1349
- typeof pendingMs === 'number' &&
1350
- (route.options.pendingComponent ??
1351
- this.options.defaultPendingComponent)
1352
-
1353
- if (shouldPending) {
1354
- // If we might show a pending component, we need to wait for the
1355
- // pending promise to resolve before we start showing that state
1356
- pendingPromise.then(async () => {
1357
- if ((latestPromise = checkLatest())) return latestPromise
1358
- // Update the match and prematurely resolve the loadMatches promise so that
1359
- // the pending component can start rendering
1360
- resolveAll()
1361
- })
1362
- }
1363
-
1364
1534
  const beforeLoadContext =
1365
1535
  (await route.options.beforeLoad?.({
1366
1536
  search: match.search,
@@ -1375,6 +1545,8 @@ export class Router<
1375
1545
  cause: preload ? 'preload' : match.cause,
1376
1546
  })) ?? ({} as any)
1377
1547
 
1548
+ checkLatest()
1549
+
1378
1550
  if (
1379
1551
  isRedirect(beforeLoadContext) ||
1380
1552
  isNotFound(beforeLoadContext)
@@ -1404,6 +1576,8 @@ export class Router<
1404
1576
  }
1405
1577
  }
1406
1578
 
1579
+ checkLatest()
1580
+
1407
1581
  const validResolvedMatches = matches.slice(0, firstBadMatchIndex)
1408
1582
  const matchPromises: Array<Promise<any>> = []
1409
1583
 
@@ -1431,7 +1605,6 @@ export class Router<
1431
1605
  let lazyPromise = Promise.resolve()
1432
1606
  let componentsPromise = Promise.resolve() as Promise<any>
1433
1607
  let loaderPromise = existing.loaderPromise
1434
- let loadPromise = existing.loadPromise
1435
1608
 
1436
1609
  // If the Matches component rendered
1437
1610
  // the pending component and needs to show it for
@@ -1444,8 +1617,7 @@ export class Router<
1444
1617
  if (latestMatch?.minPendingPromise) {
1445
1618
  await latestMatch.minPendingPromise
1446
1619
 
1447
- if ((latestPromise = checkLatest()))
1448
- return await latestPromise
1620
+ checkLatest()
1449
1621
 
1450
1622
  updateMatch(latestMatch.id, (prev) => ({
1451
1623
  ...prev,
@@ -1455,7 +1627,7 @@ export class Router<
1455
1627
  }
1456
1628
 
1457
1629
  try {
1458
- if (!match.isFetching) {
1630
+ if (match.isFetching === 'beforeLoad') {
1459
1631
  // If the user doesn't want the route to reload, just
1460
1632
  // resolve with the existing loader data
1461
1633
 
@@ -1464,11 +1636,14 @@ export class Router<
1464
1636
  // }
1465
1637
 
1466
1638
  // Otherwise, load the route
1467
- matches[index] = match = {
1468
- ...match,
1469
- isFetching: true,
1470
- fetchCount: match.fetchCount + 1,
1471
- }
1639
+ matches[index] = match = updateMatch(
1640
+ match.id,
1641
+ (prev) => ({
1642
+ ...prev,
1643
+ isFetching: 'loader',
1644
+ fetchCount: match.fetchCount + 1,
1645
+ }),
1646
+ )
1472
1647
 
1473
1648
  lazyPromise =
1474
1649
  route.lazyFn?.().then((lazyRoute) => {
@@ -1495,38 +1670,27 @@ export class Router<
1495
1670
  // we can use the options
1496
1671
  await lazyPromise
1497
1672
 
1498
- if ((latestPromise = checkLatest()))
1499
- return await latestPromise
1673
+ checkLatest()
1500
1674
 
1501
1675
  // Kick off the loader!
1502
1676
  loaderPromise = route.options.loader?.(loaderContext)
1503
1677
 
1504
- const previousResolve = loadPromise.resolve
1505
- // Create a new one
1506
- loadPromise = createControlledPromise<void>(
1507
- // Resolve the old when we we resolve the new one
1508
- previousResolve,
1678
+ matches[index] = match = updateMatch(
1679
+ match.id,
1680
+ (prev) => ({
1681
+ ...prev,
1682
+ loaderPromise,
1683
+ }),
1509
1684
  )
1510
1685
  }
1511
1686
 
1512
- matches[index] = match = updateMatch(match.id, (prev) => ({
1513
- ...prev,
1514
- loaderPromise,
1515
- loadPromise,
1516
- }))
1517
-
1518
1687
  const loaderData = await loaderPromise
1519
- if ((latestPromise = checkLatest()))
1520
- return await latestPromise
1688
+ checkLatest()
1521
1689
 
1522
1690
  handleRedirectAndNotFound(match, loaderData)
1523
1691
 
1524
- if ((latestPromise = checkLatest()))
1525
- return await latestPromise
1526
-
1527
1692
  await potentialPendingMinPromise()
1528
- if ((latestPromise = checkLatest()))
1529
- return await latestPromise
1693
+ checkLatest()
1530
1694
 
1531
1695
  const meta = route.options.meta?.({
1532
1696
  params: match.params,
@@ -1548,13 +1712,11 @@ export class Router<
1548
1712
  headers,
1549
1713
  }))
1550
1714
  } catch (e) {
1715
+ checkLatest()
1551
1716
  let error = e
1552
- if ((latestPromise = checkLatest()))
1553
- return await latestPromise
1554
1717
 
1555
1718
  await potentialPendingMinPromise()
1556
- if ((latestPromise = checkLatest()))
1557
- return await latestPromise
1719
+ checkLatest()
1558
1720
 
1559
1721
  handleRedirectAndNotFound(match, e)
1560
1722
 
@@ -1577,10 +1739,9 @@ export class Router<
1577
1739
  // to be preloaded before we resolve the match
1578
1740
  await componentsPromise
1579
1741
 
1580
- if ((latestPromise = checkLatest()))
1581
- return await latestPromise
1742
+ checkLatest()
1582
1743
 
1583
- loadPromise.resolve()
1744
+ match.loadPromise.resolve()
1584
1745
  }
1585
1746
 
1586
1747
  // This is where all of the stale-while-revalidate magic happens
@@ -1615,8 +1776,7 @@ export class Router<
1615
1776
  try {
1616
1777
  await fetch()
1617
1778
  } catch (err) {
1618
- if ((latestPromise = checkLatest()))
1619
- return await latestPromise
1779
+ checkLatest()
1620
1780
  handleRedirectAndNotFound(match, err)
1621
1781
  }
1622
1782
  }
@@ -1636,7 +1796,7 @@ export class Router<
1636
1796
  }),
1637
1797
  )
1638
1798
 
1639
- if ((latestPromise = checkLatest())) return await latestPromise
1799
+ checkLatest()
1640
1800
 
1641
1801
  resolveAll()
1642
1802
  } catch (err) {
@@ -1644,6 +1804,7 @@ export class Router<
1644
1804
  }
1645
1805
  })()
1646
1806
  })
1807
+ await triggerOnReady()
1647
1808
  } catch (err) {
1648
1809
  if (isRedirect(err) || isNotFound(err)) {
1649
1810
  throw err
@@ -1670,173 +1831,6 @@ export class Router<
1670
1831
  return this.load()
1671
1832
  }
1672
1833
 
1673
- load = async (): Promise<void> => {
1674
- let resolveLoad!: (value: void) => void
1675
- let rejectLoad!: (reason: any) => void
1676
-
1677
- const promise = new Promise<void>((resolve, reject) => {
1678
- resolveLoad = resolve
1679
- rejectLoad = reject
1680
- })
1681
-
1682
- this.latestLoadPromise = promise
1683
-
1684
- let latestPromise: Promise<void> | undefined | null
1685
-
1686
- this.startReactTransition(async () => {
1687
- try {
1688
- const next = this.latestLocation
1689
- const prevLocation = this.state.resolvedLocation
1690
- const pathDidChange = prevLocation.href !== next.href
1691
-
1692
- // Cancel any pending matches
1693
- this.cancelMatches()
1694
-
1695
- this.emit({
1696
- type: 'onBeforeLoad',
1697
- fromLocation: prevLocation,
1698
- toLocation: next,
1699
- pathChanged: pathDidChange,
1700
- })
1701
-
1702
- let pendingMatches!: Array<AnyRouteMatch>
1703
- const previousMatches = this.state.matches
1704
-
1705
- this.__store.batch(() => {
1706
- this.cleanCache()
1707
-
1708
- // Match the routes
1709
- pendingMatches = this.matchRoutes(next.pathname, next.search)
1710
-
1711
- // Ingest the new matches
1712
- // If a cached moved to pendingMatches, remove it from cachedMatches
1713
- this.__store.setState((s) => ({
1714
- ...s,
1715
- status: 'pending',
1716
- isLoading: true,
1717
- location: next,
1718
- pendingMatches,
1719
- cachedMatches: s.cachedMatches.filter((d) => {
1720
- return !pendingMatches.find((e) => e.id === d.id)
1721
- }),
1722
- }))
1723
- })
1724
-
1725
- let redirect: ResolvedRedirect | undefined
1726
- let notFound: NotFoundError | undefined
1727
-
1728
- try {
1729
- // Load the matches
1730
- const loadMatchesPromise = this.loadMatches({
1731
- matches: pendingMatches,
1732
- location: next,
1733
- checkLatest: () => this.checkLatest(promise),
1734
- })
1735
-
1736
- if (previousMatches.length || this.isServer) {
1737
- await loadMatchesPromise
1738
- }
1739
- } catch (err) {
1740
- if (isRedirect(err)) {
1741
- redirect = err as ResolvedRedirect
1742
- } else if (isNotFound(err)) {
1743
- notFound = err
1744
- }
1745
-
1746
- // Swallow all other errors that happen inside
1747
- // of loadMatches. These errors will be handled
1748
- // as state on each match.
1749
- }
1750
-
1751
- // Only apply the latest transition
1752
- if ((latestPromise = this.checkLatest(promise))) {
1753
- return latestPromise
1754
- }
1755
-
1756
- const exitingMatches = previousMatches.filter(
1757
- (match) => !pendingMatches.find((d) => d.id === match.id),
1758
- )
1759
- const enteringMatches = pendingMatches.filter(
1760
- (match) => !previousMatches.find((d) => d.id === match.id),
1761
- )
1762
- const stayingMatches = previousMatches.filter((match) =>
1763
- pendingMatches.find((d) => d.id === match.id),
1764
- )
1765
-
1766
- // Determine if we should start a view transition from the navigation
1767
- // or from the router default
1768
- const shouldViewTransition =
1769
- this.shouldViewTransition ?? this.options.defaultViewTransition
1770
-
1771
- // Reset the view transition flag
1772
- delete this.shouldViewTransition
1773
-
1774
- const apply = () => {
1775
- // this.viewTransitionPromise = createControlledPromise<true>()
1776
-
1777
- // Commit the pending matches. If a previous match was
1778
- // removed, place it in the cachedMatches
1779
- this.__store.batch(() => {
1780
- this.__store.setState((s) => ({
1781
- ...s,
1782
- isLoading: false,
1783
- matches: s.pendingMatches!,
1784
- pendingMatches: undefined,
1785
- cachedMatches: [
1786
- ...s.cachedMatches,
1787
- ...exitingMatches.filter((d) => d.status !== 'error'),
1788
- ],
1789
- statusCode:
1790
- redirect?.statusCode || notFound
1791
- ? 404
1792
- : s.matches.some((d) => d.status === 'error')
1793
- ? 500
1794
- : 200,
1795
- redirect,
1796
- }))
1797
- this.cleanCache()
1798
- })
1799
-
1800
- //
1801
- ;(
1802
- [
1803
- [exitingMatches, 'onLeave'],
1804
- [enteringMatches, 'onEnter'],
1805
- [stayingMatches, 'onStay'],
1806
- ] as const
1807
- ).forEach(([matches, hook]) => {
1808
- matches.forEach((match) => {
1809
- this.looseRoutesById[match.routeId]!.options[hook]?.(match)
1810
- })
1811
- })
1812
-
1813
- resolveLoad()
1814
-
1815
- // return this.viewTransitionPromise
1816
- }
1817
-
1818
- // Attempt to start a view transition (or just apply the changes if we can't)
1819
- ;(shouldViewTransition && typeof document !== 'undefined'
1820
- ? document
1821
- : undefined
1822
- )
1823
- // @ts-expect-error
1824
- ?.startViewTransition?.(apply) || apply()
1825
- } catch (err) {
1826
- // Only apply the latest transition
1827
- if ((latestPromise = this.checkLatest(promise))) {
1828
- return latestPromise
1829
- }
1830
-
1831
- console.error('Load Error', err)
1832
-
1833
- rejectLoad(err)
1834
- }
1835
- })
1836
-
1837
- return this.latestLoadPromise
1838
- }
1839
-
1840
1834
  resolveRedirect = (err: AnyRedirect): ResolvedRedirect => {
1841
1835
  const redirect = err as ResolvedRedirect
1842
1836
 
@@ -1942,7 +1936,7 @@ export class Router<
1942
1936
  } catch (err) {
1943
1937
  if (isRedirect(err)) {
1944
1938
  return await this.preloadRoute({
1945
- _fromDest: next,
1939
+ fromSearch: next.search,
1946
1940
  from: next.pathname,
1947
1941
  ...(err as any),
1948
1942
  })