@tanstack/router-core 0.0.1-beta.41 → 0.0.1-beta.49
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/build/cjs/actions.js +94 -0
- package/build/cjs/actions.js.map +1 -0
- package/build/cjs/history.js +163 -0
- package/build/cjs/history.js.map +1 -0
- package/build/cjs/index.js +18 -20
- package/build/cjs/index.js.map +1 -1
- package/build/cjs/interop.js +175 -0
- package/build/cjs/interop.js.map +1 -0
- package/build/cjs/path.js +4 -5
- package/build/cjs/path.js.map +1 -1
- package/build/cjs/route.js +16 -138
- package/build/cjs/route.js.map +1 -1
- package/build/cjs/routeConfig.js +1 -7
- package/build/cjs/routeConfig.js.map +1 -1
- package/build/cjs/routeMatch.js +194 -199
- package/build/cjs/routeMatch.js.map +1 -1
- package/build/cjs/router.js +726 -703
- package/build/cjs/router.js.map +1 -1
- package/build/cjs/store.js +54 -0
- package/build/cjs/store.js.map +1 -0
- package/build/esm/index.js +1305 -1114
- package/build/esm/index.js.map +1 -1
- package/build/stats-html.html +1 -1
- package/build/stats-react.json +229 -192
- package/build/types/index.d.ts +172 -109
- package/build/umd/index.development.js +1381 -2331
- package/build/umd/index.development.js.map +1 -1
- package/build/umd/index.production.js +1 -1
- package/build/umd/index.production.js.map +1 -1
- package/package.json +4 -4
- package/src/actions.ts +157 -0
- package/src/history.ts +199 -0
- package/src/index.ts +4 -7
- package/src/interop.ts +169 -0
- package/src/link.ts +2 -2
- package/src/route.ts +34 -239
- package/src/routeConfig.ts +3 -34
- package/src/routeInfo.ts +6 -21
- package/src/routeMatch.ts +270 -285
- package/src/router.ts +967 -963
- package/src/store.ts +52 -0
- package/build/cjs/sharedClone.js +0 -122
- package/build/cjs/sharedClone.js.map +0 -1
- package/src/sharedClone.ts +0 -118
package/build/cjs/router.js
CHANGED
|
@@ -12,782 +12,806 @@
|
|
|
12
12
|
|
|
13
13
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
14
14
|
|
|
15
|
-
var history = require('history');
|
|
16
15
|
var invariant = require('tiny-invariant');
|
|
17
16
|
var path = require('./path.js');
|
|
18
17
|
var route = require('./route.js');
|
|
19
18
|
var routeMatch = require('./routeMatch.js');
|
|
20
19
|
var searchParams = require('./searchParams.js');
|
|
21
|
-
var
|
|
20
|
+
var store = require('./store.js');
|
|
22
21
|
var utils = require('./utils.js');
|
|
23
|
-
var
|
|
22
|
+
var interop = require('./interop.js');
|
|
23
|
+
var history = require('./history.js');
|
|
24
24
|
|
|
25
25
|
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
26
26
|
|
|
27
27
|
var invariant__default = /*#__PURE__*/_interopDefaultLegacy(invariant);
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
const defaultFetchServerDataFn = async ({
|
|
30
|
+
router,
|
|
31
|
+
routeMatch
|
|
32
|
+
}) => {
|
|
33
|
+
const next = router.buildNext({
|
|
34
|
+
to: '.',
|
|
35
|
+
search: d => ({
|
|
36
|
+
...(d ?? {}),
|
|
37
|
+
__data: {
|
|
38
|
+
matchId: routeMatch.id
|
|
39
|
+
}
|
|
40
|
+
})
|
|
41
|
+
});
|
|
42
|
+
const res = await fetch(next.href, {
|
|
43
|
+
method: 'GET',
|
|
44
|
+
signal: routeMatch.abortController.signal
|
|
45
|
+
});
|
|
46
|
+
if (res.ok) {
|
|
47
|
+
return res.json();
|
|
48
|
+
}
|
|
49
|
+
throw new Error('Failed to fetch match data');
|
|
50
|
+
};
|
|
51
|
+
class Router {
|
|
52
|
+
// __location: Location<TAllRouteInfo['fullSearchSchema']>
|
|
53
|
+
|
|
54
|
+
startedLoadingAt = Date.now();
|
|
55
|
+
resolveNavigation = () => {};
|
|
56
|
+
constructor(options) {
|
|
57
|
+
this.options = {
|
|
58
|
+
defaultLoaderGcMaxAge: 5 * 60 * 1000,
|
|
59
|
+
defaultLoaderMaxAge: 0,
|
|
60
|
+
defaultPreloadMaxAge: 2000,
|
|
61
|
+
defaultPreloadDelay: 50,
|
|
62
|
+
context: undefined,
|
|
63
|
+
...options,
|
|
64
|
+
stringifySearch: options?.stringifySearch ?? searchParams.defaultStringifySearch,
|
|
65
|
+
parseSearch: options?.parseSearch ?? searchParams.defaultParseSearch,
|
|
66
|
+
fetchServerDataFn: options?.fetchServerDataFn ?? defaultFetchServerDataFn
|
|
67
|
+
};
|
|
68
|
+
this.history = this.options?.history ?? isServer ? history.createMemoryHistory() : history.createBrowserHistory();
|
|
69
|
+
this.store = store.createStore(getInitialRouterState());
|
|
70
|
+
this.basepath = '';
|
|
71
|
+
this.update(options);
|
|
32
72
|
|
|
33
|
-
//
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
73
|
+
// Allow frameworks to hook into the router creation
|
|
74
|
+
this.options.createRouter?.(this);
|
|
75
|
+
}
|
|
76
|
+
reset = () => {
|
|
77
|
+
this.store.setState(s => Object.assign(s, getInitialRouterState()));
|
|
78
|
+
};
|
|
79
|
+
mount = () => {
|
|
80
|
+
// Mount only does anything on the client
|
|
81
|
+
if (!isServer) {
|
|
82
|
+
// If the router matches are empty, load the matches
|
|
83
|
+
if (!this.store.state.currentMatches.length) {
|
|
84
|
+
this.load();
|
|
85
|
+
}
|
|
86
|
+
const unsubHistory = this.history.listen(() => {
|
|
87
|
+
this.load(this.#parseLocation(this.store.state.latestLocation));
|
|
88
|
+
});
|
|
89
|
+
const visibilityChangeEvent = 'visibilitychange';
|
|
90
|
+
const focusEvent = 'focus';
|
|
91
|
+
|
|
92
|
+
// addEventListener does not exist in React Native, but window does
|
|
93
|
+
// In the future, we might need to invert control here for more adapters
|
|
94
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
95
|
+
if (window.addEventListener) {
|
|
96
|
+
// Listen to visibilitychange and focus
|
|
97
|
+
window.addEventListener(visibilityChangeEvent, this.#onFocus, false);
|
|
98
|
+
window.addEventListener(focusEvent, this.#onFocus, false);
|
|
99
|
+
}
|
|
100
|
+
return () => {
|
|
101
|
+
unsubHistory();
|
|
102
|
+
if (window.removeEventListener) {
|
|
103
|
+
// Be sure to unsubscribe if a new handler is set
|
|
104
|
+
|
|
105
|
+
window.removeEventListener(visibilityChangeEvent, this.#onFocus);
|
|
106
|
+
window.removeEventListener(focusEvent, this.#onFocus);
|
|
107
|
+
}
|
|
108
|
+
};
|
|
50
109
|
}
|
|
110
|
+
return () => {};
|
|
51
111
|
};
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
112
|
+
update = opts => {
|
|
113
|
+
if (!this.store.state.latestLocation) {
|
|
114
|
+
this.store.setState(s => {
|
|
115
|
+
s.latestLocation = this.#parseLocation();
|
|
116
|
+
s.currentLocation = s.latestLocation;
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
Object.assign(this.options, opts);
|
|
120
|
+
const {
|
|
121
|
+
basepath,
|
|
122
|
+
routeConfig
|
|
123
|
+
} = this.options;
|
|
124
|
+
this.basepath = `/${path.trimPath(basepath ?? '') ?? ''}`;
|
|
125
|
+
if (routeConfig) {
|
|
126
|
+
this.routesById = {};
|
|
127
|
+
this.routeTree = this.#buildRouteTree(routeConfig);
|
|
128
|
+
}
|
|
129
|
+
return this;
|
|
63
130
|
};
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
131
|
+
buildNext = opts => {
|
|
132
|
+
const next = this.#buildLocation(opts);
|
|
133
|
+
const matches = this.matchRoutes(next.pathname);
|
|
134
|
+
const __preSearchFilters = matches.map(match => match.route.options.preSearchFilters ?? []).flat().filter(Boolean);
|
|
135
|
+
const __postSearchFilters = matches.map(match => match.route.options.postSearchFilters ?? []).flat().filter(Boolean);
|
|
136
|
+
return this.#buildLocation({
|
|
137
|
+
...opts,
|
|
138
|
+
__preSearchFilters,
|
|
139
|
+
__postSearchFilters
|
|
140
|
+
});
|
|
141
|
+
};
|
|
142
|
+
cancelMatches = () => {
|
|
143
|
+
[...this.store.state.currentMatches, ...(this.store.state.pendingMatches || [])].forEach(match => {
|
|
144
|
+
match.cancel();
|
|
145
|
+
});
|
|
146
|
+
};
|
|
147
|
+
load = async next => {
|
|
148
|
+
let now = Date.now();
|
|
149
|
+
const startedAt = now;
|
|
150
|
+
this.startedLoadingAt = startedAt;
|
|
151
|
+
|
|
152
|
+
// Cancel any pending matches
|
|
153
|
+
this.cancelMatches();
|
|
154
|
+
let matches;
|
|
155
|
+
store.batch(() => {
|
|
156
|
+
if (next) {
|
|
157
|
+
// Ingest the new location
|
|
158
|
+
this.store.setState(s => {
|
|
159
|
+
s.latestLocation = next;
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Match the routes
|
|
164
|
+
matches = this.matchRoutes(this.store.state.latestLocation.pathname, {
|
|
165
|
+
strictParseParams: true
|
|
166
|
+
});
|
|
167
|
+
this.store.setState(s => {
|
|
168
|
+
s.status = 'loading';
|
|
169
|
+
s.pendingMatches = matches;
|
|
170
|
+
s.pendingLocation = this.store.state.latestLocation;
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
// Load the matches
|
|
175
|
+
try {
|
|
176
|
+
await this.loadMatches(matches);
|
|
177
|
+
} catch (err) {
|
|
178
|
+
console.warn(err);
|
|
179
|
+
invariant__default["default"](false, 'Matches failed to load due to error above ☝️. Navigation cancelled!');
|
|
180
|
+
}
|
|
181
|
+
if (this.startedLoadingAt !== startedAt) {
|
|
182
|
+
// Ignore side-effects of outdated side-effects
|
|
183
|
+
return this.navigationPromise;
|
|
184
|
+
}
|
|
185
|
+
const previousMatches = this.store.state.currentMatches;
|
|
186
|
+
const exiting = [],
|
|
187
|
+
staying = [];
|
|
188
|
+
previousMatches.forEach(d => {
|
|
189
|
+
if (matches.find(dd => dd.id === d.id)) {
|
|
190
|
+
staying.push(d);
|
|
191
|
+
} else {
|
|
192
|
+
exiting.push(d);
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
const entering = matches.filter(d => {
|
|
196
|
+
return !previousMatches.find(dd => dd.id === d.id);
|
|
197
|
+
});
|
|
198
|
+
now = Date.now();
|
|
199
|
+
exiting.forEach(d => {
|
|
200
|
+
d.__onExit?.({
|
|
201
|
+
params: d.params,
|
|
202
|
+
search: d.store.state.routeSearch
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
// Clear non-loading error states when match leaves
|
|
206
|
+
if (d.store.state.status === 'error' && !d.store.state.isFetching) {
|
|
207
|
+
d.store.setState(s => {
|
|
208
|
+
s.status = 'idle';
|
|
209
|
+
s.error = undefined;
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
const gc = Math.max(d.route.options.loaderGcMaxAge ?? this.options.defaultLoaderGcMaxAge ?? 0, d.route.options.loaderMaxAge ?? this.options.defaultLoaderMaxAge ?? 0);
|
|
213
|
+
if (gc > 0) {
|
|
214
|
+
this.store.setState(s => {
|
|
215
|
+
s.matchCache[d.id] = {
|
|
216
|
+
gc: gc == Infinity ? Number.MAX_SAFE_INTEGER : now + gc,
|
|
217
|
+
match: d
|
|
218
|
+
};
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
staying.forEach(d => {
|
|
223
|
+
d.route.options.onTransition?.({
|
|
224
|
+
params: d.params,
|
|
225
|
+
search: d.store.state.routeSearch
|
|
226
|
+
});
|
|
227
|
+
});
|
|
228
|
+
entering.forEach(d => {
|
|
229
|
+
d.__onExit = d.route.options.onLoaded?.({
|
|
230
|
+
params: d.params,
|
|
231
|
+
search: d.store.state.search
|
|
232
|
+
});
|
|
233
|
+
delete this.store.state.matchCache[d.id];
|
|
234
|
+
});
|
|
235
|
+
this.store.setState(s => {
|
|
236
|
+
Object.assign(s, {
|
|
237
|
+
status: 'idle',
|
|
238
|
+
currentLocation: this.store.state.latestLocation,
|
|
239
|
+
currentMatches: matches,
|
|
240
|
+
pendingLocation: undefined,
|
|
241
|
+
pendingMatches: undefined
|
|
242
|
+
});
|
|
243
|
+
});
|
|
244
|
+
this.options.onRouteChange?.();
|
|
245
|
+
this.resolveNavigation();
|
|
246
|
+
};
|
|
247
|
+
cleanMatchCache = () => {
|
|
248
|
+
const now = Date.now();
|
|
249
|
+
this.store.setState(s => {
|
|
250
|
+
Object.keys(s.matchCache).forEach(matchId => {
|
|
251
|
+
const entry = s.matchCache[matchId];
|
|
252
|
+
|
|
253
|
+
// Don't remove loading matches
|
|
254
|
+
if (entry.match.store.state.status === 'loading') {
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Do not remove successful matches that are still valid
|
|
259
|
+
if (entry.gc > 0 && entry.gc > now) {
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// Everything else gets removed
|
|
264
|
+
delete s.matchCache[matchId];
|
|
265
|
+
});
|
|
266
|
+
});
|
|
267
|
+
};
|
|
268
|
+
getRoute = id => {
|
|
269
|
+
const route = this.routesById[id];
|
|
270
|
+
invariant__default["default"](route, `Route with id "${id}" not found`);
|
|
271
|
+
return route;
|
|
272
|
+
};
|
|
273
|
+
loadRoute = async (navigateOpts = this.store.state.latestLocation) => {
|
|
274
|
+
const next = this.buildNext(navigateOpts);
|
|
275
|
+
const matches = this.matchRoutes(next.pathname, {
|
|
276
|
+
strictParseParams: true
|
|
277
|
+
});
|
|
278
|
+
await this.loadMatches(matches);
|
|
279
|
+
return matches;
|
|
280
|
+
};
|
|
281
|
+
preloadRoute = async (navigateOpts = this.store.state.latestLocation, loaderOpts) => {
|
|
282
|
+
const next = this.buildNext(navigateOpts);
|
|
283
|
+
const matches = this.matchRoutes(next.pathname, {
|
|
284
|
+
strictParseParams: true
|
|
285
|
+
});
|
|
286
|
+
await this.loadMatches(matches, {
|
|
287
|
+
preload: true,
|
|
288
|
+
maxAge: loaderOpts.maxAge ?? this.options.defaultPreloadMaxAge ?? this.options.defaultLoaderMaxAge ?? 0,
|
|
289
|
+
gcMaxAge: loaderOpts.gcMaxAge ?? this.options.defaultPreloadGcMaxAge ?? this.options.defaultLoaderGcMaxAge ?? 0
|
|
290
|
+
});
|
|
291
|
+
return matches;
|
|
292
|
+
};
|
|
293
|
+
matchRoutes = (pathname, opts) => {
|
|
294
|
+
const matches = [];
|
|
295
|
+
if (!this.routeTree) {
|
|
296
|
+
return matches;
|
|
297
|
+
}
|
|
298
|
+
const existingMatches = [...this.store.state.currentMatches, ...(this.store.state.pendingMatches ?? [])];
|
|
299
|
+
const recurse = async routes => {
|
|
300
|
+
const parentMatch = utils.last(matches);
|
|
301
|
+
let params = parentMatch?.params ?? {};
|
|
302
|
+
const filteredRoutes = this.options.filterRoutes?.(routes) ?? routes;
|
|
303
|
+
let foundRoutes = [];
|
|
304
|
+
const findMatchInRoutes = (parentRoutes, routes) => {
|
|
305
|
+
routes.some(route => {
|
|
306
|
+
if (!route.path && route.childRoutes?.length) {
|
|
307
|
+
return findMatchInRoutes([...foundRoutes, route], route.childRoutes);
|
|
308
|
+
}
|
|
309
|
+
const fuzzy = !!(route.path !== '/' || route.childRoutes?.length);
|
|
310
|
+
const matchParams = path.matchPathname(this.basepath, pathname, {
|
|
311
|
+
to: route.fullPath,
|
|
312
|
+
fuzzy,
|
|
313
|
+
caseSensitive: route.options.caseSensitive ?? this.options.caseSensitive
|
|
314
|
+
});
|
|
315
|
+
if (matchParams) {
|
|
316
|
+
let parsedParams;
|
|
317
|
+
try {
|
|
318
|
+
parsedParams = route.options.parseParams?.(matchParams) ?? matchParams;
|
|
319
|
+
} catch (err) {
|
|
320
|
+
if (opts?.strictParseParams) {
|
|
321
|
+
throw err;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
params = {
|
|
325
|
+
...params,
|
|
326
|
+
...parsedParams
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
if (!!matchParams) {
|
|
330
|
+
foundRoutes = [...parentRoutes, route];
|
|
331
|
+
}
|
|
332
|
+
return !!foundRoutes.length;
|
|
333
|
+
});
|
|
334
|
+
return !!foundRoutes.length;
|
|
335
|
+
};
|
|
336
|
+
findMatchInRoutes([], filteredRoutes);
|
|
337
|
+
if (!foundRoutes.length) {
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
340
|
+
foundRoutes.forEach(foundRoute => {
|
|
341
|
+
const interpolatedPath = path.interpolatePath(foundRoute.path, params);
|
|
342
|
+
const matchId = path.interpolatePath(foundRoute.id, params, true);
|
|
343
|
+
const match = existingMatches.find(d => d.id === matchId) || this.store.state.matchCache[matchId]?.match || new routeMatch.RouteMatch(this, foundRoute, {
|
|
344
|
+
matchId,
|
|
345
|
+
params,
|
|
346
|
+
pathname: path.joinPaths([this.basepath, interpolatedPath])
|
|
347
|
+
});
|
|
348
|
+
matches.push(match);
|
|
349
|
+
});
|
|
350
|
+
const foundRoute = utils.last(foundRoutes);
|
|
351
|
+
if (foundRoute.childRoutes?.length) {
|
|
352
|
+
recurse(foundRoute.childRoutes);
|
|
353
|
+
}
|
|
354
|
+
};
|
|
355
|
+
recurse([this.routeTree]);
|
|
356
|
+
linkMatches(matches);
|
|
357
|
+
return matches;
|
|
358
|
+
};
|
|
359
|
+
loadMatches = async (resolvedMatches, loaderOpts) => {
|
|
360
|
+
this.cleanMatchCache();
|
|
361
|
+
resolvedMatches.forEach(async match => {
|
|
362
|
+
// Validate the match (loads search params etc)
|
|
363
|
+
match.__validate();
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
// Check each match middleware to see if the route can be accessed
|
|
367
|
+
await Promise.all(resolvedMatches.map(async match => {
|
|
368
|
+
try {
|
|
369
|
+
await match.route.options.beforeLoad?.({
|
|
370
|
+
router: this,
|
|
371
|
+
match
|
|
372
|
+
});
|
|
373
|
+
} catch (err) {
|
|
374
|
+
if (!loaderOpts?.preload) {
|
|
375
|
+
match.route.options.onLoadError?.(err);
|
|
376
|
+
}
|
|
377
|
+
throw err;
|
|
378
|
+
}
|
|
379
|
+
}));
|
|
380
|
+
const matchPromises = resolvedMatches.map(async (match, index) => {
|
|
381
|
+
const prevMatch = resolvedMatches[1];
|
|
382
|
+
const search = match.store.state.search;
|
|
383
|
+
if (search.__data?.matchId && search.__data.matchId !== match.id) {
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
match.load(loaderOpts);
|
|
387
|
+
if (match.store.state.status !== 'success' && match.__loadPromise) {
|
|
388
|
+
// Wait for the first sign of activity from the match
|
|
389
|
+
await match.__loadPromise;
|
|
390
|
+
}
|
|
391
|
+
if (prevMatch) {
|
|
392
|
+
await prevMatch.__loadPromise;
|
|
393
|
+
}
|
|
394
|
+
});
|
|
395
|
+
await Promise.all(matchPromises);
|
|
396
|
+
};
|
|
397
|
+
loadMatchData = async routeMatch => {
|
|
398
|
+
if (isServer || !this.options.useServerData) {
|
|
399
|
+
return (await routeMatch.route.options.loader?.({
|
|
400
|
+
// parentLoaderPromise: routeMatch.parentMatch.dataPromise,
|
|
401
|
+
params: routeMatch.params,
|
|
402
|
+
search: routeMatch.store.state.routeSearch,
|
|
403
|
+
signal: routeMatch.abortController.signal
|
|
404
|
+
})) || {};
|
|
405
|
+
} else {
|
|
406
|
+
// Refresh:
|
|
407
|
+
// '/dashboard'
|
|
408
|
+
// '/dashboard/invoices/'
|
|
409
|
+
// '/dashboard/invoices/123'
|
|
410
|
+
|
|
411
|
+
// New:
|
|
412
|
+
// '/dashboard/invoices/456'
|
|
413
|
+
|
|
414
|
+
// TODO: batch requests when possible
|
|
415
|
+
|
|
416
|
+
return this.options.fetchServerDataFn({
|
|
417
|
+
router: this,
|
|
418
|
+
routeMatch
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
};
|
|
422
|
+
invalidateRoute = async opts => {
|
|
423
|
+
const next = this.buildNext(opts);
|
|
424
|
+
const unloadedMatchIds = this.matchRoutes(next.pathname).map(d => d.id);
|
|
425
|
+
await Promise.allSettled([...this.store.state.currentMatches, ...(this.store.state.pendingMatches ?? [])].map(async match => {
|
|
426
|
+
if (unloadedMatchIds.includes(match.id)) {
|
|
427
|
+
return match.invalidate();
|
|
428
|
+
}
|
|
429
|
+
}));
|
|
430
|
+
};
|
|
431
|
+
reload = () => {
|
|
432
|
+
this.navigate({
|
|
433
|
+
fromCurrent: true,
|
|
434
|
+
replace: true,
|
|
435
|
+
search: true
|
|
436
|
+
});
|
|
437
|
+
};
|
|
438
|
+
resolvePath = (from, path$1) => {
|
|
439
|
+
return path.resolvePath(this.basepath, from, path.cleanPath(path$1));
|
|
440
|
+
};
|
|
441
|
+
navigate = async ({
|
|
442
|
+
from,
|
|
443
|
+
to = '.',
|
|
444
|
+
search,
|
|
445
|
+
hash,
|
|
446
|
+
replace,
|
|
447
|
+
params
|
|
448
|
+
}) => {
|
|
449
|
+
// If this link simply reloads the current route,
|
|
450
|
+
// make sure it has a new key so it will trigger a data refresh
|
|
451
|
+
|
|
452
|
+
// If this `to` is a valid external URL, return
|
|
453
|
+
// null for LinkUtils
|
|
454
|
+
const toString = String(to);
|
|
455
|
+
const fromString = String(from);
|
|
456
|
+
let isExternal;
|
|
457
|
+
try {
|
|
458
|
+
new URL(`${toString}`);
|
|
459
|
+
isExternal = true;
|
|
460
|
+
} catch (e) {}
|
|
461
|
+
invariant__default["default"](!isExternal, 'Attempting to navigate to external url with this.navigate!');
|
|
462
|
+
return this.#commitLocation({
|
|
463
|
+
from: fromString,
|
|
464
|
+
to: toString,
|
|
465
|
+
search,
|
|
466
|
+
hash,
|
|
467
|
+
replace,
|
|
468
|
+
params
|
|
469
|
+
});
|
|
470
|
+
};
|
|
471
|
+
matchRoute = (location, opts) => {
|
|
472
|
+
location = {
|
|
473
|
+
...location,
|
|
474
|
+
to: location.to ? this.resolvePath(location.from ?? '', location.to) : undefined
|
|
475
|
+
};
|
|
476
|
+
const next = this.buildNext(location);
|
|
477
|
+
if (opts?.pending) {
|
|
478
|
+
if (!this.store.state.pendingLocation) {
|
|
479
|
+
return false;
|
|
480
|
+
}
|
|
481
|
+
return path.matchPathname(this.basepath, this.store.state.pendingLocation.pathname, {
|
|
482
|
+
...opts,
|
|
483
|
+
to: next.pathname
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
return path.matchPathname(this.basepath, this.store.state.currentLocation.pathname, {
|
|
487
|
+
...opts,
|
|
488
|
+
to: next.pathname
|
|
489
|
+
});
|
|
490
|
+
};
|
|
491
|
+
buildLink = ({
|
|
492
|
+
from,
|
|
493
|
+
to = '.',
|
|
494
|
+
search,
|
|
495
|
+
params,
|
|
496
|
+
hash,
|
|
497
|
+
target,
|
|
498
|
+
replace,
|
|
499
|
+
activeOptions,
|
|
500
|
+
preload,
|
|
501
|
+
preloadMaxAge: userPreloadMaxAge,
|
|
502
|
+
preloadGcMaxAge: userPreloadGcMaxAge,
|
|
503
|
+
preloadDelay: userPreloadDelay,
|
|
504
|
+
disabled
|
|
505
|
+
}) => {
|
|
506
|
+
// If this link simply reloads the current route,
|
|
507
|
+
// make sure it has a new key so it will trigger a data refresh
|
|
508
|
+
|
|
509
|
+
// If this `to` is a valid external URL, return
|
|
510
|
+
// null for LinkUtils
|
|
511
|
+
|
|
512
|
+
try {
|
|
513
|
+
new URL(`${to}`);
|
|
514
|
+
return {
|
|
515
|
+
type: 'external',
|
|
516
|
+
href: to
|
|
517
|
+
};
|
|
518
|
+
} catch (e) {}
|
|
519
|
+
const nextOpts = {
|
|
520
|
+
from,
|
|
521
|
+
to,
|
|
522
|
+
search,
|
|
523
|
+
params,
|
|
524
|
+
hash,
|
|
525
|
+
replace
|
|
526
|
+
};
|
|
527
|
+
const next = this.buildNext(nextOpts);
|
|
528
|
+
preload = preload ?? this.options.defaultPreload;
|
|
529
|
+
const preloadDelay = userPreloadDelay ?? this.options.defaultPreloadDelay ?? 0;
|
|
530
|
+
|
|
531
|
+
// Compare path/hash for matches
|
|
532
|
+
const pathIsEqual = this.store.state.currentLocation.pathname === next.pathname;
|
|
533
|
+
const currentPathSplit = this.store.state.currentLocation.pathname.split('/');
|
|
534
|
+
const nextPathSplit = next.pathname.split('/');
|
|
535
|
+
const pathIsFuzzyEqual = nextPathSplit.every((d, i) => d === currentPathSplit[i]);
|
|
536
|
+
const hashIsEqual = this.store.state.currentLocation.hash === next.hash;
|
|
537
|
+
// Combine the matches based on user options
|
|
538
|
+
const pathTest = activeOptions?.exact ? pathIsEqual : pathIsFuzzyEqual;
|
|
539
|
+
const hashTest = activeOptions?.includeHash ? hashIsEqual : true;
|
|
540
|
+
|
|
541
|
+
// The final "active" test
|
|
542
|
+
const isActive = pathTest && hashTest;
|
|
543
|
+
|
|
544
|
+
// The click handler
|
|
545
|
+
const handleClick = e => {
|
|
546
|
+
if (!disabled && !isCtrlEvent(e) && !e.defaultPrevented && (!target || target === '_self') && e.button === 0) {
|
|
547
|
+
e.preventDefault();
|
|
548
|
+
if (pathIsEqual && !search && !hash) {
|
|
549
|
+
this.invalidateRoute(nextOpts);
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
// All is well? Navigate!
|
|
553
|
+
this.#commitLocation(nextOpts);
|
|
554
|
+
}
|
|
555
|
+
};
|
|
556
|
+
|
|
557
|
+
// The click handler
|
|
558
|
+
const handleFocus = e => {
|
|
559
|
+
if (preload) {
|
|
560
|
+
this.preloadRoute(nextOpts, {
|
|
561
|
+
maxAge: userPreloadMaxAge,
|
|
562
|
+
gcMaxAge: userPreloadGcMaxAge
|
|
563
|
+
}).catch(err => {
|
|
564
|
+
console.warn(err);
|
|
565
|
+
console.warn('Error preloading route! ☝️');
|
|
566
|
+
});
|
|
567
|
+
}
|
|
568
|
+
};
|
|
569
|
+
const handleEnter = e => {
|
|
570
|
+
const target = e.target || {};
|
|
571
|
+
if (preload) {
|
|
572
|
+
if (target.preloadTimeout) {
|
|
573
|
+
return;
|
|
574
|
+
}
|
|
575
|
+
target.preloadTimeout = setTimeout(() => {
|
|
576
|
+
target.preloadTimeout = null;
|
|
577
|
+
this.preloadRoute(nextOpts, {
|
|
578
|
+
maxAge: userPreloadMaxAge,
|
|
579
|
+
gcMaxAge: userPreloadGcMaxAge
|
|
580
|
+
}).catch(err => {
|
|
581
|
+
console.warn(err);
|
|
582
|
+
console.warn('Error preloading route! ☝️');
|
|
583
|
+
});
|
|
584
|
+
}, preloadDelay);
|
|
585
|
+
}
|
|
586
|
+
};
|
|
587
|
+
const handleLeave = e => {
|
|
588
|
+
const target = e.target || {};
|
|
589
|
+
if (target.preloadTimeout) {
|
|
590
|
+
clearTimeout(target.preloadTimeout);
|
|
591
|
+
target.preloadTimeout = null;
|
|
592
|
+
}
|
|
593
|
+
};
|
|
594
|
+
return {
|
|
595
|
+
type: 'internal',
|
|
596
|
+
next,
|
|
597
|
+
handleFocus,
|
|
598
|
+
handleClick,
|
|
599
|
+
handleEnter,
|
|
600
|
+
handleLeave,
|
|
601
|
+
isActive,
|
|
602
|
+
disabled
|
|
603
|
+
};
|
|
604
|
+
};
|
|
605
|
+
dehydrate = () => {
|
|
606
|
+
return {
|
|
607
|
+
state: {
|
|
608
|
+
...utils.pick(this.store.state, ['latestLocation', 'currentLocation', 'status', 'lastUpdated']),
|
|
609
|
+
currentMatches: this.store.state.currentMatches.map(match => ({
|
|
610
|
+
matchId: match.id,
|
|
611
|
+
state: {
|
|
612
|
+
...utils.pick(match.store.state, ['status', 'routeLoaderData', 'invalidAt', 'invalid'])
|
|
613
|
+
}
|
|
614
|
+
}))
|
|
615
|
+
},
|
|
616
|
+
context: this.options.context
|
|
617
|
+
};
|
|
618
|
+
};
|
|
619
|
+
hydrate = dehydratedRouter => {
|
|
620
|
+
this.store.setState(s => {
|
|
621
|
+
// Update the context TODO: make this part of state?
|
|
622
|
+
this.options.context = dehydratedRouter.context;
|
|
623
|
+
|
|
624
|
+
// Match the routes
|
|
625
|
+
const currentMatches = this.matchRoutes(dehydratedRouter.state.latestLocation.pathname, {
|
|
626
|
+
strictParseParams: true
|
|
627
|
+
});
|
|
628
|
+
currentMatches.forEach((match, index) => {
|
|
629
|
+
const dehydratedMatch = dehydratedRouter.state.currentMatches[index];
|
|
630
|
+
invariant__default["default"](dehydratedMatch && dehydratedMatch.matchId === match.id, 'Oh no! There was a hydration mismatch when attempting to rethis.store the state of the router! 😬');
|
|
631
|
+
Object.assign(match, dehydratedMatch);
|
|
632
|
+
});
|
|
633
|
+
currentMatches.forEach(match => match.__validate());
|
|
634
|
+
Object.assign(s, {
|
|
635
|
+
...dehydratedRouter.state,
|
|
636
|
+
currentMatches
|
|
637
|
+
});
|
|
638
|
+
});
|
|
639
|
+
};
|
|
640
|
+
getLoader = opts => {
|
|
641
|
+
const id = opts.from || '/';
|
|
642
|
+
const route = this.getRoute(id);
|
|
643
|
+
if (!route) return undefined;
|
|
644
|
+
let loader = this.store.state.loaders[id] || (() => {
|
|
645
|
+
this.store.setState(s => {
|
|
646
|
+
s.loaders[id] = {
|
|
647
|
+
pending: [],
|
|
648
|
+
fetch: async loaderContext => {
|
|
649
|
+
if (!route) {
|
|
650
|
+
return;
|
|
651
|
+
}
|
|
652
|
+
const loaderState = {
|
|
653
|
+
loadedAt: Date.now(),
|
|
654
|
+
loaderContext
|
|
655
|
+
};
|
|
656
|
+
this.store.setState(s => {
|
|
657
|
+
s.loaders[id].current = loaderState;
|
|
658
|
+
s.loaders[id].latest = loaderState;
|
|
659
|
+
s.loaders[id].pending.push(loaderState);
|
|
660
|
+
});
|
|
661
|
+
try {
|
|
662
|
+
return await route.options.loader?.(loaderContext);
|
|
663
|
+
} finally {
|
|
664
|
+
this.store.setState(s => {
|
|
665
|
+
s.loaders[id].pending = s.loaders[id].pending.filter(d => d !== loaderState);
|
|
666
|
+
});
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
};
|
|
670
|
+
});
|
|
671
|
+
return this.store.state.loaders[id];
|
|
672
|
+
})();
|
|
673
|
+
return loader;
|
|
674
|
+
};
|
|
675
|
+
#buildRouteTree = rootRouteConfig => {
|
|
72
676
|
const recurseRoutes = (routeConfigs, parent) => {
|
|
73
677
|
return routeConfigs.map((routeConfig, i) => {
|
|
74
678
|
const routeOptions = routeConfig.options;
|
|
75
|
-
const route$1 = route.
|
|
76
|
-
const existingRoute =
|
|
679
|
+
const route$1 = new route.Route(routeConfig, routeOptions, i, parent, this);
|
|
680
|
+
const existingRoute = this.routesById[route$1.id];
|
|
77
681
|
if (existingRoute) {
|
|
78
682
|
if (process.env.NODE_ENV !== 'production') {
|
|
79
|
-
console.warn(`Duplicate routes found with id: ${String(route$1.
|
|
683
|
+
console.warn(`Duplicate routes found with id: ${String(route$1.id)}`, this.routesById, route$1);
|
|
80
684
|
}
|
|
81
685
|
throw new Error();
|
|
82
686
|
}
|
|
83
|
-
|
|
687
|
+
this.routesById[route$1.id] = route$1;
|
|
84
688
|
const children = routeConfig.children;
|
|
85
|
-
route$1.childRoutes = children
|
|
689
|
+
route$1.childRoutes = children.length ? recurseRoutes(children, route$1) : undefined;
|
|
86
690
|
return route$1;
|
|
87
691
|
});
|
|
88
692
|
};
|
|
89
693
|
const routes = recurseRoutes([rootRouteConfig]);
|
|
90
694
|
return routes[0];
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
|
|
695
|
+
};
|
|
696
|
+
#parseLocation = previousLocation => {
|
|
697
|
+
let {
|
|
698
|
+
pathname,
|
|
699
|
+
search,
|
|
700
|
+
hash,
|
|
701
|
+
state
|
|
702
|
+
} = this.history.location;
|
|
703
|
+
const parsedSearch = this.options.parseSearch(search);
|
|
704
|
+
console.log({
|
|
705
|
+
pathname: pathname,
|
|
706
|
+
searchStr: search,
|
|
707
|
+
search: interop.replaceEqualDeep(previousLocation?.search, parsedSearch),
|
|
708
|
+
hash: hash.split('#').reverse()[0] ?? '',
|
|
709
|
+
href: `${pathname}${search}${hash}`,
|
|
710
|
+
state: state,
|
|
711
|
+
key: state?.key || '__init__'
|
|
712
|
+
});
|
|
94
713
|
return {
|
|
95
|
-
pathname:
|
|
96
|
-
searchStr:
|
|
97
|
-
search:
|
|
98
|
-
hash:
|
|
99
|
-
href: `${
|
|
100
|
-
state:
|
|
101
|
-
key:
|
|
714
|
+
pathname: pathname,
|
|
715
|
+
searchStr: search,
|
|
716
|
+
search: interop.replaceEqualDeep(previousLocation?.search, parsedSearch),
|
|
717
|
+
hash: hash.split('#').reverse()[0] ?? '',
|
|
718
|
+
href: `${pathname}${search}${hash}`,
|
|
719
|
+
state: state,
|
|
720
|
+
key: state?.key || '__init__'
|
|
102
721
|
};
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
dest = {};
|
|
112
|
-
}
|
|
113
|
-
const fromPathname = dest.fromCurrent ? store.latestLocation.pathname : dest.from ?? store.latestLocation.pathname;
|
|
114
|
-
let pathname = path.resolvePath(router.basepath ?? '/', fromPathname, `${dest.to ?? '.'}`);
|
|
115
|
-
const fromMatches = router.matchRoutes(store.latestLocation.pathname, {
|
|
722
|
+
};
|
|
723
|
+
#onFocus = () => {
|
|
724
|
+
this.load();
|
|
725
|
+
};
|
|
726
|
+
#buildLocation = (dest = {}) => {
|
|
727
|
+
const fromPathname = dest.fromCurrent ? this.store.state.latestLocation.pathname : dest.from ?? this.store.state.latestLocation.pathname;
|
|
728
|
+
let pathname = path.resolvePath(this.basepath ?? '/', fromPathname, `${dest.to ?? '.'}`);
|
|
729
|
+
const fromMatches = this.matchRoutes(this.store.state.latestLocation.pathname, {
|
|
116
730
|
strictParseParams: true
|
|
117
731
|
});
|
|
118
|
-
const toMatches =
|
|
732
|
+
const toMatches = this.matchRoutes(pathname);
|
|
119
733
|
const prevParams = {
|
|
120
|
-
...
|
|
734
|
+
...utils.last(fromMatches)?.params
|
|
121
735
|
};
|
|
122
736
|
let nextParams = (dest.params ?? true) === true ? prevParams : utils.functionalUpdate(dest.params, prevParams);
|
|
123
737
|
if (nextParams) {
|
|
124
|
-
toMatches.map(d => d.options.stringifyParams).filter(Boolean).forEach(fn => {
|
|
738
|
+
toMatches.map(d => d.route.options.stringifyParams).filter(Boolean).forEach(fn => {
|
|
125
739
|
Object.assign({}, nextParams, fn(nextParams));
|
|
126
740
|
});
|
|
127
741
|
}
|
|
128
742
|
pathname = path.interpolatePath(pathname, nextParams ?? {});
|
|
129
743
|
|
|
130
744
|
// Pre filters first
|
|
131
|
-
const preFilteredSearch =
|
|
745
|
+
const preFilteredSearch = dest.__preSearchFilters?.length ? dest.__preSearchFilters?.reduce((prev, next) => next(prev), this.store.state.latestLocation.search) : this.store.state.latestLocation.search;
|
|
132
746
|
|
|
133
747
|
// Then the link/navigate function
|
|
134
748
|
const destSearch = dest.search === true ? preFilteredSearch // Preserve resolvedFrom true
|
|
135
749
|
: dest.search ? utils.functionalUpdate(dest.search, preFilteredSearch) ?? {} // Updater
|
|
136
|
-
:
|
|
750
|
+
: dest.__preSearchFilters?.length ? preFilteredSearch // Preserve resolvedFrom filters
|
|
137
751
|
: {};
|
|
138
752
|
|
|
139
753
|
// Then post filters
|
|
140
|
-
const postFilteredSearch =
|
|
141
|
-
const search =
|
|
142
|
-
const searchStr =
|
|
143
|
-
let hash = dest.hash === true ? store.latestLocation.hash : utils.functionalUpdate(dest.hash, store.latestLocation.hash);
|
|
754
|
+
const postFilteredSearch = dest.__postSearchFilters?.length ? dest.__postSearchFilters.reduce((prev, next) => next(prev), destSearch) : destSearch;
|
|
755
|
+
const search = interop.replaceEqualDeep(this.store.state.latestLocation.search, postFilteredSearch);
|
|
756
|
+
const searchStr = this.options.stringifySearch(search);
|
|
757
|
+
let hash = dest.hash === true ? this.store.state.latestLocation.hash : utils.functionalUpdate(dest.hash, this.store.state.latestLocation.hash);
|
|
144
758
|
hash = hash ? `#${hash}` : '';
|
|
145
759
|
return {
|
|
146
760
|
pathname,
|
|
147
761
|
search,
|
|
148
762
|
searchStr,
|
|
149
|
-
state: store.latestLocation.state,
|
|
763
|
+
state: this.store.state.latestLocation.state,
|
|
150
764
|
hash,
|
|
151
765
|
href: `${pathname}${searchStr}${hash}`,
|
|
152
766
|
key: dest.key
|
|
153
767
|
};
|
|
154
|
-
}
|
|
155
|
-
|
|
768
|
+
};
|
|
769
|
+
#commitLocation = location => {
|
|
770
|
+
const next = this.buildNext(location);
|
|
156
771
|
const id = '' + Date.now() + Math.random();
|
|
772
|
+
if (this.navigateTimeout) clearTimeout(this.navigateTimeout);
|
|
157
773
|
let nextAction = 'replace';
|
|
158
|
-
if (!replace) {
|
|
774
|
+
if (!location.replace) {
|
|
159
775
|
nextAction = 'push';
|
|
160
776
|
}
|
|
161
|
-
const isSameUrl =
|
|
777
|
+
const isSameUrl = this.store.state.latestLocation.href === next.href;
|
|
162
778
|
if (isSameUrl && !next.key) {
|
|
163
779
|
nextAction = 'replace';
|
|
164
780
|
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
hash: next.hash,
|
|
168
|
-
search: next.searchStr
|
|
169
|
-
}, {
|
|
781
|
+
const href = `${next.pathname}${next.searchStr}${next.hash ? `#${next.hash}` : ''}`;
|
|
782
|
+
this.history[nextAction === 'push' ? 'push' : 'replace'](href, {
|
|
170
783
|
id,
|
|
171
784
|
...next.state
|
|
172
785
|
});
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
786
|
+
this.load(this.#parseLocation(this.store.state.latestLocation));
|
|
787
|
+
return this.navigationPromise = new Promise(resolve => {
|
|
788
|
+
const previousNavigationResolve = this.resolveNavigation;
|
|
789
|
+
this.resolveNavigation = () => {
|
|
176
790
|
previousNavigationResolve();
|
|
177
791
|
resolve();
|
|
178
792
|
};
|
|
179
793
|
});
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
types: undefined,
|
|
183
|
-
// public api
|
|
184
|
-
history: (userOptions == null ? void 0 : userOptions.history) || createDefaultHistory(),
|
|
185
|
-
store,
|
|
186
|
-
setStore,
|
|
187
|
-
options: originalOptions,
|
|
188
|
-
basepath: '',
|
|
189
|
-
routeTree: undefined,
|
|
190
|
-
routesById: {},
|
|
191
|
-
reset: () => {
|
|
192
|
-
setStore(s => Object.assign(s, getInitialRouterState()));
|
|
193
|
-
},
|
|
194
|
-
getRoute: id => {
|
|
195
|
-
return router.routesById[id];
|
|
196
|
-
},
|
|
197
|
-
dehydrate: () => {
|
|
198
|
-
return {
|
|
199
|
-
store: {
|
|
200
|
-
...utils.pick(store, ['latestLocation', 'currentLocation', 'status', 'lastUpdated']),
|
|
201
|
-
currentMatches: store.currentMatches.map(match => ({
|
|
202
|
-
matchId: match.matchId,
|
|
203
|
-
store: utils.pick(match.store, ['status', 'routeLoaderData', 'isInvalid', 'invalidAt'])
|
|
204
|
-
}))
|
|
205
|
-
},
|
|
206
|
-
context: router.options.context
|
|
207
|
-
};
|
|
208
|
-
},
|
|
209
|
-
hydrate: dehydratedRouter => {
|
|
210
|
-
setStore(s => {
|
|
211
|
-
// Update the context TODO: make this part of state?
|
|
212
|
-
router.options.context = dehydratedRouter.context;
|
|
213
|
-
|
|
214
|
-
// Match the routes
|
|
215
|
-
const currentMatches = router.matchRoutes(dehydratedRouter.store.latestLocation.pathname, {
|
|
216
|
-
strictParseParams: true
|
|
217
|
-
});
|
|
218
|
-
currentMatches.forEach((match, index) => {
|
|
219
|
-
const dehydratedMatch = dehydratedRouter.store.currentMatches[index];
|
|
220
|
-
invariant__default["default"](dehydratedMatch && dehydratedMatch.matchId === match.matchId, 'Oh no! There was a hydration mismatch when attempting to restore the state of the router! 😬');
|
|
221
|
-
Object.assign(match, dehydratedMatch);
|
|
222
|
-
});
|
|
223
|
-
currentMatches.forEach(match => match.__.validate());
|
|
224
|
-
Object.assign(s, {
|
|
225
|
-
...dehydratedRouter.store,
|
|
226
|
-
currentMatches
|
|
227
|
-
});
|
|
228
|
-
});
|
|
229
|
-
},
|
|
230
|
-
mount: () => {
|
|
231
|
-
// Mount only does anything on the client
|
|
232
|
-
if (!isServer) {
|
|
233
|
-
// If the router matches are empty, load the matches
|
|
234
|
-
if (!store.currentMatches.length) {
|
|
235
|
-
router.load();
|
|
236
|
-
}
|
|
237
|
-
const unsub = router.history.listen(event => {
|
|
238
|
-
router.load(parseLocation(event.location, store.latestLocation));
|
|
239
|
-
});
|
|
240
|
-
|
|
241
|
-
// addEventListener does not exist in React Native, but window does
|
|
242
|
-
// In the future, we might need to invert control here for more adapters
|
|
243
|
-
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
244
|
-
if (window.addEventListener) {
|
|
245
|
-
// Listen to visibilitychange and focus
|
|
246
|
-
window.addEventListener('visibilitychange', onFocus, false);
|
|
247
|
-
window.addEventListener('focus', onFocus, false);
|
|
248
|
-
}
|
|
249
|
-
return () => {
|
|
250
|
-
unsub();
|
|
251
|
-
if (window.removeEventListener) {
|
|
252
|
-
// Be sure to unsubscribe if a new handler is set
|
|
253
|
-
window.removeEventListener('visibilitychange', onFocus);
|
|
254
|
-
window.removeEventListener('focus', onFocus);
|
|
255
|
-
}
|
|
256
|
-
};
|
|
257
|
-
}
|
|
258
|
-
return () => {};
|
|
259
|
-
},
|
|
260
|
-
update: opts => {
|
|
261
|
-
const newHistory = (opts == null ? void 0 : opts.history) !== router.history;
|
|
262
|
-
if (!store.latestLocation || newHistory) {
|
|
263
|
-
if (opts != null && opts.history) {
|
|
264
|
-
router.history = opts.history;
|
|
265
|
-
}
|
|
266
|
-
setStore(s => {
|
|
267
|
-
s.latestLocation = parseLocation(router.history.location);
|
|
268
|
-
s.currentLocation = s.latestLocation;
|
|
269
|
-
});
|
|
270
|
-
}
|
|
271
|
-
Object.assign(router.options, opts);
|
|
272
|
-
const {
|
|
273
|
-
basepath,
|
|
274
|
-
routeConfig
|
|
275
|
-
} = router.options;
|
|
276
|
-
router.basepath = `/${path.trimPath(basepath ?? '') ?? ''}`;
|
|
277
|
-
if (routeConfig) {
|
|
278
|
-
router.routesById = {};
|
|
279
|
-
router.routeTree = buildRouteTree(routeConfig);
|
|
280
|
-
}
|
|
281
|
-
return router;
|
|
282
|
-
},
|
|
283
|
-
cancelMatches: () => {
|
|
284
|
-
[...store.currentMatches, ...(store.pendingMatches || [])].forEach(match => {
|
|
285
|
-
match.cancel();
|
|
286
|
-
});
|
|
287
|
-
},
|
|
288
|
-
load: async next => {
|
|
289
|
-
let now = Date.now();
|
|
290
|
-
const startedAt = now;
|
|
291
|
-
startedLoadingAt = startedAt;
|
|
292
|
-
|
|
293
|
-
// Cancel any pending matches
|
|
294
|
-
router.cancelMatches();
|
|
295
|
-
let matches;
|
|
296
|
-
reactivity.batch(() => {
|
|
297
|
-
if (next) {
|
|
298
|
-
// Ingest the new location
|
|
299
|
-
setStore(s => {
|
|
300
|
-
s.latestLocation = next;
|
|
301
|
-
});
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
// Match the routes
|
|
305
|
-
matches = router.matchRoutes(store.latestLocation.pathname, {
|
|
306
|
-
strictParseParams: true
|
|
307
|
-
});
|
|
308
|
-
console.log('set loading', matches);
|
|
309
|
-
setStore(s => {
|
|
310
|
-
s.status = 'loading';
|
|
311
|
-
s.pendingMatches = matches;
|
|
312
|
-
s.pendingLocation = store.latestLocation;
|
|
313
|
-
});
|
|
314
|
-
});
|
|
315
|
-
|
|
316
|
-
// Load the matches
|
|
317
|
-
try {
|
|
318
|
-
await router.loadMatches(matches);
|
|
319
|
-
} catch (err) {
|
|
320
|
-
console.log(err);
|
|
321
|
-
invariant__default["default"](false, 'Matches failed to load due to error above ☝️. Navigation cancelled!');
|
|
322
|
-
}
|
|
323
|
-
if (startedLoadingAt !== startedAt) {
|
|
324
|
-
// Ignore side-effects of outdated side-effects
|
|
325
|
-
return navigationPromise;
|
|
326
|
-
}
|
|
327
|
-
const previousMatches = store.currentMatches;
|
|
328
|
-
const exiting = [],
|
|
329
|
-
staying = [];
|
|
330
|
-
previousMatches.forEach(d => {
|
|
331
|
-
if (matches.find(dd => dd.matchId === d.matchId)) {
|
|
332
|
-
staying.push(d);
|
|
333
|
-
} else {
|
|
334
|
-
exiting.push(d);
|
|
335
|
-
}
|
|
336
|
-
});
|
|
337
|
-
const entering = matches.filter(d => {
|
|
338
|
-
return !previousMatches.find(dd => dd.matchId === d.matchId);
|
|
339
|
-
});
|
|
340
|
-
now = Date.now();
|
|
341
|
-
exiting.forEach(d => {
|
|
342
|
-
d.__.onExit == null ? void 0 : d.__.onExit({
|
|
343
|
-
params: d.params,
|
|
344
|
-
search: d.store.routeSearch
|
|
345
|
-
});
|
|
346
|
-
|
|
347
|
-
// Clear idle error states when match leaves
|
|
348
|
-
if (d.store.status === 'error' && !d.store.isFetching) {
|
|
349
|
-
d.store.status = 'idle';
|
|
350
|
-
d.store.error = undefined;
|
|
351
|
-
}
|
|
352
|
-
const gc = Math.max(d.options.loaderGcMaxAge ?? router.options.defaultLoaderGcMaxAge ?? 0, d.options.loaderMaxAge ?? router.options.defaultLoaderMaxAge ?? 0);
|
|
353
|
-
if (gc > 0) {
|
|
354
|
-
store.matchCache[d.matchId] = {
|
|
355
|
-
gc: gc == Infinity ? Number.MAX_SAFE_INTEGER : now + gc,
|
|
356
|
-
match: d
|
|
357
|
-
};
|
|
358
|
-
}
|
|
359
|
-
});
|
|
360
|
-
staying.forEach(d => {
|
|
361
|
-
d.options.onTransition == null ? void 0 : d.options.onTransition({
|
|
362
|
-
params: d.params,
|
|
363
|
-
search: d.store.routeSearch
|
|
364
|
-
});
|
|
365
|
-
});
|
|
366
|
-
entering.forEach(d => {
|
|
367
|
-
d.__.onExit = d.options.onLoaded == null ? void 0 : d.options.onLoaded({
|
|
368
|
-
params: d.params,
|
|
369
|
-
search: d.store.search
|
|
370
|
-
});
|
|
371
|
-
delete store.matchCache[d.matchId];
|
|
372
|
-
});
|
|
373
|
-
if (startedLoadingAt !== startedAt) {
|
|
374
|
-
// Ignore side-effects of match loading
|
|
375
|
-
return;
|
|
376
|
-
}
|
|
377
|
-
matches.forEach(match => {
|
|
378
|
-
// Clear actions
|
|
379
|
-
if (match.action) {
|
|
380
|
-
// TODO: Check reactivity here
|
|
381
|
-
match.action.current = undefined;
|
|
382
|
-
match.action.submissions = [];
|
|
383
|
-
}
|
|
384
|
-
});
|
|
385
|
-
setStore(s => {
|
|
386
|
-
console.log('set', matches);
|
|
387
|
-
Object.assign(s, {
|
|
388
|
-
status: 'idle',
|
|
389
|
-
currentLocation: store.latestLocation,
|
|
390
|
-
currentMatches: matches,
|
|
391
|
-
pendingLocation: undefined,
|
|
392
|
-
pendingMatches: undefined
|
|
393
|
-
});
|
|
394
|
-
});
|
|
395
|
-
resolveNavigation();
|
|
396
|
-
},
|
|
397
|
-
cleanMatchCache: () => {
|
|
398
|
-
const now = Date.now();
|
|
399
|
-
setStore(s => {
|
|
400
|
-
Object.keys(s.matchCache).forEach(matchId => {
|
|
401
|
-
const entry = s.matchCache[matchId];
|
|
402
|
-
|
|
403
|
-
// Don't remove loading matches
|
|
404
|
-
if (entry.match.store.status === 'loading') {
|
|
405
|
-
return;
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
// Do not remove successful matches that are still valid
|
|
409
|
-
if (entry.gc > 0 && entry.gc > now) {
|
|
410
|
-
return;
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
// Everything else gets removed
|
|
414
|
-
delete s.matchCache[matchId];
|
|
415
|
-
});
|
|
416
|
-
});
|
|
417
|
-
},
|
|
418
|
-
loadRoute: async function (navigateOpts) {
|
|
419
|
-
if (navigateOpts === void 0) {
|
|
420
|
-
navigateOpts = store.latestLocation;
|
|
421
|
-
}
|
|
422
|
-
const next = router.buildNext(navigateOpts);
|
|
423
|
-
const matches = router.matchRoutes(next.pathname, {
|
|
424
|
-
strictParseParams: true
|
|
425
|
-
});
|
|
426
|
-
await router.loadMatches(matches);
|
|
427
|
-
return matches;
|
|
428
|
-
},
|
|
429
|
-
preloadRoute: async function (navigateOpts, loaderOpts) {
|
|
430
|
-
if (navigateOpts === void 0) {
|
|
431
|
-
navigateOpts = store.latestLocation;
|
|
432
|
-
}
|
|
433
|
-
const next = router.buildNext(navigateOpts);
|
|
434
|
-
const matches = router.matchRoutes(next.pathname, {
|
|
435
|
-
strictParseParams: true
|
|
436
|
-
});
|
|
437
|
-
await router.loadMatches(matches, {
|
|
438
|
-
preload: true,
|
|
439
|
-
maxAge: loaderOpts.maxAge ?? router.options.defaultPreloadMaxAge ?? router.options.defaultLoaderMaxAge ?? 0,
|
|
440
|
-
gcMaxAge: loaderOpts.gcMaxAge ?? router.options.defaultPreloadGcMaxAge ?? router.options.defaultLoaderGcMaxAge ?? 0
|
|
441
|
-
});
|
|
442
|
-
return matches;
|
|
443
|
-
},
|
|
444
|
-
matchRoutes: (pathname, opts) => {
|
|
445
|
-
router.cleanMatchCache();
|
|
446
|
-
const matches = [];
|
|
447
|
-
if (!router.routeTree) {
|
|
448
|
-
return matches;
|
|
449
|
-
}
|
|
450
|
-
const existingMatches = [...store.currentMatches, ...(store.pendingMatches ?? [])];
|
|
451
|
-
const recurse = async routes => {
|
|
452
|
-
var _foundRoute$childRout;
|
|
453
|
-
const parentMatch = utils.last(matches);
|
|
454
|
-
let params = (parentMatch == null ? void 0 : parentMatch.params) ?? {};
|
|
455
|
-
const filteredRoutes = (router.options.filterRoutes == null ? void 0 : router.options.filterRoutes(routes)) ?? routes;
|
|
456
|
-
let foundRoutes = [];
|
|
457
|
-
const findMatchInRoutes = (parentRoutes, routes) => {
|
|
458
|
-
routes.some(route => {
|
|
459
|
-
var _route$childRoutes, _route$childRoutes2;
|
|
460
|
-
if (!route.routePath && (_route$childRoutes = route.childRoutes) != null && _route$childRoutes.length) {
|
|
461
|
-
return findMatchInRoutes([...foundRoutes, route], route.childRoutes);
|
|
462
|
-
}
|
|
463
|
-
const fuzzy = !!(route.routePath !== '/' || (_route$childRoutes2 = route.childRoutes) != null && _route$childRoutes2.length);
|
|
464
|
-
const matchParams = path.matchPathname(router.basepath, pathname, {
|
|
465
|
-
to: route.fullPath,
|
|
466
|
-
fuzzy,
|
|
467
|
-
caseSensitive: route.options.caseSensitive ?? router.options.caseSensitive
|
|
468
|
-
});
|
|
469
|
-
if (matchParams) {
|
|
470
|
-
let parsedParams;
|
|
471
|
-
try {
|
|
472
|
-
parsedParams = (route.options.parseParams == null ? void 0 : route.options.parseParams(matchParams)) ?? matchParams;
|
|
473
|
-
} catch (err) {
|
|
474
|
-
if (opts != null && opts.strictParseParams) {
|
|
475
|
-
throw err;
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
params = {
|
|
479
|
-
...params,
|
|
480
|
-
...parsedParams
|
|
481
|
-
};
|
|
482
|
-
}
|
|
483
|
-
if (!!matchParams) {
|
|
484
|
-
foundRoutes = [...parentRoutes, route];
|
|
485
|
-
}
|
|
486
|
-
return !!foundRoutes.length;
|
|
487
|
-
});
|
|
488
|
-
return !!foundRoutes.length;
|
|
489
|
-
};
|
|
490
|
-
findMatchInRoutes([], filteredRoutes);
|
|
491
|
-
if (!foundRoutes.length) {
|
|
492
|
-
return;
|
|
493
|
-
}
|
|
494
|
-
foundRoutes.forEach(foundRoute => {
|
|
495
|
-
var _store$matchCache$mat;
|
|
496
|
-
const interpolatedPath = path.interpolatePath(foundRoute.routePath, params);
|
|
497
|
-
const matchId = path.interpolatePath(foundRoute.routeId, params, true);
|
|
498
|
-
const match = existingMatches.find(d => d.matchId === matchId) || ((_store$matchCache$mat = store.matchCache[matchId]) == null ? void 0 : _store$matchCache$mat.match) || routeMatch.createRouteMatch(router, foundRoute, {
|
|
499
|
-
parentMatch,
|
|
500
|
-
matchId,
|
|
501
|
-
params,
|
|
502
|
-
pathname: path.joinPaths([router.basepath, interpolatedPath])
|
|
503
|
-
});
|
|
504
|
-
matches.push(match);
|
|
505
|
-
});
|
|
506
|
-
const foundRoute = utils.last(foundRoutes);
|
|
507
|
-
if ((_foundRoute$childRout = foundRoute.childRoutes) != null && _foundRoute$childRout.length) {
|
|
508
|
-
recurse(foundRoute.childRoutes);
|
|
509
|
-
}
|
|
510
|
-
};
|
|
511
|
-
recurse([router.routeTree]);
|
|
512
|
-
linkMatches(matches);
|
|
513
|
-
return matches;
|
|
514
|
-
},
|
|
515
|
-
loadMatches: async (resolvedMatches, loaderOpts) => {
|
|
516
|
-
resolvedMatches.forEach(async match => {
|
|
517
|
-
// Validate the match (loads search params etc)
|
|
518
|
-
match.__.validate();
|
|
519
|
-
});
|
|
520
|
-
|
|
521
|
-
// Check each match middleware to see if the route can be accessed
|
|
522
|
-
await Promise.all(resolvedMatches.map(async match => {
|
|
523
|
-
try {
|
|
524
|
-
await (match.options.beforeLoad == null ? void 0 : match.options.beforeLoad({
|
|
525
|
-
router: router,
|
|
526
|
-
match
|
|
527
|
-
}));
|
|
528
|
-
} catch (err) {
|
|
529
|
-
if (!(loaderOpts != null && loaderOpts.preload)) {
|
|
530
|
-
match.options.onLoadError == null ? void 0 : match.options.onLoadError(err);
|
|
531
|
-
}
|
|
532
|
-
throw err;
|
|
533
|
-
}
|
|
534
|
-
}));
|
|
535
|
-
const matchPromises = resolvedMatches.map(async match => {
|
|
536
|
-
var _search$__data;
|
|
537
|
-
const search = match.store.search;
|
|
538
|
-
if ((_search$__data = search.__data) != null && _search$__data.matchId && search.__data.matchId !== match.matchId) {
|
|
539
|
-
return;
|
|
540
|
-
}
|
|
541
|
-
match.load(loaderOpts);
|
|
542
|
-
if (match.store.status !== 'success' && match.__.loadPromise) {
|
|
543
|
-
// Wait for the first sign of activity from the match
|
|
544
|
-
await match.__.loadPromise;
|
|
545
|
-
}
|
|
546
|
-
});
|
|
547
|
-
await Promise.all(matchPromises);
|
|
548
|
-
},
|
|
549
|
-
loadMatchData: async routeMatch => {
|
|
550
|
-
if (isServer || !router.options.useServerData) {
|
|
551
|
-
return (await (routeMatch.options.loader == null ? void 0 : routeMatch.options.loader({
|
|
552
|
-
// parentLoaderPromise: routeMatch.parentMatch?.__.dataPromise,
|
|
553
|
-
params: routeMatch.params,
|
|
554
|
-
search: routeMatch.store.routeSearch,
|
|
555
|
-
signal: routeMatch.__.abortController.signal
|
|
556
|
-
}))) || {};
|
|
557
|
-
} else {
|
|
558
|
-
const next = router.buildNext({
|
|
559
|
-
to: '.',
|
|
560
|
-
search: d => ({
|
|
561
|
-
...(d ?? {}),
|
|
562
|
-
__data: {
|
|
563
|
-
matchId: routeMatch.matchId
|
|
564
|
-
}
|
|
565
|
-
})
|
|
566
|
-
});
|
|
567
|
-
|
|
568
|
-
// Refresh:
|
|
569
|
-
// '/dashboard'
|
|
570
|
-
// '/dashboard/invoices/'
|
|
571
|
-
// '/dashboard/invoices/123'
|
|
572
|
-
|
|
573
|
-
// New:
|
|
574
|
-
// '/dashboard/invoices/456'
|
|
575
|
-
|
|
576
|
-
// TODO: batch requests when possible
|
|
577
|
-
|
|
578
|
-
const res = await fetch(next.href, {
|
|
579
|
-
method: 'GET'
|
|
580
|
-
// signal: routeMatch.__.abortController.signal,
|
|
581
|
-
});
|
|
582
|
-
|
|
583
|
-
if (res.ok) {
|
|
584
|
-
return res.json();
|
|
585
|
-
}
|
|
586
|
-
throw new Error('Failed to fetch match data');
|
|
587
|
-
}
|
|
588
|
-
},
|
|
589
|
-
invalidateRoute: opts => {
|
|
590
|
-
const next = router.buildNext(opts);
|
|
591
|
-
const unloadedMatchIds = router.matchRoutes(next.pathname).map(d => d.matchId);
|
|
592
|
-
[...store.currentMatches, ...(store.pendingMatches ?? [])].forEach(match => {
|
|
593
|
-
if (unloadedMatchIds.includes(match.matchId)) {
|
|
594
|
-
match.invalidate();
|
|
595
|
-
}
|
|
596
|
-
});
|
|
597
|
-
},
|
|
598
|
-
reload: () => navigate({
|
|
599
|
-
fromCurrent: true,
|
|
600
|
-
replace: true,
|
|
601
|
-
search: true
|
|
602
|
-
}),
|
|
603
|
-
resolvePath: (from, path$1) => {
|
|
604
|
-
return path.resolvePath(router.basepath, from, path.cleanPath(path$1));
|
|
605
|
-
},
|
|
606
|
-
matchRoute: (location, opts) => {
|
|
607
|
-
// const location = router.buildNext(opts)
|
|
608
|
-
|
|
609
|
-
location = {
|
|
610
|
-
...location,
|
|
611
|
-
to: location.to ? router.resolvePath(location.from ?? '', location.to) : undefined
|
|
612
|
-
};
|
|
613
|
-
const next = router.buildNext(location);
|
|
614
|
-
if (opts != null && opts.pending) {
|
|
615
|
-
if (!store.pendingLocation) {
|
|
616
|
-
return false;
|
|
617
|
-
}
|
|
618
|
-
return !!path.matchPathname(router.basepath, store.pendingLocation.pathname, {
|
|
619
|
-
...opts,
|
|
620
|
-
to: next.pathname
|
|
621
|
-
});
|
|
622
|
-
}
|
|
623
|
-
return path.matchPathname(router.basepath, store.currentLocation.pathname, {
|
|
624
|
-
...opts,
|
|
625
|
-
to: next.pathname
|
|
626
|
-
});
|
|
627
|
-
},
|
|
628
|
-
navigate: async _ref => {
|
|
629
|
-
let {
|
|
630
|
-
from,
|
|
631
|
-
to = '.',
|
|
632
|
-
search,
|
|
633
|
-
hash,
|
|
634
|
-
replace,
|
|
635
|
-
params
|
|
636
|
-
} = _ref;
|
|
637
|
-
// If this link simply reloads the current route,
|
|
638
|
-
// make sure it has a new key so it will trigger a data refresh
|
|
639
|
-
|
|
640
|
-
// If this `to` is a valid external URL, return
|
|
641
|
-
// null for LinkUtils
|
|
642
|
-
const toString = String(to);
|
|
643
|
-
const fromString = String(from);
|
|
644
|
-
let isExternal;
|
|
645
|
-
try {
|
|
646
|
-
new URL(`${toString}`);
|
|
647
|
-
isExternal = true;
|
|
648
|
-
} catch (e) {}
|
|
649
|
-
invariant__default["default"](!isExternal, 'Attempting to navigate to external url with router.navigate!');
|
|
650
|
-
return navigate({
|
|
651
|
-
from: fromString,
|
|
652
|
-
to: toString,
|
|
653
|
-
search,
|
|
654
|
-
hash,
|
|
655
|
-
replace,
|
|
656
|
-
params
|
|
657
|
-
});
|
|
658
|
-
},
|
|
659
|
-
buildLink: _ref2 => {
|
|
660
|
-
let {
|
|
661
|
-
from,
|
|
662
|
-
to = '.',
|
|
663
|
-
search,
|
|
664
|
-
params,
|
|
665
|
-
hash,
|
|
666
|
-
target,
|
|
667
|
-
replace,
|
|
668
|
-
activeOptions,
|
|
669
|
-
preload,
|
|
670
|
-
preloadMaxAge: userPreloadMaxAge,
|
|
671
|
-
preloadGcMaxAge: userPreloadGcMaxAge,
|
|
672
|
-
preloadDelay: userPreloadDelay,
|
|
673
|
-
disabled
|
|
674
|
-
} = _ref2;
|
|
675
|
-
// If this link simply reloads the current route,
|
|
676
|
-
// make sure it has a new key so it will trigger a data refresh
|
|
677
|
-
|
|
678
|
-
// If this `to` is a valid external URL, return
|
|
679
|
-
// null for LinkUtils
|
|
680
|
-
|
|
681
|
-
try {
|
|
682
|
-
new URL(`${to}`);
|
|
683
|
-
return {
|
|
684
|
-
type: 'external',
|
|
685
|
-
href: to
|
|
686
|
-
};
|
|
687
|
-
} catch (e) {}
|
|
688
|
-
const nextOpts = {
|
|
689
|
-
from,
|
|
690
|
-
to,
|
|
691
|
-
search,
|
|
692
|
-
params,
|
|
693
|
-
hash,
|
|
694
|
-
replace
|
|
695
|
-
};
|
|
696
|
-
const next = router.buildNext(nextOpts);
|
|
697
|
-
preload = preload ?? router.options.defaultPreload;
|
|
698
|
-
const preloadDelay = userPreloadDelay ?? router.options.defaultPreloadDelay ?? 0;
|
|
699
|
-
|
|
700
|
-
// Compare path/hash for matches
|
|
701
|
-
const pathIsEqual = store.currentLocation.pathname === next.pathname;
|
|
702
|
-
const currentPathSplit = store.currentLocation.pathname.split('/');
|
|
703
|
-
const nextPathSplit = next.pathname.split('/');
|
|
704
|
-
const pathIsFuzzyEqual = nextPathSplit.every((d, i) => d === currentPathSplit[i]);
|
|
705
|
-
const hashIsEqual = store.currentLocation.hash === next.hash;
|
|
706
|
-
// Combine the matches based on user options
|
|
707
|
-
const pathTest = activeOptions != null && activeOptions.exact ? pathIsEqual : pathIsFuzzyEqual;
|
|
708
|
-
const hashTest = activeOptions != null && activeOptions.includeHash ? hashIsEqual : true;
|
|
709
|
-
|
|
710
|
-
// The final "active" test
|
|
711
|
-
const isActive = pathTest && hashTest;
|
|
712
|
-
|
|
713
|
-
// The click handler
|
|
714
|
-
const handleClick = e => {
|
|
715
|
-
if (!disabled && !isCtrlEvent(e) && !e.defaultPrevented && (!target || target === '_self') && e.button === 0) {
|
|
716
|
-
e.preventDefault();
|
|
717
|
-
if (pathIsEqual && !search && !hash) {
|
|
718
|
-
router.invalidateRoute(nextOpts);
|
|
719
|
-
}
|
|
720
|
-
|
|
721
|
-
// All is well? Navigate!
|
|
722
|
-
navigate(nextOpts);
|
|
723
|
-
}
|
|
724
|
-
};
|
|
794
|
+
};
|
|
795
|
+
}
|
|
725
796
|
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
const target = e.target || {};
|
|
740
|
-
if (preload) {
|
|
741
|
-
if (target.preloadTimeout) {
|
|
742
|
-
return;
|
|
743
|
-
}
|
|
744
|
-
target.preloadTimeout = setTimeout(() => {
|
|
745
|
-
target.preloadTimeout = null;
|
|
746
|
-
router.preloadRoute(nextOpts, {
|
|
747
|
-
maxAge: userPreloadMaxAge,
|
|
748
|
-
gcMaxAge: userPreloadGcMaxAge
|
|
749
|
-
}).catch(err => {
|
|
750
|
-
console.log(err);
|
|
751
|
-
console.warn('Error preloading route! ☝️');
|
|
752
|
-
});
|
|
753
|
-
}, preloadDelay);
|
|
754
|
-
}
|
|
755
|
-
};
|
|
756
|
-
const handleLeave = e => {
|
|
757
|
-
const target = e.target || {};
|
|
758
|
-
if (target.preloadTimeout) {
|
|
759
|
-
clearTimeout(target.preloadTimeout);
|
|
760
|
-
target.preloadTimeout = null;
|
|
761
|
-
}
|
|
762
|
-
};
|
|
763
|
-
return {
|
|
764
|
-
type: 'internal',
|
|
765
|
-
next,
|
|
766
|
-
handleFocus,
|
|
767
|
-
handleClick,
|
|
768
|
-
handleEnter,
|
|
769
|
-
handleLeave,
|
|
770
|
-
isActive,
|
|
771
|
-
disabled
|
|
772
|
-
};
|
|
797
|
+
// Detect if we're in the DOM
|
|
798
|
+
const isServer = typeof window === 'undefined' || !window.document.createElement;
|
|
799
|
+
function getInitialRouterState() {
|
|
800
|
+
return {
|
|
801
|
+
status: 'idle',
|
|
802
|
+
latestLocation: null,
|
|
803
|
+
currentLocation: null,
|
|
804
|
+
currentMatches: [],
|
|
805
|
+
loaders: {},
|
|
806
|
+
lastUpdated: Date.now(),
|
|
807
|
+
matchCache: {},
|
|
808
|
+
get isFetching() {
|
|
809
|
+
return this.status === 'loading' || this.currentMatches.some(d => d.store.state.isFetching);
|
|
773
810
|
},
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
const matches = router.matchRoutes(next.pathname);
|
|
777
|
-
const __preSearchFilters = matches.map(match => match.options.preSearchFilters ?? []).flat().filter(Boolean);
|
|
778
|
-
const __postSearchFilters = matches.map(match => match.options.postSearchFilters ?? []).flat().filter(Boolean);
|
|
779
|
-
return buildLocation({
|
|
780
|
-
...opts,
|
|
781
|
-
__preSearchFilters,
|
|
782
|
-
__postSearchFilters
|
|
783
|
-
});
|
|
811
|
+
get isPreloading() {
|
|
812
|
+
return Object.values(this.matchCache).some(d => d.match.store.state.isFetching && !this.currentMatches.find(dd => dd.id === d.match.id));
|
|
784
813
|
}
|
|
785
814
|
};
|
|
786
|
-
router.update(userOptions);
|
|
787
|
-
|
|
788
|
-
// Allow frameworks to hook into the router creation
|
|
789
|
-
router.options.createRouter == null ? void 0 : router.options.createRouter(router);
|
|
790
|
-
return router;
|
|
791
815
|
}
|
|
792
816
|
function isCtrlEvent(e) {
|
|
793
817
|
return !!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey);
|
|
@@ -796,12 +820,11 @@ function linkMatches(matches) {
|
|
|
796
820
|
matches.forEach((match, index) => {
|
|
797
821
|
const parent = matches[index - 1];
|
|
798
822
|
if (parent) {
|
|
799
|
-
match.
|
|
800
|
-
} else {
|
|
801
|
-
match.__.setParentMatch(undefined);
|
|
823
|
+
match.__setParentMatch(parent);
|
|
802
824
|
}
|
|
803
825
|
});
|
|
804
826
|
}
|
|
805
827
|
|
|
806
|
-
exports.
|
|
828
|
+
exports.Router = Router;
|
|
829
|
+
exports.defaultFetchServerDataFn = defaultFetchServerDataFn;
|
|
807
830
|
//# sourceMappingURL=router.js.map
|