@tanstack/router-core 0.0.1-beta.5 → 0.0.1-beta.51
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/LICENSE +21 -0
- package/build/cjs/actions.js +94 -0
- package/build/cjs/actions.js.map +1 -0
- package/build/cjs/history.js +163 -0
- package/build/cjs/history.js.map +1 -0
- package/build/cjs/{packages/router-core/src/index.js → index.js} +26 -11
- package/build/cjs/{packages/router-core/src/index.js.map → index.js.map} +1 -1
- package/build/cjs/interop.js +175 -0
- package/build/cjs/interop.js.map +1 -0
- package/build/cjs/{packages/router-core/src/path.js → path.js} +23 -48
- package/build/cjs/path.js.map +1 -0
- package/build/cjs/{packages/router-core/src/qss.js → qss.js} +8 -13
- package/build/cjs/qss.js.map +1 -0
- package/build/cjs/route.js +33 -0
- package/build/cjs/route.js.map +1 -0
- package/build/cjs/{packages/router-core/src/routeConfig.js → routeConfig.js} +13 -18
- package/build/cjs/routeConfig.js.map +1 -0
- package/build/cjs/routeMatch.js +237 -0
- package/build/cjs/routeMatch.js.map +1 -0
- package/build/cjs/router.js +824 -0
- package/build/cjs/router.js.map +1 -0
- package/build/cjs/{packages/router-core/src/searchParams.js → searchParams.js} +10 -12
- package/build/cjs/searchParams.js.map +1 -0
- package/build/cjs/store.js +54 -0
- package/build/cjs/store.js.map +1 -0
- package/build/cjs/utils.js +47 -0
- package/build/cjs/utils.js.map +1 -0
- package/build/esm/index.js +1386 -2058
- package/build/esm/index.js.map +1 -1
- package/build/stats-html.html +59 -49
- package/build/stats-react.json +248 -193
- package/build/types/index.d.ts +385 -317
- package/build/umd/index.development.js +1489 -2142
- package/build/umd/index.development.js.map +1 -1
- package/build/umd/index.production.js +1 -1
- package/build/umd/index.production.js.map +1 -1
- package/package.json +6 -4
- package/src/actions.ts +157 -0
- package/src/frameworks.ts +2 -2
- package/src/history.ts +199 -0
- package/src/index.ts +4 -7
- package/src/interop.ts +169 -0
- package/src/link.ts +87 -44
- package/src/path.ts +12 -8
- package/src/route.ts +36 -229
- package/src/routeConfig.ts +99 -102
- package/src/routeInfo.ts +28 -25
- package/src/routeMatch.ts +293 -322
- package/src/router.ts +1060 -884
- package/src/store.ts +52 -0
- package/src/utils.ts +14 -72
- package/build/cjs/_virtual/_rollupPluginBabelHelpers.js +0 -33
- package/build/cjs/_virtual/_rollupPluginBabelHelpers.js.map +0 -1
- package/build/cjs/node_modules/@babel/runtime/helpers/esm/extends.js +0 -33
- package/build/cjs/node_modules/@babel/runtime/helpers/esm/extends.js.map +0 -1
- package/build/cjs/node_modules/history/index.js +0 -815
- package/build/cjs/node_modules/history/index.js.map +0 -1
- package/build/cjs/node_modules/tiny-invariant/dist/esm/tiny-invariant.js +0 -30
- package/build/cjs/node_modules/tiny-invariant/dist/esm/tiny-invariant.js.map +0 -1
- package/build/cjs/packages/router-core/src/path.js.map +0 -1
- package/build/cjs/packages/router-core/src/qss.js.map +0 -1
- package/build/cjs/packages/router-core/src/route.js +0 -161
- package/build/cjs/packages/router-core/src/route.js.map +0 -1
- package/build/cjs/packages/router-core/src/routeConfig.js.map +0 -1
- package/build/cjs/packages/router-core/src/routeMatch.js +0 -266
- package/build/cjs/packages/router-core/src/routeMatch.js.map +0 -1
- package/build/cjs/packages/router-core/src/router.js +0 -797
- package/build/cjs/packages/router-core/src/router.js.map +0 -1
- package/build/cjs/packages/router-core/src/searchParams.js.map +0 -1
- package/build/cjs/packages/router-core/src/utils.js +0 -118
- package/build/cjs/packages/router-core/src/utils.js.map +0 -1
package/build/esm/index.js
CHANGED
|
@@ -8,900 +8,153 @@
|
|
|
8
8
|
*
|
|
9
9
|
* @license MIT
|
|
10
10
|
*/
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
11
|
+
import invariant from 'tiny-invariant';
|
|
12
|
+
export { default as invariant } from 'tiny-invariant';
|
|
13
|
+
import { setAutoFreeze, produce } from 'immer';
|
|
14
|
+
|
|
15
|
+
// While the public API was clearly inspired by the "history" npm package,
|
|
16
|
+
// This implementation attempts to be more lightweight by
|
|
17
|
+
// making assumptions about the way TanStack Router works
|
|
18
|
+
|
|
19
|
+
const popStateEvent = 'popstate';
|
|
20
|
+
function createHistory(opts) {
|
|
21
|
+
let currentLocation = opts.getLocation();
|
|
22
|
+
let unsub = () => {};
|
|
23
|
+
let listeners = new Set();
|
|
24
|
+
const onUpdate = () => {
|
|
25
|
+
currentLocation = opts.getLocation();
|
|
26
|
+
listeners.forEach(listener => listener());
|
|
24
27
|
};
|
|
25
|
-
return
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Actions represent the type of change to a location value.
|
|
30
|
-
*
|
|
31
|
-
* @see https://github.com/remix-run/history/tree/main/docs/api-reference.md#action
|
|
32
|
-
*/
|
|
33
|
-
var Action;
|
|
34
|
-
|
|
35
|
-
(function (Action) {
|
|
36
|
-
/**
|
|
37
|
-
* A POP indicates a change to an arbitrary index in the history stack, such
|
|
38
|
-
* as a back or forward navigation. It does not describe the direction of the
|
|
39
|
-
* navigation, only that the current index changed.
|
|
40
|
-
*
|
|
41
|
-
* Note: This is the default action for newly created history objects.
|
|
42
|
-
*/
|
|
43
|
-
Action["Pop"] = "POP";
|
|
44
|
-
/**
|
|
45
|
-
* A PUSH indicates a new entry being added to the history stack, such as when
|
|
46
|
-
* a link is clicked and a new page loads. When this happens, all subsequent
|
|
47
|
-
* entries in the stack are lost.
|
|
48
|
-
*/
|
|
49
|
-
|
|
50
|
-
Action["Push"] = "PUSH";
|
|
51
|
-
/**
|
|
52
|
-
* A REPLACE indicates the entry at the current index in the history stack
|
|
53
|
-
* being replaced by a new one.
|
|
54
|
-
*/
|
|
55
|
-
|
|
56
|
-
Action["Replace"] = "REPLACE";
|
|
57
|
-
})(Action || (Action = {}));
|
|
58
|
-
|
|
59
|
-
var readOnly = process.env.NODE_ENV !== "production" ? function (obj) {
|
|
60
|
-
return Object.freeze(obj);
|
|
61
|
-
} : function (obj) {
|
|
62
|
-
return obj;
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
function warning$1(cond, message) {
|
|
66
|
-
if (!cond) {
|
|
67
|
-
// eslint-disable-next-line no-console
|
|
68
|
-
if (typeof console !== 'undefined') console.warn(message);
|
|
69
|
-
|
|
70
|
-
try {
|
|
71
|
-
// Welcome to debugging history!
|
|
72
|
-
//
|
|
73
|
-
// This error is thrown as a convenience so you can more easily
|
|
74
|
-
// find the source for a warning that appears in the console by
|
|
75
|
-
// enabling "pause on exceptions" in your JavaScript debugger.
|
|
76
|
-
throw new Error(message); // eslint-disable-next-line no-empty
|
|
77
|
-
} catch (e) {}
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
var BeforeUnloadEventType = 'beforeunload';
|
|
82
|
-
var HashChangeEventType = 'hashchange';
|
|
83
|
-
var PopStateEventType = 'popstate';
|
|
84
|
-
/**
|
|
85
|
-
* Browser history stores the location in regular URLs. This is the standard for
|
|
86
|
-
* most web apps, but it requires some configuration on the server to ensure you
|
|
87
|
-
* serve the same app at multiple URLs.
|
|
88
|
-
*
|
|
89
|
-
* @see https://github.com/remix-run/history/tree/main/docs/api-reference.md#createbrowserhistory
|
|
90
|
-
*/
|
|
91
|
-
|
|
92
|
-
function createBrowserHistory(options) {
|
|
93
|
-
if (options === void 0) {
|
|
94
|
-
options = {};
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
var _options = options,
|
|
98
|
-
_options$window = _options.window,
|
|
99
|
-
window = _options$window === void 0 ? document.defaultView : _options$window;
|
|
100
|
-
var globalHistory = window.history;
|
|
101
|
-
|
|
102
|
-
function getIndexAndLocation() {
|
|
103
|
-
var _window$location = window.location,
|
|
104
|
-
pathname = _window$location.pathname,
|
|
105
|
-
search = _window$location.search,
|
|
106
|
-
hash = _window$location.hash;
|
|
107
|
-
var state = globalHistory.state || {};
|
|
108
|
-
return [state.idx, readOnly({
|
|
109
|
-
pathname: pathname,
|
|
110
|
-
search: search,
|
|
111
|
-
hash: hash,
|
|
112
|
-
state: state.usr || null,
|
|
113
|
-
key: state.key || 'default'
|
|
114
|
-
})];
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
var blockedPopTx = null;
|
|
118
|
-
|
|
119
|
-
function handlePop() {
|
|
120
|
-
if (blockedPopTx) {
|
|
121
|
-
blockers.call(blockedPopTx);
|
|
122
|
-
blockedPopTx = null;
|
|
123
|
-
} else {
|
|
124
|
-
var nextAction = Action.Pop;
|
|
125
|
-
|
|
126
|
-
var _getIndexAndLocation = getIndexAndLocation(),
|
|
127
|
-
nextIndex = _getIndexAndLocation[0],
|
|
128
|
-
nextLocation = _getIndexAndLocation[1];
|
|
129
|
-
|
|
130
|
-
if (blockers.length) {
|
|
131
|
-
if (nextIndex != null) {
|
|
132
|
-
var delta = index - nextIndex;
|
|
133
|
-
|
|
134
|
-
if (delta) {
|
|
135
|
-
// Revert the POP
|
|
136
|
-
blockedPopTx = {
|
|
137
|
-
action: nextAction,
|
|
138
|
-
location: nextLocation,
|
|
139
|
-
retry: function retry() {
|
|
140
|
-
go(delta * -1);
|
|
141
|
-
}
|
|
142
|
-
};
|
|
143
|
-
go(delta);
|
|
144
|
-
}
|
|
145
|
-
} else {
|
|
146
|
-
// Trying to POP to a location with no index. We did not create
|
|
147
|
-
// this location, so we can't effectively block the navigation.
|
|
148
|
-
process.env.NODE_ENV !== "production" ? warning$1(false, // TODO: Write up a doc that explains our blocking strategy in
|
|
149
|
-
// detail and link to it here so people can understand better what
|
|
150
|
-
// is going on and how to avoid it.
|
|
151
|
-
"You are trying to block a POP navigation to a location that was not " + "created by the history library. The block will fail silently in " + "production, but in general you should do all navigation with the " + "history library (instead of using window.history.pushState directly) " + "to avoid this situation.") : void 0;
|
|
152
|
-
}
|
|
153
|
-
} else {
|
|
154
|
-
applyTx(nextAction);
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
window.addEventListener(PopStateEventType, handlePop);
|
|
160
|
-
var action = Action.Pop;
|
|
161
|
-
|
|
162
|
-
var _getIndexAndLocation2 = getIndexAndLocation(),
|
|
163
|
-
index = _getIndexAndLocation2[0],
|
|
164
|
-
location = _getIndexAndLocation2[1];
|
|
165
|
-
|
|
166
|
-
var listeners = createEvents();
|
|
167
|
-
var blockers = createEvents();
|
|
168
|
-
|
|
169
|
-
if (index == null) {
|
|
170
|
-
index = 0;
|
|
171
|
-
globalHistory.replaceState(_extends$1({}, globalHistory.state, {
|
|
172
|
-
idx: index
|
|
173
|
-
}), '');
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
function createHref(to) {
|
|
177
|
-
return typeof to === 'string' ? to : createPath(to);
|
|
178
|
-
} // state defaults to `null` because `window.history.state` does
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
function getNextLocation(to, state) {
|
|
182
|
-
if (state === void 0) {
|
|
183
|
-
state = null;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
return readOnly(_extends$1({
|
|
187
|
-
pathname: location.pathname,
|
|
188
|
-
hash: '',
|
|
189
|
-
search: ''
|
|
190
|
-
}, typeof to === 'string' ? parsePath(to) : to, {
|
|
191
|
-
state: state,
|
|
192
|
-
key: createKey()
|
|
193
|
-
}));
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
function getHistoryStateAndUrl(nextLocation, index) {
|
|
197
|
-
return [{
|
|
198
|
-
usr: nextLocation.state,
|
|
199
|
-
key: nextLocation.key,
|
|
200
|
-
idx: index
|
|
201
|
-
}, createHref(nextLocation)];
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
function allowTx(action, location, retry) {
|
|
205
|
-
return !blockers.length || (blockers.call({
|
|
206
|
-
action: action,
|
|
207
|
-
location: location,
|
|
208
|
-
retry: retry
|
|
209
|
-
}), false);
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
function applyTx(nextAction) {
|
|
213
|
-
action = nextAction;
|
|
214
|
-
|
|
215
|
-
var _getIndexAndLocation3 = getIndexAndLocation();
|
|
216
|
-
|
|
217
|
-
index = _getIndexAndLocation3[0];
|
|
218
|
-
location = _getIndexAndLocation3[1];
|
|
219
|
-
listeners.call({
|
|
220
|
-
action: action,
|
|
221
|
-
location: location
|
|
222
|
-
});
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
function push(to, state) {
|
|
226
|
-
var nextAction = Action.Push;
|
|
227
|
-
var nextLocation = getNextLocation(to, state);
|
|
228
|
-
|
|
229
|
-
function retry() {
|
|
230
|
-
push(to, state);
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
if (allowTx(nextAction, nextLocation, retry)) {
|
|
234
|
-
var _getHistoryStateAndUr = getHistoryStateAndUrl(nextLocation, index + 1),
|
|
235
|
-
historyState = _getHistoryStateAndUr[0],
|
|
236
|
-
url = _getHistoryStateAndUr[1]; // TODO: Support forced reloading
|
|
237
|
-
// try...catch because iOS limits us to 100 pushState calls :/
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
try {
|
|
241
|
-
globalHistory.pushState(historyState, '', url);
|
|
242
|
-
} catch (error) {
|
|
243
|
-
// They are going to lose state here, but there is no real
|
|
244
|
-
// way to warn them about it since the page will refresh...
|
|
245
|
-
window.location.assign(url);
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
applyTx(nextAction);
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
function replace(to, state) {
|
|
253
|
-
var nextAction = Action.Replace;
|
|
254
|
-
var nextLocation = getNextLocation(to, state);
|
|
255
|
-
|
|
256
|
-
function retry() {
|
|
257
|
-
replace(to, state);
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
if (allowTx(nextAction, nextLocation, retry)) {
|
|
261
|
-
var _getHistoryStateAndUr2 = getHistoryStateAndUrl(nextLocation, index),
|
|
262
|
-
historyState = _getHistoryStateAndUr2[0],
|
|
263
|
-
url = _getHistoryStateAndUr2[1]; // TODO: Support forced reloading
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
globalHistory.replaceState(historyState, '', url);
|
|
267
|
-
applyTx(nextAction);
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
function go(delta) {
|
|
272
|
-
globalHistory.go(delta);
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
var history = {
|
|
276
|
-
get action() {
|
|
277
|
-
return action;
|
|
278
|
-
},
|
|
279
|
-
|
|
28
|
+
return {
|
|
280
29
|
get location() {
|
|
281
|
-
return
|
|
282
|
-
},
|
|
283
|
-
|
|
284
|
-
createHref: createHref,
|
|
285
|
-
push: push,
|
|
286
|
-
replace: replace,
|
|
287
|
-
go: go,
|
|
288
|
-
back: function back() {
|
|
289
|
-
go(-1);
|
|
30
|
+
return currentLocation;
|
|
290
31
|
},
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
listen: function listen(listener) {
|
|
295
|
-
return listeners.push(listener);
|
|
296
|
-
},
|
|
297
|
-
block: function block(blocker) {
|
|
298
|
-
var unblock = blockers.push(blocker);
|
|
299
|
-
|
|
300
|
-
if (blockers.length === 1) {
|
|
301
|
-
window.addEventListener(BeforeUnloadEventType, promptBeforeUnload);
|
|
32
|
+
listen: cb => {
|
|
33
|
+
if (listeners.size === 0) {
|
|
34
|
+
unsub = opts.listener(onUpdate);
|
|
302
35
|
}
|
|
303
|
-
|
|
304
|
-
return
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
if (!blockers.length) {
|
|
310
|
-
window.removeEventListener(BeforeUnloadEventType, promptBeforeUnload);
|
|
36
|
+
listeners.add(cb);
|
|
37
|
+
return () => {
|
|
38
|
+
listeners.delete(cb);
|
|
39
|
+
if (listeners.size === 0) {
|
|
40
|
+
unsub();
|
|
311
41
|
}
|
|
312
42
|
};
|
|
313
|
-
}
|
|
314
|
-
};
|
|
315
|
-
return history;
|
|
316
|
-
}
|
|
317
|
-
/**
|
|
318
|
-
* Hash history stores the location in window.location.hash. This makes it ideal
|
|
319
|
-
* for situations where you don't want to send the location to the server for
|
|
320
|
-
* some reason, either because you do cannot configure it or the URL space is
|
|
321
|
-
* reserved for something else.
|
|
322
|
-
*
|
|
323
|
-
* @see https://github.com/remix-run/history/tree/main/docs/api-reference.md#createhashhistory
|
|
324
|
-
*/
|
|
325
|
-
|
|
326
|
-
function createHashHistory(options) {
|
|
327
|
-
if (options === void 0) {
|
|
328
|
-
options = {};
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
var _options2 = options,
|
|
332
|
-
_options2$window = _options2.window,
|
|
333
|
-
window = _options2$window === void 0 ? document.defaultView : _options2$window;
|
|
334
|
-
var globalHistory = window.history;
|
|
335
|
-
|
|
336
|
-
function getIndexAndLocation() {
|
|
337
|
-
var _parsePath = parsePath(window.location.hash.substr(1)),
|
|
338
|
-
_parsePath$pathname = _parsePath.pathname,
|
|
339
|
-
pathname = _parsePath$pathname === void 0 ? '/' : _parsePath$pathname,
|
|
340
|
-
_parsePath$search = _parsePath.search,
|
|
341
|
-
search = _parsePath$search === void 0 ? '' : _parsePath$search,
|
|
342
|
-
_parsePath$hash = _parsePath.hash,
|
|
343
|
-
hash = _parsePath$hash === void 0 ? '' : _parsePath$hash;
|
|
344
|
-
|
|
345
|
-
var state = globalHistory.state || {};
|
|
346
|
-
return [state.idx, readOnly({
|
|
347
|
-
pathname: pathname,
|
|
348
|
-
search: search,
|
|
349
|
-
hash: hash,
|
|
350
|
-
state: state.usr || null,
|
|
351
|
-
key: state.key || 'default'
|
|
352
|
-
})];
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
var blockedPopTx = null;
|
|
356
|
-
|
|
357
|
-
function handlePop() {
|
|
358
|
-
if (blockedPopTx) {
|
|
359
|
-
blockers.call(blockedPopTx);
|
|
360
|
-
blockedPopTx = null;
|
|
361
|
-
} else {
|
|
362
|
-
var nextAction = Action.Pop;
|
|
363
|
-
|
|
364
|
-
var _getIndexAndLocation4 = getIndexAndLocation(),
|
|
365
|
-
nextIndex = _getIndexAndLocation4[0],
|
|
366
|
-
nextLocation = _getIndexAndLocation4[1];
|
|
367
|
-
|
|
368
|
-
if (blockers.length) {
|
|
369
|
-
if (nextIndex != null) {
|
|
370
|
-
var delta = index - nextIndex;
|
|
371
|
-
|
|
372
|
-
if (delta) {
|
|
373
|
-
// Revert the POP
|
|
374
|
-
blockedPopTx = {
|
|
375
|
-
action: nextAction,
|
|
376
|
-
location: nextLocation,
|
|
377
|
-
retry: function retry() {
|
|
378
|
-
go(delta * -1);
|
|
379
|
-
}
|
|
380
|
-
};
|
|
381
|
-
go(delta);
|
|
382
|
-
}
|
|
383
|
-
} else {
|
|
384
|
-
// Trying to POP to a location with no index. We did not create
|
|
385
|
-
// this location, so we can't effectively block the navigation.
|
|
386
|
-
process.env.NODE_ENV !== "production" ? warning$1(false, // TODO: Write up a doc that explains our blocking strategy in
|
|
387
|
-
// detail and link to it here so people can understand better
|
|
388
|
-
// what is going on and how to avoid it.
|
|
389
|
-
"You are trying to block a POP navigation to a location that was not " + "created by the history library. The block will fail silently in " + "production, but in general you should do all navigation with the " + "history library (instead of using window.history.pushState directly) " + "to avoid this situation.") : void 0;
|
|
390
|
-
}
|
|
391
|
-
} else {
|
|
392
|
-
applyTx(nextAction);
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
window.addEventListener(PopStateEventType, handlePop); // popstate does not fire on hashchange in IE 11 and old (trident) Edge
|
|
398
|
-
// https://developer.mozilla.org/de/docs/Web/API/Window/popstate_event
|
|
399
|
-
|
|
400
|
-
window.addEventListener(HashChangeEventType, function () {
|
|
401
|
-
var _getIndexAndLocation5 = getIndexAndLocation(),
|
|
402
|
-
nextLocation = _getIndexAndLocation5[1]; // Ignore extraneous hashchange events.
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
if (createPath(nextLocation) !== createPath(location)) {
|
|
406
|
-
handlePop();
|
|
407
|
-
}
|
|
408
|
-
});
|
|
409
|
-
var action = Action.Pop;
|
|
410
|
-
|
|
411
|
-
var _getIndexAndLocation6 = getIndexAndLocation(),
|
|
412
|
-
index = _getIndexAndLocation6[0],
|
|
413
|
-
location = _getIndexAndLocation6[1];
|
|
414
|
-
|
|
415
|
-
var listeners = createEvents();
|
|
416
|
-
var blockers = createEvents();
|
|
417
|
-
|
|
418
|
-
if (index == null) {
|
|
419
|
-
index = 0;
|
|
420
|
-
globalHistory.replaceState(_extends$1({}, globalHistory.state, {
|
|
421
|
-
idx: index
|
|
422
|
-
}), '');
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
function getBaseHref() {
|
|
426
|
-
var base = document.querySelector('base');
|
|
427
|
-
var href = '';
|
|
428
|
-
|
|
429
|
-
if (base && base.getAttribute('href')) {
|
|
430
|
-
var url = window.location.href;
|
|
431
|
-
var hashIndex = url.indexOf('#');
|
|
432
|
-
href = hashIndex === -1 ? url : url.slice(0, hashIndex);
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
return href;
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
function createHref(to) {
|
|
439
|
-
return getBaseHref() + '#' + (typeof to === 'string' ? to : createPath(to));
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
function getNextLocation(to, state) {
|
|
443
|
-
if (state === void 0) {
|
|
444
|
-
state = null;
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
return readOnly(_extends$1({
|
|
448
|
-
pathname: location.pathname,
|
|
449
|
-
hash: '',
|
|
450
|
-
search: ''
|
|
451
|
-
}, typeof to === 'string' ? parsePath(to) : to, {
|
|
452
|
-
state: state,
|
|
453
|
-
key: createKey()
|
|
454
|
-
}));
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
function getHistoryStateAndUrl(nextLocation, index) {
|
|
458
|
-
return [{
|
|
459
|
-
usr: nextLocation.state,
|
|
460
|
-
key: nextLocation.key,
|
|
461
|
-
idx: index
|
|
462
|
-
}, createHref(nextLocation)];
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
function allowTx(action, location, retry) {
|
|
466
|
-
return !blockers.length || (blockers.call({
|
|
467
|
-
action: action,
|
|
468
|
-
location: location,
|
|
469
|
-
retry: retry
|
|
470
|
-
}), false);
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
function applyTx(nextAction) {
|
|
474
|
-
action = nextAction;
|
|
475
|
-
|
|
476
|
-
var _getIndexAndLocation7 = getIndexAndLocation();
|
|
477
|
-
|
|
478
|
-
index = _getIndexAndLocation7[0];
|
|
479
|
-
location = _getIndexAndLocation7[1];
|
|
480
|
-
listeners.call({
|
|
481
|
-
action: action,
|
|
482
|
-
location: location
|
|
483
|
-
});
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
function push(to, state) {
|
|
487
|
-
var nextAction = Action.Push;
|
|
488
|
-
var nextLocation = getNextLocation(to, state);
|
|
489
|
-
|
|
490
|
-
function retry() {
|
|
491
|
-
push(to, state);
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
process.env.NODE_ENV !== "production" ? warning$1(nextLocation.pathname.charAt(0) === '/', "Relative pathnames are not supported in hash history.push(" + JSON.stringify(to) + ")") : void 0;
|
|
495
|
-
|
|
496
|
-
if (allowTx(nextAction, nextLocation, retry)) {
|
|
497
|
-
var _getHistoryStateAndUr3 = getHistoryStateAndUrl(nextLocation, index + 1),
|
|
498
|
-
historyState = _getHistoryStateAndUr3[0],
|
|
499
|
-
url = _getHistoryStateAndUr3[1]; // TODO: Support forced reloading
|
|
500
|
-
// try...catch because iOS limits us to 100 pushState calls :/
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
try {
|
|
504
|
-
globalHistory.pushState(historyState, '', url);
|
|
505
|
-
} catch (error) {
|
|
506
|
-
// They are going to lose state here, but there is no real
|
|
507
|
-
// way to warn them about it since the page will refresh...
|
|
508
|
-
window.location.assign(url);
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
applyTx(nextAction);
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
function replace(to, state) {
|
|
516
|
-
var nextAction = Action.Replace;
|
|
517
|
-
var nextLocation = getNextLocation(to, state);
|
|
518
|
-
|
|
519
|
-
function retry() {
|
|
520
|
-
replace(to, state);
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
process.env.NODE_ENV !== "production" ? warning$1(nextLocation.pathname.charAt(0) === '/', "Relative pathnames are not supported in hash history.replace(" + JSON.stringify(to) + ")") : void 0;
|
|
524
|
-
|
|
525
|
-
if (allowTx(nextAction, nextLocation, retry)) {
|
|
526
|
-
var _getHistoryStateAndUr4 = getHistoryStateAndUrl(nextLocation, index),
|
|
527
|
-
historyState = _getHistoryStateAndUr4[0],
|
|
528
|
-
url = _getHistoryStateAndUr4[1]; // TODO: Support forced reloading
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
globalHistory.replaceState(historyState, '', url);
|
|
532
|
-
applyTx(nextAction);
|
|
533
|
-
}
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
function go(delta) {
|
|
537
|
-
globalHistory.go(delta);
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
var history = {
|
|
541
|
-
get action() {
|
|
542
|
-
return action;
|
|
543
43
|
},
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
44
|
+
push: (path, state) => {
|
|
45
|
+
opts.pushState(path, state);
|
|
46
|
+
onUpdate();
|
|
547
47
|
},
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
replace: replace,
|
|
552
|
-
go: go,
|
|
553
|
-
back: function back() {
|
|
554
|
-
go(-1);
|
|
48
|
+
replace: (path, state) => {
|
|
49
|
+
opts.replaceState(path, state);
|
|
50
|
+
onUpdate();
|
|
555
51
|
},
|
|
556
|
-
|
|
557
|
-
go(
|
|
52
|
+
go: index => {
|
|
53
|
+
opts.go(index);
|
|
54
|
+
onUpdate();
|
|
558
55
|
},
|
|
559
|
-
|
|
560
|
-
|
|
56
|
+
back: () => {
|
|
57
|
+
opts.back();
|
|
58
|
+
onUpdate();
|
|
561
59
|
},
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
if (blockers.length === 1) {
|
|
566
|
-
window.addEventListener(BeforeUnloadEventType, promptBeforeUnload);
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
return function () {
|
|
570
|
-
unblock(); // Remove the beforeunload listener so the document may
|
|
571
|
-
// still be salvageable in the pagehide event.
|
|
572
|
-
// See https://html.spec.whatwg.org/#unloading-documents
|
|
573
|
-
|
|
574
|
-
if (!blockers.length) {
|
|
575
|
-
window.removeEventListener(BeforeUnloadEventType, promptBeforeUnload);
|
|
576
|
-
}
|
|
577
|
-
};
|
|
60
|
+
forward: () => {
|
|
61
|
+
opts.forward();
|
|
62
|
+
onUpdate();
|
|
578
63
|
}
|
|
579
64
|
};
|
|
580
|
-
return history;
|
|
581
65
|
}
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
var _options3 = options,
|
|
595
|
-
_options3$initialEntr = _options3.initialEntries,
|
|
596
|
-
initialEntries = _options3$initialEntr === void 0 ? ['/'] : _options3$initialEntr,
|
|
597
|
-
initialIndex = _options3.initialIndex;
|
|
598
|
-
var entries = initialEntries.map(function (entry) {
|
|
599
|
-
var location = readOnly(_extends$1({
|
|
600
|
-
pathname: '/',
|
|
601
|
-
search: '',
|
|
602
|
-
hash: '',
|
|
603
|
-
state: null,
|
|
604
|
-
key: createKey()
|
|
605
|
-
}, typeof entry === 'string' ? parsePath(entry) : entry));
|
|
606
|
-
process.env.NODE_ENV !== "production" ? warning$1(location.pathname.charAt(0) === '/', "Relative pathnames are not supported in createMemoryHistory({ initialEntries }) (invalid entry: " + JSON.stringify(entry) + ")") : void 0;
|
|
607
|
-
return location;
|
|
608
|
-
});
|
|
609
|
-
var index = clamp(initialIndex == null ? entries.length - 1 : initialIndex, 0, entries.length - 1);
|
|
610
|
-
var action = Action.Pop;
|
|
611
|
-
var location = entries[index];
|
|
612
|
-
var listeners = createEvents();
|
|
613
|
-
var blockers = createEvents();
|
|
614
|
-
|
|
615
|
-
function createHref(to) {
|
|
616
|
-
return typeof to === 'string' ? to : createPath(to);
|
|
617
|
-
}
|
|
618
|
-
|
|
619
|
-
function getNextLocation(to, state) {
|
|
620
|
-
if (state === void 0) {
|
|
621
|
-
state = null;
|
|
622
|
-
}
|
|
623
|
-
|
|
624
|
-
return readOnly(_extends$1({
|
|
625
|
-
pathname: location.pathname,
|
|
626
|
-
search: '',
|
|
627
|
-
hash: ''
|
|
628
|
-
}, typeof to === 'string' ? parsePath(to) : to, {
|
|
629
|
-
state: state,
|
|
630
|
-
key: createKey()
|
|
631
|
-
}));
|
|
632
|
-
}
|
|
633
|
-
|
|
634
|
-
function allowTx(action, location, retry) {
|
|
635
|
-
return !blockers.length || (blockers.call({
|
|
636
|
-
action: action,
|
|
637
|
-
location: location,
|
|
638
|
-
retry: retry
|
|
639
|
-
}), false);
|
|
640
|
-
}
|
|
641
|
-
|
|
642
|
-
function applyTx(nextAction, nextLocation) {
|
|
643
|
-
action = nextAction;
|
|
644
|
-
location = nextLocation;
|
|
645
|
-
listeners.call({
|
|
646
|
-
action: action,
|
|
647
|
-
location: location
|
|
648
|
-
});
|
|
649
|
-
}
|
|
650
|
-
|
|
651
|
-
function push(to, state) {
|
|
652
|
-
var nextAction = Action.Push;
|
|
653
|
-
var nextLocation = getNextLocation(to, state);
|
|
654
|
-
|
|
655
|
-
function retry() {
|
|
656
|
-
push(to, state);
|
|
657
|
-
}
|
|
658
|
-
|
|
659
|
-
process.env.NODE_ENV !== "production" ? warning$1(location.pathname.charAt(0) === '/', "Relative pathnames are not supported in memory history.push(" + JSON.stringify(to) + ")") : void 0;
|
|
660
|
-
|
|
661
|
-
if (allowTx(nextAction, nextLocation, retry)) {
|
|
662
|
-
index += 1;
|
|
663
|
-
entries.splice(index, entries.length, nextLocation);
|
|
664
|
-
applyTx(nextAction, nextLocation);
|
|
665
|
-
}
|
|
666
|
-
}
|
|
667
|
-
|
|
668
|
-
function replace(to, state) {
|
|
669
|
-
var nextAction = Action.Replace;
|
|
670
|
-
var nextLocation = getNextLocation(to, state);
|
|
671
|
-
|
|
672
|
-
function retry() {
|
|
673
|
-
replace(to, state);
|
|
674
|
-
}
|
|
675
|
-
|
|
676
|
-
process.env.NODE_ENV !== "production" ? warning$1(location.pathname.charAt(0) === '/', "Relative pathnames are not supported in memory history.replace(" + JSON.stringify(to) + ")") : void 0;
|
|
677
|
-
|
|
678
|
-
if (allowTx(nextAction, nextLocation, retry)) {
|
|
679
|
-
entries[index] = nextLocation;
|
|
680
|
-
applyTx(nextAction, nextLocation);
|
|
681
|
-
}
|
|
682
|
-
}
|
|
683
|
-
|
|
684
|
-
function go(delta) {
|
|
685
|
-
var nextIndex = clamp(index + delta, 0, entries.length - 1);
|
|
686
|
-
var nextAction = Action.Pop;
|
|
687
|
-
var nextLocation = entries[nextIndex];
|
|
688
|
-
|
|
689
|
-
function retry() {
|
|
690
|
-
go(delta);
|
|
691
|
-
}
|
|
692
|
-
|
|
693
|
-
if (allowTx(nextAction, nextLocation, retry)) {
|
|
694
|
-
index = nextIndex;
|
|
695
|
-
applyTx(nextAction, nextLocation);
|
|
696
|
-
}
|
|
697
|
-
}
|
|
698
|
-
|
|
699
|
-
var history = {
|
|
700
|
-
get index() {
|
|
701
|
-
return index;
|
|
702
|
-
},
|
|
703
|
-
|
|
704
|
-
get action() {
|
|
705
|
-
return action;
|
|
706
|
-
},
|
|
707
|
-
|
|
708
|
-
get location() {
|
|
709
|
-
return location;
|
|
710
|
-
},
|
|
711
|
-
|
|
712
|
-
createHref: createHref,
|
|
713
|
-
push: push,
|
|
714
|
-
replace: replace,
|
|
715
|
-
go: go,
|
|
716
|
-
back: function back() {
|
|
717
|
-
go(-1);
|
|
66
|
+
function createBrowserHistory(opts) {
|
|
67
|
+
const getHref = opts?.getHref ?? (() => `${window.location.pathname}${window.location.hash}${window.location.search}`);
|
|
68
|
+
const createHref = opts?.createHref ?? (path => path);
|
|
69
|
+
const getLocation = () => parseLocation(getHref(), history.state);
|
|
70
|
+
return createHistory({
|
|
71
|
+
getLocation,
|
|
72
|
+
listener: onUpdate => {
|
|
73
|
+
window.addEventListener(popStateEvent, onUpdate);
|
|
74
|
+
return () => {
|
|
75
|
+
window.removeEventListener(popStateEvent, onUpdate);
|
|
76
|
+
};
|
|
718
77
|
},
|
|
719
|
-
|
|
720
|
-
|
|
78
|
+
pushState: (path, state) => {
|
|
79
|
+
window.history.pushState({
|
|
80
|
+
...state,
|
|
81
|
+
key: createRandomKey()
|
|
82
|
+
}, '', createHref(path));
|
|
721
83
|
},
|
|
722
|
-
|
|
723
|
-
|
|
84
|
+
replaceState: (path, state) => {
|
|
85
|
+
window.history.replaceState({
|
|
86
|
+
...state,
|
|
87
|
+
key: createRandomKey()
|
|
88
|
+
}, '', createHref(path));
|
|
724
89
|
},
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
};
|
|
729
|
-
return history;
|
|
730
|
-
} ////////////////////////////////////////////////////////////////////////////////
|
|
731
|
-
// UTILS
|
|
732
|
-
////////////////////////////////////////////////////////////////////////////////
|
|
733
|
-
|
|
734
|
-
function clamp(n, lowerBound, upperBound) {
|
|
735
|
-
return Math.min(Math.max(n, lowerBound), upperBound);
|
|
90
|
+
back: () => window.history.back(),
|
|
91
|
+
forward: () => window.history.forward(),
|
|
92
|
+
go: n => window.history.go(n)
|
|
93
|
+
});
|
|
736
94
|
}
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
event.returnValue = '';
|
|
95
|
+
function createHashHistory() {
|
|
96
|
+
return createBrowserHistory({
|
|
97
|
+
getHref: () => window.location.hash.substring(1),
|
|
98
|
+
createHref: path => `#${path}`
|
|
99
|
+
});
|
|
743
100
|
}
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
101
|
+
function createMemoryHistory(opts = {
|
|
102
|
+
initialEntries: ['/']
|
|
103
|
+
}) {
|
|
104
|
+
const entries = opts.initialEntries;
|
|
105
|
+
let index = opts.initialIndex ?? entries.length - 1;
|
|
106
|
+
let currentState = {};
|
|
107
|
+
const getLocation = () => parseLocation(entries[index], currentState);
|
|
108
|
+
return createHistory({
|
|
109
|
+
getLocation,
|
|
110
|
+
listener: onUpdate => {
|
|
111
|
+
window.addEventListener(popStateEvent, onUpdate);
|
|
112
|
+
// We might need to handle the hashchange event in the future
|
|
113
|
+
// window.addEventListener(hashChangeEvent, onUpdate)
|
|
114
|
+
return () => {
|
|
115
|
+
window.removeEventListener(popStateEvent, onUpdate);
|
|
116
|
+
};
|
|
750
117
|
},
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
handlers = handlers.filter(function (handler) {
|
|
756
|
-
return handler !== fn;
|
|
757
|
-
});
|
|
118
|
+
pushState: (path, state) => {
|
|
119
|
+
currentState = {
|
|
120
|
+
...state,
|
|
121
|
+
key: createRandomKey()
|
|
758
122
|
};
|
|
123
|
+
entries.push(path);
|
|
124
|
+
index++;
|
|
759
125
|
},
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
}
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
*/
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
function createPath(_ref) {
|
|
779
|
-
var _ref$pathname = _ref.pathname,
|
|
780
|
-
pathname = _ref$pathname === void 0 ? '/' : _ref$pathname,
|
|
781
|
-
_ref$search = _ref.search,
|
|
782
|
-
search = _ref$search === void 0 ? '' : _ref$search,
|
|
783
|
-
_ref$hash = _ref.hash,
|
|
784
|
-
hash = _ref$hash === void 0 ? '' : _ref$hash;
|
|
785
|
-
if (search && search !== '?') pathname += search.charAt(0) === '?' ? search : '?' + search;
|
|
786
|
-
if (hash && hash !== '#') pathname += hash.charAt(0) === '#' ? hash : '#' + hash;
|
|
787
|
-
return pathname;
|
|
788
|
-
}
|
|
789
|
-
/**
|
|
790
|
-
* Parses a string URL path into its separate pathname, search, and hash components.
|
|
791
|
-
*
|
|
792
|
-
* @see https://github.com/remix-run/history/tree/main/docs/api-reference.md#parsepath
|
|
793
|
-
*/
|
|
794
|
-
|
|
795
|
-
function parsePath(path) {
|
|
796
|
-
var parsedPath = {};
|
|
797
|
-
|
|
798
|
-
if (path) {
|
|
799
|
-
var hashIndex = path.indexOf('#');
|
|
800
|
-
|
|
801
|
-
if (hashIndex >= 0) {
|
|
802
|
-
parsedPath.hash = path.substr(hashIndex);
|
|
803
|
-
path = path.substr(0, hashIndex);
|
|
804
|
-
}
|
|
805
|
-
|
|
806
|
-
var searchIndex = path.indexOf('?');
|
|
807
|
-
|
|
808
|
-
if (searchIndex >= 0) {
|
|
809
|
-
parsedPath.search = path.substr(searchIndex);
|
|
810
|
-
path = path.substr(0, searchIndex);
|
|
811
|
-
}
|
|
812
|
-
|
|
813
|
-
if (path) {
|
|
814
|
-
parsedPath.pathname = path;
|
|
815
|
-
}
|
|
816
|
-
}
|
|
817
|
-
|
|
818
|
-
return parsedPath;
|
|
819
|
-
}
|
|
820
|
-
|
|
821
|
-
var isProduction = process.env.NODE_ENV === 'production';
|
|
822
|
-
var prefix = 'Invariant failed';
|
|
823
|
-
function invariant(condition, message) {
|
|
824
|
-
if (condition) {
|
|
825
|
-
return;
|
|
826
|
-
}
|
|
827
|
-
if (isProduction) {
|
|
828
|
-
throw new Error(prefix);
|
|
829
|
-
}
|
|
830
|
-
var provided = typeof message === 'function' ? message() : message;
|
|
831
|
-
var value = provided ? "".concat(prefix, ": ").concat(provided) : prefix;
|
|
832
|
-
throw new Error(value);
|
|
126
|
+
replaceState: (path, state) => {
|
|
127
|
+
currentState = {
|
|
128
|
+
...state,
|
|
129
|
+
key: createRandomKey()
|
|
130
|
+
};
|
|
131
|
+
entries[index] = path;
|
|
132
|
+
},
|
|
133
|
+
back: () => {
|
|
134
|
+
index--;
|
|
135
|
+
},
|
|
136
|
+
forward: () => {
|
|
137
|
+
index = Math.min(index + 1, entries.length - 1);
|
|
138
|
+
},
|
|
139
|
+
go: n => window.history.go(n)
|
|
140
|
+
});
|
|
833
141
|
}
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
*/
|
|
846
|
-
function replaceEqualDeep(prev, next) {
|
|
847
|
-
if (prev === next) {
|
|
848
|
-
return prev;
|
|
849
|
-
}
|
|
850
|
-
|
|
851
|
-
const array = Array.isArray(prev) && Array.isArray(next);
|
|
852
|
-
|
|
853
|
-
if (array || isPlainObject(prev) && isPlainObject(next)) {
|
|
854
|
-
const aSize = array ? prev.length : Object.keys(prev).length;
|
|
855
|
-
const bItems = array ? next : Object.keys(next);
|
|
856
|
-
const bSize = bItems.length;
|
|
857
|
-
const copy = array ? [] : {};
|
|
858
|
-
let equalItems = 0;
|
|
859
|
-
|
|
860
|
-
for (let i = 0; i < bSize; i++) {
|
|
861
|
-
const key = array ? i : bItems[i];
|
|
862
|
-
copy[key] = replaceEqualDeep(prev[key], next[key]);
|
|
863
|
-
|
|
864
|
-
if (copy[key] === prev[key]) {
|
|
865
|
-
equalItems++;
|
|
866
|
-
}
|
|
867
|
-
}
|
|
868
|
-
|
|
869
|
-
return aSize === bSize && equalItems === aSize ? prev : copy;
|
|
870
|
-
}
|
|
871
|
-
|
|
872
|
-
return next;
|
|
873
|
-
} // Copied from: https://github.com/jonschlinkert/is-plain-object
|
|
874
|
-
|
|
875
|
-
function isPlainObject(o) {
|
|
876
|
-
if (!hasObjectPrototype(o)) {
|
|
877
|
-
return false;
|
|
878
|
-
} // If has modified constructor
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
const ctor = o.constructor;
|
|
882
|
-
|
|
883
|
-
if (typeof ctor === 'undefined') {
|
|
884
|
-
return true;
|
|
885
|
-
} // If has modified prototype
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
const prot = ctor.prototype;
|
|
889
|
-
|
|
890
|
-
if (!hasObjectPrototype(prot)) {
|
|
891
|
-
return false;
|
|
892
|
-
} // If constructor does not have an Object-specific method
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
if (!prot.hasOwnProperty('isPrototypeOf')) {
|
|
896
|
-
return false;
|
|
897
|
-
} // Most likely a plain Object
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
return true;
|
|
142
|
+
function parseLocation(href, state) {
|
|
143
|
+
let hashIndex = href.indexOf('#');
|
|
144
|
+
let searchIndex = href.indexOf('?');
|
|
145
|
+
const pathEnd = Math.min(hashIndex, searchIndex);
|
|
146
|
+
return {
|
|
147
|
+
href,
|
|
148
|
+
pathname: pathEnd > -1 ? href.substring(0, pathEnd) : href,
|
|
149
|
+
hash: hashIndex > -1 ? href.substring(hashIndex, searchIndex) : '',
|
|
150
|
+
search: searchIndex > -1 ? href.substring(searchIndex) : '',
|
|
151
|
+
state
|
|
152
|
+
};
|
|
901
153
|
}
|
|
902
154
|
|
|
903
|
-
|
|
904
|
-
|
|
155
|
+
// Thanks co-pilot!
|
|
156
|
+
function createRandomKey() {
|
|
157
|
+
return (Math.random() + 1).toString(36).substring(7);
|
|
905
158
|
}
|
|
906
159
|
|
|
907
160
|
function last(arr) {
|
|
@@ -910,26 +163,27 @@ function last(arr) {
|
|
|
910
163
|
function warning(cond, message) {
|
|
911
164
|
if (cond) {
|
|
912
165
|
if (typeof console !== 'undefined') console.warn(message);
|
|
913
|
-
|
|
914
166
|
try {
|
|
915
167
|
throw new Error(message);
|
|
916
|
-
} catch
|
|
168
|
+
} catch {}
|
|
917
169
|
}
|
|
918
|
-
|
|
919
170
|
return true;
|
|
920
171
|
}
|
|
921
|
-
|
|
922
172
|
function isFunction(d) {
|
|
923
173
|
return typeof d === 'function';
|
|
924
174
|
}
|
|
925
|
-
|
|
926
175
|
function functionalUpdate(updater, previous) {
|
|
927
176
|
if (isFunction(updater)) {
|
|
928
177
|
return updater(previous);
|
|
929
178
|
}
|
|
930
|
-
|
|
931
179
|
return updater;
|
|
932
180
|
}
|
|
181
|
+
function pick(parent, keys) {
|
|
182
|
+
return keys.reduce((obj, key) => {
|
|
183
|
+
obj[key] = parent[key];
|
|
184
|
+
return obj;
|
|
185
|
+
}, {});
|
|
186
|
+
}
|
|
933
187
|
|
|
934
188
|
function joinPaths(paths) {
|
|
935
189
|
return cleanPath(paths.filter(Boolean).join('/'));
|
|
@@ -948,8 +202,8 @@ function trimPath(path) {
|
|
|
948
202
|
return trimPathRight(trimPathLeft(path));
|
|
949
203
|
}
|
|
950
204
|
function resolvePath(basepath, base, to) {
|
|
951
|
-
base = base.replace(new RegExp(
|
|
952
|
-
to = to.replace(new RegExp(
|
|
205
|
+
base = base.replace(new RegExp(`^${basepath}`), '/');
|
|
206
|
+
to = to.replace(new RegExp(`^${basepath}`), '/');
|
|
953
207
|
let baseSegments = parsePathname(base);
|
|
954
208
|
const toSegments = parsePathname(to);
|
|
955
209
|
toSegments.forEach((toSegment, index) => {
|
|
@@ -962,13 +216,10 @@ function resolvePath(basepath, base, to) {
|
|
|
962
216
|
baseSegments.push(toSegment);
|
|
963
217
|
} else ;
|
|
964
218
|
} else if (toSegment.value === '..') {
|
|
965
|
-
var _last;
|
|
966
|
-
|
|
967
219
|
// Extra trailing slash? pop it off
|
|
968
|
-
if (baseSegments.length > 1 &&
|
|
220
|
+
if (baseSegments.length > 1 && last(baseSegments)?.value === '/') {
|
|
969
221
|
baseSegments.pop();
|
|
970
222
|
}
|
|
971
|
-
|
|
972
223
|
baseSegments.pop();
|
|
973
224
|
} else if (toSegment.value === '.') {
|
|
974
225
|
return;
|
|
@@ -983,10 +234,8 @@ function parsePathname(pathname) {
|
|
|
983
234
|
if (!pathname) {
|
|
984
235
|
return [];
|
|
985
236
|
}
|
|
986
|
-
|
|
987
237
|
pathname = cleanPath(pathname);
|
|
988
238
|
const segments = [];
|
|
989
|
-
|
|
990
239
|
if (pathname.slice(0, 1) === '/') {
|
|
991
240
|
pathname = pathname.substring(1);
|
|
992
241
|
segments.push({
|
|
@@ -994,12 +243,11 @@ function parsePathname(pathname) {
|
|
|
994
243
|
value: '/'
|
|
995
244
|
});
|
|
996
245
|
}
|
|
997
|
-
|
|
998
246
|
if (!pathname) {
|
|
999
247
|
return segments;
|
|
1000
|
-
}
|
|
1001
|
-
|
|
248
|
+
}
|
|
1002
249
|
|
|
250
|
+
// Remove empty segments and '.' segments
|
|
1003
251
|
const split = pathname.split('/').filter(Boolean);
|
|
1004
252
|
segments.push(...split.map(part => {
|
|
1005
253
|
if (part.startsWith('*')) {
|
|
@@ -1008,20 +256,17 @@ function parsePathname(pathname) {
|
|
|
1008
256
|
value: part
|
|
1009
257
|
};
|
|
1010
258
|
}
|
|
1011
|
-
|
|
1012
|
-
if (part.charAt(0) === ':') {
|
|
259
|
+
if (part.charAt(0) === '$') {
|
|
1013
260
|
return {
|
|
1014
261
|
type: 'param',
|
|
1015
262
|
value: part
|
|
1016
263
|
};
|
|
1017
264
|
}
|
|
1018
|
-
|
|
1019
265
|
return {
|
|
1020
266
|
type: 'pathname',
|
|
1021
267
|
value: part
|
|
1022
268
|
};
|
|
1023
269
|
}));
|
|
1024
|
-
|
|
1025
270
|
if (pathname.slice(-1) === '/') {
|
|
1026
271
|
pathname = pathname.substring(1);
|
|
1027
272
|
segments.push({
|
|
@@ -1029,7 +274,6 @@ function parsePathname(pathname) {
|
|
|
1029
274
|
value: '/'
|
|
1030
275
|
});
|
|
1031
276
|
}
|
|
1032
|
-
|
|
1033
277
|
return segments;
|
|
1034
278
|
}
|
|
1035
279
|
function interpolatePath(path, params, leaveWildcard) {
|
|
@@ -1038,57 +282,48 @@ function interpolatePath(path, params, leaveWildcard) {
|
|
|
1038
282
|
if (segment.value === '*' && !leaveWildcard) {
|
|
1039
283
|
return '';
|
|
1040
284
|
}
|
|
1041
|
-
|
|
1042
285
|
if (segment.type === 'param') {
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
return (_segment$value$substr = params[segment.value.substring(1)]) != null ? _segment$value$substr : '';
|
|
286
|
+
return params[segment.value.substring(1)] ?? '';
|
|
1046
287
|
}
|
|
1047
|
-
|
|
1048
288
|
return segment.value;
|
|
1049
289
|
}));
|
|
1050
290
|
}
|
|
1051
|
-
function matchPathname(currentPathname, matchLocation) {
|
|
1052
|
-
const pathParams = matchByPath(currentPathname, matchLocation);
|
|
291
|
+
function matchPathname(basepath, currentPathname, matchLocation) {
|
|
292
|
+
const pathParams = matchByPath(basepath, currentPathname, matchLocation);
|
|
293
|
+
// const searchMatched = matchBySearch(currentLocation.search, matchLocation)
|
|
1053
294
|
|
|
1054
295
|
if (matchLocation.to && !pathParams) {
|
|
1055
296
|
return;
|
|
1056
|
-
}
|
|
1057
|
-
|
|
1058
|
-
// }
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
return pathParams != null ? pathParams : {};
|
|
297
|
+
}
|
|
298
|
+
return pathParams ?? {};
|
|
1062
299
|
}
|
|
1063
|
-
function matchByPath(from, matchLocation) {
|
|
1064
|
-
|
|
1065
|
-
|
|
300
|
+
function matchByPath(basepath, from, matchLocation) {
|
|
301
|
+
if (!from.startsWith(basepath)) {
|
|
302
|
+
return undefined;
|
|
303
|
+
}
|
|
304
|
+
from = basepath != '/' ? from.substring(basepath.length) : from;
|
|
1066
305
|
const baseSegments = parsePathname(from);
|
|
1067
|
-
const
|
|
306
|
+
const to = `${matchLocation.to ?? '*'}`;
|
|
307
|
+
const routeSegments = parsePathname(to);
|
|
1068
308
|
const params = {};
|
|
1069
|
-
|
|
1070
309
|
let isMatch = (() => {
|
|
1071
310
|
for (let i = 0; i < Math.max(baseSegments.length, routeSegments.length); i++) {
|
|
1072
311
|
const baseSegment = baseSegments[i];
|
|
1073
312
|
const routeSegment = routeSegments[i];
|
|
1074
313
|
const isLastRouteSegment = i === routeSegments.length - 1;
|
|
1075
314
|
const isLastBaseSegment = i === baseSegments.length - 1;
|
|
1076
|
-
|
|
1077
315
|
if (routeSegment) {
|
|
1078
316
|
if (routeSegment.type === 'wildcard') {
|
|
1079
|
-
if (baseSegment
|
|
317
|
+
if (baseSegment?.value) {
|
|
1080
318
|
params['*'] = joinPaths(baseSegments.slice(i).map(d => d.value));
|
|
1081
319
|
return true;
|
|
1082
320
|
}
|
|
1083
|
-
|
|
1084
321
|
return false;
|
|
1085
322
|
}
|
|
1086
|
-
|
|
1087
323
|
if (routeSegment.type === 'pathname') {
|
|
1088
|
-
if (routeSegment.value === '/' && !
|
|
324
|
+
if (routeSegment.value === '/' && !baseSegment?.value) {
|
|
1089
325
|
return true;
|
|
1090
326
|
}
|
|
1091
|
-
|
|
1092
327
|
if (baseSegment) {
|
|
1093
328
|
if (matchLocation.caseSensitive) {
|
|
1094
329
|
if (routeSegment.value !== baseSegment.value) {
|
|
@@ -1099,41 +334,36 @@ function matchByPath(from, matchLocation) {
|
|
|
1099
334
|
}
|
|
1100
335
|
}
|
|
1101
336
|
}
|
|
1102
|
-
|
|
1103
337
|
if (!baseSegment) {
|
|
1104
338
|
return false;
|
|
1105
339
|
}
|
|
1106
|
-
|
|
1107
340
|
if (routeSegment.type === 'param') {
|
|
1108
|
-
if (
|
|
341
|
+
if (baseSegment?.value === '/') {
|
|
1109
342
|
return false;
|
|
1110
343
|
}
|
|
1111
|
-
|
|
1112
|
-
if (!baseSegment.value.startsWith(':')) {
|
|
344
|
+
if (baseSegment.value.charAt(0) !== '$') {
|
|
1113
345
|
params[routeSegment.value.substring(1)] = baseSegment.value;
|
|
1114
346
|
}
|
|
1115
347
|
}
|
|
1116
348
|
}
|
|
1117
|
-
|
|
1118
349
|
if (isLastRouteSegment && !isLastBaseSegment) {
|
|
1119
350
|
return !!matchLocation.fuzzy;
|
|
1120
351
|
}
|
|
1121
352
|
}
|
|
1122
|
-
|
|
1123
353
|
return true;
|
|
1124
354
|
})();
|
|
1125
|
-
|
|
1126
355
|
return isMatch ? params : undefined;
|
|
1127
356
|
}
|
|
1128
357
|
|
|
1129
358
|
// @ts-nocheck
|
|
359
|
+
|
|
1130
360
|
// qss has been slightly modified and inlined here for our use cases (and compression's sake). We've included it as a hard dependency for MIT license attribution.
|
|
361
|
+
|
|
1131
362
|
function encode(obj, pfx) {
|
|
1132
363
|
var k,
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
364
|
+
i,
|
|
365
|
+
tmp,
|
|
366
|
+
str = '';
|
|
1137
367
|
for (k in obj) {
|
|
1138
368
|
if ((tmp = obj[k]) !== void 0) {
|
|
1139
369
|
if (Array.isArray(tmp)) {
|
|
@@ -1147,10 +377,8 @@ function encode(obj, pfx) {
|
|
|
1147
377
|
}
|
|
1148
378
|
}
|
|
1149
379
|
}
|
|
1150
|
-
|
|
1151
380
|
return (pfx || '') + str;
|
|
1152
381
|
}
|
|
1153
|
-
|
|
1154
382
|
function toValue(mix) {
|
|
1155
383
|
if (!mix) return '';
|
|
1156
384
|
var str = decodeURIComponent(mix);
|
|
@@ -1159,221 +387,64 @@ function toValue(mix) {
|
|
|
1159
387
|
if (str.charAt(0) === '0') return str;
|
|
1160
388
|
return +str * 0 === 0 ? +str : str;
|
|
1161
389
|
}
|
|
1162
|
-
|
|
1163
390
|
function decode(str) {
|
|
1164
391
|
var tmp,
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
392
|
+
k,
|
|
393
|
+
out = {},
|
|
394
|
+
arr = str.split('&');
|
|
1169
395
|
while (tmp = arr.shift()) {
|
|
1170
396
|
tmp = tmp.split('=');
|
|
1171
397
|
k = tmp.shift();
|
|
1172
|
-
|
|
1173
398
|
if (out[k] !== void 0) {
|
|
1174
399
|
out[k] = [].concat(out[k], toValue(tmp.shift()));
|
|
1175
400
|
} else {
|
|
1176
401
|
out[k] = toValue(tmp.shift());
|
|
1177
402
|
}
|
|
1178
403
|
}
|
|
1179
|
-
|
|
1180
404
|
return out;
|
|
1181
405
|
}
|
|
1182
406
|
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
}
|
|
1199
|
-
|
|
1200
|
-
function createRoute(routeConfig, options, parent, router) {
|
|
1201
|
-
const {
|
|
1202
|
-
id,
|
|
1203
|
-
routeId,
|
|
1204
|
-
path: routePath,
|
|
1205
|
-
fullPath
|
|
1206
|
-
} = routeConfig;
|
|
1207
|
-
|
|
1208
|
-
const action = router.state.actions[id] || (() => {
|
|
1209
|
-
router.state.actions[id] = {
|
|
1210
|
-
pending: [],
|
|
1211
|
-
submit: async (submission, actionOpts) => {
|
|
1212
|
-
var _actionOpts$invalidat;
|
|
1213
|
-
|
|
1214
|
-
if (!route) {
|
|
1215
|
-
return;
|
|
1216
|
-
}
|
|
1217
|
-
|
|
1218
|
-
const invalidate = (_actionOpts$invalidat = actionOpts == null ? void 0 : actionOpts.invalidate) != null ? _actionOpts$invalidat : true;
|
|
1219
|
-
const actionState = {
|
|
1220
|
-
submittedAt: Date.now(),
|
|
1221
|
-
status: 'pending',
|
|
1222
|
-
submission
|
|
1223
|
-
};
|
|
1224
|
-
action.current = actionState;
|
|
1225
|
-
action.latest = actionState;
|
|
1226
|
-
action.pending.push(actionState);
|
|
1227
|
-
router.state = _extends({}, router.state, {
|
|
1228
|
-
currentAction: actionState,
|
|
1229
|
-
latestAction: actionState
|
|
1230
|
-
});
|
|
1231
|
-
router.notify();
|
|
1232
|
-
|
|
1233
|
-
try {
|
|
1234
|
-
const res = await (route.options.action == null ? void 0 : route.options.action(submission));
|
|
1235
|
-
actionState.data = res;
|
|
1236
|
-
|
|
1237
|
-
if (invalidate) {
|
|
1238
|
-
router.invalidateRoute({
|
|
1239
|
-
to: '.',
|
|
1240
|
-
fromCurrent: true
|
|
1241
|
-
});
|
|
1242
|
-
await router.reload();
|
|
1243
|
-
}
|
|
1244
|
-
|
|
1245
|
-
actionState.status = 'success';
|
|
1246
|
-
return res;
|
|
1247
|
-
} catch (err) {
|
|
1248
|
-
console.error(err);
|
|
1249
|
-
actionState.error = err;
|
|
1250
|
-
actionState.status = 'error';
|
|
1251
|
-
} finally {
|
|
1252
|
-
action.pending = action.pending.filter(d => d !== actionState);
|
|
1253
|
-
router.removeActionQueue.push({
|
|
1254
|
-
action,
|
|
1255
|
-
actionState
|
|
1256
|
-
});
|
|
1257
|
-
router.notify();
|
|
1258
|
-
}
|
|
1259
|
-
}
|
|
1260
|
-
};
|
|
1261
|
-
return router.state.actions[id];
|
|
1262
|
-
})();
|
|
1263
|
-
|
|
1264
|
-
const loader = router.state.loaders[id] || (() => {
|
|
1265
|
-
router.state.loaders[id] = {
|
|
1266
|
-
pending: [],
|
|
1267
|
-
fetch: async loaderContext => {
|
|
1268
|
-
if (!route) {
|
|
1269
|
-
return;
|
|
1270
|
-
}
|
|
1271
|
-
|
|
1272
|
-
const loaderState = {
|
|
1273
|
-
loadedAt: Date.now(),
|
|
1274
|
-
loaderContext
|
|
1275
|
-
};
|
|
1276
|
-
loader.current = loaderState;
|
|
1277
|
-
loader.latest = loaderState;
|
|
1278
|
-
loader.pending.push(loaderState); // router.state = {
|
|
1279
|
-
// ...router.state,
|
|
1280
|
-
// currentAction: loaderState,
|
|
1281
|
-
// latestAction: loaderState,
|
|
1282
|
-
// }
|
|
1283
|
-
|
|
1284
|
-
router.notify();
|
|
1285
|
-
|
|
1286
|
-
try {
|
|
1287
|
-
return await (route.options.loader == null ? void 0 : route.options.loader(loaderContext));
|
|
1288
|
-
} finally {
|
|
1289
|
-
loader.pending = loader.pending.filter(d => d !== loaderState); // router.removeActionQueue.push({ loader, loaderState })
|
|
1290
|
-
|
|
1291
|
-
router.notify();
|
|
1292
|
-
}
|
|
1293
|
-
}
|
|
1294
|
-
};
|
|
1295
|
-
return router.state.loaders[id];
|
|
1296
|
-
})();
|
|
1297
|
-
|
|
1298
|
-
let route = {
|
|
1299
|
-
routeId: id,
|
|
1300
|
-
routeRouteId: routeId,
|
|
1301
|
-
routePath,
|
|
1302
|
-
fullPath,
|
|
1303
|
-
options,
|
|
1304
|
-
router,
|
|
1305
|
-
childRoutes: undefined,
|
|
1306
|
-
parentRoute: parent,
|
|
1307
|
-
action,
|
|
1308
|
-
loader: loader,
|
|
1309
|
-
buildLink: options => {
|
|
1310
|
-
return router.buildLink(_extends({}, options, {
|
|
1311
|
-
from: fullPath
|
|
1312
|
-
}));
|
|
1313
|
-
},
|
|
1314
|
-
navigate: options => {
|
|
1315
|
-
return router.navigate(_extends({}, options, {
|
|
1316
|
-
from: fullPath
|
|
1317
|
-
}));
|
|
1318
|
-
},
|
|
1319
|
-
matchRoute: (matchLocation, opts) => {
|
|
1320
|
-
return router.matchRoute(_extends({}, matchLocation, {
|
|
1321
|
-
from: fullPath
|
|
1322
|
-
}), opts);
|
|
1323
|
-
}
|
|
1324
|
-
};
|
|
1325
|
-
router.options.createRoute == null ? void 0 : router.options.createRoute({
|
|
1326
|
-
router,
|
|
1327
|
-
route
|
|
1328
|
-
});
|
|
1329
|
-
return route;
|
|
1330
|
-
}
|
|
1331
|
-
function cascadeLoaderData(matches) {
|
|
1332
|
-
matches.forEach((match, index) => {
|
|
1333
|
-
const parent = matches[index - 1];
|
|
1334
|
-
|
|
1335
|
-
if (parent) {
|
|
1336
|
-
match.loaderData = replaceEqualDeep(match.loaderData, _extends({}, parent.loaderData, match.routeLoaderData));
|
|
1337
|
-
}
|
|
1338
|
-
});
|
|
407
|
+
class Route {
|
|
408
|
+
constructor(routeConfig, options, originalIndex, parent, router) {
|
|
409
|
+
Object.assign(this, {
|
|
410
|
+
...routeConfig,
|
|
411
|
+
originalIndex,
|
|
412
|
+
options,
|
|
413
|
+
getRouter: () => router,
|
|
414
|
+
childRoutes: undefined,
|
|
415
|
+
getParentRoute: () => parent
|
|
416
|
+
});
|
|
417
|
+
router.options.createRoute?.({
|
|
418
|
+
router,
|
|
419
|
+
route: this
|
|
420
|
+
});
|
|
421
|
+
}
|
|
1339
422
|
}
|
|
1340
423
|
|
|
1341
424
|
const rootRouteId = '__root__';
|
|
1342
|
-
const createRouteConfig =
|
|
1343
|
-
if (options === void 0) {
|
|
1344
|
-
options = {};
|
|
1345
|
-
}
|
|
1346
|
-
|
|
1347
|
-
if (isRoot === void 0) {
|
|
1348
|
-
isRoot = true;
|
|
1349
|
-
}
|
|
1350
|
-
|
|
425
|
+
const createRouteConfig = (options = {}, children = [], isRoot = true, parentId, parentPath) => {
|
|
1351
426
|
if (isRoot) {
|
|
1352
427
|
options.path = rootRouteId;
|
|
1353
|
-
}
|
|
1354
|
-
|
|
428
|
+
}
|
|
1355
429
|
|
|
430
|
+
// Strip the root from parentIds
|
|
1356
431
|
if (parentId === rootRouteId) {
|
|
1357
432
|
parentId = '';
|
|
1358
433
|
}
|
|
434
|
+
let path = isRoot ? rootRouteId : options.path;
|
|
1359
435
|
|
|
1360
|
-
|
|
1361
|
-
|
|
436
|
+
// If the path is anything other than an index path, trim it up
|
|
1362
437
|
if (path && path !== '/') {
|
|
1363
438
|
path = trimPath(path);
|
|
1364
439
|
}
|
|
1365
|
-
|
|
1366
440
|
const routeId = path || options.id;
|
|
1367
441
|
let id = joinPaths([parentId, routeId]);
|
|
1368
|
-
|
|
1369
442
|
if (path === rootRouteId) {
|
|
1370
443
|
path = '/';
|
|
1371
444
|
}
|
|
1372
|
-
|
|
1373
445
|
if (id !== rootRouteId) {
|
|
1374
446
|
id = joinPaths(['/', id]);
|
|
1375
447
|
}
|
|
1376
|
-
|
|
1377
448
|
const fullPath = id === rootRouteId ? '/' : trimPathRight(joinPaths([parentPath, path]));
|
|
1378
449
|
return {
|
|
1379
450
|
id: id,
|
|
@@ -1382,257 +453,423 @@ const createRouteConfig = function createRouteConfig(options, children, isRoot,
|
|
|
1382
453
|
fullPath: fullPath,
|
|
1383
454
|
options: options,
|
|
1384
455
|
children,
|
|
1385
|
-
createChildren: cb => createRouteConfig(options, cb(childOptions => createRouteConfig(childOptions, undefined, false, id, fullPath)), false, parentId, parentPath),
|
|
1386
456
|
addChildren: children => createRouteConfig(options, children, false, parentId, parentPath),
|
|
1387
|
-
createRoute: childOptions => createRouteConfig(childOptions, undefined, false, id, fullPath)
|
|
457
|
+
createRoute: childOptions => createRouteConfig(childOptions, undefined, false, id, fullPath),
|
|
458
|
+
generate: () => {
|
|
459
|
+
invariant(false, `routeConfig.generate() is used by TanStack Router's file-based routing code generation and should not actually be called during runtime. `);
|
|
460
|
+
}
|
|
1388
461
|
};
|
|
1389
462
|
};
|
|
1390
463
|
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
464
|
+
setAutoFreeze(false);
|
|
465
|
+
let queue = [];
|
|
466
|
+
let batching = false;
|
|
467
|
+
function flush() {
|
|
468
|
+
if (batching) return;
|
|
469
|
+
queue.forEach(cb => cb());
|
|
470
|
+
queue = [];
|
|
471
|
+
}
|
|
472
|
+
function createStore(initialState, debug) {
|
|
473
|
+
const listeners = new Set();
|
|
474
|
+
const store = {
|
|
475
|
+
state: initialState,
|
|
476
|
+
subscribe: listener => {
|
|
477
|
+
listeners.add(listener);
|
|
478
|
+
return () => listeners.delete(listener);
|
|
479
|
+
},
|
|
480
|
+
setState: updater => {
|
|
481
|
+
const previous = store.state;
|
|
482
|
+
store.state = produce(d => {
|
|
483
|
+
updater(d);
|
|
484
|
+
})(previous);
|
|
485
|
+
if (debug) console.log(store.state);
|
|
486
|
+
queue.push(() => listeners.forEach(listener => listener(store.state, previous)));
|
|
487
|
+
flush();
|
|
488
|
+
}
|
|
489
|
+
};
|
|
490
|
+
return store;
|
|
491
|
+
}
|
|
492
|
+
function batch(cb) {
|
|
493
|
+
batching = true;
|
|
494
|
+
cb();
|
|
495
|
+
batching = false;
|
|
496
|
+
flush();
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
// /**
|
|
500
|
+
// * This function converts a store to an immutable value, which is
|
|
501
|
+
// * more complex than you think. On first read, (when prev is undefined)
|
|
502
|
+
// * every value must be recursively touched so tracking is "deep".
|
|
503
|
+
// * Every object/array structure must also be cloned to
|
|
504
|
+
// * have a new reference, otherwise it will get mutated by subsequent
|
|
505
|
+
// * store updates.
|
|
506
|
+
// *
|
|
507
|
+
// * In the case that prev is supplied, we have to do deep comparisons
|
|
508
|
+
// * between prev and next objects/array references and if they are deeply
|
|
509
|
+
// * equal, we can return the prev version for referential equality.
|
|
510
|
+
// */
|
|
511
|
+
// export function storeToImmutable<T>(prev: any, next: T): T {
|
|
512
|
+
// const cache = new Map()
|
|
513
|
+
|
|
514
|
+
// // Visit all nodes
|
|
515
|
+
// // clone all next structures
|
|
516
|
+
// // from bottom up, if prev === next, return prev
|
|
517
|
+
|
|
518
|
+
// function recurse(prev: any, next: any) {
|
|
519
|
+
// if (cache.has(next)) {
|
|
520
|
+
// return cache.get(next)
|
|
521
|
+
// }
|
|
522
|
+
|
|
523
|
+
// const prevIsArray = Array.isArray(prev)
|
|
524
|
+
// const nextIsArray = Array.isArray(next)
|
|
525
|
+
// const prevIsObj = isPlainObject(prev)
|
|
526
|
+
// const nextIsObj = isPlainObject(next)
|
|
527
|
+
// const nextIsComplex = nextIsArray || nextIsObj
|
|
528
|
+
|
|
529
|
+
// const isArray = prevIsArray && nextIsArray
|
|
530
|
+
// const isObj = prevIsObj && nextIsObj
|
|
531
|
+
|
|
532
|
+
// const isSameStructure = isArray || isObj
|
|
533
|
+
|
|
534
|
+
// if (nextIsComplex) {
|
|
535
|
+
// const prevSize = isArray
|
|
536
|
+
// ? prev.length
|
|
537
|
+
// : isObj
|
|
538
|
+
// ? Object.keys(prev).length
|
|
539
|
+
// : -1
|
|
540
|
+
// const nextKeys = isArray ? next : Object.keys(next)
|
|
541
|
+
// const nextSize = nextKeys.length
|
|
542
|
+
|
|
543
|
+
// let changed = false
|
|
544
|
+
// const copy: any = nextIsArray ? [] : {}
|
|
545
|
+
|
|
546
|
+
// for (let i = 0; i < nextSize; i++) {
|
|
547
|
+
// const key = isArray ? i : nextKeys[i]
|
|
548
|
+
// const prevValue = isSameStructure ? prev[key] : undefined
|
|
549
|
+
// const nextValue = next[key]
|
|
550
|
+
|
|
551
|
+
// // Recurse the new value
|
|
552
|
+
// try {
|
|
553
|
+
// console.count(key)
|
|
554
|
+
// copy[key] = recurse(prevValue, nextValue)
|
|
555
|
+
// } catch {}
|
|
556
|
+
|
|
557
|
+
// // If the new value has changed reference,
|
|
558
|
+
// // mark the obj/array as changed
|
|
559
|
+
// if (!changed && copy[key] !== prevValue) {
|
|
560
|
+
// changed = true
|
|
561
|
+
// }
|
|
562
|
+
// }
|
|
563
|
+
|
|
564
|
+
// // No items have changed!
|
|
565
|
+
// // If something has changed, return a clone of the next obj/array
|
|
566
|
+
// if (changed || prevSize !== nextSize) {
|
|
567
|
+
// cache.set(next, copy)
|
|
568
|
+
// return copy
|
|
569
|
+
// }
|
|
570
|
+
|
|
571
|
+
// // If they are exactly the same, return the prev obj/array
|
|
572
|
+
// cache.set(next, prev)
|
|
573
|
+
// return prev
|
|
574
|
+
// }
|
|
575
|
+
|
|
576
|
+
// cache.set(next, next)
|
|
577
|
+
// return next
|
|
578
|
+
// }
|
|
579
|
+
|
|
580
|
+
// return recurse(prev, next)
|
|
581
|
+
// }
|
|
582
|
+
|
|
583
|
+
/**
|
|
584
|
+
* This function returns `a` if `b` is deeply equal.
|
|
585
|
+
* If not, it will replace any deeply equal children of `b` with those of `a`.
|
|
586
|
+
* This can be used for structural sharing between immutable JSON values for example.
|
|
587
|
+
* Do not use this with signals
|
|
588
|
+
*/
|
|
589
|
+
function replaceEqualDeep(prev, _next) {
|
|
590
|
+
if (prev === _next) {
|
|
591
|
+
return prev;
|
|
592
|
+
}
|
|
593
|
+
const next = _next;
|
|
594
|
+
const array = Array.isArray(prev) && Array.isArray(next);
|
|
595
|
+
if (array || isPlainObject(prev) && isPlainObject(next)) {
|
|
596
|
+
const prevSize = array ? prev.length : Object.keys(prev).length;
|
|
597
|
+
const nextItems = array ? next : Object.keys(next);
|
|
598
|
+
const nextSize = nextItems.length;
|
|
599
|
+
const copy = array ? [] : {};
|
|
600
|
+
let equalItems = 0;
|
|
601
|
+
for (let i = 0; i < nextSize; i++) {
|
|
602
|
+
const key = array ? i : nextItems[i];
|
|
603
|
+
copy[key] = replaceEqualDeep(prev[key], next[key]);
|
|
604
|
+
if (copy[key] === prev[key]) {
|
|
605
|
+
equalItems++;
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
return prevSize === nextSize && equalItems === prevSize ? prev : copy;
|
|
609
|
+
}
|
|
610
|
+
return next;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
// Copied from: https://github.com/jonschlinkert/is-plain-object
|
|
614
|
+
function isPlainObject(o) {
|
|
615
|
+
if (!hasObjectPrototype(o)) {
|
|
616
|
+
return false;
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
// If has modified constructor
|
|
620
|
+
const ctor = o.constructor;
|
|
621
|
+
if (typeof ctor === 'undefined') {
|
|
622
|
+
return true;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
// If has modified prototype
|
|
626
|
+
const prot = ctor.prototype;
|
|
627
|
+
if (!hasObjectPrototype(prot)) {
|
|
628
|
+
return false;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
// If constructor does not have an Object-specific method
|
|
632
|
+
if (!prot.hasOwnProperty('isPrototypeOf')) {
|
|
633
|
+
return false;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
// Most likely a plain Object
|
|
637
|
+
return true;
|
|
638
|
+
}
|
|
639
|
+
function hasObjectPrototype(o) {
|
|
640
|
+
return Object.prototype.toString.call(o) === '[object Object]';
|
|
641
|
+
}
|
|
642
|
+
function trackDeep(obj) {
|
|
643
|
+
const seen = new Set();
|
|
644
|
+
JSON.stringify(obj, (_, value) => {
|
|
645
|
+
if (typeof value === 'function') {
|
|
646
|
+
return undefined;
|
|
647
|
+
}
|
|
648
|
+
if (typeof value === 'object' && value !== null) {
|
|
649
|
+
if (seen.has(value)) return;
|
|
650
|
+
seen.add(value);
|
|
651
|
+
}
|
|
652
|
+
return value;
|
|
653
|
+
});
|
|
654
|
+
return obj;
|
|
655
|
+
}
|
|
1420
656
|
|
|
1421
|
-
|
|
1422
|
-
|
|
657
|
+
const componentTypes = ['component', 'errorComponent', 'pendingComponent'];
|
|
658
|
+
class RouteMatch {
|
|
659
|
+
abortController = new AbortController();
|
|
660
|
+
#latestId = '';
|
|
661
|
+
#resolve = () => {};
|
|
662
|
+
onLoaderDataListeners = new Set();
|
|
663
|
+
constructor(router, route, opts) {
|
|
664
|
+
Object.assign(this, {
|
|
665
|
+
route,
|
|
666
|
+
router,
|
|
667
|
+
id: opts.id,
|
|
668
|
+
pathname: opts.pathname,
|
|
669
|
+
params: opts.params,
|
|
670
|
+
store: createStore({
|
|
671
|
+
routeSearch: {},
|
|
672
|
+
search: {},
|
|
673
|
+
status: 'idle',
|
|
674
|
+
routeLoaderData: {},
|
|
675
|
+
loaderData: {},
|
|
676
|
+
isFetching: false,
|
|
677
|
+
invalid: false,
|
|
678
|
+
invalidAt: Infinity
|
|
679
|
+
})
|
|
680
|
+
});
|
|
681
|
+
if (!this.__hasLoaders()) {
|
|
682
|
+
this.store.setState(s => s.status = 'success');
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
#setLoaderData = loaderData => {
|
|
686
|
+
batch(() => {
|
|
687
|
+
this.store.setState(s => {
|
|
688
|
+
s.routeLoaderData = loaderData;
|
|
689
|
+
});
|
|
690
|
+
this.#updateLoaderData();
|
|
691
|
+
});
|
|
692
|
+
};
|
|
693
|
+
cancel = () => {
|
|
694
|
+
this.abortController?.abort();
|
|
695
|
+
};
|
|
696
|
+
load = async loaderOpts => {
|
|
697
|
+
const now = Date.now();
|
|
698
|
+
const minMaxAge = loaderOpts?.preload ? Math.max(loaderOpts?.maxAge, loaderOpts?.gcMaxAge) : 0;
|
|
699
|
+
|
|
700
|
+
// If this is a preload, add it to the preload cache
|
|
701
|
+
if (loaderOpts?.preload && minMaxAge > 0) {
|
|
702
|
+
// If the match is currently active, don't preload it
|
|
703
|
+
if (this.router.store.state.currentMatches.find(d => d.id === this.id)) {
|
|
704
|
+
return;
|
|
705
|
+
}
|
|
706
|
+
this.router.store.setState(s => {
|
|
707
|
+
s.matchCache[this.id] = {
|
|
708
|
+
gc: now + loaderOpts.gcMaxAge,
|
|
709
|
+
match: this
|
|
710
|
+
};
|
|
711
|
+
});
|
|
712
|
+
}
|
|
1423
713
|
|
|
1424
|
-
|
|
1425
|
-
|
|
714
|
+
// If the match is invalid, errored or idle, trigger it to load
|
|
715
|
+
if (this.store.state.status === 'success' && this.getIsInvalid() || this.store.state.status === 'error' || this.store.state.status === 'idle') {
|
|
716
|
+
const maxAge = loaderOpts?.preload ? loaderOpts?.maxAge : undefined;
|
|
717
|
+
await this.fetch({
|
|
718
|
+
maxAge
|
|
719
|
+
});
|
|
720
|
+
}
|
|
721
|
+
};
|
|
722
|
+
fetch = async opts => {
|
|
723
|
+
this.__loadPromise = new Promise(async resolve => {
|
|
724
|
+
const loadId = '' + Date.now() + Math.random();
|
|
725
|
+
this.#latestId = loadId;
|
|
726
|
+
const checkLatest = () => loadId !== this.#latestId ? this.__loadPromise?.then(() => resolve()) : undefined;
|
|
727
|
+
let latestPromise;
|
|
728
|
+
batch(() => {
|
|
729
|
+
// If the match was in an error state, set it
|
|
730
|
+
// to a loading state again. Otherwise, keep it
|
|
731
|
+
// as loading or resolved
|
|
732
|
+
if (this.store.state.status === 'idle') {
|
|
733
|
+
this.store.setState(s => s.status = 'loading');
|
|
1426
734
|
}
|
|
1427
735
|
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
routeMatch.__.resolve();
|
|
736
|
+
// We started loading the route, so it's no longer invalid
|
|
737
|
+
this.store.setState(s => s.invalid = false);
|
|
738
|
+
});
|
|
1432
739
|
|
|
1433
|
-
|
|
1434
|
-
|
|
740
|
+
// We are now fetching, even if it's in the background of a
|
|
741
|
+
// resolved state
|
|
742
|
+
this.store.setState(s => s.isFetching = true);
|
|
743
|
+
this.#resolve = resolve;
|
|
744
|
+
const componentsPromise = (async () => {
|
|
745
|
+
// then run all component and data loaders in parallel
|
|
746
|
+
// For each component type, potentially load it asynchronously
|
|
747
|
+
|
|
748
|
+
await Promise.all(componentTypes.map(async type => {
|
|
749
|
+
const component = this.route.options[type];
|
|
750
|
+
if (this[type]?.preload) {
|
|
751
|
+
this[type] = await this.router.options.loadComponent(component);
|
|
1435
752
|
}
|
|
1436
|
-
}
|
|
1437
|
-
}
|
|
1438
|
-
|
|
1439
|
-
routeMatch.isPending = false;
|
|
1440
|
-
clearTimeout(routeMatch.__.pendingTimeout);
|
|
1441
|
-
clearTimeout(routeMatch.__.pendingMinTimeout);
|
|
1442
|
-
delete routeMatch.__.pendingMinPromise;
|
|
1443
|
-
},
|
|
1444
|
-
// setParentMatch: (parentMatch?: RouteMatch) => {
|
|
1445
|
-
// routeMatch.parentMatch = parentMatch
|
|
1446
|
-
// },
|
|
1447
|
-
// addChildMatch: (childMatch: RouteMatch) => {
|
|
1448
|
-
// if (
|
|
1449
|
-
// routeMatch.childMatches.find((d) => d.matchId === childMatch.matchId)
|
|
1450
|
-
// ) {
|
|
1451
|
-
// return
|
|
1452
|
-
// }
|
|
1453
|
-
// routeMatch.childMatches.push(childMatch)
|
|
1454
|
-
// },
|
|
1455
|
-
validate: () => {
|
|
1456
|
-
var _routeMatch$parentMat, _routeMatch$parentMat2;
|
|
1457
|
-
|
|
1458
|
-
// Validate the search params and stabilize them
|
|
1459
|
-
const parentSearch = (_routeMatch$parentMat = (_routeMatch$parentMat2 = routeMatch.parentMatch) == null ? void 0 : _routeMatch$parentMat2.search) != null ? _routeMatch$parentMat : router.location.search;
|
|
1460
|
-
|
|
753
|
+
}));
|
|
754
|
+
})();
|
|
755
|
+
const dataPromise = Promise.resolve().then(async () => {
|
|
1461
756
|
try {
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
if (prevSearch !== nextSearch) {
|
|
1467
|
-
routeMatch.isInvalid = true;
|
|
757
|
+
if (this.route.options.loader) {
|
|
758
|
+
const data = await this.router.loadMatchData(this);
|
|
759
|
+
if (latestPromise = checkLatest()) return latestPromise;
|
|
760
|
+
this.#setLoaderData(data);
|
|
1468
761
|
}
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
762
|
+
this.store.setState(s => {
|
|
763
|
+
s.error = undefined;
|
|
764
|
+
s.status = 'success';
|
|
765
|
+
s.updatedAt = Date.now();
|
|
766
|
+
s.invalidAt = s.updatedAt + (opts?.maxAge ?? this.route.options.loaderMaxAge ?? this.router.options.defaultLoaderMaxAge ?? 0);
|
|
767
|
+
});
|
|
768
|
+
return this.store.state.routeLoaderData;
|
|
1472
769
|
} catch (err) {
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
770
|
+
if (latestPromise = checkLatest()) return latestPromise;
|
|
771
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
772
|
+
console.error(err);
|
|
773
|
+
}
|
|
774
|
+
this.store.setState(s => {
|
|
775
|
+
s.error = err;
|
|
776
|
+
s.status = 'error';
|
|
777
|
+
s.updatedAt = Date.now();
|
|
1476
778
|
});
|
|
1477
|
-
|
|
1478
|
-
routeMatch.status = 'error';
|
|
1479
|
-
routeMatch.error = error; // Do not proceed with loading the route
|
|
1480
|
-
|
|
1481
|
-
return;
|
|
779
|
+
throw err;
|
|
1482
780
|
}
|
|
781
|
+
});
|
|
782
|
+
const after = async () => {
|
|
783
|
+
if (latestPromise = checkLatest()) return latestPromise;
|
|
784
|
+
this.store.setState(s => s.isFetching = false);
|
|
785
|
+
this.#resolve();
|
|
786
|
+
delete this.__loadPromise;
|
|
787
|
+
};
|
|
788
|
+
try {
|
|
789
|
+
await Promise.all([componentsPromise, dataPromise.catch(() => {})]);
|
|
790
|
+
after();
|
|
791
|
+
} catch {
|
|
792
|
+
after();
|
|
1483
793
|
}
|
|
1484
|
-
}
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
}
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
794
|
+
});
|
|
795
|
+
return this.__loadPromise;
|
|
796
|
+
};
|
|
797
|
+
invalidate = async () => {
|
|
798
|
+
this.store.setState(s => s.invalid = true);
|
|
799
|
+
if (this.router.store.state.currentMatches.find(d => d.id === this.id)) {
|
|
800
|
+
await this.load();
|
|
801
|
+
}
|
|
802
|
+
};
|
|
803
|
+
__hasLoaders = () => {
|
|
804
|
+
return !!(this.route.options.loader || componentTypes.some(d => this.route.options[d]?.preload));
|
|
805
|
+
};
|
|
806
|
+
getIsInvalid = () => {
|
|
807
|
+
const now = Date.now();
|
|
808
|
+
return this.store.state.invalid || this.store.state.invalidAt < now;
|
|
809
|
+
};
|
|
810
|
+
#updateLoaderData = () => {
|
|
811
|
+
this.store.setState(s => {
|
|
812
|
+
s.loaderData = replaceEqualDeep(s.loaderData, {
|
|
813
|
+
...this.parentMatch?.store.state.loaderData,
|
|
814
|
+
...s.routeLoaderData
|
|
815
|
+
});
|
|
816
|
+
});
|
|
817
|
+
this.onLoaderDataListeners.forEach(listener => listener());
|
|
818
|
+
};
|
|
819
|
+
__setParentMatch = parentMatch => {
|
|
820
|
+
if (!this.parentMatch && parentMatch) {
|
|
821
|
+
this.parentMatch = parentMatch;
|
|
822
|
+
this.parentMatch.__onLoaderData(() => {
|
|
823
|
+
this.#updateLoaderData();
|
|
824
|
+
});
|
|
825
|
+
}
|
|
826
|
+
};
|
|
827
|
+
__onLoaderData = listener => {
|
|
828
|
+
this.onLoaderDataListeners.add(listener);
|
|
829
|
+
// return () => this.onLoaderDataListeners.delete(listener)
|
|
830
|
+
};
|
|
1501
831
|
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
832
|
+
__validate = () => {
|
|
833
|
+
// Validate the search params and stabilize them
|
|
834
|
+
const parentSearch = this.parentMatch?.store.state.search ?? this.router.store.state.latestLocation.search;
|
|
835
|
+
try {
|
|
836
|
+
const prevSearch = this.store.state.routeSearch;
|
|
837
|
+
const validator = typeof this.route.options.validateSearch === 'object' ? this.route.options.validateSearch.parse : this.route.options.validateSearch;
|
|
838
|
+
let nextSearch = validator?.(parentSearch) ?? {};
|
|
839
|
+
batch(() => {
|
|
840
|
+
// Invalidate route matches when search param stability changes
|
|
841
|
+
if (prevSearch !== nextSearch) {
|
|
842
|
+
this.store.setState(s => s.invalid = true);
|
|
1506
843
|
}
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
if (routeMatch.status === 'success' && routeMatch.getIsInvalid() || routeMatch.status === 'error' || routeMatch.status === 'idle') {
|
|
1516
|
-
const maxAge = loaderOpts != null && loaderOpts.preload ? loaderOpts == null ? void 0 : loaderOpts.maxAge : undefined;
|
|
1517
|
-
routeMatch.fetch({
|
|
1518
|
-
maxAge
|
|
844
|
+
this.store.setState(s => {
|
|
845
|
+
s.routeSearch = nextSearch;
|
|
846
|
+
s.search = {
|
|
847
|
+
...parentSearch,
|
|
848
|
+
...nextSearch
|
|
849
|
+
};
|
|
1519
850
|
});
|
|
1520
|
-
}
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
// to a loading state again. Otherwise, keep it
|
|
1526
|
-
// as loading or resolved
|
|
1527
|
-
|
|
1528
|
-
if (routeMatch.status === 'idle') {
|
|
1529
|
-
routeMatch.status = 'loading';
|
|
1530
|
-
} // We started loading the route, so it's no longer invalid
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
routeMatch.isInvalid = false;
|
|
1534
|
-
routeMatch.__.loadPromise = new Promise(async resolve => {
|
|
1535
|
-
// We are now fetching, even if it's in the background of a
|
|
1536
|
-
// resolved state
|
|
1537
|
-
routeMatch.isFetching = true;
|
|
1538
|
-
routeMatch.__.resolve = resolve;
|
|
1539
|
-
|
|
1540
|
-
const loaderPromise = (async () => {
|
|
1541
|
-
// Load the elements and data in parallel
|
|
1542
|
-
routeMatch.__.elementsPromise = (async () => {
|
|
1543
|
-
// then run all element and data loaders in parallel
|
|
1544
|
-
// For each element type, potentially load it asynchronously
|
|
1545
|
-
await Promise.all(elementTypes.map(async type => {
|
|
1546
|
-
const routeElement = routeMatch.options[type];
|
|
1547
|
-
|
|
1548
|
-
if (routeMatch.__[type]) {
|
|
1549
|
-
return;
|
|
1550
|
-
}
|
|
1551
|
-
|
|
1552
|
-
routeMatch.__[type] = await router.options.createElement(routeElement);
|
|
1553
|
-
}));
|
|
1554
|
-
})();
|
|
1555
|
-
|
|
1556
|
-
routeMatch.__.dataPromise = Promise.resolve().then(async () => {
|
|
1557
|
-
try {
|
|
1558
|
-
var _ref, _ref2, _opts$maxAge;
|
|
1559
|
-
|
|
1560
|
-
if (routeMatch.options.loader) {
|
|
1561
|
-
const data = await routeMatch.options.loader({
|
|
1562
|
-
params: routeMatch.params,
|
|
1563
|
-
search: routeMatch.routeSearch,
|
|
1564
|
-
signal: routeMatch.__.abortController.signal
|
|
1565
|
-
});
|
|
1566
|
-
|
|
1567
|
-
if (id !== routeMatch.__.latestId) {
|
|
1568
|
-
return routeMatch.__.loaderPromise;
|
|
1569
|
-
}
|
|
1570
|
-
|
|
1571
|
-
routeMatch.routeLoaderData = replaceEqualDeep(routeMatch.routeLoaderData, data);
|
|
1572
|
-
}
|
|
1573
|
-
|
|
1574
|
-
routeMatch.error = undefined;
|
|
1575
|
-
routeMatch.status = 'success';
|
|
1576
|
-
routeMatch.updatedAt = Date.now();
|
|
1577
|
-
routeMatch.invalidAt = routeMatch.updatedAt + ((_ref = (_ref2 = (_opts$maxAge = opts == null ? void 0 : opts.maxAge) != null ? _opts$maxAge : routeMatch.options.loaderMaxAge) != null ? _ref2 : router.options.defaultLoaderMaxAge) != null ? _ref : 0);
|
|
1578
|
-
} catch (err) {
|
|
1579
|
-
if (id !== routeMatch.__.latestId) {
|
|
1580
|
-
return routeMatch.__.loaderPromise;
|
|
1581
|
-
}
|
|
1582
|
-
|
|
1583
|
-
if (process.env.NODE_ENV !== 'production') {
|
|
1584
|
-
console.error(err);
|
|
1585
|
-
}
|
|
1586
|
-
|
|
1587
|
-
routeMatch.error = err;
|
|
1588
|
-
routeMatch.status = 'error';
|
|
1589
|
-
routeMatch.updatedAt = Date.now();
|
|
1590
|
-
}
|
|
1591
|
-
});
|
|
1592
|
-
|
|
1593
|
-
try {
|
|
1594
|
-
await Promise.all([routeMatch.__.elementsPromise, routeMatch.__.dataPromise]);
|
|
1595
|
-
|
|
1596
|
-
if (id !== routeMatch.__.latestId) {
|
|
1597
|
-
return routeMatch.__.loaderPromise;
|
|
1598
|
-
}
|
|
1599
|
-
|
|
1600
|
-
if (routeMatch.__.pendingMinPromise) {
|
|
1601
|
-
await routeMatch.__.pendingMinPromise;
|
|
1602
|
-
delete routeMatch.__.pendingMinPromise;
|
|
1603
|
-
}
|
|
1604
|
-
} finally {
|
|
1605
|
-
if (id !== routeMatch.__.latestId) {
|
|
1606
|
-
return routeMatch.__.loaderPromise;
|
|
1607
|
-
}
|
|
1608
|
-
|
|
1609
|
-
routeMatch.__.cancelPending();
|
|
1610
|
-
|
|
1611
|
-
routeMatch.isPending = false;
|
|
1612
|
-
routeMatch.isFetching = false;
|
|
1613
|
-
|
|
1614
|
-
routeMatch.__.notify();
|
|
1615
|
-
}
|
|
1616
|
-
})();
|
|
1617
|
-
|
|
1618
|
-
routeMatch.__.loaderPromise = loaderPromise;
|
|
1619
|
-
await loaderPromise;
|
|
1620
|
-
|
|
1621
|
-
if (id !== routeMatch.__.latestId) {
|
|
1622
|
-
return routeMatch.__.loaderPromise;
|
|
851
|
+
});
|
|
852
|
+
componentTypes.map(async type => {
|
|
853
|
+
const component = this.route.options[type];
|
|
854
|
+
if (typeof this[type] !== 'function') {
|
|
855
|
+
this[type] = component;
|
|
1623
856
|
}
|
|
1624
|
-
|
|
1625
|
-
delete routeMatch.__.loaderPromise;
|
|
1626
857
|
});
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
858
|
+
} catch (err) {
|
|
859
|
+
console.error(err);
|
|
860
|
+
const error = new Error('Invalid search params found', {
|
|
861
|
+
cause: err
|
|
862
|
+
});
|
|
863
|
+
error.code = 'INVALID_SEARCH_PARAMS';
|
|
864
|
+
this.store.setState(s => {
|
|
865
|
+
s.status = 'error';
|
|
866
|
+
s.error = error;
|
|
867
|
+
});
|
|
1634
868
|
|
|
1635
|
-
|
|
869
|
+
// Do not proceed with loading the route
|
|
870
|
+
return;
|
|
871
|
+
}
|
|
872
|
+
};
|
|
1636
873
|
}
|
|
1637
874
|
|
|
1638
875
|
const defaultParseSearch = parseSearchWith(JSON.parse);
|
|
@@ -1642,818 +879,909 @@ function parseSearchWith(parser) {
|
|
|
1642
879
|
if (searchStr.substring(0, 1) === '?') {
|
|
1643
880
|
searchStr = searchStr.substring(1);
|
|
1644
881
|
}
|
|
882
|
+
let query = decode(searchStr);
|
|
1645
883
|
|
|
1646
|
-
|
|
1647
|
-
|
|
884
|
+
// Try to parse any query params that might be json
|
|
1648
885
|
for (let key in query) {
|
|
1649
886
|
const value = query[key];
|
|
1650
|
-
|
|
1651
887
|
if (typeof value === 'string') {
|
|
1652
888
|
try {
|
|
1653
889
|
query[key] = parser(value);
|
|
1654
|
-
} catch (err) {
|
|
890
|
+
} catch (err) {
|
|
891
|
+
//
|
|
1655
892
|
}
|
|
1656
893
|
}
|
|
1657
894
|
}
|
|
1658
|
-
|
|
1659
895
|
return query;
|
|
1660
896
|
};
|
|
1661
897
|
}
|
|
1662
898
|
function stringifySearchWith(stringify) {
|
|
1663
899
|
return search => {
|
|
1664
|
-
search =
|
|
1665
|
-
|
|
900
|
+
search = {
|
|
901
|
+
...search
|
|
902
|
+
};
|
|
1666
903
|
if (search) {
|
|
1667
904
|
Object.keys(search).forEach(key => {
|
|
1668
905
|
const val = search[key];
|
|
1669
|
-
|
|
1670
906
|
if (typeof val === 'undefined' || val === undefined) {
|
|
1671
907
|
delete search[key];
|
|
1672
908
|
} else if (val && typeof val === 'object' && val !== null) {
|
|
1673
909
|
try {
|
|
1674
910
|
search[key] = stringify(val);
|
|
1675
|
-
} catch (err) {
|
|
911
|
+
} catch (err) {
|
|
912
|
+
// silent
|
|
1676
913
|
}
|
|
1677
914
|
}
|
|
1678
915
|
});
|
|
1679
916
|
}
|
|
1680
|
-
|
|
1681
917
|
const searchStr = encode(search).toString();
|
|
1682
|
-
return searchStr ?
|
|
918
|
+
return searchStr ? `?${searchStr}` : '';
|
|
1683
919
|
};
|
|
1684
920
|
}
|
|
1685
921
|
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
const
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
defaultLoaderGcMaxAge: 5 * 60 * 1000,
|
|
1699
|
-
defaultLoaderMaxAge: 0,
|
|
1700
|
-
defaultPreloadMaxAge: 2000,
|
|
1701
|
-
defaultPreloadDelay: 50
|
|
1702
|
-
}, userOptions, {
|
|
1703
|
-
stringifySearch: (_userOptions$stringif = userOptions == null ? void 0 : userOptions.stringifySearch) != null ? _userOptions$stringif : defaultStringifySearch,
|
|
1704
|
-
parseSearch: (_userOptions$parseSea = userOptions == null ? void 0 : userOptions.parseSearch) != null ? _userOptions$parseSea : defaultParseSearch
|
|
922
|
+
const defaultFetchServerDataFn = async ({
|
|
923
|
+
router,
|
|
924
|
+
routeMatch
|
|
925
|
+
}) => {
|
|
926
|
+
const next = router.buildNext({
|
|
927
|
+
to: '.',
|
|
928
|
+
search: d => ({
|
|
929
|
+
...(d ?? {}),
|
|
930
|
+
__data: {
|
|
931
|
+
matchId: routeMatch.id
|
|
932
|
+
}
|
|
933
|
+
})
|
|
1705
934
|
});
|
|
935
|
+
const res = await fetch(next.href, {
|
|
936
|
+
method: 'GET',
|
|
937
|
+
signal: routeMatch.abortController.signal
|
|
938
|
+
});
|
|
939
|
+
if (res.ok) {
|
|
940
|
+
return res.json();
|
|
941
|
+
}
|
|
942
|
+
throw new Error('Failed to fetch match data');
|
|
943
|
+
};
|
|
944
|
+
class Router {
|
|
945
|
+
// __location: Location<TAllRouteInfo['fullSearchSchema']>
|
|
946
|
+
|
|
947
|
+
startedLoadingAt = Date.now();
|
|
948
|
+
resolveNavigation = () => {};
|
|
949
|
+
constructor(options) {
|
|
950
|
+
this.options = {
|
|
951
|
+
defaultLoaderGcMaxAge: 5 * 60 * 1000,
|
|
952
|
+
defaultLoaderMaxAge: 0,
|
|
953
|
+
defaultPreloadMaxAge: 2000,
|
|
954
|
+
defaultPreloadDelay: 50,
|
|
955
|
+
context: undefined,
|
|
956
|
+
...options,
|
|
957
|
+
stringifySearch: options?.stringifySearch ?? defaultStringifySearch,
|
|
958
|
+
parseSearch: options?.parseSearch ?? defaultParseSearch,
|
|
959
|
+
fetchServerDataFn: options?.fetchServerDataFn ?? defaultFetchServerDataFn
|
|
960
|
+
};
|
|
961
|
+
this.store = createStore(getInitialRouterState());
|
|
962
|
+
this.basepath = '';
|
|
963
|
+
this.update(options);
|
|
1706
964
|
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
navigationPromise: Promise.resolve(),
|
|
1720
|
-
resolveNavigation: () => {},
|
|
1721
|
-
matchCache: {},
|
|
1722
|
-
state: {
|
|
1723
|
-
status: 'idle',
|
|
1724
|
-
location: null,
|
|
1725
|
-
matches: [],
|
|
1726
|
-
actions: {},
|
|
1727
|
-
loaders: {},
|
|
1728
|
-
lastUpdated: Date.now(),
|
|
1729
|
-
isFetching: false,
|
|
1730
|
-
isPreloading: false
|
|
1731
|
-
},
|
|
1732
|
-
startedLoadingAt: Date.now(),
|
|
1733
|
-
subscribe: listener => {
|
|
1734
|
-
router.listeners.push(listener);
|
|
1735
|
-
return () => {
|
|
1736
|
-
router.listeners = router.listeners.filter(x => x !== listener);
|
|
1737
|
-
};
|
|
1738
|
-
},
|
|
1739
|
-
getRoute: id => {
|
|
1740
|
-
return router.routesById[id];
|
|
1741
|
-
},
|
|
1742
|
-
notify: () => {
|
|
1743
|
-
router.state = _extends({}, router.state, {
|
|
1744
|
-
isFetching: router.state.status === 'loading' || router.state.matches.some(d => d.isFetching),
|
|
1745
|
-
isPreloading: Object.values(router.matchCache).some(d => d.match.isFetching && !router.state.matches.find(dd => dd.matchId === d.match.matchId))
|
|
1746
|
-
});
|
|
1747
|
-
cascadeLoaderData(router.state.matches);
|
|
1748
|
-
router.listeners.forEach(listener => listener(router));
|
|
1749
|
-
},
|
|
1750
|
-
mount: () => {
|
|
1751
|
-
const next = router.__.buildLocation({
|
|
1752
|
-
to: '.',
|
|
1753
|
-
search: true,
|
|
1754
|
-
hash: true
|
|
1755
|
-
}); // If the current location isn't updated, trigger a navigation
|
|
1756
|
-
// to the current location. Otherwise, load the current location.
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
if (next.href !== router.location.href) {
|
|
1760
|
-
router.__.commitLocation(next, true);
|
|
965
|
+
// Allow frameworks to hook into the router creation
|
|
966
|
+
this.options.Router?.(this);
|
|
967
|
+
}
|
|
968
|
+
reset = () => {
|
|
969
|
+
this.store.setState(s => Object.assign(s, getInitialRouterState()));
|
|
970
|
+
};
|
|
971
|
+
mount = () => {
|
|
972
|
+
// Mount only does anything on the client
|
|
973
|
+
if (!isServer) {
|
|
974
|
+
// If the router matches are empty, load the matches
|
|
975
|
+
if (!this.store.state.currentMatches.length) {
|
|
976
|
+
this.load();
|
|
1761
977
|
}
|
|
978
|
+
const unsubHistory = this.history.listen(() => {
|
|
979
|
+
this.load(this.#parseLocation(this.store.state.latestLocation));
|
|
980
|
+
});
|
|
981
|
+
const visibilityChangeEvent = 'visibilitychange';
|
|
982
|
+
const focusEvent = 'focus';
|
|
1762
983
|
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
console.log(event.location);
|
|
1766
|
-
router.loadLocation(router.__.parseLocation(event.location, router.location));
|
|
1767
|
-
}); // addEventListener does not exist in React Native, but window does
|
|
984
|
+
// addEventListener does not exist in React Native, but window does
|
|
985
|
+
// In the future, we might need to invert control here for more adapters
|
|
1768
986
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
window.addEventListener(
|
|
1773
|
-
window.addEventListener('focus', router.onFocus, false);
|
|
987
|
+
if (window.addEventListener) {
|
|
988
|
+
// Listen to visibilitychange and focus
|
|
989
|
+
window.addEventListener(visibilityChangeEvent, this.#onFocus, false);
|
|
990
|
+
window.addEventListener(focusEvent, this.#onFocus, false);
|
|
1774
991
|
}
|
|
1775
|
-
|
|
1776
992
|
return () => {
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
window.removeEventListener('focus', router.onFocus);
|
|
1781
|
-
};
|
|
1782
|
-
},
|
|
1783
|
-
onFocus: () => {
|
|
1784
|
-
router.loadLocation();
|
|
1785
|
-
},
|
|
1786
|
-
update: opts => {
|
|
1787
|
-
const newHistory = (opts == null ? void 0 : opts.history) !== router.history;
|
|
993
|
+
unsubHistory();
|
|
994
|
+
if (window.removeEventListener) {
|
|
995
|
+
// Be sure to unsubscribe if a new handler is set
|
|
1788
996
|
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
router.history = opts.history;
|
|
997
|
+
window.removeEventListener(visibilityChangeEvent, this.#onFocus);
|
|
998
|
+
window.removeEventListener(focusEvent, this.#onFocus);
|
|
1792
999
|
}
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
if (routeConfig) {
|
|
1806
|
-
router.routesById = {};
|
|
1807
|
-
router.routeTree = router.__.buildRouteTree(routeConfig);
|
|
1808
|
-
}
|
|
1809
|
-
|
|
1810
|
-
return router;
|
|
1811
|
-
},
|
|
1812
|
-
cancelMatches: () => {
|
|
1813
|
-
var _router$state$pending, _router$state$pending2;
|
|
1814
|
-
[...router.state.matches, ...((_router$state$pending = (_router$state$pending2 = router.state.pending) == null ? void 0 : _router$state$pending2.matches) != null ? _router$state$pending : [])].forEach(match => {
|
|
1815
|
-
match.cancel();
|
|
1000
|
+
};
|
|
1001
|
+
}
|
|
1002
|
+
return () => {};
|
|
1003
|
+
};
|
|
1004
|
+
update = opts => {
|
|
1005
|
+
Object.assign(this.options, opts);
|
|
1006
|
+
if (!this.history || this.options.history && this.options.history !== this.history) {
|
|
1007
|
+
this.history = this.options?.history ?? isServer ? createMemoryHistory() : createBrowserHistory();
|
|
1008
|
+
this.store.setState(s => {
|
|
1009
|
+
s.latestLocation = this.#parseLocation();
|
|
1010
|
+
s.currentLocation = s.latestLocation;
|
|
1816
1011
|
});
|
|
1817
|
-
}
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1012
|
+
}
|
|
1013
|
+
const {
|
|
1014
|
+
basepath,
|
|
1015
|
+
routeConfig
|
|
1016
|
+
} = this.options;
|
|
1017
|
+
this.basepath = `/${trimPath(basepath ?? '') ?? ''}`;
|
|
1018
|
+
if (routeConfig) {
|
|
1019
|
+
this.routesById = {};
|
|
1020
|
+
this.routeTree = this.#buildRouteTree(routeConfig);
|
|
1021
|
+
}
|
|
1022
|
+
return this;
|
|
1023
|
+
};
|
|
1024
|
+
buildNext = opts => {
|
|
1025
|
+
const next = this.#buildLocation(opts);
|
|
1026
|
+
const matches = this.matchRoutes(next.pathname);
|
|
1027
|
+
const __preSearchFilters = matches.map(match => match.route.options.preSearchFilters ?? []).flat().filter(Boolean);
|
|
1028
|
+
const __postSearchFilters = matches.map(match => match.route.options.postSearchFilters ?? []).flat().filter(Boolean);
|
|
1029
|
+
return this.#buildLocation({
|
|
1030
|
+
...opts,
|
|
1031
|
+
__preSearchFilters,
|
|
1032
|
+
__postSearchFilters
|
|
1033
|
+
});
|
|
1034
|
+
};
|
|
1035
|
+
cancelMatches = () => {
|
|
1036
|
+
[...this.store.state.currentMatches, ...(this.store.state.pendingMatches || [])].forEach(match => {
|
|
1037
|
+
match.cancel();
|
|
1038
|
+
});
|
|
1039
|
+
};
|
|
1040
|
+
load = async next => {
|
|
1041
|
+
let now = Date.now();
|
|
1042
|
+
const startedAt = now;
|
|
1043
|
+
this.startedLoadingAt = startedAt;
|
|
1044
|
+
|
|
1045
|
+
// Cancel any pending matches
|
|
1046
|
+
this.cancelMatches();
|
|
1047
|
+
let matches;
|
|
1048
|
+
batch(() => {
|
|
1822
1049
|
if (next) {
|
|
1823
1050
|
// Ingest the new location
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
router.removeActionQueue.forEach(_ref => {
|
|
1829
|
-
let {
|
|
1830
|
-
action,
|
|
1831
|
-
actionState
|
|
1832
|
-
} = _ref;
|
|
1833
|
-
|
|
1834
|
-
if (router.state.currentAction === actionState) {
|
|
1835
|
-
router.state.currentAction = undefined;
|
|
1836
|
-
}
|
|
1837
|
-
|
|
1838
|
-
if (action.current === actionState) {
|
|
1839
|
-
action.current = undefined;
|
|
1840
|
-
}
|
|
1841
|
-
});
|
|
1842
|
-
router.removeActionQueue = []; // Cancel any pending matches
|
|
1843
|
-
|
|
1844
|
-
router.cancelMatches(); // Match the routes
|
|
1845
|
-
|
|
1846
|
-
const matches = router.matchRoutes(router.location.pathname, {
|
|
1847
|
-
strictParseParams: true
|
|
1848
|
-
});
|
|
1849
|
-
router.state = _extends({}, router.state, {
|
|
1850
|
-
pending: {
|
|
1851
|
-
matches: matches,
|
|
1852
|
-
location: router.location
|
|
1853
|
-
},
|
|
1854
|
-
status: 'loading'
|
|
1855
|
-
});
|
|
1856
|
-
router.notify(); // Load the matches
|
|
1857
|
-
|
|
1858
|
-
await router.loadMatches(matches, {
|
|
1859
|
-
withPending: true
|
|
1860
|
-
});
|
|
1861
|
-
|
|
1862
|
-
if (router.startedLoadingAt !== id) {
|
|
1863
|
-
// Ignore side-effects of match loading
|
|
1864
|
-
return router.navigationPromise;
|
|
1865
|
-
}
|
|
1866
|
-
|
|
1867
|
-
const previousMatches = router.state.matches;
|
|
1868
|
-
const exiting = [],
|
|
1869
|
-
staying = [];
|
|
1870
|
-
previousMatches.forEach(d => {
|
|
1871
|
-
if (matches.find(dd => dd.matchId === d.matchId)) {
|
|
1872
|
-
staying.push(d);
|
|
1873
|
-
} else {
|
|
1874
|
-
exiting.push(d);
|
|
1875
|
-
}
|
|
1876
|
-
});
|
|
1877
|
-
const now = Date.now();
|
|
1878
|
-
exiting.forEach(d => {
|
|
1879
|
-
var _ref2, _d$options$loaderGcMa, _ref3, _d$options$loaderMaxA;
|
|
1880
|
-
|
|
1881
|
-
d.__.onExit == null ? void 0 : d.__.onExit({
|
|
1882
|
-
params: d.params,
|
|
1883
|
-
search: d.routeSearch
|
|
1884
|
-
}); // Clear idle error states when match leaves
|
|
1885
|
-
|
|
1886
|
-
if (d.status === 'error' && !d.isFetching) {
|
|
1887
|
-
d.status = 'idle';
|
|
1888
|
-
d.error = undefined;
|
|
1889
|
-
}
|
|
1890
|
-
|
|
1891
|
-
const gc = Math.max((_ref2 = (_d$options$loaderGcMa = d.options.loaderGcMaxAge) != null ? _d$options$loaderGcMa : router.options.defaultLoaderGcMaxAge) != null ? _ref2 : 0, (_ref3 = (_d$options$loaderMaxA = d.options.loaderMaxAge) != null ? _d$options$loaderMaxA : router.options.defaultLoaderMaxAge) != null ? _ref3 : 0);
|
|
1892
|
-
|
|
1893
|
-
if (gc > 0) {
|
|
1894
|
-
router.matchCache[d.matchId] = {
|
|
1895
|
-
gc: gc == Infinity ? Number.MAX_SAFE_INTEGER : now + gc,
|
|
1896
|
-
match: d
|
|
1897
|
-
};
|
|
1898
|
-
}
|
|
1899
|
-
});
|
|
1900
|
-
staying.forEach(d => {
|
|
1901
|
-
d.options.onTransition == null ? void 0 : d.options.onTransition({
|
|
1902
|
-
params: d.params,
|
|
1903
|
-
search: d.routeSearch
|
|
1904
|
-
});
|
|
1905
|
-
});
|
|
1906
|
-
const entering = matches.filter(d => {
|
|
1907
|
-
return !previousMatches.find(dd => dd.matchId === d.matchId);
|
|
1908
|
-
});
|
|
1909
|
-
entering.forEach(d => {
|
|
1910
|
-
d.__.onExit = d.options.onMatch == null ? void 0 : d.options.onMatch({
|
|
1911
|
-
params: d.params,
|
|
1912
|
-
search: d.search
|
|
1051
|
+
this.store.setState(s => {
|
|
1052
|
+
s.latestLocation = next;
|
|
1913
1053
|
});
|
|
1914
|
-
delete router.matchCache[d.matchId];
|
|
1915
|
-
});
|
|
1916
|
-
|
|
1917
|
-
if (matches.some(d => d.status === 'loading')) {
|
|
1918
|
-
router.notify();
|
|
1919
|
-
await Promise.all(matches.map(d => d.__.loaderPromise || Promise.resolve()));
|
|
1920
|
-
}
|
|
1921
|
-
|
|
1922
|
-
if (router.startedLoadingAt !== id) {
|
|
1923
|
-
// Ignore side-effects of match loading
|
|
1924
|
-
return;
|
|
1925
|
-
}
|
|
1926
|
-
|
|
1927
|
-
router.state = _extends({}, router.state, {
|
|
1928
|
-
location: router.location,
|
|
1929
|
-
matches,
|
|
1930
|
-
pending: undefined,
|
|
1931
|
-
status: 'idle'
|
|
1932
|
-
});
|
|
1933
|
-
router.notify();
|
|
1934
|
-
router.resolveNavigation();
|
|
1935
|
-
},
|
|
1936
|
-
cleanMatchCache: () => {
|
|
1937
|
-
const now = Date.now();
|
|
1938
|
-
Object.keys(router.matchCache).forEach(matchId => {
|
|
1939
|
-
const entry = router.matchCache[matchId]; // Don't remove loading matches
|
|
1940
|
-
|
|
1941
|
-
if (entry.match.status === 'loading') {
|
|
1942
|
-
return;
|
|
1943
|
-
} // Do not remove successful matches that are still valid
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
if (entry.gc > 0 && entry.gc > now) {
|
|
1947
|
-
return;
|
|
1948
|
-
} // Everything else gets removed
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
delete router.matchCache[matchId];
|
|
1952
|
-
});
|
|
1953
|
-
},
|
|
1954
|
-
loadRoute: async function loadRoute(navigateOpts) {
|
|
1955
|
-
if (navigateOpts === void 0) {
|
|
1956
|
-
navigateOpts = router.location;
|
|
1957
|
-
}
|
|
1958
|
-
|
|
1959
|
-
const next = router.buildNext(navigateOpts);
|
|
1960
|
-
const matches = router.matchRoutes(next.pathname, {
|
|
1961
|
-
strictParseParams: true
|
|
1962
|
-
});
|
|
1963
|
-
await router.loadMatches(matches);
|
|
1964
|
-
return matches;
|
|
1965
|
-
},
|
|
1966
|
-
preloadRoute: async function preloadRoute(navigateOpts, loaderOpts) {
|
|
1967
|
-
var _ref4, _ref5, _loaderOpts$maxAge, _ref6, _ref7, _loaderOpts$gcMaxAge;
|
|
1968
|
-
|
|
1969
|
-
if (navigateOpts === void 0) {
|
|
1970
|
-
navigateOpts = router.location;
|
|
1971
1054
|
}
|
|
1972
1055
|
|
|
1973
|
-
|
|
1974
|
-
|
|
1056
|
+
// Match the routes
|
|
1057
|
+
matches = this.matchRoutes(this.store.state.latestLocation.pathname, {
|
|
1975
1058
|
strictParseParams: true
|
|
1976
1059
|
});
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1060
|
+
this.store.setState(s => {
|
|
1061
|
+
s.status = 'loading';
|
|
1062
|
+
s.pendingMatches = matches;
|
|
1063
|
+
s.pendingLocation = this.store.state.latestLocation;
|
|
1981
1064
|
});
|
|
1982
|
-
|
|
1983
|
-
},
|
|
1984
|
-
matchRoutes: (pathname, opts) => {
|
|
1985
|
-
var _router$state$pending3, _router$state$pending4;
|
|
1986
|
-
|
|
1987
|
-
router.cleanMatchCache();
|
|
1988
|
-
const matches = [];
|
|
1989
|
-
|
|
1990
|
-
if (!router.routeTree) {
|
|
1991
|
-
return matches;
|
|
1992
|
-
}
|
|
1993
|
-
|
|
1994
|
-
const existingMatches = [...router.state.matches, ...((_router$state$pending3 = (_router$state$pending4 = router.state.pending) == null ? void 0 : _router$state$pending4.matches) != null ? _router$state$pending3 : [])];
|
|
1995
|
-
|
|
1996
|
-
const recurse = async routes => {
|
|
1997
|
-
var _parentMatch$params, _router$options$filte, _foundRoute$childRout;
|
|
1998
|
-
|
|
1999
|
-
const parentMatch = last(matches);
|
|
2000
|
-
let params = (_parentMatch$params = parentMatch == null ? void 0 : parentMatch.params) != null ? _parentMatch$params : {};
|
|
2001
|
-
const filteredRoutes = (_router$options$filte = router.options.filterRoutes == null ? void 0 : router.options.filterRoutes(routes)) != null ? _router$options$filte : routes;
|
|
2002
|
-
let foundRoutes = [];
|
|
2003
|
-
|
|
2004
|
-
const findMatchInRoutes = (parentRoutes, routes) => {
|
|
2005
|
-
routes.some(route => {
|
|
2006
|
-
var _route$childRoutes, _route$childRoutes2, _route$options$caseSe;
|
|
2007
|
-
|
|
2008
|
-
if (!route.routePath && (_route$childRoutes = route.childRoutes) != null && _route$childRoutes.length) {
|
|
2009
|
-
return findMatchInRoutes([...foundRoutes, route], route.childRoutes);
|
|
2010
|
-
}
|
|
2011
|
-
|
|
2012
|
-
const fuzzy = !!(route.routePath !== '/' || (_route$childRoutes2 = route.childRoutes) != null && _route$childRoutes2.length);
|
|
2013
|
-
const matchParams = matchPathname(pathname, {
|
|
2014
|
-
to: route.fullPath,
|
|
2015
|
-
fuzzy,
|
|
2016
|
-
caseSensitive: (_route$options$caseSe = route.options.caseSensitive) != null ? _route$options$caseSe : router.options.caseSensitive
|
|
2017
|
-
});
|
|
2018
|
-
|
|
2019
|
-
if (matchParams) {
|
|
2020
|
-
let parsedParams;
|
|
2021
|
-
|
|
2022
|
-
try {
|
|
2023
|
-
var _route$options$parseP;
|
|
2024
|
-
|
|
2025
|
-
parsedParams = (_route$options$parseP = route.options.parseParams == null ? void 0 : route.options.parseParams(matchParams)) != null ? _route$options$parseP : matchParams;
|
|
2026
|
-
} catch (err) {
|
|
2027
|
-
if (opts != null && opts.strictParseParams) {
|
|
2028
|
-
throw err;
|
|
2029
|
-
}
|
|
2030
|
-
}
|
|
2031
|
-
|
|
2032
|
-
params = _extends({}, params, parsedParams);
|
|
2033
|
-
}
|
|
2034
|
-
|
|
2035
|
-
if (!!matchParams) {
|
|
2036
|
-
foundRoutes = [...parentRoutes, route];
|
|
2037
|
-
}
|
|
2038
|
-
|
|
2039
|
-
return !!foundRoutes.length;
|
|
2040
|
-
});
|
|
2041
|
-
return !!foundRoutes.length;
|
|
2042
|
-
};
|
|
1065
|
+
});
|
|
2043
1066
|
|
|
2044
|
-
|
|
1067
|
+
// Load the matches
|
|
1068
|
+
try {
|
|
1069
|
+
await this.loadMatches(matches);
|
|
1070
|
+
} catch (err) {
|
|
1071
|
+
console.warn(err);
|
|
1072
|
+
invariant(false, 'Matches failed to load due to error above ☝️. Navigation cancelled!');
|
|
1073
|
+
}
|
|
1074
|
+
if (this.startedLoadingAt !== startedAt) {
|
|
1075
|
+
// Ignore side-effects of outdated side-effects
|
|
1076
|
+
return this.navigationPromise;
|
|
1077
|
+
}
|
|
1078
|
+
const previousMatches = this.store.state.currentMatches;
|
|
1079
|
+
const exiting = [],
|
|
1080
|
+
staying = [];
|
|
1081
|
+
previousMatches.forEach(d => {
|
|
1082
|
+
if (matches.find(dd => dd.id === d.id)) {
|
|
1083
|
+
staying.push(d);
|
|
1084
|
+
} else {
|
|
1085
|
+
exiting.push(d);
|
|
1086
|
+
}
|
|
1087
|
+
});
|
|
1088
|
+
const entering = matches.filter(d => {
|
|
1089
|
+
return !previousMatches.find(dd => dd.id === d.id);
|
|
1090
|
+
});
|
|
1091
|
+
now = Date.now();
|
|
1092
|
+
exiting.forEach(d => {
|
|
1093
|
+
d.__onExit?.({
|
|
1094
|
+
params: d.params,
|
|
1095
|
+
search: d.store.state.routeSearch
|
|
1096
|
+
});
|
|
2045
1097
|
|
|
2046
|
-
|
|
1098
|
+
// Clear non-loading error states when match leaves
|
|
1099
|
+
if (d.store.state.status === 'error' && !d.store.state.isFetching) {
|
|
1100
|
+
d.store.setState(s => {
|
|
1101
|
+
s.status = 'idle';
|
|
1102
|
+
s.error = undefined;
|
|
1103
|
+
});
|
|
1104
|
+
}
|
|
1105
|
+
const gc = Math.max(d.route.options.loaderGcMaxAge ?? this.options.defaultLoaderGcMaxAge ?? 0, d.route.options.loaderMaxAge ?? this.options.defaultLoaderMaxAge ?? 0);
|
|
1106
|
+
if (gc > 0) {
|
|
1107
|
+
this.store.setState(s => {
|
|
1108
|
+
s.matchCache[d.id] = {
|
|
1109
|
+
gc: gc == Infinity ? Number.MAX_SAFE_INTEGER : now + gc,
|
|
1110
|
+
match: d
|
|
1111
|
+
};
|
|
1112
|
+
});
|
|
1113
|
+
}
|
|
1114
|
+
});
|
|
1115
|
+
staying.forEach(d => {
|
|
1116
|
+
d.route.options.onTransition?.({
|
|
1117
|
+
params: d.params,
|
|
1118
|
+
search: d.store.state.routeSearch
|
|
1119
|
+
});
|
|
1120
|
+
});
|
|
1121
|
+
entering.forEach(d => {
|
|
1122
|
+
d.__onExit = d.route.options.onLoaded?.({
|
|
1123
|
+
params: d.params,
|
|
1124
|
+
search: d.store.state.search
|
|
1125
|
+
});
|
|
1126
|
+
delete this.store.state.matchCache[d.id];
|
|
1127
|
+
});
|
|
1128
|
+
this.store.setState(s => {
|
|
1129
|
+
Object.assign(s, {
|
|
1130
|
+
status: 'idle',
|
|
1131
|
+
currentLocation: this.store.state.latestLocation,
|
|
1132
|
+
currentMatches: matches,
|
|
1133
|
+
pendingLocation: undefined,
|
|
1134
|
+
pendingMatches: undefined
|
|
1135
|
+
});
|
|
1136
|
+
});
|
|
1137
|
+
this.options.onRouteChange?.();
|
|
1138
|
+
this.resolveNavigation();
|
|
1139
|
+
};
|
|
1140
|
+
cleanMatchCache = () => {
|
|
1141
|
+
const now = Date.now();
|
|
1142
|
+
this.store.setState(s => {
|
|
1143
|
+
Object.keys(s.matchCache).forEach(matchId => {
|
|
1144
|
+
const entry = s.matchCache[matchId];
|
|
1145
|
+
|
|
1146
|
+
// Don't remove loading matches
|
|
1147
|
+
if (entry.match.store.state.status === 'loading') {
|
|
2047
1148
|
return;
|
|
2048
1149
|
}
|
|
2049
1150
|
|
|
2050
|
-
|
|
2051
|
-
|
|
1151
|
+
// Do not remove successful matches that are still valid
|
|
1152
|
+
if (entry.gc > 0 && entry.gc > now) {
|
|
1153
|
+
return;
|
|
1154
|
+
}
|
|
2052
1155
|
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
1156
|
+
// Everything else gets removed
|
|
1157
|
+
delete s.matchCache[matchId];
|
|
1158
|
+
});
|
|
1159
|
+
});
|
|
1160
|
+
};
|
|
1161
|
+
getRoute = id => {
|
|
1162
|
+
const route = this.routesById[id];
|
|
1163
|
+
invariant(route, `Route with id "${id}" not found`);
|
|
1164
|
+
return route;
|
|
1165
|
+
};
|
|
1166
|
+
loadRoute = async (navigateOpts = this.store.state.latestLocation) => {
|
|
1167
|
+
const next = this.buildNext(navigateOpts);
|
|
1168
|
+
const matches = this.matchRoutes(next.pathname, {
|
|
1169
|
+
strictParseParams: true
|
|
1170
|
+
});
|
|
1171
|
+
await this.loadMatches(matches);
|
|
1172
|
+
return matches;
|
|
1173
|
+
};
|
|
1174
|
+
preloadRoute = async (navigateOpts = this.store.state.latestLocation, loaderOpts) => {
|
|
1175
|
+
const next = this.buildNext(navigateOpts);
|
|
1176
|
+
const matches = this.matchRoutes(next.pathname, {
|
|
1177
|
+
strictParseParams: true
|
|
1178
|
+
});
|
|
1179
|
+
await this.loadMatches(matches, {
|
|
1180
|
+
preload: true,
|
|
1181
|
+
maxAge: loaderOpts.maxAge ?? this.options.defaultPreloadMaxAge ?? this.options.defaultLoaderMaxAge ?? 0,
|
|
1182
|
+
gcMaxAge: loaderOpts.gcMaxAge ?? this.options.defaultPreloadGcMaxAge ?? this.options.defaultLoaderGcMaxAge ?? 0
|
|
1183
|
+
});
|
|
1184
|
+
return matches;
|
|
1185
|
+
};
|
|
1186
|
+
matchRoutes = (pathname, opts) => {
|
|
1187
|
+
const matches = [];
|
|
1188
|
+
if (!this.routeTree) {
|
|
1189
|
+
return matches;
|
|
1190
|
+
}
|
|
1191
|
+
const existingMatches = [...this.store.state.currentMatches, ...(this.store.state.pendingMatches ?? [])];
|
|
1192
|
+
const recurse = async routes => {
|
|
1193
|
+
const parentMatch = last(matches);
|
|
1194
|
+
let params = parentMatch?.params ?? {};
|
|
1195
|
+
const filteredRoutes = this.options.filterRoutes?.(routes) ?? routes;
|
|
1196
|
+
let foundRoutes = [];
|
|
1197
|
+
const findMatchInRoutes = (parentRoutes, routes) => {
|
|
1198
|
+
routes.some(route => {
|
|
1199
|
+
if (!route.path && route.childRoutes?.length) {
|
|
1200
|
+
return findMatchInRoutes([...foundRoutes, route], route.childRoutes);
|
|
1201
|
+
}
|
|
1202
|
+
const fuzzy = !!(route.path !== '/' || route.childRoutes?.length);
|
|
1203
|
+
const matchParams = matchPathname(this.basepath, pathname, {
|
|
1204
|
+
to: route.fullPath,
|
|
1205
|
+
fuzzy,
|
|
1206
|
+
caseSensitive: route.options.caseSensitive ?? this.options.caseSensitive
|
|
2059
1207
|
});
|
|
2060
|
-
|
|
1208
|
+
if (matchParams) {
|
|
1209
|
+
let parsedParams;
|
|
1210
|
+
try {
|
|
1211
|
+
parsedParams = route.options.parseParams?.(matchParams) ?? matchParams;
|
|
1212
|
+
} catch (err) {
|
|
1213
|
+
if (opts?.strictParseParams) {
|
|
1214
|
+
throw err;
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
params = {
|
|
1218
|
+
...params,
|
|
1219
|
+
...parsedParams
|
|
1220
|
+
};
|
|
1221
|
+
}
|
|
1222
|
+
if (!!matchParams) {
|
|
1223
|
+
foundRoutes = [...parentRoutes, route];
|
|
1224
|
+
}
|
|
1225
|
+
return !!foundRoutes.length;
|
|
2061
1226
|
});
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
if ((_foundRoute$childRout = foundRoute.childRoutes) != null && _foundRoute$childRout.length) {
|
|
2065
|
-
recurse(foundRoute.childRoutes);
|
|
2066
|
-
}
|
|
1227
|
+
return !!foundRoutes.length;
|
|
2067
1228
|
};
|
|
1229
|
+
findMatchInRoutes([], filteredRoutes);
|
|
1230
|
+
if (!foundRoutes.length) {
|
|
1231
|
+
return;
|
|
1232
|
+
}
|
|
1233
|
+
foundRoutes.forEach(foundRoute => {
|
|
1234
|
+
const interpolatedPath = interpolatePath(foundRoute.path, params);
|
|
1235
|
+
const matchId = interpolatePath(foundRoute.id, params, true);
|
|
1236
|
+
const match = existingMatches.find(d => d.id === matchId) || this.store.state.matchCache[matchId]?.match || new RouteMatch(this, foundRoute, {
|
|
1237
|
+
id: matchId,
|
|
1238
|
+
params,
|
|
1239
|
+
pathname: joinPaths([this.basepath, interpolatedPath])
|
|
1240
|
+
});
|
|
1241
|
+
matches.push(match);
|
|
1242
|
+
});
|
|
1243
|
+
const foundRoute = last(foundRoutes);
|
|
1244
|
+
if (foundRoute.childRoutes?.length) {
|
|
1245
|
+
recurse(foundRoute.childRoutes);
|
|
1246
|
+
}
|
|
1247
|
+
};
|
|
1248
|
+
recurse([this.routeTree]);
|
|
1249
|
+
linkMatches(matches);
|
|
1250
|
+
return matches;
|
|
1251
|
+
};
|
|
1252
|
+
loadMatches = async (resolvedMatches, loaderOpts) => {
|
|
1253
|
+
this.cleanMatchCache();
|
|
1254
|
+
resolvedMatches.forEach(async match => {
|
|
1255
|
+
// Validate the match (loads search params etc)
|
|
1256
|
+
match.__validate();
|
|
1257
|
+
});
|
|
2068
1258
|
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
1259
|
+
// Check each match middleware to see if the route can be accessed
|
|
1260
|
+
await Promise.all(resolvedMatches.map(async match => {
|
|
1261
|
+
try {
|
|
1262
|
+
await match.route.options.beforeLoad?.({
|
|
1263
|
+
router: this,
|
|
1264
|
+
match
|
|
1265
|
+
});
|
|
1266
|
+
} catch (err) {
|
|
1267
|
+
if (!loaderOpts?.preload) {
|
|
1268
|
+
match.route.options.onLoadError?.(err);
|
|
1269
|
+
}
|
|
1270
|
+
throw err;
|
|
1271
|
+
}
|
|
1272
|
+
}));
|
|
1273
|
+
const matchPromises = resolvedMatches.map(async (match, index) => {
|
|
1274
|
+
const prevMatch = resolvedMatches[1];
|
|
1275
|
+
const search = match.store.state.search;
|
|
1276
|
+
if (search.__data?.matchId && search.__data.matchId !== match.id) {
|
|
1277
|
+
return;
|
|
1278
|
+
}
|
|
1279
|
+
match.load(loaderOpts);
|
|
1280
|
+
if (match.store.state.status !== 'success' && match.__loadPromise) {
|
|
1281
|
+
// Wait for the first sign of activity from the match
|
|
1282
|
+
await match.__loadPromise;
|
|
1283
|
+
}
|
|
1284
|
+
if (prevMatch) {
|
|
1285
|
+
await prevMatch.__loadPromise;
|
|
1286
|
+
}
|
|
1287
|
+
});
|
|
1288
|
+
await Promise.all(matchPromises);
|
|
1289
|
+
};
|
|
1290
|
+
loadMatchData = async routeMatch => {
|
|
1291
|
+
if (isServer || !this.options.useServerData) {
|
|
1292
|
+
return (await routeMatch.route.options.loader?.({
|
|
1293
|
+
// parentLoaderPromise: routeMatch.parentMatch.dataPromise,
|
|
1294
|
+
params: routeMatch.params,
|
|
1295
|
+
search: routeMatch.store.state.routeSearch,
|
|
1296
|
+
signal: routeMatch.abortController.signal
|
|
1297
|
+
})) || {};
|
|
1298
|
+
} else {
|
|
1299
|
+
// Refresh:
|
|
1300
|
+
// '/dashboard'
|
|
1301
|
+
// '/dashboard/invoices/'
|
|
1302
|
+
// '/dashboard/invoices/123'
|
|
2077
1303
|
|
|
2078
|
-
|
|
1304
|
+
// New:
|
|
1305
|
+
// '/dashboard/invoices/456'
|
|
2079
1306
|
|
|
2080
|
-
|
|
2081
|
-
// If requested, start the pending timers
|
|
2082
|
-
if (loaderOpts != null && loaderOpts.withPending) match.__.startPending(); // Wait for the first sign of activity from the match
|
|
2083
|
-
// This might be completion, error, or a pending state
|
|
1307
|
+
// TODO: batch requests when possible
|
|
2084
1308
|
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
router.notify();
|
|
2089
|
-
await Promise.all(matchPromises);
|
|
2090
|
-
},
|
|
2091
|
-
invalidateRoute: opts => {
|
|
2092
|
-
var _router$state$pending5, _router$state$pending6;
|
|
2093
|
-
|
|
2094
|
-
const next = router.buildNext(opts);
|
|
2095
|
-
const unloadedMatchIds = router.matchRoutes(next.pathname).map(d => d.matchId);
|
|
2096
|
-
[...router.state.matches, ...((_router$state$pending5 = (_router$state$pending6 = router.state.pending) == null ? void 0 : _router$state$pending6.matches) != null ? _router$state$pending5 : [])].forEach(match => {
|
|
2097
|
-
if (unloadedMatchIds.includes(match.matchId)) {
|
|
2098
|
-
match.invalidate();
|
|
2099
|
-
}
|
|
1309
|
+
const res = await this.options.fetchServerDataFn({
|
|
1310
|
+
router: this,
|
|
1311
|
+
routeMatch
|
|
2100
1312
|
});
|
|
2101
|
-
|
|
2102
|
-
|
|
1313
|
+
return res;
|
|
1314
|
+
}
|
|
1315
|
+
};
|
|
1316
|
+
invalidateRoute = async opts => {
|
|
1317
|
+
const next = this.buildNext(opts);
|
|
1318
|
+
const unloadedMatchIds = this.matchRoutes(next.pathname).map(d => d.id);
|
|
1319
|
+
await Promise.allSettled([...this.store.state.currentMatches, ...(this.store.state.pendingMatches ?? [])].map(async match => {
|
|
1320
|
+
if (unloadedMatchIds.includes(match.id)) {
|
|
1321
|
+
return match.invalidate();
|
|
1322
|
+
}
|
|
1323
|
+
}));
|
|
1324
|
+
};
|
|
1325
|
+
reload = () => {
|
|
1326
|
+
this.navigate({
|
|
2103
1327
|
fromCurrent: true,
|
|
2104
1328
|
replace: true,
|
|
2105
1329
|
search: true
|
|
2106
|
-
})
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
1330
|
+
});
|
|
1331
|
+
};
|
|
1332
|
+
resolvePath = (from, path) => {
|
|
1333
|
+
return resolvePath(this.basepath, from, cleanPath(path));
|
|
1334
|
+
};
|
|
1335
|
+
navigate = async ({
|
|
1336
|
+
from,
|
|
1337
|
+
to = '.',
|
|
1338
|
+
search,
|
|
1339
|
+
hash,
|
|
1340
|
+
replace,
|
|
1341
|
+
params
|
|
1342
|
+
}) => {
|
|
1343
|
+
// If this link simply reloads the current route,
|
|
1344
|
+
// make sure it has a new key so it will trigger a data refresh
|
|
1345
|
+
|
|
1346
|
+
// If this `to` is a valid external URL, return
|
|
1347
|
+
// null for LinkUtils
|
|
1348
|
+
const toString = String(to);
|
|
1349
|
+
const fromString = String(from);
|
|
1350
|
+
let isExternal;
|
|
1351
|
+
try {
|
|
1352
|
+
new URL(`${toString}`);
|
|
1353
|
+
isExternal = true;
|
|
1354
|
+
} catch (e) {}
|
|
1355
|
+
invariant(!isExternal, 'Attempting to navigate to external url with this.navigate!');
|
|
1356
|
+
return this.#commitLocation({
|
|
1357
|
+
from: fromString,
|
|
1358
|
+
to: toString,
|
|
1359
|
+
search,
|
|
1360
|
+
hash,
|
|
1361
|
+
replace,
|
|
1362
|
+
params
|
|
1363
|
+
});
|
|
1364
|
+
};
|
|
1365
|
+
matchRoute = (location, opts) => {
|
|
1366
|
+
location = {
|
|
1367
|
+
...location,
|
|
1368
|
+
to: location.to ? this.resolvePath(location.from ?? '', location.to) : undefined
|
|
1369
|
+
};
|
|
1370
|
+
const next = this.buildNext(location);
|
|
1371
|
+
if (opts?.pending) {
|
|
1372
|
+
if (!this.store.state.pendingLocation) {
|
|
1373
|
+
return false;
|
|
2129
1374
|
}
|
|
2130
|
-
|
|
2131
|
-
|
|
1375
|
+
return matchPathname(this.basepath, this.store.state.pendingLocation.pathname, {
|
|
1376
|
+
...opts,
|
|
2132
1377
|
to: next.pathname
|
|
2133
|
-
}));
|
|
2134
|
-
},
|
|
2135
|
-
navigate: async _ref8 => {
|
|
2136
|
-
let {
|
|
2137
|
-
from,
|
|
2138
|
-
to = '.',
|
|
2139
|
-
search,
|
|
2140
|
-
hash,
|
|
2141
|
-
replace,
|
|
2142
|
-
params
|
|
2143
|
-
} = _ref8;
|
|
2144
|
-
// If this link simply reloads the current route,
|
|
2145
|
-
// make sure it has a new key so it will trigger a data refresh
|
|
2146
|
-
// If this `to` is a valid external URL, return
|
|
2147
|
-
// null for LinkUtils
|
|
2148
|
-
const toString = String(to);
|
|
2149
|
-
const fromString = String(from);
|
|
2150
|
-
let isExternal;
|
|
2151
|
-
|
|
2152
|
-
try {
|
|
2153
|
-
new URL("" + toString);
|
|
2154
|
-
isExternal = true;
|
|
2155
|
-
} catch (e) {}
|
|
2156
|
-
|
|
2157
|
-
invariant(!isExternal, 'Attempting to navigate to external url with router.navigate!');
|
|
2158
|
-
return router.__.navigate({
|
|
2159
|
-
from: fromString,
|
|
2160
|
-
to: toString,
|
|
2161
|
-
search,
|
|
2162
|
-
hash,
|
|
2163
|
-
replace,
|
|
2164
|
-
params
|
|
2165
1378
|
});
|
|
2166
|
-
}
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
return {
|
|
2193
|
-
type: 'external',
|
|
2194
|
-
href: to
|
|
2195
|
-
};
|
|
2196
|
-
} catch (e) {}
|
|
2197
|
-
|
|
2198
|
-
const nextOpts = {
|
|
2199
|
-
from,
|
|
2200
|
-
to,
|
|
2201
|
-
search,
|
|
2202
|
-
params,
|
|
2203
|
-
hash,
|
|
2204
|
-
replace
|
|
2205
|
-
};
|
|
2206
|
-
const next = router.buildNext(nextOpts);
|
|
2207
|
-
preload = (_preload = preload) != null ? _preload : router.options.defaultPreload;
|
|
2208
|
-
const preloadDelay = (_ref10 = userPreloadDelay != null ? userPreloadDelay : router.options.defaultPreloadDelay) != null ? _ref10 : 0; // Compare path/hash for matches
|
|
2209
|
-
|
|
2210
|
-
const pathIsEqual = router.state.location.pathname === next.pathname;
|
|
2211
|
-
const currentPathSplit = router.state.location.pathname.split('/');
|
|
2212
|
-
const nextPathSplit = next.pathname.split('/');
|
|
2213
|
-
const pathIsFuzzyEqual = nextPathSplit.every((d, i) => d === currentPathSplit[i]);
|
|
2214
|
-
const hashIsEqual = router.state.location.hash === next.hash; // Combine the matches based on user options
|
|
2215
|
-
|
|
2216
|
-
const pathTest = activeOptions != null && activeOptions.exact ? pathIsEqual : pathIsFuzzyEqual;
|
|
2217
|
-
const hashTest = activeOptions != null && activeOptions.includeHash ? hashIsEqual : true; // The final "active" test
|
|
2218
|
-
|
|
2219
|
-
const isActive = pathTest && hashTest; // The click handler
|
|
2220
|
-
|
|
2221
|
-
const handleClick = e => {
|
|
2222
|
-
if (!disabled && !isCtrlEvent(e) && !e.defaultPrevented && (!target || target === '_self') && e.button === 0) {
|
|
2223
|
-
e.preventDefault();
|
|
2224
|
-
|
|
2225
|
-
if (pathIsEqual && !search && !hash) {
|
|
2226
|
-
router.invalidateRoute(nextOpts);
|
|
2227
|
-
} // All is well? Navigate!)
|
|
2228
|
-
|
|
1379
|
+
}
|
|
1380
|
+
return matchPathname(this.basepath, this.store.state.currentLocation.pathname, {
|
|
1381
|
+
...opts,
|
|
1382
|
+
to: next.pathname
|
|
1383
|
+
});
|
|
1384
|
+
};
|
|
1385
|
+
buildLink = ({
|
|
1386
|
+
from,
|
|
1387
|
+
to = '.',
|
|
1388
|
+
search,
|
|
1389
|
+
params,
|
|
1390
|
+
hash,
|
|
1391
|
+
target,
|
|
1392
|
+
replace,
|
|
1393
|
+
activeOptions,
|
|
1394
|
+
preload,
|
|
1395
|
+
preloadMaxAge: userPreloadMaxAge,
|
|
1396
|
+
preloadGcMaxAge: userPreloadGcMaxAge,
|
|
1397
|
+
preloadDelay: userPreloadDelay,
|
|
1398
|
+
disabled
|
|
1399
|
+
}) => {
|
|
1400
|
+
// If this link simply reloads the current route,
|
|
1401
|
+
// make sure it has a new key so it will trigger a data refresh
|
|
1402
|
+
|
|
1403
|
+
// If this `to` is a valid external URL, return
|
|
1404
|
+
// null for LinkUtils
|
|
2229
1405
|
|
|
2230
|
-
|
|
1406
|
+
try {
|
|
1407
|
+
new URL(`${to}`);
|
|
1408
|
+
return {
|
|
1409
|
+
type: 'external',
|
|
1410
|
+
href: to
|
|
1411
|
+
};
|
|
1412
|
+
} catch (e) {}
|
|
1413
|
+
const nextOpts = {
|
|
1414
|
+
from,
|
|
1415
|
+
to,
|
|
1416
|
+
search,
|
|
1417
|
+
params,
|
|
1418
|
+
hash,
|
|
1419
|
+
replace
|
|
1420
|
+
};
|
|
1421
|
+
const next = this.buildNext(nextOpts);
|
|
1422
|
+
preload = preload ?? this.options.defaultPreload;
|
|
1423
|
+
const preloadDelay = userPreloadDelay ?? this.options.defaultPreloadDelay ?? 0;
|
|
1424
|
+
|
|
1425
|
+
// Compare path/hash for matches
|
|
1426
|
+
const pathIsEqual = this.store.state.currentLocation.pathname === next.pathname;
|
|
1427
|
+
const currentPathSplit = this.store.state.currentLocation.pathname.split('/');
|
|
1428
|
+
const nextPathSplit = next.pathname.split('/');
|
|
1429
|
+
const pathIsFuzzyEqual = nextPathSplit.every((d, i) => d === currentPathSplit[i]);
|
|
1430
|
+
const hashIsEqual = this.store.state.currentLocation.hash === next.hash;
|
|
1431
|
+
// Combine the matches based on user options
|
|
1432
|
+
const pathTest = activeOptions?.exact ? pathIsEqual : pathIsFuzzyEqual;
|
|
1433
|
+
const hashTest = activeOptions?.includeHash ? hashIsEqual : true;
|
|
1434
|
+
|
|
1435
|
+
// The final "active" test
|
|
1436
|
+
const isActive = pathTest && hashTest;
|
|
1437
|
+
|
|
1438
|
+
// The click handler
|
|
1439
|
+
const handleClick = e => {
|
|
1440
|
+
if (!disabled && !isCtrlEvent(e) && !e.defaultPrevented && (!target || target === '_self') && e.button === 0) {
|
|
1441
|
+
e.preventDefault();
|
|
1442
|
+
if (pathIsEqual && !search && !hash) {
|
|
1443
|
+
this.invalidateRoute(nextOpts);
|
|
2231
1444
|
}
|
|
2232
|
-
}; // The click handler
|
|
2233
1445
|
|
|
1446
|
+
// All is well? Navigate!
|
|
1447
|
+
this.#commitLocation(nextOpts);
|
|
1448
|
+
}
|
|
1449
|
+
};
|
|
2234
1450
|
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
1451
|
+
// The click handler
|
|
1452
|
+
const handleFocus = e => {
|
|
1453
|
+
if (preload) {
|
|
1454
|
+
this.preloadRoute(nextOpts, {
|
|
1455
|
+
maxAge: userPreloadMaxAge,
|
|
1456
|
+
gcMaxAge: userPreloadGcMaxAge
|
|
1457
|
+
}).catch(err => {
|
|
1458
|
+
console.warn(err);
|
|
1459
|
+
console.warn('Error preloading route! ☝️');
|
|
1460
|
+
});
|
|
1461
|
+
}
|
|
1462
|
+
};
|
|
1463
|
+
const handleEnter = e => {
|
|
1464
|
+
const target = e.target || {};
|
|
1465
|
+
if (preload) {
|
|
1466
|
+
if (target.preloadTimeout) {
|
|
1467
|
+
return;
|
|
1468
|
+
}
|
|
1469
|
+
target.preloadTimeout = setTimeout(() => {
|
|
1470
|
+
target.preloadTimeout = null;
|
|
1471
|
+
this.preloadRoute(nextOpts, {
|
|
2238
1472
|
maxAge: userPreloadMaxAge,
|
|
2239
1473
|
gcMaxAge: userPreloadGcMaxAge
|
|
1474
|
+
}).catch(err => {
|
|
1475
|
+
console.warn(err);
|
|
1476
|
+
console.warn('Error preloading route! ☝️');
|
|
2240
1477
|
});
|
|
2241
|
-
}
|
|
2242
|
-
}
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
1478
|
+
}, preloadDelay);
|
|
1479
|
+
}
|
|
1480
|
+
};
|
|
1481
|
+
const handleLeave = e => {
|
|
1482
|
+
const target = e.target || {};
|
|
1483
|
+
if (target.preloadTimeout) {
|
|
1484
|
+
clearTimeout(target.preloadTimeout);
|
|
1485
|
+
target.preloadTimeout = null;
|
|
1486
|
+
}
|
|
1487
|
+
};
|
|
1488
|
+
return {
|
|
1489
|
+
type: 'internal',
|
|
1490
|
+
next,
|
|
1491
|
+
handleFocus,
|
|
1492
|
+
handleClick,
|
|
1493
|
+
handleEnter,
|
|
1494
|
+
handleLeave,
|
|
1495
|
+
isActive,
|
|
1496
|
+
disabled
|
|
1497
|
+
};
|
|
1498
|
+
};
|
|
1499
|
+
dehydrate = () => {
|
|
1500
|
+
return {
|
|
1501
|
+
state: {
|
|
1502
|
+
...pick(this.store.state, ['latestLocation', 'currentLocation', 'status', 'lastUpdated']),
|
|
1503
|
+
currentMatches: this.store.state.currentMatches.map(match => ({
|
|
1504
|
+
id: match.id,
|
|
1505
|
+
state: {
|
|
1506
|
+
...pick(match.store.state, ['status', 'routeLoaderData', 'invalidAt', 'invalid'])
|
|
2250
1507
|
}
|
|
1508
|
+
}))
|
|
1509
|
+
},
|
|
1510
|
+
context: this.options.context
|
|
1511
|
+
};
|
|
1512
|
+
};
|
|
1513
|
+
hydrate = dehydratedRouter => {
|
|
1514
|
+
this.store.setState(s => {
|
|
1515
|
+
// Update the context TODO: make this part of state?
|
|
1516
|
+
this.options.context = dehydratedRouter.context;
|
|
2251
1517
|
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
1518
|
+
// Match the routes
|
|
1519
|
+
const currentMatches = this.matchRoutes(dehydratedRouter.state.latestLocation.pathname, {
|
|
1520
|
+
strictParseParams: true
|
|
1521
|
+
});
|
|
1522
|
+
currentMatches.forEach((match, index) => {
|
|
1523
|
+
const dehydratedMatch = dehydratedRouter.state.currentMatches[index];
|
|
1524
|
+
invariant(dehydratedMatch && dehydratedMatch.id === match.id, 'Oh no! There was a hydration mismatch when attempting to rethis.store the state of the router! 😬');
|
|
1525
|
+
Object.assign(match, dehydratedMatch);
|
|
1526
|
+
});
|
|
1527
|
+
currentMatches.forEach(match => match.__validate());
|
|
1528
|
+
Object.assign(s, {
|
|
1529
|
+
...dehydratedRouter.state,
|
|
1530
|
+
currentMatches
|
|
1531
|
+
});
|
|
1532
|
+
});
|
|
1533
|
+
};
|
|
1534
|
+
getLoader = opts => {
|
|
1535
|
+
const id = opts.from || '/';
|
|
1536
|
+
const route = this.getRoute(id);
|
|
1537
|
+
if (!route) return undefined;
|
|
1538
|
+
let loader = this.store.state.loaders[id] || (() => {
|
|
1539
|
+
this.store.setState(s => {
|
|
1540
|
+
s.loaders[id] = {
|
|
1541
|
+
pending: [],
|
|
1542
|
+
fetch: async loaderContext => {
|
|
1543
|
+
if (!route) {
|
|
1544
|
+
return;
|
|
1545
|
+
}
|
|
1546
|
+
const loaderState = {
|
|
1547
|
+
loadedAt: Date.now(),
|
|
1548
|
+
loaderContext
|
|
1549
|
+
};
|
|
1550
|
+
this.store.setState(s => {
|
|
1551
|
+
s.loaders[id].current = loaderState;
|
|
1552
|
+
s.loaders[id].latest = loaderState;
|
|
1553
|
+
s.loaders[id].pending.push(loaderState);
|
|
2257
1554
|
});
|
|
2258
|
-
|
|
1555
|
+
try {
|
|
1556
|
+
return await route.options.loader?.(loaderContext);
|
|
1557
|
+
} finally {
|
|
1558
|
+
this.store.setState(s => {
|
|
1559
|
+
s.loaders[id].pending = s.loaders[id].pending.filter(d => d !== loaderState);
|
|
1560
|
+
});
|
|
1561
|
+
}
|
|
1562
|
+
}
|
|
1563
|
+
};
|
|
1564
|
+
});
|
|
1565
|
+
return this.store.state.loaders[id];
|
|
1566
|
+
})();
|
|
1567
|
+
return loader;
|
|
1568
|
+
};
|
|
1569
|
+
#buildRouteTree = rootRouteConfig => {
|
|
1570
|
+
const recurseRoutes = (routeConfigs, parent) => {
|
|
1571
|
+
return routeConfigs.map((routeConfig, i) => {
|
|
1572
|
+
const routeOptions = routeConfig.options;
|
|
1573
|
+
const route = new Route(routeConfig, routeOptions, i, parent, this);
|
|
1574
|
+
const existingRoute = this.routesById[route.id];
|
|
1575
|
+
if (existingRoute) {
|
|
1576
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
1577
|
+
console.warn(`Duplicate routes found with id: ${String(route.id)}`, this.routesById, route);
|
|
1578
|
+
}
|
|
1579
|
+
throw new Error();
|
|
2259
1580
|
}
|
|
2260
|
-
|
|
1581
|
+
this.routesById[route.id] = route;
|
|
1582
|
+
const children = routeConfig.children;
|
|
1583
|
+
route.childRoutes = children.length ? recurseRoutes(children, route) : undefined;
|
|
1584
|
+
return route;
|
|
1585
|
+
});
|
|
1586
|
+
};
|
|
1587
|
+
const routes = recurseRoutes([rootRouteConfig]);
|
|
1588
|
+
return routes[0];
|
|
1589
|
+
};
|
|
1590
|
+
#parseLocation = previousLocation => {
|
|
1591
|
+
let {
|
|
1592
|
+
pathname,
|
|
1593
|
+
search,
|
|
1594
|
+
hash,
|
|
1595
|
+
state
|
|
1596
|
+
} = this.history.location;
|
|
1597
|
+
const parsedSearch = this.options.parseSearch(search);
|
|
1598
|
+
return {
|
|
1599
|
+
pathname: pathname,
|
|
1600
|
+
searchStr: search,
|
|
1601
|
+
search: replaceEqualDeep(previousLocation?.search, parsedSearch),
|
|
1602
|
+
hash: hash.split('#').reverse()[0] ?? '',
|
|
1603
|
+
href: `${pathname}${search}${hash}`,
|
|
1604
|
+
state: state,
|
|
1605
|
+
key: state?.key || '__init__'
|
|
1606
|
+
};
|
|
1607
|
+
};
|
|
1608
|
+
#onFocus = () => {
|
|
1609
|
+
this.load();
|
|
1610
|
+
};
|
|
1611
|
+
#buildLocation = (dest = {}) => {
|
|
1612
|
+
const fromPathname = dest.fromCurrent ? this.store.state.latestLocation.pathname : dest.from ?? this.store.state.latestLocation.pathname;
|
|
1613
|
+
let pathname = resolvePath(this.basepath ?? '/', fromPathname, `${dest.to ?? '.'}`);
|
|
1614
|
+
const fromMatches = this.matchRoutes(this.store.state.latestLocation.pathname, {
|
|
1615
|
+
strictParseParams: true
|
|
1616
|
+
});
|
|
1617
|
+
const toMatches = this.matchRoutes(pathname);
|
|
1618
|
+
const prevParams = {
|
|
1619
|
+
...last(fromMatches)?.params
|
|
1620
|
+
};
|
|
1621
|
+
let nextParams = (dest.params ?? true) === true ? prevParams : functionalUpdate(dest.params, prevParams);
|
|
1622
|
+
if (nextParams) {
|
|
1623
|
+
toMatches.map(d => d.route.options.stringifyParams).filter(Boolean).forEach(fn => {
|
|
1624
|
+
Object.assign({}, nextParams, fn(nextParams));
|
|
1625
|
+
});
|
|
1626
|
+
}
|
|
1627
|
+
pathname = interpolatePath(pathname, nextParams ?? {});
|
|
1628
|
+
|
|
1629
|
+
// Pre filters first
|
|
1630
|
+
const preFilteredSearch = dest.__preSearchFilters?.length ? dest.__preSearchFilters?.reduce((prev, next) => next(prev), this.store.state.latestLocation.search) : this.store.state.latestLocation.search;
|
|
1631
|
+
|
|
1632
|
+
// Then the link/navigate function
|
|
1633
|
+
const destSearch = dest.search === true ? preFilteredSearch // Preserve resolvedFrom true
|
|
1634
|
+
: dest.search ? functionalUpdate(dest.search, preFilteredSearch) ?? {} // Updater
|
|
1635
|
+
: dest.__preSearchFilters?.length ? preFilteredSearch // Preserve resolvedFrom filters
|
|
1636
|
+
: {};
|
|
1637
|
+
|
|
1638
|
+
// Then post filters
|
|
1639
|
+
const postFilteredSearch = dest.__postSearchFilters?.length ? dest.__postSearchFilters.reduce((prev, next) => next(prev), destSearch) : destSearch;
|
|
1640
|
+
const search = replaceEqualDeep(this.store.state.latestLocation.search, postFilteredSearch);
|
|
1641
|
+
const searchStr = this.options.stringifySearch(search);
|
|
1642
|
+
let hash = dest.hash === true ? this.store.state.latestLocation.hash : functionalUpdate(dest.hash, this.store.state.latestLocation.hash);
|
|
1643
|
+
hash = hash ? `#${hash}` : '';
|
|
1644
|
+
return {
|
|
1645
|
+
pathname,
|
|
1646
|
+
search,
|
|
1647
|
+
searchStr,
|
|
1648
|
+
state: this.store.state.latestLocation.state,
|
|
1649
|
+
hash,
|
|
1650
|
+
href: `${pathname}${searchStr}${hash}`,
|
|
1651
|
+
key: dest.key
|
|
1652
|
+
};
|
|
1653
|
+
};
|
|
1654
|
+
#commitLocation = location => {
|
|
1655
|
+
const next = this.buildNext(location);
|
|
1656
|
+
const id = '' + Date.now() + Math.random();
|
|
1657
|
+
if (this.navigateTimeout) clearTimeout(this.navigateTimeout);
|
|
1658
|
+
let nextAction = 'replace';
|
|
1659
|
+
if (!location.replace) {
|
|
1660
|
+
nextAction = 'push';
|
|
1661
|
+
}
|
|
1662
|
+
const isSameUrl = this.store.state.latestLocation.href === next.href;
|
|
1663
|
+
if (isSameUrl && !next.key) {
|
|
1664
|
+
nextAction = 'replace';
|
|
1665
|
+
}
|
|
1666
|
+
const href = `${next.pathname}${next.searchStr}${next.hash ? `#${next.hash}` : ''}`;
|
|
1667
|
+
this.history[nextAction === 'push' ? 'push' : 'replace'](href, {
|
|
1668
|
+
id,
|
|
1669
|
+
...next.state
|
|
1670
|
+
});
|
|
2261
1671
|
|
|
2262
|
-
|
|
2263
|
-
const target = e.target || {};
|
|
1672
|
+
// this.load(this.#parseLocation(this.store.state.latestLocation))
|
|
2264
1673
|
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
1674
|
+
return this.navigationPromise = new Promise(resolve => {
|
|
1675
|
+
const previousNavigationResolve = this.resolveNavigation;
|
|
1676
|
+
this.resolveNavigation = () => {
|
|
1677
|
+
previousNavigationResolve();
|
|
1678
|
+
resolve();
|
|
2269
1679
|
};
|
|
1680
|
+
});
|
|
1681
|
+
};
|
|
1682
|
+
}
|
|
2270
1683
|
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
1684
|
+
// Detect if we're in the DOM
|
|
1685
|
+
const isServer = typeof window === 'undefined' || !window.document.createElement;
|
|
1686
|
+
function getInitialRouterState() {
|
|
1687
|
+
return {
|
|
1688
|
+
status: 'idle',
|
|
1689
|
+
latestLocation: null,
|
|
1690
|
+
currentLocation: null,
|
|
1691
|
+
currentMatches: [],
|
|
1692
|
+
loaders: {},
|
|
1693
|
+
lastUpdated: Date.now(),
|
|
1694
|
+
matchCache: {},
|
|
1695
|
+
get isFetching() {
|
|
1696
|
+
return this.status === 'loading' || this.currentMatches.some(d => d.store.state.isFetching);
|
|
2281
1697
|
},
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
1698
|
+
get isPreloading() {
|
|
1699
|
+
return Object.values(this.matchCache).some(d => d.match.store.state.isFetching && !this.currentMatches.find(dd => dd.id === d.match.id));
|
|
1700
|
+
}
|
|
1701
|
+
};
|
|
1702
|
+
}
|
|
1703
|
+
function isCtrlEvent(e) {
|
|
1704
|
+
return !!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey);
|
|
1705
|
+
}
|
|
1706
|
+
function linkMatches(matches) {
|
|
1707
|
+
matches.forEach((match, index) => {
|
|
1708
|
+
const parent = matches[index - 1];
|
|
1709
|
+
if (parent) {
|
|
1710
|
+
match.__setParentMatch(parent);
|
|
1711
|
+
}
|
|
1712
|
+
});
|
|
1713
|
+
}
|
|
2298
1714
|
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
1715
|
+
// RouterAction is a constrained identify function that takes options: key, action, onSuccess, onError, onSettled, etc
|
|
1716
|
+
function createAction(options) {
|
|
1717
|
+
const store = createStore({
|
|
1718
|
+
submissions: []
|
|
1719
|
+
}, options.debug);
|
|
1720
|
+
return {
|
|
1721
|
+
options,
|
|
1722
|
+
store,
|
|
1723
|
+
reset: () => {
|
|
1724
|
+
store.setState(s => {
|
|
1725
|
+
s.submissions = [];
|
|
1726
|
+
});
|
|
2303
1727
|
},
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
// }
|
|
2313
|
-
|
|
2314
|
-
const existingRoute = router.routesById[route.routeId];
|
|
2315
|
-
|
|
2316
|
-
if (existingRoute) {
|
|
2317
|
-
if (process.env.NODE_ENV !== 'production') {
|
|
2318
|
-
console.warn("Duplicate routes found with id: " + String(route.routeId), router.routesById, route);
|
|
2319
|
-
}
|
|
2320
|
-
|
|
2321
|
-
throw new Error();
|
|
2322
|
-
}
|
|
2323
|
-
router.routesById[route.routeId] = route;
|
|
2324
|
-
const children = routeConfig.children;
|
|
2325
|
-
route.childRoutes = children != null && children.length ? recurseRoutes(children, route) : undefined;
|
|
2326
|
-
return route;
|
|
1728
|
+
submit: async payload => {
|
|
1729
|
+
const submission = {
|
|
1730
|
+
submittedAt: Date.now(),
|
|
1731
|
+
status: 'pending',
|
|
1732
|
+
payload: payload,
|
|
1733
|
+
invalidate: () => {
|
|
1734
|
+
setSubmission(s => {
|
|
1735
|
+
s.isInvalid = true;
|
|
2327
1736
|
});
|
|
2328
|
-
}
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
const parsedSearch = router.options.parseSearch(location.search);
|
|
2337
|
-
return {
|
|
2338
|
-
pathname: location.pathname,
|
|
2339
|
-
searchStr: location.search,
|
|
2340
|
-
search: replaceEqualDeep(previousLocation == null ? void 0 : previousLocation.search, parsedSearch),
|
|
2341
|
-
hash: (_location$hash$split$ = location.hash.split('#').reverse()[0]) != null ? _location$hash$split$ : '',
|
|
2342
|
-
href: "" + location.pathname + location.search + location.hash,
|
|
2343
|
-
state: location.state,
|
|
2344
|
-
key: location.key
|
|
2345
|
-
};
|
|
2346
|
-
},
|
|
2347
|
-
navigate: location => {
|
|
2348
|
-
const next = router.buildNext(location);
|
|
2349
|
-
return router.__.commitLocation(next, location.replace);
|
|
2350
|
-
},
|
|
2351
|
-
buildLocation: function buildLocation(dest) {
|
|
2352
|
-
var _dest$from, _router$basepath, _dest$to, _last, _dest$params, _dest$__preSearchFilt, _functionalUpdate, _dest$__preSearchFilt2, _dest$__postSearchFil;
|
|
2353
|
-
|
|
2354
|
-
if (dest === void 0) {
|
|
2355
|
-
dest = {};
|
|
2356
|
-
}
|
|
2357
|
-
|
|
2358
|
-
// const resolvedFrom: Location = {
|
|
2359
|
-
// ...router.location,
|
|
2360
|
-
const fromPathname = dest.fromCurrent ? router.location.pathname : (_dest$from = dest.from) != null ? _dest$from : router.location.pathname;
|
|
2361
|
-
|
|
2362
|
-
let pathname = resolvePath((_router$basepath = router.basepath) != null ? _router$basepath : '/', fromPathname, "" + ((_dest$to = dest.to) != null ? _dest$to : '.'));
|
|
2363
|
-
|
|
2364
|
-
const fromMatches = router.matchRoutes(router.location.pathname, {
|
|
2365
|
-
strictParseParams: true
|
|
1737
|
+
},
|
|
1738
|
+
getIsLatest: () => store.state.submissions[store.state.submissions.length - 1]?.submittedAt === submission.submittedAt
|
|
1739
|
+
};
|
|
1740
|
+
const setSubmission = updater => {
|
|
1741
|
+
store.setState(s => {
|
|
1742
|
+
const a = s.submissions.find(d => d.submittedAt === submission.submittedAt);
|
|
1743
|
+
invariant(a, 'Could not find submission in store');
|
|
1744
|
+
updater(a);
|
|
2366
1745
|
});
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
const destSearch = dest.search === true ? preFilteredSearch // Preserve resolvedFrom true
|
|
2384
|
-
: dest.search ? (_functionalUpdate = functionalUpdate(dest.search, preFilteredSearch)) != null ? _functionalUpdate : {} // Updater
|
|
2385
|
-
: (_dest$__preSearchFilt2 = dest.__preSearchFilters) != null && _dest$__preSearchFilt2.length ? preFilteredSearch // Preserve resolvedFrom filters
|
|
2386
|
-
: {}; // Then post filters
|
|
2387
|
-
|
|
2388
|
-
const postFilteredSearch = (_dest$__postSearchFil = dest.__postSearchFilters) != null && _dest$__postSearchFil.length ? dest.__postSearchFilters.reduce((prev, next) => next(prev), destSearch) : destSearch;
|
|
2389
|
-
const search = replaceEqualDeep(router.location.search, postFilteredSearch);
|
|
2390
|
-
const searchStr = router.options.stringifySearch(search);
|
|
2391
|
-
let hash = dest.hash === true ? router.location.hash : functionalUpdate(dest.hash, router.location.hash);
|
|
2392
|
-
hash = hash ? "#" + hash : '';
|
|
2393
|
-
return {
|
|
2394
|
-
pathname,
|
|
2395
|
-
search,
|
|
2396
|
-
searchStr,
|
|
2397
|
-
state: router.location.state,
|
|
2398
|
-
hash,
|
|
2399
|
-
href: "" + pathname + searchStr + hash,
|
|
2400
|
-
key: dest.key
|
|
2401
|
-
};
|
|
2402
|
-
},
|
|
2403
|
-
commitLocation: (next, replace) => {
|
|
2404
|
-
const id = '' + Date.now() + Math.random();
|
|
2405
|
-
if (router.navigateTimeout) clearTimeout(router.navigateTimeout);
|
|
2406
|
-
let nextAction = 'replace';
|
|
2407
|
-
|
|
2408
|
-
if (!replace) {
|
|
2409
|
-
nextAction = 'push';
|
|
2410
|
-
}
|
|
2411
|
-
|
|
2412
|
-
const isSameUrl = router.__.parseLocation(history.location).href === next.href;
|
|
2413
|
-
|
|
2414
|
-
if (isSameUrl && !next.key) {
|
|
2415
|
-
nextAction = 'replace';
|
|
2416
|
-
}
|
|
2417
|
-
|
|
2418
|
-
if (nextAction === 'replace') {
|
|
2419
|
-
history.replace({
|
|
2420
|
-
pathname: next.pathname,
|
|
2421
|
-
hash: next.hash,
|
|
2422
|
-
search: next.searchStr
|
|
2423
|
-
}, {
|
|
2424
|
-
id
|
|
2425
|
-
});
|
|
2426
|
-
} else {
|
|
2427
|
-
history.push({
|
|
2428
|
-
pathname: next.pathname,
|
|
2429
|
-
hash: next.hash,
|
|
2430
|
-
search: next.searchStr
|
|
2431
|
-
}, {
|
|
2432
|
-
id
|
|
2433
|
-
});
|
|
2434
|
-
}
|
|
2435
|
-
|
|
2436
|
-
router.navigationPromise = new Promise(resolve => {
|
|
2437
|
-
const previousNavigationResolve = router.resolveNavigation;
|
|
2438
|
-
|
|
2439
|
-
router.resolveNavigation = () => {
|
|
2440
|
-
previousNavigationResolve();
|
|
2441
|
-
resolve();
|
|
2442
|
-
};
|
|
1746
|
+
};
|
|
1747
|
+
store.setState(s => {
|
|
1748
|
+
s.submissions.push(submission);
|
|
1749
|
+
s.submissions.reverse();
|
|
1750
|
+
s.submissions = s.submissions.slice(0, options.maxSubmissions ?? 10);
|
|
1751
|
+
s.submissions.reverse();
|
|
1752
|
+
});
|
|
1753
|
+
const after = async () => {
|
|
1754
|
+
options.onEachSettled?.(submission);
|
|
1755
|
+
if (submission.getIsLatest()) await options.onLatestSettled?.(submission);
|
|
1756
|
+
};
|
|
1757
|
+
try {
|
|
1758
|
+
const res = await options.action?.(submission.payload);
|
|
1759
|
+
setSubmission(s => {
|
|
1760
|
+
s.response = res;
|
|
2443
1761
|
});
|
|
2444
|
-
|
|
1762
|
+
await options.onEachSuccess?.(submission);
|
|
1763
|
+
if (submission.getIsLatest()) await options.onLatestSuccess?.(submission);
|
|
1764
|
+
await after();
|
|
1765
|
+
setSubmission(s => {
|
|
1766
|
+
s.status = 'success';
|
|
1767
|
+
});
|
|
1768
|
+
return res;
|
|
1769
|
+
} catch (err) {
|
|
1770
|
+
console.error(err);
|
|
1771
|
+
setSubmission(s => {
|
|
1772
|
+
s.error = err;
|
|
1773
|
+
});
|
|
1774
|
+
await options.onEachError?.(submission);
|
|
1775
|
+
if (submission.getIsLatest()) await options.onLatestError?.(submission);
|
|
1776
|
+
await after();
|
|
1777
|
+
setSubmission(s => {
|
|
1778
|
+
s.status = 'error';
|
|
1779
|
+
});
|
|
1780
|
+
throw err;
|
|
2445
1781
|
}
|
|
2446
1782
|
}
|
|
2447
1783
|
};
|
|
2448
|
-
router.update(userOptions); // Allow frameworks to hook into the router creation
|
|
2449
|
-
|
|
2450
|
-
router.options.createRouter == null ? void 0 : router.options.createRouter(router);
|
|
2451
|
-
return router;
|
|
2452
|
-
}
|
|
2453
|
-
|
|
2454
|
-
function isCtrlEvent(e) {
|
|
2455
|
-
return !!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey);
|
|
2456
1784
|
}
|
|
2457
1785
|
|
|
2458
|
-
export {
|
|
1786
|
+
export { Route, RouteMatch, Router, batch, cleanPath, createAction, createBrowserHistory, createHashHistory, createMemoryHistory, createRouteConfig, createStore, decode, defaultFetchServerDataFn, defaultParseSearch, defaultStringifySearch, encode, functionalUpdate, interpolatePath, joinPaths, last, matchByPath, matchPathname, parsePathname, parseSearchWith, pick, replaceEqualDeep, resolvePath, rootRouteId, stringifySearchWith, trackDeep, trimPath, trimPathLeft, trimPathRight, warning };
|
|
2459
1787
|
//# sourceMappingURL=index.js.map
|