@tanstack/router-core 0.0.1-beta.151 → 0.0.1-beta.153

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/src/router.ts CHANGED
@@ -24,7 +24,6 @@ import {
24
24
  Route,
25
25
  AnySearchSchema,
26
26
  AnyRoute,
27
- RootRoute,
28
27
  AnyContext,
29
28
  AnyPathParams,
30
29
  RouteProps,
@@ -32,11 +31,12 @@ import {
32
31
  RegisteredRouteErrorComponent,
33
32
  } from './route'
34
33
  import {
35
- RoutesInfo,
36
- AnyRoutesInfo,
37
34
  RoutesById,
38
35
  RoutesByPath,
39
- DefaultRoutesInfo,
36
+ ParseRoute,
37
+ FullSearchSchema,
38
+ RouteById,
39
+ RoutePaths,
40
40
  } from './routeInfo'
41
41
  import { defaultParseSearch, defaultStringifySearch } from './searchParams'
42
42
  import {
@@ -68,28 +68,25 @@ export interface Register {
68
68
  // router: Router
69
69
  }
70
70
 
71
- export type AnyRouter = Router<any, any, any>
71
+ export type AnyRouter = Router<any, any>
72
72
 
73
- export type RegisteredRouterPair = Register extends {
73
+ export type RegisteredRouter = Register extends {
74
74
  router: infer TRouter extends AnyRouter
75
75
  }
76
- ? [TRouter, TRouter['types']['RoutesInfo']]
77
- : [Router, AnyRoutesInfo]
78
-
79
- export type RegisteredRouter = RegisteredRouterPair[0]
80
- export type RegisteredRoutesInfo = RegisteredRouterPair[1]
76
+ ? TRouter
77
+ : Router
81
78
 
82
79
  export interface LocationState {}
83
80
 
84
81
  export interface ParsedLocation<
85
82
  TSearchObj extends AnySearchSchema = {},
86
- TState extends LocationState = LocationState,
83
+ // TState extends LocationState = LocationState,
87
84
  > {
88
85
  href: string
89
86
  pathname: string
90
87
  search: TSearchObj
91
88
  searchStr: string
92
- state: TState
89
+ state: LocationState
93
90
  hash: string
94
91
  key?: string
95
92
  }
@@ -110,7 +107,7 @@ export type HydrationCtx = {
110
107
  }
111
108
 
112
109
  export interface RouteMatch<
113
- TRoutesInfo extends AnyRoutesInfo = DefaultRoutesInfo,
110
+ TRouteTree extends AnyRoute = AnyRoute,
114
111
  TRoute extends AnyRoute = Route,
115
112
  > {
116
113
  id: string
@@ -133,13 +130,12 @@ export interface RouteMatch<
133
130
  routeContext: TRoute['__types']['routeContext']
134
131
  context: TRoute['__types']['context']
135
132
  routeSearch: TRoute['__types']['searchSchema']
136
- search: TRoutesInfo['fullSearchSchema'] &
137
- TRoute['__types']['fullSearchSchema']
133
+ search: FullSearchSchema<TRouteTree> & TRoute['__types']['fullSearchSchema']
138
134
  fetchedAt: number
139
135
  abortController: AbortController
140
136
  }
141
137
 
142
- export type AnyRouteMatch = RouteMatch<AnyRoutesInfo, AnyRoute>
138
+ export type AnyRouteMatch = RouteMatch<AnyRoute, AnyRoute>
143
139
 
144
140
  export type RouterContextOptions<TRouteTree extends AnyRoute> =
145
141
  AnyContext extends TRouteTree['__types']['routerContext']
@@ -186,21 +182,18 @@ export interface RouterOptions<
186
182
  }
187
183
 
188
184
  export interface RouterState<
189
- TRoutesInfo extends AnyRoutesInfo = AnyRoutesInfo,
190
- TState extends LocationState = LocationState,
185
+ TRouteTree extends AnyRoute = AnyRoute,
186
+ // TState extends LocationState = LocationState,
191
187
  > {
192
188
  status: 'idle' | 'pending'
193
189
  isFetching: boolean
194
- matchesById: Record<
195
- string,
196
- RouteMatch<TRoutesInfo, TRoutesInfo['routeIntersection']>
197
- >
190
+ matchesById: Record<string, RouteMatch<any, any>>
198
191
  matchIds: string[]
199
192
  pendingMatchIds: string[]
200
- matches: RouteMatch<TRoutesInfo, TRoutesInfo['routeIntersection']>[]
201
- pendingMatches: RouteMatch<TRoutesInfo, TRoutesInfo['routeIntersection']>[]
202
- location: ParsedLocation<TRoutesInfo['fullSearchSchema'], TState>
203
- resolvedLocation: ParsedLocation<TRoutesInfo['fullSearchSchema'], TState>
193
+ matches: RouteMatch<any, any>[]
194
+ pendingMatches: RouteMatch<any, any>[]
195
+ location: ParsedLocation<FullSearchSchema<TRouteTree>>
196
+ resolvedLocation: ParsedLocation<FullSearchSchema<TRouteTree>>
204
197
  lastUpdated: number
205
198
  }
206
199
 
@@ -258,12 +251,10 @@ export const componentTypes = [
258
251
 
259
252
  export class Router<
260
253
  TRouteTree extends AnyRoute = AnyRoute,
261
- TRoutesInfo extends AnyRoutesInfo = RoutesInfo<TRouteTree>,
262
254
  TDehydrated extends Record<string, any> = Record<string, any>,
263
255
  > {
264
256
  types!: {
265
257
  RootRoute: TRouteTree
266
- RoutesInfo: TRoutesInfo
267
258
  }
268
259
 
269
260
  options: PickAsRequired<
@@ -273,16 +264,16 @@ export class Router<
273
264
  history!: RouterHistory
274
265
  #unsubHistory?: () => void
275
266
  basepath!: string
276
- routeTree!: RootRoute
277
- routesById!: RoutesById<TRoutesInfo>
278
- routesByPath!: RoutesByPath<TRoutesInfo>
279
- flatRoutes!: TRoutesInfo['routesByFullPath'][keyof TRoutesInfo['routesByFullPath']][]
267
+ routeTree!: TRouteTree
268
+ routesById!: RoutesById<TRouteTree>
269
+ routesByPath!: RoutesByPath<TRouteTree>
270
+ flatRoutes!: ParseRoute<TRouteTree>[]
280
271
  navigateTimeout: undefined | Timeout
281
272
  nextAction: undefined | 'push' | 'replace'
282
273
  navigationPromise: undefined | Promise<void>
283
274
 
284
- __store: Store<RouterState<TRoutesInfo>>
285
- state: RouterState<TRoutesInfo>
275
+ __store: Store<RouterState<TRouteTree>>
276
+ state: RouterState<TRouteTree>
286
277
  dehydratedData?: TDehydrated
287
278
 
288
279
  constructor(options: RouterConstructorOptions<TRouteTree, TDehydrated>) {
@@ -295,51 +286,47 @@ export class Router<
295
286
  // fetchServerDataFn: options?.fetchServerDataFn ?? defaultFetchServerDataFn,
296
287
  }
297
288
 
298
- this.__store = new Store<RouterState<TRoutesInfo>>(
299
- getInitialRouterState(),
300
- {
301
- onUpdate: () => {
302
- const prev = this.state
303
-
304
- this.state = this.__store.state
305
-
306
- const matchesByIdChanged = prev.matchesById !== this.state.matchesById
307
- let matchesChanged
308
- let pendingMatchesChanged
309
-
310
- if (!matchesByIdChanged) {
311
- matchesChanged =
312
- prev.matchIds.length !== this.state.matchIds.length ||
313
- prev.matchIds.some((d, i) => d !== this.state.matchIds[i])
314
-
315
- pendingMatchesChanged =
316
- prev.pendingMatchIds.length !==
317
- this.state.pendingMatchIds.length ||
318
- prev.pendingMatchIds.some(
319
- (d, i) => d !== this.state.pendingMatchIds[i],
320
- )
321
- }
289
+ this.__store = new Store<RouterState<TRouteTree>>(getInitialRouterState(), {
290
+ onUpdate: () => {
291
+ const prev = this.state
322
292
 
323
- if (matchesByIdChanged || matchesChanged) {
324
- this.state.matches = this.state.matchIds.map((id) => {
325
- return this.state.matchesById[id] as any
326
- })
327
- }
293
+ this.state = this.__store.state
328
294
 
329
- if (matchesByIdChanged || pendingMatchesChanged) {
330
- this.state.pendingMatches = this.state.pendingMatchIds.map((id) => {
331
- return this.state.matchesById[id] as any
332
- })
333
- }
295
+ const matchesByIdChanged = prev.matchesById !== this.state.matchesById
296
+ let matchesChanged
297
+ let pendingMatchesChanged
334
298
 
335
- this.state.isFetching = [
336
- ...this.state.matches,
337
- ...this.state.pendingMatches,
338
- ].some((d) => d.isFetching)
339
- },
340
- defaultPriority: 'low',
299
+ if (!matchesByIdChanged) {
300
+ matchesChanged =
301
+ prev.matchIds.length !== this.state.matchIds.length ||
302
+ prev.matchIds.some((d, i) => d !== this.state.matchIds[i])
303
+
304
+ pendingMatchesChanged =
305
+ prev.pendingMatchIds.length !== this.state.pendingMatchIds.length ||
306
+ prev.pendingMatchIds.some(
307
+ (d, i) => d !== this.state.pendingMatchIds[i],
308
+ )
309
+ }
310
+
311
+ if (matchesByIdChanged || matchesChanged) {
312
+ this.state.matches = this.state.matchIds.map((id) => {
313
+ return this.state.matchesById[id] as any
314
+ })
315
+ }
316
+
317
+ if (matchesByIdChanged || pendingMatchesChanged) {
318
+ this.state.pendingMatches = this.state.pendingMatchIds.map((id) => {
319
+ return this.state.matchesById[id] as any
320
+ })
321
+ }
322
+
323
+ this.state.isFetching = [
324
+ ...this.state.matches,
325
+ ...this.state.pendingMatches,
326
+ ].some((d) => d.isFetching)
341
327
  },
342
- )
328
+ defaultPriority: 'low',
329
+ })
343
330
 
344
331
  this.state = this.__store.state
345
332
 
@@ -394,8 +381,8 @@ export class Router<
394
381
 
395
382
  this.__store.setState((s) => ({
396
383
  ...s,
397
- resolvedLocation: parsedLocation,
398
- location: parsedLocation,
384
+ resolvedLocation: parsedLocation as any,
385
+ location: parsedLocation as any,
399
386
  }))
400
387
 
401
388
  this.#unsubHistory = this.history.listen(() => {
@@ -466,7 +453,7 @@ export class Router<
466
453
  // Ingest the new location
467
454
  this.__store.setState((s) => ({
468
455
  ...s,
469
- location: opts.next!,
456
+ location: opts.next! as any,
470
457
  }))
471
458
  }
472
459
 
@@ -530,13 +517,10 @@ export class Router<
530
517
  #mergeMatches = (
531
518
  prevMatchesById: Record<
532
519
  string,
533
- RouteMatch<TRoutesInfo, TRoutesInfo['routeIntersection']>
520
+ RouteMatch<TRouteTree, ParseRoute<TRouteTree>>
534
521
  >,
535
522
  nextMatches: RouteMatch[],
536
- ): Record<
537
- string,
538
- RouteMatch<TRoutesInfo, TRoutesInfo['routeIntersection']>
539
- > => {
523
+ ): Record<string, RouteMatch<TRouteTree, ParseRoute<TRouteTree>>> => {
540
524
  const nextMatchesById: any = {
541
525
  ...prevMatchesById,
542
526
  }
@@ -557,14 +541,12 @@ export class Router<
557
541
  return nextMatchesById
558
542
  }
559
543
 
560
- getRoute = <TId extends keyof TRoutesInfo['routesById']>(
561
- id: TId,
562
- ): TRoutesInfo['routesById'][TId] => {
563
- const route = this.routesById[id]
544
+ getRoute = (id: string): Route => {
545
+ const route = (this.routesById as any)[id]
564
546
 
565
547
  invariant(route, `Route with id "${id as string}" not found`)
566
548
 
567
- return route
549
+ return route as any
568
550
  }
569
551
 
570
552
  preloadRoute = async (
@@ -627,7 +609,7 @@ export class Router<
627
609
  pathname: string,
628
610
  locationSearch: AnySearchSchema,
629
611
  opts?: { throwOnError?: boolean; debug?: boolean },
630
- ): RouteMatch<TRoutesInfo, TRoutesInfo['routeIntersection']>[] => {
612
+ ): RouteMatch<TRouteTree, ParseRoute<TRouteTree>>[] => {
631
613
  let routeParams: AnyPathParams = {}
632
614
 
633
615
  let foundRoute = this.flatRoutes.find((route) => {
@@ -649,10 +631,11 @@ export class Router<
649
631
  return false
650
632
  })
651
633
 
652
- let routeCursor = foundRoute || (this.routesById['__root__'] as any)
634
+ let routeCursor: AnyRoute =
635
+ foundRoute || (this.routesById as any)['__root__']
653
636
 
654
637
  let matchedRoutes: AnyRoute[] = [routeCursor]
655
-
638
+ // let includingLayouts = true
656
639
  while (routeCursor?.parentRoute) {
657
640
  routeCursor = routeCursor.parentRoute
658
641
  if (routeCursor) matchedRoutes.unshift(routeCursor)
@@ -1047,14 +1030,17 @@ export class Router<
1047
1030
  return resolvePath(this.basepath!, from, cleanPath(path))
1048
1031
  }
1049
1032
 
1050
- navigate = async <TFrom extends string = '/', TTo extends string = ''>({
1033
+ navigate = async <
1034
+ TFrom extends RoutePaths<TRouteTree> = '/',
1035
+ TTo extends string = '',
1036
+ >({
1051
1037
  from,
1052
1038
  to = '' as any,
1053
1039
  search,
1054
1040
  hash,
1055
1041
  replace,
1056
1042
  params,
1057
- }: NavigateOptions<TRoutesInfo, TFrom, TTo>) => {
1043
+ }: NavigateOptions<TRouteTree, TFrom, TTo>) => {
1058
1044
  // If this link simply reloads the current route,
1059
1045
  // make sure it has a new key so it will trigger a data refresh
1060
1046
 
@@ -1085,13 +1071,13 @@ export class Router<
1085
1071
  }
1086
1072
 
1087
1073
  matchRoute = <
1088
- TFrom extends string = '/',
1074
+ TFrom extends RoutePaths<TRouteTree> = '/',
1089
1075
  TTo extends string = '',
1090
1076
  TResolved extends string = ResolveRelativePath<TFrom, NoInfer<TTo>>,
1091
1077
  >(
1092
- location: ToOptions<TRoutesInfo, TFrom, TTo>,
1078
+ location: ToOptions<TRouteTree, TFrom, TTo>,
1093
1079
  opts?: MatchRouteOptions,
1094
- ): false | TRoutesInfo['routesById'][TResolved]['__types']['allParams'] => {
1080
+ ): false | RouteById<TRouteTree, TResolved>['__types']['allParams'] => {
1095
1081
  location = {
1096
1082
  ...location,
1097
1083
  to: location.to
@@ -1128,7 +1114,10 @@ export class Router<
1128
1114
  return match
1129
1115
  }
1130
1116
 
1131
- buildLink = <TFrom extends string = '/', TTo extends string = ''>({
1117
+ buildLink = <
1118
+ TFrom extends RoutePaths<TRouteTree> = '/',
1119
+ TTo extends string = '',
1120
+ >({
1132
1121
  from,
1133
1122
  to = '.' as any,
1134
1123
  search,
@@ -1140,7 +1129,7 @@ export class Router<
1140
1129
  preload,
1141
1130
  preloadDelay: userPreloadDelay,
1142
1131
  disabled,
1143
- }: LinkOptions<TRoutesInfo, TFrom, TTo>): LinkInfo => {
1132
+ }: LinkOptions<TRouteTree, TFrom, TTo>): LinkInfo => {
1144
1133
  // If this link simply reloads the current route,
1145
1134
  // make sure it has a new key so it will trigger a data refresh
1146
1135
 
@@ -1285,12 +1274,13 @@ export class Router<
1285
1274
  const ctx = _ctx
1286
1275
  this.dehydratedData = ctx.payload as any
1287
1276
  this.options.hydrate?.(ctx.payload as any)
1277
+ const routerState = ctx.router.state as RouterState<TRouteTree>
1288
1278
 
1289
1279
  this.__store.setState((s) => {
1290
1280
  return {
1291
1281
  ...s,
1292
- ...ctx.router.state,
1293
- resolvedLocation: ctx.router.state.location,
1282
+ ...routerState,
1283
+ resolvedLocation: routerState.location,
1294
1284
  }
1295
1285
  })
1296
1286
 
@@ -1366,7 +1356,7 @@ export class Router<
1366
1356
  if (!route.isRoot && route.path) {
1367
1357
  const trimmedFullPath = trimPathRight(route.fullPath)
1368
1358
  if (
1369
- !this.routesByPath[trimmedFullPath] ||
1359
+ !(this.routesByPath as any)[trimmedFullPath] ||
1370
1360
  route.fullPath.endsWith('/')
1371
1361
  ) {
1372
1362
  ;(this.routesByPath as any)[trimmedFullPath] = route
@@ -1446,7 +1436,9 @@ export class Router<
1446
1436
  }) as any
1447
1437
  }
1448
1438
 
1449
- #parseLocation = (previousLocation?: ParsedLocation): ParsedLocation => {
1439
+ #parseLocation = (
1440
+ previousLocation?: ParsedLocation,
1441
+ ): ParsedLocation<FullSearchSchema<TRouteTree>> => {
1450
1442
  let { pathname, search, hash, state } = this.history.location
1451
1443
 
1452
1444
  const parsedSearch = this.options.parseSearch(search)
@@ -1454,7 +1446,7 @@ export class Router<
1454
1446
  return {
1455
1447
  pathname: pathname,
1456
1448
  searchStr: search,
1457
- search: replaceEqualDeep(previousLocation?.search, parsedSearch),
1449
+ search: replaceEqualDeep(previousLocation?.search, parsedSearch) as any,
1458
1450
  hash: hash.split('#').reverse()[0] ?? '',
1459
1451
  href: `${pathname}${search}${hash}`,
1460
1452
  state: state as LocationState,
@@ -1603,15 +1595,15 @@ export class Router<
1603
1595
 
1604
1596
  getRouteMatch = (
1605
1597
  id: string,
1606
- ): undefined | RouteMatch<TRoutesInfo, AnyRoute> => {
1598
+ ): undefined | RouteMatch<TRouteTree, AnyRoute> => {
1607
1599
  return this.state.matchesById[id]
1608
1600
  }
1609
1601
 
1610
1602
  setRouteMatch = (
1611
1603
  id: string,
1612
1604
  updater: (
1613
- prev: RouteMatch<TRoutesInfo, AnyRoute>,
1614
- ) => RouteMatch<TRoutesInfo, AnyRoute>,
1605
+ prev: RouteMatch<TRouteTree, AnyRoute>,
1606
+ ) => RouteMatch<TRouteTree, AnyRoute>,
1615
1607
  ) => {
1616
1608
  this.__store.setState((prev) => ({
1617
1609
  ...prev,
@@ -1724,7 +1716,7 @@ export class Router<
1724
1716
  // Detect if we're in the DOM
1725
1717
  const isServer = typeof window === 'undefined' || !window.document.createElement
1726
1718
 
1727
- function getInitialRouterState(): RouterState<any, any> {
1719
+ function getInitialRouterState(): RouterState<any> {
1728
1720
  return {
1729
1721
  status: 'idle',
1730
1722
  isFetching: false,
@@ -1746,18 +1738,18 @@ function isCtrlEvent(e: MouseEvent) {
1746
1738
  export type AnyRedirect = Redirect<any, any, any>
1747
1739
 
1748
1740
  export type Redirect<
1749
- TRoutesInfo extends AnyRoutesInfo = RegisteredRoutesInfo,
1750
- TFrom extends TRoutesInfo['routePaths'] = '/',
1741
+ TRouteTree extends AnyRoute = AnyRoute,
1742
+ TFrom extends RoutePaths<TRouteTree> = '/',
1751
1743
  TTo extends string = '',
1752
- > = NavigateOptions<TRoutesInfo, TFrom, TTo> & {
1744
+ > = NavigateOptions<TRouteTree, TFrom, TTo> & {
1753
1745
  code?: number
1754
1746
  }
1755
1747
 
1756
1748
  export function redirect<
1757
- TRoutesInfo extends AnyRoutesInfo = RegisteredRoutesInfo,
1758
- TFrom extends TRoutesInfo['routePaths'] = '/',
1749
+ TRouteTree extends AnyRoute = AnyRoute,
1750
+ TFrom extends RoutePaths<TRouteTree> = '/',
1759
1751
  TTo extends string = '',
1760
- >(opts: Redirect<TRoutesInfo, TFrom, TTo>): Redirect<TRoutesInfo, TFrom, TTo> {
1752
+ >(opts: Redirect<TRouteTree, TFrom, TTo>): Redirect<TRouteTree, TFrom, TTo> {
1761
1753
  ;(opts as any).isRedirect = true
1762
1754
  return opts
1763
1755
  }