@tanstack/router-core 1.120.4 → 1.120.7

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tanstack/router-core",
3
- "version": "1.120.4",
3
+ "version": "1.120.7",
4
4
  "description": "Modern and scalable routing for React applications",
5
5
  "author": "Tanner Linsley",
6
6
  "license": "MIT",
package/src/index.ts CHANGED
@@ -198,6 +198,7 @@ export type {
198
198
  RouteAddChildrenFn,
199
199
  RouteAddFileChildrenFn,
200
200
  RouteAddFileTypesFn,
201
+ RootRoute,
201
202
  } from './route'
202
203
 
203
204
  export {
package/src/route.ts CHANGED
@@ -621,7 +621,24 @@ export interface Route<
621
621
  TBeforeLoadFn
622
622
  >,
623
623
  ) => this
624
- lazy: RouteLazyFn<this>
624
+ lazy: RouteLazyFn<
625
+ Route<
626
+ TParentRoute,
627
+ TPath,
628
+ TFullPath,
629
+ TCustomId,
630
+ TId,
631
+ TSearchValidator,
632
+ TParams,
633
+ TRouterContext,
634
+ TRouteContextFn,
635
+ TBeforeLoadFn,
636
+ TLoaderDeps,
637
+ TLoaderFn,
638
+ TChildren,
639
+ TFileRouteTypes
640
+ >
641
+ >
625
642
  addChildren: RouteAddChildrenFn<
626
643
  TParentRoute,
627
644
  TPath,
@@ -960,7 +977,7 @@ type AssetFnContextOptions<
960
977
  TLoaderDeps
961
978
  >
962
979
  params: ResolveAllParamsFromParent<TParentRoute, TParams>
963
- loaderData: ResolveLoaderData<TLoaderFn>
980
+ loaderData?: ResolveLoaderData<TLoaderFn>
964
981
  }
965
982
 
966
983
  export interface DefaultUpdatableRouteOptionsExtensions {
@@ -1070,9 +1087,20 @@ export interface UpdatableRouteOptions<
1070
1087
  TLoaderDeps
1071
1088
  >,
1072
1089
  ) => void
1073
- headers?: (ctx: {
1074
- loaderData: ResolveLoaderData<TLoaderFn>
1075
- }) => Record<string, string>
1090
+ headers?: (
1091
+ ctx: AssetFnContextOptions<
1092
+ TRouteId,
1093
+ TFullPath,
1094
+ TParentRoute,
1095
+ TParams,
1096
+ TSearchValidator,
1097
+ TLoaderFn,
1098
+ TRouterContext,
1099
+ TRouteContextFn,
1100
+ TBeforeLoadFn,
1101
+ TLoaderDeps
1102
+ >,
1103
+ ) => Record<string, string>
1076
1104
  head?: (
1077
1105
  ctx: AssetFnContextOptions<
1078
1106
  TRouteId,
@@ -1270,24 +1298,7 @@ export class BaseRoute<
1270
1298
  in out TLoaderFn = undefined,
1271
1299
  in out TChildren = unknown,
1272
1300
  in out TFileRouteTypes = unknown,
1273
- > implements
1274
- Route<
1275
- TParentRoute,
1276
- TPath,
1277
- TFullPath,
1278
- TCustomId,
1279
- TId,
1280
- TSearchValidator,
1281
- TParams,
1282
- TRouterContext,
1283
- TRouteContextFn,
1284
- TBeforeLoadFn,
1285
- TLoaderDeps,
1286
- TLoaderFn,
1287
- TChildren,
1288
- TFileRouteTypes
1289
- >
1290
- {
1301
+ > {
1291
1302
  isRoot: TParentRoute extends AnyRoute ? true : false
1292
1303
  options: RouteOptions<
1293
1304
  TParentRoute,
@@ -1562,7 +1573,24 @@ export class BaseRoute<
1562
1573
  return this
1563
1574
  }
1564
1575
 
1565
- lazy: RouteLazyFn<this> = (lazyFn) => {
1576
+ lazy: RouteLazyFn<
1577
+ Route<
1578
+ TParentRoute,
1579
+ TPath,
1580
+ TFullPath,
1581
+ TCustomId,
1582
+ TId,
1583
+ TSearchValidator,
1584
+ TParams,
1585
+ TRouterContext,
1586
+ TRouteContextFn,
1587
+ TBeforeLoadFn,
1588
+ TLoaderDeps,
1589
+ TLoaderFn,
1590
+ TChildren,
1591
+ TFileRouteTypes
1592
+ >
1593
+ > = (lazyFn) => {
1566
1594
  this.lazyFn = lazyFn
1567
1595
  return this
1568
1596
  }
@@ -1580,6 +1608,32 @@ export class BaseRouteApi<TId, TRouter extends AnyRouter = RegisteredRouter> {
1580
1608
  }
1581
1609
  }
1582
1610
 
1611
+ export interface RootRoute<
1612
+ in out TSearchValidator = undefined,
1613
+ in out TRouterContext = {},
1614
+ in out TRouteContextFn = AnyContext,
1615
+ in out TBeforeLoadFn = AnyContext,
1616
+ in out TLoaderDeps extends Record<string, any> = {},
1617
+ in out TLoaderFn = undefined,
1618
+ in out TChildren = unknown,
1619
+ in out TFileRouteTypes = unknown,
1620
+ > extends Route<
1621
+ any, // TParentRoute
1622
+ '/', // TPath
1623
+ '/', // TFullPath
1624
+ string, // TCustomId
1625
+ RootRouteId, // TId
1626
+ TSearchValidator, // TSearchValidator
1627
+ {}, // TParams
1628
+ TRouterContext,
1629
+ TRouteContextFn,
1630
+ TBeforeLoadFn,
1631
+ TLoaderDeps,
1632
+ TLoaderFn,
1633
+ TChildren, // TChildren
1634
+ TFileRouteTypes
1635
+ > {}
1636
+
1583
1637
  export class BaseRootRoute<
1584
1638
  in out TSearchValidator = undefined,
1585
1639
  in out TRouterContext = {},
package/src/router.ts CHANGED
@@ -1447,26 +1447,6 @@ export class RouterCore<
1447
1447
  ...match.__beforeLoadContext,
1448
1448
  }
1449
1449
  }
1450
-
1451
- // If it's already a success, update headers and head content
1452
- // These may get updated again if the match is refreshed
1453
- // due to being stale
1454
- if (match.status === 'success') {
1455
- match.headers = route.options.headers?.({
1456
- loaderData: match.loaderData,
1457
- })
1458
- const assetContext = {
1459
- matches,
1460
- match,
1461
- params: match.params,
1462
- loaderData: match.loaderData,
1463
- }
1464
- const headFnContent = route.options.head?.(assetContext)
1465
- match.links = headFnContent?.links
1466
- match.headScripts = headFnContent?.scripts
1467
- match.meta = headFnContent?.meta
1468
- match.scripts = route.options.scripts?.(assetContext)
1469
- }
1470
1450
  })
1471
1451
 
1472
1452
  return matches
@@ -2609,6 +2589,35 @@ export class RouterCore<
2609
2589
  !this.state.matches.find((d) => d.id === matchId),
2610
2590
  }))
2611
2591
 
2592
+ const executeHead = () => {
2593
+ const match = this.getMatch(matchId)
2594
+ // in case of a redirecting match during preload, the match does not exist
2595
+ if (!match) {
2596
+ return
2597
+ }
2598
+ const assetContext = {
2599
+ matches,
2600
+ match,
2601
+ params: match.params,
2602
+ loaderData: match.loaderData,
2603
+ }
2604
+ const headFnContent = route.options.head?.(assetContext)
2605
+ const meta = headFnContent?.meta
2606
+ const links = headFnContent?.links
2607
+ const headScripts = headFnContent?.scripts
2608
+
2609
+ const scripts = route.options.scripts?.(assetContext)
2610
+ const headers = route.options.headers?.(assetContext)
2611
+ updateMatch(matchId, (prev) => ({
2612
+ ...prev,
2613
+ meta,
2614
+ links,
2615
+ headScripts,
2616
+ headers,
2617
+ scripts,
2618
+ }))
2619
+ }
2620
+
2612
2621
  const runLoader = async () => {
2613
2622
  try {
2614
2623
  // If the Matches component rendered
@@ -2649,40 +2658,21 @@ export class RouterCore<
2649
2658
 
2650
2659
  await potentialPendingMinPromise()
2651
2660
 
2652
- const assetContext = {
2653
- matches,
2654
- match: this.getMatch(matchId)!,
2655
- params: this.getMatch(matchId)!.params,
2656
- loaderData,
2657
- }
2658
- const headFnContent =
2659
- route.options.head?.(assetContext)
2660
- const meta = headFnContent?.meta
2661
- const links = headFnContent?.links
2662
- const headScripts = headFnContent?.scripts
2663
-
2664
- const scripts = route.options.scripts?.(assetContext)
2665
- const headers = route.options.headers?.({
2666
- loaderData,
2667
- })
2668
-
2669
2661
  // Last but not least, wait for the the components
2670
2662
  // to be preloaded before we resolve the match
2671
2663
  await route._componentsPromise
2672
2664
 
2673
- updateMatch(matchId, (prev) => ({
2674
- ...prev,
2675
- error: undefined,
2676
- status: 'success',
2677
- isFetching: false,
2678
- updatedAt: Date.now(),
2679
- loaderData,
2680
- meta,
2681
- links,
2682
- headScripts,
2683
- headers,
2684
- scripts,
2685
- }))
2665
+ batch(() => {
2666
+ updateMatch(matchId, (prev) => ({
2667
+ ...prev,
2668
+ error: undefined,
2669
+ status: 'success',
2670
+ isFetching: false,
2671
+ updatedAt: Date.now(),
2672
+ loaderData,
2673
+ }))
2674
+ executeHead()
2675
+ })
2686
2676
  } catch (e) {
2687
2677
  let error = e
2688
2678
 
@@ -2700,12 +2690,15 @@ export class RouterCore<
2700
2690
  )
2701
2691
  }
2702
2692
 
2703
- updateMatch(matchId, (prev) => ({
2704
- ...prev,
2705
- error,
2706
- status: 'error',
2707
- isFetching: false,
2708
- }))
2693
+ batch(() => {
2694
+ updateMatch(matchId, (prev) => ({
2695
+ ...prev,
2696
+ error,
2697
+ status: 'error',
2698
+ isFetching: false,
2699
+ }))
2700
+ executeHead()
2701
+ })
2709
2702
  }
2710
2703
 
2711
2704
  this.serverSsr?.onMatchSettled({
@@ -2713,10 +2706,13 @@ export class RouterCore<
2713
2706
  match: this.getMatch(matchId)!,
2714
2707
  })
2715
2708
  } catch (err) {
2716
- updateMatch(matchId, (prev) => ({
2717
- ...prev,
2718
- loaderPromise: undefined,
2719
- }))
2709
+ batch(() => {
2710
+ updateMatch(matchId, (prev) => ({
2711
+ ...prev,
2712
+ loaderPromise: undefined,
2713
+ }))
2714
+ executeHead()
2715
+ })
2720
2716
  handleRedirectAndNotFound(this.getMatch(matchId)!, err)
2721
2717
  }
2722
2718
  }
@@ -2752,6 +2748,11 @@ export class RouterCore<
2752
2748
  (loaderShouldRunAsync && sync)
2753
2749
  ) {
2754
2750
  await runLoader()
2751
+ } else {
2752
+ // if the loader did not run, still update head.
2753
+ // reason: parent's beforeLoad may have changed the route context
2754
+ // and only now do we know the route context (and that the loader would not run)
2755
+ executeHead()
2755
2756
  }
2756
2757
  }
2757
2758
  if (!loaderIsRunningAsync) {