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

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 +961 -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 +1799 -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 +1116 -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 +4 -4
  51. package/src/RouteMatch.ts +28 -0
  52. package/src/RouterProvider.tsx +1384 -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
@@ -8,13 +8,396 @@
8
8
  *
9
9
  * @license MIT
10
10
  */
11
- import { useStore } from '@tanstack/react-store';
12
- export { useStore } from '@tanstack/react-store';
13
- import { Route, functionalUpdate, rootRouteId, last, pick, watchScrollPositions, restoreScrollPositions, isDehydratedDeferred } from '@tanstack/router-core';
14
- export * from '@tanstack/router-core';
15
- import * as React from 'react';
11
+ import { createBrowserHistory } from '@tanstack/history';
12
+ export * from '@tanstack/history';
16
13
  import invariant from 'tiny-invariant';
14
+ export { default as invariant } from 'tiny-invariant';
17
15
  import warning from 'tiny-warning';
16
+ export { default as warning } from 'tiny-warning';
17
+ import * as React from 'react';
18
+
19
+ // export type Expand<T> = T
20
+
21
+ // type Compute<T> = { [K in keyof T]: T[K] } | never
22
+
23
+ // type AllKeys<T> = T extends any ? keyof T : never
24
+
25
+ // export type MergeUnion<T, Keys extends keyof T = keyof T> = Compute<
26
+ // {
27
+ // [K in Keys]: T[Keys]
28
+ // } & {
29
+ // [K in AllKeys<T>]?: T extends any
30
+ // ? K extends keyof T
31
+ // ? T[K]
32
+ // : never
33
+ // : never
34
+ // }
35
+ // >
36
+ // // Sample types to merge
37
+ // type TypeA = {
38
+ // shared: string
39
+ // onlyInA: string
40
+ // nested: {
41
+ // shared: string
42
+ // aProp: string
43
+ // }
44
+ // array: string[]
45
+ // }
46
+ // type TypeB = {
47
+ // shared: number
48
+ // onlyInB: number
49
+ // nested: {
50
+ // shared: number
51
+ // bProp: number
52
+ // }
53
+ // array: number[]
54
+ // }
55
+ // type TypeC = {
56
+ // shared: boolean
57
+ // onlyInC: boolean
58
+ // nested: {
59
+ // shared: boolean
60
+ // cProp: boolean
61
+ // }
62
+ // array: boolean[]
63
+ // }
64
+ // type Test = Expand<Assign<TypeA, TypeB>>
65
+ // // Using DeepMerge to merge TypeA and TypeB
66
+ // type MergedType = Expand<AssignAll<[TypeA, TypeB, TypeC]>>
67
+ //
68
+
69
+ const isServer = typeof document === 'undefined';
70
+ function last(arr) {
71
+ return arr[arr.length - 1];
72
+ }
73
+ function isFunction(d) {
74
+ return typeof d === 'function';
75
+ }
76
+ function functionalUpdate(updater, previous) {
77
+ if (isFunction(updater)) {
78
+ return updater(previous);
79
+ }
80
+ return updater;
81
+ }
82
+ function pick(parent, keys) {
83
+ return keys.reduce((obj, key) => {
84
+ obj[key] = parent[key];
85
+ return obj;
86
+ }, {});
87
+ }
88
+
89
+ /**
90
+ * This function returns `a` if `b` is deeply equal.
91
+ * If not, it will replace any deeply equal children of `b` with those of `a`.
92
+ * This can be used for structural sharing between immutable JSON values for example.
93
+ * Do not use this with signals
94
+ */
95
+ function replaceEqualDeep(prev, _next) {
96
+ if (prev === _next) {
97
+ return prev;
98
+ }
99
+ const next = _next;
100
+ const array = Array.isArray(prev) && Array.isArray(next);
101
+ if (array || isPlainObject(prev) && isPlainObject(next)) {
102
+ const prevSize = array ? prev.length : Object.keys(prev).length;
103
+ const nextItems = array ? next : Object.keys(next);
104
+ const nextSize = nextItems.length;
105
+ const copy = array ? [] : {};
106
+ let equalItems = 0;
107
+ for (let i = 0; i < nextSize; i++) {
108
+ const key = array ? i : nextItems[i];
109
+ copy[key] = replaceEqualDeep(prev[key], next[key]);
110
+ if (copy[key] === prev[key]) {
111
+ equalItems++;
112
+ }
113
+ }
114
+ return prevSize === nextSize && equalItems === prevSize ? prev : copy;
115
+ }
116
+ return next;
117
+ }
118
+
119
+ // Copied from: https://github.com/jonschlinkert/is-plain-object
120
+ function isPlainObject(o) {
121
+ if (!hasObjectPrototype(o)) {
122
+ return false;
123
+ }
124
+
125
+ // If has modified constructor
126
+ const ctor = o.constructor;
127
+ if (typeof ctor === 'undefined') {
128
+ return true;
129
+ }
130
+
131
+ // If has modified prototype
132
+ const prot = ctor.prototype;
133
+ if (!hasObjectPrototype(prot)) {
134
+ return false;
135
+ }
136
+
137
+ // If constructor does not have an Object-specific method
138
+ if (!prot.hasOwnProperty('isPrototypeOf')) {
139
+ return false;
140
+ }
141
+
142
+ // Most likely a plain Object
143
+ return true;
144
+ }
145
+ function hasObjectPrototype(o) {
146
+ return Object.prototype.toString.call(o) === '[object Object]';
147
+ }
148
+ function partialDeepEqual(a, b) {
149
+ if (a === b) {
150
+ return true;
151
+ }
152
+ if (typeof a !== typeof b) {
153
+ return false;
154
+ }
155
+ if (isPlainObject(a) && isPlainObject(b)) {
156
+ return !Object.keys(b).some(key => !partialDeepEqual(a[key], b[key]));
157
+ }
158
+ if (Array.isArray(a) && Array.isArray(b)) {
159
+ return a.length === b.length && a.every((item, index) => partialDeepEqual(item, b[index]));
160
+ }
161
+ return false;
162
+ }
163
+ function useStableCallback(fn) {
164
+ const fnRef = React.useRef(fn);
165
+ fnRef.current = fn;
166
+ const ref = React.useRef((...args) => fnRef.current(...args));
167
+ return ref.current;
168
+ }
169
+
170
+ function joinPaths(paths) {
171
+ return cleanPath(paths.filter(Boolean).join('/'));
172
+ }
173
+ function cleanPath(path) {
174
+ // remove double slashes
175
+ return path.replace(/\/{2,}/g, '/');
176
+ }
177
+ function trimPathLeft(path) {
178
+ return path === '/' ? path : path.replace(/^\/{1,}/, '');
179
+ }
180
+ function trimPathRight(path) {
181
+ return path === '/' ? path : path.replace(/\/{1,}$/, '');
182
+ }
183
+ function trimPath(path) {
184
+ return trimPathRight(trimPathLeft(path));
185
+ }
186
+ function resolvePath(basepath, base, to) {
187
+ base = base.replace(new RegExp(`^${basepath}`), '/');
188
+ to = to.replace(new RegExp(`^${basepath}`), '/');
189
+ let baseSegments = parsePathname(base);
190
+ const toSegments = parsePathname(to);
191
+ toSegments.forEach((toSegment, index) => {
192
+ if (toSegment.value === '/') {
193
+ if (!index) {
194
+ // Leading slash
195
+ baseSegments = [toSegment];
196
+ } else if (index === toSegments.length - 1) {
197
+ // Trailing Slash
198
+ baseSegments.push(toSegment);
199
+ } else ;
200
+ } else if (toSegment.value === '..') {
201
+ // Extra trailing slash? pop it off
202
+ if (baseSegments.length > 1 && last(baseSegments)?.value === '/') {
203
+ baseSegments.pop();
204
+ }
205
+ baseSegments.pop();
206
+ } else if (toSegment.value === '.') {
207
+ return;
208
+ } else {
209
+ baseSegments.push(toSegment);
210
+ }
211
+ });
212
+ const joined = joinPaths([basepath, ...baseSegments.map(d => d.value)]);
213
+ return cleanPath(joined);
214
+ }
215
+ function parsePathname(pathname) {
216
+ if (!pathname) {
217
+ return [];
218
+ }
219
+ pathname = cleanPath(pathname);
220
+ const segments = [];
221
+ if (pathname.slice(0, 1) === '/') {
222
+ pathname = pathname.substring(1);
223
+ segments.push({
224
+ type: 'pathname',
225
+ value: '/'
226
+ });
227
+ }
228
+ if (!pathname) {
229
+ return segments;
230
+ }
231
+
232
+ // Remove empty segments and '.' segments
233
+ const split = pathname.split('/').filter(Boolean);
234
+ segments.push(...split.map(part => {
235
+ if (part === '$' || part === '*') {
236
+ return {
237
+ type: 'wildcard',
238
+ value: part
239
+ };
240
+ }
241
+ if (part.charAt(0) === '$') {
242
+ return {
243
+ type: 'param',
244
+ value: part
245
+ };
246
+ }
247
+ return {
248
+ type: 'pathname',
249
+ value: part
250
+ };
251
+ }));
252
+ if (pathname.slice(-1) === '/') {
253
+ pathname = pathname.substring(1);
254
+ segments.push({
255
+ type: 'pathname',
256
+ value: '/'
257
+ });
258
+ }
259
+ return segments;
260
+ }
261
+ function interpolatePath(path, params, leaveWildcards = false) {
262
+ const interpolatedPathSegments = parsePathname(path);
263
+ return joinPaths(interpolatedPathSegments.map(segment => {
264
+ if (segment.type === 'wildcard') {
265
+ const value = params[segment.value];
266
+ if (leaveWildcards) return `${segment.value}${value ?? ''}`;
267
+ return value;
268
+ }
269
+ if (segment.type === 'param') {
270
+ return params[segment.value.substring(1)] ?? '';
271
+ }
272
+ return segment.value;
273
+ }));
274
+ }
275
+ function matchPathname(basepath, currentPathname, matchLocation) {
276
+ const pathParams = matchByPath(basepath, currentPathname, matchLocation);
277
+ // const searchMatched = matchBySearch(location.search, matchLocation)
278
+
279
+ if (matchLocation.to && !pathParams) {
280
+ return;
281
+ }
282
+ return pathParams ?? {};
283
+ }
284
+ function matchByPath(basepath, from, matchLocation) {
285
+ // Remove the base path from the pathname
286
+ from = basepath != '/' ? from.substring(basepath.length) : from;
287
+ // Default to to $ (wildcard)
288
+ const to = `${matchLocation.to ?? '$'}`;
289
+ // Parse the from and to
290
+ const baseSegments = parsePathname(from);
291
+ const routeSegments = parsePathname(to);
292
+ if (!from.startsWith('/')) {
293
+ baseSegments.unshift({
294
+ type: 'pathname',
295
+ value: '/'
296
+ });
297
+ }
298
+ if (!to.startsWith('/')) {
299
+ routeSegments.unshift({
300
+ type: 'pathname',
301
+ value: '/'
302
+ });
303
+ }
304
+ const params = {};
305
+ let isMatch = (() => {
306
+ for (let i = 0; i < Math.max(baseSegments.length, routeSegments.length); i++) {
307
+ const baseSegment = baseSegments[i];
308
+ const routeSegment = routeSegments[i];
309
+ const isLastBaseSegment = i >= baseSegments.length - 1;
310
+ const isLastRouteSegment = i >= routeSegments.length - 1;
311
+ if (routeSegment) {
312
+ if (routeSegment.type === 'wildcard') {
313
+ if (baseSegment?.value) {
314
+ params['*'] = joinPaths(baseSegments.slice(i).map(d => d.value));
315
+ return true;
316
+ }
317
+ return false;
318
+ }
319
+ if (routeSegment.type === 'pathname') {
320
+ if (routeSegment.value === '/' && !baseSegment?.value) {
321
+ return true;
322
+ }
323
+ if (baseSegment) {
324
+ if (matchLocation.caseSensitive) {
325
+ if (routeSegment.value !== baseSegment.value) {
326
+ return false;
327
+ }
328
+ } else if (routeSegment.value.toLowerCase() !== baseSegment.value.toLowerCase()) {
329
+ return false;
330
+ }
331
+ }
332
+ }
333
+ if (!baseSegment) {
334
+ return false;
335
+ }
336
+ if (routeSegment.type === 'param') {
337
+ if (baseSegment?.value === '/') {
338
+ return false;
339
+ }
340
+ if (baseSegment.value.charAt(0) !== '$') {
341
+ params[routeSegment.value.substring(1)] = baseSegment.value;
342
+ }
343
+ }
344
+ }
345
+ if (!isLastBaseSegment && isLastRouteSegment) {
346
+ return !!matchLocation.fuzzy;
347
+ }
348
+ }
349
+ return true;
350
+ })();
351
+ return isMatch ? params : undefined;
352
+ }
353
+
354
+ // @ts-nocheck
355
+
356
+ // qss has been slightly modified and inlined here for our use cases (and compression's sake). We've included it as a hard dependency for MIT license attribution.
357
+
358
+ function encode(obj, pfx) {
359
+ var k,
360
+ i,
361
+ tmp,
362
+ str = '';
363
+ for (k in obj) {
364
+ if ((tmp = obj[k]) !== void 0) {
365
+ if (Array.isArray(tmp)) {
366
+ for (i = 0; i < tmp.length; i++) {
367
+ str && (str += '&');
368
+ str += encodeURIComponent(k) + '=' + encodeURIComponent(tmp[i]);
369
+ }
370
+ } else {
371
+ str && (str += '&');
372
+ str += encodeURIComponent(k) + '=' + encodeURIComponent(tmp);
373
+ }
374
+ }
375
+ }
376
+ return (pfx || '') + str;
377
+ }
378
+ function toValue(mix) {
379
+ if (!mix) return '';
380
+ var str = decodeURIComponent(mix);
381
+ if (str === 'false') return false;
382
+ if (str === 'true') return true;
383
+ return +str * 0 === 0 && +str + '' === str ? +str : str;
384
+ }
385
+ function decode(str) {
386
+ var tmp,
387
+ k,
388
+ out = {},
389
+ arr = str.split('&');
390
+ while (tmp = arr.shift()) {
391
+ tmp = tmp.split('=');
392
+ k = tmp.shift();
393
+ if (out[k] !== void 0) {
394
+ out[k] = [].concat(out[k], toValue(tmp.shift()));
395
+ } else {
396
+ out[k] = toValue(tmp.shift());
397
+ }
398
+ }
399
+ return out;
400
+ }
18
401
 
19
402
  function _extends() {
20
403
  _extends = Object.assign ? Object.assign.bind() : function (target) {
@@ -31,42 +414,1170 @@ function _extends() {
31
414
  return _extends.apply(this, arguments);
32
415
  }
33
416
 
34
- const useLayoutEffect$1 = typeof window !== 'undefined' ? React.useLayoutEffect : React.useEffect;
35
- Route.__onInit = route => {
36
- Object.assign(route, {
37
- useMatch: (opts = {}) => {
38
- return useMatch({
39
- ...opts,
40
- from: route.id
417
+ const defaultParseSearch = parseSearchWith(JSON.parse);
418
+ const defaultStringifySearch = stringifySearchWith(JSON.stringify, JSON.parse);
419
+ function parseSearchWith(parser) {
420
+ return searchStr => {
421
+ if (searchStr.substring(0, 1) === '?') {
422
+ searchStr = searchStr.substring(1);
423
+ }
424
+ let query = decode(searchStr);
425
+
426
+ // Try to parse any query params that might be json
427
+ for (let key in query) {
428
+ const value = query[key];
429
+ if (typeof value === 'string') {
430
+ try {
431
+ query[key] = parser(value);
432
+ } catch (err) {
433
+ //
434
+ }
435
+ }
436
+ }
437
+ return query;
438
+ };
439
+ }
440
+ function stringifySearchWith(stringify, parser) {
441
+ function stringifyValue(val) {
442
+ if (typeof val === 'object' && val !== null) {
443
+ try {
444
+ return stringify(val);
445
+ } catch (err) {
446
+ // silent
447
+ }
448
+ } else if (typeof val === 'string' && typeof parser === 'function') {
449
+ try {
450
+ // Check if it's a valid parseable string.
451
+ // If it is, then stringify it again.
452
+ parser(val);
453
+ return stringify(val);
454
+ } catch (err) {
455
+ // silent
456
+ }
457
+ }
458
+ return val;
459
+ }
460
+ return search => {
461
+ search = {
462
+ ...search
463
+ };
464
+ if (search) {
465
+ Object.keys(search).forEach(key => {
466
+ const val = search[key];
467
+ if (typeof val === 'undefined' || val === undefined) {
468
+ delete search[key];
469
+ } else {
470
+ search[key] = stringifyValue(val);
471
+ }
41
472
  });
42
- },
43
- useLoader: (opts = {}) => {
44
- return useLoader({
45
- ...opts,
46
- from: route.id
473
+ }
474
+ const searchStr = encode(search).toString();
475
+ return searchStr ? `?${searchStr}` : '';
476
+ };
477
+ }
478
+
479
+ //
480
+
481
+ //
482
+
483
+ const componentTypes = ['component', 'errorComponent', 'pendingComponent'];
484
+ class Router {
485
+ // dehydratedData?: TDehydrated
486
+ // resetNextScroll = false
487
+ // tempLocationKey = `${Math.round(Math.random() * 10000000)}`
488
+ constructor(options) {
489
+ this.options = {
490
+ defaultPreloadDelay: 50,
491
+ meta: undefined,
492
+ ...options,
493
+ stringifySearch: options?.stringifySearch ?? defaultStringifySearch,
494
+ parseSearch: options?.parseSearch ?? defaultParseSearch
495
+ };
496
+ this.routeTree = this.options.routeTree;
497
+ }
498
+ subscribers = new Set();
499
+ subscribe = (eventType, fn) => {
500
+ const listener = {
501
+ eventType,
502
+ fn
503
+ };
504
+ this.subscribers.add(listener);
505
+ return () => {
506
+ this.subscribers.delete(listener);
507
+ };
508
+ };
509
+ emit = routerEvent => {
510
+ this.subscribers.forEach(listener => {
511
+ if (listener.eventType === routerEvent.type) {
512
+ listener.fn(routerEvent);
513
+ }
514
+ });
515
+ };
516
+
517
+ // dehydrate = (): DehydratedRouter => {
518
+ // return {
519
+ // state: {
520
+ // dehydratedMatches: state.matches.map((d) =>
521
+ // pick(d, ['fetchedAt', 'invalid', 'id', 'status', 'updatedAt']),
522
+ // ),
523
+ // },
524
+ // }
525
+ // }
526
+
527
+ // hydrate = async (__do_not_use_server_ctx?: HydrationCtx) => {
528
+ // let _ctx = __do_not_use_server_ctx
529
+ // // Client hydrates from window
530
+ // if (typeof document !== 'undefined') {
531
+ // _ctx = window.__TSR_DEHYDRATED__
532
+ // }
533
+
534
+ // invariant(
535
+ // _ctx,
536
+ // 'Expected to find a __TSR_DEHYDRATED__ property on window... but we did not. Did you forget to render <DehydrateRouter /> in your app?',
537
+ // )
538
+
539
+ // const ctx = _ctx
540
+ // this.dehydratedData = ctx.payload as any
541
+ // this.options.hydrate?.(ctx.payload as any)
542
+ // const dehydratedState = ctx.router.state
543
+
544
+ // let matches = this.matchRoutes(
545
+ // state.location.pathname,
546
+ // state.location.search,
547
+ // ).map((match) => {
548
+ // const dehydratedMatch = dehydratedState.dehydratedMatches.find(
549
+ // (d) => d.id === match.id,
550
+ // )
551
+
552
+ // invariant(
553
+ // dehydratedMatch,
554
+ // `Could not find a client-side match for dehydrated match with id: ${match.id}!`,
555
+ // )
556
+
557
+ // if (dehydratedMatch) {
558
+ // return {
559
+ // ...match,
560
+ // ...dehydratedMatch,
561
+ // }
562
+ // }
563
+ // return match
564
+ // })
565
+
566
+ // this.setState((s) => {
567
+ // return {
568
+ // ...s,
569
+ // matches: dehydratedState.dehydratedMatches as any,
570
+ // }
571
+ // })
572
+ // }
573
+
574
+ // TODO:
575
+ // injectedHtml: (string | (() => Promise<string> | string))[] = []
576
+
577
+ // TODO:
578
+ // injectHtml = async (html: string | (() => Promise<string> | string)) => {
579
+ // this.injectedHtml.push(html)
580
+ // }
581
+
582
+ // TODO:
583
+ // dehydrateData = <T>(key: any, getData: T | (() => Promise<T> | T)) => {
584
+ // if (typeof document === 'undefined') {
585
+ // const strKey = typeof key === 'string' ? key : JSON.stringify(key)
586
+
587
+ // this.injectHtml(async () => {
588
+ // const id = `__TSR_DEHYDRATED__${strKey}`
589
+ // const data =
590
+ // typeof getData === 'function' ? await (getData as any)() : getData
591
+ // return `<script id='${id}' suppressHydrationWarning>window["__TSR_DEHYDRATED__${escapeJSON(
592
+ // strKey,
593
+ // )}"] = ${JSON.stringify(data)}
594
+ // ;(() => {
595
+ // var el = document.getElementById('${id}')
596
+ // el.parentElement.removeChild(el)
597
+ // })()
598
+ // </script>`
599
+ // })
600
+
601
+ // return () => this.hydrateData<T>(key)
602
+ // }
603
+
604
+ // return () => undefined
605
+ // }
606
+
607
+ // hydrateData = <T = unknown>(key: any) => {
608
+ // if (typeof document !== 'undefined') {
609
+ // const strKey = typeof key === 'string' ? key : JSON.stringify(key)
610
+
611
+ // return window[`__TSR_DEHYDRATED__${strKey}` as any] as T
612
+ // }
613
+
614
+ // return undefined
615
+ // }
616
+
617
+ // resolveMatchPromise = (matchId: string, key: string, value: any) => {
618
+ // state.matches
619
+ // .find((d) => d.id === matchId)
620
+ // ?.__promisesByKey[key]?.resolve(value)
621
+ // }
622
+
623
+ // setRouteMatch = (
624
+ // id: string,
625
+ // pending: boolean,
626
+ // updater: NonNullableUpdater<RouteMatch<TRouteTree>>,
627
+ // ) => {
628
+ // const key = pending ? 'pendingMatches' : 'matches'
629
+
630
+ // this.setState((prev) => {
631
+ // return {
632
+ // ...prev,
633
+ // [key]: prev[key].map((d) => {
634
+ // if (d.id === id) {
635
+ // return functionalUpdate(updater, d)
636
+ // }
637
+
638
+ // return d
639
+ // }),
640
+ // }
641
+ // })
642
+ // }
643
+
644
+ // setPendingRouteMatch = (
645
+ // id: string,
646
+ // updater: NonNullableUpdater<RouteMatch<TRouteTree>>,
647
+ // ) => {
648
+ // this.setRouteMatch(id, true, updater)
649
+ // }
650
+ }
651
+
652
+ // A function that takes an import() argument which is a function and returns a new function that will
653
+ // proxy arguments from the caller to the imported function, retaining all type
654
+ // information along the way
655
+ function lazyFn(fn, key) {
656
+ return async (...args) => {
657
+ const imported = await fn();
658
+ return imported[key || 'default'](...args);
659
+ };
660
+ }
661
+
662
+ // Detect if we're in the DOM
663
+
664
+ function redirect(opts) {
665
+ opts.isRedirect = true;
666
+ return opts;
667
+ }
668
+ function isRedirect(obj) {
669
+ return !!obj?.isRedirect;
670
+ }
671
+
672
+ const preloadWarning = 'Error preloading route! ☝️';
673
+ const routerContext = /*#__PURE__*/React.createContext(null);
674
+ function getInitialRouterState(location) {
675
+ return {
676
+ status: 'idle',
677
+ isFetching: false,
678
+ resolvedLocation: location,
679
+ location: location,
680
+ matches: [],
681
+ pendingMatches: [],
682
+ lastUpdated: Date.now()
683
+ };
684
+ }
685
+ function RouterProvider({
686
+ router,
687
+ ...rest
688
+ }) {
689
+ const options = {
690
+ ...router.options,
691
+ ...rest,
692
+ meta: {
693
+ ...router.options.meta,
694
+ ...rest?.meta
695
+ }
696
+ };
697
+ const history = React.useState(() => options.history ?? createBrowserHistory())[0];
698
+ const tempLocationKeyRef = React.useRef(`${Math.round(Math.random() * 10000000)}`);
699
+ const resetNextScrollRef = React.useRef(false);
700
+ const navigateTimeoutRef = React.useRef(null);
701
+ const parseLocation = useStableCallback(previousLocation => {
702
+ const parse = ({
703
+ pathname,
704
+ search,
705
+ hash,
706
+ state
707
+ }) => {
708
+ const parsedSearch = options.parseSearch(search);
709
+ return {
710
+ pathname: pathname,
711
+ searchStr: search,
712
+ search: replaceEqualDeep(previousLocation?.search, parsedSearch),
713
+ hash: hash.split('#').reverse()[0] ?? '',
714
+ href: `${pathname}${search}${hash}`,
715
+ state: replaceEqualDeep(previousLocation?.state, state)
716
+ };
717
+ };
718
+ const location = parse(history.location);
719
+ let {
720
+ __tempLocation,
721
+ __tempKey
722
+ } = location.state;
723
+ if (__tempLocation && (!__tempKey || __tempKey === tempLocationKeyRef.current)) {
724
+ // Sync up the location keys
725
+ const parsedTempLocation = parse(__tempLocation);
726
+ parsedTempLocation.state.key = location.state.key;
727
+ delete parsedTempLocation.state.__tempLocation;
728
+ return {
729
+ ...parsedTempLocation,
730
+ maskedLocation: location
731
+ };
732
+ }
733
+ return location;
734
+ });
735
+ const [state, setState] = React.useState(() => getInitialRouterState(parseLocation()));
736
+ const basepath = `/${trimPath(options.basepath ?? '') ?? ''}`;
737
+ const resolvePathWithBase = useStableCallback((from, path) => {
738
+ return resolvePath(basepath, from, cleanPath(path));
739
+ });
740
+ const [routesById, routesByPath] = React.useMemo(() => {
741
+ const routesById = {};
742
+ const routesByPath = {};
743
+ const recurseRoutes = routes => {
744
+ routes.forEach((route, i) => {
745
+ route.init({
746
+ originalIndex: i
747
+ });
748
+ const existingRoute = routesById[route.id];
749
+ invariant(!existingRoute, `Duplicate routes found with id: ${String(route.id)}`);
750
+ routesById[route.id] = route;
751
+ if (!route.isRoot && route.path) {
752
+ const trimmedFullPath = trimPathRight(route.fullPath);
753
+ if (!routesByPath[trimmedFullPath] || route.fullPath.endsWith('/')) {
754
+ routesByPath[trimmedFullPath] = route;
755
+ }
756
+ }
757
+ const children = route.children;
758
+ if (children?.length) {
759
+ recurseRoutes(children);
760
+ }
47
761
  });
48
- },
49
- useRouteContext: (opts = {}) => {
50
- return useMatch({
51
- ...opts,
52
- from: route.id,
53
- select: d => opts?.select ? opts.select(d.context) : d.context
762
+ };
763
+ recurseRoutes([router.routeTree]);
764
+ return [routesById, routesByPath];
765
+ }, []);
766
+ const looseRoutesById = routesById;
767
+ const flatRoutes = React.useMemo(() => Object.values(routesByPath).map((d, i) => {
768
+ const trimmed = trimPath(d.fullPath);
769
+ const parsed = parsePathname(trimmed);
770
+ while (parsed.length > 1 && parsed[0]?.value === '/') {
771
+ parsed.shift();
772
+ }
773
+ const score = parsed.map(d => {
774
+ if (d.type === 'param') {
775
+ return 0.5;
776
+ }
777
+ if (d.type === 'wildcard') {
778
+ return 0.25;
779
+ }
780
+ return 1;
781
+ });
782
+ return {
783
+ child: d,
784
+ trimmed,
785
+ parsed,
786
+ index: i,
787
+ score
788
+ };
789
+ }).sort((a, b) => {
790
+ let isIndex = a.trimmed === '/' ? 1 : b.trimmed === '/' ? -1 : 0;
791
+ if (isIndex !== 0) return isIndex;
792
+ const length = Math.min(a.score.length, b.score.length);
793
+
794
+ // Sort by length of score
795
+ if (a.score.length !== b.score.length) {
796
+ return b.score.length - a.score.length;
797
+ }
798
+
799
+ // Sort by min available score
800
+ for (let i = 0; i < length; i++) {
801
+ if (a.score[i] !== b.score[i]) {
802
+ return b.score[i] - a.score[i];
803
+ }
804
+ }
805
+
806
+ // Sort by min available parsed value
807
+ for (let i = 0; i < length; i++) {
808
+ if (a.parsed[i].value !== b.parsed[i].value) {
809
+ return a.parsed[i].value > b.parsed[i].value ? 1 : -1;
810
+ }
811
+ }
812
+
813
+ // Sort by length of trimmed full path
814
+ if (a.trimmed !== b.trimmed) {
815
+ return a.trimmed > b.trimmed ? 1 : -1;
816
+ }
817
+
818
+ // Sort by original index
819
+ return a.index - b.index;
820
+ }).map((d, i) => {
821
+ d.child.rank = i;
822
+ return d.child;
823
+ }), [routesByPath]);
824
+ const latestLoadPromiseRef = React.useRef(Promise.resolve());
825
+ const matchRoutes = useStableCallback((pathname, locationSearch, opts) => {
826
+ let routeParams = {};
827
+ let foundRoute = flatRoutes.find(route => {
828
+ const matchedParams = matchPathname(basepath, trimPathRight(pathname), {
829
+ to: route.fullPath,
830
+ caseSensitive: route.options.caseSensitive ?? options.caseSensitive,
831
+ fuzzy: false
54
832
  });
55
- },
56
- useSearch: (opts = {}) => {
57
- return useSearch({
58
- ...opts,
59
- from: route.id
833
+ if (matchedParams) {
834
+ routeParams = matchedParams;
835
+ return true;
836
+ }
837
+ return false;
838
+ });
839
+ let routeCursor = foundRoute || routesById['__root__'];
840
+ let matchedRoutes = [routeCursor];
841
+ // let includingLayouts = true
842
+ while (routeCursor?.parentRoute) {
843
+ routeCursor = routeCursor.parentRoute;
844
+ if (routeCursor) matchedRoutes.unshift(routeCursor);
845
+ }
846
+
847
+ // Existing matches are matches that are already loaded along with
848
+ // pending matches that are still loading
849
+
850
+ const parseErrors = matchedRoutes.map(route => {
851
+ let parsedParamsError;
852
+ if (route.options.parseParams) {
853
+ try {
854
+ const parsedParams = route.options.parseParams(routeParams);
855
+ // Add the parsed params to the accumulated params bag
856
+ Object.assign(routeParams, parsedParams);
857
+ } catch (err) {
858
+ parsedParamsError = new PathParamError(err.message, {
859
+ cause: err
860
+ });
861
+ if (opts?.throwOnError) {
862
+ throw parsedParamsError;
863
+ }
864
+ return parsedParamsError;
865
+ }
866
+ }
867
+ return;
868
+ });
869
+ const matches = matchedRoutes.map((route, index) => {
870
+ const interpolatedPath = interpolatePath(route.path, routeParams);
871
+ const matchId = interpolatePath(route.id, routeParams, true);
872
+
873
+ // Waste not, want not. If we already have a match for this route,
874
+ // reuse it. This is important for layout routes, which might stick
875
+ // around between navigation actions that only change leaf routes.
876
+ const existingMatch = getRouteMatch(state, matchId);
877
+ if (existingMatch) {
878
+ return {
879
+ ...existingMatch
880
+ };
881
+ }
882
+
883
+ // Create a fresh route match
884
+ const hasLoaders = !!(route.options.load || componentTypes.some(d => route.options[d]?.preload));
885
+ const routeMatch = {
886
+ id: matchId,
887
+ routeId: route.id,
888
+ params: routeParams,
889
+ pathname: joinPaths([basepath, interpolatedPath]),
890
+ updatedAt: Date.now(),
891
+ routeSearch: {},
892
+ search: {},
893
+ status: hasLoaders ? 'pending' : 'success',
894
+ isFetching: false,
895
+ invalid: false,
896
+ error: undefined,
897
+ paramsError: parseErrors[index],
898
+ searchError: undefined,
899
+ loadPromise: Promise.resolve(),
900
+ meta: undefined,
901
+ abortController: new AbortController(),
902
+ fetchedAt: 0
903
+ };
904
+ return routeMatch;
905
+ });
906
+
907
+ // Take each match and resolve its search params and meta
908
+ // This has to happen after the matches are created or found
909
+ // so that we can use the parent match's search params and meta
910
+ matches.forEach((match, i) => {
911
+ const parentMatch = matches[i - 1];
912
+ const route = looseRoutesById[match.routeId];
913
+ const searchInfo = (() => {
914
+ // Validate the search params and stabilize them
915
+ const parentSearchInfo = {
916
+ search: parentMatch?.search ?? locationSearch,
917
+ routeSearch: parentMatch?.routeSearch ?? locationSearch
918
+ };
919
+ try {
920
+ const validator = typeof route.options.validateSearch === 'object' ? route.options.validateSearch.parse : route.options.validateSearch;
921
+ let routeSearch = validator?.(parentSearchInfo.search) ?? {};
922
+ let search = {
923
+ ...parentSearchInfo.search,
924
+ ...routeSearch
925
+ };
926
+ routeSearch = replaceEqualDeep(match.routeSearch, routeSearch);
927
+ search = replaceEqualDeep(match.search, search);
928
+ return {
929
+ routeSearch,
930
+ search,
931
+ searchDidChange: match.routeSearch !== routeSearch
932
+ };
933
+ } catch (err) {
934
+ match.searchError = new SearchParamError(err.message, {
935
+ cause: err
936
+ });
937
+ if (opts?.throwOnError) {
938
+ throw match.searchError;
939
+ }
940
+ return parentSearchInfo;
941
+ }
942
+ })();
943
+ Object.assign(match, searchInfo);
944
+ });
945
+ return matches;
946
+ });
947
+ const cancelMatch = useStableCallback(id => {
948
+ getRouteMatch(state, id)?.abortController?.abort();
949
+ });
950
+ const cancelMatches = useStableCallback(state => {
951
+ state.matches.forEach(match => {
952
+ cancelMatch(match.id);
953
+ });
954
+ });
955
+ const buildLocation = useStableCallback((opts = {}) => {
956
+ const build = (dest = {}, matches) => {
957
+ const from = state.location;
958
+ const fromPathname = dest.from ?? from.pathname;
959
+ let pathname = resolvePathWithBase(fromPathname, `${dest.to ?? ''}`);
960
+ const fromMatches = matchRoutes(fromPathname, from.search);
961
+ const stayingMatches = matches?.filter(d => fromMatches?.find(e => e.routeId === d.routeId));
962
+ const prevParams = {
963
+ ...last(fromMatches)?.params
964
+ };
965
+ let nextParams = (dest.params ?? true) === true ? prevParams : functionalUpdate(dest.params, prevParams);
966
+ if (nextParams) {
967
+ matches?.map(d => looseRoutesById[d.routeId].options.stringifyParams).filter(Boolean).forEach(fn => {
968
+ nextParams = {
969
+ ...nextParams,
970
+ ...fn(nextParams)
971
+ };
972
+ });
973
+ }
974
+ pathname = interpolatePath(pathname, nextParams ?? {});
975
+ const preSearchFilters = stayingMatches?.map(match => looseRoutesById[match.routeId].options.preSearchFilters ?? []).flat().filter(Boolean) ?? [];
976
+ const postSearchFilters = stayingMatches?.map(match => looseRoutesById[match.routeId].options.postSearchFilters ?? []).flat().filter(Boolean) ?? [];
977
+
978
+ // Pre filters first
979
+ const preFilteredSearch = preSearchFilters?.length ? preSearchFilters?.reduce((prev, next) => next(prev), from.search) : from.search;
980
+
981
+ // Then the link/navigate function
982
+ const destSearch = dest.search === true ? preFilteredSearch // Preserve resolvedFrom true
983
+ : dest.search ? functionalUpdate(dest.search, preFilteredSearch) ?? {} // Updater
984
+ : preSearchFilters?.length ? preFilteredSearch // Preserve resolvedFrom filters
985
+ : {};
986
+
987
+ // Then post filters
988
+ const postFilteredSearch = postSearchFilters?.length ? postSearchFilters.reduce((prev, next) => next(prev), destSearch) : destSearch;
989
+ const search = replaceEqualDeep(from.search, postFilteredSearch);
990
+ const searchStr = options.stringifySearch(search);
991
+ const hash = dest.hash === true ? from.hash : dest.hash ? functionalUpdate(dest.hash, from.hash) : from.hash;
992
+ const hashStr = hash ? `#${hash}` : '';
993
+ let nextState = dest.state === true ? from.state : dest.state ? functionalUpdate(dest.state, from.state) : from.state;
994
+ nextState = replaceEqualDeep(from.state, nextState);
995
+ return {
996
+ pathname,
997
+ search,
998
+ searchStr,
999
+ state: nextState,
1000
+ hash,
1001
+ href: history.createHref(`${pathname}${searchStr}${hashStr}`),
1002
+ unmaskOnReload: dest.unmaskOnReload
1003
+ };
1004
+ };
1005
+ const buildWithMatches = (dest = {}, maskedDest) => {
1006
+ let next = build(dest);
1007
+ let maskedNext = maskedDest ? build(maskedDest) : undefined;
1008
+ if (!maskedNext) {
1009
+ let params = {};
1010
+ let foundMask = options.routeMasks?.find(d => {
1011
+ const match = matchPathname(basepath, next.pathname, {
1012
+ to: d.from,
1013
+ caseSensitive: false,
1014
+ fuzzy: false
1015
+ });
1016
+ if (match) {
1017
+ params = match;
1018
+ return true;
1019
+ }
1020
+ return false;
1021
+ });
1022
+ if (foundMask) {
1023
+ foundMask = {
1024
+ ...foundMask,
1025
+ from: interpolatePath(foundMask.from, params)
1026
+ };
1027
+ maskedDest = foundMask;
1028
+ maskedNext = build(maskedDest);
1029
+ }
1030
+ }
1031
+ const nextMatches = matchRoutes(next.pathname, next.search);
1032
+ const maskedMatches = maskedNext ? matchRoutes(maskedNext.pathname, maskedNext.search) : undefined;
1033
+ const maskedFinal = maskedNext ? build(maskedDest, maskedMatches) : undefined;
1034
+ const final = build(dest, nextMatches);
1035
+ if (maskedFinal) {
1036
+ final.maskedLocation = maskedFinal;
1037
+ }
1038
+ return final;
1039
+ };
1040
+ if (opts.mask) {
1041
+ return buildWithMatches(opts, {
1042
+ ...pick(opts, ['from']),
1043
+ ...opts.mask
60
1044
  });
61
- },
62
- useParams: (opts = {}) => {
63
- return useParams({
64
- ...opts,
65
- from: route.id
1045
+ }
1046
+ return buildWithMatches(opts);
1047
+ });
1048
+ const commitLocation = useStableCallback(async next => {
1049
+ if (navigateTimeoutRef.current) clearTimeout(navigateTimeoutRef.current);
1050
+ const isSameUrl = state.location.href === next.href;
1051
+
1052
+ // If the next urls are the same and we're not replacing,
1053
+ // do nothing
1054
+ if (!isSameUrl || !next.replace) {
1055
+ let {
1056
+ maskedLocation,
1057
+ ...nextHistory
1058
+ } = next;
1059
+ if (maskedLocation) {
1060
+ nextHistory = {
1061
+ ...maskedLocation,
1062
+ state: {
1063
+ ...maskedLocation.state,
1064
+ __tempKey: undefined,
1065
+ __tempLocation: {
1066
+ ...nextHistory,
1067
+ search: nextHistory.searchStr,
1068
+ state: {
1069
+ ...nextHistory.state,
1070
+ __tempKey: undefined,
1071
+ __tempLocation: undefined,
1072
+ key: undefined
1073
+ }
1074
+ }
1075
+ }
1076
+ };
1077
+ if (nextHistory.unmaskOnReload ?? options.unmaskOnReload ?? false) {
1078
+ nextHistory.state.__tempKey = tempLocationKeyRef.current;
1079
+ }
1080
+ }
1081
+ history[next.replace ? 'replace' : 'push'](nextHistory.href, nextHistory.state);
1082
+ }
1083
+ resetNextScrollRef.current = next.resetScroll ?? true;
1084
+ return latestLoadPromiseRef.current;
1085
+ });
1086
+ const buildAndCommitLocation = useStableCallback(({
1087
+ replace,
1088
+ resetScroll,
1089
+ ...rest
1090
+ } = {}) => {
1091
+ const location = buildLocation(rest);
1092
+ return commitLocation({
1093
+ ...location,
1094
+ replace,
1095
+ resetScroll
1096
+ });
1097
+ });
1098
+ const navigate = useStableCallback(({
1099
+ from,
1100
+ to = '',
1101
+ ...rest
1102
+ }) => {
1103
+ // If this link simply reloads the current route,
1104
+ // make sure it has a new key so it will trigger a data refresh
1105
+
1106
+ // If this `to` is a valid external URL, return
1107
+ // null for LinkUtils
1108
+ const toString = String(to);
1109
+ const fromString = typeof from === 'undefined' ? from : String(from);
1110
+ let isExternal;
1111
+ try {
1112
+ new URL(`${toString}`);
1113
+ isExternal = true;
1114
+ } catch (e) {}
1115
+ invariant(!isExternal, 'Attempting to navigate to external url with this.navigate!');
1116
+ return buildAndCommitLocation({
1117
+ ...rest,
1118
+ from: fromString,
1119
+ to: toString
1120
+ });
1121
+ });
1122
+ const loadMatches = useStableCallback(async ({
1123
+ matches,
1124
+ preload
1125
+ }) => {
1126
+ let firstBadMatchIndex;
1127
+
1128
+ // Check each match middleware to see if the route can be accessed
1129
+ try {
1130
+ for (let [index, match] of matches.entries()) {
1131
+ const parentMatch = matches[index - 1];
1132
+ const route = looseRoutesById[match.routeId];
1133
+ const handleError = (err, code) => {
1134
+ err.routerCode = code;
1135
+ firstBadMatchIndex = firstBadMatchIndex ?? index;
1136
+ if (isRedirect(err)) {
1137
+ throw err;
1138
+ }
1139
+ try {
1140
+ route.options.onError?.(err);
1141
+ } catch (errorHandlerErr) {
1142
+ err = errorHandlerErr;
1143
+ if (isRedirect(errorHandlerErr)) {
1144
+ throw errorHandlerErr;
1145
+ }
1146
+ }
1147
+ matches[index] = match = {
1148
+ ...match,
1149
+ error: err,
1150
+ status: 'error',
1151
+ updatedAt: Date.now()
1152
+ };
1153
+ };
1154
+ try {
1155
+ if (match.paramsError) {
1156
+ handleError(match.paramsError, 'PARSE_PARAMS');
1157
+ }
1158
+ if (match.searchError) {
1159
+ handleError(match.searchError, 'VALIDATE_SEARCH');
1160
+ }
1161
+ const parentMeta = parentMatch?.meta ?? options.meta ?? {};
1162
+ const beforeLoadMeta = (await route.options.beforeLoad?.({
1163
+ search: match.search,
1164
+ abortController: match.abortController,
1165
+ params: match.params,
1166
+ preload: !!preload,
1167
+ meta: parentMeta,
1168
+ location: state.location
1169
+ })) ?? {};
1170
+ const meta = {
1171
+ ...parentMeta,
1172
+ ...beforeLoadMeta
1173
+ };
1174
+ matches[index] = match = {
1175
+ ...match,
1176
+ meta: replaceEqualDeep(match.meta, meta)
1177
+ };
1178
+ } catch (err) {
1179
+ handleError(err, 'BEFORE_LOAD');
1180
+ break;
1181
+ }
1182
+ }
1183
+ } catch (err) {
1184
+ if (isRedirect(err)) {
1185
+ if (!preload) navigate(err);
1186
+ return;
1187
+ }
1188
+ throw err;
1189
+ }
1190
+ const validResolvedMatches = matches.slice(0, firstBadMatchIndex);
1191
+ const matchPromises = [];
1192
+ validResolvedMatches.forEach((match, index) => {
1193
+ matchPromises.push((async () => {
1194
+ const parentMatchPromise = matchPromises[index - 1];
1195
+ const route = looseRoutesById[match.routeId];
1196
+ if (match.isFetching) {
1197
+ return getRouteMatch(state, match.id)?.loadPromise;
1198
+ }
1199
+ const fetchedAt = Date.now();
1200
+ const checkLatest = () => {
1201
+ const latest = getRouteMatch(state, match.id);
1202
+ return latest && latest.fetchedAt !== fetchedAt ? latest.loadPromise : undefined;
1203
+ };
1204
+ const handleIfRedirect = err => {
1205
+ if (isRedirect(err)) {
1206
+ if (!preload) {
1207
+ navigate(err);
1208
+ }
1209
+ return true;
1210
+ }
1211
+ return false;
1212
+ };
1213
+ const load = async () => {
1214
+ let latestPromise;
1215
+ try {
1216
+ const componentsPromise = Promise.all(componentTypes.map(async type => {
1217
+ const component = route.options[type];
1218
+ if (component?.preload) {
1219
+ await component.preload();
1220
+ }
1221
+ }));
1222
+ const loaderPromise = route.options.load?.({
1223
+ params: match.params,
1224
+ search: match.search,
1225
+ preload: !!preload,
1226
+ parentMatchPromise,
1227
+ abortController: match.abortController,
1228
+ meta: match.meta
1229
+ });
1230
+ await Promise.all([componentsPromise, loaderPromise]);
1231
+ if (latestPromise = checkLatest()) return await latestPromise;
1232
+ matches[index] = match = {
1233
+ ...match,
1234
+ error: undefined,
1235
+ status: 'success',
1236
+ isFetching: false,
1237
+ updatedAt: Date.now()
1238
+ };
1239
+ } catch (error) {
1240
+ if (latestPromise = checkLatest()) return await latestPromise;
1241
+ if (handleIfRedirect(error)) return;
1242
+ try {
1243
+ route.options.onError?.(error);
1244
+ } catch (onErrorError) {
1245
+ error = onErrorError;
1246
+ if (handleIfRedirect(onErrorError)) return;
1247
+ }
1248
+ matches[index] = match = {
1249
+ ...match,
1250
+ error,
1251
+ status: 'error',
1252
+ isFetching: false,
1253
+ updatedAt: Date.now()
1254
+ };
1255
+ }
1256
+ };
1257
+ let loadPromise;
1258
+ matches[index] = match = {
1259
+ ...match,
1260
+ isFetching: true,
1261
+ fetchedAt,
1262
+ invalid: false
1263
+ };
1264
+ loadPromise = load();
1265
+ matches[index] = match = {
1266
+ ...match,
1267
+ loadPromise
1268
+ };
1269
+ await loadPromise;
1270
+ })());
1271
+ });
1272
+ await Promise.all(matchPromises);
1273
+ });
1274
+ const load = useStableCallback(async opts => {
1275
+ const promise = new Promise(async (resolve, reject) => {
1276
+ const prevLocation = state.resolvedLocation;
1277
+ const pathDidChange = !!(opts?.next && prevLocation.href !== opts.next.href);
1278
+ let latestPromise;
1279
+ const checkLatest = () => {
1280
+ return latestLoadPromiseRef.current !== promise ? latestLoadPromiseRef.current : undefined;
1281
+ };
1282
+
1283
+ // Cancel any pending matches
1284
+ cancelMatches(state);
1285
+ router.emit({
1286
+ type: 'onBeforeLoad',
1287
+ from: prevLocation,
1288
+ to: opts?.next ?? state.location,
1289
+ pathChanged: pathDidChange
1290
+ });
1291
+ if (opts?.next) {
1292
+ // Ingest the new location
1293
+ setState(s => ({
1294
+ ...s,
1295
+ location: opts.next
1296
+ }));
1297
+ }
1298
+
1299
+ // Match the routes
1300
+ const matches = matchRoutes(state.location.pathname, state.location.search, {
1301
+ throwOnError: opts?.throwOnError,
1302
+ debug: true
1303
+ });
1304
+ setState(s => ({
1305
+ ...s,
1306
+ status: 'pending',
1307
+ matches
1308
+ }));
1309
+ try {
1310
+ // Load the matches
1311
+ try {
1312
+ await loadMatches({
1313
+ matches
1314
+ });
1315
+ } catch (err) {
1316
+ // swallow this error, since we'll display the
1317
+ // errors on the route components
1318
+ }
1319
+
1320
+ // Only apply the latest transition
1321
+ if (latestPromise = checkLatest()) {
1322
+ return latestPromise;
1323
+ }
1324
+
1325
+ // TODO:
1326
+ // const exitingMatchIds = previousMatches.filter(
1327
+ // (id) => !state.pendingMatches.includes(id),
1328
+ // )
1329
+ // const enteringMatchIds = state.pendingMatches.filter(
1330
+ // (id) => !previousMatches.includes(id),
1331
+ // )
1332
+ // const stayingMatchIds = previousMatches.filter((id) =>
1333
+ // state.pendingMatches.includes(id),
1334
+ // )
1335
+
1336
+ setState(s => ({
1337
+ ...s,
1338
+ status: 'idle',
1339
+ resolvedLocation: s.location
1340
+ }));
1341
+
1342
+ // TODO:
1343
+ // ;(
1344
+ // [
1345
+ // [exitingMatchIds, 'onLeave'],
1346
+ // [enteringMatchIds, 'onEnter'],
1347
+ // [stayingMatchIds, 'onTransition'],
1348
+ // ] as const
1349
+ // ).forEach(([matches, hook]) => {
1350
+ // matches.forEach((match) => {
1351
+ // const route = this.getRoute(match.routeId)
1352
+ // route.options[hook]?.(match)
1353
+ // })
1354
+ // })
1355
+ router.emit({
1356
+ type: 'onLoad',
1357
+ from: prevLocation,
1358
+ to: state.location,
1359
+ pathChanged: pathDidChange
1360
+ });
1361
+ resolve();
1362
+ } catch (err) {
1363
+ // Only apply the latest transition
1364
+ if (latestPromise = checkLatest()) {
1365
+ return latestPromise;
1366
+ }
1367
+ reject(err);
1368
+ }
1369
+ });
1370
+ latestLoadPromiseRef.current = promise;
1371
+ return latestLoadPromiseRef.current;
1372
+ });
1373
+ const safeLoad = React.useCallback(async () => {
1374
+ try {
1375
+ return load();
1376
+ } catch (err) {
1377
+ // Don't do anything
1378
+ }
1379
+ }, []);
1380
+ const preloadRoute = useStableCallback(async (navigateOpts = state.location) => {
1381
+ let next = buildLocation(navigateOpts);
1382
+ let matches = matchRoutes(next.pathname, next.search, {
1383
+ throwOnError: true
1384
+ });
1385
+ await loadMatches({
1386
+ matches,
1387
+ preload: true
1388
+ });
1389
+ return [last(matches), matches];
1390
+ });
1391
+ const buildLink = useStableCallback((state, dest) => {
1392
+ // If this link simply reloads the current route,
1393
+ // make sure it has a new key so it will trigger a data refresh
1394
+
1395
+ // If this `to` is a valid external URL, return
1396
+ // null for LinkUtils
1397
+
1398
+ const {
1399
+ to,
1400
+ preload: userPreload,
1401
+ preloadDelay: userPreloadDelay,
1402
+ activeOptions,
1403
+ disabled,
1404
+ target,
1405
+ replace,
1406
+ resetScroll
1407
+ } = dest;
1408
+ try {
1409
+ new URL(`${to}`);
1410
+ return {
1411
+ type: 'external',
1412
+ href: to
1413
+ };
1414
+ } catch (e) {}
1415
+ const nextOpts = dest;
1416
+ const next = buildLocation(nextOpts);
1417
+ const preload = userPreload ?? options.defaultPreload;
1418
+ const preloadDelay = userPreloadDelay ?? options.defaultPreloadDelay ?? 0;
1419
+
1420
+ // Compare path/hash for matches
1421
+ const currentPathSplit = state.location.pathname.split('/');
1422
+ const nextPathSplit = next.pathname.split('/');
1423
+ const pathIsFuzzyEqual = nextPathSplit.every((d, i) => d === currentPathSplit[i]);
1424
+ // Combine the matches based on user options
1425
+ const pathTest = activeOptions?.exact ? state.location.pathname === next.pathname : pathIsFuzzyEqual;
1426
+ const hashTest = activeOptions?.includeHash ? state.location.hash === next.hash : true;
1427
+ const searchTest = activeOptions?.includeSearch ?? true ? partialDeepEqual(state.location.search, next.search) : true;
1428
+
1429
+ // The final "active" test
1430
+ const isActive = pathTest && hashTest && searchTest;
1431
+
1432
+ // The click handler
1433
+ const handleClick = e => {
1434
+ if (!disabled && !isCtrlEvent(e) && !e.defaultPrevented && (!target || target === '_self') && e.button === 0) {
1435
+ e.preventDefault();
1436
+
1437
+ // All is well? Navigate!
1438
+ commitLocation({
1439
+ ...next,
1440
+ replace,
1441
+ resetScroll
1442
+ });
1443
+ }
1444
+ };
1445
+
1446
+ // The click handler
1447
+ const handleFocus = e => {
1448
+ if (preload) {
1449
+ preloadRoute(nextOpts).catch(err => {
1450
+ console.warn(err);
1451
+ console.warn(preloadWarning);
1452
+ });
1453
+ }
1454
+ };
1455
+ const handleTouchStart = e => {
1456
+ preloadRoute(nextOpts).catch(err => {
1457
+ console.warn(err);
1458
+ console.warn(preloadWarning);
66
1459
  });
1460
+ };
1461
+ const handleEnter = e => {
1462
+ const target = e.target || {};
1463
+ if (preload) {
1464
+ if (target.preloadTimeout) {
1465
+ return;
1466
+ }
1467
+ target.preloadTimeout = setTimeout(() => {
1468
+ target.preloadTimeout = null;
1469
+ preloadRoute(nextOpts).catch(err => {
1470
+ console.warn(err);
1471
+ console.warn(preloadWarning);
1472
+ });
1473
+ }, preloadDelay);
1474
+ }
1475
+ };
1476
+ const handleLeave = e => {
1477
+ const target = e.target || {};
1478
+ if (target.preloadTimeout) {
1479
+ clearTimeout(target.preloadTimeout);
1480
+ target.preloadTimeout = null;
1481
+ }
1482
+ };
1483
+ return {
1484
+ type: 'internal',
1485
+ next,
1486
+ handleFocus,
1487
+ handleClick,
1488
+ handleEnter,
1489
+ handleLeave,
1490
+ handleTouchStart,
1491
+ isActive,
1492
+ disabled
1493
+ };
1494
+ });
1495
+ React.useLayoutEffect(() => {
1496
+ const unsub = history.subscribe(() => {
1497
+ React.startTransition(() => {
1498
+ setState(s => ({
1499
+ ...s,
1500
+ location: parseLocation(state.location)
1501
+ }));
1502
+ });
1503
+ });
1504
+ const nextLocation = buildLocation({
1505
+ search: true,
1506
+ params: true,
1507
+ hash: true,
1508
+ state: true
1509
+ });
1510
+ if (state.location.href !== nextLocation.href) {
1511
+ commitLocation({
1512
+ ...nextLocation,
1513
+ replace: true
1514
+ });
1515
+ }
1516
+ return () => {
1517
+ unsub();
1518
+ };
1519
+ }, [history]);
1520
+ const initialLoad = React.useRef(true);
1521
+ if (initialLoad.current) {
1522
+ initialLoad.current = false;
1523
+ safeLoad();
1524
+ }
1525
+ React.useLayoutEffect(() => {
1526
+ if (state.resolvedLocation !== state.location) {
1527
+ safeLoad();
1528
+ }
1529
+ }, [state.location]);
1530
+ React.useMemo(() => [...state.matches, ...state.pendingMatches].some(d => d.isFetching), [state.matches, state.pendingMatches]);
1531
+ const matchRoute = useStableCallback((state, location, opts) => {
1532
+ location = {
1533
+ ...location,
1534
+ to: location.to ? resolvePathWithBase(location.from || '', location.to) : undefined
1535
+ };
1536
+ const next = buildLocation(location);
1537
+ if (opts?.pending && state.status !== 'pending') {
1538
+ return false;
67
1539
  }
1540
+ const baseLocation = opts?.pending ? state.location : state.resolvedLocation;
1541
+ if (!baseLocation) {
1542
+ return false;
1543
+ }
1544
+ const match = matchPathname(basepath, baseLocation.pathname, {
1545
+ ...opts,
1546
+ to: next.pathname
1547
+ });
1548
+ if (!match) {
1549
+ return false;
1550
+ }
1551
+ if (opts?.includeSearch ?? true) {
1552
+ return partialDeepEqual(baseLocation.search, next.search) ? match : false;
1553
+ }
1554
+ return match;
68
1555
  });
69
- };
1556
+ const routerContextValue = {
1557
+ routeTree: router.routeTree,
1558
+ navigate,
1559
+ buildLink,
1560
+ state,
1561
+ matchRoute,
1562
+ routesById,
1563
+ options,
1564
+ history,
1565
+ load
1566
+ };
1567
+ return /*#__PURE__*/React.createElement(routerContext.Provider, {
1568
+ value: routerContextValue
1569
+ }, /*#__PURE__*/React.createElement(Matches, null));
1570
+ }
1571
+ function isCtrlEvent(e) {
1572
+ return !!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey);
1573
+ }
1574
+ class SearchParamError extends Error {}
1575
+ class PathParamError extends Error {}
1576
+ function getRouteMatch(state, id) {
1577
+ return [...state.pendingMatches, ...state.matches].find(d => d.id === id);
1578
+ }
1579
+
1580
+ const useLayoutEffect = typeof window !== 'undefined' ? React.useLayoutEffect : React.useEffect;
70
1581
 
71
1582
  //
72
1583
 
@@ -91,7 +1602,10 @@ function lazyRouteComponent(importer, exportName) {
91
1602
  //
92
1603
 
93
1604
  function useLinkProps(options) {
94
- const router = useRouter();
1605
+ const {
1606
+ buildLink,
1607
+ state: routerState
1608
+ } = useRouter();
95
1609
  const match = useMatch({
96
1610
  strict: false
97
1611
  });
@@ -125,7 +1639,7 @@ function useLinkProps(options) {
125
1639
  onTouchStart,
126
1640
  ...rest
127
1641
  } = options;
128
- const linkInfo = router.buildLink({
1642
+ const linkInfo = buildLink(routerState, {
129
1643
  from: options.to ? match.pathname : undefined,
130
1644
  ...options
131
1645
  });
@@ -201,92 +1715,48 @@ const Link = /*#__PURE__*/React.forwardRef((props, ref) => {
201
1715
  }));
202
1716
  });
203
1717
  function Navigate(props) {
204
- const router = useRouter();
1718
+ const {
1719
+ navigate
1720
+ } = useRouter();
205
1721
  const match = useMatch({
206
1722
  strict: false
207
1723
  });
208
- useLayoutEffect$1(() => {
209
- router.navigate({
1724
+ useLayoutEffect(() => {
1725
+ navigate({
210
1726
  from: props.to ? match.pathname : undefined,
211
1727
  ...props
212
1728
  });
213
1729
  }, []);
214
1730
  return null;
215
1731
  }
216
- const matchIdsContext = /*#__PURE__*/React.createContext(null);
217
- const routerContext = /*#__PURE__*/React.createContext(null);
218
- function useRouterState(opts) {
219
- const router = useRouter();
220
- return useStore(router.__store, opts?.select);
221
- }
222
- function RouterProvider({
223
- router,
224
- ...rest
225
- }) {
226
- router.update(rest);
227
- React.useEffect(() => {
228
- let unsub;
229
- React.startTransition(() => {
230
- unsub = router.mount();
231
- });
232
- return unsub;
233
- }, [router]);
234
- const Wrap = router.options.Wrap || React.Fragment;
235
- return /*#__PURE__*/React.createElement(Wrap, null, /*#__PURE__*/React.createElement(routerContext.Provider, {
236
- value: router
237
- }, /*#__PURE__*/React.createElement(Matches, null)));
238
- }
239
- function Matches() {
240
- const router = useRouter();
241
- const matchIds = useRouterState({
242
- select: state => {
243
- return state.renderedMatchIds;
244
- }
245
- });
246
- const locationKey = useRouterState({
247
- select: d => d.resolvedLocation.state?.key
248
- });
249
- const route = router.getRoute(rootRouteId);
250
- const errorComponent = React.useCallback(props => {
251
- return /*#__PURE__*/React.createElement(ErrorComponent, {
252
- ...props,
253
- useMatch: route.useMatch,
254
- useRouteContext: route.useRouteContext,
255
- useSearch: route.useSearch,
256
- useParams: route.useParams
257
- });
258
- }, [route]);
259
- return /*#__PURE__*/React.createElement(matchIdsContext.Provider, {
260
- value: [undefined, ...matchIds]
261
- }, /*#__PURE__*/React.createElement(CatchBoundary, {
262
- resetKey: locationKey,
263
- errorComponent: errorComponent,
264
- onCatch: () => {
265
- warning(false, `Error in router! Consider setting an 'errorComponent' in your RootRoute! 👍`);
266
- }
267
- }, /*#__PURE__*/React.createElement(Outlet, null)));
268
- }
1732
+ const matchesContext = /*#__PURE__*/React.createContext(null);
269
1733
  function useRouter() {
270
1734
  const value = React.useContext(routerContext);
271
- warning(value, 'useRouter must be used inside a <Router> component!');
1735
+ warning(value, 'useRouter must be used inside a <RouterProvider> component!');
272
1736
  return value;
273
1737
  }
1738
+ function useRouterState(opts) {
1739
+ const {
1740
+ state
1741
+ } = useRouter();
1742
+ // return useStore(router.__store, opts?.select as any)
1743
+ return opts?.select ? opts.select(state) : state;
1744
+ }
274
1745
  function useMatches(opts) {
275
- const matchIds = React.useContext(matchIdsContext);
1746
+ const contextMatches = React.useContext(matchesContext);
276
1747
  return useRouterState({
277
1748
  select: state => {
278
- const matches = state.renderedMatches.slice(state.renderedMatches.findIndex(d => d.id === matchIds[0]));
1749
+ const matches = state.matches.slice(state.matches.findIndex(d => d.id === contextMatches[0]?.id));
279
1750
  return opts?.select ? opts.select(matches) : matches;
280
1751
  }
281
1752
  });
282
1753
  }
283
1754
  function useMatch(opts) {
284
- const router = useRouter();
285
- const nearestMatchId = React.useContext(matchIdsContext)[0];
286
- const nearestMatchRouteId = router.getRouteMatch(nearestMatchId)?.routeId;
1755
+ const nearestMatch = React.useContext(matchesContext)[0];
1756
+ const nearestMatchRouteId = nearestMatch?.routeId;
287
1757
  const matchRouteId = useRouterState({
288
1758
  select: state => {
289
- const match = opts?.from ? state.renderedMatches.find(d => d.routeId === opts?.from) : state.renderedMatches.find(d => d.id === nearestMatchId);
1759
+ const match = opts?.from ? state.matches.find(d => d.routeId === opts?.from) : state.matches.find(d => d.id === nearestMatch.id);
290
1760
  return match.routeId;
291
1761
  }
292
1762
  });
@@ -295,29 +1765,17 @@ function useMatch(opts) {
295
1765
  }
296
1766
  const matchSelection = useRouterState({
297
1767
  select: state => {
298
- const match = opts?.from ? state.renderedMatches.find(d => d.routeId === opts?.from) : state.renderedMatches.find(d => d.id === nearestMatchId);
1768
+ const match = opts?.from ? state.matches.find(d => d.routeId === opts?.from) : state.matches.find(d => d.id === nearestMatch.id);
299
1769
  invariant(match, `Could not find ${opts?.from ? `an active match from "${opts.from}"` : 'a nearest match!'}`);
300
1770
  return opts?.select ? opts.select(match) : match;
301
1771
  }
302
1772
  });
303
1773
  return matchSelection;
304
1774
  }
305
- function useLoader(opts) {
306
- return useMatch({
307
- ...opts,
308
- select: match => opts?.select ? opts?.select(match.loaderData) : match.loaderData
309
- });
310
- }
311
- function useRouterContext(opts) {
1775
+ function useRouteMeta(opts) {
312
1776
  return useMatch({
313
1777
  ...opts,
314
- select: match => opts?.select ? opts.select(match.context) : match.context
315
- });
316
- }
317
- function useRouteContext(opts) {
318
- return useMatch({
319
- ...opts,
320
- select: match => opts?.select ? opts.select(match.context) : match.context
1778
+ select: match => opts?.select ? opts.select(match.meta) : match.meta
321
1779
  });
322
1780
  }
323
1781
  function useSearch(opts) {
@@ -331,18 +1789,20 @@ function useSearch(opts) {
331
1789
  function useParams(opts) {
332
1790
  return useRouterState({
333
1791
  select: state => {
334
- const params = last(state.renderedMatches)?.params;
1792
+ const params = last(state.matches)?.params;
335
1793
  return opts?.select ? opts.select(params) : params;
336
1794
  }
337
1795
  });
338
1796
  }
339
1797
  function useNavigate(defaultOpts) {
340
- const router = useRouter();
1798
+ const {
1799
+ navigate
1800
+ } = useRouter();
341
1801
  const match = useMatch({
342
1802
  strict: false
343
1803
  });
344
1804
  return React.useCallback(opts => {
345
- return router.navigate({
1805
+ return navigate({
346
1806
  from: opts?.to ? match.pathname : undefined,
347
1807
  ...defaultOpts,
348
1808
  ...opts
@@ -350,19 +1810,62 @@ function useNavigate(defaultOpts) {
350
1810
  }, []);
351
1811
  }
352
1812
  function useMatchRoute() {
353
- const router = useRouter();
1813
+ const {
1814
+ state,
1815
+ matchRoute
1816
+ } = useRouter();
354
1817
  return React.useCallback(opts => {
355
1818
  const {
356
1819
  pending,
357
1820
  caseSensitive,
358
1821
  ...rest
359
1822
  } = opts;
360
- return router.matchRoute(rest, {
1823
+ return matchRoute(state, rest, {
361
1824
  pending,
362
1825
  caseSensitive
363
1826
  });
364
1827
  }, []);
365
1828
  }
1829
+ function Matches() {
1830
+ const {
1831
+ routesById,
1832
+ state
1833
+ } = useRouter();
1834
+
1835
+ // const matches = useRouterState({
1836
+ // select: (state) => {
1837
+ // return state.matches
1838
+ // },
1839
+ // })
1840
+
1841
+ const {
1842
+ matches
1843
+ } = state;
1844
+ const locationKey = useRouterState({
1845
+ select: d => d.resolvedLocation.state?.key
1846
+ });
1847
+ const route = routesById[rootRouteId];
1848
+ const errorComponent = React.useCallback(props => {
1849
+ return /*#__PURE__*/React.createElement(ErrorComponent, {
1850
+ ...props,
1851
+ useMatch: route.useMatch,
1852
+ useRouteMeta: route.useRouteMeta,
1853
+ useSearch: route.useSearch,
1854
+ useParams: route.useParams
1855
+ });
1856
+ }, [route]);
1857
+ return /*#__PURE__*/React.createElement(matchesContext.Provider, {
1858
+ value: matches
1859
+ }, /*#__PURE__*/React.createElement(CatchBoundary, {
1860
+ resetKey: locationKey,
1861
+ errorComponent: errorComponent,
1862
+ onCatch: () => {
1863
+ warning(false, `Error in router! Consider setting an 'errorComponent' in your RootRoute! 👍`);
1864
+ }
1865
+ }, matches.length ? /*#__PURE__*/React.createElement(Match, {
1866
+ matches: matches
1867
+ }) : null));
1868
+ }
366
1869
  function MatchRoute(props) {
367
1870
  const matchRoute = useMatchRoute();
368
1871
  const params = matchRoute(props);
@@ -372,44 +1875,47 @@ function MatchRoute(props) {
372
1875
  return !!params ? props.children : null;
373
1876
  }
374
1877
  function Outlet() {
375
- const matchIds = React.useContext(matchIdsContext).slice(1);
376
- if (!matchIds[0]) {
1878
+ const matches = React.useContext(matchesContext).slice(1);
1879
+ if (!matches[0]) {
377
1880
  return null;
378
1881
  }
379
1882
  return /*#__PURE__*/React.createElement(Match, {
380
- matchIds: matchIds
1883
+ matches: matches
381
1884
  });
382
1885
  }
383
1886
  const defaultPending = () => null;
384
1887
  function Match({
385
- matchIds
1888
+ matches
386
1889
  }) {
387
- const router = useRouter();
388
- const matchId = matchIds[0];
389
- const routeId = router.getRouteMatch(matchId).routeId;
390
- const route = router.getRoute(routeId);
1890
+ const {
1891
+ options,
1892
+ routesById
1893
+ } = useRouter();
1894
+ const match = matches[0];
1895
+ const routeId = match?.routeId;
1896
+ const route = routesById[routeId];
391
1897
  const locationKey = useRouterState({
392
1898
  select: s => s.resolvedLocation.state?.key
393
1899
  });
394
- const PendingComponent = route.options.pendingComponent ?? router.options.defaultPendingComponent ?? defaultPending;
395
- const routeErrorComponent = route.options.errorComponent ?? router.options.defaultErrorComponent ?? ErrorComponent;
1900
+ const PendingComponent = route.options.pendingComponent ?? options.defaultPendingComponent ?? defaultPending;
1901
+ const routeErrorComponent = route.options.errorComponent ?? options.defaultErrorComponent ?? ErrorComponent;
396
1902
  const ResolvedSuspenseBoundary = route.options.wrapInSuspense ?? !route.isRoot ? React.Suspense : SafeFragment;
397
1903
  const ResolvedCatchBoundary = !!routeErrorComponent ? CatchBoundary : SafeFragment;
398
1904
  const errorComponent = React.useCallback(props => {
399
1905
  return /*#__PURE__*/React.createElement(routeErrorComponent, {
400
1906
  ...props,
401
1907
  useMatch: route.useMatch,
402
- useRouteContext: route.useRouteContext,
1908
+ useRouteMeta: route.useRouteMeta,
403
1909
  useSearch: route.useSearch,
404
1910
  useParams: route.useParams
405
1911
  });
406
1912
  }, [route]);
407
- return /*#__PURE__*/React.createElement(matchIdsContext.Provider, {
408
- value: matchIds
1913
+ return /*#__PURE__*/React.createElement(matchesContext.Provider, {
1914
+ value: matches
409
1915
  }, /*#__PURE__*/React.createElement(ResolvedSuspenseBoundary, {
410
1916
  fallback: /*#__PURE__*/React.createElement(PendingComponent, {
411
1917
  useMatch: route.useMatch,
412
- useRouteContext: route.useRouteContext,
1918
+ useRouteMeta: route.useRouteMeta,
413
1919
  useSearch: route.useSearch,
414
1920
  useParams: route.useParams
415
1921
  })
@@ -417,44 +1923,32 @@ function Match({
417
1923
  resetKey: locationKey,
418
1924
  errorComponent: errorComponent,
419
1925
  onCatch: () => {
420
- warning(false, `Error in route match: ${matchId}`);
1926
+ warning(false, `Error in route match: ${match.id}`);
421
1927
  }
422
1928
  }, /*#__PURE__*/React.createElement(MatchInner, {
423
- matchId: matchId,
424
- PendingComponent: PendingComponent
1929
+ match: match
425
1930
  }))));
426
1931
  }
427
1932
  function MatchInner({
428
- matchId,
429
- PendingComponent
1933
+ match
430
1934
  }) {
431
- const router = useRouter();
432
- const match = useRouterState({
433
- select: d => {
434
- const match = d.matchesById[matchId];
435
- return pick(match, ['status', 'loadPromise', 'routeId', 'error']);
436
- }
437
- });
438
- const route = router.getRoute(match.routeId);
1935
+ const {
1936
+ options,
1937
+ routesById
1938
+ } = useRouter();
1939
+ const route = routesById[match.routeId];
439
1940
  if (match.status === 'error') {
440
1941
  throw match.error;
441
1942
  }
442
1943
  if (match.status === 'pending') {
443
- return /*#__PURE__*/React.createElement(PendingComponent, {
444
- useLoader: route.useLoader,
445
- useMatch: route.useMatch,
446
- useRouteContext: route.useRouteContext,
447
- useSearch: route.useSearch,
448
- useParams: route.useParams
449
- });
1944
+ throw match.loadPromise;
450
1945
  }
451
1946
  if (match.status === 'success') {
452
- let comp = route.options.component ?? router.options.defaultComponent;
1947
+ let comp = route.options.component ?? options.defaultComponent;
453
1948
  if (comp) {
454
1949
  return /*#__PURE__*/React.createElement(comp, {
455
- useLoader: route.useLoader,
456
1950
  useMatch: route.useMatch,
457
- useRouteContext: route.useRouteContext,
1951
+ useRouteMeta: route.useRouteMeta,
458
1952
  useSearch: route.useSearch,
459
1953
  useParams: route.useParams
460
1954
  });
@@ -466,24 +1960,37 @@ function MatchInner({
466
1960
  function SafeFragment(props) {
467
1961
  return /*#__PURE__*/React.createElement(React.Fragment, null, props.children);
468
1962
  }
469
- function useInjectHtml() {
470
- const router = useRouter();
471
- return React.useCallback(html => {
472
- router.injectHtml(html);
473
- }, []);
474
- }
475
- function useDehydrate() {
476
- const router = useRouter();
477
- return React.useCallback(function dehydrate(key, data) {
478
- return router.dehydrateData(key, data);
479
- }, []);
480
- }
481
- function useHydrate() {
482
- const router = useRouter();
483
- return function hydrate(key) {
484
- return router.hydrateData(key);
485
- };
486
- }
1963
+
1964
+ // export function useInjectHtml() {
1965
+ // const { } = useRouter()
1966
+
1967
+ // return React.useCallback(
1968
+ // (html: string | (() => Promise<string> | string)) => {
1969
+ // router.injectHtml(html)
1970
+ // },
1971
+ // [],
1972
+ // )
1973
+ // }
1974
+
1975
+ // export function useDehydrate() {
1976
+ // const { } = useRouter()
1977
+
1978
+ // return React.useCallback(function dehydrate<T>(
1979
+ // key: any,
1980
+ // data: T | (() => Promise<T> | T),
1981
+ // ) {
1982
+ // return router.dehydrateData(key, data)
1983
+ // },
1984
+ // [])
1985
+ // }
1986
+
1987
+ // export function useHydrate() {
1988
+ // const { } = useRouter()
1989
+
1990
+ // return function hydrate<T = unknown>(key: any) {
1991
+ // return router.hydrateData(key) as T
1992
+ // }
1993
+ // }
487
1994
 
488
1995
  // This is the messiest thing ever... I'm either seriously tired (likely) or
489
1996
  // there has to be a better way to reset error boundaries when the
@@ -574,10 +2081,12 @@ function ErrorComponent({
574
2081
  }, error.message ? /*#__PURE__*/React.createElement("code", null, error.message) : null)) : null);
575
2082
  }
576
2083
  function useBlocker(message, condition = true) {
577
- const router = useRouter();
2084
+ const {
2085
+ history
2086
+ } = useRouter();
578
2087
  React.useEffect(() => {
579
2088
  if (!condition) return;
580
- let unblock = router.history.block((retry, cancel) => {
2089
+ let unblock = history.block((retry, cancel) => {
581
2090
  if (window.confirm(message)) {
582
2091
  unblock();
583
2092
  retry();
@@ -613,45 +2122,124 @@ function shallow(objA, objB) {
613
2122
  return true;
614
2123
  }
615
2124
 
616
- const useLayoutEffect = typeof window !== 'undefined' ? React.useLayoutEffect : React.useEffect;
617
- function useScrollRestoration(options) {
618
- const router = useRouter();
619
- useLayoutEffect(() => {
620
- return watchScrollPositions(router, options);
621
- }, []);
622
- useLayoutEffect(() => {
623
- restoreScrollPositions(router, options);
624
- });
625
- }
626
- function ScrollRestoration(props) {
627
- useScrollRestoration(props);
628
- return null;
629
- }
2125
+ const rootRouteId = '__root__';
630
2126
 
631
- function useAwaited({
632
- promise
633
- }) {
634
- const router = useRouter();
635
- let state = promise.__deferredState;
636
- const key = `__TSR__DEFERRED__${state.uid}`;
637
- if (isDehydratedDeferred(promise)) {
638
- state = router.hydrateData(key);
639
- promise = Promise.resolve(state.data);
640
- promise.__deferredState = state;
641
- }
642
- if (state.status === 'pending') {
643
- throw promise;
2127
+ // export type MetaOptions = keyof PickRequired<RouteMeta> extends never
2128
+ // ? {
2129
+ // meta?: RouteMeta
2130
+ // }
2131
+ // : {
2132
+ // meta: RouteMeta
2133
+ // }
2134
+ // The parse type here allows a zod schema to be passed directly to the validator
2135
+ class Route {
2136
+ // Set up in this.init()
2137
+
2138
+ // customId!: TCustomId
2139
+
2140
+ // Optional
2141
+
2142
+ constructor(options) {
2143
+ this.options = options || {};
2144
+ this.isRoot = !options?.getParentRoute;
2145
+ Route.__onInit(this);
644
2146
  }
645
- if (state.status === 'error') {
646
- throw state.error;
2147
+ init = opts => {
2148
+ this.originalIndex = opts.originalIndex;
2149
+ const options = this.options;
2150
+ const isRoot = !options?.path && !options?.id;
2151
+ this.parentRoute = this.options?.getParentRoute?.();
2152
+ if (isRoot) {
2153
+ this.path = rootRouteId;
2154
+ } else {
2155
+ invariant(this.parentRoute, `Child Route instances must pass a 'getParentRoute: () => ParentRoute' option that returns a Route instance.`);
2156
+ }
2157
+ let path = isRoot ? rootRouteId : options.path;
2158
+
2159
+ // If the path is anything other than an index path, trim it up
2160
+ if (path && path !== '/') {
2161
+ path = trimPath(path);
2162
+ }
2163
+ const customId = options?.id || path;
2164
+
2165
+ // Strip the parentId prefix from the first level of children
2166
+ let id = isRoot ? rootRouteId : joinPaths([this.parentRoute.id === rootRouteId ? '' : this.parentRoute.id, customId]);
2167
+ if (path === rootRouteId) {
2168
+ path = '/';
2169
+ }
2170
+ if (id !== rootRouteId) {
2171
+ id = joinPaths(['/', id]);
2172
+ }
2173
+ const fullPath = id === rootRouteId ? '/' : joinPaths([this.parentRoute.fullPath, path]);
2174
+ this.path = path;
2175
+ this.id = id;
2176
+ // this.customId = customId as TCustomId
2177
+ this.fullPath = fullPath;
2178
+ this.to = fullPath;
2179
+ };
2180
+ addChildren = children => {
2181
+ this.children = children;
2182
+ return this;
2183
+ };
2184
+ update = options => {
2185
+ Object.assign(this.options, options);
2186
+ return this;
2187
+ };
2188
+ static __onInit = route => {
2189
+ // This is a dummy static method that should get
2190
+ // replaced by a framework specific implementation if necessary
2191
+ };
2192
+ useMatch = opts => {
2193
+ return useMatch({
2194
+ ...opts,
2195
+ from: this.id
2196
+ });
2197
+ };
2198
+ useRouteMeta = opts => {
2199
+ return useMatch({
2200
+ ...opts,
2201
+ from: this.id,
2202
+ select: d => opts?.select ? opts.select(d.meta) : d.meta
2203
+ });
2204
+ };
2205
+ useSearch = opts => {
2206
+ return useSearch({
2207
+ ...opts,
2208
+ from: this.id
2209
+ });
2210
+ };
2211
+ useParams = opts => {
2212
+ return useParams({
2213
+ ...opts,
2214
+ from: this.id
2215
+ });
2216
+ };
2217
+ }
2218
+ class RouterMeta {
2219
+ constructor() {}
2220
+ createRootRoute = options => {
2221
+ return new RootRoute(options);
2222
+ };
2223
+ }
2224
+ class RootRoute extends Route {
2225
+ constructor(options) {
2226
+ super(options);
647
2227
  }
648
- router.dehydrateData(key, state);
649
- return [state.data];
650
2228
  }
651
- function Await(props) {
652
- const awaited = useAwaited(props);
653
- return props.children(...awaited);
2229
+ function createRouteMask(opts) {
2230
+ return opts;
2231
+ }
2232
+
2233
+ class FileRoute {
2234
+ constructor(path) {
2235
+ this.path = path;
2236
+ }
2237
+ createRoute = options => {
2238
+ const route = new Route(options);
2239
+ route.isRoot = false;
2240
+ return route;
2241
+ };
654
2242
  }
655
2243
 
656
- export { Await, Block, CatchBoundary, CatchBoundaryImpl, ErrorComponent, Link, MatchRoute, Navigate, Outlet, RouterProvider, ScrollRestoration, lazyRouteComponent, matchIdsContext, routerContext, shallow, useAwaited, useBlocker, useDehydrate, useHydrate, useInjectHtml, useLinkProps, useLoader, useMatch, useMatchRoute, useMatches, useNavigate, useParams, useRouteContext, useRouter, useRouterContext, useRouterState, useScrollRestoration, useSearch };
2244
+ export { Block, CatchBoundary, CatchBoundaryImpl, ErrorComponent, FileRoute, Link, MatchRoute, Matches, Navigate, Outlet, PathParamError, RootRoute, Route, Router, RouterMeta, RouterProvider, SearchParamError, cleanPath, componentTypes, createRouteMask, decode, defaultParseSearch, defaultStringifySearch, encode, functionalUpdate, getInitialRouterState, getRouteMatch, interpolatePath, isPlainObject, isRedirect, isServer, joinPaths, last, lazyFn, lazyRouteComponent, matchByPath, matchPathname, matchesContext, parsePathname, parseSearchWith, partialDeepEqual, pick, redirect, replaceEqualDeep, resolvePath, rootRouteId, routerContext, shallow, stringifySearchWith, trimPath, trimPathLeft, trimPathRight, useBlocker, useLinkProps, useMatch, useMatchRoute, useMatches, useNavigate, useParams, useRouteMeta, useRouter, useRouterState, useSearch, useStableCallback };
657
2245
  //# sourceMappingURL=index.js.map