@tanstack/router-core 0.0.1-alpha.7 → 0.0.1-alpha.9

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,7 +1,7 @@
1
1
  {
2
2
  "name": "@tanstack/router-core",
3
3
  "author": "Tanner Linsley",
4
- "version": "0.0.1-alpha.7",
4
+ "version": "0.0.1-alpha.9",
5
5
  "license": "MIT",
6
6
  "repository": "tanstack/router",
7
7
  "homepage": "https://tanstack.com/router",
package/src/link.ts CHANGED
@@ -220,8 +220,10 @@ export type LinkOptions<
220
220
  activeOptions?: ActiveOptions
221
221
  // If set, will preload the linked route on hover and cache it for this many milliseconds in hopes that the user will eventually navigate there.
222
222
  preload?: false | 'intent'
223
- // When preloaded and set, will cache the preloaded result for this duration in milliseconds
223
+ // When preloaded, the preloaded result will be considered "fresh" for this duration in milliseconds
224
224
  preloadMaxAge?: number
225
+ // When preloaded and subsequently inactive, the preloaded result will remain in memory for this duration in milliseconds
226
+ preloadGcMaxAge?: number
225
227
  // Delay intent preloading by this many milliseconds. If the intent exits before this delay, the preload will be cancelled.
226
228
  preloadDelay?: number
227
229
  // If true, will render the link without the href attribute
package/src/routeMatch.ts CHANGED
@@ -30,6 +30,7 @@ export interface RouteMatch<
30
30
  routeLoaderData: TRouteInfo['routeLoaderData']
31
31
  isFetching: boolean
32
32
  isPending: boolean
33
+ invalidAt: number
33
34
  __: {
34
35
  element?: GetFrameworkGeneric<'Element'> // , TRouteInfo['loaderData']>
35
36
  errorElement?: GetFrameworkGeneric<'Element'> // , TRouteInfo['loaderData']>
@@ -60,7 +61,7 @@ export interface RouteMatch<
60
61
  resolve: () => void
61
62
  }
62
63
  cancel: () => void
63
- load: () => Promise<void>
64
+ load: (opts?: { maxAge?: number }) => Promise<void>
64
65
  invalidate: () => void
65
66
  hasLoaders: () => boolean
66
67
  }
@@ -97,13 +98,10 @@ export function createRouteMatch<
97
98
  isPending: false,
98
99
  isFetching: false,
99
100
  isInvalid: false,
101
+ invalidAt: Infinity,
100
102
  getIsInvalid: () => {
101
103
  const now = Date.now()
102
- const maxAge =
103
- routeMatch.options.loaderMaxAge ??
104
- router.options.defaultLoaderMaxAge ??
105
- 0
106
- return routeMatch.isInvalid || routeMatch.updatedAt! + maxAge < now
104
+ return routeMatch.isInvalid || routeMatch.invalidAt < now
107
105
  },
108
106
  __: {
109
107
  abortController: new AbortController(),
@@ -212,7 +210,7 @@ export function createRouteMatch<
212
210
  elementTypes.some((d) => typeof route.options[d] === 'function')
213
211
  )
214
212
  },
215
- load: async () => {
213
+ load: async (opts) => {
216
214
  const id = '' + Date.now() + Math.random()
217
215
  routeMatch.__.latestId = id
218
216
 
@@ -298,6 +296,12 @@ export function createRouteMatch<
298
296
  routeMatch.error = undefined
299
297
  routeMatch.status = 'success'
300
298
  routeMatch.updatedAt = Date.now()
299
+ routeMatch.invalidAt =
300
+ routeMatch.updatedAt +
301
+ (opts?.maxAge ??
302
+ routeMatch.options.loaderMaxAge ??
303
+ router.options.defaultLoaderMaxAge ??
304
+ 0)
301
305
  } catch (err) {
302
306
  if (id !== routeMatch.__.latestId) {
303
307
  return routeMatch.__.loaderPromise
package/src/router.ts CHANGED
@@ -81,9 +81,10 @@ export interface RouterOptions<TRouteConfig extends AnyRouteConfig> {
81
81
  stringifySearch?: SearchSerializer
82
82
  parseSearch?: SearchParser
83
83
  filterRoutes?: FilterRoutesFn
84
- defaultLinkPreload?: false | 'intent'
85
- defaultLinkPreloadMaxAge?: number
86
- defaultLinkPreloadDelay?: number
84
+ defaultPreload?: false | 'intent'
85
+ defaultPreloadMaxAge?: number
86
+ defaultPreloadGcMaxAge?: number
87
+ defaultPreloadDelay?: number
87
88
  useErrorBoundary?: boolean
88
89
  defaultElement?: GetFrameworkGeneric<'Element'>
89
90
  defaultErrorElement?: GetFrameworkGeneric<'Element'>
@@ -227,9 +228,10 @@ export interface Router<
227
228
  getRoute: <TId extends keyof TAllRouteInfo['routeInfoById']>(
228
229
  id: TId,
229
230
  ) => Route<TAllRouteInfo, TAllRouteInfo['routeInfoById'][TId]>
230
- loadRoute: (
231
+ loadRoute: (navigateOpts: BuildNextOptions) => Promise<RouteMatch[]>
232
+ preloadRoute: (
231
233
  navigateOpts: BuildNextOptions,
232
- loaderOpts: { maxAge: number },
234
+ loaderOpts: { maxAge?: number; gcMaxAge?: number },
233
235
  ) => Promise<RouteMatch[]>
234
236
  matchRoutes: (
235
237
  pathname: string,
@@ -238,8 +240,8 @@ export interface Router<
238
240
  loadMatches: (
239
241
  resolvedMatches: RouteMatch[],
240
242
  loaderOpts?: { withPending?: boolean } & (
241
- | { preload: true; maxAge: number }
242
- | { preload?: false; maxAge?: never }
243
+ | { preload: true; maxAge: number; gcMaxAge: number }
244
+ | { preload?: false; maxAge?: never; gcMaxAge?: never }
243
245
  ),
244
246
  ) => Promise<void>
245
247
  invalidateRoute: (opts: MatchLocation) => void
@@ -289,7 +291,8 @@ export function createRouter<
289
291
  const originalOptions = {
290
292
  defaultLoaderGcMaxAge: 5 * 60 * 1000,
291
293
  defaultLoaderMaxAge: 0,
292
- defaultLinkPreloadDelay: 50,
294
+ defaultPreloadMaxAge: 2000,
295
+ defaultPreloadDelay: 50,
293
296
  ...userOptions,
294
297
  stringifySearch: userOptions?.stringifySearch ?? defaultStringifySearch,
295
298
  parseSearch: userOptions?.parseSearch ?? defaultParseSearch,
@@ -782,17 +785,32 @@ export function createRouter<
782
785
  })
783
786
  },
784
787
 
785
- loadRoute: async (
786
- navigateOpts: BuildNextOptions = router.location,
787
- loaderOpts: { maxAge: number },
788
- ) => {
788
+ loadRoute: async (navigateOpts = router.location) => {
789
+ const next = router.buildNext(navigateOpts)
790
+ const matches = router.matchRoutes(next.pathname, {
791
+ strictParseParams: true,
792
+ })
793
+ await router.loadMatches(matches)
794
+ return matches
795
+ },
796
+
797
+ preloadRoute: async (navigateOpts = router.location, loaderOpts) => {
789
798
  const next = router.buildNext(navigateOpts)
790
799
  const matches = router.matchRoutes(next.pathname, {
791
800
  strictParseParams: true,
792
801
  })
793
802
  await router.loadMatches(matches, {
794
803
  preload: true,
795
- maxAge: loaderOpts.maxAge,
804
+ maxAge:
805
+ loaderOpts.maxAge ??
806
+ router.options.defaultPreloadMaxAge ??
807
+ router.options.defaultLoaderMaxAge ??
808
+ 0,
809
+ gcMaxAge:
810
+ loaderOpts.gcMaxAge ??
811
+ router.options.defaultPreloadGcMaxAge ??
812
+ router.options.defaultLoaderGcMaxAge ??
813
+ 0,
796
814
  })
797
815
  return matches
798
816
  },
@@ -905,23 +923,23 @@ export function createRouter<
905
923
 
906
924
  loadMatches: async (resolvedMatches, loaderOpts) => {
907
925
  const now = Date.now()
926
+ const minMaxAge = loaderOpts?.preload
927
+ ? Math.max(loaderOpts?.maxAge, loaderOpts?.gcMaxAge)
928
+ : 0
908
929
 
909
930
  const matchPromises = resolvedMatches.map(async (match) => {
910
931
  // Validate the match (loads search params etc)
911
932
  match.__.validate()
912
933
 
913
- // If the match doesn't have a loader, don't attempt to load it
914
- if (!match.hasLoaders()) {
915
- return
916
- }
917
934
  // If this is a preload, add it to the preload cache
918
- if (loaderOpts?.preload && loaderOpts?.maxAge > 0) {
935
+ if (loaderOpts?.preload && minMaxAge > 0) {
919
936
  // If the match is currently active, don't preload it
920
937
  if (router.state.matches.find((d) => d.matchId === match.matchId)) {
921
938
  return
922
939
  }
940
+
923
941
  router.matchCache[match.matchId] = {
924
- gc: now + loaderOpts.maxAge, // TODO: Should this use the route's maxAge?
942
+ gc: now + loaderOpts.gcMaxAge,
925
943
  match,
926
944
  }
927
945
  }
@@ -932,7 +950,9 @@ export function createRouter<
932
950
  match.status === 'error' ||
933
951
  match.status === 'idle'
934
952
  ) {
935
- match.load()
953
+ const maxAge = loaderOpts?.preload ? loaderOpts?.maxAge : undefined
954
+
955
+ match.load({ maxAge })
936
956
  }
937
957
 
938
958
  if (match.status === 'loading') {
@@ -1051,6 +1071,7 @@ export function createRouter<
1051
1071
  activeOptions,
1052
1072
  preload,
1053
1073
  preloadMaxAge: userPreloadMaxAge,
1074
+ preloadGcMaxAge: userPreloadGcMaxAge,
1054
1075
  preloadDelay: userPreloadDelay,
1055
1076
  disabled,
1056
1077
  }) => {
@@ -1079,14 +1100,9 @@ export function createRouter<
1079
1100
 
1080
1101
  const next = router.buildNext(nextOpts)
1081
1102
 
1082
- preload = preload ?? router.options.defaultLinkPreload
1083
- const preloadMaxAge =
1084
- userPreloadMaxAge ??
1085
- router.options.defaultLinkPreloadMaxAge ??
1086
- router.options.defaultLoaderGcMaxAge ??
1087
- 0
1103
+ preload = preload ?? router.options.defaultPreload
1088
1104
  const preloadDelay =
1089
- userPreloadDelay ?? router.options.defaultLinkPreloadDelay ?? 0
1105
+ userPreloadDelay ?? router.options.defaultPreloadDelay ?? 0
1090
1106
 
1091
1107
  // Compare path/hash for matches
1092
1108
  const pathIsEqual = router.state.location.pathname === next.pathname
@@ -1124,22 +1140,28 @@ export function createRouter<
1124
1140
 
1125
1141
  // The click handler
1126
1142
  const handleFocus = (e: MouseEvent) => {
1127
- if (preload && preloadMaxAge > 0) {
1128
- router.loadRoute(nextOpts, { maxAge: preloadMaxAge })
1143
+ if (preload) {
1144
+ router.preloadRoute(nextOpts, {
1145
+ maxAge: userPreloadMaxAge,
1146
+ gcMaxAge: userPreloadGcMaxAge,
1147
+ })
1129
1148
  }
1130
1149
  }
1131
1150
 
1132
1151
  const handleEnter = (e: MouseEvent) => {
1133
1152
  const target = (e.target || {}) as LinkCurrentTargetElement
1134
1153
 
1135
- if (preload && preloadMaxAge > 0) {
1154
+ if (preload) {
1136
1155
  if (target.preloadTimeout) {
1137
1156
  return
1138
1157
  }
1139
1158
 
1140
1159
  target.preloadTimeout = setTimeout(() => {
1141
1160
  target.preloadTimeout = null
1142
- router.loadRoute(nextOpts, { maxAge: preloadMaxAge })
1161
+ router.preloadRoute(nextOpts, {
1162
+ maxAge: userPreloadMaxAge,
1163
+ gcMaxAge: userPreloadGcMaxAge,
1164
+ })
1143
1165
  }, preloadDelay)
1144
1166
  }
1145
1167
  }