@tanstack/router-core 0.0.1-beta.15 → 0.0.1-beta.19

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-beta.15",
4
+ "version": "0.0.1-beta.19",
5
5
  "license": "MIT",
6
6
  "repository": "tanstack/router",
7
7
  "homepage": "https://tanstack.com/router",
package/src/link.ts CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  DefaultAllRouteInfo,
5
5
  RouteInfoByPath,
6
6
  } from './routeInfo'
7
- import { Location } from './router'
7
+ import { Location, LocationState } from './router'
8
8
  import { Expand, NoInfer, PickAsRequired, PickRequired, Updater } from './utils'
9
9
 
10
10
  export type LinkInfo =
@@ -126,6 +126,8 @@ export type ToOptions<
126
126
  to?: ToPathOption<TAllRouteInfo, TFrom, TTo>
127
127
  // The new has string or a function to update it
128
128
  hash?: Updater<string>
129
+ // State to pass to the history stack
130
+ state?: LocationState
129
131
  // The source route path. This is automatically set when using route-level APIs, but for type-safe relative routing on the router itself, this is required
130
132
  from?: TFrom
131
133
  // // When using relative route paths, this option forces resolution from the current path, instead of the route API's path or `from` path
@@ -70,7 +70,7 @@ export interface LoaderContext<
70
70
  params: TAllParams
71
71
  search: TFullSearchSchema
72
72
  signal?: AbortSignal
73
- parentLoaderPromise?: Promise<TParentRouteLoaderData>
73
+ // parentLoaderPromise?: Promise<TParentRouteLoaderData>
74
74
  }
75
75
 
76
76
  export type ActionFn<TActionPayload = unknown, TActionResponse = unknown> = (
@@ -138,9 +138,6 @@ export type RouteOptions<
138
138
  // An asynchronous function made available to the route for performing asynchronous or mutative actions that
139
139
  // might invalidate the route's data.
140
140
  action?: ActionFn<TActionPayload, TActionResponse>
141
- // Set this to true to rethrow errors up the component tree to either the nearest error boundary or
142
- // route with error component, whichever comes first.
143
- useErrorBoundary?: boolean
144
141
  // This function is called
145
142
  // when moving from an inactive state to an active one. Likewise, when moving from
146
143
  // an active to an inactive state, the return function (if provided) is called.
package/src/routeInfo.ts CHANGED
@@ -116,10 +116,10 @@ export interface RoutesInfoInner<
116
116
  any,
117
117
  any
118
118
  > = RouteInfo,
119
- TRouteInfoById = {
119
+ TRouteInfoById = { '/': TRouteInfo } & {
120
120
  [TInfo in TRouteInfo as TInfo['id']]: TInfo
121
121
  },
122
- TRouteInfoByFullPath = {
122
+ TRouteInfoByFullPath = { '/': TRouteInfo } & {
123
123
  [TInfo in TRouteInfo as TInfo['fullPath'] extends RootRouteId
124
124
  ? never
125
125
  : string extends TInfo['fullPath']
package/src/routeMatch.ts CHANGED
@@ -6,8 +6,8 @@ import {
6
6
  DefaultAllRouteInfo,
7
7
  RouteInfo,
8
8
  } from './routeInfo'
9
- import { ActionState, Router } from './router'
10
- import { replaceEqualDeep, Timeout } from './utils'
9
+ import { Router } from './router'
10
+ import { replaceEqualDeep, warning } from './utils'
11
11
 
12
12
  export interface RouteMatch<
13
13
  TAllRouteInfo extends AnyAllRouteInfo = DefaultAllRouteInfo,
@@ -201,8 +201,14 @@ export function createRouteMatch<
201
201
  }
202
202
  },
203
203
  fetch: async (opts) => {
204
- const id = '' + Date.now() + Math.random()
205
- routeMatch.__.latestId = id
204
+ const loadId = '' + Date.now() + Math.random()
205
+ routeMatch.__.latestId = loadId
206
+ const checkLatest = async () => {
207
+ if (loadId !== routeMatch.__.latestId) {
208
+ // warning(true, 'Data loader is out of date!')
209
+ return new Promise(() => {})
210
+ }
211
+ }
206
212
 
207
213
  // If the match was in an error state, set it
208
214
  // to a loading state again. Otherwise, keep it
@@ -240,15 +246,8 @@ export function createRouteMatch<
240
246
  routeMatch.__.dataPromise = Promise.resolve().then(async () => {
241
247
  try {
242
248
  if (routeMatch.options.loader) {
243
- const data = await routeMatch.options.loader({
244
- parentLoaderPromise: routeMatch.parentMatch?.__.dataPromise,
245
- params: routeMatch.params,
246
- search: routeMatch.routeSearch,
247
- signal: routeMatch.__.abortController.signal,
248
- })
249
- if (id !== routeMatch.__.latestId) {
250
- return routeMatch.__.loadPromise
251
- }
249
+ const data = await router.loadMatchData(routeMatch)
250
+ await checkLatest()
252
251
 
253
252
  routeMatch.routeLoaderData = replaceEqualDeep(
254
253
  routeMatch.routeLoaderData,
@@ -268,9 +267,7 @@ export function createRouteMatch<
268
267
 
269
268
  return routeMatch.routeLoaderData
270
269
  } catch (err) {
271
- if (id !== routeMatch.__.latestId) {
272
- return routeMatch.__.loadPromise
273
- }
270
+ await checkLatest()
274
271
 
275
272
  if (process.env.NODE_ENV !== 'production') {
276
273
  console.error(err)
@@ -284,30 +281,26 @@ export function createRouteMatch<
284
281
  }
285
282
  })
286
283
 
284
+ const after = async () => {
285
+ await checkLatest()
286
+ routeMatch.isFetching = false
287
+ delete routeMatch.__.loadPromise
288
+ routeMatch.__.notify()
289
+ }
290
+
287
291
  try {
288
292
  await Promise.all([
289
293
  routeMatch.__.componentsPromise,
290
294
  routeMatch.__.dataPromise.catch(() => {}),
291
295
  ])
292
- if (id !== routeMatch.__.latestId) {
293
- return routeMatch.__.loadPromise
294
- }
295
- } finally {
296
- if (id !== routeMatch.__.latestId) {
297
- return routeMatch.__.loadPromise
298
- }
299
- routeMatch.isFetching = false
300
- routeMatch.__.notify()
296
+ after()
297
+ } catch {
298
+ after()
301
299
  }
302
300
  })
303
301
 
304
302
  await routeMatch.__.loadPromise
305
-
306
- if (id !== routeMatch.__.latestId) {
307
- return routeMatch.__.loadPromise
308
- }
309
-
310
- delete routeMatch.__.loadPromise
303
+ await checkLatest()
311
304
  },
312
305
  }
313
306
 
package/src/router.ts CHANGED
@@ -6,7 +6,6 @@ import {
6
6
  History,
7
7
  MemoryHistory,
8
8
  } from 'history'
9
- import React from 'react'
10
9
  import invariant from 'tiny-invariant'
11
10
  import { GetFrameworkGeneric } from './frameworks'
12
11
 
@@ -91,7 +90,6 @@ export interface RouterOptions<TRouteConfig extends AnyRouteConfig> {
91
90
  defaultPreloadMaxAge?: number
92
91
  defaultPreloadGcMaxAge?: number
93
92
  defaultPreloadDelay?: number
94
- useErrorBoundary?: boolean
95
93
  defaultComponent?: GetFrameworkGeneric<'Component'>
96
94
  defaultErrorComponent?: GetFrameworkGeneric<'Component'>
97
95
  defaultPendingComponent?: GetFrameworkGeneric<'Component'>
@@ -100,6 +98,7 @@ export interface RouterOptions<TRouteConfig extends AnyRouteConfig> {
100
98
  caseSensitive?: boolean
101
99
  routeConfig?: TRouteConfig
102
100
  basepath?: string
101
+ useServerData?: boolean
103
102
  createRouter?: (router: Router<any, any>) => void
104
103
  createRoute?: (opts: { route: AnyRoute; router: Router<any, any> }) => void
105
104
  loadComponent?: (
@@ -166,11 +165,11 @@ export interface Loader<
166
165
  }
167
166
 
168
167
  export interface LoaderState<
169
- TFullSearchSchema = unknown,
170
- TAllParams = unknown,
168
+ TFullSearchSchema extends AnySearchSchema = {},
169
+ TAllParams extends AnyPathParams = {},
171
170
  > {
172
171
  loadedAt: number
173
- loaderContext: LoaderContext<TFullSearchSchema, TAllParams>
172
+ loaderContext: LoaderContext<AnyLoaderData, TFullSearchSchema, TAllParams>
174
173
  }
175
174
 
176
175
  export interface RouterState {
@@ -199,6 +198,7 @@ export interface BuildNextOptions {
199
198
  params?: true | Updater<Record<string, any>>
200
199
  search?: true | Updater<unknown>
201
200
  hash?: true | Updater<string>
201
+ state?: LocationState
202
202
  key?: string
203
203
  from?: string
204
204
  fromCurrent?: boolean
@@ -248,6 +248,13 @@ export interface Router<
248
248
  TRouteConfig extends AnyRouteConfig = RouteConfig,
249
249
  TAllRouteInfo extends AnyAllRouteInfo = AllRouteInfo<TRouteConfig>,
250
250
  > {
251
+ types: {
252
+ // Super secret internal stuff
253
+ RouteConfig: TRouteConfig
254
+ AllRouteInfo: TAllRouteInfo
255
+ }
256
+
257
+ // Public API
251
258
  history: BrowserHistory | MemoryHistory | HashHistory
252
259
  options: PickAsRequired<
253
260
  RouterOptions<TRouteConfig>,
@@ -256,7 +263,6 @@ export interface Router<
256
263
  // Computed in this.update()
257
264
  basepath: string
258
265
  // Internal:
259
- allRouteInfo: TAllRouteInfo
260
266
  listeners: Listener[]
261
267
  location: Location
262
268
  navigateTimeout?: Timeout
@@ -299,6 +305,9 @@ export interface Router<
299
305
  | { preload: true; maxAge: number; gcMaxAge: number }
300
306
  | { preload?: false; maxAge?: never; gcMaxAge?: never },
301
307
  ) => Promise<void>
308
+ loadMatchData: (
309
+ routeMatch: RouteMatch<any, any>,
310
+ ) => Promise<Record<string, unknown>>
302
311
  invalidateRoute: (opts: MatchLocation) => void
303
312
  reload: () => Promise<void>
304
313
  resolvePath: (from: string, path: string) => string
@@ -379,6 +388,9 @@ export function createRouter<
379
388
  }
380
389
 
381
390
  let router: Router<TRouteConfig, TAllRouteInfo> = {
391
+ types: undefined!,
392
+
393
+ // public api
382
394
  history,
383
395
  options: originalOptions,
384
396
  listeners: [],
@@ -387,7 +399,6 @@ export function createRouter<
387
399
  routeTree: undefined!,
388
400
  routesById: {} as any,
389
401
  location: undefined!,
390
- allRouteInfo: undefined!,
391
402
  //
392
403
  navigationPromise: Promise.resolve(),
393
404
  resolveNavigation: () => {},
@@ -840,6 +851,12 @@ export function createRouter<
840
851
  match.__.validate()
841
852
  match.load(loaderOpts)
842
853
 
854
+ const search = match.search as { __data?: any }
855
+
856
+ if (search.__data && search.__data.matchId !== match.matchId) {
857
+ return
858
+ }
859
+
843
860
  if (match.__.loadPromise) {
844
861
  // Wait for the first sign of activity from the match
845
862
  await match.__.loadPromise
@@ -851,6 +868,40 @@ export function createRouter<
851
868
  await Promise.all(matchPromises)
852
869
  },
853
870
 
871
+ loadMatchData: async (routeMatch) => {
872
+ if (isServer || !router.options.useServerData) {
873
+ return (
874
+ (await routeMatch.options.loader?.({
875
+ // parentLoaderPromise: routeMatch.parentMatch?.__.dataPromise,
876
+ params: routeMatch.params,
877
+ search: routeMatch.routeSearch,
878
+ signal: routeMatch.__.abortController.signal,
879
+ })) ?? {}
880
+ )
881
+ } else {
882
+ const next = router.buildNext({
883
+ to: '.',
884
+ search: (d: any) => ({
885
+ ...(d ?? {}),
886
+ __data: {
887
+ matchId: routeMatch.matchId,
888
+ },
889
+ }),
890
+ })
891
+
892
+ const res = await fetch(next.href, {
893
+ method: 'GET',
894
+ // signal: routeMatch.__.abortController.signal,
895
+ })
896
+
897
+ if (res.ok) {
898
+ return res.json()
899
+ }
900
+
901
+ throw new Error('Failed to fetch match data')
902
+ }
903
+ },
904
+
854
905
  invalidateRoute: (opts: MatchLocation) => {
855
906
  const next = router.buildNext(opts)
856
907
  const unloadedMatchIds = router
@@ -1258,6 +1309,7 @@ export function createRouter<
1258
1309
  },
1259
1310
  {
1260
1311
  id,
1312
+ ...next.state,
1261
1313
  },
1262
1314
  )
1263
1315
  } else {