@tanstack/router-core 0.0.1-beta.165 → 0.0.1-beta.167

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.165",
4
+ "version": "0.0.1-beta.167",
5
5
  "license": "MIT",
6
6
  "repository": "tanstack/router",
7
7
  "homepage": "https://tanstack.com/router",
@@ -43,7 +43,7 @@
43
43
  "tiny-invariant": "^1.3.1",
44
44
  "tiny-warning": "^1.0.3",
45
45
  "@gisatcz/cross-package-react-context": "^0.2.0",
46
- "@tanstack/react-store": "0.0.1-beta.165"
46
+ "@tanstack/react-store": "0.0.1-beta.167"
47
47
  },
48
48
  "scripts": {
49
49
  "build": "rollup --config rollup.config.js",
package/src/defer.ts ADDED
@@ -0,0 +1,55 @@
1
+ export type DeferredPromiseState<T> = { uid: string } & (
2
+ | {
3
+ status: 'pending'
4
+ data?: T
5
+ error?: unknown
6
+ }
7
+ | {
8
+ status: 'success'
9
+ data: T
10
+ }
11
+ | {
12
+ status: 'error'
13
+ data?: T
14
+ error: unknown
15
+ }
16
+ )
17
+
18
+ export type DeferredPromise<T> = Promise<T> & {
19
+ __deferredState: DeferredPromiseState<T>
20
+ }
21
+
22
+ export function defer<T>(_promise: Promise<T>) {
23
+ const promise = _promise as DeferredPromise<T>
24
+
25
+ if (!promise.__deferredState) {
26
+ promise.__deferredState = {
27
+ uid: Math.random().toString(36).slice(2),
28
+ status: 'pending',
29
+ }
30
+
31
+ const state = promise.__deferredState
32
+
33
+ promise
34
+ .then((data) => {
35
+ state.status = 'success' as any
36
+ state.data = data
37
+ })
38
+ .catch((error) => {
39
+ state.status = 'error' as any
40
+ state.error = error
41
+ })
42
+ }
43
+
44
+ return promise
45
+ }
46
+
47
+ export function isDehydratedDeferred(obj: any): boolean {
48
+ return (
49
+ typeof obj === 'object' &&
50
+ obj !== null &&
51
+ !(obj instanceof Promise) &&
52
+ !obj.then &&
53
+ '__deferredState' in obj
54
+ )
55
+ }
package/src/index.ts CHANGED
@@ -11,3 +11,4 @@ export * from './router'
11
11
  export * from './searchParams'
12
12
  export * from './utils'
13
13
  export * from './scroll-restoration'
14
+ export * from './defer'
package/src/route.ts CHANGED
@@ -17,6 +17,32 @@ export interface RegisterRouteComponent<TProps> {
17
17
  export interface RegisterRouteErrorComponent<TProps> {
18
18
  // RouteErrorComponent: unknown // This is registered by the framework
19
19
  }
20
+ export interface RegisterRouteProps<
21
+ TLoader = unknown,
22
+ TFullSearchSchema extends AnySearchSchema = AnySearchSchema,
23
+ TAllParams extends AnyPathParams = AnyPathParams,
24
+ TRouteContext extends AnyContext = AnyContext,
25
+ TAllContext extends AnyContext = AnyContext,
26
+ > {
27
+ // RouteProps: unknown // This is registered by the framework
28
+ }
29
+ export interface RegisterErrorRouteProps<
30
+ TFullSearchSchema extends AnySearchSchema = AnySearchSchema,
31
+ TAllParams extends AnyPathParams = AnyPathParams,
32
+ TRouteContext extends AnyContext = AnyContext,
33
+ TAllContext extends AnyContext = AnyContext,
34
+ > {
35
+ // ErrorRouteProps: unknown // This is registered by the framework
36
+ }
37
+
38
+ export interface RegisterPendingRouteProps<
39
+ TFullSearchSchema extends AnySearchSchema = AnySearchSchema,
40
+ TAllParams extends AnyPathParams = AnyPathParams,
41
+ TRouteContext extends AnyContext = AnyContext,
42
+ TAllContext extends AnyContext = AnyContext,
43
+ > {
44
+ // PendingRouteProps: unknown // This is registered by the framework
45
+ }
20
46
 
21
47
  export type RegisteredRouteComponent<TProps> =
22
48
  RegisterRouteComponent<TProps> extends {
@@ -32,6 +58,56 @@ export type RegisteredRouteErrorComponent<TProps> =
32
58
  ? T
33
59
  : (props: TProps) => unknown
34
60
 
61
+ export type RegisteredRouteProps<
62
+ TLoader = unknown,
63
+ TFullSearchSchema extends AnySearchSchema = AnySearchSchema,
64
+ TAllParams extends AnyPathParams = AnyPathParams,
65
+ TRouteContext extends AnyContext = AnyContext,
66
+ TAllContext extends AnyContext = AnyContext,
67
+ > = RegisterRouteProps<
68
+ TLoader,
69
+ TFullSearchSchema,
70
+ TAllParams,
71
+ TRouteContext,
72
+ TAllContext
73
+ > extends {
74
+ RouteProps: infer T
75
+ }
76
+ ? T
77
+ : (props: {}) => unknown
78
+
79
+ export type RegisteredErrorRouteProps<
80
+ TFullSearchSchema extends AnySearchSchema = AnySearchSchema,
81
+ TAllParams extends AnyPathParams = AnyPathParams,
82
+ TRouteContext extends AnyContext = AnyContext,
83
+ TAllContext extends AnyContext = AnyContext,
84
+ > = RegisterRouteProps<
85
+ TFullSearchSchema,
86
+ TAllParams,
87
+ TRouteContext,
88
+ TAllContext
89
+ > extends {
90
+ ErrorRouteProps: infer T
91
+ }
92
+ ? T
93
+ : (props: {}) => unknown
94
+
95
+ export type RegisteredPendingRouteProps<
96
+ TFullSearchSchema extends AnySearchSchema = AnySearchSchema,
97
+ TAllParams extends AnyPathParams = AnyPathParams,
98
+ TRouteContext extends AnyContext = AnyContext,
99
+ TAllContext extends AnyContext = AnyContext,
100
+ > = RegisterRouteProps<
101
+ TFullSearchSchema,
102
+ TAllParams,
103
+ TRouteContext,
104
+ TAllContext
105
+ > extends {
106
+ PendingRouteProps: infer T
107
+ }
108
+ ? T
109
+ : (props: {}) => unknown
110
+
35
111
  export type PreloadableObj = { preload?: () => Promise<void> }
36
112
 
37
113
  export type RoutePathOptions<TCustomId, TPath> =
@@ -53,7 +129,7 @@ export type MetaOptions = keyof PickRequired<RouteMeta> extends never
53
129
  meta: RouteMeta
54
130
  }
55
131
 
56
- export type AnyRouteProps = RouteProps<any, any, any, any, any>
132
+ export type AnyRouteProps = RegisteredRouteProps<any, any, any, any, any>
57
133
  export type ComponentPropsFromRoute<TRoute> = TRoute extends Route<
58
134
  infer TParentRoute,
59
135
  infer TPath,
@@ -73,7 +149,13 @@ export type ComponentPropsFromRoute<TRoute> = TRoute extends Route<
73
149
  infer TChildren,
74
150
  infer TRouteTree
75
151
  >
76
- ? RouteProps<TLoader, TFullSearchSchema, TAllParams, TRouteContext, TContext>
152
+ ? RegisteredRouteProps<
153
+ TLoader,
154
+ TFullSearchSchema,
155
+ TAllParams,
156
+ TRouteContext,
157
+ TContext
158
+ >
77
159
  : never
78
160
 
79
161
  export type ComponentFromRoute<TRoute> = RegisteredRouteComponent<
@@ -89,43 +171,6 @@ export type RouteLoaderFromRoute<TRoute extends AnyRoute> = LoaderFn<
89
171
  TRoute['types']['context']
90
172
  >
91
173
 
92
- export type RouteProps<
93
- TLoader extends any = unknown,
94
- TFullSearchSchema extends AnySearchSchema = AnySearchSchema,
95
- TAllParams extends AnyPathParams = AnyPathParams,
96
- TRouteContext extends AnyContext = AnyContext,
97
- TContext extends AnyContext = AnyContext,
98
- > = {
99
- useMatch: () => RouteMatch<any, any>
100
- useLoader: () => UseLoaderResult<TLoader>
101
- useSearch: <
102
- TStrict extends boolean = true,
103
- TSearch = TFullSearchSchema,
104
- TSelected = TSearch,
105
- >(opts?: {
106
- strict?: TStrict
107
- select?: (search: TSearch) => TSelected
108
- }) => TStrict extends true ? TSelected : TSelected | undefined
109
- useParams: <
110
- TDefaultSelected = TAllParams,
111
- TSelected = TDefaultSelected,
112
- >(opts?: {
113
- select?: (params: TDefaultSelected) => TSelected
114
- }) => TSelected
115
- useContext: <
116
- TDefaultSelected = TContext,
117
- TSelected = TDefaultSelected,
118
- >(opts?: {
119
- select?: (context: TDefaultSelected) => TSelected
120
- }) => TSelected
121
- useRouteContext: <
122
- TDefaultSelected = TRouteContext,
123
- TSelected = TDefaultSelected,
124
- >(opts?: {
125
- select?: (context: TDefaultSelected) => TSelected
126
- }) => TSelected
127
- }
128
-
129
174
  export type RouteOptions<
130
175
  TParentRoute extends AnyRoute = AnyRoute,
131
176
  TCustomId extends string = string,
@@ -271,7 +316,7 @@ export type UpdatableRouteOptions<
271
316
  TFullSearchSchema extends AnySearchSchema,
272
317
  TAllParams extends AnyPathParams,
273
318
  TRouteContext extends AnyContext,
274
- TContext extends AnyContext,
319
+ TAllContext extends AnyContext,
275
320
  > = MetaOptions & {
276
321
  key?: null | false | GetKeyFn<TFullSearchSchema, TAllParams>
277
322
  // If true, this route will be matched as case-sensitive
@@ -280,23 +325,31 @@ export type UpdatableRouteOptions<
280
325
  wrapInSuspense?: boolean
281
326
  // The content to be rendered when the route is matched. If no component is provided, defaults to `<Outlet />`
282
327
  component?: RegisteredRouteComponent<
283
- RouteProps<TLoader, TFullSearchSchema, TAllParams, TRouteContext, TContext>
328
+ RegisteredRouteProps<
329
+ TLoader,
330
+ TFullSearchSchema,
331
+ TAllParams,
332
+ TRouteContext,
333
+ TAllContext
334
+ >
284
335
  >
285
336
  // The content to be rendered when the route encounters an error
286
337
  errorComponent?: RegisteredRouteErrorComponent<
287
- { error: unknown } & Partial<
288
- RouteProps<
289
- TLoader,
290
- TFullSearchSchema,
291
- TAllParams,
292
- TRouteContext,
293
- TContext
294
- >
338
+ RegisteredErrorRouteProps<
339
+ TFullSearchSchema,
340
+ TAllParams,
341
+ TRouteContext,
342
+ TAllContext
295
343
  >
296
344
  > //
297
345
  // If supported by your framework, the content to be rendered as the fallback content until the route is ready to render
298
346
  pendingComponent?: RegisteredRouteComponent<
299
- RouteProps<TLoader, TFullSearchSchema, TAllParams, TRouteContext, TContext>
347
+ RegisteredPendingRouteProps<
348
+ TFullSearchSchema,
349
+ TAllParams,
350
+ TRouteContext,
351
+ TAllContext
352
+ >
300
353
  >
301
354
  // Filter functions that can manipulate search params *before* they are passed to links and navigate
302
355
  // calls that match this route.
@@ -320,7 +373,7 @@ export type UpdatableRouteOptions<
320
373
  TFullSearchSchema,
321
374
  TAllParams,
322
375
  NoInfer<TRouteContext>,
323
- TContext
376
+ TAllContext
324
377
  >,
325
378
  ) => Promise<void> | void
326
379
  onError?: (err: any) => void
@@ -473,15 +526,16 @@ export interface AnyRoute
473
526
 
474
527
  export type MergeParamsFromParent<T, U> = IsAny<T, U, T & U>
475
528
 
476
- export type UseLoaderResult<T> = T extends Record<PropertyKey, infer U>
477
- ? {
478
- [K in keyof T]: UseLoaderResultPromise<T[K]>
479
- }
480
- : UseLoaderResultPromise<T>
529
+ export type UseLoaderResult<T> = T
530
+ // T extends Record<PropertyKey, infer U>
531
+ // ? {
532
+ // [K in keyof T]: UseLoaderResultPromise<T[K]>
533
+ // }
534
+ // : UseLoaderResultPromise<T>
481
535
 
482
- export type UseLoaderResultPromise<T> = T extends Promise<infer U>
483
- ? StreamedPromise<U>
484
- : T
536
+ // export type UseLoaderResultPromise<T> = T extends Promise<infer U>
537
+ // ? StreamedPromise<U>
538
+ // : T
485
539
 
486
540
  export type StreamedPromise<T> = {
487
541
  promise: Promise<T>
package/src/router.ts CHANGED
@@ -26,9 +26,10 @@ import {
26
26
  AnyRoute,
27
27
  AnyContext,
28
28
  AnyPathParams,
29
- RouteProps,
30
29
  RegisteredRouteComponent,
31
30
  RegisteredRouteErrorComponent,
31
+ RegisteredRouteProps,
32
+ RegisteredErrorRouteProps,
32
33
  } from './route'
33
34
  import {
34
35
  RoutesById,
@@ -156,13 +157,30 @@ export interface RouterOptions<
156
157
  defaultPreload?: false | 'intent'
157
158
  defaultPreloadDelay?: number
158
159
  defaultComponent?: RegisteredRouteComponent<
159
- RouteProps<unknown, AnySearchSchema, AnyPathParams, AnyContext, AnyContext>
160
+ RegisteredRouteProps<
161
+ unknown,
162
+ AnySearchSchema,
163
+ AnyPathParams,
164
+ AnyContext,
165
+ AnyContext
166
+ >
160
167
  >
161
168
  defaultErrorComponent?: RegisteredRouteErrorComponent<
162
- RouteProps<unknown, AnySearchSchema, AnyPathParams, AnyContext, AnyContext>
169
+ RegisteredErrorRouteProps<
170
+ AnySearchSchema,
171
+ AnyPathParams,
172
+ AnyContext,
173
+ AnyContext
174
+ >
163
175
  >
164
176
  defaultPendingComponent?: RegisteredRouteComponent<
165
- RouteProps<unknown, AnySearchSchema, AnyPathParams, AnyContext, AnyContext>
177
+ RegisteredRouteProps<
178
+ unknown,
179
+ AnySearchSchema,
180
+ AnyPathParams,
181
+ AnyContext,
182
+ AnyContext
183
+ >
166
184
  >
167
185
  defaultMaxAge?: number
168
186
  defaultGcMaxAge?: number
@@ -229,8 +247,20 @@ type LinkCurrentTargetElement = {
229
247
  preloadTimeout?: null | ReturnType<typeof setTimeout>
230
248
  }
231
249
 
232
- export interface DehydratedRouterState
233
- extends Pick<RouterState, 'status' | 'location' | 'lastUpdated'> {}
250
+ export interface DehydratedRouterState {
251
+ dehydratedMatches: DehydratedRouteMatch[]
252
+ }
253
+
254
+ export type DehydratedRouteMatch = Pick<
255
+ RouteMatch,
256
+ | 'fetchedAt'
257
+ | 'invalid'
258
+ | 'invalidAt'
259
+ | 'id'
260
+ | 'loaderData'
261
+ | 'status'
262
+ | 'updatedAt'
263
+ >
234
264
 
235
265
  export interface DehydratedRouter {
236
266
  state: DehydratedRouterState
@@ -443,7 +473,7 @@ export class Router<
443
473
  this.basepath = `/${trimPath(basepath ?? '') ?? ''}`
444
474
 
445
475
  if (routeTree && routeTree !== this.routeTree) {
446
- this.#buildRouteTree(routeTree)
476
+ this.#processRoutes(routeTree)
447
477
  }
448
478
 
449
479
  return this
@@ -480,7 +510,11 @@ export class Router<
480
510
 
481
511
  latestLoadPromise: Promise<void> = Promise.resolve()
482
512
 
483
- load = async (opts?: { next?: ParsedLocation; throwOnError?: boolean }) => {
513
+ load = async (opts?: {
514
+ next?: ParsedLocation
515
+ throwOnError?: boolean
516
+ __dehydratedMatches?: DehydratedRouteMatch[]
517
+ }) => {
484
518
  const promise = new Promise<void>(async (resolve, reject) => {
485
519
  const prevLocation = this.state.resolvedLocation
486
520
  const pathDidChange = !!(
@@ -769,8 +803,8 @@ export class Router<
769
803
  params: routeParams,
770
804
  pathname: joinPaths([this.basepath, interpolatedPath]),
771
805
  updatedAt: Date.now(),
772
- invalidAt: Infinity,
773
- preloadInvalidAt: Infinity,
806
+ invalidAt: 9999999999999,
807
+ preloadInvalidAt: 9999999999999,
774
808
  routeSearch: {},
775
809
  search: {} as any,
776
810
  status: hasLoaders ? 'pending' : 'success',
@@ -1312,7 +1346,19 @@ export class Router<
1312
1346
 
1313
1347
  dehydrate = (): DehydratedRouter => {
1314
1348
  return {
1315
- state: pick(this.state, ['location', 'status', 'lastUpdated']),
1349
+ state: {
1350
+ dehydratedMatches: this.state.matches.map((d) =>
1351
+ pick(d, [
1352
+ 'fetchedAt',
1353
+ 'invalid',
1354
+ 'invalidAt',
1355
+ 'id',
1356
+ 'loaderData',
1357
+ 'status',
1358
+ 'updatedAt',
1359
+ ]),
1360
+ ),
1361
+ },
1316
1362
  }
1317
1363
  }
1318
1364
 
@@ -1331,19 +1377,35 @@ export class Router<
1331
1377
  const ctx = _ctx
1332
1378
  this.dehydratedData = ctx.payload as any
1333
1379
  this.options.hydrate?.(ctx.payload as any)
1334
- const routerState = ctx.router.state as RouterState<TRouteTree>
1380
+ const { dehydratedMatches } = ctx.router.state
1381
+
1382
+ let matches = this.matchRoutes(
1383
+ this.state.location.pathname,
1384
+ this.state.location.search,
1385
+ ).map((match) => {
1386
+ const dehydratedMatch = dehydratedMatches.find((d) => d.id === match.id)
1387
+
1388
+ invariant(
1389
+ dehydratedMatch,
1390
+ `Could not find a client-side match for dehydrated match with id: ${match.id}!`,
1391
+ )
1392
+
1393
+ if (dehydratedMatch) {
1394
+ return {
1395
+ ...match,
1396
+ ...dehydratedMatch,
1397
+ }
1398
+ }
1399
+ return match
1400
+ })
1335
1401
 
1336
1402
  this.__store.setState((s) => {
1337
1403
  return {
1338
1404
  ...s,
1339
- ...routerState,
1340
- resolvedLocation: routerState.location,
1405
+ matches,
1406
+ matchesById: this.#mergeMatches(s.matchesById, matches),
1341
1407
  }
1342
1408
  })
1343
-
1344
- await this.load()
1345
-
1346
- return
1347
1409
  }
1348
1410
 
1349
1411
  injectedHtml: (string | (() => Promise<string> | string))[] = []
@@ -1363,10 +1425,10 @@ export class Router<
1363
1425
  return `<script id='${id}' suppressHydrationWarning>window["__TSR_DEHYDRATED__${escapeJSON(
1364
1426
  strKey,
1365
1427
  )}"] = ${JSON.stringify(data)}
1366
- ;(() => {
1367
- var el = document.getElementById('${id}')
1368
- el.parentElement.removeChild(el)
1369
- })()
1428
+ // ;(() => {
1429
+ // var el = document.getElementById('${id}')
1430
+ // el.parentElement.removeChild(el)
1431
+ // })()
1370
1432
  </script>`
1371
1433
  })
1372
1434
 
@@ -1392,7 +1454,7 @@ export class Router<
1392
1454
  // ?.__promisesByKey[key]?.resolve(value)
1393
1455
  // }
1394
1456
 
1395
- #buildRouteTree = (routeTree: TRouteTree) => {
1457
+ #processRoutes = (routeTree: TRouteTree) => {
1396
1458
  this.routeTree = routeTree as any
1397
1459
  this.routesById = {} as any
1398
1460
  this.routesByPath = {} as any
@@ -1704,7 +1766,7 @@ export class Router<
1704
1766
  (opts?.maxAge ??
1705
1767
  route.options.maxAge ??
1706
1768
  this.options.defaultMaxAge ??
1707
- Infinity)
1769
+ 9999999999999)
1708
1770
 
1709
1771
  this.setRouteMatch(id, (s) => ({
1710
1772
  ...s,