@tanstack/react-router 0.0.1-beta.22 → 0.0.1-beta.221

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.
Files changed (108) hide show
  1. package/LICENSE +21 -0
  2. package/build/cjs/CatchBoundary.js +126 -0
  3. package/build/cjs/CatchBoundary.js.map +1 -0
  4. package/build/cjs/Matches.js +235 -0
  5. package/build/cjs/Matches.js.map +1 -0
  6. package/build/cjs/RouterProvider.js +1085 -0
  7. package/build/cjs/RouterProvider.js.map +1 -0
  8. package/build/cjs/_virtual/_rollupPluginBabelHelpers.js +1 -19
  9. package/build/cjs/_virtual/_rollupPluginBabelHelpers.js.map +1 -1
  10. package/build/cjs/awaited.js +45 -0
  11. package/build/cjs/awaited.js.map +1 -0
  12. package/build/cjs/defer.js +39 -0
  13. package/build/cjs/defer.js.map +1 -0
  14. package/build/cjs/fileRoute.js +29 -0
  15. package/build/cjs/fileRoute.js.map +1 -0
  16. package/build/cjs/index.js +135 -0
  17. package/build/cjs/index.js.map +1 -0
  18. package/build/cjs/lazyRouteComponent.js +57 -0
  19. package/build/cjs/lazyRouteComponent.js.map +1 -0
  20. package/build/cjs/link.js +151 -0
  21. package/build/cjs/link.js.map +1 -0
  22. package/build/cjs/path.js +211 -0
  23. package/build/cjs/path.js.map +1 -0
  24. package/build/cjs/qss.js +65 -0
  25. package/build/cjs/qss.js.map +1 -0
  26. package/build/cjs/redirects.js +27 -0
  27. package/build/cjs/redirects.js.map +1 -0
  28. package/build/cjs/route.js +139 -0
  29. package/build/cjs/route.js.map +1 -0
  30. package/build/cjs/router.js +160 -0
  31. package/build/cjs/router.js.map +1 -0
  32. package/build/cjs/scroll-restoration.js +186 -0
  33. package/build/cjs/scroll-restoration.js.map +1 -0
  34. package/build/cjs/searchParams.js +83 -0
  35. package/build/cjs/searchParams.js.map +1 -0
  36. package/build/cjs/useBlocker.js +64 -0
  37. package/build/cjs/useBlocker.js.map +1 -0
  38. package/build/cjs/useNavigate.js +78 -0
  39. package/build/cjs/useNavigate.js.map +1 -0
  40. package/build/cjs/useParams.js +28 -0
  41. package/build/cjs/useParams.js.map +1 -0
  42. package/build/cjs/useSearch.js +27 -0
  43. package/build/cjs/useSearch.js.map +1 -0
  44. package/build/cjs/utils.js +236 -0
  45. package/build/cjs/utils.js.map +1 -0
  46. package/build/esm/index.js +2136 -2564
  47. package/build/esm/index.js.map +1 -1
  48. package/build/stats-html.html +59 -49
  49. package/build/stats-react.json +922 -43
  50. package/build/types/CatchBoundary.d.ts +33 -0
  51. package/build/types/Matches.d.ts +34 -0
  52. package/build/types/RouterProvider.d.ts +87 -0
  53. package/build/types/awaited.d.ts +8 -0
  54. package/build/types/defer.d.ts +19 -0
  55. package/build/types/fileRoute.d.ts +32 -0
  56. package/build/types/history.d.ts +7 -0
  57. package/build/types/index.d.ts +27 -104
  58. package/build/types/injectHtml.d.ts +0 -0
  59. package/build/types/lazyRouteComponent.d.ts +2 -0
  60. package/build/types/link.d.ts +105 -0
  61. package/build/types/location.d.ts +14 -0
  62. package/build/types/path.d.ts +16 -0
  63. package/build/types/qss.d.ts +2 -0
  64. package/build/types/redirects.d.ts +10 -0
  65. package/build/types/route.d.ts +270 -0
  66. package/build/types/routeInfo.d.ts +22 -0
  67. package/build/types/router.d.ts +123 -0
  68. package/build/types/scroll-restoration.d.ts +6 -0
  69. package/build/types/searchParams.d.ts +7 -0
  70. package/build/types/useBlocker.d.ts +8 -0
  71. package/build/types/useNavigate.d.ts +20 -0
  72. package/build/types/useParams.d.ts +7 -0
  73. package/build/types/useSearch.d.ts +7 -0
  74. package/build/types/utils.d.ts +66 -0
  75. package/build/umd/index.development.js +2399 -2484
  76. package/build/umd/index.development.js.map +1 -1
  77. package/build/umd/index.production.js +4 -4
  78. package/build/umd/index.production.js.map +1 -1
  79. package/package.json +9 -10
  80. package/src/CatchBoundary.tsx +98 -0
  81. package/src/Matches.tsx +345 -0
  82. package/src/RouterProvider.tsx +1575 -0
  83. package/src/awaited.tsx +40 -0
  84. package/src/defer.ts +55 -0
  85. package/src/fileRoute.ts +153 -0
  86. package/src/history.ts +8 -0
  87. package/src/index.tsx +28 -693
  88. package/src/injectHtml.ts +28 -0
  89. package/src/lazyRouteComponent.tsx +33 -0
  90. package/src/link.tsx +508 -0
  91. package/src/location.ts +15 -0
  92. package/src/path.ts +256 -0
  93. package/src/qss.ts +53 -0
  94. package/src/redirects.ts +31 -0
  95. package/src/route.ts +837 -0
  96. package/src/routeInfo.ts +68 -0
  97. package/src/router.ts +330 -0
  98. package/src/scroll-restoration.tsx +192 -0
  99. package/src/searchParams.ts +79 -0
  100. package/src/useBlocker.tsx +34 -0
  101. package/src/useNavigate.tsx +109 -0
  102. package/src/useParams.tsx +25 -0
  103. package/src/useSearch.tsx +25 -0
  104. package/src/utils.ts +350 -0
  105. package/build/cjs/react-router/src/index.js +0 -466
  106. package/build/cjs/react-router/src/index.js.map +0 -1
  107. package/build/cjs/router-core/build/esm/index.js +0 -2523
  108. package/build/cjs/router-core/build/esm/index.js.map +0 -1
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router
2
+ * @tanstack/react-router/src/index.tsx
3
3
  *
4
4
  * Copyright (c) TanStack
5
5
  *
@@ -8,978 +8,284 @@
8
8
  *
9
9
  * @license MIT
10
10
  */
11
+ import { createBrowserHistory } from '@tanstack/history';
12
+ export * from '@tanstack/history';
13
+ import invariant from 'tiny-invariant';
14
+ export { default as invariant } from 'tiny-invariant';
15
+ import warning from 'tiny-warning';
16
+ export { default as warning } from 'tiny-warning';
11
17
  import * as React from 'react';
12
- import { useSyncExternalStore } from 'use-sync-external-store/shim';
13
18
 
14
- function _extends$2() {
15
- _extends$2 = Object.assign ? Object.assign.bind() : function (target) {
16
- for (var i = 1; i < arguments.length; i++) {
17
- var source = arguments[i];
18
-
19
- for (var key in source) {
20
- if (Object.prototype.hasOwnProperty.call(source, key)) {
21
- target[key] = source[key];
22
- }
19
+ function CatchBoundary(props) {
20
+ const errorComponent = props.errorComponent ?? ErrorComponent;
21
+ return /*#__PURE__*/React.createElement(CatchBoundaryImpl, {
22
+ resetKey: props.resetKey,
23
+ onCatch: props.onCatch,
24
+ children: ({
25
+ error
26
+ }) => {
27
+ if (error) {
28
+ return /*#__PURE__*/React.createElement(errorComponent, {
29
+ error
30
+ });
23
31
  }
32
+ return props.children;
24
33
  }
25
-
26
- return target;
27
- };
28
- return _extends$2.apply(this, arguments);
34
+ });
29
35
  }
30
-
31
- function _objectWithoutPropertiesLoose(source, excluded) {
32
- if (source == null) return {};
33
- var target = {};
34
- var sourceKeys = Object.keys(source);
35
- var key, i;
36
-
37
- for (i = 0; i < sourceKeys.length; i++) {
38
- key = sourceKeys[i];
39
- if (excluded.indexOf(key) >= 0) continue;
40
- target[key] = source[key];
36
+ class CatchBoundaryImpl extends React.Component {
37
+ state = {
38
+ error: null
39
+ };
40
+ static getDerivedStateFromError(error) {
41
+ return {
42
+ error
43
+ };
41
44
  }
42
-
43
- return target;
44
- }
45
-
46
- /**
47
- * router-core
48
- *
49
- * Copyright (c) TanStack
50
- *
51
- * This source code is licensed under the MIT license found in the
52
- * LICENSE.md file in the root directory of this source tree.
53
- *
54
- * @license MIT
55
- */
56
- function _extends$1() {
57
- _extends$1 = Object.assign ? Object.assign.bind() : function (target) {
58
- for (var i = 1; i < arguments.length; i++) {
59
- var source = arguments[i];
60
-
61
- for (var key in source) {
62
- if (Object.prototype.hasOwnProperty.call(source, key)) {
63
- target[key] = source[key];
64
- }
65
- }
45
+ componentDidUpdate(prevProps, prevState) {
46
+ if (prevState.error && prevProps.resetKey !== this.props.resetKey) {
47
+ this.setState({
48
+ error: null
49
+ });
66
50
  }
67
-
68
- return target;
69
- };
70
- return _extends$1.apply(this, arguments);
71
- }
72
-
73
- /**
74
- * Actions represent the type of change to a location value.
75
- *
76
- * @see https://github.com/remix-run/history/tree/main/docs/api-reference.md#action
77
- */
78
- var Action;
79
-
80
- (function (Action) {
81
- /**
82
- * A POP indicates a change to an arbitrary index in the history stack, such
83
- * as a back or forward navigation. It does not describe the direction of the
84
- * navigation, only that the current index changed.
85
- *
86
- * Note: This is the default action for newly created history objects.
87
- */
88
- Action["Pop"] = "POP";
89
- /**
90
- * A PUSH indicates a new entry being added to the history stack, such as when
91
- * a link is clicked and a new page loads. When this happens, all subsequent
92
- * entries in the stack are lost.
93
- */
94
-
95
- Action["Push"] = "PUSH";
96
- /**
97
- * A REPLACE indicates the entry at the current index in the history stack
98
- * being replaced by a new one.
99
- */
100
-
101
- Action["Replace"] = "REPLACE";
102
- })(Action || (Action = {}));
103
-
104
- var readOnly = process.env.NODE_ENV !== "production" ? function (obj) {
105
- return Object.freeze(obj);
106
- } : function (obj) {
107
- return obj;
108
- };
109
-
110
- function warning$1(cond, message) {
111
- if (!cond) {
112
- // eslint-disable-next-line no-console
113
- if (typeof console !== 'undefined') console.warn(message);
114
-
115
- try {
116
- // Welcome to debugging history!
117
- //
118
- // This error is thrown as a convenience so you can more easily
119
- // find the source for a warning that appears in the console by
120
- // enabling "pause on exceptions" in your JavaScript debugger.
121
- throw new Error(message); // eslint-disable-next-line no-empty
122
- } catch (e) {}
123
51
  }
124
- }
125
-
126
- var BeforeUnloadEventType = 'beforeunload';
127
- var HashChangeEventType = 'hashchange';
128
- var PopStateEventType = 'popstate';
129
- /**
130
- * Browser history stores the location in regular URLs. This is the standard for
131
- * most web apps, but it requires some configuration on the server to ensure you
132
- * serve the same app at multiple URLs.
133
- *
134
- * @see https://github.com/remix-run/history/tree/main/docs/api-reference.md#createbrowserhistory
135
- */
136
-
137
- function createBrowserHistory(options) {
138
- if (options === void 0) {
139
- options = {};
52
+ componentDidCatch(error) {
53
+ console.error(error);
54
+ this.props.onCatch?.(error);
140
55
  }
141
-
142
- var _options = options,
143
- _options$window = _options.window,
144
- window = _options$window === void 0 ? document.defaultView : _options$window;
145
- var globalHistory = window.history;
146
-
147
- function getIndexAndLocation() {
148
- var _window$location = window.location,
149
- pathname = _window$location.pathname,
150
- search = _window$location.search,
151
- hash = _window$location.hash;
152
- var state = globalHistory.state || {};
153
- return [state.idx, readOnly({
154
- pathname: pathname,
155
- search: search,
156
- hash: hash,
157
- state: state.usr || null,
158
- key: state.key || 'default'
159
- })];
56
+ render() {
57
+ return this.props.children(this.state);
160
58
  }
161
-
162
- var blockedPopTx = null;
163
-
164
- function handlePop() {
165
- if (blockedPopTx) {
166
- blockers.call(blockedPopTx);
167
- blockedPopTx = null;
168
- } else {
169
- var nextAction = Action.Pop;
170
-
171
- var _getIndexAndLocation = getIndexAndLocation(),
172
- nextIndex = _getIndexAndLocation[0],
173
- nextLocation = _getIndexAndLocation[1];
174
-
175
- if (blockers.length) {
176
- if (nextIndex != null) {
177
- var delta = index - nextIndex;
178
-
179
- if (delta) {
180
- // Revert the POP
181
- blockedPopTx = {
182
- action: nextAction,
183
- location: nextLocation,
184
- retry: function retry() {
185
- go(delta * -1);
186
- }
187
- };
188
- go(delta);
189
- }
190
- } else {
191
- // Trying to POP to a location with no index. We did not create
192
- // this location, so we can't effectively block the navigation.
193
- process.env.NODE_ENV !== "production" ? warning$1(false, // TODO: Write up a doc that explains our blocking strategy in
194
- // detail and link to it here so people can understand better what
195
- // is going on and how to avoid it.
196
- "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;
197
- }
198
- } else {
199
- applyTx(nextAction);
200
- }
59
+ }
60
+ function ErrorComponent({
61
+ error
62
+ }) {
63
+ const [show, setShow] = React.useState(process.env.NODE_ENV !== 'production');
64
+ return /*#__PURE__*/React.createElement("div", {
65
+ style: {
66
+ padding: '.5rem',
67
+ maxWidth: '100%'
201
68
  }
202
- }
203
-
204
- window.addEventListener(PopStateEventType, handlePop);
205
- var action = Action.Pop;
206
-
207
- var _getIndexAndLocation2 = getIndexAndLocation(),
208
- index = _getIndexAndLocation2[0],
209
- location = _getIndexAndLocation2[1];
210
-
211
- var listeners = createEvents();
212
- var blockers = createEvents();
213
-
214
- if (index == null) {
215
- index = 0;
216
- globalHistory.replaceState(_extends$1({}, globalHistory.state, {
217
- idx: index
218
- }), '');
219
- }
220
-
221
- function createHref(to) {
222
- return typeof to === 'string' ? to : createPath(to);
223
- } // state defaults to `null` because `window.history.state` does
224
-
225
-
226
- function getNextLocation(to, state) {
227
- if (state === void 0) {
228
- state = null;
69
+ }, /*#__PURE__*/React.createElement("div", {
70
+ style: {
71
+ display: 'flex',
72
+ alignItems: 'center',
73
+ gap: '.5rem'
229
74
  }
230
-
231
- return readOnly(_extends$1({
232
- pathname: location.pathname,
233
- hash: '',
234
- search: ''
235
- }, typeof to === 'string' ? parsePath(to) : to, {
236
- state: state,
237
- key: createKey()
238
- }));
239
- }
240
-
241
- function getHistoryStateAndUrl(nextLocation, index) {
242
- return [{
243
- usr: nextLocation.state,
244
- key: nextLocation.key,
245
- idx: index
246
- }, createHref(nextLocation)];
247
- }
248
-
249
- function allowTx(action, location, retry) {
250
- return !blockers.length || (blockers.call({
251
- action: action,
252
- location: location,
253
- retry: retry
254
- }), false);
255
- }
256
-
257
- function applyTx(nextAction) {
258
- action = nextAction;
259
-
260
- var _getIndexAndLocation3 = getIndexAndLocation();
261
-
262
- index = _getIndexAndLocation3[0];
263
- location = _getIndexAndLocation3[1];
264
- listeners.call({
265
- action: action,
266
- location: location
267
- });
268
- }
269
-
270
- function push(to, state) {
271
- var nextAction = Action.Push;
272
- var nextLocation = getNextLocation(to, state);
273
-
274
- function retry() {
275
- push(to, state);
75
+ }, /*#__PURE__*/React.createElement("strong", {
76
+ style: {
77
+ fontSize: '1rem'
276
78
  }
277
-
278
- if (allowTx(nextAction, nextLocation, retry)) {
279
- var _getHistoryStateAndUr = getHistoryStateAndUrl(nextLocation, index + 1),
280
- historyState = _getHistoryStateAndUr[0],
281
- url = _getHistoryStateAndUr[1]; // TODO: Support forced reloading
282
- // try...catch because iOS limits us to 100 pushState calls :/
283
-
284
-
285
- try {
286
- globalHistory.pushState(historyState, '', url);
287
- } catch (error) {
288
- // They are going to lose state here, but there is no real
289
- // way to warn them about it since the page will refresh...
290
- window.location.assign(url);
291
- }
292
-
293
- applyTx(nextAction);
79
+ }, "Something went wrong!"), /*#__PURE__*/React.createElement("button", {
80
+ style: {
81
+ appearance: 'none',
82
+ fontSize: '.6em',
83
+ border: '1px solid currentColor',
84
+ padding: '.1rem .2rem',
85
+ fontWeight: 'bold',
86
+ borderRadius: '.25rem'
87
+ },
88
+ onClick: () => setShow(d => !d)
89
+ }, show ? 'Hide Error' : 'Show Error')), /*#__PURE__*/React.createElement("div", {
90
+ style: {
91
+ height: '.25rem'
294
92
  }
295
- }
296
-
297
- function replace(to, state) {
298
- var nextAction = Action.Replace;
299
- var nextLocation = getNextLocation(to, state);
300
-
301
- function retry() {
302
- replace(to, state);
93
+ }), show ? /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("pre", {
94
+ style: {
95
+ fontSize: '.7em',
96
+ border: '1px solid red',
97
+ borderRadius: '.25rem',
98
+ padding: '.3rem',
99
+ color: 'red',
100
+ overflow: 'auto'
303
101
  }
102
+ }, error.message ? /*#__PURE__*/React.createElement("code", null, error.message) : null)) : null);
103
+ }
304
104
 
305
- if (allowTx(nextAction, nextLocation, retry)) {
306
- var _getHistoryStateAndUr2 = getHistoryStateAndUrl(nextLocation, index),
307
- historyState = _getHistoryStateAndUr2[0],
308
- url = _getHistoryStateAndUr2[1]; // TODO: Support forced reloading
309
-
105
+ // export type Expand<T> = T
106
+
107
+ // type Compute<T> = { [K in keyof T]: T[K] } | never
108
+
109
+ // type AllKeys<T> = T extends any ? keyof T : never
110
+
111
+ // export type MergeUnion<T, Keys extends keyof T = keyof T> = Compute<
112
+ // {
113
+ // [K in Keys]: T[Keys]
114
+ // } & {
115
+ // [K in AllKeys<T>]?: T extends any
116
+ // ? K extends keyof T
117
+ // ? T[K]
118
+ // : never
119
+ // : never
120
+ // }
121
+ // >
122
+ // // Sample types to merge
123
+ // type TypeA = {
124
+ // shared: string
125
+ // onlyInA: string
126
+ // nested: {
127
+ // shared: string
128
+ // aProp: string
129
+ // }
130
+ // array: string[]
131
+ // }
132
+ // type TypeB = {
133
+ // shared: number
134
+ // onlyInB: number
135
+ // nested: {
136
+ // shared: number
137
+ // bProp: number
138
+ // }
139
+ // array: number[]
140
+ // }
141
+ // type TypeC = {
142
+ // shared: boolean
143
+ // onlyInC: boolean
144
+ // nested: {
145
+ // shared: boolean
146
+ // cProp: boolean
147
+ // }
148
+ // array: boolean[]
149
+ // }
150
+ // type Test = Expand<Assign<TypeA, TypeB>>
151
+ // // Using DeepMerge to merge TypeA and TypeB
152
+ // type MergedType = Expand<AssignAll<[TypeA, TypeB, TypeC]>>
153
+ //
310
154
 
311
- globalHistory.replaceState(historyState, '', url);
312
- applyTx(nextAction);
313
- }
155
+ const isServer = typeof document === 'undefined';
156
+ function last(arr) {
157
+ return arr[arr.length - 1];
158
+ }
159
+ function isFunction(d) {
160
+ return typeof d === 'function';
161
+ }
162
+ function functionalUpdate(updater, previous) {
163
+ if (isFunction(updater)) {
164
+ return updater(previous);
314
165
  }
166
+ return updater;
167
+ }
168
+ function pick(parent, keys) {
169
+ return keys.reduce((obj, key) => {
170
+ obj[key] = parent[key];
171
+ return obj;
172
+ }, {});
173
+ }
315
174
 
316
- function go(delta) {
317
- globalHistory.go(delta);
175
+ /**
176
+ * This function returns `a` if `b` is deeply equal.
177
+ * If not, it will replace any deeply equal children of `b` with those of `a`.
178
+ * This can be used for structural sharing between immutable JSON values for example.
179
+ * Do not use this with signals
180
+ */
181
+ function replaceEqualDeep(prev, _next) {
182
+ if (prev === _next) {
183
+ return prev;
318
184
  }
319
-
320
- var history = {
321
- get action() {
322
- return action;
323
- },
324
-
325
- get location() {
326
- return location;
327
- },
328
-
329
- createHref: createHref,
330
- push: push,
331
- replace: replace,
332
- go: go,
333
- back: function back() {
334
- go(-1);
335
- },
336
- forward: function forward() {
337
- go(1);
338
- },
339
- listen: function listen(listener) {
340
- return listeners.push(listener);
341
- },
342
- block: function block(blocker) {
343
- var unblock = blockers.push(blocker);
344
-
345
- if (blockers.length === 1) {
346
- window.addEventListener(BeforeUnloadEventType, promptBeforeUnload);
185
+ const next = _next;
186
+ const array = Array.isArray(prev) && Array.isArray(next);
187
+ if (array || isPlainObject(prev) && isPlainObject(next)) {
188
+ const prevSize = array ? prev.length : Object.keys(prev).length;
189
+ const nextItems = array ? next : Object.keys(next);
190
+ const nextSize = nextItems.length;
191
+ const copy = array ? [] : {};
192
+ let equalItems = 0;
193
+ for (let i = 0; i < nextSize; i++) {
194
+ const key = array ? i : nextItems[i];
195
+ copy[key] = replaceEqualDeep(prev[key], next[key]);
196
+ if (copy[key] === prev[key]) {
197
+ equalItems++;
347
198
  }
348
-
349
- return function () {
350
- unblock(); // Remove the beforeunload listener so the document may
351
- // still be salvageable in the pagehide event.
352
- // See https://html.spec.whatwg.org/#unloading-documents
353
-
354
- if (!blockers.length) {
355
- window.removeEventListener(BeforeUnloadEventType, promptBeforeUnload);
356
- }
357
- };
358
199
  }
359
- };
360
- return history;
361
- }
362
- /**
363
- * Hash history stores the location in window.location.hash. This makes it ideal
364
- * for situations where you don't want to send the location to the server for
365
- * some reason, either because you do cannot configure it or the URL space is
366
- * reserved for something else.
367
- *
368
- * @see https://github.com/remix-run/history/tree/main/docs/api-reference.md#createhashhistory
369
- */
370
-
371
- function createHashHistory(options) {
372
- if (options === void 0) {
373
- options = {};
200
+ return prevSize === nextSize && equalItems === prevSize ? prev : copy;
374
201
  }
202
+ return next;
203
+ }
375
204
 
376
- var _options2 = options,
377
- _options2$window = _options2.window,
378
- window = _options2$window === void 0 ? document.defaultView : _options2$window;
379
- var globalHistory = window.history;
380
-
381
- function getIndexAndLocation() {
382
- var _parsePath = parsePath(window.location.hash.substr(1)),
383
- _parsePath$pathname = _parsePath.pathname,
384
- pathname = _parsePath$pathname === void 0 ? '/' : _parsePath$pathname,
385
- _parsePath$search = _parsePath.search,
386
- search = _parsePath$search === void 0 ? '' : _parsePath$search,
387
- _parsePath$hash = _parsePath.hash,
388
- hash = _parsePath$hash === void 0 ? '' : _parsePath$hash;
389
-
390
- var state = globalHistory.state || {};
391
- return [state.idx, readOnly({
392
- pathname: pathname,
393
- search: search,
394
- hash: hash,
395
- state: state.usr || null,
396
- key: state.key || 'default'
397
- })];
205
+ // Copied from: https://github.com/jonschlinkert/is-plain-object
206
+ function isPlainObject(o) {
207
+ if (!hasObjectPrototype(o)) {
208
+ return false;
398
209
  }
399
210
 
400
- var blockedPopTx = null;
401
-
402
- function handlePop() {
403
- if (blockedPopTx) {
404
- blockers.call(blockedPopTx);
405
- blockedPopTx = null;
406
- } else {
407
- var nextAction = Action.Pop;
408
-
409
- var _getIndexAndLocation4 = getIndexAndLocation(),
410
- nextIndex = _getIndexAndLocation4[0],
411
- nextLocation = _getIndexAndLocation4[1];
412
-
413
- if (blockers.length) {
414
- if (nextIndex != null) {
415
- var delta = index - nextIndex;
416
-
417
- if (delta) {
418
- // Revert the POP
419
- blockedPopTx = {
420
- action: nextAction,
421
- location: nextLocation,
422
- retry: function retry() {
423
- go(delta * -1);
424
- }
425
- };
426
- go(delta);
427
- }
428
- } else {
429
- // Trying to POP to a location with no index. We did not create
430
- // this location, so we can't effectively block the navigation.
431
- process.env.NODE_ENV !== "production" ? warning$1(false, // TODO: Write up a doc that explains our blocking strategy in
432
- // detail and link to it here so people can understand better
433
- // what is going on and how to avoid it.
434
- "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;
435
- }
436
- } else {
437
- applyTx(nextAction);
438
- }
439
- }
211
+ // If has modified constructor
212
+ const ctor = o.constructor;
213
+ if (typeof ctor === 'undefined') {
214
+ return true;
440
215
  }
441
216
 
442
- window.addEventListener(PopStateEventType, handlePop); // popstate does not fire on hashchange in IE 11 and old (trident) Edge
443
- // https://developer.mozilla.org/de/docs/Web/API/Window/popstate_event
444
-
445
- window.addEventListener(HashChangeEventType, function () {
446
- var _getIndexAndLocation5 = getIndexAndLocation(),
447
- nextLocation = _getIndexAndLocation5[1]; // Ignore extraneous hashchange events.
448
-
449
-
450
- if (createPath(nextLocation) !== createPath(location)) {
451
- handlePop();
452
- }
453
- });
454
- var action = Action.Pop;
455
-
456
- var _getIndexAndLocation6 = getIndexAndLocation(),
457
- index = _getIndexAndLocation6[0],
458
- location = _getIndexAndLocation6[1];
459
-
460
- var listeners = createEvents();
461
- var blockers = createEvents();
462
-
463
- if (index == null) {
464
- index = 0;
465
- globalHistory.replaceState(_extends$1({}, globalHistory.state, {
466
- idx: index
467
- }), '');
217
+ // If has modified prototype
218
+ const prot = ctor.prototype;
219
+ if (!hasObjectPrototype(prot)) {
220
+ return false;
468
221
  }
469
222
 
470
- function getBaseHref() {
471
- var base = document.querySelector('base');
472
- var href = '';
473
-
474
- if (base && base.getAttribute('href')) {
475
- var url = window.location.href;
476
- var hashIndex = url.indexOf('#');
477
- href = hashIndex === -1 ? url : url.slice(0, hashIndex);
478
- }
479
-
480
- return href;
223
+ // If constructor does not have an Object-specific method
224
+ if (!prot.hasOwnProperty('isPrototypeOf')) {
225
+ return false;
481
226
  }
482
227
 
483
- function createHref(to) {
484
- return getBaseHref() + '#' + (typeof to === 'string' ? to : createPath(to));
228
+ // Most likely a plain Object
229
+ return true;
230
+ }
231
+ function hasObjectPrototype(o) {
232
+ return Object.prototype.toString.call(o) === '[object Object]';
233
+ }
234
+ function deepEqual(a, b, partial = false) {
235
+ if (a === b) {
236
+ return true;
485
237
  }
486
-
487
- function getNextLocation(to, state) {
488
- if (state === void 0) {
489
- state = null;
238
+ if (typeof a !== typeof b) {
239
+ return false;
240
+ }
241
+ if (isPlainObject(a) && isPlainObject(b)) {
242
+ const aKeys = Object.keys(a);
243
+ const bKeys = Object.keys(b);
244
+ if (!partial && aKeys.length !== bKeys.length) {
245
+ return false;
490
246
  }
491
-
492
- return readOnly(_extends$1({
493
- pathname: location.pathname,
494
- hash: '',
495
- search: ''
496
- }, typeof to === 'string' ? parsePath(to) : to, {
497
- state: state,
498
- key: createKey()
499
- }));
247
+ return !bKeys.some(key => !(key in a) || !deepEqual(a[key], b[key], partial));
500
248
  }
501
-
502
- function getHistoryStateAndUrl(nextLocation, index) {
503
- return [{
504
- usr: nextLocation.state,
505
- key: nextLocation.key,
506
- idx: index
507
- }, createHref(nextLocation)];
249
+ if (Array.isArray(a) && Array.isArray(b)) {
250
+ return !a.some((item, index) => !deepEqual(item, b[index], partial));
508
251
  }
509
-
510
- function allowTx(action, location, retry) {
511
- return !blockers.length || (blockers.call({
512
- action: action,
513
- location: location,
514
- retry: retry
515
- }), false);
252
+ return false;
253
+ }
254
+ function useStableCallback(fn) {
255
+ const fnRef = React.useRef(fn);
256
+ fnRef.current = fn;
257
+ const ref = React.useRef((...args) => fnRef.current(...args));
258
+ return ref.current;
259
+ }
260
+ function shallow(objA, objB) {
261
+ if (Object.is(objA, objB)) {
262
+ return true;
516
263
  }
517
-
518
- function applyTx(nextAction) {
519
- action = nextAction;
520
-
521
- var _getIndexAndLocation7 = getIndexAndLocation();
522
-
523
- index = _getIndexAndLocation7[0];
524
- location = _getIndexAndLocation7[1];
525
- listeners.call({
526
- action: action,
527
- location: location
528
- });
264
+ if (typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null) {
265
+ return false;
529
266
  }
530
-
531
- function push(to, state) {
532
- var nextAction = Action.Push;
533
- var nextLocation = getNextLocation(to, state);
534
-
535
- function retry() {
536
- push(to, state);
537
- }
538
-
539
- 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;
540
-
541
- if (allowTx(nextAction, nextLocation, retry)) {
542
- var _getHistoryStateAndUr3 = getHistoryStateAndUrl(nextLocation, index + 1),
543
- historyState = _getHistoryStateAndUr3[0],
544
- url = _getHistoryStateAndUr3[1]; // TODO: Support forced reloading
545
- // try...catch because iOS limits us to 100 pushState calls :/
546
-
547
-
548
- try {
549
- globalHistory.pushState(historyState, '', url);
550
- } catch (error) {
551
- // They are going to lose state here, but there is no real
552
- // way to warn them about it since the page will refresh...
553
- window.location.assign(url);
554
- }
555
-
556
- applyTx(nextAction);
557
- }
267
+ const keysA = Object.keys(objA);
268
+ if (keysA.length !== Object.keys(objB).length) {
269
+ return false;
558
270
  }
559
-
560
- function replace(to, state) {
561
- var nextAction = Action.Replace;
562
- var nextLocation = getNextLocation(to, state);
563
-
564
- function retry() {
565
- replace(to, state);
566
- }
567
-
568
- 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;
569
-
570
- if (allowTx(nextAction, nextLocation, retry)) {
571
- var _getHistoryStateAndUr4 = getHistoryStateAndUrl(nextLocation, index),
572
- historyState = _getHistoryStateAndUr4[0],
573
- url = _getHistoryStateAndUr4[1]; // TODO: Support forced reloading
574
-
575
-
576
- globalHistory.replaceState(historyState, '', url);
577
- applyTx(nextAction);
271
+ for (let i = 0; i < keysA.length; i++) {
272
+ if (!Object.prototype.hasOwnProperty.call(objB, keysA[i]) || !Object.is(objA[keysA[i]], objB[keysA[i]])) {
273
+ return false;
578
274
  }
579
275
  }
580
-
581
- function go(delta) {
582
- globalHistory.go(delta);
583
- }
584
-
585
- var history = {
586
- get action() {
587
- return action;
588
- },
589
-
590
- get location() {
591
- return location;
592
- },
593
-
594
- createHref: createHref,
595
- push: push,
596
- replace: replace,
597
- go: go,
598
- back: function back() {
599
- go(-1);
600
- },
601
- forward: function forward() {
602
- go(1);
603
- },
604
- listen: function listen(listener) {
605
- return listeners.push(listener);
606
- },
607
- block: function block(blocker) {
608
- var unblock = blockers.push(blocker);
609
-
610
- if (blockers.length === 1) {
611
- window.addEventListener(BeforeUnloadEventType, promptBeforeUnload);
612
- }
613
-
614
- return function () {
615
- unblock(); // Remove the beforeunload listener so the document may
616
- // still be salvageable in the pagehide event.
617
- // See https://html.spec.whatwg.org/#unloading-documents
618
-
619
- if (!blockers.length) {
620
- window.removeEventListener(BeforeUnloadEventType, promptBeforeUnload);
621
- }
622
- };
623
- }
624
- };
625
- return history;
276
+ return true;
626
277
  }
627
- /**
628
- * Memory history stores the current location in memory. It is designed for use
629
- * in stateful non-browser environments like tests and React Native.
630
- *
631
- * @see https://github.com/remix-run/history/tree/main/docs/api-reference.md#creatememoryhistory
632
- */
633
-
634
- function createMemoryHistory(options) {
635
- if (options === void 0) {
636
- options = {};
637
- }
638
-
639
- var _options3 = options,
640
- _options3$initialEntr = _options3.initialEntries,
641
- initialEntries = _options3$initialEntr === void 0 ? ['/'] : _options3$initialEntr,
642
- initialIndex = _options3.initialIndex;
643
- var entries = initialEntries.map(function (entry) {
644
- var location = readOnly(_extends$1({
645
- pathname: '/',
646
- search: '',
647
- hash: '',
648
- state: null,
649
- key: createKey()
650
- }, typeof entry === 'string' ? parsePath(entry) : entry));
651
- 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;
652
- return location;
278
+ function useRouteContext(opts) {
279
+ return useMatch({
280
+ ...opts,
281
+ select: match => opts?.select ? opts.select(match.context) : match.context
653
282
  });
654
- var index = clamp(initialIndex == null ? entries.length - 1 : initialIndex, 0, entries.length - 1);
655
- var action = Action.Pop;
656
- var location = entries[index];
657
- var listeners = createEvents();
658
- var blockers = createEvents();
659
-
660
- function createHref(to) {
661
- return typeof to === 'string' ? to : createPath(to);
662
- }
663
-
664
- function getNextLocation(to, state) {
665
- if (state === void 0) {
666
- state = null;
667
- }
668
-
669
- return readOnly(_extends$1({
670
- pathname: location.pathname,
671
- search: '',
672
- hash: ''
673
- }, typeof to === 'string' ? parsePath(to) : to, {
674
- state: state,
675
- key: createKey()
676
- }));
677
- }
678
-
679
- function allowTx(action, location, retry) {
680
- return !blockers.length || (blockers.call({
681
- action: action,
682
- location: location,
683
- retry: retry
684
- }), false);
685
- }
686
-
687
- function applyTx(nextAction, nextLocation) {
688
- action = nextAction;
689
- location = nextLocation;
690
- listeners.call({
691
- action: action,
692
- location: location
693
- });
694
- }
695
-
696
- function push(to, state) {
697
- var nextAction = Action.Push;
698
- var nextLocation = getNextLocation(to, state);
699
-
700
- function retry() {
701
- push(to, state);
702
- }
703
-
704
- 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;
705
-
706
- if (allowTx(nextAction, nextLocation, retry)) {
707
- index += 1;
708
- entries.splice(index, entries.length, nextLocation);
709
- applyTx(nextAction, nextLocation);
710
- }
711
- }
712
-
713
- function replace(to, state) {
714
- var nextAction = Action.Replace;
715
- var nextLocation = getNextLocation(to, state);
716
-
717
- function retry() {
718
- replace(to, state);
719
- }
720
-
721
- 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;
722
-
723
- if (allowTx(nextAction, nextLocation, retry)) {
724
- entries[index] = nextLocation;
725
- applyTx(nextAction, nextLocation);
726
- }
727
- }
728
-
729
- function go(delta) {
730
- var nextIndex = clamp(index + delta, 0, entries.length - 1);
731
- var nextAction = Action.Pop;
732
- var nextLocation = entries[nextIndex];
733
-
734
- function retry() {
735
- go(delta);
736
- }
737
-
738
- if (allowTx(nextAction, nextLocation, retry)) {
739
- index = nextIndex;
740
- applyTx(nextAction, nextLocation);
741
- }
742
- }
743
-
744
- var history = {
745
- get index() {
746
- return index;
747
- },
748
-
749
- get action() {
750
- return action;
751
- },
752
-
753
- get location() {
754
- return location;
755
- },
756
-
757
- createHref: createHref,
758
- push: push,
759
- replace: replace,
760
- go: go,
761
- back: function back() {
762
- go(-1);
763
- },
764
- forward: function forward() {
765
- go(1);
766
- },
767
- listen: function listen(listener) {
768
- return listeners.push(listener);
769
- },
770
- block: function block(blocker) {
771
- return blockers.push(blocker);
772
- }
773
- };
774
- return history;
775
- } ////////////////////////////////////////////////////////////////////////////////
776
- // UTILS
777
- ////////////////////////////////////////////////////////////////////////////////
778
-
779
- function clamp(n, lowerBound, upperBound) {
780
- return Math.min(Math.max(n, lowerBound), upperBound);
781
- }
782
-
783
- function promptBeforeUnload(event) {
784
- // Cancel the event.
785
- event.preventDefault(); // Chrome (and legacy IE) requires returnValue to be set.
786
-
787
- event.returnValue = '';
788
- }
789
-
790
- function createEvents() {
791
- var handlers = [];
792
- return {
793
- get length() {
794
- return handlers.length;
795
- },
796
-
797
- push: function push(fn) {
798
- handlers.push(fn);
799
- return function () {
800
- handlers = handlers.filter(function (handler) {
801
- return handler !== fn;
802
- });
803
- };
804
- },
805
- call: function call(arg) {
806
- handlers.forEach(function (fn) {
807
- return fn && fn(arg);
808
- });
809
- }
810
- };
811
- }
812
-
813
- function createKey() {
814
- return Math.random().toString(36).substr(2, 8);
815
- }
816
- /**
817
- * Creates a string URL path from the given pathname, search, and hash components.
818
- *
819
- * @see https://github.com/remix-run/history/tree/main/docs/api-reference.md#createpath
820
- */
821
-
822
-
823
- function createPath(_ref) {
824
- var _ref$pathname = _ref.pathname,
825
- pathname = _ref$pathname === void 0 ? '/' : _ref$pathname,
826
- _ref$search = _ref.search,
827
- search = _ref$search === void 0 ? '' : _ref$search,
828
- _ref$hash = _ref.hash,
829
- hash = _ref$hash === void 0 ? '' : _ref$hash;
830
- if (search && search !== '?') pathname += search.charAt(0) === '?' ? search : '?' + search;
831
- if (hash && hash !== '#') pathname += hash.charAt(0) === '#' ? hash : '#' + hash;
832
- return pathname;
833
- }
834
- /**
835
- * Parses a string URL path into its separate pathname, search, and hash components.
836
- *
837
- * @see https://github.com/remix-run/history/tree/main/docs/api-reference.md#parsepath
838
- */
839
-
840
- function parsePath(path) {
841
- var parsedPath = {};
842
-
843
- if (path) {
844
- var hashIndex = path.indexOf('#');
845
-
846
- if (hashIndex >= 0) {
847
- parsedPath.hash = path.substr(hashIndex);
848
- path = path.substr(0, hashIndex);
849
- }
850
-
851
- var searchIndex = path.indexOf('?');
852
-
853
- if (searchIndex >= 0) {
854
- parsedPath.search = path.substr(searchIndex);
855
- path = path.substr(0, searchIndex);
856
- }
857
-
858
- if (path) {
859
- parsedPath.pathname = path;
860
- }
861
- }
862
-
863
- return parsedPath;
864
- }
865
-
866
- var isProduction = process.env.NODE_ENV === 'production';
867
- var prefix = 'Invariant failed';
868
- function invariant(condition, message) {
869
- if (condition) {
870
- return;
871
- }
872
- if (isProduction) {
873
- throw new Error(prefix);
874
- }
875
- var provided = typeof message === 'function' ? message() : message;
876
- var value = provided ? "".concat(prefix, ": ").concat(provided) : prefix;
877
- throw new Error(value);
878
- }
879
-
880
- // type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
881
- // k: infer I,
882
- // ) => any
883
- // ? I
884
- // : never
885
-
886
- /**
887
- * This function returns `a` if `b` is deeply equal.
888
- * If not, it will replace any deeply equal children of `b` with those of `a`.
889
- * This can be used for structural sharing between JSON values for example.
890
- */
891
- function replaceEqualDeep(prev, next) {
892
- if (prev === next) {
893
- return prev;
894
- }
895
-
896
- const array = Array.isArray(prev) && Array.isArray(next);
897
-
898
- if (array || isPlainObject(prev) && isPlainObject(next)) {
899
- const aSize = array ? prev.length : Object.keys(prev).length;
900
- const bItems = array ? next : Object.keys(next);
901
- const bSize = bItems.length;
902
- const copy = array ? [] : {};
903
- let equalItems = 0;
904
-
905
- for (let i = 0; i < bSize; i++) {
906
- const key = array ? i : bItems[i];
907
- copy[key] = replaceEqualDeep(prev[key], next[key]);
908
-
909
- if (copy[key] === prev[key]) {
910
- equalItems++;
911
- }
912
- }
913
-
914
- return aSize === bSize && equalItems === aSize ? prev : copy;
915
- }
916
-
917
- return next;
918
- } // Copied from: https://github.com/jonschlinkert/is-plain-object
919
-
920
- function isPlainObject(o) {
921
- if (!hasObjectPrototype(o)) {
922
- return false;
923
- } // If has modified constructor
924
-
925
-
926
- const ctor = o.constructor;
927
-
928
- if (typeof ctor === 'undefined') {
929
- return true;
930
- } // If has modified prototype
931
-
932
-
933
- const prot = ctor.prototype;
934
-
935
- if (!hasObjectPrototype(prot)) {
936
- return false;
937
- } // If constructor does not have an Object-specific method
938
-
939
-
940
- if (!prot.hasOwnProperty('isPrototypeOf')) {
941
- return false;
942
- } // Most likely a plain Object
943
-
944
-
945
- return true;
946
283
  }
947
-
948
- function hasObjectPrototype(o) {
949
- return Object.prototype.toString.call(o) === '[object Object]';
950
- }
951
-
952
- function last(arr) {
953
- return arr[arr.length - 1];
954
- }
955
- function warning(cond, message) {
956
- if (cond) {
957
- if (typeof console !== 'undefined') console.warn(message);
958
-
959
- try {
960
- throw new Error(message);
961
- } catch (_unused) {}
962
- }
963
-
964
- return true;
965
- }
966
-
967
- function isFunction(d) {
968
- return typeof d === 'function';
969
- }
970
-
971
- function functionalUpdate(updater, previous) {
972
- if (isFunction(updater)) {
973
- return updater(previous);
974
- }
975
-
976
- return updater;
977
- }
978
- function pick(parent, keys) {
979
- return keys.reduce((obj, key) => {
980
- obj[key] = parent[key];
981
- return obj;
982
- }, {});
284
+ const useLayoutEffect$1 = typeof window !== 'undefined' ? React.useLayoutEffect : React.useEffect;
285
+ function escapeJSON(jsonString) {
286
+ return jsonString.replace(/\\/g, '\\\\') // Escape backslashes
287
+ .replace(/'/g, "\\'") // Escape single quotes
288
+ .replace(/"/g, '\\"'); // Escape double quotes
983
289
  }
984
290
 
985
291
  function joinPaths(paths) {
@@ -999,8 +305,8 @@ function trimPath(path) {
999
305
  return trimPathRight(trimPathLeft(path));
1000
306
  }
1001
307
  function resolvePath(basepath, base, to) {
1002
- base = base.replace(new RegExp("^" + basepath), '/');
1003
- to = to.replace(new RegExp("^" + basepath), '/');
308
+ base = base.replace(new RegExp(`^${basepath}`), '/');
309
+ to = to.replace(new RegExp(`^${basepath}`), '/');
1004
310
  let baseSegments = parsePathname(base);
1005
311
  const toSegments = parsePathname(to);
1006
312
  toSegments.forEach((toSegment, index) => {
@@ -1013,13 +319,10 @@ function resolvePath(basepath, base, to) {
1013
319
  baseSegments.push(toSegment);
1014
320
  } else ;
1015
321
  } else if (toSegment.value === '..') {
1016
- var _last;
1017
-
1018
322
  // Extra trailing slash? pop it off
1019
- if (baseSegments.length > 1 && ((_last = last(baseSegments)) == null ? void 0 : _last.value) === '/') {
323
+ if (baseSegments.length > 1 && last(baseSegments)?.value === '/') {
1020
324
  baseSegments.pop();
1021
325
  }
1022
-
1023
326
  baseSegments.pop();
1024
327
  } else if (toSegment.value === '.') {
1025
328
  return;
@@ -1034,10 +337,8 @@ function parsePathname(pathname) {
1034
337
  if (!pathname) {
1035
338
  return [];
1036
339
  }
1037
-
1038
340
  pathname = cleanPath(pathname);
1039
341
  const segments = [];
1040
-
1041
342
  if (pathname.slice(0, 1) === '/') {
1042
343
  pathname = pathname.substring(1);
1043
344
  segments.push({
@@ -1045,34 +346,30 @@ function parsePathname(pathname) {
1045
346
  value: '/'
1046
347
  });
1047
348
  }
1048
-
1049
349
  if (!pathname) {
1050
350
  return segments;
1051
- } // Remove empty segments and '.' segments
1052
-
351
+ }
1053
352
 
353
+ // Remove empty segments and '.' segments
1054
354
  const split = pathname.split('/').filter(Boolean);
1055
355
  segments.push(...split.map(part => {
1056
- if (part.startsWith('*')) {
356
+ if (part === '$' || part === '*') {
1057
357
  return {
1058
358
  type: 'wildcard',
1059
359
  value: part
1060
360
  };
1061
361
  }
1062
-
1063
- if (part.charAt(0) === ':') {
362
+ if (part.charAt(0) === '$') {
1064
363
  return {
1065
364
  type: 'param',
1066
365
  value: part
1067
366
  };
1068
367
  }
1069
-
1070
368
  return {
1071
369
  type: 'pathname',
1072
370
  value: part
1073
371
  };
1074
372
  }));
1075
-
1076
373
  if (pathname.slice(-1) === '/') {
1077
374
  pathname = pathname.substring(1);
1078
375
  segments.push({
@@ -1080,66 +377,70 @@ function parsePathname(pathname) {
1080
377
  value: '/'
1081
378
  });
1082
379
  }
1083
-
1084
380
  return segments;
1085
381
  }
1086
- function interpolatePath(path, params, leaveWildcard) {
382
+ function interpolatePath(path, params, leaveWildcards = false) {
1087
383
  const interpolatedPathSegments = parsePathname(path);
1088
384
  return joinPaths(interpolatedPathSegments.map(segment => {
1089
- if (segment.value === '*' && !leaveWildcard) {
1090
- return '';
385
+ if (segment.type === 'wildcard') {
386
+ const value = params[segment.value];
387
+ if (leaveWildcards) return `${segment.value}${value ?? ''}`;
388
+ return value;
1091
389
  }
1092
-
1093
390
  if (segment.type === 'param') {
1094
- var _segment$value$substr;
1095
-
1096
- return (_segment$value$substr = params[segment.value.substring(1)]) != null ? _segment$value$substr : '';
391
+ return params[segment.value.substring(1)] ?? '';
1097
392
  }
1098
-
1099
393
  return segment.value;
1100
394
  }));
1101
395
  }
1102
- function matchPathname(currentPathname, matchLocation) {
1103
- const pathParams = matchByPath(currentPathname, matchLocation); // const searchMatched = matchBySearch(currentLocation.search, matchLocation)
396
+ function matchPathname(basepath, currentPathname, matchLocation) {
397
+ const pathParams = matchByPath(basepath, currentPathname, matchLocation);
398
+ // const searchMatched = matchBySearch(location.search, matchLocation)
1104
399
 
1105
400
  if (matchLocation.to && !pathParams) {
1106
401
  return;
1107
- } // if (matchLocation.search && !searchMatched) {
1108
- // return
1109
- // }
1110
-
1111
-
1112
- return pathParams != null ? pathParams : {};
402
+ }
403
+ return pathParams ?? {};
1113
404
  }
1114
- function matchByPath(from, matchLocation) {
1115
- var _matchLocation$to;
1116
-
405
+ function matchByPath(basepath, from, matchLocation) {
406
+ // Remove the base path from the pathname
407
+ from = basepath != '/' ? from.substring(basepath.length) : from;
408
+ // Default to to $ (wildcard)
409
+ const to = `${matchLocation.to ?? '$'}`;
410
+ // Parse the from and to
1117
411
  const baseSegments = parsePathname(from);
1118
- const routeSegments = parsePathname("" + ((_matchLocation$to = matchLocation.to) != null ? _matchLocation$to : '*'));
412
+ const routeSegments = parsePathname(to);
413
+ if (!from.startsWith('/')) {
414
+ baseSegments.unshift({
415
+ type: 'pathname',
416
+ value: '/'
417
+ });
418
+ }
419
+ if (!to.startsWith('/')) {
420
+ routeSegments.unshift({
421
+ type: 'pathname',
422
+ value: '/'
423
+ });
424
+ }
1119
425
  const params = {};
1120
-
1121
426
  let isMatch = (() => {
1122
427
  for (let i = 0; i < Math.max(baseSegments.length, routeSegments.length); i++) {
1123
428
  const baseSegment = baseSegments[i];
1124
429
  const routeSegment = routeSegments[i];
1125
- const isLastRouteSegment = i === routeSegments.length - 1;
1126
- const isLastBaseSegment = i === baseSegments.length - 1;
1127
-
430
+ const isLastBaseSegment = i >= baseSegments.length - 1;
431
+ const isLastRouteSegment = i >= routeSegments.length - 1;
1128
432
  if (routeSegment) {
1129
433
  if (routeSegment.type === 'wildcard') {
1130
- if (baseSegment != null && baseSegment.value) {
434
+ if (baseSegment?.value) {
1131
435
  params['*'] = joinPaths(baseSegments.slice(i).map(d => d.value));
1132
436
  return true;
1133
437
  }
1134
-
1135
438
  return false;
1136
439
  }
1137
-
1138
440
  if (routeSegment.type === 'pathname') {
1139
- if (routeSegment.value === '/' && !(baseSegment != null && baseSegment.value)) {
441
+ if (routeSegment.value === '/' && !baseSegment?.value) {
1140
442
  return true;
1141
443
  }
1142
-
1143
444
  if (baseSegment) {
1144
445
  if (matchLocation.caseSensitive) {
1145
446
  if (routeSegment.value !== baseSegment.value) {
@@ -1150,1754 +451,2025 @@ function matchByPath(from, matchLocation) {
1150
451
  }
1151
452
  }
1152
453
  }
1153
-
1154
454
  if (!baseSegment) {
1155
455
  return false;
1156
456
  }
1157
-
1158
457
  if (routeSegment.type === 'param') {
1159
- if ((baseSegment == null ? void 0 : baseSegment.value) === '/') {
458
+ if (baseSegment?.value === '/') {
1160
459
  return false;
1161
460
  }
1162
-
1163
- if (!baseSegment.value.startsWith(':')) {
461
+ if (baseSegment.value.charAt(0) !== '$') {
1164
462
  params[routeSegment.value.substring(1)] = baseSegment.value;
1165
463
  }
1166
464
  }
1167
465
  }
1168
-
1169
- if (isLastRouteSegment && !isLastBaseSegment) {
466
+ if (!isLastBaseSegment && isLastRouteSegment) {
1170
467
  return !!matchLocation.fuzzy;
1171
468
  }
1172
469
  }
1173
-
1174
470
  return true;
1175
471
  })();
1176
-
1177
472
  return isMatch ? params : undefined;
1178
473
  }
1179
474
 
1180
- // @ts-nocheck
1181
- // 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.
1182
- function encode(obj, pfx) {
1183
- var k,
1184
- i,
1185
- tmp,
1186
- str = '';
1187
-
1188
- for (k in obj) {
1189
- if ((tmp = obj[k]) !== void 0) {
1190
- if (Array.isArray(tmp)) {
1191
- for (i = 0; i < tmp.length; i++) {
1192
- str && (str += '&');
1193
- str += encodeURIComponent(k) + '=' + encodeURIComponent(tmp[i]);
1194
- }
1195
- } else {
1196
- str && (str += '&');
1197
- str += encodeURIComponent(k) + '=' + encodeURIComponent(tmp);
1198
- }
475
+ function useParams(opts) {
476
+ return useRouterState({
477
+ select: state => {
478
+ const params = last(state.matches)?.params;
479
+ return opts?.select ? opts.select(params) : params;
1199
480
  }
1200
- }
1201
-
1202
- return (pfx || '') + str;
481
+ });
1203
482
  }
1204
483
 
1205
- function toValue(mix) {
1206
- if (!mix) return '';
1207
- var str = decodeURIComponent(mix);
1208
- if (str === 'false') return false;
1209
- if (str === 'true') return true;
1210
- if (str.charAt(0) === '0') return str;
1211
- return +str * 0 === 0 ? +str : str;
484
+ function useSearch(opts) {
485
+ return useMatch({
486
+ ...opts,
487
+ select: match => {
488
+ return opts?.select ? opts.select(match.search) : match.search;
489
+ }
490
+ });
1212
491
  }
1213
492
 
1214
- function decode(str) {
1215
- var tmp,
1216
- k,
1217
- out = {},
1218
- arr = str.split('&');
493
+ const rootRouteId = '__root__';
1219
494
 
1220
- while (tmp = arr.shift()) {
1221
- tmp = tmp.split('=');
1222
- k = tmp.shift();
495
+ // The parse type here allows a zod schema to be passed directly to the validator
1223
496
 
1224
- if (out[k] !== void 0) {
1225
- out[k] = [].concat(out[k], toValue(tmp.shift()));
1226
- } else {
1227
- out[k] = toValue(tmp.shift());
1228
- }
1229
- }
497
+ class Route {
498
+ // Set up in this.init()
1230
499
 
1231
- return out;
1232
- }
500
+ // customId!: TCustomId
1233
501
 
1234
- function _extends() {
1235
- _extends = Object.assign ? Object.assign.bind() : function (target) {
1236
- for (var i = 1; i < arguments.length; i++) {
1237
- var source = arguments[i];
502
+ // Optional
1238
503
 
1239
- for (var key in source) {
1240
- if (Object.prototype.hasOwnProperty.call(source, key)) {
1241
- target[key] = source[key];
1242
- }
1243
- }
504
+ constructor(options) {
505
+ this.options = options || {};
506
+ this.isRoot = !options?.getParentRoute;
507
+ Route.__onInit(this);
508
+ }
509
+ init = opts => {
510
+ this.originalIndex = opts.originalIndex;
511
+ const options = this.options;
512
+ const isRoot = !options?.path && !options?.id;
513
+ this.parentRoute = this.options?.getParentRoute?.();
514
+ if (isRoot) {
515
+ this.path = rootRouteId;
516
+ } else {
517
+ invariant(this.parentRoute, `Child Route instances must pass a 'getParentRoute: () => ParentRoute' option that returns a Route instance.`);
1244
518
  }
519
+ let path = isRoot ? rootRouteId : options.path;
1245
520
 
1246
- return target;
1247
- };
1248
- return _extends.apply(this, arguments);
1249
- }
521
+ // If the path is anything other than an index path, trim it up
522
+ if (path && path !== '/') {
523
+ path = trimPath(path);
524
+ }
525
+ const customId = options?.id || path;
1250
526
 
1251
- function createRoute(routeConfig, options, parent, router) {
1252
- const {
1253
- id,
1254
- routeId,
1255
- path: routePath,
1256
- fullPath
1257
- } = routeConfig;
1258
-
1259
- const action = router.state.actions[id] || (() => {
1260
- router.state.actions[id] = {
1261
- submissions: [],
1262
- submit: async (submission, actionOpts) => {
1263
- var _actionOpts$invalidat;
1264
-
1265
- if (!route) {
1266
- return;
1267
- }
1268
-
1269
- const invalidate = (_actionOpts$invalidat = actionOpts == null ? void 0 : actionOpts.invalidate) != null ? _actionOpts$invalidat : true;
1270
-
1271
- if (!(actionOpts != null && actionOpts.multi)) {
1272
- action.submissions = action.submissions.filter(d => d.isMulti);
1273
- }
1274
-
1275
- const actionState = {
1276
- submittedAt: Date.now(),
1277
- status: 'pending',
1278
- submission,
1279
- isMulti: !!(actionOpts != null && actionOpts.multi)
1280
- };
1281
- action.current = actionState;
1282
- action.latest = actionState;
1283
- action.submissions.push(actionState);
1284
- router.notify();
1285
-
1286
- try {
1287
- const res = await (route.options.action == null ? void 0 : route.options.action(submission));
1288
- actionState.data = res;
1289
-
1290
- if (invalidate) {
1291
- router.invalidateRoute({
1292
- to: '.',
1293
- fromCurrent: true
1294
- });
1295
- await router.reload();
1296
- }
1297
-
1298
- actionState.status = 'success';
1299
- return res;
1300
- } catch (err) {
1301
- console.error(err);
1302
- actionState.error = err;
1303
- actionState.status = 'error';
1304
- } finally {
1305
- router.notify();
1306
- }
1307
- }
1308
- };
1309
- return router.state.actions[id];
1310
- })();
1311
-
1312
- const loader = router.state.loaders[id] || (() => {
1313
- router.state.loaders[id] = {
1314
- pending: [],
1315
- fetch: async loaderContext => {
1316
- if (!route) {
1317
- return;
1318
- }
1319
-
1320
- const loaderState = {
1321
- loadedAt: Date.now(),
1322
- loaderContext
1323
- };
1324
- loader.current = loaderState;
1325
- loader.latest = loaderState;
1326
- loader.pending.push(loaderState); // router.state = {
1327
- // ...router.state,
1328
- // currentAction: loaderState,
1329
- // latestAction: loaderState,
1330
- // }
1331
-
1332
- router.notify();
1333
-
1334
- try {
1335
- return await (route.options.loader == null ? void 0 : route.options.loader(loaderContext));
1336
- } finally {
1337
- loader.pending = loader.pending.filter(d => d !== loaderState); // router.removeActionQueue.push({ loader, loaderState })
1338
-
1339
- router.notify();
1340
- }
1341
- }
1342
- };
1343
- return router.state.loaders[id];
1344
- })();
1345
-
1346
- let route = {
1347
- routeId: id,
1348
- routeRouteId: routeId,
1349
- routePath,
1350
- fullPath,
1351
- options,
1352
- router,
1353
- childRoutes: undefined,
1354
- parentRoute: parent,
1355
- action,
1356
- loader: loader,
1357
- buildLink: options => {
1358
- return router.buildLink(_extends({}, options, {
1359
- from: fullPath
1360
- }));
1361
- },
1362
- navigate: options => {
1363
- return router.navigate(_extends({}, options, {
1364
- from: fullPath
1365
- }));
1366
- },
1367
- matchRoute: (matchLocation, opts) => {
1368
- return router.matchRoute(_extends({}, matchLocation, {
1369
- from: fullPath
1370
- }), opts);
527
+ // Strip the parentId prefix from the first level of children
528
+ let id = isRoot ? rootRouteId : joinPaths([this.parentRoute.id === rootRouteId ? '' : this.parentRoute.id, customId]);
529
+ if (path === rootRouteId) {
530
+ path = '/';
1371
531
  }
532
+ if (id !== rootRouteId) {
533
+ id = joinPaths(['/', id]);
534
+ }
535
+ const fullPath = id === rootRouteId ? '/' : joinPaths([this.parentRoute.fullPath, path]);
536
+ this.path = path;
537
+ this.id = id;
538
+ // this.customId = customId as TCustomId
539
+ this.fullPath = fullPath;
540
+ this.to = fullPath;
541
+ };
542
+ addChildren = children => {
543
+ this.children = children;
544
+ return this;
545
+ };
546
+ update = options => {
547
+ Object.assign(this.options, options);
548
+ return this;
549
+ };
550
+ static __onInit = route => {
551
+ // This is a dummy static method that should get
552
+ // replaced by a framework specific implementation if necessary
553
+ };
554
+ useMatch = opts => {
555
+ return useMatch({
556
+ ...opts,
557
+ from: this.id
558
+ });
559
+ };
560
+ useRouteContext = opts => {
561
+ return useMatch({
562
+ ...opts,
563
+ from: this.id,
564
+ select: d => opts?.select ? opts.select(d.context) : d.context
565
+ });
566
+ };
567
+ useSearch = opts => {
568
+ return useSearch({
569
+ ...opts,
570
+ from: this.id
571
+ });
572
+ };
573
+ useParams = opts => {
574
+ return useParams({
575
+ ...opts,
576
+ from: this.id
577
+ });
578
+ };
579
+ useLoaderData = opts => {
580
+ return useLoaderData({
581
+ ...opts,
582
+ from: this.id
583
+ });
1372
584
  };
1373
- router.options.createRoute == null ? void 0 : router.options.createRoute({
1374
- router,
1375
- route
1376
- });
1377
- return route;
1378
585
  }
1379
-
1380
- const rootRouteId = '__root__';
1381
- const createRouteConfig = function createRouteConfig(options, children, isRoot, parentId, parentPath) {
1382
- if (options === void 0) {
1383
- options = {};
1384
- }
1385
-
1386
- if (isRoot === void 0) {
1387
- isRoot = true;
1388
- }
1389
-
1390
- if (isRoot) {
1391
- options.path = rootRouteId;
1392
- } // Strip the root from parentIds
1393
-
1394
-
1395
- if (parentId === rootRouteId) {
1396
- parentId = '';
1397
- }
1398
-
1399
- let path = isRoot ? rootRouteId : options.path; // If the path is anything other than an index path, trim it up
1400
-
1401
- if (path && path !== '/') {
1402
- path = trimPath(path);
586
+ function rootRouteWithContext() {
587
+ return options => {
588
+ return new RootRoute(options);
589
+ };
590
+ }
591
+ class RootRoute extends Route {
592
+ constructor(options) {
593
+ super(options);
1403
594
  }
595
+ }
596
+ function createRouteMask(opts) {
597
+ return opts;
598
+ }
1404
599
 
1405
- const routeId = path || options.id;
1406
- let id = joinPaths([parentId, routeId]);
600
+ //
1407
601
 
1408
- if (path === rootRouteId) {
1409
- path = '/';
602
+ function Matches() {
603
+ const {
604
+ routesById,
605
+ state
606
+ } = useRouter();
607
+ const {
608
+ matches
609
+ } = state;
610
+ const locationKey = useRouterState().location.state.key;
611
+ const route = routesById[rootRouteId];
612
+ const errorComponent = React.useCallback(props => {
613
+ return /*#__PURE__*/React.createElement(ErrorComponent, {
614
+ ...props,
615
+ useMatch: route.useMatch,
616
+ useRouteContext: route.useRouteContext,
617
+ useSearch: route.useSearch,
618
+ useParams: route.useParams
619
+ });
620
+ }, [route]);
621
+ return /*#__PURE__*/React.createElement(matchesContext.Provider, {
622
+ value: matches
623
+ }, /*#__PURE__*/React.createElement(CatchBoundary, {
624
+ resetKey: locationKey,
625
+ errorComponent: errorComponent,
626
+ onCatch: () => {
627
+ warning(false, `Error in router! Consider setting an 'errorComponent' in your RootRoute! 👍`);
628
+ }
629
+ }, matches.length ? /*#__PURE__*/React.createElement(Match, {
630
+ matches: matches
631
+ }) : null));
632
+ }
633
+ const defaultPending = () => null;
634
+ function SafeFragment(props) {
635
+ return /*#__PURE__*/React.createElement(React.Fragment, null, props.children);
636
+ }
637
+ function Match({
638
+ matches
639
+ }) {
640
+ const {
641
+ options,
642
+ routesById
643
+ } = useRouter();
644
+ const match = matches[0];
645
+ const routeId = match?.routeId;
646
+ const route = routesById[routeId];
647
+ const locationKey = useRouterState().location.state?.key;
648
+ const PendingComponent = route.options.pendingComponent ?? options.defaultPendingComponent ?? defaultPending;
649
+ const routeErrorComponent = route.options.errorComponent ?? options.defaultErrorComponent ?? ErrorComponent;
650
+ const ResolvedSuspenseBoundary = route.options.wrapInSuspense ? React.Suspense : SafeFragment;
651
+ const errorComponent = routeErrorComponent ? React.useCallback(props => {
652
+ return /*#__PURE__*/React.createElement(routeErrorComponent, {
653
+ ...props,
654
+ useMatch: route.useMatch,
655
+ useRouteContext: route.useRouteContext,
656
+ useSearch: route.useSearch,
657
+ useParams: route.useParams
658
+ });
659
+ }, [route]) : undefined;
660
+ return /*#__PURE__*/React.createElement(matchesContext.Provider, {
661
+ value: matches
662
+ }, /*#__PURE__*/React.createElement(ResolvedSuspenseBoundary, {
663
+ fallback: /*#__PURE__*/React.createElement(PendingComponent, {
664
+ useMatch: route.useMatch,
665
+ useRouteContext: route.useRouteContext,
666
+ useSearch: route.useSearch,
667
+ useParams: route.useParams
668
+ })
669
+ }, errorComponent ? /*#__PURE__*/React.createElement(CatchBoundary, {
670
+ resetKey: locationKey,
671
+ errorComponent: errorComponent,
672
+ onCatch: () => {
673
+ warning(false, `Error in route match: ${match.id}`);
674
+ }
675
+ }, /*#__PURE__*/React.createElement(MatchInner, {
676
+ match: match
677
+ })) : /*#__PURE__*/React.createElement(SafeFragment, null, /*#__PURE__*/React.createElement(MatchInner, {
678
+ match: match
679
+ }))));
680
+ }
681
+ function MatchInner({
682
+ match
683
+ }) {
684
+ const {
685
+ options,
686
+ routesById
687
+ } = useRouter();
688
+ const route = routesById[match.routeId];
689
+ if (match.status === 'error') {
690
+ throw match.error;
1410
691
  }
1411
-
1412
- if (id !== rootRouteId) {
1413
- id = joinPaths(['/', id]);
692
+ if (match.status === 'pending') {
693
+ throw match.loadPromise;
1414
694
  }
1415
-
1416
- const fullPath = id === rootRouteId ? '/' : trimPathRight(joinPaths([parentPath, path]));
1417
- return {
1418
- id: id,
1419
- routeId: routeId,
1420
- path: path,
1421
- fullPath: fullPath,
1422
- options: options,
1423
- children,
1424
- createChildren: cb => createRouteConfig(options, cb(childOptions => createRouteConfig(childOptions, undefined, false, id, fullPath)), false, parentId, parentPath),
1425
- addChildren: children => createRouteConfig(options, children, false, parentId, parentPath),
1426
- createRoute: childOptions => createRouteConfig(childOptions, undefined, false, id, fullPath)
1427
- };
1428
- };
1429
-
1430
- const componentTypes = ['component', 'errorComponent', 'pendingComponent'];
1431
- function createRouteMatch(router, route, opts) {
1432
- const routeMatch = _extends({}, route, opts, {
1433
- router,
1434
- routeSearch: {},
1435
- search: {},
1436
- childMatches: [],
1437
- status: 'idle',
1438
- routeLoaderData: {},
1439
- loaderData: {},
1440
- isFetching: false,
1441
- isInvalid: false,
1442
- invalidAt: Infinity,
1443
- // pendingActions: [],
1444
- getIsInvalid: () => {
1445
- const now = Date.now();
1446
- return routeMatch.isInvalid || routeMatch.invalidAt < now;
1447
- },
1448
- __: {
1449
- abortController: new AbortController(),
1450
- latestId: '',
1451
- resolve: () => {},
1452
- notify: () => {
1453
- routeMatch.__.resolve();
1454
-
1455
- routeMatch.router.notify();
1456
- },
1457
- validate: () => {
1458
- var _routeMatch$parentMat, _routeMatch$parentMat2;
1459
-
1460
- // Validate the search params and stabilize them
1461
- const parentSearch = (_routeMatch$parentMat = (_routeMatch$parentMat2 = routeMatch.parentMatch) == null ? void 0 : _routeMatch$parentMat2.search) != null ? _routeMatch$parentMat : router.location.search;
1462
-
1463
- try {
1464
- var _validator;
1465
-
1466
- const prevSearch = routeMatch.routeSearch;
1467
- const validator = typeof routeMatch.options.validateSearch === 'object' ? routeMatch.options.validateSearch.parse : routeMatch.options.validateSearch;
1468
- let nextSearch = replaceEqualDeep(prevSearch, (_validator = validator == null ? void 0 : validator(parentSearch)) != null ? _validator : {}); // Invalidate route matches when search param stability changes
1469
-
1470
- if (prevSearch !== nextSearch) {
1471
- routeMatch.isInvalid = true;
1472
- }
1473
-
1474
- routeMatch.routeSearch = nextSearch;
1475
- routeMatch.search = replaceEqualDeep(parentSearch, _extends({}, parentSearch, nextSearch));
1476
- componentTypes.map(async type => {
1477
- const component = routeMatch.options[type];
1478
-
1479
- if (typeof routeMatch.__[type] !== 'function') {
1480
- routeMatch.__[type] = component;
1481
- }
1482
- });
1483
- } catch (err) {
1484
- console.error(err);
1485
- const error = new Error('Invalid search params found', {
1486
- cause: err
1487
- });
1488
- error.code = 'INVALID_SEARCH_PARAMS';
1489
- routeMatch.status = 'error';
1490
- routeMatch.error = error; // Do not proceed with loading the route
1491
-
1492
- return;
1493
- }
1494
- }
1495
- },
1496
- cancel: () => {
1497
- var _routeMatch$__$abortC;
1498
-
1499
- (_routeMatch$__$abortC = routeMatch.__.abortController) == null ? void 0 : _routeMatch$__$abortC.abort();
1500
- },
1501
- invalidate: () => {
1502
- routeMatch.isInvalid = true;
1503
- },
1504
- hasLoaders: () => {
1505
- return !!(route.options.loader || componentTypes.some(d => {
1506
- var _route$options$d;
1507
-
1508
- return (_route$options$d = route.options[d]) == null ? void 0 : _route$options$d.preload;
1509
- }));
1510
- },
1511
- load: async loaderOpts => {
1512
- const now = Date.now();
1513
- const minMaxAge = loaderOpts != null && loaderOpts.preload ? Math.max(loaderOpts == null ? void 0 : loaderOpts.maxAge, loaderOpts == null ? void 0 : loaderOpts.gcMaxAge) : 0; // If this is a preload, add it to the preload cache
1514
-
1515
- if (loaderOpts != null && loaderOpts.preload && minMaxAge > 0) {
1516
- // If the match is currently active, don't preload it
1517
- if (router.state.matches.find(d => d.matchId === routeMatch.matchId)) {
1518
- return;
1519
- }
1520
-
1521
- router.matchCache[routeMatch.matchId] = {
1522
- gc: now + loaderOpts.gcMaxAge,
1523
- match: routeMatch
1524
- };
1525
- } // If the match is invalid, errored or idle, trigger it to load
1526
-
1527
-
1528
- if (routeMatch.status === 'success' && routeMatch.getIsInvalid() || routeMatch.status === 'error' || routeMatch.status === 'idle') {
1529
- const maxAge = loaderOpts != null && loaderOpts.preload ? loaderOpts == null ? void 0 : loaderOpts.maxAge : undefined;
1530
- await routeMatch.fetch({
1531
- maxAge
1532
- });
1533
- }
1534
- },
1535
- fetch: async opts => {
1536
- const loadId = '' + Date.now() + Math.random();
1537
- routeMatch.__.latestId = loadId;
1538
-
1539
- const checkLatest = async () => {
1540
- if (loadId !== routeMatch.__.latestId) {
1541
- // warning(true, 'Data loader is out of date!')
1542
- return new Promise(() => {});
1543
- }
1544
- }; // If the match was in an error state, set it
1545
- // to a loading state again. Otherwise, keep it
1546
- // as loading or resolved
1547
-
1548
-
1549
- if (routeMatch.status === 'idle') {
1550
- routeMatch.status = 'loading';
1551
- } // We started loading the route, so it's no longer invalid
1552
-
1553
-
1554
- routeMatch.isInvalid = false;
1555
- routeMatch.__.loadPromise = new Promise(async resolve => {
1556
- // We are now fetching, even if it's in the background of a
1557
- // resolved state
1558
- routeMatch.isFetching = true;
1559
- routeMatch.__.resolve = resolve;
1560
-
1561
- routeMatch.__.componentsPromise = (async () => {
1562
- // then run all component and data loaders in parallel
1563
- // For each component type, potentially load it asynchronously
1564
- await Promise.all(componentTypes.map(async type => {
1565
- var _routeMatch$__$type;
1566
-
1567
- const component = routeMatch.options[type];
1568
-
1569
- if ((_routeMatch$__$type = routeMatch.__[type]) != null && _routeMatch$__$type.preload) {
1570
- routeMatch.__[type] = await router.options.loadComponent(component);
1571
- }
1572
- }));
1573
- })();
1574
-
1575
- routeMatch.__.dataPromise = Promise.resolve().then(async () => {
1576
- try {
1577
- var _ref, _ref2, _opts$maxAge;
1578
-
1579
- if (routeMatch.options.loader) {
1580
- const data = await router.loadMatchData(routeMatch);
1581
- await checkLatest();
1582
- routeMatch.routeLoaderData = replaceEqualDeep(routeMatch.routeLoaderData, data);
1583
- }
1584
-
1585
- routeMatch.error = undefined;
1586
- routeMatch.status = 'success';
1587
- routeMatch.updatedAt = Date.now();
1588
- 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);
1589
- return routeMatch.routeLoaderData;
1590
- } catch (err) {
1591
- await checkLatest();
1592
-
1593
- if (process.env.NODE_ENV !== 'production') {
1594
- console.error(err);
1595
- }
1596
-
1597
- routeMatch.error = err;
1598
- routeMatch.status = 'error';
1599
- routeMatch.updatedAt = Date.now();
1600
- throw err;
1601
- }
1602
- });
1603
-
1604
- const after = async () => {
1605
- await checkLatest();
1606
- routeMatch.isFetching = false;
1607
- delete routeMatch.__.loadPromise;
1608
-
1609
- routeMatch.__.notify();
1610
- };
1611
-
1612
- try {
1613
- await Promise.all([routeMatch.__.componentsPromise, routeMatch.__.dataPromise.catch(() => {})]);
1614
- after();
1615
- } catch (_unused) {
1616
- after();
1617
- }
695
+ if (match.status === 'success') {
696
+ let comp = route.options.component ?? options.defaultComponent;
697
+ if (comp) {
698
+ return /*#__PURE__*/React.createElement(comp, {
699
+ useMatch: route.useMatch,
700
+ useRouteContext: route.useRouteContext,
701
+ useSearch: route.useSearch,
702
+ useParams: route.useParams,
703
+ useLoaderData: route.useLoaderData
1618
704
  });
1619
- await routeMatch.__.loadPromise;
1620
- await checkLatest();
1621
705
  }
706
+ return /*#__PURE__*/React.createElement(Outlet, null);
707
+ }
708
+ invariant(false, 'Idle routeMatch status encountered during rendering! You should never see this. File an issue!');
709
+ }
710
+ function Outlet() {
711
+ const matches = React.useContext(matchesContext).slice(1);
712
+ if (!matches[0]) {
713
+ return null;
714
+ }
715
+ return /*#__PURE__*/React.createElement(Match, {
716
+ matches: matches
1622
717
  });
1623
-
1624
- if (!routeMatch.hasLoaders()) {
1625
- routeMatch.status = 'success';
718
+ }
719
+ function useMatchRoute() {
720
+ const {
721
+ matchRoute
722
+ } = useRouter();
723
+ return React.useCallback(opts => {
724
+ const {
725
+ pending,
726
+ caseSensitive,
727
+ ...rest
728
+ } = opts;
729
+ return matchRoute(rest, {
730
+ pending,
731
+ caseSensitive
732
+ });
733
+ }, []);
734
+ }
735
+ function MatchRoute(props) {
736
+ const matchRoute = useMatchRoute();
737
+ const params = matchRoute(props);
738
+ if (typeof props.children === 'function') {
739
+ return props.children(params);
1626
740
  }
1627
-
1628
- return routeMatch;
741
+ return !!params ? props.children : null;
1629
742
  }
1630
-
1631
- const defaultParseSearch = parseSearchWith(JSON.parse);
1632
- const defaultStringifySearch = stringifySearchWith(JSON.stringify);
1633
- function parseSearchWith(parser) {
1634
- return searchStr => {
1635
- if (searchStr.substring(0, 1) === '?') {
1636
- searchStr = searchStr.substring(1);
743
+ function useMatch(opts) {
744
+ const nearestMatch = React.useContext(matchesContext)[0];
745
+ const nearestMatchRouteId = nearestMatch?.routeId;
746
+ const matchRouteId = useRouterState({
747
+ select: state => {
748
+ const match = opts?.from ? state.matches.find(d => d.routeId === opts?.from) : state.matches.find(d => d.id === nearestMatch.id);
749
+ return match.routeId;
1637
750
  }
1638
-
1639
- let query = decode(searchStr); // Try to parse any query params that might be json
1640
-
1641
- for (let key in query) {
1642
- const value = query[key];
1643
-
1644
- if (typeof value === 'string') {
1645
- try {
1646
- query[key] = parser(value);
1647
- } catch (err) {//
1648
- }
1649
- }
751
+ });
752
+ if (opts?.strict ?? true) {
753
+ invariant(nearestMatchRouteId == matchRouteId, `useMatch("${matchRouteId}") is being called in a component that is meant to render the '${nearestMatchRouteId}' route. Did you mean to 'useMatch("${matchRouteId}", { strict: false })' or 'useRoute("${matchRouteId}")' instead?`);
754
+ }
755
+ const matchSelection = useRouterState({
756
+ select: state => {
757
+ const match = opts?.from ? state.matches.find(d => d.routeId === opts?.from) : state.matches.find(d => d.id === nearestMatch.id);
758
+ invariant(match, `Could not find ${opts?.from ? `an active match from "${opts.from}"` : 'a nearest match!'}`);
759
+ return opts?.select ? opts.select(match) : match;
1650
760
  }
1651
-
1652
- return query;
1653
- };
761
+ });
762
+ return matchSelection;
1654
763
  }
1655
- function stringifySearchWith(stringify) {
1656
- return search => {
1657
- search = _extends({}, search);
1658
-
1659
- if (search) {
1660
- Object.keys(search).forEach(key => {
1661
- const val = search[key];
1662
-
1663
- if (typeof val === 'undefined' || val === undefined) {
1664
- delete search[key];
1665
- } else if (val && typeof val === 'object' && val !== null) {
1666
- try {
1667
- search[key] = stringify(val);
1668
- } catch (err) {// silent
1669
- }
1670
- }
1671
- });
764
+ const matchesContext = /*#__PURE__*/React.createContext(null);
765
+ function useMatches(opts) {
766
+ const contextMatches = React.useContext(matchesContext);
767
+ return useRouterState({
768
+ select: state => {
769
+ const matches = state.matches.slice(state.matches.findIndex(d => d.id === contextMatches[0]?.id));
770
+ return opts?.select ? opts.select(matches) : matches;
1672
771
  }
1673
-
1674
- const searchStr = encode(search).toString();
1675
- return searchStr ? "?" + searchStr : '';
1676
- };
772
+ });
773
+ }
774
+ function useLoaderData(opts) {
775
+ const match = useMatch({
776
+ ...opts,
777
+ select: undefined
778
+ });
779
+ return typeof opts.select === 'function' ? opts.select(match?.loaderData) : match?.loaderData;
1677
780
  }
1678
781
 
1679
- var _window$document;
1680
782
  // Detect if we're in the DOM
1681
- const isServer = typeof window === 'undefined' || !((_window$document = window.document) != null && _window$document.createElement); // This is the default history object if none is defined
1682
-
1683
- const createDefaultHistory = () => isServer ? createMemoryHistory() : createBrowserHistory();
1684
783
 
1685
- function getInitialRouterState() {
1686
- return {
1687
- status: 'idle',
1688
- location: null,
1689
- matches: [],
1690
- actions: {},
1691
- loaders: {},
1692
- lastUpdated: Date.now(),
1693
- isFetching: false,
1694
- isPreloading: false
1695
- };
784
+ function redirect(opts) {
785
+ opts.isRedirect = true;
786
+ return opts;
787
+ }
788
+ function isRedirect(obj) {
789
+ return !!obj?.isRedirect;
1696
790
  }
1697
791
 
1698
- function createRouter(userOptions) {
1699
- var _userOptions$stringif, _userOptions$parseSea;
1700
-
1701
- const history = (userOptions == null ? void 0 : userOptions.history) || createDefaultHistory();
1702
-
1703
- const originalOptions = _extends({
1704
- defaultLoaderGcMaxAge: 5 * 60 * 1000,
1705
- defaultLoaderMaxAge: 0,
1706
- defaultPreloadMaxAge: 2000,
1707
- defaultPreloadDelay: 50
1708
- }, userOptions, {
1709
- stringifySearch: (_userOptions$stringif = userOptions == null ? void 0 : userOptions.stringifySearch) != null ? _userOptions$stringif : defaultStringifySearch,
1710
- parseSearch: (_userOptions$parseSea = userOptions == null ? void 0 : userOptions.parseSearch) != null ? _userOptions$parseSea : defaultParseSearch
1711
- });
1712
-
1713
- let router = {
1714
- types: undefined,
1715
- // public api
1716
- history,
1717
- options: originalOptions,
1718
- listeners: [],
1719
- // Resolved after construction
1720
- basepath: '',
1721
- routeTree: undefined,
1722
- routesById: {},
1723
- location: undefined,
1724
- //
1725
- navigationPromise: Promise.resolve(),
1726
- resolveNavigation: () => {},
1727
- matchCache: {},
1728
- state: getInitialRouterState(),
1729
- reset: () => {
1730
- router.state = getInitialRouterState();
1731
- router.notify();
1732
- },
1733
- startedLoadingAt: Date.now(),
1734
- subscribe: listener => {
1735
- router.listeners.push(listener);
1736
- return () => {
1737
- router.listeners = router.listeners.filter(x => x !== listener);
1738
- };
1739
- },
1740
- getRoute: id => {
1741
- return router.routesById[id];
1742
- },
1743
- notify: () => {
1744
- const isFetching = router.state.status === 'loading' || router.state.matches.some(d => d.isFetching);
1745
- const isPreloading = Object.values(router.matchCache).some(d => d.match.isFetching && !router.state.matches.find(dd => dd.matchId === d.match.matchId));
1746
-
1747
- if (router.state.isFetching !== isFetching || router.state.isPreloading !== isPreloading) {
1748
- router.state = _extends({}, router.state, {
1749
- isFetching,
1750
- isPreloading
1751
- });
1752
- }
1753
-
1754
- cascadeLoaderData(router.state.matches);
1755
- router.listeners.forEach(listener => listener(router));
1756
- },
1757
- dehydrateState: () => {
1758
- return _extends({}, pick(router.state, ['status', 'location', 'lastUpdated']), {
1759
- matches: router.state.matches.map(match => pick(match, ['matchId', 'status', 'routeLoaderData', 'loaderData', 'isInvalid', 'invalidAt']))
1760
- });
1761
- },
1762
- hydrateState: dehydratedState => {
1763
- // Match the routes
1764
- const matches = router.matchRoutes(router.location.pathname, {
1765
- strictParseParams: true
1766
- });
1767
- matches.forEach((match, index) => {
1768
- const dehydratedMatch = dehydratedState.matches[index];
1769
- invariant(dehydratedMatch, 'Oh no! Dehydrated route matches did not match the active state of the router 😬');
1770
- Object.assign(match, dehydratedMatch);
1771
- });
1772
- matches.forEach(match => match.__.validate());
1773
- router.state = _extends({}, router.state, dehydratedState, {
1774
- matches
1775
- });
1776
- },
1777
- mount: () => {
1778
- const next = router.__.buildLocation({
1779
- to: '.',
1780
- search: true,
1781
- hash: true
1782
- }); // If the current location isn't updated, trigger a navigation
1783
- // to the current location. Otherwise, load the current location.
1784
-
1785
-
1786
- if (next.href !== router.location.href) {
1787
- router.__.commitLocation(next, true);
1788
- }
1789
-
1790
- if (!router.state.matches.length) {
1791
- router.load();
1792
- }
1793
-
1794
- const unsub = router.history.listen(event => {
1795
- router.load(router.__.parseLocation(event.location, router.location));
1796
- }); // addEventListener does not exist in React Native, but window does
1797
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
1798
-
1799
- if (!isServer && window.addEventListener) {
1800
- // Listen to visibillitychange and focus
1801
- window.addEventListener('visibilitychange', router.onFocus, false);
1802
- window.addEventListener('focus', router.onFocus, false);
1803
- }
1804
-
1805
- return () => {
1806
- unsub();
792
+ // @ts-nocheck
1807
793
 
1808
- if (!isServer && window.removeEventListener) {
1809
- // Be sure to unsubscribe if a new handler is set
1810
- window.removeEventListener('visibilitychange', router.onFocus);
1811
- window.removeEventListener('focus', router.onFocus);
1812
- }
1813
- };
1814
- },
1815
- onFocus: () => {
1816
- router.load();
1817
- },
1818
- update: opts => {
1819
- const newHistory = (opts == null ? void 0 : opts.history) !== router.history;
794
+ // 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.
1820
795
 
1821
- if (!router.location || newHistory) {
1822
- if (opts != null && opts.history) {
1823
- router.history = opts.history;
796
+ function encode(obj, pfx) {
797
+ var k,
798
+ i,
799
+ tmp,
800
+ str = '';
801
+ for (k in obj) {
802
+ if ((tmp = obj[k]) !== void 0) {
803
+ if (Array.isArray(tmp)) {
804
+ for (i = 0; i < tmp.length; i++) {
805
+ str && (str += '&');
806
+ str += encodeURIComponent(k) + '=' + encodeURIComponent(tmp[i]);
1824
807
  }
1825
-
1826
- router.location = router.__.parseLocation(router.history.location);
1827
- router.state.location = router.location;
1828
- }
1829
-
1830
- Object.assign(router.options, opts);
1831
- const {
1832
- basepath,
1833
- routeConfig
1834
- } = router.options;
1835
- router.basepath = cleanPath("/" + (basepath != null ? basepath : ''));
1836
-
1837
- if (routeConfig) {
1838
- router.routesById = {};
1839
- router.routeTree = router.__.buildRouteTree(routeConfig);
1840
- }
1841
-
1842
- return router;
1843
- },
1844
- cancelMatches: () => {
1845
- var _router$state$pending, _router$state$pending2;
1846
- [...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 => {
1847
- match.cancel();
1848
- });
1849
- },
1850
- load: async next => {
1851
- const id = Math.random();
1852
- router.startedLoadingAt = id;
1853
-
1854
- if (next) {
1855
- // Ingest the new location
1856
- router.location = next;
1857
- } // Cancel any pending matches
1858
-
1859
-
1860
- router.cancelMatches(); // Match the routes
1861
-
1862
- const matches = router.matchRoutes(router.location.pathname, {
1863
- strictParseParams: true
1864
- });
1865
-
1866
- if (typeof document !== 'undefined') {
1867
- router.state = _extends({}, router.state, {
1868
- pending: {
1869
- matches: matches,
1870
- location: router.location
1871
- },
1872
- status: 'loading'
1873
- });
1874
808
  } else {
1875
- router.state = _extends({}, router.state, {
1876
- matches: matches,
1877
- location: router.location,
1878
- status: 'loading'
1879
- });
1880
- }
1881
-
1882
- router.notify(); // Load the matches
1883
-
1884
- await router.loadMatches(matches);
1885
-
1886
- if (router.startedLoadingAt !== id) {
1887
- // Ignore side-effects of match loading
1888
- return router.navigationPromise;
1889
- }
1890
-
1891
- const previousMatches = router.state.matches;
1892
- const exiting = [],
1893
- staying = [];
1894
- previousMatches.forEach(d => {
1895
- if (matches.find(dd => dd.matchId === d.matchId)) {
1896
- staying.push(d);
1897
- } else {
1898
- exiting.push(d);
1899
- }
1900
- });
1901
- const entering = matches.filter(d => {
1902
- return !previousMatches.find(dd => dd.matchId === d.matchId);
1903
- });
1904
- const now = Date.now();
1905
- exiting.forEach(d => {
1906
- var _ref, _d$options$loaderGcMa, _ref2, _d$options$loaderMaxA;
1907
-
1908
- d.__.onExit == null ? void 0 : d.__.onExit({
1909
- params: d.params,
1910
- search: d.routeSearch
1911
- }); // Clear idle error states when match leaves
1912
-
1913
- if (d.status === 'error' && !d.isFetching) {
1914
- d.status = 'idle';
1915
- d.error = undefined;
1916
- }
1917
-
1918
- const gc = Math.max((_ref = (_d$options$loaderGcMa = d.options.loaderGcMaxAge) != null ? _d$options$loaderGcMa : router.options.defaultLoaderGcMaxAge) != null ? _ref : 0, (_ref2 = (_d$options$loaderMaxA = d.options.loaderMaxAge) != null ? _d$options$loaderMaxA : router.options.defaultLoaderMaxAge) != null ? _ref2 : 0);
1919
-
1920
- if (gc > 0) {
1921
- router.matchCache[d.matchId] = {
1922
- gc: gc == Infinity ? Number.MAX_SAFE_INTEGER : now + gc,
1923
- match: d
1924
- };
1925
- }
1926
- });
1927
- staying.forEach(d => {
1928
- d.options.onTransition == null ? void 0 : d.options.onTransition({
1929
- params: d.params,
1930
- search: d.routeSearch
1931
- });
1932
- });
1933
- entering.forEach(d => {
1934
- d.__.onExit = d.options.onMatch == null ? void 0 : d.options.onMatch({
1935
- params: d.params,
1936
- search: d.search
1937
- });
1938
- delete router.matchCache[d.matchId];
1939
- });
1940
-
1941
- if (router.startedLoadingAt !== id) {
1942
- // Ignore side-effects of match loading
1943
- return;
1944
- }
1945
-
1946
- matches.forEach(match => {
1947
- // Clear actions
1948
- if (match.action) {
1949
- match.action.current = undefined;
1950
- match.action.submissions = [];
1951
- }
1952
- });
1953
- router.state = _extends({}, router.state, {
1954
- location: router.location,
1955
- matches,
1956
- pending: undefined,
1957
- status: 'idle'
1958
- });
1959
- router.notify();
1960
- router.resolveNavigation();
1961
- },
1962
- cleanMatchCache: () => {
1963
- const now = Date.now();
1964
- Object.keys(router.matchCache).forEach(matchId => {
1965
- const entry = router.matchCache[matchId]; // Don't remove loading matches
1966
-
1967
- if (entry.match.status === 'loading') {
1968
- return;
1969
- } // Do not remove successful matches that are still valid
1970
-
1971
-
1972
- if (entry.gc > 0 && entry.gc > now) {
1973
- return;
1974
- } // Everything else gets removed
1975
-
1976
-
1977
- delete router.matchCache[matchId];
1978
- });
1979
- },
1980
- loadRoute: async function loadRoute(navigateOpts) {
1981
- if (navigateOpts === void 0) {
1982
- navigateOpts = router.location;
1983
- }
1984
-
1985
- const next = router.buildNext(navigateOpts);
1986
- const matches = router.matchRoutes(next.pathname, {
1987
- strictParseParams: true
1988
- });
1989
- await router.loadMatches(matches);
1990
- return matches;
1991
- },
1992
- preloadRoute: async function preloadRoute(navigateOpts, loaderOpts) {
1993
- var _ref3, _ref4, _loaderOpts$maxAge, _ref5, _ref6, _loaderOpts$gcMaxAge;
1994
-
1995
- if (navigateOpts === void 0) {
1996
- navigateOpts = router.location;
1997
- }
1998
-
1999
- const next = router.buildNext(navigateOpts);
2000
- const matches = router.matchRoutes(next.pathname, {
2001
- strictParseParams: true
2002
- });
2003
- await router.loadMatches(matches, {
2004
- preload: true,
2005
- maxAge: (_ref3 = (_ref4 = (_loaderOpts$maxAge = loaderOpts.maxAge) != null ? _loaderOpts$maxAge : router.options.defaultPreloadMaxAge) != null ? _ref4 : router.options.defaultLoaderMaxAge) != null ? _ref3 : 0,
2006
- gcMaxAge: (_ref5 = (_ref6 = (_loaderOpts$gcMaxAge = loaderOpts.gcMaxAge) != null ? _loaderOpts$gcMaxAge : router.options.defaultPreloadGcMaxAge) != null ? _ref6 : router.options.defaultLoaderGcMaxAge) != null ? _ref5 : 0
2007
- });
2008
- return matches;
2009
- },
2010
- matchRoutes: (pathname, opts) => {
2011
- var _router$state$pending3, _router$state$pending4;
2012
-
2013
- router.cleanMatchCache();
2014
- const matches = [];
2015
-
2016
- if (!router.routeTree) {
2017
- return matches;
809
+ str && (str += '&');
810
+ str += encodeURIComponent(k) + '=' + encodeURIComponent(tmp);
2018
811
  }
812
+ }
813
+ }
814
+ return (pfx || '') + str;
815
+ }
816
+ function toValue(mix) {
817
+ if (!mix) return '';
818
+ var str = decodeURIComponent(mix);
819
+ if (str === 'false') return false;
820
+ if (str === 'true') return true;
821
+ return +str * 0 === 0 && +str + '' === str ? +str : str;
822
+ }
823
+ function decode(str) {
824
+ var tmp,
825
+ k,
826
+ out = {},
827
+ arr = str.split('&');
828
+ while (tmp = arr.shift()) {
829
+ tmp = tmp.split('=');
830
+ k = tmp.shift();
831
+ if (out[k] !== void 0) {
832
+ out[k] = [].concat(out[k], toValue(tmp.shift()));
833
+ } else {
834
+ out[k] = toValue(tmp.shift());
835
+ }
836
+ }
837
+ return out;
838
+ }
2019
839
 
2020
- 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 : [])];
2021
-
2022
- const recurse = async routes => {
2023
- var _parentMatch$params, _router$options$filte, _foundRoute$childRout;
2024
-
2025
- const parentMatch = last(matches);
2026
- let params = (_parentMatch$params = parentMatch == null ? void 0 : parentMatch.params) != null ? _parentMatch$params : {};
2027
- const filteredRoutes = (_router$options$filte = router.options.filterRoutes == null ? void 0 : router.options.filterRoutes(routes)) != null ? _router$options$filte : routes;
2028
- let foundRoutes = [];
2029
-
2030
- const findMatchInRoutes = (parentRoutes, routes) => {
2031
- routes.some(route => {
2032
- var _route$childRoutes, _route$childRoutes2, _route$options$caseSe;
2033
-
2034
- if (!route.routePath && (_route$childRoutes = route.childRoutes) != null && _route$childRoutes.length) {
2035
- return findMatchInRoutes([...foundRoutes, route], route.childRoutes);
2036
- }
2037
-
2038
- const fuzzy = !!(route.routePath !== '/' || (_route$childRoutes2 = route.childRoutes) != null && _route$childRoutes2.length);
2039
- const matchParams = matchPathname(pathname, {
2040
- to: route.fullPath,
2041
- fuzzy,
2042
- caseSensitive: (_route$options$caseSe = route.options.caseSensitive) != null ? _route$options$caseSe : router.options.caseSensitive
2043
- });
2044
-
2045
- if (matchParams) {
2046
- let parsedParams;
2047
-
2048
- try {
2049
- var _route$options$parseP;
2050
-
2051
- parsedParams = (_route$options$parseP = route.options.parseParams == null ? void 0 : route.options.parseParams(matchParams)) != null ? _route$options$parseP : matchParams;
2052
- } catch (err) {
2053
- if (opts != null && opts.strictParseParams) {
2054
- throw err;
2055
- }
2056
- }
2057
-
2058
- params = _extends({}, params, parsedParams);
2059
- }
2060
-
2061
- if (!!matchParams) {
2062
- foundRoutes = [...parentRoutes, route];
2063
- }
2064
-
2065
- return !!foundRoutes.length;
2066
- });
2067
- return !!foundRoutes.length;
2068
- };
2069
-
2070
- findMatchInRoutes([], filteredRoutes);
2071
-
2072
- if (!foundRoutes.length) {
2073
- return;
2074
- }
2075
-
2076
- foundRoutes.forEach(foundRoute => {
2077
- var _router$matchCache$ma;
2078
-
2079
- const interpolatedPath = interpolatePath(foundRoute.routePath, params);
2080
- const matchId = interpolatePath(foundRoute.routeId, params, true);
2081
- const match = existingMatches.find(d => d.matchId === matchId) || ((_router$matchCache$ma = router.matchCache[matchId]) == null ? void 0 : _router$matchCache$ma.match) || createRouteMatch(router, foundRoute, {
2082
- parentMatch,
2083
- matchId,
2084
- params,
2085
- pathname: joinPaths([pathname, interpolatedPath])
2086
- });
2087
- matches.push(match);
2088
- });
2089
- const foundRoute = last(foundRoutes);
2090
-
2091
- if ((_foundRoute$childRout = foundRoute.childRoutes) != null && _foundRoute$childRout.length) {
2092
- recurse(foundRoute.childRoutes);
2093
- }
2094
- };
2095
-
2096
- recurse([router.routeTree]);
2097
- cascadeLoaderData(matches);
2098
- return matches;
2099
- },
2100
- loadMatches: async (resolvedMatches, loaderOpts) => {
2101
- const matchPromises = resolvedMatches.map(async match => {
2102
- // Validate the match (loads search params etc)
2103
- match.__.validate();
2104
-
2105
- match.load(loaderOpts);
2106
- const search = match.search;
2107
-
2108
- if (search.__data && search.__data.matchId !== match.matchId) {
2109
- return;
2110
- }
2111
-
2112
- if (match.__.loadPromise) {
2113
- // Wait for the first sign of activity from the match
2114
- await match.__.loadPromise;
2115
- }
2116
- });
2117
- router.notify();
2118
- await Promise.all(matchPromises);
2119
- },
2120
- loadMatchData: async routeMatch => {
2121
- if (isServer || !router.options.useServerData) {
2122
- var _await$routeMatch$opt;
2123
-
2124
- return (_await$routeMatch$opt = await (routeMatch.options.loader == null ? void 0 : routeMatch.options.loader({
2125
- // parentLoaderPromise: routeMatch.parentMatch?.__.dataPromise,
2126
- params: routeMatch.params,
2127
- search: routeMatch.routeSearch,
2128
- signal: routeMatch.__.abortController.signal
2129
- }))) != null ? _await$routeMatch$opt : {};
2130
- } else {
2131
- const next = router.buildNext({
2132
- to: '.',
2133
- search: d => _extends({}, d != null ? d : {}, {
2134
- __data: {
2135
- matchId: routeMatch.matchId
2136
- }
2137
- })
2138
- });
2139
- const res = await fetch(next.href, {
2140
- method: 'GET' // signal: routeMatch.__.abortController.signal,
2141
-
2142
- });
840
+ const defaultParseSearch = parseSearchWith(JSON.parse);
841
+ const defaultStringifySearch = stringifySearchWith(JSON.stringify, JSON.parse);
842
+ function parseSearchWith(parser) {
843
+ return searchStr => {
844
+ if (searchStr.substring(0, 1) === '?') {
845
+ searchStr = searchStr.substring(1);
846
+ }
847
+ let query = decode(searchStr);
2143
848
 
2144
- if (res.ok) {
2145
- return res.json();
849
+ // Try to parse any query params that might be json
850
+ for (let key in query) {
851
+ const value = query[key];
852
+ if (typeof value === 'string') {
853
+ try {
854
+ query[key] = parser(value);
855
+ } catch (err) {
856
+ //
2146
857
  }
2147
-
2148
- throw new Error('Failed to fetch match data');
2149
858
  }
2150
- },
2151
- invalidateRoute: opts => {
2152
- var _router$state$pending5, _router$state$pending6;
2153
-
2154
- const next = router.buildNext(opts);
2155
- const unloadedMatchIds = router.matchRoutes(next.pathname).map(d => d.matchId);
2156
- [...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 => {
2157
- if (unloadedMatchIds.includes(match.matchId)) {
2158
- match.invalidate();
2159
- }
2160
- });
2161
- },
2162
- reload: () => router.__.navigate({
2163
- fromCurrent: true,
2164
- replace: true,
2165
- search: true
2166
- }),
2167
- resolvePath: (from, path) => {
2168
- return resolvePath(router.basepath, from, cleanPath(path));
2169
- },
2170
- matchRoute: (location, opts) => {
2171
- var _location$from;
2172
-
2173
- // const location = router.buildNext(opts)
2174
- location = _extends({}, location, {
2175
- to: location.to ? router.resolvePath((_location$from = location.from) != null ? _location$from : '', location.to) : undefined
2176
- });
2177
- const next = router.buildNext(location);
2178
-
2179
- if (opts != null && opts.pending) {
2180
- var _router$state$pending7;
2181
-
2182
- if (!((_router$state$pending7 = router.state.pending) != null && _router$state$pending7.location)) {
2183
- return false;
2184
- }
2185
-
2186
- return !!matchPathname(router.state.pending.location.pathname, _extends({}, opts, {
2187
- to: next.pathname
2188
- }));
859
+ }
860
+ return query;
861
+ };
862
+ }
863
+ function stringifySearchWith(stringify, parser) {
864
+ function stringifyValue(val) {
865
+ if (typeof val === 'object' && val !== null) {
866
+ try {
867
+ return stringify(val);
868
+ } catch (err) {
869
+ // silent
2189
870
  }
2190
-
2191
- return !!matchPathname(router.state.location.pathname, _extends({}, opts, {
2192
- to: next.pathname
2193
- }));
2194
- },
2195
- navigate: async _ref7 => {
2196
- let {
2197
- from,
2198
- to = '.',
2199
- search,
2200
- hash,
2201
- replace,
2202
- params
2203
- } = _ref7;
2204
- // If this link simply reloads the current route,
2205
- // make sure it has a new key so it will trigger a data refresh
2206
- // If this `to` is a valid external URL, return
2207
- // null for LinkUtils
2208
- const toString = String(to);
2209
- const fromString = String(from);
2210
- let isExternal;
2211
-
871
+ } else if (typeof val === 'string' && typeof parser === 'function') {
2212
872
  try {
2213
- new URL("" + toString);
2214
- isExternal = true;
2215
- } catch (e) {}
2216
-
2217
- invariant(!isExternal, 'Attempting to navigate to external url with router.navigate!');
2218
- return router.__.navigate({
2219
- from: fromString,
2220
- to: toString,
2221
- search,
2222
- hash,
2223
- replace,
2224
- params
873
+ // Check if it's a valid parseable string.
874
+ // If it is, then stringify it again.
875
+ parser(val);
876
+ return stringify(val);
877
+ } catch (err) {
878
+ // silent
879
+ }
880
+ }
881
+ return val;
882
+ }
883
+ return search => {
884
+ search = {
885
+ ...search
886
+ };
887
+ if (search) {
888
+ Object.keys(search).forEach(key => {
889
+ const val = search[key];
890
+ if (typeof val === 'undefined' || val === undefined) {
891
+ delete search[key];
892
+ } else {
893
+ search[key] = stringifyValue(val);
894
+ }
2225
895
  });
2226
- },
2227
- buildLink: _ref8 => {
2228
- var _preload, _ref9;
2229
-
2230
- let {
2231
- from,
2232
- to = '.',
2233
- search,
2234
- params,
2235
- hash,
2236
- target,
2237
- replace,
2238
- activeOptions,
2239
- preload,
2240
- preloadMaxAge: userPreloadMaxAge,
2241
- preloadGcMaxAge: userPreloadGcMaxAge,
2242
- preloadDelay: userPreloadDelay,
2243
- disabled
2244
- } = _ref8;
2245
-
2246
- // If this link simply reloads the current route,
2247
- // make sure it has a new key so it will trigger a data refresh
2248
- // If this `to` is a valid external URL, return
2249
- // null for LinkUtils
2250
- try {
2251
- new URL("" + to);
2252
- return {
2253
- type: 'external',
2254
- href: to
2255
- };
2256
- } catch (e) {}
2257
-
2258
- const nextOpts = {
2259
- from,
2260
- to,
2261
- search,
2262
- params,
2263
- hash,
2264
- replace
2265
- };
2266
- const next = router.buildNext(nextOpts);
2267
- preload = (_preload = preload) != null ? _preload : router.options.defaultPreload;
2268
- const preloadDelay = (_ref9 = userPreloadDelay != null ? userPreloadDelay : router.options.defaultPreloadDelay) != null ? _ref9 : 0; // Compare path/hash for matches
2269
-
2270
- const pathIsEqual = router.state.location.pathname === next.pathname;
2271
- const currentPathSplit = router.state.location.pathname.split('/');
2272
- const nextPathSplit = next.pathname.split('/');
2273
- const pathIsFuzzyEqual = nextPathSplit.every((d, i) => d === currentPathSplit[i]);
2274
- const hashIsEqual = router.state.location.hash === next.hash; // Combine the matches based on user options
2275
-
2276
- const pathTest = activeOptions != null && activeOptions.exact ? pathIsEqual : pathIsFuzzyEqual;
2277
- const hashTest = activeOptions != null && activeOptions.includeHash ? hashIsEqual : true; // The final "active" test
2278
-
2279
- const isActive = pathTest && hashTest; // The click handler
2280
-
2281
- const handleClick = e => {
2282
- if (!disabled && !isCtrlEvent(e) && !e.defaultPrevented && (!target || target === '_self') && e.button === 0) {
2283
- e.preventDefault();
896
+ }
897
+ const searchStr = encode(search).toString();
898
+ return searchStr ? `?${searchStr}` : '';
899
+ };
900
+ }
2284
901
 
2285
- if (pathIsEqual && !search && !hash) {
2286
- router.invalidateRoute(nextOpts);
2287
- } // All is well? Navigate!)
902
+ //
2288
903
 
904
+ //
2289
905
 
2290
- router.__.navigate(nextOpts);
2291
- }
2292
- }; // The click handler
906
+ const componentTypes = ['component', 'errorComponent', 'pendingComponent'];
907
+ class Router {
908
+ // dehydratedData?: TDehydrated
909
+ // resetNextScroll = false
910
+ // tempLocationKey = `${Math.round(Math.random() * 10000000)}`
911
+ constructor(options) {
912
+ this.options = {
913
+ defaultPreloadDelay: 50,
914
+ context: undefined,
915
+ ...options,
916
+ stringifySearch: options?.stringifySearch ?? defaultStringifySearch,
917
+ parseSearch: options?.parseSearch ?? defaultParseSearch
918
+ };
919
+ this.routeTree = this.options.routeTree;
920
+ }
921
+ subscribers = new Set();
922
+ subscribe = (eventType, fn) => {
923
+ const listener = {
924
+ eventType,
925
+ fn
926
+ };
927
+ this.subscribers.add(listener);
928
+ return () => {
929
+ this.subscribers.delete(listener);
930
+ };
931
+ };
932
+ emit = routerEvent => {
933
+ this.subscribers.forEach(listener => {
934
+ if (listener.eventType === routerEvent.type) {
935
+ listener.fn(routerEvent);
936
+ }
937
+ });
938
+ };
2293
939
 
940
+ // dehydrate = (): DehydratedRouter => {
941
+ // return {
942
+ // state: {
943
+ // dehydratedMatches: state.matches.map((d) =>
944
+ // pick(d, ['fetchedAt', 'invalid', 'id', 'status', 'updatedAt']),
945
+ // ),
946
+ // },
947
+ // }
948
+ // }
2294
949
 
2295
- const handleFocus = e => {
2296
- if (preload) {
2297
- router.preloadRoute(nextOpts, {
2298
- maxAge: userPreloadMaxAge,
2299
- gcMaxAge: userPreloadGcMaxAge
2300
- });
2301
- }
2302
- };
950
+ // hydrate = async (__do_not_use_server_ctx?: HydrationCtx) => {
951
+ // let _ctx = __do_not_use_server_ctx
952
+ // // Client hydrates from window
953
+ // if (typeof document !== 'undefined') {
954
+ // _ctx = window.__TSR_DEHYDRATED__
955
+ // }
956
+
957
+ // invariant(
958
+ // _ctx,
959
+ // 'Expected to find a __TSR_DEHYDRATED__ property on window... but we did not. Did you forget to render <DehydrateRouter /> in your app?',
960
+ // )
961
+
962
+ // const ctx = _ctx
963
+ // this.dehydratedData = ctx.payload as any
964
+ // this.options.hydrate?.(ctx.payload as any)
965
+ // const dehydratedState = ctx.router.state
966
+
967
+ // let matches = this.matchRoutes(
968
+ // state.location.pathname,
969
+ // state.location.search,
970
+ // ).map((match) => {
971
+ // const dehydratedMatch = dehydratedState.dehydratedMatches.find(
972
+ // (d) => d.id === match.id,
973
+ // )
974
+
975
+ // invariant(
976
+ // dehydratedMatch,
977
+ // `Could not find a client-side match for dehydrated match with id: ${match.id}!`,
978
+ // )
979
+
980
+ // if (dehydratedMatch) {
981
+ // return {
982
+ // ...match,
983
+ // ...dehydratedMatch,
984
+ // }
985
+ // }
986
+ // return match
987
+ // })
988
+
989
+ // this.setState((s) => {
990
+ // return {
991
+ // ...s,
992
+ // matches: dehydratedState.dehydratedMatches as any,
993
+ // }
994
+ // })
995
+ // }
2303
996
 
2304
- const handleEnter = e => {
2305
- const target = e.target || {};
997
+ // resolveMatchPromise = (matchId: string, key: string, value: any) => {
998
+ // state.matches
999
+ // .find((d) => d.id === matchId)
1000
+ // ?.__promisesByKey[key]?.resolve(value)
1001
+ // }
2306
1002
 
2307
- if (preload) {
2308
- if (target.preloadTimeout) {
2309
- return;
2310
- }
1003
+ // setRouteMatch = (
1004
+ // id: string,
1005
+ // pending: boolean,
1006
+ // updater: NonNullableUpdater<RouteMatch<TRouteTree>>,
1007
+ // ) => {
1008
+ // const key = pending ? 'pendingMatches' : 'matches'
1009
+
1010
+ // this.setState((prev) => {
1011
+ // return {
1012
+ // ...prev,
1013
+ // [key]: prev[key].map((d) => {
1014
+ // if (d.id === id) {
1015
+ // return functionalUpdate(updater, d)
1016
+ // }
1017
+
1018
+ // return d
1019
+ // }),
1020
+ // }
1021
+ // })
1022
+ // }
2311
1023
 
2312
- target.preloadTimeout = setTimeout(() => {
2313
- target.preloadTimeout = null;
2314
- router.preloadRoute(nextOpts, {
2315
- maxAge: userPreloadMaxAge,
2316
- gcMaxAge: userPreloadGcMaxAge
2317
- });
2318
- }, preloadDelay);
2319
- }
2320
- };
1024
+ // setPendingRouteMatch = (
1025
+ // id: string,
1026
+ // updater: NonNullableUpdater<RouteMatch<TRouteTree>>,
1027
+ // ) => {
1028
+ // this.setRouteMatch(id, true, updater)
1029
+ // }
1030
+ }
2321
1031
 
2322
- const handleLeave = e => {
2323
- const target = e.target || {};
1032
+ // A function that takes an import() argument which is a function and returns a new function that will
1033
+ // proxy arguments from the caller to the imported function, retaining all type
1034
+ // information along the way
1035
+ function lazyFn(fn, key) {
1036
+ return async (...args) => {
1037
+ const imported = await fn();
1038
+ return imported[key || 'default'](...args);
1039
+ };
1040
+ }
2324
1041
 
2325
- if (target.preloadTimeout) {
2326
- clearTimeout(target.preloadTimeout);
2327
- target.preloadTimeout = null;
2328
- }
1042
+ const routerContext = /*#__PURE__*/React.createContext(null);
1043
+ if (typeof document !== 'undefined') {
1044
+ window.__TSR_ROUTER_CONTEXT__ = routerContext;
1045
+ }
1046
+ const preloadWarning = 'Error preloading route! ☝️';
1047
+ function isCtrlEvent(e) {
1048
+ return !!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey);
1049
+ }
1050
+ class SearchParamError extends Error {}
1051
+ class PathParamError extends Error {}
1052
+ function getInitialRouterState(location) {
1053
+ return {
1054
+ status: 'idle',
1055
+ resolvedLocation: location,
1056
+ location,
1057
+ matches: [],
1058
+ pendingMatches: [],
1059
+ lastUpdated: Date.now()
1060
+ };
1061
+ }
1062
+ function RouterProvider({
1063
+ router,
1064
+ ...rest
1065
+ }) {
1066
+ const options = {
1067
+ ...router.options,
1068
+ ...rest,
1069
+ context: {
1070
+ ...router.options.context,
1071
+ ...rest?.context
1072
+ }
1073
+ };
1074
+ const history = React.useState(() => options.history ?? createBrowserHistory())[0];
1075
+ const tempLocationKeyRef = React.useRef(`${Math.round(Math.random() * 10000000)}`);
1076
+ const resetNextScrollRef = React.useRef(true);
1077
+ const navigateTimeoutRef = React.useRef(null);
1078
+ const latestLoadPromiseRef = React.useRef(Promise.resolve());
1079
+ const checkLatest = promise => {
1080
+ return latestLoadPromiseRef.current !== promise ? latestLoadPromiseRef.current : undefined;
1081
+ };
1082
+ const parseLocation = useStableCallback(previousLocation => {
1083
+ const parse = ({
1084
+ pathname,
1085
+ search,
1086
+ hash,
1087
+ state
1088
+ }) => {
1089
+ const parsedSearch = options.parseSearch(search);
1090
+ return {
1091
+ pathname: pathname,
1092
+ searchStr: search,
1093
+ search: replaceEqualDeep(previousLocation?.search, parsedSearch),
1094
+ hash: hash.split('#').reverse()[0] ?? '',
1095
+ href: `${pathname}${search}${hash}`,
1096
+ state: replaceEqualDeep(previousLocation?.state, state)
2329
1097
  };
2330
-
1098
+ };
1099
+ const location = parse(history.location);
1100
+ let {
1101
+ __tempLocation,
1102
+ __tempKey
1103
+ } = location.state;
1104
+ if (__tempLocation && (!__tempKey || __tempKey === tempLocationKeyRef.current)) {
1105
+ // Sync up the location keys
1106
+ const parsedTempLocation = parse(__tempLocation);
1107
+ parsedTempLocation.state.key = location.state.key;
1108
+ delete parsedTempLocation.state.__tempLocation;
2331
1109
  return {
2332
- type: 'internal',
2333
- next,
2334
- handleFocus,
2335
- handleClick,
2336
- handleEnter,
2337
- handleLeave,
2338
- isActive,
2339
- disabled
1110
+ ...parsedTempLocation,
1111
+ maskedLocation: location
2340
1112
  };
2341
- },
2342
- buildNext: opts => {
2343
- const next = router.__.buildLocation(opts);
2344
-
2345
- const matches = router.matchRoutes(next.pathname);
2346
-
2347
- const __preSearchFilters = matches.map(match => {
2348
- var _match$options$preSea;
2349
-
2350
- return (_match$options$preSea = match.options.preSearchFilters) != null ? _match$options$preSea : [];
2351
- }).flat().filter(Boolean);
2352
-
2353
- const __postSearchFilters = matches.map(match => {
2354
- var _match$options$postSe;
2355
-
2356
- return (_match$options$postSe = match.options.postSearchFilters) != null ? _match$options$postSe : [];
2357
- }).flat().filter(Boolean);
2358
-
2359
- return router.__.buildLocation(_extends({}, opts, {
2360
- __preSearchFilters,
2361
- __postSearchFilters
1113
+ }
1114
+ return location;
1115
+ });
1116
+ const latestLocationRef = React.useRef(parseLocation());
1117
+ const [preState, setState] = React.useState(() => getInitialRouterState(latestLocationRef.current));
1118
+ const [isTransitioning, startReactTransition] = React.useTransition();
1119
+ const pendingMatchesRef = React.useRef([]);
1120
+ const state = React.useMemo(() => ({
1121
+ ...preState,
1122
+ status: isTransitioning ? 'pending' : 'idle',
1123
+ location: isTransitioning ? latestLocationRef.current : preState.location,
1124
+ pendingMatches: pendingMatchesRef.current
1125
+ }), [preState, isTransitioning]);
1126
+ React.useLayoutEffect(() => {
1127
+ if (!isTransitioning && state.resolvedLocation !== state.location) {
1128
+ router.emit({
1129
+ type: 'onResolved',
1130
+ fromLocation: state.resolvedLocation,
1131
+ toLocation: state.location,
1132
+ pathChanged: state.location.href !== state.resolvedLocation?.href
1133
+ });
1134
+ pendingMatchesRef.current = [];
1135
+ setState(s => ({
1136
+ ...s,
1137
+ resolvedLocation: s.location
2362
1138
  }));
2363
- },
2364
- __: {
2365
- buildRouteTree: rootRouteConfig => {
2366
- const recurseRoutes = (routeConfigs, parent) => {
2367
- return routeConfigs.map(routeConfig => {
2368
- const routeOptions = routeConfig.options;
2369
- const route = createRoute(routeConfig, routeOptions, parent, router);
2370
- const existingRoute = router.routesById[route.routeId];
2371
-
2372
- if (existingRoute) {
2373
- if (process.env.NODE_ENV !== 'production') {
2374
- console.warn("Duplicate routes found with id: " + String(route.routeId), router.routesById, route);
2375
- }
2376
-
2377
- throw new Error();
2378
- }
2379
- router.routesById[route.routeId] = route;
2380
- const children = routeConfig.children;
2381
- route.childRoutes = children != null && children.length ? recurseRoutes(children, route) : undefined;
2382
- return route;
2383
- });
2384
- };
2385
-
2386
- const routes = recurseRoutes([rootRouteConfig]);
2387
- return routes[0];
2388
- },
2389
- parseLocation: (location, previousLocation) => {
2390
- var _location$hash$split$;
2391
-
2392
- const parsedSearch = router.options.parseSearch(location.search);
2393
- return {
2394
- pathname: location.pathname,
2395
- searchStr: location.search,
2396
- search: replaceEqualDeep(previousLocation == null ? void 0 : previousLocation.search, parsedSearch),
2397
- hash: (_location$hash$split$ = location.hash.split('#').reverse()[0]) != null ? _location$hash$split$ : '',
2398
- href: "" + location.pathname + location.search + location.hash,
2399
- state: location.state,
2400
- key: location.key
2401
- };
2402
- },
2403
- navigate: location => {
2404
- const next = router.buildNext(location);
2405
- return router.__.commitLocation(next, location.replace);
2406
- },
2407
- buildLocation: function buildLocation(dest) {
2408
- var _dest$from, _router$basepath, _dest$to, _last, _dest$params, _dest$__preSearchFilt, _functionalUpdate, _dest$__preSearchFilt2, _dest$__postSearchFil;
2409
-
2410
- if (dest === void 0) {
2411
- dest = {};
1139
+ }
1140
+ });
1141
+ const basepath = `/${trimPath(options.basepath ?? '') ?? ''}`;
1142
+ const resolvePathWithBase = useStableCallback((from, path) => {
1143
+ return resolvePath(basepath, from, cleanPath(path));
1144
+ });
1145
+ const [routesById, routesByPath] = React.useMemo(() => {
1146
+ const routesById = {};
1147
+ const routesByPath = {};
1148
+ const recurseRoutes = routes => {
1149
+ routes.forEach((route, i) => {
1150
+ route.init({
1151
+ originalIndex: i
1152
+ });
1153
+ const existingRoute = routesById[route.id];
1154
+ invariant(!existingRoute, `Duplicate routes found with id: ${String(route.id)}`);
1155
+ routesById[route.id] = route;
1156
+ if (!route.isRoot && route.path) {
1157
+ const trimmedFullPath = trimPathRight(route.fullPath);
1158
+ if (!routesByPath[trimmedFullPath] || route.fullPath.endsWith('/')) {
1159
+ routesByPath[trimmedFullPath] = route;
1160
+ }
2412
1161
  }
1162
+ const children = route.children;
1163
+ if (children?.length) {
1164
+ recurseRoutes(children);
1165
+ }
1166
+ });
1167
+ };
1168
+ recurseRoutes([router.routeTree]);
1169
+ return [routesById, routesByPath];
1170
+ }, []);
1171
+ const looseRoutesById = routesById;
1172
+ const flatRoutes = React.useMemo(() => Object.values(routesByPath).map((d, i) => {
1173
+ const trimmed = trimPath(d.fullPath);
1174
+ const parsed = parsePathname(trimmed);
1175
+ while (parsed.length > 1 && parsed[0]?.value === '/') {
1176
+ parsed.shift();
1177
+ }
1178
+ const score = parsed.map(d => {
1179
+ if (d.type === 'param') {
1180
+ return 0.5;
1181
+ }
1182
+ if (d.type === 'wildcard') {
1183
+ return 0.25;
1184
+ }
1185
+ return 1;
1186
+ });
1187
+ return {
1188
+ child: d,
1189
+ trimmed,
1190
+ parsed,
1191
+ index: i,
1192
+ score
1193
+ };
1194
+ }).sort((a, b) => {
1195
+ let isIndex = a.trimmed === '/' ? 1 : b.trimmed === '/' ? -1 : 0;
1196
+ if (isIndex !== 0) return isIndex;
1197
+ const length = Math.min(a.score.length, b.score.length);
1198
+
1199
+ // Sort by length of score
1200
+ if (a.score.length !== b.score.length) {
1201
+ return b.score.length - a.score.length;
1202
+ }
2413
1203
 
2414
- // const resolvedFrom: Location = {
2415
- // ...router.location,
2416
- const fromPathname = dest.fromCurrent ? router.location.pathname : (_dest$from = dest.from) != null ? _dest$from : router.location.pathname;
1204
+ // Sort by min available score
1205
+ for (let i = 0; i < length; i++) {
1206
+ if (a.score[i] !== b.score[i]) {
1207
+ return b.score[i] - a.score[i];
1208
+ }
1209
+ }
2417
1210
 
2418
- let pathname = resolvePath((_router$basepath = router.basepath) != null ? _router$basepath : '/', fromPathname, "" + ((_dest$to = dest.to) != null ? _dest$to : '.'));
1211
+ // Sort by min available parsed value
1212
+ for (let i = 0; i < length; i++) {
1213
+ if (a.parsed[i].value !== b.parsed[i].value) {
1214
+ return a.parsed[i].value > b.parsed[i].value ? 1 : -1;
1215
+ }
1216
+ }
2419
1217
 
2420
- const fromMatches = router.matchRoutes(router.location.pathname, {
2421
- strictParseParams: true
2422
- });
2423
- const toMatches = router.matchRoutes(pathname);
1218
+ // Sort by length of trimmed full path
1219
+ if (a.trimmed !== b.trimmed) {
1220
+ return a.trimmed > b.trimmed ? 1 : -1;
1221
+ }
2424
1222
 
2425
- const prevParams = _extends({}, (_last = last(fromMatches)) == null ? void 0 : _last.params);
1223
+ // Sort by original index
1224
+ return a.index - b.index;
1225
+ }).map((d, i) => {
1226
+ d.child.rank = i;
1227
+ return d.child;
1228
+ }), [routesByPath]);
1229
+ const matchRoutes = useStableCallback((pathname, locationSearch, opts) => {
1230
+ let routeParams = {};
1231
+ let foundRoute = flatRoutes.find(route => {
1232
+ const matchedParams = matchPathname(basepath, trimPathRight(pathname), {
1233
+ to: route.fullPath,
1234
+ caseSensitive: route.options.caseSensitive ?? options.caseSensitive,
1235
+ fuzzy: false
1236
+ });
1237
+ if (matchedParams) {
1238
+ routeParams = matchedParams;
1239
+ return true;
1240
+ }
1241
+ return false;
1242
+ });
1243
+ let routeCursor = foundRoute || routesById['__root__'];
1244
+ let matchedRoutes = [routeCursor];
1245
+ // let includingLayouts = true
1246
+ while (routeCursor?.parentRoute) {
1247
+ routeCursor = routeCursor.parentRoute;
1248
+ if (routeCursor) matchedRoutes.unshift(routeCursor);
1249
+ }
2426
1250
 
2427
- let nextParams = ((_dest$params = dest.params) != null ? _dest$params : true) === true ? prevParams : functionalUpdate(dest.params, prevParams);
1251
+ // Existing matches are matches that are already loaded along with
1252
+ // pending matches that are still loading
2428
1253
 
2429
- if (nextParams) {
2430
- toMatches.map(d => d.options.stringifyParams).filter(Boolean).forEach(fn => {
2431
- Object.assign({}, nextParams, fn(nextParams));
1254
+ const parseErrors = matchedRoutes.map(route => {
1255
+ let parsedParamsError;
1256
+ if (route.options.parseParams) {
1257
+ try {
1258
+ const parsedParams = route.options.parseParams(routeParams);
1259
+ // Add the parsed params to the accumulated params bag
1260
+ Object.assign(routeParams, parsedParams);
1261
+ } catch (err) {
1262
+ parsedParamsError = new PathParamError(err.message, {
1263
+ cause: err
2432
1264
  });
1265
+ if (opts?.throwOnError) {
1266
+ throw parsedParamsError;
1267
+ }
1268
+ return parsedParamsError;
2433
1269
  }
2434
-
2435
- pathname = interpolatePath(pathname, nextParams != null ? nextParams : {}); // Pre filters first
2436
-
2437
- const preFilteredSearch = (_dest$__preSearchFilt = dest.__preSearchFilters) != null && _dest$__preSearchFilt.length ? dest.__preSearchFilters.reduce((prev, next) => next(prev), router.location.search) : router.location.search; // Then the link/navigate function
2438
-
2439
- const destSearch = dest.search === true ? preFilteredSearch // Preserve resolvedFrom true
2440
- : dest.search ? (_functionalUpdate = functionalUpdate(dest.search, preFilteredSearch)) != null ? _functionalUpdate : {} // Updater
2441
- : (_dest$__preSearchFilt2 = dest.__preSearchFilters) != null && _dest$__preSearchFilt2.length ? preFilteredSearch // Preserve resolvedFrom filters
2442
- : {}; // Then post filters
2443
-
2444
- const postFilteredSearch = (_dest$__postSearchFil = dest.__postSearchFilters) != null && _dest$__postSearchFil.length ? dest.__postSearchFilters.reduce((prev, next) => next(prev), destSearch) : destSearch;
2445
- const search = replaceEqualDeep(router.location.search, postFilteredSearch);
2446
- const searchStr = router.options.stringifySearch(search);
2447
- let hash = dest.hash === true ? router.location.hash : functionalUpdate(dest.hash, router.location.hash);
2448
- hash = hash ? "#" + hash : '';
1270
+ }
1271
+ return;
1272
+ });
1273
+ const matches = matchedRoutes.map((route, index) => {
1274
+ const interpolatedPath = interpolatePath(route.path, routeParams);
1275
+ const matchId = interpolatePath(route.id, routeParams, true);
1276
+
1277
+ // Waste not, want not. If we already have a match for this route,
1278
+ // reuse it. This is important for layout routes, which might stick
1279
+ // around between navigation actions that only change leaf routes.
1280
+ const existingMatch = getRouteMatch(state, matchId);
1281
+ if (existingMatch) {
2449
1282
  return {
2450
- pathname,
2451
- search,
2452
- searchStr,
2453
- state: router.location.state,
2454
- hash,
2455
- href: "" + pathname + searchStr + hash,
2456
- key: dest.key
1283
+ ...existingMatch
2457
1284
  };
2458
- },
2459
- commitLocation: (next, replace) => {
2460
- const id = '' + Date.now() + Math.random();
2461
- if (router.navigateTimeout) clearTimeout(router.navigateTimeout);
2462
- let nextAction = 'replace';
2463
-
2464
- if (!replace) {
2465
- nextAction = 'push';
2466
- }
2467
-
2468
- const isSameUrl = router.__.parseLocation(history.location).href === next.href;
1285
+ }
2469
1286
 
2470
- if (isSameUrl && !next.key) {
2471
- nextAction = 'replace';
2472
- }
1287
+ // Create a fresh route match
1288
+ const hasLoaders = !!(route.options.loader || componentTypes.some(d => route.options[d]?.preload));
1289
+ const routeMatch = {
1290
+ id: matchId,
1291
+ routeId: route.id,
1292
+ params: routeParams,
1293
+ pathname: joinPaths([basepath, interpolatedPath]),
1294
+ updatedAt: Date.now(),
1295
+ routeSearch: {},
1296
+ search: {},
1297
+ status: hasLoaders ? 'pending' : 'success',
1298
+ isFetching: false,
1299
+ invalid: false,
1300
+ error: undefined,
1301
+ paramsError: parseErrors[index],
1302
+ searchError: undefined,
1303
+ loadPromise: Promise.resolve(),
1304
+ context: undefined,
1305
+ abortController: new AbortController(),
1306
+ shouldReloadDeps: undefined,
1307
+ fetchedAt: 0
1308
+ };
1309
+ return routeMatch;
1310
+ });
2473
1311
 
2474
- if (nextAction === 'replace') {
2475
- history.replace({
2476
- pathname: next.pathname,
2477
- hash: next.hash,
2478
- search: next.searchStr
2479
- }, _extends({
2480
- id
2481
- }, next.state));
2482
- } else {
2483
- history.push({
2484
- pathname: next.pathname,
2485
- hash: next.hash,
2486
- search: next.searchStr
2487
- }, {
2488
- id
1312
+ // Take each match and resolve its search params and context
1313
+ // This has to happen after the matches are created or found
1314
+ // so that we can use the parent match's search params and context
1315
+ matches.forEach((match, i) => {
1316
+ const parentMatch = matches[i - 1];
1317
+ const route = looseRoutesById[match.routeId];
1318
+ const searchInfo = (() => {
1319
+ // Validate the search params and stabilize them
1320
+ const parentSearchInfo = {
1321
+ search: parentMatch?.search ?? locationSearch,
1322
+ routeSearch: parentMatch?.routeSearch ?? locationSearch
1323
+ };
1324
+ try {
1325
+ const validator = typeof route.options.validateSearch === 'object' ? route.options.validateSearch.parse : route.options.validateSearch;
1326
+ let routeSearch = validator?.(parentSearchInfo.search) ?? {};
1327
+ let search = {
1328
+ ...parentSearchInfo.search,
1329
+ ...routeSearch
1330
+ };
1331
+ routeSearch = replaceEqualDeep(match.routeSearch, routeSearch);
1332
+ search = replaceEqualDeep(match.search, search);
1333
+ return {
1334
+ routeSearch,
1335
+ search,
1336
+ searchDidChange: match.routeSearch !== routeSearch
1337
+ };
1338
+ } catch (err) {
1339
+ match.searchError = new SearchParamError(err.message, {
1340
+ cause: err
2489
1341
  });
1342
+ if (opts?.throwOnError) {
1343
+ throw match.searchError;
1344
+ }
1345
+ return parentSearchInfo;
2490
1346
  }
2491
-
2492
- router.navigationPromise = new Promise(resolve => {
2493
- const previousNavigationResolve = router.resolveNavigation;
2494
-
2495
- router.resolveNavigation = () => {
2496
- previousNavigationResolve();
2497
- resolve();
1347
+ })();
1348
+ Object.assign(match, searchInfo);
1349
+ });
1350
+ return matches;
1351
+ });
1352
+ const cancelMatch = useStableCallback(id => {
1353
+ getRouteMatch(state, id)?.abortController?.abort();
1354
+ });
1355
+ const cancelMatches = useStableCallback(state => {
1356
+ state.matches.forEach(match => {
1357
+ cancelMatch(match.id);
1358
+ });
1359
+ });
1360
+ const buildLocation = useStableCallback(opts => {
1361
+ const build = (dest = {}, matches) => {
1362
+ const from = latestLocationRef.current;
1363
+ const fromPathname = dest.from ?? from.pathname;
1364
+ let pathname = resolvePathWithBase(fromPathname, `${dest.to ?? ''}`);
1365
+ const fromMatches = matchRoutes(fromPathname, from.search);
1366
+ const stayingMatches = matches?.filter(d => fromMatches?.find(e => e.routeId === d.routeId));
1367
+ const prevParams = {
1368
+ ...last(fromMatches)?.params
1369
+ };
1370
+ let nextParams = (dest.params ?? true) === true ? prevParams : functionalUpdate(dest.params, prevParams);
1371
+ if (nextParams) {
1372
+ matches?.map(d => looseRoutesById[d.routeId].options.stringifyParams).filter(Boolean).forEach(fn => {
1373
+ nextParams = {
1374
+ ...nextParams,
1375
+ ...fn(nextParams)
2498
1376
  };
2499
1377
  });
2500
- return router.navigationPromise;
2501
1378
  }
1379
+ pathname = interpolatePath(pathname, nextParams ?? {});
1380
+ const preSearchFilters = stayingMatches?.map(match => looseRoutesById[match.routeId].options.preSearchFilters ?? []).flat().filter(Boolean) ?? [];
1381
+ const postSearchFilters = stayingMatches?.map(match => looseRoutesById[match.routeId].options.postSearchFilters ?? []).flat().filter(Boolean) ?? [];
1382
+
1383
+ // Pre filters first
1384
+ const preFilteredSearch = preSearchFilters?.length ? preSearchFilters?.reduce((prev, next) => next(prev), from.search) : from.search;
1385
+
1386
+ // Then the link/navigate function
1387
+ const destSearch = dest.search === true ? preFilteredSearch // Preserve resolvedFrom true
1388
+ : dest.search ? functionalUpdate(dest.search, preFilteredSearch) ?? {} // Updater
1389
+ : preSearchFilters?.length ? preFilteredSearch // Preserve resolvedFrom filters
1390
+ : {};
1391
+
1392
+ // Then post filters
1393
+ const postFilteredSearch = postSearchFilters?.length ? postSearchFilters.reduce((prev, next) => next(prev), destSearch) : destSearch;
1394
+ const search = replaceEqualDeep(from.search, postFilteredSearch);
1395
+ const searchStr = options.stringifySearch(search);
1396
+ const hash = dest.hash === true ? from.hash : dest.hash ? functionalUpdate(dest.hash, from.hash) : from.hash;
1397
+ const hashStr = hash ? `#${hash}` : '';
1398
+ let nextState = dest.state === true ? from.state : dest.state ? functionalUpdate(dest.state, from.state) : from.state;
1399
+ nextState = replaceEqualDeep(from.state, nextState);
1400
+ return {
1401
+ pathname,
1402
+ search,
1403
+ searchStr,
1404
+ state: nextState,
1405
+ hash,
1406
+ href: history.createHref(`${pathname}${searchStr}${hashStr}`),
1407
+ unmaskOnReload: dest.unmaskOnReload
1408
+ };
1409
+ };
1410
+ const buildWithMatches = (dest = {}, maskedDest) => {
1411
+ let next = build(dest);
1412
+ let maskedNext = maskedDest ? build(maskedDest) : undefined;
1413
+ if (!maskedNext) {
1414
+ let params = {};
1415
+ let foundMask = options.routeMasks?.find(d => {
1416
+ const match = matchPathname(basepath, next.pathname, {
1417
+ to: d.from,
1418
+ caseSensitive: false,
1419
+ fuzzy: false
1420
+ });
1421
+ if (match) {
1422
+ params = match;
1423
+ return true;
1424
+ }
1425
+ return false;
1426
+ });
1427
+ if (foundMask) {
1428
+ foundMask = {
1429
+ ...foundMask,
1430
+ from: interpolatePath(foundMask.from, params)
1431
+ };
1432
+ maskedDest = foundMask;
1433
+ maskedNext = build(maskedDest);
1434
+ }
1435
+ }
1436
+ const nextMatches = matchRoutes(next.pathname, next.search);
1437
+ const maskedMatches = maskedNext ? matchRoutes(maskedNext.pathname, maskedNext.search) : undefined;
1438
+ const maskedFinal = maskedNext ? build(maskedDest, maskedMatches) : undefined;
1439
+ const final = build(dest, nextMatches);
1440
+ if (maskedFinal) {
1441
+ final.maskedLocation = maskedFinal;
1442
+ }
1443
+ return final;
1444
+ };
1445
+ if (opts.mask) {
1446
+ return buildWithMatches(opts, {
1447
+ ...pick(opts, ['from']),
1448
+ ...opts.mask
1449
+ });
2502
1450
  }
2503
- };
2504
- router.update(userOptions); // Allow frameworks to hook into the router creation
2505
-
2506
- router.options.createRouter == null ? void 0 : router.options.createRouter(router);
2507
- return router;
2508
- }
2509
-
2510
- function isCtrlEvent(e) {
2511
- return !!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey);
2512
- }
2513
-
2514
- function cascadeLoaderData(matches) {
2515
- matches.forEach((match, index) => {
2516
- const parent = matches[index - 1];
2517
-
2518
- if (parent) {
2519
- match.loaderData = replaceEqualDeep(match.loaderData, _extends({}, parent.loaderData, match.routeLoaderData));
1451
+ return buildWithMatches(opts);
1452
+ });
1453
+ const commitLocation = useStableCallback(async ({
1454
+ startTransition,
1455
+ ...next
1456
+ }) => {
1457
+ if (navigateTimeoutRef.current) clearTimeout(navigateTimeoutRef.current);
1458
+ const isSameUrl = latestLocationRef.current.href === next.href;
1459
+
1460
+ // If the next urls are the same and we're not replacing,
1461
+ // do nothing
1462
+ if (!isSameUrl || !next.replace) {
1463
+ let {
1464
+ maskedLocation,
1465
+ ...nextHistory
1466
+ } = next;
1467
+ if (maskedLocation) {
1468
+ nextHistory = {
1469
+ ...maskedLocation,
1470
+ state: {
1471
+ ...maskedLocation.state,
1472
+ __tempKey: undefined,
1473
+ __tempLocation: {
1474
+ ...nextHistory,
1475
+ search: nextHistory.searchStr,
1476
+ state: {
1477
+ ...nextHistory.state,
1478
+ __tempKey: undefined,
1479
+ __tempLocation: undefined,
1480
+ key: undefined
1481
+ }
1482
+ }
1483
+ }
1484
+ };
1485
+ if (nextHistory.unmaskOnReload ?? options.unmaskOnReload ?? false) {
1486
+ nextHistory.state.__tempKey = tempLocationKeyRef.current;
1487
+ }
1488
+ }
1489
+ const apply = () => {
1490
+ history[next.replace ? 'replace' : 'push'](nextHistory.href, nextHistory.state);
1491
+ };
1492
+ if (startTransition ?? true) {
1493
+ startReactTransition(apply);
1494
+ } else {
1495
+ apply();
1496
+ }
2520
1497
  }
1498
+ resetNextScrollRef.current = next.resetScroll ?? true;
1499
+ return latestLoadPromiseRef.current;
2521
1500
  });
2522
- }
2523
-
2524
- const _excluded = ["type", "children", "target", "activeProps", "inactiveProps", "activeOptions", "disabled", "hash", "search", "params", "to", "preload", "preloadDelay", "preloadMaxAge", "replace", "style", "className", "onClick", "onFocus", "onMouseEnter", "onMouseLeave", "onTouchStart", "onTouchEnd"],
2525
- _excluded2 = ["pending", "caseSensitive", "children"],
2526
- _excluded3 = ["children", "router"];
2527
- function lazy(importer) {
2528
- const lazyComp = /*#__PURE__*/React.lazy(importer);
2529
- let promise;
2530
- let resolvedComp;
2531
- const forwardedComp = /*#__PURE__*/React.forwardRef((props, ref) => {
2532
- const resolvedCompRef = React.useRef(resolvedComp || lazyComp);
2533
- return /*#__PURE__*/React.createElement(resolvedCompRef.current, _extends$2({}, ref ? {
2534
- ref
2535
- } : {}, props));
1501
+ const buildAndCommitLocation = useStableCallback(({
1502
+ replace,
1503
+ resetScroll,
1504
+ startTransition,
1505
+ ...rest
1506
+ } = {}) => {
1507
+ const location = buildLocation(rest);
1508
+ return commitLocation({
1509
+ ...location,
1510
+ startTransition,
1511
+ replace,
1512
+ resetScroll
1513
+ });
2536
1514
  });
2537
- const finalComp = forwardedComp;
2538
-
2539
- finalComp.preload = () => {
2540
- if (!promise) {
2541
- promise = importer().then(module => {
2542
- resolvedComp = module.default;
2543
- return resolvedComp;
2544
- });
1515
+ const navigate = useStableCallback(({
1516
+ from,
1517
+ to = '',
1518
+ ...rest
1519
+ }) => {
1520
+ // If this link simply reloads the current route,
1521
+ // make sure it has a new key so it will trigger a data refresh
1522
+
1523
+ // If this `to` is a valid external URL, return
1524
+ // null for LinkUtils
1525
+ const toString = String(to);
1526
+ const fromString = typeof from === 'undefined' ? from : String(from);
1527
+ let isExternal;
1528
+ try {
1529
+ new URL(`${toString}`);
1530
+ isExternal = true;
1531
+ } catch (e) {}
1532
+ invariant(!isExternal, 'Attempting to navigate to external url with this.navigate!');
1533
+ return buildAndCommitLocation({
1534
+ ...rest,
1535
+ from: fromString,
1536
+ to: toString
1537
+ });
1538
+ });
1539
+ const loadMatches = useStableCallback(async ({
1540
+ checkLatest,
1541
+ matches,
1542
+ preload
1543
+ }) => {
1544
+ let latestPromise;
1545
+ let firstBadMatchIndex;
1546
+
1547
+ // Check each match middleware to see if the route can be accessed
1548
+ try {
1549
+ for (let [index, match] of matches.entries()) {
1550
+ const parentMatch = matches[index - 1];
1551
+ const route = looseRoutesById[match.routeId];
1552
+ const handleError = (err, code) => {
1553
+ err.routerCode = code;
1554
+ firstBadMatchIndex = firstBadMatchIndex ?? index;
1555
+ if (isRedirect(err)) {
1556
+ throw err;
1557
+ }
1558
+ try {
1559
+ route.options.onError?.(err);
1560
+ } catch (errorHandlerErr) {
1561
+ err = errorHandlerErr;
1562
+ if (isRedirect(errorHandlerErr)) {
1563
+ throw errorHandlerErr;
1564
+ }
1565
+ }
1566
+ matches[index] = match = {
1567
+ ...match,
1568
+ error: err,
1569
+ status: 'error',
1570
+ updatedAt: Date.now()
1571
+ };
1572
+ };
1573
+ try {
1574
+ if (match.paramsError) {
1575
+ handleError(match.paramsError, 'PARSE_PARAMS');
1576
+ }
1577
+ if (match.searchError) {
1578
+ handleError(match.searchError, 'VALIDATE_SEARCH');
1579
+ }
1580
+ const parentContext = parentMatch?.context ?? options.context ?? {};
1581
+ const beforeLoadContext = (await route.options.beforeLoad?.({
1582
+ search: match.search,
1583
+ abortController: match.abortController,
1584
+ params: match.params,
1585
+ preload: !!preload,
1586
+ context: parentContext,
1587
+ location: state.location,
1588
+ navigate: opts => navigate({
1589
+ ...opts,
1590
+ from: match.pathname
1591
+ }),
1592
+ buildLocation
1593
+ })) ?? {};
1594
+ const context = {
1595
+ ...parentContext,
1596
+ ...beforeLoadContext
1597
+ };
1598
+ matches[index] = match = {
1599
+ ...match,
1600
+ context: replaceEqualDeep(match.context, context)
1601
+ };
1602
+ } catch (err) {
1603
+ handleError(err, 'BEFORE_LOAD');
1604
+ break;
1605
+ }
1606
+ }
1607
+ } catch (err) {
1608
+ if (isRedirect(err)) {
1609
+ if (!preload) navigate(err);
1610
+ return matches;
1611
+ }
1612
+ throw err;
2545
1613
  }
1614
+ const validResolvedMatches = matches.slice(0, firstBadMatchIndex);
1615
+ const matchPromises = [];
1616
+ validResolvedMatches.forEach((match, index) => {
1617
+ matchPromises.push((async () => {
1618
+ const parentMatchPromise = matchPromises[index - 1];
1619
+ const route = looseRoutesById[match.routeId];
1620
+ const handleIfRedirect = err => {
1621
+ if (isRedirect(err)) {
1622
+ if (!preload) {
1623
+ navigate(err);
1624
+ }
1625
+ return true;
1626
+ }
1627
+ return false;
1628
+ };
1629
+ let loadPromise;
1630
+ matches[index] = match = {
1631
+ ...match,
1632
+ fetchedAt: Date.now(),
1633
+ invalid: false
1634
+ };
1635
+ if (match.isFetching) {
1636
+ loadPromise = getRouteMatch(state, match.id)?.loadPromise;
1637
+ } else {
1638
+ const cause = state.matches.find(d => d.id === match.id) ? 'stay' : 'enter';
1639
+ const loaderContext = {
1640
+ params: match.params,
1641
+ search: match.search,
1642
+ preload: !!preload,
1643
+ parentMatchPromise,
1644
+ abortController: match.abortController,
1645
+ context: match.context,
1646
+ location: state.location,
1647
+ navigate: opts => navigate({
1648
+ ...opts,
1649
+ from: match.pathname
1650
+ }),
1651
+ cause
1652
+ };
2546
1653
 
2547
- return promise;
2548
- };
2549
-
2550
- return finalComp;
2551
- }
2552
- //
2553
- function Link(props) {
2554
- const router = useRouter();
2555
- return /*#__PURE__*/React.createElement(router.Link, props);
2556
- }
2557
- const matchesContext = /*#__PURE__*/React.createContext(null);
2558
- const routerContext = /*#__PURE__*/React.createContext(null);
2559
- function MatchesProvider(props) {
2560
- return /*#__PURE__*/React.createElement(matchesContext.Provider, props);
2561
- }
1654
+ // Default to reloading the route all the time
1655
+ let shouldReload = true;
1656
+ if (cause !== 'enter') {
1657
+ let shouldReloadDeps = typeof route.options.shouldReload === 'function' ? route.options.shouldReload?.(loaderContext) : !!(route.options.shouldReload ?? true);
1658
+ if (typeof shouldReloadDeps === 'object') {
1659
+ // compare the deps to see if they've changed
1660
+ shouldReload = !deepEqual(shouldReloadDeps, match.shouldReloadDeps);
1661
+ match.shouldReloadDeps = shouldReloadDeps;
1662
+ } else {
1663
+ shouldReload = !!shouldReloadDeps;
1664
+ }
1665
+ }
2562
1666
 
2563
- const useRouterSubscription = router => {
2564
- useSyncExternalStore(cb => router.subscribe(() => cb()), () => router.state, () => router.state);
2565
- };
1667
+ // If the user doesn't want the route to reload, just
1668
+ // resolve with the existing loader data
2566
1669
 
2567
- function createReactRouter(opts) {
2568
- const makeRouteExt = (route, router) => {
2569
- return {
2570
- useRoute: function useRoute(subRouteId) {
2571
- if (subRouteId === void 0) {
2572
- subRouteId = '.';
1670
+ if (!shouldReload) {
1671
+ loadPromise = Promise.resolve(match.loaderData);
1672
+ } else {
1673
+ // Otherwise, load the route
1674
+ matches[index] = match = {
1675
+ ...match,
1676
+ isFetching: true
1677
+ };
1678
+ const componentsPromise = Promise.all(componentTypes.map(async type => {
1679
+ const component = route.options[type];
1680
+ if (component?.preload) {
1681
+ await component.preload();
1682
+ }
1683
+ }));
1684
+ const loaderPromise = route.options.loader?.(loaderContext);
1685
+ loadPromise = Promise.all([componentsPromise, loaderPromise]).then(d => d[1]);
1686
+ }
2573
1687
  }
2574
-
2575
- const resolvedRouteId = router.resolvePath(route.routeId, subRouteId);
2576
- const resolvedRoute = router.getRoute(resolvedRouteId);
2577
- useRouterSubscription(router);
2578
- invariant(resolvedRoute, "Could not find a route for route \"" + resolvedRouteId + "\"! Did you forget to add it to your route config?");
2579
- return resolvedRoute;
2580
- },
2581
- linkProps: options => {
2582
- var _functionalUpdate, _functionalUpdate2;
2583
-
2584
- const {
2585
- // custom props
2586
- target,
2587
- activeProps = () => ({
2588
- className: 'active'
2589
- }),
2590
- inactiveProps = () => ({}),
2591
- disabled,
2592
- // element props
2593
- style,
2594
- className,
2595
- onClick,
2596
- onFocus,
2597
- onMouseEnter,
2598
- onMouseLeave
2599
- } = options,
2600
- rest = _objectWithoutPropertiesLoose(options, _excluded);
2601
-
2602
- const linkInfo = route.buildLink(options);
2603
-
2604
- if (linkInfo.type === 'external') {
2605
- const {
2606
- href
2607
- } = linkInfo;
2608
- return {
2609
- href
1688
+ matches[index] = match = {
1689
+ ...match,
1690
+ loadPromise
1691
+ };
1692
+ if (!preload) {
1693
+ setState(s => ({
1694
+ ...s,
1695
+ matches: s.matches.map(d => d.id === match.id ? match : d)
1696
+ }));
1697
+ }
1698
+ try {
1699
+ const loaderData = await loadPromise;
1700
+ if (latestPromise = checkLatest()) return await latestPromise;
1701
+ matches[index] = match = {
1702
+ ...match,
1703
+ error: undefined,
1704
+ status: 'success',
1705
+ isFetching: false,
1706
+ updatedAt: Date.now(),
1707
+ loaderData,
1708
+ loadPromise: undefined
1709
+ };
1710
+ } catch (error) {
1711
+ if (latestPromise = checkLatest()) return await latestPromise;
1712
+ if (handleIfRedirect(error)) return;
1713
+ try {
1714
+ route.options.onError?.(error);
1715
+ } catch (onErrorError) {
1716
+ error = onErrorError;
1717
+ if (handleIfRedirect(onErrorError)) return;
1718
+ }
1719
+ matches[index] = match = {
1720
+ ...match,
1721
+ error,
1722
+ status: 'error',
1723
+ isFetching: false,
1724
+ updatedAt: Date.now()
2610
1725
  };
2611
1726
  }
1727
+ if (!preload) {
1728
+ setState(s => ({
1729
+ ...s,
1730
+ matches: s.matches.map(d => d.id === match.id ? match : d)
1731
+ }));
1732
+ }
1733
+ })());
1734
+ });
1735
+ await Promise.all(matchPromises);
1736
+ return matches;
1737
+ });
1738
+ const load = useStableCallback(async () => {
1739
+ const promise = new Promise(async (resolve, reject) => {
1740
+ const next = latestLocationRef.current;
1741
+ const prevLocation = state.resolvedLocation;
1742
+ const pathDidChange = prevLocation.href !== next.href;
1743
+ let latestPromise;
1744
+
1745
+ // Cancel any pending matches
1746
+ cancelMatches(state);
1747
+ router.emit({
1748
+ type: 'onBeforeLoad',
1749
+ fromLocation: prevLocation,
1750
+ toLocation: next,
1751
+ pathChanged: pathDidChange
1752
+ });
2612
1753
 
2613
- const {
2614
- handleClick,
2615
- handleFocus,
2616
- handleEnter,
2617
- handleLeave,
2618
- isActive,
2619
- next
2620
- } = linkInfo;
2621
-
2622
- const reactHandleClick = e => {
2623
- React.startTransition(() => {
2624
- handleClick(e);
1754
+ // Match the routes
1755
+ let matches = matchRoutes(next.pathname, next.search, {
1756
+ debug: true
1757
+ });
1758
+ pendingMatchesRef.current = matches;
1759
+ const previousMatches = state.matches;
1760
+
1761
+ // Ingest the new matches
1762
+ setState(s => ({
1763
+ ...s,
1764
+ status: 'pending',
1765
+ location: next,
1766
+ matches
1767
+ }));
1768
+ try {
1769
+ try {
1770
+ // Load the matches
1771
+ await loadMatches({
1772
+ matches,
1773
+ checkLatest: () => checkLatest(promise)
2625
1774
  });
2626
- };
1775
+ } catch (err) {
1776
+ // swallow this error, since we'll display the
1777
+ // errors on the route components
1778
+ }
2627
1779
 
2628
- const composeHandlers = handlers => e => {
2629
- e.persist();
2630
- handlers.forEach(handler => {
2631
- if (handler) handler(e);
1780
+ // Only apply the latest transition
1781
+ if (latestPromise = checkLatest(promise)) {
1782
+ return latestPromise;
1783
+ }
1784
+ const exitingMatchIds = previousMatches.filter(id => !pendingMatchesRef.current.includes(id));
1785
+ const enteringMatchIds = pendingMatchesRef.current.filter(id => !previousMatches.includes(id));
1786
+ const stayingMatchIds = previousMatches.filter(id => pendingMatchesRef.current.includes(id))
1787
+
1788
+ // setState((s) => ({
1789
+ // ...s,
1790
+ // status: 'idle',
1791
+ // resolvedLocation: s.location,
1792
+ // }))
1793
+
1794
+ //
1795
+ ;
1796
+ [[exitingMatchIds, 'onLeave'], [enteringMatchIds, 'onEnter'], [stayingMatchIds, 'onTransition']].forEach(([matches, hook]) => {
1797
+ matches.forEach(match => {
1798
+ looseRoutesById[match.routeId].options[hook]?.(match);
2632
1799
  });
2633
- }; // Get the active props
2634
-
2635
-
2636
- const resolvedActiveProps = isActive ? (_functionalUpdate = functionalUpdate(activeProps, {})) != null ? _functionalUpdate : {} : {}; // Get the inactive props
2637
-
2638
- const resolvedInactiveProps = isActive ? {} : (_functionalUpdate2 = functionalUpdate(inactiveProps, {})) != null ? _functionalUpdate2 : {};
2639
- return _extends$2({}, resolvedActiveProps, resolvedInactiveProps, rest, {
2640
- href: disabled ? undefined : next.href,
2641
- onClick: composeHandlers([reactHandleClick, onClick]),
2642
- onFocus: composeHandlers([handleFocus, onFocus]),
2643
- onMouseEnter: composeHandlers([handleEnter, onMouseEnter]),
2644
- onMouseLeave: composeHandlers([handleLeave, onMouseLeave]),
2645
- target,
2646
- style: _extends$2({}, style, resolvedActiveProps.style, resolvedInactiveProps.style),
2647
- className: [className, resolvedActiveProps.className, resolvedInactiveProps.className].filter(Boolean).join(' ') || undefined
2648
- }, disabled ? {
2649
- role: 'link',
2650
- 'aria-disabled': true
2651
- } : undefined, {
2652
- ['data-status']: isActive ? 'active' : undefined
2653
1800
  });
2654
- },
2655
- Link: /*#__PURE__*/React.forwardRef((props, ref) => {
2656
- const linkProps = route.linkProps(props);
2657
- useRouterSubscription(router);
2658
- return /*#__PURE__*/React.createElement("a", _extends$2({
2659
- ref: ref
2660
- }, linkProps, {
2661
- children: typeof props.children === 'function' ? props.children({
2662
- isActive: linkProps['data-status'] === 'active'
2663
- }) : props.children
2664
- }));
2665
- }),
2666
- MatchRoute: opts => {
2667
- const {
2668
- pending,
2669
- caseSensitive
2670
- } = opts,
2671
- rest = _objectWithoutPropertiesLoose(opts, _excluded2);
2672
-
2673
- const params = route.matchRoute(rest, {
2674
- pending,
2675
- caseSensitive
1801
+ router.emit({
1802
+ type: 'onLoad',
1803
+ fromLocation: prevLocation,
1804
+ toLocation: next,
1805
+ pathChanged: pathDidChange
2676
1806
  });
2677
-
2678
- if (!params) {
2679
- return null;
1807
+ resolve();
1808
+ } catch (err) {
1809
+ // Only apply the latest transition
1810
+ if (latestPromise = checkLatest(promise)) {
1811
+ return latestPromise;
2680
1812
  }
2681
-
2682
- return typeof opts.children === 'function' ? opts.children(params) : opts.children;
1813
+ reject(err);
1814
+ }
1815
+ });
1816
+ latestLoadPromiseRef.current = promise;
1817
+ return latestLoadPromiseRef.current;
1818
+ });
1819
+ const preloadRoute = useStableCallback(async (navigateOpts = state.location) => {
1820
+ let next = buildLocation(navigateOpts);
1821
+ let matches = matchRoutes(next.pathname, next.search, {
1822
+ throwOnError: true
1823
+ });
1824
+ await loadMatches({
1825
+ matches,
1826
+ preload: true,
1827
+ checkLatest: () => undefined
1828
+ });
1829
+ return [last(matches), matches];
1830
+ });
1831
+ const buildLink = useStableCallback(dest => {
1832
+ // If this link simply reloads the current route,
1833
+ // make sure it has a new key so it will trigger a data refresh
1834
+
1835
+ // If this `to` is a valid external URL, return
1836
+ // null for LinkUtils
1837
+
1838
+ const {
1839
+ to,
1840
+ preload: userPreload,
1841
+ preloadDelay: userPreloadDelay,
1842
+ activeOptions,
1843
+ disabled,
1844
+ target,
1845
+ replace,
1846
+ resetScroll,
1847
+ startTransition
1848
+ } = dest;
1849
+ try {
1850
+ new URL(`${to}`);
1851
+ return {
1852
+ type: 'external',
1853
+ href: to
1854
+ };
1855
+ } catch (e) {}
1856
+ const nextOpts = dest;
1857
+ const next = buildLocation(nextOpts);
1858
+ const preload = userPreload ?? options.defaultPreload;
1859
+ const preloadDelay = userPreloadDelay ?? options.defaultPreloadDelay ?? 0;
1860
+
1861
+ // Compare path/hash for matches
1862
+ const currentPathSplit = latestLocationRef.current.pathname.split('/');
1863
+ const nextPathSplit = next.pathname.split('/');
1864
+ const pathIsFuzzyEqual = nextPathSplit.every((d, i) => d === currentPathSplit[i]);
1865
+ // Combine the matches based on user options
1866
+ const pathTest = activeOptions?.exact ? latestLocationRef.current.pathname === next.pathname : pathIsFuzzyEqual;
1867
+ const hashTest = activeOptions?.includeHash ? latestLocationRef.current.hash === next.hash : true;
1868
+ const searchTest = activeOptions?.includeSearch ?? true ? deepEqual(latestLocationRef.current.search, next.search, true) : true;
1869
+
1870
+ // The final "active" test
1871
+ const isActive = pathTest && hashTest && searchTest;
1872
+
1873
+ // The click handler
1874
+ const handleClick = e => {
1875
+ if (!disabled && !isCtrlEvent(e) && !e.defaultPrevented && (!target || target === '_self') && e.button === 0) {
1876
+ e.preventDefault();
1877
+
1878
+ // All is well? Navigate!
1879
+ commitLocation({
1880
+ ...next,
1881
+ replace,
1882
+ resetScroll,
1883
+ startTransition
1884
+ });
2683
1885
  }
2684
1886
  };
2685
- };
2686
1887
 
2687
- const coreRouter = createRouter(_extends$2({}, opts, {
2688
- createRouter: router => {
2689
- const routerExt = {
2690
- useState: () => {
2691
- useRouterSubscription(router);
2692
- return router.state;
2693
- },
2694
- useMatch: (routeId, opts) => {
2695
- var _useMatches, _opts$strict;
2696
-
2697
- useRouterSubscription(router);
2698
- invariant(routeId !== rootRouteId, "\"" + rootRouteId + "\" cannot be used with useMatch! Did you mean to useRoute(\"" + rootRouteId + "\")?");
2699
- const runtimeMatch = (_useMatches = useMatches()) == null ? void 0 : _useMatches[0];
2700
- const match = router.state.matches.find(d => d.routeId === routeId);
2701
-
2702
- if ((_opts$strict = opts == null ? void 0 : opts.strict) != null ? _opts$strict : true) {
2703
- invariant(match, "Could not find an active match for \"" + routeId + "\"!");
2704
- invariant(runtimeMatch.routeId == (match == null ? void 0 : match.routeId), "useMatch(\"" + (match == null ? void 0 : match.routeId) + "\") is being called in a component that is meant to render the '" + runtimeMatch.routeId + "' route. Did you mean to 'useMatch(\"" + (match == null ? void 0 : match.routeId) + "\", { strict: false })' or 'useRoute(\"" + (match == null ? void 0 : match.routeId) + "\")' instead?");
2705
- }
2706
-
2707
- return match;
1888
+ // The click handler
1889
+ const handleFocus = e => {
1890
+ if (preload) {
1891
+ preloadRoute(nextOpts).catch(err => {
1892
+ console.warn(err);
1893
+ console.warn(preloadWarning);
1894
+ });
1895
+ }
1896
+ };
1897
+ const handleTouchStart = e => {
1898
+ preloadRoute(nextOpts).catch(err => {
1899
+ console.warn(err);
1900
+ console.warn(preloadWarning);
1901
+ });
1902
+ };
1903
+ const handleEnter = e => {
1904
+ const target = e.target || {};
1905
+ if (preload) {
1906
+ if (target.preloadTimeout) {
1907
+ return;
2708
1908
  }
2709
- };
2710
- const routeExt = makeRouteExt(router.getRoute(rootRouteId), router);
2711
- Object.assign(router, routerExt, routeExt);
2712
- },
2713
- createRoute: _ref => {
2714
- let {
2715
- router,
2716
- route
2717
- } = _ref;
2718
- const routeExt = makeRouteExt(route, router);
2719
- Object.assign(route, routeExt);
2720
- },
2721
- loadComponent: async component => {
2722
- if (component.preload && typeof document !== 'undefined') {
2723
- component.preload(); // return await component.preload()
1909
+ target.preloadTimeout = setTimeout(() => {
1910
+ target.preloadTimeout = null;
1911
+ preloadRoute(nextOpts).catch(err => {
1912
+ console.warn(err);
1913
+ console.warn(preloadWarning);
1914
+ });
1915
+ }, preloadDelay);
2724
1916
  }
2725
-
2726
- return component;
1917
+ };
1918
+ const handleLeave = e => {
1919
+ const target = e.target || {};
1920
+ if (target.preloadTimeout) {
1921
+ clearTimeout(target.preloadTimeout);
1922
+ target.preloadTimeout = null;
1923
+ }
1924
+ };
1925
+ return {
1926
+ type: 'internal',
1927
+ next,
1928
+ handleFocus,
1929
+ handleClick,
1930
+ handleEnter,
1931
+ handleLeave,
1932
+ handleTouchStart,
1933
+ isActive,
1934
+ disabled
1935
+ };
1936
+ });
1937
+ React.useLayoutEffect(() => {
1938
+ const unsub = history.subscribe(() => {
1939
+ latestLocationRef.current = parseLocation(latestLocationRef.current);
1940
+ if (state.location !== latestLocationRef.current) {
1941
+ startReactTransition(() => {
1942
+ try {
1943
+ load();
1944
+ } catch (err) {
1945
+ console.error(err);
1946
+ }
1947
+ });
1948
+ }
1949
+ });
1950
+ const nextLocation = buildLocation({
1951
+ search: true,
1952
+ params: true,
1953
+ hash: true,
1954
+ state: true
1955
+ });
1956
+ if (state.location.href !== nextLocation.href) {
1957
+ commitLocation({
1958
+ ...nextLocation,
1959
+ replace: true
1960
+ });
2727
1961
  }
2728
- }));
2729
- return coreRouter;
2730
- }
2731
- function RouterProvider(_ref2) {
2732
- let {
2733
- children,
2734
- router
2735
- } = _ref2,
2736
- rest = _objectWithoutPropertiesLoose(_ref2, _excluded3);
1962
+ return () => {
1963
+ unsub();
1964
+ };
1965
+ }, [history]);
1966
+ const matchRoute = useStableCallback((location, opts) => {
1967
+ location = {
1968
+ ...location,
1969
+ to: location.to ? resolvePathWithBase(location.from || '', location.to) : undefined
1970
+ };
1971
+ const next = buildLocation(location);
1972
+ if (opts?.pending && state.status !== 'pending') {
1973
+ return false;
1974
+ }
1975
+ const baseLocation = opts?.pending ? latestLocationRef.current : state.resolvedLocation;
2737
1976
 
2738
- router.update(rest);
2739
- useRouterSubscription(router);
2740
- React.useEffect(() => {
2741
- console.log('hello');
2742
- return router.mount();
2743
- }, [router]);
2744
- return /*#__PURE__*/React.createElement(routerContext.Provider, {
2745
- value: {
2746
- router: router
1977
+ // const baseLocation = state.resolvedLocation
1978
+
1979
+ if (!baseLocation) {
1980
+ return false;
1981
+ }
1982
+ const match = matchPathname(basepath, baseLocation.pathname, {
1983
+ ...opts,
1984
+ to: next.pathname
1985
+ });
1986
+ if (!match) {
1987
+ return false;
1988
+ }
1989
+ if (match && (opts?.includeSearch ?? true)) {
1990
+ return deepEqual(baseLocation.search, next.search, true) ? match : false;
1991
+ }
1992
+ return match;
1993
+ });
1994
+ const injectedHtmlRef = React.useRef([]);
1995
+ const injectHtml = useStableCallback(async html => {
1996
+ injectedHtmlRef.current.push(html);
1997
+ });
1998
+ const dehydrateData = useStableCallback((key, getData) => {
1999
+ if (typeof document === 'undefined') {
2000
+ const strKey = typeof key === 'string' ? key : JSON.stringify(key);
2001
+ injectHtml(async () => {
2002
+ const id = `__TSR_DEHYDRATED__${strKey}`;
2003
+ const data = typeof getData === 'function' ? await getData() : getData;
2004
+ return `<script id='${id}' suppressHydrationWarning>window["__TSR_DEHYDRATED__${escapeJSON(strKey)}"] = ${JSON.stringify(data)}
2005
+ ;(() => {
2006
+ var el = document.getElementById('${id}')
2007
+ el.parentElement.removeChild(el)
2008
+ })()
2009
+ </script>`;
2010
+ });
2011
+ return () => hydrateData(key);
2012
+ }
2013
+ return () => undefined;
2014
+ });
2015
+ const hydrateData = useStableCallback(key => {
2016
+ if (typeof document !== 'undefined') {
2017
+ const strKey = typeof key === 'string' ? key : JSON.stringify(key);
2018
+ return window[`__TSR_DEHYDRATED__${strKey}`];
2747
2019
  }
2748
- }, /*#__PURE__*/React.createElement(MatchesProvider, {
2749
- value: router.state.matches
2750
- }, children != null ? children : /*#__PURE__*/React.createElement(Outlet, null)));
2020
+ return undefined;
2021
+ });
2022
+ React.useLayoutEffect(() => {
2023
+ startReactTransition(() => {
2024
+ try {
2025
+ load();
2026
+ } catch (err) {
2027
+ console.error(err);
2028
+ }
2029
+ });
2030
+ }, []);
2031
+ const routerContextValue = {
2032
+ routeTree: router.routeTree,
2033
+ navigate,
2034
+ buildLink,
2035
+ state,
2036
+ matchRoute,
2037
+ routesById,
2038
+ options,
2039
+ history,
2040
+ load,
2041
+ buildLocation,
2042
+ subscribe: router.subscribe,
2043
+ resetNextScrollRef,
2044
+ injectedHtmlRef,
2045
+ injectHtml,
2046
+ dehydrateData,
2047
+ hydrateData
2048
+ };
2049
+ return /*#__PURE__*/React.createElement(routerContext.Provider, {
2050
+ value: routerContextValue
2051
+ }, /*#__PURE__*/React.createElement(Matches, null));
2751
2052
  }
2752
- function useRouter() {
2753
- const value = React.useContext(routerContext);
2754
- warning(!value, 'useRouter must be used inside a <Router> component!');
2755
- useRouterSubscription(value.router);
2756
- return value.router;
2053
+ function getRouteMatch(state, id) {
2054
+ return [...state.pendingMatches, ...state.matches].find(d => d.id === id);
2757
2055
  }
2758
- function useMatches() {
2759
- return React.useContext(matchesContext);
2056
+ function useRouterState(opts) {
2057
+ const {
2058
+ state
2059
+ } = useRouter();
2060
+ // return useStore(router.__store, opts?.select as any)
2061
+ return opts?.select ? opts.select(state) : state;
2760
2062
  }
2761
- function useMatch(routeId, opts) {
2762
- const router = useRouter();
2763
- return router.useMatch(routeId, opts);
2063
+ function useRouter() {
2064
+ const resolvedContext = window.__TSR_ROUTER_CONTEXT__ || routerContext;
2065
+ const value = React.useContext(resolvedContext);
2066
+ warning(value, 'useRouter must be used inside a <RouterProvider> component!');
2067
+ return value;
2764
2068
  }
2765
- function useNearestMatch() {
2766
- var _useMatches2;
2767
2069
 
2768
- const runtimeMatch = (_useMatches2 = useMatches()) == null ? void 0 : _useMatches2[0];
2769
- invariant(runtimeMatch, "Could not find a nearest match!");
2770
- return runtimeMatch;
2070
+ function defer(_promise) {
2071
+ const promise = _promise;
2072
+ if (!promise.__deferredState) {
2073
+ promise.__deferredState = {
2074
+ uid: Math.random().toString(36).slice(2),
2075
+ status: 'pending'
2076
+ };
2077
+ const state = promise.__deferredState;
2078
+ promise.then(data => {
2079
+ state.status = 'success';
2080
+ state.data = data;
2081
+ }).catch(error => {
2082
+ state.status = 'error';
2083
+ state.error = error;
2084
+ });
2085
+ }
2086
+ return promise;
2771
2087
  }
2772
- function useRoute(routeId) {
2773
- const router = useRouter();
2774
- return router.useRoute(routeId);
2088
+ function isDehydratedDeferred(obj) {
2089
+ return typeof obj === 'object' && obj !== null && !(obj instanceof Promise) && !obj.then && '__deferredState' in obj;
2775
2090
  }
2776
- function linkProps(props) {
2091
+
2092
+ function useAwaited({
2093
+ promise
2094
+ }) {
2777
2095
  const router = useRouter();
2778
- return router.linkProps(props);
2096
+ let state = promise.__deferredState;
2097
+ const key = `__TSR__DEFERRED__${state.uid}`;
2098
+ if (isDehydratedDeferred(promise)) {
2099
+ state = router.hydrateData(key);
2100
+ promise = Promise.resolve(state.data);
2101
+ promise.__deferredState = state;
2102
+ }
2103
+ if (state.status === 'pending') {
2104
+ throw promise;
2105
+ }
2106
+ if (state.status === 'error') {
2107
+ throw state.error;
2108
+ }
2109
+ router.dehydrateData(key, state);
2110
+ return [state.data];
2779
2111
  }
2780
- function MatchRoute(props) {
2781
- const router = useRouter();
2782
- return /*#__PURE__*/React.createElement(router.MatchRoute, props);
2112
+ function Await(props) {
2113
+ const awaited = useAwaited(props);
2114
+ return props.children(...awaited);
2783
2115
  }
2784
- function Outlet() {
2785
- var _ref3, _match$__$pendingComp, _match$__$errorCompon;
2786
-
2787
- const router = useRouter();
2788
- const matches = useMatches().slice(1);
2789
- const match = matches[0];
2790
- const defaultPending = React.useCallback(() => null, []);
2791
2116
 
2792
- if (!match) {
2793
- return null;
2117
+ class FileRoute {
2118
+ constructor(path) {
2119
+ this.path = path;
2794
2120
  }
2121
+ createRoute = options => {
2122
+ const route = new Route(options);
2123
+ route.isRoot = false;
2124
+ return route;
2125
+ };
2126
+ }
2795
2127
 
2796
- const PendingComponent = (_ref3 = (_match$__$pendingComp = match.__.pendingComponent) != null ? _match$__$pendingComp : router.options.defaultPendingComponent) != null ? _ref3 : defaultPending;
2797
- const errorComponent = (_match$__$errorCompon = match.__.errorComponent) != null ? _match$__$errorCompon : router.options.defaultErrorComponent;
2798
- return /*#__PURE__*/React.createElement(MatchesProvider, {
2799
- value: matches
2800
- }, /*#__PURE__*/React.createElement(React.Suspense, {
2801
- fallback: /*#__PURE__*/React.createElement(PendingComponent, null)
2802
- }, /*#__PURE__*/React.createElement(CatchBoundary, {
2803
- errorComponent: errorComponent
2804
- }, (() => {
2805
- if (match.status === 'error') {
2806
- throw match.error;
2128
+ function lazyRouteComponent(importer, exportName) {
2129
+ let loadPromise;
2130
+ const load = () => {
2131
+ if (!loadPromise) {
2132
+ loadPromise = importer();
2807
2133
  }
2134
+ return loadPromise;
2135
+ };
2136
+ const lazyComp = /*#__PURE__*/React.lazy(async () => {
2137
+ const moduleExports = await load();
2138
+ const comp = moduleExports[exportName ?? 'default'];
2139
+ return {
2140
+ default: comp
2141
+ };
2142
+ });
2143
+ lazyComp.preload = load;
2144
+ return lazyComp;
2145
+ }
2808
2146
 
2809
- if (match.status === 'success') {
2810
- var _ref4, _ref5;
2811
-
2812
- return /*#__PURE__*/React.createElement((_ref4 = (_ref5 = match.__.component) != null ? _ref5 : router.options.defaultComponent) != null ? _ref4 : Outlet);
2147
+ function _extends() {
2148
+ _extends = Object.assign ? Object.assign.bind() : function (target) {
2149
+ for (var i = 1; i < arguments.length; i++) {
2150
+ var source = arguments[i];
2151
+ for (var key in source) {
2152
+ if (Object.prototype.hasOwnProperty.call(source, key)) {
2153
+ target[key] = source[key];
2154
+ }
2155
+ }
2813
2156
  }
2814
-
2815
- console.log(match.matchId, 'suspend');
2816
- throw match.__.loadPromise;
2817
- })())));
2157
+ return target;
2158
+ };
2159
+ return _extends.apply(this, arguments);
2818
2160
  }
2819
2161
 
2820
- class CatchBoundary extends React.Component {
2821
- constructor() {
2822
- super(...arguments);
2823
- this.state = {
2824
- error: false
2162
+ function useLinkProps(options) {
2163
+ const {
2164
+ buildLink
2165
+ } = useRouter();
2166
+ const match = useMatch({
2167
+ strict: false
2168
+ });
2169
+ const {
2170
+ // custom props
2171
+ type,
2172
+ children,
2173
+ target,
2174
+ activeProps = () => ({
2175
+ className: 'active'
2176
+ }),
2177
+ inactiveProps = () => ({}),
2178
+ activeOptions,
2179
+ disabled,
2180
+ hash,
2181
+ search,
2182
+ params,
2183
+ to,
2184
+ state,
2185
+ mask,
2186
+ preload,
2187
+ preloadDelay,
2188
+ replace,
2189
+ startTransition,
2190
+ resetScroll,
2191
+ // element props
2192
+ style,
2193
+ className,
2194
+ onClick,
2195
+ onFocus,
2196
+ onMouseEnter,
2197
+ onMouseLeave,
2198
+ onTouchStart,
2199
+ ...rest
2200
+ } = options;
2201
+ const linkInfo = buildLink({
2202
+ from: options.to ? match.pathname : undefined,
2203
+ ...options
2204
+ });
2205
+ if (linkInfo.type === 'external') {
2206
+ const {
2207
+ href
2208
+ } = linkInfo;
2209
+ return {
2210
+ href
2825
2211
  };
2826
2212
  }
2827
-
2828
- componentDidCatch(error, info) {
2829
- console.error(error);
2830
- this.setState({
2831
- error,
2832
- info
2213
+ const {
2214
+ handleClick,
2215
+ handleFocus,
2216
+ handleEnter,
2217
+ handleLeave,
2218
+ handleTouchStart,
2219
+ isActive,
2220
+ next
2221
+ } = linkInfo;
2222
+ const composeHandlers = handlers => e => {
2223
+ if (e.persist) e.persist();
2224
+ handlers.filter(Boolean).forEach(handler => {
2225
+ if (e.defaultPrevented) return;
2226
+ handler(e);
2833
2227
  });
2834
- }
2835
-
2836
- render() {
2837
- var _this$props$errorComp;
2838
-
2839
- const errorComponent = (_this$props$errorComp = this.props.errorComponent) != null ? _this$props$errorComp : DefaultErrorBoundary;
2840
-
2841
- if (this.state.error) {
2842
- return /*#__PURE__*/React.createElement(errorComponent, this.state);
2843
- }
2844
-
2845
- return this.props.children;
2846
- }
2228
+ };
2847
2229
 
2848
- }
2230
+ // Get the active props
2231
+ const resolvedActiveProps = isActive ? functionalUpdate(activeProps, {}) ?? {} : {};
2849
2232
 
2850
- function DefaultErrorBoundary(_ref6) {
2851
- let {
2852
- error
2853
- } = _ref6;
2854
- return /*#__PURE__*/React.createElement("div", {
2855
- style: {
2856
- padding: '.5rem',
2857
- maxWidth: '100%'
2858
- }
2859
- }, /*#__PURE__*/React.createElement("strong", {
2233
+ // Get the inactive props
2234
+ const resolvedInactiveProps = isActive ? {} : functionalUpdate(inactiveProps, {}) ?? {};
2235
+ return {
2236
+ ...resolvedActiveProps,
2237
+ ...resolvedInactiveProps,
2238
+ ...rest,
2239
+ href: disabled ? undefined : next.maskedLocation ? next.maskedLocation.href : next.href,
2240
+ onClick: composeHandlers([onClick, handleClick]),
2241
+ onFocus: composeHandlers([onFocus, handleFocus]),
2242
+ onMouseEnter: composeHandlers([onMouseEnter, handleEnter]),
2243
+ onMouseLeave: composeHandlers([onMouseLeave, handleLeave]),
2244
+ onTouchStart: composeHandlers([onTouchStart, handleTouchStart]),
2245
+ target,
2860
2246
  style: {
2861
- fontSize: '1.2rem'
2247
+ ...style,
2248
+ ...resolvedActiveProps.style,
2249
+ ...resolvedInactiveProps.style
2250
+ },
2251
+ className: [className, resolvedActiveProps.className, resolvedInactiveProps.className].filter(Boolean).join(' ') || undefined,
2252
+ ...(disabled ? {
2253
+ role: 'link',
2254
+ 'aria-disabled': true
2255
+ } : undefined),
2256
+ ['data-status']: isActive ? 'active' : undefined
2257
+ };
2258
+ }
2259
+ const Link = /*#__PURE__*/React.forwardRef((props, ref) => {
2260
+ const linkProps = useLinkProps(props);
2261
+ return /*#__PURE__*/React.createElement("a", _extends({
2262
+ ref: ref
2263
+ }, linkProps, {
2264
+ children: typeof props.children === 'function' ? props.children({
2265
+ isActive: linkProps['data-status'] === 'active'
2266
+ }) : props.children
2267
+ }));
2268
+ });
2269
+
2270
+ const useLayoutEffect = typeof window !== 'undefined' ? React.useLayoutEffect : React.useEffect;
2271
+ const windowKey = 'window';
2272
+ const delimiter = '___';
2273
+ let weakScrolledElements = new WeakSet();
2274
+ let cache;
2275
+ const sessionsStorage = typeof window !== 'undefined' && window.sessionStorage;
2276
+ const defaultGetKey = location => location.state.key;
2277
+ function useScrollRestoration(options) {
2278
+ const {
2279
+ state,
2280
+ subscribe,
2281
+ resetNextScrollRef
2282
+ } = useRouter();
2283
+ useLayoutEffect(() => {
2284
+ const getKey = options?.getKey || defaultGetKey;
2285
+ if (sessionsStorage) {
2286
+ if (!cache) {
2287
+ cache = (() => {
2288
+ const storageKey = 'tsr-scroll-restoration-v2';
2289
+ const state = JSON.parse(window.sessionStorage.getItem(storageKey) || 'null') || {
2290
+ cached: {},
2291
+ next: {}
2292
+ };
2293
+ return {
2294
+ state,
2295
+ set: updater => {
2296
+ cache.state = functionalUpdate(updater, cache.state);
2297
+ window.sessionStorage.setItem(storageKey, JSON.stringify(cache.state));
2298
+ }
2299
+ };
2300
+ })();
2301
+ }
2862
2302
  }
2863
- }, "Something went wrong!"), /*#__PURE__*/React.createElement("div", {
2864
- style: {
2865
- height: '.5rem'
2303
+ const {
2304
+ history
2305
+ } = window;
2306
+ if (history.scrollRestoration) {
2307
+ history.scrollRestoration = 'manual';
2866
2308
  }
2867
- }), /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("pre", null, error.message ? /*#__PURE__*/React.createElement("code", {
2868
- style: {
2869
- fontSize: '.7em',
2870
- border: '1px solid red',
2871
- borderRadius: '.25rem',
2872
- padding: '.5rem',
2873
- color: 'red'
2309
+ const onScroll = event => {
2310
+ if (weakScrolledElements.has(event.target)) return;
2311
+ weakScrolledElements.add(event.target);
2312
+ const elementSelector = event.target === document || event.target === window ? windowKey : getCssSelector(event.target);
2313
+ if (!cache.state.next[elementSelector]) {
2314
+ cache.set(c => ({
2315
+ ...c,
2316
+ next: {
2317
+ ...c.next,
2318
+ [elementSelector]: {
2319
+ scrollX: NaN,
2320
+ scrollY: NaN
2321
+ }
2322
+ }
2323
+ }));
2324
+ }
2325
+ };
2326
+ const getCssSelector = el => {
2327
+ let path = [],
2328
+ parent;
2329
+ while (parent = el.parentNode) {
2330
+ path.unshift(`${el.tagName}:nth-child(${[].indexOf.call(parent.children, el) + 1})`);
2331
+ el = parent;
2332
+ }
2333
+ return `${path.join(' > ')}`.toLowerCase();
2334
+ };
2335
+ if (typeof document !== 'undefined') {
2336
+ document.addEventListener('scroll', onScroll, true);
2874
2337
  }
2875
- }, error.message) : null)));
2338
+ const unsubOnBeforeLoad = subscribe('onBeforeLoad', event => {
2339
+ if (event.pathChanged) {
2340
+ const restoreKey = getKey(event.fromLocation);
2341
+ for (const elementSelector in cache.state.next) {
2342
+ const entry = cache.state.next[elementSelector];
2343
+ if (elementSelector === windowKey) {
2344
+ entry.scrollX = window.scrollX || 0;
2345
+ entry.scrollY = window.scrollY || 0;
2346
+ } else if (elementSelector) {
2347
+ const element = document.querySelector(elementSelector);
2348
+ entry.scrollX = element?.scrollLeft || 0;
2349
+ entry.scrollY = element?.scrollTop || 0;
2350
+ }
2351
+ cache.set(c => {
2352
+ const next = {
2353
+ ...c.next
2354
+ };
2355
+ delete next[elementSelector];
2356
+ return {
2357
+ ...c,
2358
+ next,
2359
+ cached: {
2360
+ ...c.cached,
2361
+ [[restoreKey, elementSelector].join(delimiter)]: entry
2362
+ }
2363
+ };
2364
+ });
2365
+ }
2366
+ }
2367
+ });
2368
+ const unsubOnResolved = subscribe('onResolved', event => {
2369
+ if (event.pathChanged) {
2370
+ if (!resetNextScrollRef.current) {
2371
+ return;
2372
+ }
2373
+ resetNextScrollRef.current = true;
2374
+ const getKey = options?.getKey || defaultGetKey;
2375
+ const restoreKey = getKey(event.toLocation);
2376
+ let windowRestored = false;
2377
+ for (const cacheKey in cache.state.cached) {
2378
+ const entry = cache.state.cached[cacheKey];
2379
+ const [key, elementSelector] = cacheKey.split(delimiter);
2380
+ if (key === restoreKey) {
2381
+ if (elementSelector === windowKey) {
2382
+ windowRestored = true;
2383
+ window.scrollTo(entry.scrollX, entry.scrollY);
2384
+ } else if (elementSelector) {
2385
+ const element = document.querySelector(elementSelector);
2386
+ if (element) {
2387
+ element.scrollLeft = entry.scrollX;
2388
+ element.scrollTop = entry.scrollY;
2389
+ }
2390
+ }
2391
+ }
2392
+ }
2393
+ if (!windowRestored) {
2394
+ window.scrollTo(0, 0);
2395
+ }
2396
+ cache.set(c => ({
2397
+ ...c,
2398
+ next: {}
2399
+ }));
2400
+ weakScrolledElements = new WeakSet();
2401
+ }
2402
+ });
2403
+ return () => {
2404
+ document.removeEventListener('scroll', onScroll);
2405
+ unsubOnBeforeLoad();
2406
+ unsubOnResolved();
2407
+ };
2408
+ }, []);
2876
2409
  }
2877
- function usePrompt(message, when) {
2878
- const router = useRouter();
2410
+ function ScrollRestoration(props) {
2411
+ useScrollRestoration(props);
2412
+ return null;
2413
+ }
2414
+
2415
+ function useBlocker(message, condition = true) {
2416
+ const {
2417
+ history
2418
+ } = useRouter();
2879
2419
  React.useEffect(() => {
2880
- if (!when) return;
2881
- let unblock = router.history.block(transition => {
2420
+ if (!condition) return;
2421
+ let unblock = history.block((retry, cancel) => {
2882
2422
  if (window.confirm(message)) {
2883
2423
  unblock();
2884
- transition.retry();
2885
- } else {
2886
- router.location.pathname = window.location.pathname;
2424
+ retry();
2887
2425
  }
2888
2426
  });
2889
2427
  return unblock;
2890
- }, [when, location, message]);
2428
+ });
2429
+ }
2430
+ function Block({
2431
+ message,
2432
+ condition,
2433
+ children
2434
+ }) {
2435
+ useBlocker(message, condition);
2436
+ return children ?? null;
2437
+ }
2438
+
2439
+ function useNavigate(defaultOpts) {
2440
+ const {
2441
+ navigate
2442
+ } = useRouter();
2443
+ const match = useMatch({
2444
+ strict: false
2445
+ });
2446
+ return React.useCallback(opts => {
2447
+ return navigate({
2448
+ from: opts?.to ? match.pathname : undefined,
2449
+ ...defaultOpts,
2450
+ ...opts
2451
+ });
2452
+ }, []);
2891
2453
  }
2892
- function Prompt(_ref7) {
2893
- let {
2894
- message,
2895
- when,
2896
- children
2897
- } = _ref7;
2898
- usePrompt(message, when != null ? when : true);
2899
- return children != null ? children : null;
2454
+ function typedNavigate(navigate) {
2455
+ return navigate;
2456
+ } //
2457
+
2458
+ function Navigate(props) {
2459
+ const {
2460
+ navigate
2461
+ } = useRouter();
2462
+ const match = useMatch({
2463
+ strict: false
2464
+ });
2465
+ useLayoutEffect$1(() => {
2466
+ navigate({
2467
+ from: props.to ? match.pathname : undefined,
2468
+ ...props
2469
+ });
2470
+ }, []);
2471
+ return null;
2900
2472
  }
2901
2473
 
2902
- export { DefaultErrorBoundary, Link, MatchRoute, MatchesProvider, Outlet, Prompt, RouterProvider, cleanPath, createBrowserHistory, createHashHistory, createMemoryHistory, createReactRouter, createRoute, createRouteConfig, createRouteMatch, createRouter, decode, defaultParseSearch, defaultStringifySearch, encode, functionalUpdate, interpolatePath, invariant, joinPaths, last, lazy, linkProps, matchByPath, matchPathname, matchesContext, parsePathname, parseSearchWith, pick, replaceEqualDeep, resolvePath, rootRouteId, routerContext, stringifySearchWith, trimPath, trimPathLeft, trimPathRight, useMatch, useMatches, useNearestMatch, usePrompt, useRoute, useRouter, warning };
2474
+ export { Await, Block, CatchBoundary, CatchBoundaryImpl, ErrorComponent, FileRoute, Link, Match, MatchRoute, Matches, Navigate, Outlet, PathParamError, RootRoute, Route, Router, RouterProvider, ScrollRestoration, SearchParamError, cleanPath, componentTypes, createRouteMask, decode, deepEqual, defaultParseSearch, defaultStringifySearch, defer, encode, escapeJSON, functionalUpdate, getInitialRouterState, getRouteMatch, interpolatePath, isDehydratedDeferred, isPlainObject, isRedirect, isServer, joinPaths, last, lazyFn, lazyRouteComponent, matchByPath, matchPathname, matchesContext, parsePathname, parseSearchWith, pick, redirect, replaceEqualDeep, resolvePath, rootRouteId, rootRouteWithContext, routerContext, shallow, stringifySearchWith, trimPath, trimPathLeft, trimPathRight, typedNavigate, useAwaited, useBlocker, useLayoutEffect$1 as useLayoutEffect, useLinkProps, useLoaderData, useMatch, useMatchRoute, useMatches, useNavigate, useParams, useRouteContext, useRouter, useRouterState, useScrollRestoration, useSearch, useStableCallback };
2903
2475
  //# sourceMappingURL=index.js.map