@tanstack/react-router 0.0.1-beta.204 → 0.0.1-beta.206

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 (73) hide show
  1. package/build/cjs/RouterProvider.js +963 -0
  2. package/build/cjs/RouterProvider.js.map +1 -0
  3. package/build/cjs/fileRoute.js +29 -0
  4. package/build/cjs/fileRoute.js.map +1 -0
  5. package/build/cjs/index.js +69 -21
  6. package/build/cjs/index.js.map +1 -1
  7. package/build/cjs/path.js +211 -0
  8. package/build/cjs/path.js.map +1 -0
  9. package/build/cjs/qss.js +65 -0
  10. package/build/cjs/qss.js.map +1 -0
  11. package/build/cjs/react.js +148 -190
  12. package/build/cjs/react.js.map +1 -1
  13. package/build/cjs/redirects.js +27 -0
  14. package/build/cjs/redirects.js.map +1 -0
  15. package/build/cjs/route.js +136 -0
  16. package/build/cjs/route.js.map +1 -0
  17. package/build/cjs/router.js +203 -0
  18. package/build/cjs/router.js.map +1 -0
  19. package/build/cjs/searchParams.js +83 -0
  20. package/build/cjs/searchParams.js.map +1 -0
  21. package/build/cjs/utils.js +196 -0
  22. package/build/cjs/utils.js.map +1 -0
  23. package/build/esm/index.js +1801 -211
  24. package/build/esm/index.js.map +1 -1
  25. package/build/stats-html.html +1 -1
  26. package/build/stats-react.json +385 -164
  27. package/build/types/RouteMatch.d.ts +23 -0
  28. package/build/types/RouterProvider.d.ts +54 -0
  29. package/build/types/awaited.d.ts +0 -8
  30. package/build/types/defer.d.ts +0 -0
  31. package/build/types/fileRoute.d.ts +17 -0
  32. package/build/types/history.d.ts +7 -0
  33. package/build/types/index.d.ts +17 -4
  34. package/build/types/link.d.ts +98 -0
  35. package/build/types/location.d.ts +14 -0
  36. package/build/types/path.d.ts +16 -0
  37. package/build/types/qss.d.ts +2 -0
  38. package/build/types/react.d.ts +23 -83
  39. package/build/types/redirects.d.ts +10 -0
  40. package/build/types/route.d.ts +222 -0
  41. package/build/types/routeInfo.d.ts +22 -0
  42. package/build/types/router.d.ts +115 -0
  43. package/build/types/scroll-restoration.d.ts +0 -3
  44. package/build/types/searchParams.d.ts +7 -0
  45. package/build/types/utils.d.ts +48 -0
  46. package/build/umd/index.development.js +1118 -1540
  47. package/build/umd/index.development.js.map +1 -1
  48. package/build/umd/index.production.js +2 -33
  49. package/build/umd/index.production.js.map +1 -1
  50. package/package.json +2 -4
  51. package/src/RouteMatch.ts +28 -0
  52. package/src/RouterProvider.tsx +1390 -0
  53. package/src/awaited.tsx +40 -40
  54. package/src/defer.ts +55 -0
  55. package/src/fileRoute.ts +143 -0
  56. package/src/history.ts +8 -0
  57. package/src/index.tsx +18 -5
  58. package/src/link.ts +347 -0
  59. package/src/location.ts +14 -0
  60. package/src/path.ts +256 -0
  61. package/src/qss.ts +53 -0
  62. package/src/react.tsx +174 -422
  63. package/src/redirects.ts +31 -0
  64. package/src/route.ts +710 -0
  65. package/src/routeInfo.ts +68 -0
  66. package/src/router.ts +373 -0
  67. package/src/scroll-restoration.tsx +205 -27
  68. package/src/searchParams.ts +78 -0
  69. package/src/utils.ts +257 -0
  70. package/build/cjs/awaited.js +0 -45
  71. package/build/cjs/awaited.js.map +0 -1
  72. package/build/cjs/scroll-restoration.js +0 -56
  73. package/build/cjs/scroll-restoration.js.map +0 -1
@@ -0,0 +1,963 @@
1
+ /**
2
+ * @tanstack/react-router/src/index.tsx
3
+ *
4
+ * Copyright (c) TanStack
5
+ *
6
+ * This source code is licensed under the MIT license found in the
7
+ * LICENSE.md file in the root directory of this source tree.
8
+ *
9
+ * @license MIT
10
+ */
11
+ 'use strict';
12
+
13
+ Object.defineProperty(exports, '__esModule', { value: true });
14
+
15
+ var React = require('react');
16
+ var router = require('./router.js');
17
+ var redirects = require('./redirects.js');
18
+ var utils = require('./utils.js');
19
+ var react = require('./react.js');
20
+ var path = require('./path.js');
21
+ var invariant = require('tiny-invariant');
22
+ var history = require('@tanstack/history');
23
+
24
+ function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
25
+
26
+ function _interopNamespace(e) {
27
+ if (e && e.__esModule) return e;
28
+ var n = Object.create(null);
29
+ if (e) {
30
+ Object.keys(e).forEach(function (k) {
31
+ if (k !== 'default') {
32
+ var d = Object.getOwnPropertyDescriptor(e, k);
33
+ Object.defineProperty(n, k, d.get ? d : {
34
+ enumerable: true,
35
+ get: function () { return e[k]; }
36
+ });
37
+ }
38
+ });
39
+ }
40
+ n["default"] = e;
41
+ return Object.freeze(n);
42
+ }
43
+
44
+ var React__namespace = /*#__PURE__*/_interopNamespace(React);
45
+ var invariant__default = /*#__PURE__*/_interopDefaultLegacy(invariant);
46
+
47
+ const preloadWarning = 'Error preloading route! ☝️';
48
+ const routerContext = /*#__PURE__*/React__namespace.createContext(null);
49
+ function getInitialRouterState(location) {
50
+ return {
51
+ status: 'idle',
52
+ isFetching: false,
53
+ resolvedLocation: location,
54
+ location: location,
55
+ matches: [],
56
+ pendingMatches: [],
57
+ lastUpdated: Date.now()
58
+ };
59
+ }
60
+ function RouterProvider({
61
+ router: router$1,
62
+ ...rest
63
+ }) {
64
+ const options = {
65
+ ...router$1.options,
66
+ ...rest,
67
+ meta: {
68
+ ...router$1.options.meta,
69
+ ...rest?.meta
70
+ }
71
+ };
72
+ const history$1 = React__namespace.useState(() => options.history ?? history.createBrowserHistory())[0];
73
+ const tempLocationKeyRef = React__namespace.useRef(`${Math.round(Math.random() * 10000000)}`);
74
+ const resetNextScrollRef = React__namespace.useRef(false);
75
+ const navigateTimeoutRef = React__namespace.useRef(null);
76
+ const parseLocation = utils.useStableCallback(previousLocation => {
77
+ const parse = ({
78
+ pathname,
79
+ search,
80
+ hash,
81
+ state
82
+ }) => {
83
+ const parsedSearch = options.parseSearch(search);
84
+ return {
85
+ pathname: pathname,
86
+ searchStr: search,
87
+ search: utils.replaceEqualDeep(previousLocation?.search, parsedSearch),
88
+ hash: hash.split('#').reverse()[0] ?? '',
89
+ href: `${pathname}${search}${hash}`,
90
+ state: utils.replaceEqualDeep(previousLocation?.state, state)
91
+ };
92
+ };
93
+ const location = parse(history$1.location);
94
+ let {
95
+ __tempLocation,
96
+ __tempKey
97
+ } = location.state;
98
+ if (__tempLocation && (!__tempKey || __tempKey === tempLocationKeyRef.current)) {
99
+ // Sync up the location keys
100
+ const parsedTempLocation = parse(__tempLocation);
101
+ parsedTempLocation.state.key = location.state.key;
102
+ delete parsedTempLocation.state.__tempLocation;
103
+ return {
104
+ ...parsedTempLocation,
105
+ maskedLocation: location
106
+ };
107
+ }
108
+ return location;
109
+ });
110
+ const [state, setState] = React__namespace.useState(() => getInitialRouterState(parseLocation()));
111
+ const basepath = `/${path.trimPath(options.basepath ?? '') ?? ''}`;
112
+ const resolvePathWithBase = utils.useStableCallback((from, path$1) => {
113
+ return path.resolvePath(basepath, from, path.cleanPath(path$1));
114
+ });
115
+ const [routesById, routesByPath] = React__namespace.useMemo(() => {
116
+ const routesById = {};
117
+ const routesByPath = {};
118
+ const recurseRoutes = routes => {
119
+ routes.forEach((route, i) => {
120
+ route.init({
121
+ originalIndex: i
122
+ });
123
+ const existingRoute = routesById[route.id];
124
+ invariant__default["default"](!existingRoute, `Duplicate routes found with id: ${String(route.id)}`);
125
+ routesById[route.id] = route;
126
+ if (!route.isRoot && route.path) {
127
+ const trimmedFullPath = path.trimPathRight(route.fullPath);
128
+ if (!routesByPath[trimmedFullPath] || route.fullPath.endsWith('/')) {
129
+ routesByPath[trimmedFullPath] = route;
130
+ }
131
+ }
132
+ const children = route.children;
133
+ if (children?.length) {
134
+ recurseRoutes(children);
135
+ }
136
+ });
137
+ };
138
+ recurseRoutes([router$1.routeTree]);
139
+ return [routesById, routesByPath];
140
+ }, []);
141
+ const looseRoutesById = routesById;
142
+ const flatRoutes = React__namespace.useMemo(() => Object.values(routesByPath).map((d, i) => {
143
+ const trimmed = path.trimPath(d.fullPath);
144
+ const parsed = path.parsePathname(trimmed);
145
+ while (parsed.length > 1 && parsed[0]?.value === '/') {
146
+ parsed.shift();
147
+ }
148
+ const score = parsed.map(d => {
149
+ if (d.type === 'param') {
150
+ return 0.5;
151
+ }
152
+ if (d.type === 'wildcard') {
153
+ return 0.25;
154
+ }
155
+ return 1;
156
+ });
157
+ return {
158
+ child: d,
159
+ trimmed,
160
+ parsed,
161
+ index: i,
162
+ score
163
+ };
164
+ }).sort((a, b) => {
165
+ let isIndex = a.trimmed === '/' ? 1 : b.trimmed === '/' ? -1 : 0;
166
+ if (isIndex !== 0) return isIndex;
167
+ const length = Math.min(a.score.length, b.score.length);
168
+
169
+ // Sort by length of score
170
+ if (a.score.length !== b.score.length) {
171
+ return b.score.length - a.score.length;
172
+ }
173
+
174
+ // Sort by min available score
175
+ for (let i = 0; i < length; i++) {
176
+ if (a.score[i] !== b.score[i]) {
177
+ return b.score[i] - a.score[i];
178
+ }
179
+ }
180
+
181
+ // Sort by min available parsed value
182
+ for (let i = 0; i < length; i++) {
183
+ if (a.parsed[i].value !== b.parsed[i].value) {
184
+ return a.parsed[i].value > b.parsed[i].value ? 1 : -1;
185
+ }
186
+ }
187
+
188
+ // Sort by length of trimmed full path
189
+ if (a.trimmed !== b.trimmed) {
190
+ return a.trimmed > b.trimmed ? 1 : -1;
191
+ }
192
+
193
+ // Sort by original index
194
+ return a.index - b.index;
195
+ }).map((d, i) => {
196
+ d.child.rank = i;
197
+ return d.child;
198
+ }), [routesByPath]);
199
+ const latestLoadPromiseRef = React__namespace.useRef(Promise.resolve());
200
+ const matchRoutes = utils.useStableCallback((pathname, locationSearch, opts) => {
201
+ let routeParams = {};
202
+ let foundRoute = flatRoutes.find(route => {
203
+ const matchedParams = path.matchPathname(basepath, path.trimPathRight(pathname), {
204
+ to: route.fullPath,
205
+ caseSensitive: route.options.caseSensitive ?? options.caseSensitive,
206
+ fuzzy: false
207
+ });
208
+ if (matchedParams) {
209
+ routeParams = matchedParams;
210
+ return true;
211
+ }
212
+ return false;
213
+ });
214
+ let routeCursor = foundRoute || routesById['__root__'];
215
+ let matchedRoutes = [routeCursor];
216
+ // let includingLayouts = true
217
+ while (routeCursor?.parentRoute) {
218
+ routeCursor = routeCursor.parentRoute;
219
+ if (routeCursor) matchedRoutes.unshift(routeCursor);
220
+ }
221
+
222
+ // Existing matches are matches that are already loaded along with
223
+ // pending matches that are still loading
224
+
225
+ const parseErrors = matchedRoutes.map(route => {
226
+ let parsedParamsError;
227
+ if (route.options.parseParams) {
228
+ try {
229
+ const parsedParams = route.options.parseParams(routeParams);
230
+ // Add the parsed params to the accumulated params bag
231
+ Object.assign(routeParams, parsedParams);
232
+ } catch (err) {
233
+ parsedParamsError = new PathParamError(err.message, {
234
+ cause: err
235
+ });
236
+ if (opts?.throwOnError) {
237
+ throw parsedParamsError;
238
+ }
239
+ return parsedParamsError;
240
+ }
241
+ }
242
+ return;
243
+ });
244
+ const matches = matchedRoutes.map((route, index) => {
245
+ const interpolatedPath = path.interpolatePath(route.path, routeParams);
246
+ const matchId = path.interpolatePath(route.id, routeParams, true);
247
+
248
+ // Waste not, want not. If we already have a match for this route,
249
+ // reuse it. This is important for layout routes, which might stick
250
+ // around between navigation actions that only change leaf routes.
251
+ const existingMatch = getRouteMatch(state, matchId);
252
+ if (existingMatch) {
253
+ return {
254
+ ...existingMatch
255
+ };
256
+ }
257
+
258
+ // Create a fresh route match
259
+ const hasLoaders = !!(route.options.load || router.componentTypes.some(d => route.options[d]?.preload));
260
+ const routeMatch = {
261
+ id: matchId,
262
+ routeId: route.id,
263
+ params: routeParams,
264
+ pathname: path.joinPaths([basepath, interpolatedPath]),
265
+ updatedAt: Date.now(),
266
+ routeSearch: {},
267
+ search: {},
268
+ status: hasLoaders ? 'pending' : 'success',
269
+ isFetching: false,
270
+ invalid: false,
271
+ error: undefined,
272
+ paramsError: parseErrors[index],
273
+ searchError: undefined,
274
+ loadPromise: Promise.resolve(),
275
+ meta: undefined,
276
+ abortController: new AbortController(),
277
+ fetchedAt: 0
278
+ };
279
+ return routeMatch;
280
+ });
281
+
282
+ // Take each match and resolve its search params and meta
283
+ // This has to happen after the matches are created or found
284
+ // so that we can use the parent match's search params and meta
285
+ matches.forEach((match, i) => {
286
+ const parentMatch = matches[i - 1];
287
+ const route = looseRoutesById[match.routeId];
288
+ const searchInfo = (() => {
289
+ // Validate the search params and stabilize them
290
+ const parentSearchInfo = {
291
+ search: parentMatch?.search ?? locationSearch,
292
+ routeSearch: parentMatch?.routeSearch ?? locationSearch
293
+ };
294
+ try {
295
+ const validator = typeof route.options.validateSearch === 'object' ? route.options.validateSearch.parse : route.options.validateSearch;
296
+ let routeSearch = validator?.(parentSearchInfo.search) ?? {};
297
+ let search = {
298
+ ...parentSearchInfo.search,
299
+ ...routeSearch
300
+ };
301
+ routeSearch = utils.replaceEqualDeep(match.routeSearch, routeSearch);
302
+ search = utils.replaceEqualDeep(match.search, search);
303
+ return {
304
+ routeSearch,
305
+ search,
306
+ searchDidChange: match.routeSearch !== routeSearch
307
+ };
308
+ } catch (err) {
309
+ match.searchError = new SearchParamError(err.message, {
310
+ cause: err
311
+ });
312
+ if (opts?.throwOnError) {
313
+ throw match.searchError;
314
+ }
315
+ return parentSearchInfo;
316
+ }
317
+ })();
318
+ Object.assign(match, searchInfo);
319
+ });
320
+ return matches;
321
+ });
322
+ const cancelMatch = utils.useStableCallback(id => {
323
+ getRouteMatch(state, id)?.abortController?.abort();
324
+ });
325
+ const cancelMatches = utils.useStableCallback(state => {
326
+ state.matches.forEach(match => {
327
+ cancelMatch(match.id);
328
+ });
329
+ });
330
+ const buildLocation = utils.useStableCallback((opts = {}) => {
331
+ const build = (dest = {}, matches) => {
332
+ const from = latestLocationRef.current;
333
+ const fromPathname = dest.from ?? from.pathname;
334
+ let pathname = resolvePathWithBase(fromPathname, `${dest.to ?? ''}`);
335
+ const fromMatches = matchRoutes(fromPathname, from.search);
336
+ const stayingMatches = matches?.filter(d => fromMatches?.find(e => e.routeId === d.routeId));
337
+ const prevParams = {
338
+ ...utils.last(fromMatches)?.params
339
+ };
340
+ let nextParams = (dest.params ?? true) === true ? prevParams : utils.functionalUpdate(dest.params, prevParams);
341
+ if (nextParams) {
342
+ matches?.map(d => looseRoutesById[d.routeId].options.stringifyParams).filter(Boolean).forEach(fn => {
343
+ nextParams = {
344
+ ...nextParams,
345
+ ...fn(nextParams)
346
+ };
347
+ });
348
+ }
349
+ pathname = path.interpolatePath(pathname, nextParams ?? {});
350
+ const preSearchFilters = stayingMatches?.map(match => looseRoutesById[match.routeId].options.preSearchFilters ?? []).flat().filter(Boolean) ?? [];
351
+ const postSearchFilters = stayingMatches?.map(match => looseRoutesById[match.routeId].options.postSearchFilters ?? []).flat().filter(Boolean) ?? [];
352
+
353
+ // Pre filters first
354
+ const preFilteredSearch = preSearchFilters?.length ? preSearchFilters?.reduce((prev, next) => next(prev), from.search) : from.search;
355
+
356
+ // Then the link/navigate function
357
+ const destSearch = dest.search === true ? preFilteredSearch // Preserve resolvedFrom true
358
+ : dest.search ? utils.functionalUpdate(dest.search, preFilteredSearch) ?? {} // Updater
359
+ : preSearchFilters?.length ? preFilteredSearch // Preserve resolvedFrom filters
360
+ : {};
361
+
362
+ // Then post filters
363
+ const postFilteredSearch = postSearchFilters?.length ? postSearchFilters.reduce((prev, next) => next(prev), destSearch) : destSearch;
364
+ const search = utils.replaceEqualDeep(from.search, postFilteredSearch);
365
+ const searchStr = options.stringifySearch(search);
366
+ const hash = dest.hash === true ? from.hash : dest.hash ? utils.functionalUpdate(dest.hash, from.hash) : from.hash;
367
+ const hashStr = hash ? `#${hash}` : '';
368
+ let nextState = dest.state === true ? from.state : dest.state ? utils.functionalUpdate(dest.state, from.state) : from.state;
369
+ nextState = utils.replaceEqualDeep(from.state, nextState);
370
+ return {
371
+ pathname,
372
+ search,
373
+ searchStr,
374
+ state: nextState,
375
+ hash,
376
+ href: history$1.createHref(`${pathname}${searchStr}${hashStr}`),
377
+ unmaskOnReload: dest.unmaskOnReload
378
+ };
379
+ };
380
+ const buildWithMatches = (dest = {}, maskedDest) => {
381
+ let next = build(dest);
382
+ let maskedNext = maskedDest ? build(maskedDest) : undefined;
383
+ if (!maskedNext) {
384
+ let params = {};
385
+ let foundMask = options.routeMasks?.find(d => {
386
+ const match = path.matchPathname(basepath, next.pathname, {
387
+ to: d.from,
388
+ caseSensitive: false,
389
+ fuzzy: false
390
+ });
391
+ if (match) {
392
+ params = match;
393
+ return true;
394
+ }
395
+ return false;
396
+ });
397
+ if (foundMask) {
398
+ foundMask = {
399
+ ...foundMask,
400
+ from: path.interpolatePath(foundMask.from, params)
401
+ };
402
+ maskedDest = foundMask;
403
+ maskedNext = build(maskedDest);
404
+ }
405
+ }
406
+ const nextMatches = matchRoutes(next.pathname, next.search);
407
+ const maskedMatches = maskedNext ? matchRoutes(maskedNext.pathname, maskedNext.search) : undefined;
408
+ const maskedFinal = maskedNext ? build(maskedDest, maskedMatches) : undefined;
409
+ const final = build(dest, nextMatches);
410
+ if (maskedFinal) {
411
+ final.maskedLocation = maskedFinal;
412
+ }
413
+ return final;
414
+ };
415
+ if (opts.mask) {
416
+ return buildWithMatches(opts, {
417
+ ...utils.pick(opts, ['from']),
418
+ ...opts.mask
419
+ });
420
+ }
421
+ return buildWithMatches(opts);
422
+ });
423
+ const commitLocation = utils.useStableCallback(async next => {
424
+ if (navigateTimeoutRef.current) clearTimeout(navigateTimeoutRef.current);
425
+ const isSameUrl = latestLocationRef.current.href === next.href;
426
+
427
+ // If the next urls are the same and we're not replacing,
428
+ // do nothing
429
+ if (!isSameUrl || !next.replace) {
430
+ let {
431
+ maskedLocation,
432
+ ...nextHistory
433
+ } = next;
434
+ if (maskedLocation) {
435
+ nextHistory = {
436
+ ...maskedLocation,
437
+ state: {
438
+ ...maskedLocation.state,
439
+ __tempKey: undefined,
440
+ __tempLocation: {
441
+ ...nextHistory,
442
+ search: nextHistory.searchStr,
443
+ state: {
444
+ ...nextHistory.state,
445
+ __tempKey: undefined,
446
+ __tempLocation: undefined,
447
+ key: undefined
448
+ }
449
+ }
450
+ }
451
+ };
452
+ if (nextHistory.unmaskOnReload ?? options.unmaskOnReload ?? false) {
453
+ nextHistory.state.__tempKey = tempLocationKeyRef.current;
454
+ }
455
+ }
456
+ history$1[next.replace ? 'replace' : 'push'](nextHistory.href, nextHistory.state);
457
+ }
458
+ resetNextScrollRef.current = next.resetScroll ?? true;
459
+ return latestLoadPromiseRef.current;
460
+ });
461
+ const buildAndCommitLocation = utils.useStableCallback(({
462
+ replace,
463
+ resetScroll,
464
+ ...rest
465
+ } = {}) => {
466
+ const location = buildLocation(rest);
467
+ return commitLocation({
468
+ ...location,
469
+ replace,
470
+ resetScroll
471
+ });
472
+ });
473
+ const navigate = utils.useStableCallback(({
474
+ from,
475
+ to = '',
476
+ ...rest
477
+ }) => {
478
+ // If this link simply reloads the current route,
479
+ // make sure it has a new key so it will trigger a data refresh
480
+
481
+ // If this `to` is a valid external URL, return
482
+ // null for LinkUtils
483
+ const toString = String(to);
484
+ const fromString = typeof from === 'undefined' ? from : String(from);
485
+ let isExternal;
486
+ try {
487
+ new URL(`${toString}`);
488
+ isExternal = true;
489
+ } catch (e) {}
490
+ invariant__default["default"](!isExternal, 'Attempting to navigate to external url with this.navigate!');
491
+ return buildAndCommitLocation({
492
+ ...rest,
493
+ from: fromString,
494
+ to: toString
495
+ });
496
+ });
497
+ const loadMatches = utils.useStableCallback(async ({
498
+ matches,
499
+ preload
500
+ }) => {
501
+ let firstBadMatchIndex;
502
+
503
+ // Check each match middleware to see if the route can be accessed
504
+ try {
505
+ for (let [index, match] of matches.entries()) {
506
+ const parentMatch = matches[index - 1];
507
+ const route = looseRoutesById[match.routeId];
508
+ const handleError = (err, code) => {
509
+ err.routerCode = code;
510
+ firstBadMatchIndex = firstBadMatchIndex ?? index;
511
+ if (redirects.isRedirect(err)) {
512
+ throw err;
513
+ }
514
+ try {
515
+ route.options.onError?.(err);
516
+ } catch (errorHandlerErr) {
517
+ err = errorHandlerErr;
518
+ if (redirects.isRedirect(errorHandlerErr)) {
519
+ throw errorHandlerErr;
520
+ }
521
+ }
522
+ matches[index] = match = {
523
+ ...match,
524
+ error: err,
525
+ status: 'error',
526
+ updatedAt: Date.now()
527
+ };
528
+ };
529
+ try {
530
+ if (match.paramsError) {
531
+ handleError(match.paramsError, 'PARSE_PARAMS');
532
+ }
533
+ if (match.searchError) {
534
+ handleError(match.searchError, 'VALIDATE_SEARCH');
535
+ }
536
+ const parentMeta = parentMatch?.meta ?? options.meta ?? {};
537
+ const beforeLoadMeta = (await route.options.beforeLoad?.({
538
+ search: match.search,
539
+ abortController: match.abortController,
540
+ params: match.params,
541
+ preload: !!preload,
542
+ meta: parentMeta,
543
+ location: state.location // TODO: This might need to be latestLocationRef.current...?
544
+ })) ?? {};
545
+ const meta = {
546
+ ...parentMeta,
547
+ ...beforeLoadMeta
548
+ };
549
+ matches[index] = match = {
550
+ ...match,
551
+ meta: utils.replaceEqualDeep(match.meta, meta)
552
+ };
553
+ } catch (err) {
554
+ handleError(err, 'BEFORE_LOAD');
555
+ break;
556
+ }
557
+ }
558
+ } catch (err) {
559
+ if (redirects.isRedirect(err)) {
560
+ if (!preload) navigate(err);
561
+ return;
562
+ }
563
+ throw err;
564
+ }
565
+ const validResolvedMatches = matches.slice(0, firstBadMatchIndex);
566
+ const matchPromises = [];
567
+ validResolvedMatches.forEach((match, index) => {
568
+ matchPromises.push((async () => {
569
+ const parentMatchPromise = matchPromises[index - 1];
570
+ const route = looseRoutesById[match.routeId];
571
+ if (match.isFetching) {
572
+ return getRouteMatch(state, match.id)?.loadPromise;
573
+ }
574
+ const fetchedAt = Date.now();
575
+ const checkLatest = () => {
576
+ const latest = getRouteMatch(state, match.id);
577
+ return latest && latest.fetchedAt !== fetchedAt ? latest.loadPromise : undefined;
578
+ };
579
+ const handleIfRedirect = err => {
580
+ if (redirects.isRedirect(err)) {
581
+ if (!preload) {
582
+ navigate(err);
583
+ }
584
+ return true;
585
+ }
586
+ return false;
587
+ };
588
+ const load = async () => {
589
+ let latestPromise;
590
+ try {
591
+ const componentsPromise = Promise.all(router.componentTypes.map(async type => {
592
+ const component = route.options[type];
593
+ if (component?.preload) {
594
+ await component.preload();
595
+ }
596
+ }));
597
+ const loaderPromise = route.options.load?.({
598
+ params: match.params,
599
+ search: match.search,
600
+ preload: !!preload,
601
+ parentMatchPromise,
602
+ abortController: match.abortController,
603
+ meta: match.meta
604
+ });
605
+ await Promise.all([componentsPromise, loaderPromise]);
606
+ if (latestPromise = checkLatest()) return await latestPromise;
607
+ matches[index] = match = {
608
+ ...match,
609
+ error: undefined,
610
+ status: 'success',
611
+ isFetching: false,
612
+ updatedAt: Date.now()
613
+ };
614
+ } catch (error) {
615
+ if (latestPromise = checkLatest()) return await latestPromise;
616
+ if (handleIfRedirect(error)) return;
617
+ try {
618
+ route.options.onError?.(error);
619
+ } catch (onErrorError) {
620
+ error = onErrorError;
621
+ if (handleIfRedirect(onErrorError)) return;
622
+ }
623
+ matches[index] = match = {
624
+ ...match,
625
+ error,
626
+ status: 'error',
627
+ isFetching: false,
628
+ updatedAt: Date.now()
629
+ };
630
+ }
631
+ };
632
+ let loadPromise;
633
+ matches[index] = match = {
634
+ ...match,
635
+ isFetching: true,
636
+ fetchedAt,
637
+ invalid: false
638
+ };
639
+ loadPromise = load();
640
+ matches[index] = match = {
641
+ ...match,
642
+ loadPromise
643
+ };
644
+ await loadPromise;
645
+ })());
646
+ });
647
+ await Promise.all(matchPromises);
648
+ });
649
+ const load = utils.useStableCallback(async opts => {
650
+ const promise = new Promise(async (resolve, reject) => {
651
+ const prevLocation = state.resolvedLocation;
652
+ const pathDidChange = !!(opts?.next && prevLocation.href !== opts.next.href);
653
+ let latestPromise;
654
+ const checkLatest = () => {
655
+ return latestLoadPromiseRef.current !== promise ? latestLoadPromiseRef.current : undefined;
656
+ };
657
+
658
+ // Cancel any pending matches
659
+ cancelMatches(state);
660
+ router$1.emit({
661
+ type: 'onBeforeLoad',
662
+ from: prevLocation,
663
+ to: opts?.next ?? state.location,
664
+ pathChanged: pathDidChange
665
+ });
666
+ if (opts?.next) {
667
+ // Ingest the new location
668
+ setState(s => ({
669
+ ...s,
670
+ location: opts.next
671
+ }));
672
+ }
673
+
674
+ // Match the routes
675
+ const matches = matchRoutes(state.location.pathname, state.location.search, {
676
+ throwOnError: opts?.throwOnError,
677
+ debug: true
678
+ });
679
+ setState(s => ({
680
+ ...s,
681
+ status: 'pending',
682
+ matches
683
+ }));
684
+ try {
685
+ // Load the matches
686
+ try {
687
+ await loadMatches({
688
+ matches
689
+ });
690
+ } catch (err) {
691
+ // swallow this error, since we'll display the
692
+ // errors on the route components
693
+ }
694
+
695
+ // Only apply the latest transition
696
+ if (latestPromise = checkLatest()) {
697
+ return latestPromise;
698
+ }
699
+
700
+ // TODO:
701
+ // const exitingMatchIds = previousMatches.filter(
702
+ // (id) => !state.pendingMatches.includes(id),
703
+ // )
704
+ // const enteringMatchIds = state.pendingMatches.filter(
705
+ // (id) => !previousMatches.includes(id),
706
+ // )
707
+ // const stayingMatchIds = previousMatches.filter((id) =>
708
+ // state.pendingMatches.includes(id),
709
+ // )
710
+
711
+ setState(s => ({
712
+ ...s,
713
+ status: 'idle',
714
+ resolvedLocation: s.location
715
+ }));
716
+
717
+ // TODO:
718
+ // ;(
719
+ // [
720
+ // [exitingMatchIds, 'onLeave'],
721
+ // [enteringMatchIds, 'onEnter'],
722
+ // [stayingMatchIds, 'onTransition'],
723
+ // ] as const
724
+ // ).forEach(([matches, hook]) => {
725
+ // matches.forEach((match) => {
726
+ // const route = this.getRoute(match.routeId)
727
+ // route.options[hook]?.(match)
728
+ // })
729
+ // })
730
+ router$1.emit({
731
+ type: 'onLoad',
732
+ from: prevLocation,
733
+ to: state.location,
734
+ pathChanged: pathDidChange
735
+ });
736
+ resolve();
737
+ } catch (err) {
738
+ // Only apply the latest transition
739
+ if (latestPromise = checkLatest()) {
740
+ return latestPromise;
741
+ }
742
+ reject(err);
743
+ }
744
+ });
745
+ latestLoadPromiseRef.current = promise;
746
+ return latestLoadPromiseRef.current;
747
+ });
748
+ const safeLoad = React__namespace.useCallback(async () => {
749
+ try {
750
+ return load();
751
+ } catch (err) {
752
+ // Don't do anything
753
+ }
754
+ }, []);
755
+ const preloadRoute = utils.useStableCallback(async (navigateOpts = state.location) => {
756
+ let next = buildLocation(navigateOpts);
757
+ let matches = matchRoutes(next.pathname, next.search, {
758
+ throwOnError: true
759
+ });
760
+ await loadMatches({
761
+ matches,
762
+ preload: true
763
+ });
764
+ return [utils.last(matches), matches];
765
+ });
766
+ const buildLink = utils.useStableCallback((state, dest) => {
767
+ // If this link simply reloads the current route,
768
+ // make sure it has a new key so it will trigger a data refresh
769
+
770
+ // If this `to` is a valid external URL, return
771
+ // null for LinkUtils
772
+
773
+ const {
774
+ to,
775
+ preload: userPreload,
776
+ preloadDelay: userPreloadDelay,
777
+ activeOptions,
778
+ disabled,
779
+ target,
780
+ replace,
781
+ resetScroll
782
+ } = dest;
783
+ try {
784
+ new URL(`${to}`);
785
+ return {
786
+ type: 'external',
787
+ href: to
788
+ };
789
+ } catch (e) {}
790
+ const nextOpts = dest;
791
+ const next = buildLocation(nextOpts);
792
+ const preload = userPreload ?? options.defaultPreload;
793
+ const preloadDelay = userPreloadDelay ?? options.defaultPreloadDelay ?? 0;
794
+
795
+ // Compare path/hash for matches
796
+ const currentPathSplit = latestLocationRef.current.pathname.split('/');
797
+ const nextPathSplit = next.pathname.split('/');
798
+ const pathIsFuzzyEqual = nextPathSplit.every((d, i) => d === currentPathSplit[i]);
799
+ // Combine the matches based on user options
800
+ const pathTest = activeOptions?.exact ? latestLocationRef.current.pathname === next.pathname : pathIsFuzzyEqual;
801
+ const hashTest = activeOptions?.includeHash ? latestLocationRef.current.hash === next.hash : true;
802
+ const searchTest = activeOptions?.includeSearch ?? true ? utils.partialDeepEqual(latestLocationRef.current.search, next.search) : true;
803
+
804
+ // The final "active" test
805
+ const isActive = pathTest && hashTest && searchTest;
806
+
807
+ // The click handler
808
+ const handleClick = e => {
809
+ if (!disabled && !isCtrlEvent(e) && !e.defaultPrevented && (!target || target === '_self') && e.button === 0) {
810
+ e.preventDefault();
811
+
812
+ // All is well? Navigate!
813
+ commitLocation({
814
+ ...next,
815
+ replace,
816
+ resetScroll
817
+ });
818
+ }
819
+ };
820
+
821
+ // The click handler
822
+ const handleFocus = e => {
823
+ if (preload) {
824
+ preloadRoute(nextOpts).catch(err => {
825
+ console.warn(err);
826
+ console.warn(preloadWarning);
827
+ });
828
+ }
829
+ };
830
+ const handleTouchStart = e => {
831
+ preloadRoute(nextOpts).catch(err => {
832
+ console.warn(err);
833
+ console.warn(preloadWarning);
834
+ });
835
+ };
836
+ const handleEnter = e => {
837
+ const target = e.target || {};
838
+ if (preload) {
839
+ if (target.preloadTimeout) {
840
+ return;
841
+ }
842
+ target.preloadTimeout = setTimeout(() => {
843
+ target.preloadTimeout = null;
844
+ preloadRoute(nextOpts).catch(err => {
845
+ console.warn(err);
846
+ console.warn(preloadWarning);
847
+ });
848
+ }, preloadDelay);
849
+ }
850
+ };
851
+ const handleLeave = e => {
852
+ const target = e.target || {};
853
+ if (target.preloadTimeout) {
854
+ clearTimeout(target.preloadTimeout);
855
+ target.preloadTimeout = null;
856
+ }
857
+ };
858
+ return {
859
+ type: 'internal',
860
+ next,
861
+ handleFocus,
862
+ handleClick,
863
+ handleEnter,
864
+ handleLeave,
865
+ handleTouchStart,
866
+ isActive,
867
+ disabled
868
+ };
869
+ });
870
+ const latestLocationRef = React__namespace.useRef(state.location);
871
+ React__namespace.useLayoutEffect(() => {
872
+ const unsub = history$1.subscribe(() => {
873
+ latestLocationRef.current = parseLocation(latestLocationRef.current);
874
+ React__namespace.startTransition(() => {
875
+ setState(s => ({
876
+ ...s,
877
+ location: latestLocationRef.current
878
+ }));
879
+ });
880
+ });
881
+ const nextLocation = buildLocation({
882
+ search: true,
883
+ params: true,
884
+ hash: true,
885
+ state: true
886
+ });
887
+ if (state.location.href !== nextLocation.href) {
888
+ commitLocation({
889
+ ...nextLocation,
890
+ replace: true
891
+ });
892
+ }
893
+ return () => {
894
+ unsub();
895
+ };
896
+ }, [history$1]);
897
+ const initialLoad = React__namespace.useRef(true);
898
+ if (initialLoad.current) {
899
+ initialLoad.current = false;
900
+ safeLoad();
901
+ }
902
+ React__namespace.useLayoutEffect(() => {
903
+ if (state.resolvedLocation !== state.location) {
904
+ safeLoad();
905
+ }
906
+ }, [state.location]);
907
+ React__namespace.useMemo(() => [...state.matches, ...state.pendingMatches].some(d => d.isFetching), [state.matches, state.pendingMatches]);
908
+ const matchRoute = utils.useStableCallback((state, location, opts) => {
909
+ location = {
910
+ ...location,
911
+ to: location.to ? resolvePathWithBase(location.from || '', location.to) : undefined
912
+ };
913
+ const next = buildLocation(location);
914
+ if (opts?.pending && state.status !== 'pending') {
915
+ return false;
916
+ }
917
+ const baseLocation = opts?.pending ? latestLocationRef.current : state.resolvedLocation;
918
+ if (!baseLocation) {
919
+ return false;
920
+ }
921
+ const match = path.matchPathname(basepath, baseLocation.pathname, {
922
+ ...opts,
923
+ to: next.pathname
924
+ });
925
+ if (!match) {
926
+ return false;
927
+ }
928
+ if (opts?.includeSearch ?? true) {
929
+ return utils.partialDeepEqual(baseLocation.search, next.search) ? match : false;
930
+ }
931
+ return match;
932
+ });
933
+ const routerContextValue = {
934
+ routeTree: router$1.routeTree,
935
+ navigate,
936
+ buildLink,
937
+ state,
938
+ matchRoute,
939
+ routesById,
940
+ options,
941
+ history: history$1,
942
+ load
943
+ };
944
+ return /*#__PURE__*/React__namespace.createElement(routerContext.Provider, {
945
+ value: routerContextValue
946
+ }, /*#__PURE__*/React__namespace.createElement(react.Matches, null));
947
+ }
948
+ function isCtrlEvent(e) {
949
+ return !!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey);
950
+ }
951
+ class SearchParamError extends Error {}
952
+ class PathParamError extends Error {}
953
+ function getRouteMatch(state, id) {
954
+ return [...state.pendingMatches, ...state.matches].find(d => d.id === id);
955
+ }
956
+
957
+ exports.PathParamError = PathParamError;
958
+ exports.RouterProvider = RouterProvider;
959
+ exports.SearchParamError = SearchParamError;
960
+ exports.getInitialRouterState = getInitialRouterState;
961
+ exports.getRouteMatch = getRouteMatch;
962
+ exports.routerContext = routerContext;
963
+ //# sourceMappingURL=RouterProvider.js.map