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

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.
@@ -8,294 +8,13 @@
8
8
  *
9
9
  * @license MIT
10
10
  */
11
+ import { createMemoryHistory, createBrowserHistory } from '@tanstack/history';
12
+ export * from '@tanstack/history';
11
13
  import invariant from 'tiny-invariant';
12
14
  export { default as invariant } from 'tiny-invariant';
13
15
  export { default as warning } from 'tiny-warning';
14
16
  import { Store } from '@tanstack/store';
15
17
 
16
- // While the public API was clearly inspired by the "history" npm package,
17
- // This implementation attempts to be more lightweight by
18
- // making assumptions about the way TanStack Router works
19
-
20
- const pushStateEvent = 'pushstate';
21
- const popStateEvent = 'popstate';
22
- const beforeUnloadEvent = 'beforeunload';
23
- const beforeUnloadListener = event => {
24
- event.preventDefault();
25
- // @ts-ignore
26
- return event.returnValue = '';
27
- };
28
- const stopBlocking = () => {
29
- removeEventListener(beforeUnloadEvent, beforeUnloadListener, {
30
- capture: true
31
- });
32
- };
33
- function createHistory(opts) {
34
- let location = opts.getLocation();
35
- let unsub = () => {};
36
- let subscribers = new Set();
37
- let blockers = [];
38
- let queue = [];
39
- const tryFlush = () => {
40
- if (blockers.length) {
41
- blockers[0]?.(tryFlush, () => {
42
- blockers = [];
43
- stopBlocking();
44
- });
45
- return;
46
- }
47
- while (queue.length) {
48
- queue.shift()?.();
49
- }
50
- if (!opts.subscriber) {
51
- onUpdate();
52
- }
53
- };
54
- const queueTask = task => {
55
- queue.push(task);
56
- tryFlush();
57
- };
58
- const onUpdate = () => {
59
- location = opts.getLocation();
60
- subscribers.forEach(subscriber => subscriber());
61
- };
62
- return {
63
- get location() {
64
- return location;
65
- },
66
- subscribe: cb => {
67
- if (subscribers.size === 0) {
68
- unsub = typeof opts.subscriber === 'function' ? opts.subscriber(onUpdate) : () => {};
69
- }
70
- subscribers.add(cb);
71
- return () => {
72
- subscribers.delete(cb);
73
- if (subscribers.size === 0) {
74
- unsub();
75
- }
76
- };
77
- },
78
- push: (path, state) => {
79
- assignKey(state);
80
- queueTask(() => {
81
- opts.pushState(path, state, onUpdate);
82
- });
83
- },
84
- replace: (path, state) => {
85
- assignKey(state);
86
- queueTask(() => {
87
- opts.replaceState(path, state, onUpdate);
88
- });
89
- },
90
- go: index => {
91
- queueTask(() => {
92
- opts.go(index);
93
- });
94
- },
95
- back: () => {
96
- queueTask(() => {
97
- opts.back();
98
- });
99
- },
100
- forward: () => {
101
- queueTask(() => {
102
- opts.forward();
103
- });
104
- },
105
- createHref: str => opts.createHref(str),
106
- block: cb => {
107
- blockers.push(cb);
108
- if (blockers.length === 1) {
109
- addEventListener(beforeUnloadEvent, beforeUnloadListener, {
110
- capture: true
111
- });
112
- }
113
- return () => {
114
- blockers = blockers.filter(b => b !== cb);
115
- if (!blockers.length) {
116
- stopBlocking();
117
- }
118
- };
119
- },
120
- flush: () => opts.flush?.()
121
- };
122
- }
123
- function assignKey(state) {
124
- state.key = createRandomKey();
125
- // if (state.__actualLocation) {
126
- // state.__actualLocation.state = {
127
- // ...state.__actualLocation.state,
128
- // key,
129
- // }
130
- // }
131
- }
132
-
133
- /**
134
- * Creates a history object that can be used to interact with the browser's
135
- * navigation. This is a lightweight API wrapping the browser's native methods.
136
- * It is designed to work with TanStack Router, but could be used as a standalone API as well.
137
- * IMPORTANT: This API implements history throttling via a microtask to prevent
138
- * excessive calls to the history API. In some browsers, calling history.pushState or
139
- * history.replaceState in quick succession can cause the browser to ignore subsequent
140
- * calls. This API smooths out those differences and ensures that your application
141
- * state will *eventually* match the browser state. In most cases, this is not a problem,
142
- * but if you need to ensure that the browser state is up to date, you can use the
143
- * `history.flush` method to immediately flush all pending state changes to the browser URL.
144
- * @param opts
145
- * @param opts.getHref A function that returns the current href (path + search + hash)
146
- * @param opts.createHref A function that takes a path and returns a href (path + search + hash)
147
- * @returns A history instance
148
- */
149
- function createBrowserHistory(opts) {
150
- const getHref = opts?.getHref ?? (() => `${window.location.pathname}${window.location.search}${window.location.hash}`);
151
- const createHref = opts?.createHref ?? (path => path);
152
- let currentLocation = parseLocation(getHref(), window.history.state);
153
- const getLocation = () => currentLocation;
154
- let next;
155
-
156
- // Because we are proactively updating the location
157
- // in memory before actually updating the browser history,
158
- // we need to track when we are doing this so we don't
159
- // notify subscribers twice on the last update.
160
- let tracking = true;
161
-
162
- // We need to track the current scheduled update to prevent
163
- // multiple updates from being scheduled at the same time.
164
- let scheduled;
165
-
166
- // This function is a wrapper to prevent any of the callback's
167
- // side effects from causing a subscriber notification
168
- const untrack = fn => {
169
- tracking = false;
170
- fn();
171
- tracking = true;
172
- };
173
-
174
- // This function flushes the next update to the browser history
175
- const flush = () => {
176
- // Do not notify subscribers about this push/replace call
177
- untrack(() => {
178
- if (!next) return;
179
- window.history[next.isPush ? 'pushState' : 'replaceState'](next.state, '', next.href);
180
- // Reset the nextIsPush flag and clear the scheduled update
181
- next = undefined;
182
- scheduled = undefined;
183
- });
184
- };
185
-
186
- // This function queues up a call to update the browser history
187
- const queueHistoryAction = (type, path, state, onUpdate) => {
188
- const href = createHref(path);
189
-
190
- // Update the location in memory
191
- currentLocation = parseLocation(href, state);
192
-
193
- // Keep track of the next location we need to flush to the URL
194
- next = {
195
- href,
196
- state,
197
- isPush: next?.isPush || type === 'push'
198
- };
199
- // Notify subscribers
200
- onUpdate();
201
- if (!scheduled) {
202
- // Schedule an update to the browser history
203
- scheduled = Promise.resolve().then(() => flush());
204
- }
205
- };
206
- return createHistory({
207
- getLocation,
208
- subscriber: onUpdate => {
209
- window.addEventListener(pushStateEvent, () => {
210
- currentLocation = parseLocation(getHref(), window.history.state);
211
- onUpdate();
212
- });
213
- window.addEventListener(popStateEvent, () => {
214
- currentLocation = parseLocation(getHref(), window.history.state);
215
- onUpdate();
216
- });
217
- var pushState = window.history.pushState;
218
- window.history.pushState = function () {
219
- let res = pushState.apply(history, arguments);
220
- if (tracking) onUpdate();
221
- return res;
222
- };
223
- var replaceState = window.history.replaceState;
224
- window.history.replaceState = function () {
225
- let res = replaceState.apply(history, arguments);
226
- if (tracking) onUpdate();
227
- return res;
228
- };
229
- return () => {
230
- window.history.pushState = pushState;
231
- window.history.replaceState = replaceState;
232
- window.removeEventListener(pushStateEvent, onUpdate);
233
- window.removeEventListener(popStateEvent, onUpdate);
234
- };
235
- },
236
- pushState: (path, state, onUpdate) => queueHistoryAction('push', path, state, onUpdate),
237
- replaceState: (path, state, onUpdate) => queueHistoryAction('replace', path, state, onUpdate),
238
- back: () => window.history.back(),
239
- forward: () => window.history.forward(),
240
- go: n => window.history.go(n),
241
- createHref: path => createHref(path),
242
- flush
243
- });
244
- }
245
- function createHashHistory() {
246
- return createBrowserHistory({
247
- getHref: () => window.location.hash.substring(1),
248
- createHref: path => `#${path}`
249
- });
250
- }
251
- function createMemoryHistory(opts = {
252
- initialEntries: ['/']
253
- }) {
254
- const entries = opts.initialEntries;
255
- let index = opts.initialIndex ?? entries.length - 1;
256
- let currentState = {
257
- key: createRandomKey()
258
- };
259
- const getLocation = () => parseLocation(entries[index], currentState);
260
- return createHistory({
261
- getLocation,
262
- subscriber: false,
263
- pushState: (path, state) => {
264
- currentState = state;
265
- entries.push(path);
266
- index++;
267
- },
268
- replaceState: (path, state) => {
269
- currentState = state;
270
- entries[index] = path;
271
- },
272
- back: () => {
273
- index--;
274
- },
275
- forward: () => {
276
- index = Math.min(index + 1, entries.length - 1);
277
- },
278
- go: n => window.history.go(n),
279
- createHref: path => path
280
- });
281
- }
282
- function parseLocation(href, state) {
283
- let hashIndex = href.indexOf('#');
284
- let searchIndex = href.indexOf('?');
285
- return {
286
- href,
287
- pathname: href.substring(0, hashIndex > 0 ? searchIndex > 0 ? Math.min(hashIndex, searchIndex) : hashIndex : searchIndex > 0 ? searchIndex : href.length),
288
- hash: hashIndex > -1 ? href.substring(hashIndex) : '',
289
- search: searchIndex > -1 ? href.slice(searchIndex, hashIndex === -1 ? undefined : hashIndex) : '',
290
- state: state || {}
291
- };
292
- }
293
-
294
- // Thanks co-pilot!
295
- function createRandomKey() {
296
- return (Math.random() + 1).toString(36).substring(7);
297
- }
298
-
299
18
  // export type Expand<T> = T
300
19
 
301
20
  // type Compute<T> = { [K in keyof T]: T[K] } | never
@@ -2187,5 +1906,5 @@ function isDehydratedDeferred(obj) {
2187
1906
  return typeof obj === 'object' && obj !== null && !(obj instanceof Promise) && !obj.then && '__deferredState' in obj;
2188
1907
  }
2189
1908
 
2190
- export { FileRoute, PathParamError, RootRoute, Route, Router, RouterContext, SearchParamError, cleanPath, componentTypes, createBrowserHistory, createHashHistory, createMemoryHistory, createRouteMask, decode, defaultParseSearch, defaultStringifySearch, defer, encode, functionalUpdate, interpolatePath, isDehydratedDeferred, isMatchInvalid, isPlainObject, isRedirect, joinPaths, last, lazyFn, matchByPath, matchPathname, parsePathname, parseSearchWith, partialDeepEqual, pick, redirect, replaceEqualDeep, resolvePath, restoreScrollPositions, rootRouteId, stringifySearchWith, trimPath, trimPathLeft, trimPathRight, watchScrollPositions };
1909
+ export { FileRoute, PathParamError, RootRoute, Route, Router, RouterContext, SearchParamError, cleanPath, componentTypes, createRouteMask, decode, defaultParseSearch, defaultStringifySearch, defer, encode, functionalUpdate, interpolatePath, isDehydratedDeferred, isMatchInvalid, isPlainObject, isRedirect, joinPaths, last, lazyFn, matchByPath, matchPathname, parsePathname, parseSearchWith, partialDeepEqual, pick, redirect, replaceEqualDeep, resolvePath, restoreScrollPositions, rootRouteId, stringifySearchWith, trimPath, trimPathLeft, trimPathRight, watchScrollPositions };
2191
1910
  //# sourceMappingURL=index.js.map