@tanstack/router-core 0.0.1-beta.194 → 0.0.1-beta.195

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.194",
4
+ "version": "0.0.1-beta.195",
5
5
  "license": "MIT",
6
6
  "repository": "tanstack/router",
7
7
  "homepage": "https://tanstack.com/router",
package/src/fileRoute.ts CHANGED
@@ -130,14 +130,7 @@ export class FileRoute<
130
130
  >,
131
131
  'getParentRoute' | 'path' | 'id'
132
132
  > &
133
- UpdatableRouteOptions<
134
- TLoader,
135
- TSearchSchema,
136
- TFullSearchSchema,
137
- TAllParams,
138
- TRouteContext,
139
- TContext
140
- >,
133
+ UpdatableRouteOptions<TLoader, TFullSearchSchema, TAllParams, TContext>,
141
134
  ): Route<
142
135
  TParentRoute,
143
136
  TPath,
package/src/history.ts CHANGED
@@ -12,6 +12,7 @@ export interface RouterHistory {
12
12
  forward: () => void
13
13
  createHref: (href: string) => string
14
14
  block: (blockerFn: BlockerFn) => () => void
15
+ flush: () => void
15
16
  }
16
17
 
17
18
  export interface HistoryLocation extends ParsedPath {
@@ -52,12 +53,13 @@ const stopBlocking = () => {
52
53
  function createHistory(opts: {
53
54
  getLocation: () => HistoryLocation
54
55
  subscriber: false | ((onUpdate: () => void) => () => void)
55
- pushState: (path: string, state: any) => void
56
- replaceState: (path: string, state: any) => void
56
+ pushState: (path: string, state: any, onUpdate: () => void) => void
57
+ replaceState: (path: string, state: any, onUpdate: () => void) => void
57
58
  go: (n: number) => void
58
59
  back: () => void
59
60
  forward: () => void
60
61
  createHref: (path: string) => string
62
+ flush?: () => void
61
63
  }): RouterHistory {
62
64
  let location = opts.getLocation()
63
65
  let unsub = () => {}
@@ -116,13 +118,13 @@ function createHistory(opts: {
116
118
  push: (path: string, state: any) => {
117
119
  assignKey(state)
118
120
  queueTask(() => {
119
- opts.pushState(path, state)
121
+ opts.pushState(path, state, onUpdate)
120
122
  })
121
123
  },
122
124
  replace: (path: string, state: any) => {
123
125
  assignKey(state)
124
126
  queueTask(() => {
125
- opts.replaceState(path, state)
127
+ opts.replaceState(path, state, onUpdate)
126
128
  })
127
129
  },
128
130
  go: (index) => {
@@ -158,6 +160,7 @@ function createHistory(opts: {
158
160
  }
159
161
  }
160
162
  },
163
+ flush: () => opts.flush?.(),
161
164
  }
162
165
  }
163
166
 
@@ -171,6 +174,22 @@ function assignKey(state: HistoryState) {
171
174
  // }
172
175
  }
173
176
 
177
+ /**
178
+ * Creates a history object that can be used to interact with the browser's
179
+ * navigation. This is a lightweight API wrapping the browser's native methods.
180
+ * It is designed to work with TanStack Router, but could be used as a standalone API as well.
181
+ * IMPORTANT: This API implements history throttling via a microtask to prevent
182
+ * excessive calls to the history API. In some browsers, calling history.pushState or
183
+ * history.replaceState in quick succession can cause the browser to ignore subsequent
184
+ * calls. This API smooths out those differences and ensures that your application
185
+ * state will *eventually* match the browser state. In most cases, this is not a problem,
186
+ * but if you need to ensure that the browser state is up to date, you can use the
187
+ * `history.flush` method to immediately flush all pending state changes to the browser URL.
188
+ * @param opts
189
+ * @param opts.getHref A function that returns the current href (path + search + hash)
190
+ * @param opts.createHref A function that takes a path and returns a href (path + search + hash)
191
+ * @returns A history instance
192
+ */
174
193
  export function createBrowserHistory(opts?: {
175
194
  getHref?: () => string
176
195
  createHref?: (path: string) => string
@@ -182,24 +201,104 @@ export function createBrowserHistory(opts?: {
182
201
 
183
202
  const createHref = opts?.createHref ?? ((path) => path)
184
203
 
185
- const getLocation = () => parseLocation(getHref(), window.history.state)
204
+ let currentLocation = parseLocation(getHref(), window.history.state)
205
+
206
+ const getLocation = () => currentLocation
207
+
208
+ let next:
209
+ | undefined
210
+ | {
211
+ // This is the latest location that we were attempting to push/replace
212
+ href: string
213
+ // This is the latest state that we were attempting to push/replace
214
+ state: any
215
+ // This is the latest type that we were attempting to push/replace
216
+ isPush: boolean
217
+ }
218
+
219
+ // Because we are proactively updating the location
220
+ // in memory before actually updating the browser history,
221
+ // we need to track when we are doing this so we don't
222
+ // notify subscribers twice on the last update.
223
+ let tracking = true
224
+
225
+ // We need to track the current scheduled update to prevent
226
+ // multiple updates from being scheduled at the same time.
227
+ let scheduled: Promise<void> | undefined
228
+
229
+ // This function is a wrapper to prevent any of the callback's
230
+ // side effects from causing a subscriber notification
231
+ const untrack = (fn: () => void) => {
232
+ tracking = false
233
+ fn()
234
+ tracking = true
235
+ }
236
+
237
+ // This function flushes the next update to the browser history
238
+ const flush = () => {
239
+ // Do not notify subscribers about this push/replace call
240
+ untrack(() => {
241
+ if (!next) return
242
+ window.history[next.isPush ? 'pushState' : 'replaceState'](
243
+ next.state,
244
+ '',
245
+ next.href,
246
+ )
247
+ // Reset the nextIsPush flag and clear the scheduled update
248
+ next = undefined
249
+ scheduled = undefined
250
+ })
251
+ }
252
+
253
+ // This function queues up a call to update the browser history
254
+ const queueHistoryAction = (
255
+ type: 'push' | 'replace',
256
+ path: string,
257
+ state: any,
258
+ onUpdate: () => void,
259
+ ) => {
260
+ const href = createHref(path)
261
+
262
+ // Update the location in memory
263
+ currentLocation = parseLocation(href, state)
264
+
265
+ // Keep track of the next location we need to flush to the URL
266
+ next = {
267
+ href,
268
+ state,
269
+ isPush: next?.isPush || type === 'push',
270
+ }
271
+ // Notify subscribers
272
+ onUpdate()
273
+
274
+ if (!scheduled) {
275
+ // Schedule an update to the browser history
276
+ scheduled = Promise.resolve().then(() => flush())
277
+ }
278
+ }
186
279
 
187
280
  return createHistory({
188
281
  getLocation,
189
282
  subscriber: (onUpdate) => {
190
- window.addEventListener(pushStateEvent, onUpdate)
191
- window.addEventListener(popStateEvent, onUpdate)
283
+ window.addEventListener(pushStateEvent, () => {
284
+ currentLocation = parseLocation(getHref(), window.history.state)
285
+ onUpdate()
286
+ })
287
+ window.addEventListener(popStateEvent, () => {
288
+ currentLocation = parseLocation(getHref(), window.history.state)
289
+ onUpdate()
290
+ })
192
291
 
193
292
  var pushState = window.history.pushState
194
293
  window.history.pushState = function () {
195
294
  let res = pushState.apply(history, arguments as any)
196
- onUpdate()
295
+ if (tracking) onUpdate()
197
296
  return res
198
297
  }
199
298
  var replaceState = window.history.replaceState
200
299
  window.history.replaceState = function () {
201
300
  let res = replaceState.apply(history, arguments as any)
202
- onUpdate()
301
+ if (tracking) onUpdate()
203
302
  return res
204
303
  }
205
304
 
@@ -210,16 +309,15 @@ export function createBrowserHistory(opts?: {
210
309
  window.removeEventListener(popStateEvent, onUpdate)
211
310
  }
212
311
  },
213
- pushState: (path, state) => {
214
- window.history.pushState(state, '', createHref(path))
215
- },
216
- replaceState: (path, state) => {
217
- window.history.replaceState(state, '', createHref(path))
218
- },
312
+ pushState: (path, state, onUpdate) =>
313
+ queueHistoryAction('push', path, state, onUpdate),
314
+ replaceState: (path, state, onUpdate) =>
315
+ queueHistoryAction('replace', path, state, onUpdate),
219
316
  back: () => window.history.back(),
220
317
  forward: () => window.history.forward(),
221
318
  go: (n) => window.history.go(n),
222
319
  createHref: (path) => createHref(path),
320
+ flush,
223
321
  })
224
322
  }
225
323
 
package/src/route.ts CHANGED
@@ -24,7 +24,6 @@ export interface RegisterRouteComponent<
24
24
  TLoader = unknown,
25
25
  TFullSearchSchema extends Record<string, any> = AnySearchSchema,
26
26
  TAllParams extends AnyPathParams = AnyPathParams,
27
- TRouteContext extends Record<string, any> = AnyContext,
28
27
  TAllContext extends Record<string, any> = AnyContext,
29
28
  > {
30
29
  // RouteComponent: unknown // This is registered by the framework
@@ -32,7 +31,6 @@ export interface RegisterRouteComponent<
32
31
  export interface RegisterErrorRouteComponent<
33
32
  TFullSearchSchema extends Record<string, any> = AnySearchSchema,
34
33
  TAllParams extends AnyPathParams = AnyPathParams,
35
- TRouteContext extends Record<string, any> = AnyContext,
36
34
  TAllContext extends Record<string, any> = AnyContext,
37
35
  > {
38
36
  // ErrorRouteComponent: unknown // This is registered by the framework
@@ -40,7 +38,6 @@ export interface RegisterErrorRouteComponent<
40
38
  export interface RegisterPendingRouteComponent<
41
39
  TFullSearchSchema extends Record<string, any> = AnySearchSchema,
42
40
  TAllParams extends AnyPathParams = AnyPathParams,
43
- TRouteContext extends Record<string, any> = AnyContext,
44
41
  TAllContext extends Record<string, any> = AnyContext,
45
42
  > {
46
43
  // PendingRouteComponent: unknown // This is registered by the framework
@@ -50,7 +47,6 @@ export interface RegisterRouteProps<
50
47
  TLoader = unknown,
51
48
  TFullSearchSchema extends Record<string, any> = AnySearchSchema,
52
49
  TAllParams extends AnyPathParams = AnyPathParams,
53
- TRouteContext extends Record<string, any> = AnyContext,
54
50
  TAllContext extends Record<string, any> = AnyContext,
55
51
  > {
56
52
  // RouteProps: unknown // This is registered by the framework
@@ -58,7 +54,6 @@ export interface RegisterRouteProps<
58
54
  export interface RegisterErrorRouteProps<
59
55
  TFullSearchSchema extends Record<string, any> = AnySearchSchema,
60
56
  TAllParams extends AnyPathParams = AnyPathParams,
61
- TRouteContext extends Record<string, any> = AnyContext,
62
57
  TAllContext extends Record<string, any> = AnyContext,
63
58
  > {
64
59
  // ErrorRouteProps: unknown // This is registered by the framework
@@ -67,7 +62,6 @@ export interface RegisterErrorRouteProps<
67
62
  export interface RegisterPendingRouteProps<
68
63
  TFullSearchSchema extends Record<string, any> = AnySearchSchema,
69
64
  TAllParams extends AnyPathParams = AnyPathParams,
70
- TRouteContext extends Record<string, any> = AnyContext,
71
65
  TAllContext extends Record<string, any> = AnyContext,
72
66
  > {
73
67
  // PendingRouteProps: unknown // This is registered by the framework
@@ -77,13 +71,11 @@ export type RegisteredRouteComponent<
77
71
  TLoader = unknown,
78
72
  TFullSearchSchema extends Record<string, any> = AnySearchSchema,
79
73
  TAllParams extends AnyPathParams = AnyPathParams,
80
- TRouteContext extends Record<string, any> = AnyContext,
81
74
  TAllContext extends Record<string, any> = AnyContext,
82
75
  > = RegisterRouteComponent<
83
76
  TLoader,
84
77
  TFullSearchSchema,
85
78
  TAllParams,
86
- TRouteContext,
87
79
  TAllContext
88
80
  > extends {
89
81
  RouteComponent: infer T
@@ -94,12 +86,10 @@ export type RegisteredRouteComponent<
94
86
  export type RegisteredErrorRouteComponent<
95
87
  TFullSearchSchema extends Record<string, any> = AnySearchSchema,
96
88
  TAllParams extends AnyPathParams = AnyPathParams,
97
- TRouteContext extends Record<string, any> = AnyContext,
98
89
  TAllContext extends Record<string, any> = AnyContext,
99
90
  > = RegisterErrorRouteComponent<
100
91
  TFullSearchSchema,
101
92
  TAllParams,
102
- TRouteContext,
103
93
  TAllContext
104
94
  > extends {
105
95
  ErrorRouteComponent: infer T
@@ -110,12 +100,10 @@ export type RegisteredErrorRouteComponent<
110
100
  export type RegisteredPendingRouteComponent<
111
101
  TFullSearchSchema extends Record<string, any> = AnySearchSchema,
112
102
  TAllParams extends AnyPathParams = AnyPathParams,
113
- TRouteContext extends Record<string, any> = AnyContext,
114
103
  TAllContext extends Record<string, any> = AnyContext,
115
104
  > = RegisterPendingRouteComponent<
116
105
  TFullSearchSchema,
117
106
  TAllParams,
118
- TRouteContext,
119
107
  TAllContext
120
108
  > extends {
121
109
  PendingRouteComponent: infer T
@@ -127,13 +115,11 @@ export type RegisteredRouteProps<
127
115
  TLoader = unknown,
128
116
  TFullSearchSchema extends Record<string, any> = AnySearchSchema,
129
117
  TAllParams extends AnyPathParams = AnyPathParams,
130
- TRouteContext extends Record<string, any> = AnyContext,
131
118
  TAllContext extends Record<string, any> = AnyContext,
132
119
  > = RegisterRouteProps<
133
120
  TLoader,
134
121
  TFullSearchSchema,
135
122
  TAllParams,
136
- TRouteContext,
137
123
  TAllContext
138
124
  > extends {
139
125
  RouteProps: infer T
@@ -144,14 +130,8 @@ export type RegisteredRouteProps<
144
130
  export type RegisteredErrorRouteProps<
145
131
  TFullSearchSchema extends Record<string, any> = AnySearchSchema,
146
132
  TAllParams extends AnyPathParams = AnyPathParams,
147
- TRouteContext extends Record<string, any> = AnyContext,
148
133
  TAllContext extends Record<string, any> = AnyContext,
149
- > = RegisterRouteProps<
150
- TFullSearchSchema,
151
- TAllParams,
152
- TRouteContext,
153
- TAllContext
154
- > extends {
134
+ > = RegisterRouteProps<TFullSearchSchema, TAllParams, TAllContext> extends {
155
135
  ErrorRouteProps: infer T
156
136
  }
157
137
  ? T
@@ -160,14 +140,8 @@ export type RegisteredErrorRouteProps<
160
140
  export type RegisteredPendingRouteProps<
161
141
  TFullSearchSchema extends Record<string, any> = AnySearchSchema,
162
142
  TAllParams extends AnyPathParams = AnyPathParams,
163
- TRouteContext extends Record<string, any> = AnyContext,
164
143
  TAllContext extends Record<string, any> = AnyContext,
165
- > = RegisterRouteProps<
166
- TFullSearchSchema,
167
- TAllParams,
168
- TRouteContext,
169
- TAllContext
170
- > extends {
144
+ > = RegisterRouteProps<TFullSearchSchema, TAllParams, TAllContext> extends {
171
145
  PendingRouteProps: infer T
172
146
  }
173
147
  ? T
@@ -194,7 +168,7 @@ export type MetaOptions = keyof PickRequired<RouteMeta> extends never
194
168
  meta: RouteMeta
195
169
  }
196
170
 
197
- export type AnyRouteProps = RegisteredRouteProps<any, any, any, any, any>
171
+ export type AnyRouteProps = RegisteredRouteProps<any, any, any, any>
198
172
 
199
173
  export type RouteOptions<
200
174
  TParentRoute extends AnyRoute = AnyRoute,
@@ -221,14 +195,7 @@ export type RouteOptions<
221
195
  TRouteContext,
222
196
  TAllContext
223
197
  > &
224
- UpdatableRouteOptions<
225
- TLoader,
226
- TSearchSchema,
227
- TFullSearchSchema,
228
- TAllParams,
229
- TRouteContext,
230
- TAllContext
231
- >
198
+ UpdatableRouteOptions<TLoader, TFullSearchSchema, TAllParams, TAllContext>
232
199
 
233
200
  export type ParamsFallback<
234
201
  TPath extends string,
@@ -320,10 +287,8 @@ type BeforeLoadFn<
320
287
 
321
288
  export type UpdatableRouteOptions<
322
289
  TLoader,
323
- TSearchSchema extends Record<string, any>,
324
290
  TFullSearchSchema extends Record<string, any>,
325
291
  TAllParams extends AnyPathParams,
326
- TRouteContext extends Record<string, any>,
327
292
  TAllContext extends Record<string, any>,
328
293
  > = MetaOptions & {
329
294
  // If true, this route will be matched as case-sensitive
@@ -335,21 +300,18 @@ export type UpdatableRouteOptions<
335
300
  TLoader,
336
301
  TFullSearchSchema,
337
302
  TAllParams,
338
- TRouteContext,
339
303
  TAllContext
340
304
  >
341
305
  // The content to be rendered when the route encounters an error
342
306
  errorComponent?: RegisteredErrorRouteComponent<
343
307
  TFullSearchSchema,
344
308
  TAllParams,
345
- TRouteContext,
346
309
  TAllContext
347
310
  > //
348
311
  // If supported by your framework, the content to be rendered as the fallback content until the route is ready to render
349
312
  pendingComponent?: RegisteredPendingRouteComponent<
350
313
  TFullSearchSchema,
351
314
  TAllParams,
352
- TRouteContext,
353
315
  TAllContext
354
316
  >
355
317
  // Filter functions that can manipulate search params *before* they are passed to links and navigate
@@ -438,7 +400,7 @@ export interface LoaderContext<
438
400
  abortController: AbortController
439
401
  preload: boolean
440
402
  params: TAllParams
441
- context: DeepMergeAll<[TAllContext, TLoaderContext, TRouteContext]>
403
+ context: Expand<DeepMergeAll<[TAllContext, TLoaderContext, TRouteContext]>>
442
404
  }
443
405
 
444
406
  export type SearchFilter<T, U = T> = (prev: T) => U
@@ -635,10 +597,8 @@ export class Route<
635
597
  > &
636
598
  UpdatableRouteOptions<
637
599
  TLoader,
638
- TSearchSchema,
639
600
  TFullSearchSchema,
640
601
  TAllParams,
641
- TRouteContext,
642
602
  TAllContext
643
603
  >,
644
604
  ) {
@@ -743,10 +703,8 @@ export class Route<
743
703
  update = (
744
704
  options: UpdatableRouteOptions<
745
705
  TLoader,
746
- TSearchSchema,
747
706
  TFullSearchSchema,
748
707
  TAllParams,
749
- TRouteContext,
750
708
  TAllContext
751
709
  >,
752
710
  ) => {
package/src/routeInfo.ts CHANGED
@@ -61,9 +61,7 @@ export type RoutePaths<TRouteTree extends AnyRoute> =
61
61
 
62
62
  export type FullSearchSchema<TRouteTree extends AnyRoute> = Partial<
63
63
  Expand<
64
- UnionToIntersection<
65
- ParseRoute<TRouteTree>['types']['fullSearchSchema']
66
- > & {}
64
+ UnionToIntersection<ParseRoute<TRouteTree>['types']['fullSearchSchema']>
67
65
  >
68
66
  >
69
67
 
package/src/router.ts CHANGED
@@ -163,19 +163,16 @@ export interface RouterOptions<
163
163
  unknown,
164
164
  AnySearchSchema,
165
165
  AnyPathParams,
166
- AnyContext,
167
166
  AnyContext
168
167
  >
169
168
  defaultErrorComponent?: RegisteredErrorRouteComponent<
170
169
  AnySearchSchema,
171
170
  AnyPathParams,
172
- AnyContext,
173
171
  AnyContext
174
172
  >
175
173
  defaultPendingComponent?: RegisteredPendingRouteComponent<
176
174
  AnySearchSchema,
177
175
  AnyPathParams,
178
- AnyContext,
179
176
  AnyContext
180
177
  >
181
178
  defaultMaxAge?: number
package/src/utils.ts CHANGED
@@ -19,6 +19,7 @@ export type PickRequired<T> = {
19
19
  [K in keyof T as undefined extends T[K] ? never : K]: T[K]
20
20
  }
21
21
 
22
+ // export type Expand<T> = T
22
23
  export type Expand<T> = T extends object
23
24
  ? T extends infer O
24
25
  ? { [K in keyof O]: O[K] }