@tanstack/router-core 0.0.1-beta.166 → 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.166",
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.166"
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,67 +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
- export type PendingRouteProps<
130
- TFullSearchSchema extends AnySearchSchema = AnySearchSchema,
131
- TAllParams extends AnyPathParams = AnyPathParams,
132
- TRouteContext extends AnyContext = AnyContext,
133
- TContext extends AnyContext = AnyContext,
134
- > = Omit<
135
- RouteProps<any, TFullSearchSchema, TAllParams, TRouteContext, TContext>,
136
- 'useLoader'
137
- >
138
-
139
- export type ErrorRouteProps<
140
- TFullSearchSchema extends AnySearchSchema = AnySearchSchema,
141
- TAllParams extends AnyPathParams = AnyPathParams,
142
- TRouteContext extends AnyContext = AnyContext,
143
- TContext extends AnyContext = AnyContext,
144
- > = PendingRouteProps<
145
- TFullSearchSchema,
146
- TAllParams,
147
- TRouteContext,
148
- TContext
149
- > & {
150
- error: unknown
151
- }
152
-
153
174
  export type RouteOptions<
154
175
  TParentRoute extends AnyRoute = AnyRoute,
155
176
  TCustomId extends string = string,
@@ -295,7 +316,7 @@ export type UpdatableRouteOptions<
295
316
  TFullSearchSchema extends AnySearchSchema,
296
317
  TAllParams extends AnyPathParams,
297
318
  TRouteContext extends AnyContext,
298
- TContext extends AnyContext,
319
+ TAllContext extends AnyContext,
299
320
  > = MetaOptions & {
300
321
  key?: null | false | GetKeyFn<TFullSearchSchema, TAllParams>
301
322
  // If true, this route will be matched as case-sensitive
@@ -304,15 +325,31 @@ export type UpdatableRouteOptions<
304
325
  wrapInSuspense?: boolean
305
326
  // The content to be rendered when the route is matched. If no component is provided, defaults to `<Outlet />`
306
327
  component?: RegisteredRouteComponent<
307
- RouteProps<TLoader, TFullSearchSchema, TAllParams, TRouteContext, TContext>
328
+ RegisteredRouteProps<
329
+ TLoader,
330
+ TFullSearchSchema,
331
+ TAllParams,
332
+ TRouteContext,
333
+ TAllContext
334
+ >
308
335
  >
309
336
  // The content to be rendered when the route encounters an error
310
337
  errorComponent?: RegisteredRouteErrorComponent<
311
- ErrorRouteProps<TFullSearchSchema, TAllParams, TRouteContext, TContext>
338
+ RegisteredErrorRouteProps<
339
+ TFullSearchSchema,
340
+ TAllParams,
341
+ TRouteContext,
342
+ TAllContext
343
+ >
312
344
  > //
313
345
  // If supported by your framework, the content to be rendered as the fallback content until the route is ready to render
314
346
  pendingComponent?: RegisteredRouteComponent<
315
- PendingRouteProps<TFullSearchSchema, TAllParams, TRouteContext, TContext>
347
+ RegisteredPendingRouteProps<
348
+ TFullSearchSchema,
349
+ TAllParams,
350
+ TRouteContext,
351
+ TAllContext
352
+ >
316
353
  >
317
354
  // Filter functions that can manipulate search params *before* they are passed to links and navigate
318
355
  // calls that match this route.
@@ -336,7 +373,7 @@ export type UpdatableRouteOptions<
336
373
  TFullSearchSchema,
337
374
  TAllParams,
338
375
  NoInfer<TRouteContext>,
339
- TContext
376
+ TAllContext
340
377
  >,
341
378
  ) => Promise<void> | void
342
379
  onError?: (err: any) => void
@@ -489,15 +526,16 @@ export interface AnyRoute
489
526
 
490
527
  export type MergeParamsFromParent<T, U> = IsAny<T, U, T & U>
491
528
 
492
- export type UseLoaderResult<T> = T extends Record<PropertyKey, infer U>
493
- ? {
494
- [K in keyof T]: UseLoaderResultPromise<T[K]>
495
- }
496
- : 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>
497
535
 
498
- export type UseLoaderResultPromise<T> = T extends Promise<infer U>
499
- ? StreamedPromise<U>
500
- : T
536
+ // export type UseLoaderResultPromise<T> = T extends Promise<infer U>
537
+ // ? StreamedPromise<U>
538
+ // : T
501
539
 
502
540
  export type StreamedPromise<T> = {
503
541
  promise: Promise<T>
package/src/router.ts CHANGED
@@ -26,10 +26,10 @@ import {
26
26
  AnyRoute,
27
27
  AnyContext,
28
28
  AnyPathParams,
29
- RouteProps,
30
29
  RegisteredRouteComponent,
31
30
  RegisteredRouteErrorComponent,
32
- ErrorRouteProps,
31
+ RegisteredRouteProps,
32
+ RegisteredErrorRouteProps,
33
33
  } from './route'
34
34
  import {
35
35
  RoutesById,
@@ -157,13 +157,30 @@ export interface RouterOptions<
157
157
  defaultPreload?: false | 'intent'
158
158
  defaultPreloadDelay?: number
159
159
  defaultComponent?: RegisteredRouteComponent<
160
- RouteProps<unknown, AnySearchSchema, AnyPathParams, AnyContext, AnyContext>
160
+ RegisteredRouteProps<
161
+ unknown,
162
+ AnySearchSchema,
163
+ AnyPathParams,
164
+ AnyContext,
165
+ AnyContext
166
+ >
161
167
  >
162
168
  defaultErrorComponent?: RegisteredRouteErrorComponent<
163
- ErrorRouteProps<AnySearchSchema, AnyPathParams, AnyContext, AnyContext>
169
+ RegisteredErrorRouteProps<
170
+ AnySearchSchema,
171
+ AnyPathParams,
172
+ AnyContext,
173
+ AnyContext
174
+ >
164
175
  >
165
176
  defaultPendingComponent?: RegisteredRouteComponent<
166
- RouteProps<unknown, AnySearchSchema, AnyPathParams, AnyContext, AnyContext>
177
+ RegisteredRouteProps<
178
+ unknown,
179
+ AnySearchSchema,
180
+ AnyPathParams,
181
+ AnyContext,
182
+ AnyContext
183
+ >
167
184
  >
168
185
  defaultMaxAge?: number
169
186
  defaultGcMaxAge?: number
@@ -230,8 +247,20 @@ type LinkCurrentTargetElement = {
230
247
  preloadTimeout?: null | ReturnType<typeof setTimeout>
231
248
  }
232
249
 
233
- export interface DehydratedRouterState
234
- 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
+ >
235
264
 
236
265
  export interface DehydratedRouter {
237
266
  state: DehydratedRouterState
@@ -444,7 +473,7 @@ export class Router<
444
473
  this.basepath = `/${trimPath(basepath ?? '') ?? ''}`
445
474
 
446
475
  if (routeTree && routeTree !== this.routeTree) {
447
- this.#buildRouteTree(routeTree)
476
+ this.#processRoutes(routeTree)
448
477
  }
449
478
 
450
479
  return this
@@ -481,7 +510,11 @@ export class Router<
481
510
 
482
511
  latestLoadPromise: Promise<void> = Promise.resolve()
483
512
 
484
- load = async (opts?: { next?: ParsedLocation; throwOnError?: boolean }) => {
513
+ load = async (opts?: {
514
+ next?: ParsedLocation
515
+ throwOnError?: boolean
516
+ __dehydratedMatches?: DehydratedRouteMatch[]
517
+ }) => {
485
518
  const promise = new Promise<void>(async (resolve, reject) => {
486
519
  const prevLocation = this.state.resolvedLocation
487
520
  const pathDidChange = !!(
@@ -770,8 +803,8 @@ export class Router<
770
803
  params: routeParams,
771
804
  pathname: joinPaths([this.basepath, interpolatedPath]),
772
805
  updatedAt: Date.now(),
773
- invalidAt: Infinity,
774
- preloadInvalidAt: Infinity,
806
+ invalidAt: 9999999999999,
807
+ preloadInvalidAt: 9999999999999,
775
808
  routeSearch: {},
776
809
  search: {} as any,
777
810
  status: hasLoaders ? 'pending' : 'success',
@@ -1313,7 +1346,19 @@ export class Router<
1313
1346
 
1314
1347
  dehydrate = (): DehydratedRouter => {
1315
1348
  return {
1316
- 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
+ },
1317
1362
  }
1318
1363
  }
1319
1364
 
@@ -1332,19 +1377,35 @@ export class Router<
1332
1377
  const ctx = _ctx
1333
1378
  this.dehydratedData = ctx.payload as any
1334
1379
  this.options.hydrate?.(ctx.payload as any)
1335
- 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
+ })
1336
1401
 
1337
1402
  this.__store.setState((s) => {
1338
1403
  return {
1339
1404
  ...s,
1340
- ...routerState,
1341
- resolvedLocation: routerState.location,
1405
+ matches,
1406
+ matchesById: this.#mergeMatches(s.matchesById, matches),
1342
1407
  }
1343
1408
  })
1344
-
1345
- await this.load()
1346
-
1347
- return
1348
1409
  }
1349
1410
 
1350
1411
  injectedHtml: (string | (() => Promise<string> | string))[] = []
@@ -1364,10 +1425,10 @@ export class Router<
1364
1425
  return `<script id='${id}' suppressHydrationWarning>window["__TSR_DEHYDRATED__${escapeJSON(
1365
1426
  strKey,
1366
1427
  )}"] = ${JSON.stringify(data)}
1367
- ;(() => {
1368
- var el = document.getElementById('${id}')
1369
- el.parentElement.removeChild(el)
1370
- })()
1428
+ // ;(() => {
1429
+ // var el = document.getElementById('${id}')
1430
+ // el.parentElement.removeChild(el)
1431
+ // })()
1371
1432
  </script>`
1372
1433
  })
1373
1434
 
@@ -1393,7 +1454,7 @@ export class Router<
1393
1454
  // ?.__promisesByKey[key]?.resolve(value)
1394
1455
  // }
1395
1456
 
1396
- #buildRouteTree = (routeTree: TRouteTree) => {
1457
+ #processRoutes = (routeTree: TRouteTree) => {
1397
1458
  this.routeTree = routeTree as any
1398
1459
  this.routesById = {} as any
1399
1460
  this.routesByPath = {} as any
@@ -1705,7 +1766,7 @@ export class Router<
1705
1766
  (opts?.maxAge ??
1706
1767
  route.options.maxAge ??
1707
1768
  this.options.defaultMaxAge ??
1708
- Infinity)
1769
+ 9999999999999)
1709
1770
 
1710
1771
  this.setRouteMatch(id, (s) => ({
1711
1772
  ...s,