@tanstack/router-core 0.0.1-beta.35 → 0.0.1-beta.39

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 (38) hide show
  1. package/build/cjs/index.js +2 -1
  2. package/build/cjs/index.js.map +1 -1
  3. package/build/cjs/path.js +5 -7
  4. package/build/cjs/path.js.map +1 -1
  5. package/build/cjs/route.js +112 -96
  6. package/build/cjs/route.js.map +1 -1
  7. package/build/cjs/routeConfig.js +2 -2
  8. package/build/cjs/routeConfig.js.map +1 -1
  9. package/build/cjs/routeMatch.js +107 -65
  10. package/build/cjs/routeMatch.js.map +1 -1
  11. package/build/cjs/router.js +352 -372
  12. package/build/cjs/router.js.map +1 -1
  13. package/build/cjs/searchParams.js +4 -3
  14. package/build/cjs/searchParams.js.map +1 -1
  15. package/build/cjs/sharedClone.js +122 -0
  16. package/build/cjs/sharedClone.js.map +1 -0
  17. package/build/cjs/utils.js +1 -59
  18. package/build/cjs/utils.js.map +1 -1
  19. package/build/esm/index.js +686 -614
  20. package/build/esm/index.js.map +1 -1
  21. package/build/stats-html.html +1 -1
  22. package/build/stats-react.json +183 -158
  23. package/build/types/index.d.ts +61 -78
  24. package/build/umd/index.development.js +1032 -617
  25. package/build/umd/index.development.js.map +1 -1
  26. package/build/umd/index.production.js +1 -1
  27. package/build/umd/index.production.js.map +1 -1
  28. package/package.json +2 -1
  29. package/src/index.ts +1 -0
  30. package/src/link.ts +20 -12
  31. package/src/route.ts +160 -140
  32. package/src/routeConfig.ts +7 -2
  33. package/src/routeMatch.ts +146 -99
  34. package/src/router.ts +462 -523
  35. package/src/sharedClone.ts +118 -0
  36. package/src/utils.ts +0 -65
  37. package/build/cjs/_virtual/_rollupPluginBabelHelpers.js +0 -31
  38. package/build/cjs/_virtual/_rollupPluginBabelHelpers.js.map +0 -1
@@ -12,64 +12,8 @@ import { createMemoryHistory, createBrowserHistory } from 'history';
12
12
  export { createBrowserHistory, createHashHistory, createMemoryHistory } from 'history';
13
13
  import invariant from 'tiny-invariant';
14
14
  export { default as invariant } from 'tiny-invariant';
15
+ import { createStore, batch } from '@solidjs/reactivity';
15
16
 
16
- /**
17
- * This function returns `a` if `b` is deeply equal.
18
- * If not, it will replace any deeply equal children of `b` with those of `a`.
19
- * This can be used for structural sharing between JSON values for example.
20
- */
21
- function replaceEqualDeep(prev, next) {
22
- if (prev === next) {
23
- return prev;
24
- }
25
- const array = Array.isArray(prev) && Array.isArray(next);
26
- if (array || isPlainObject(prev) && isPlainObject(next)) {
27
- const aSize = array ? prev.length : Object.keys(prev).length;
28
- const bItems = array ? next : Object.keys(next);
29
- const bSize = bItems.length;
30
- const copy = array ? [] : {};
31
- let equalItems = 0;
32
- for (let i = 0; i < bSize; i++) {
33
- const key = array ? i : bItems[i];
34
- copy[key] = replaceEqualDeep(prev[key], next[key]);
35
- if (copy[key] === prev[key]) {
36
- equalItems++;
37
- }
38
- }
39
- return aSize === bSize && equalItems === aSize ? prev : copy;
40
- }
41
- return next;
42
- }
43
-
44
- // Copied from: https://github.com/jonschlinkert/is-plain-object
45
- function isPlainObject(o) {
46
- if (!hasObjectPrototype(o)) {
47
- return false;
48
- }
49
-
50
- // If has modified constructor
51
- const ctor = o.constructor;
52
- if (typeof ctor === 'undefined') {
53
- return true;
54
- }
55
-
56
- // If has modified prototype
57
- const prot = ctor.prototype;
58
- if (!hasObjectPrototype(prot)) {
59
- return false;
60
- }
61
-
62
- // If constructor does not have an Object-specific method
63
- if (!prot.hasOwnProperty('isPrototypeOf')) {
64
- return false;
65
- }
66
-
67
- // Most likely a plain Object
68
- return true;
69
- }
70
- function hasObjectPrototype(o) {
71
- return Object.prototype.toString.call(o) === '[object Object]';
72
- }
73
17
  function last(arr) {
74
18
  return arr[arr.length - 1];
75
19
  }
@@ -78,7 +22,7 @@ function warning(cond, message) {
78
22
  if (typeof console !== 'undefined') console.warn(message);
79
23
  try {
80
24
  throw new Error(message);
81
- } catch (_unused) {}
25
+ } catch {}
82
26
  }
83
27
  return true;
84
28
  }
@@ -115,8 +59,8 @@ function trimPath(path) {
115
59
  return trimPathRight(trimPathLeft(path));
116
60
  }
117
61
  function resolvePath(basepath, base, to) {
118
- base = base.replace(new RegExp("^" + basepath), '/');
119
- to = to.replace(new RegExp("^" + basepath), '/');
62
+ base = base.replace(new RegExp(`^${basepath}`), '/');
63
+ to = to.replace(new RegExp(`^${basepath}`), '/');
120
64
  let baseSegments = parsePathname(base);
121
65
  const toSegments = parsePathname(to);
122
66
  toSegments.forEach((toSegment, index) => {
@@ -197,8 +141,7 @@ function interpolatePath(path, params, leaveWildcard) {
197
141
  return '';
198
142
  }
199
143
  if (segment.type === 'param') {
200
- var _segment$value$substr;
201
- return (_segment$value$substr = params[segment.value.substring(1)]) != null ? _segment$value$substr : '';
144
+ return params[segment.value.substring(1)] ?? '';
202
145
  }
203
146
  return segment.value;
204
147
  }));
@@ -210,16 +153,15 @@ function matchPathname(basepath, currentPathname, matchLocation) {
210
153
  if (matchLocation.to && !pathParams) {
211
154
  return;
212
155
  }
213
- return pathParams != null ? pathParams : {};
156
+ return pathParams ?? {};
214
157
  }
215
158
  function matchByPath(basepath, from, matchLocation) {
216
- var _matchLocation$to;
217
159
  if (!from.startsWith(basepath)) {
218
160
  return undefined;
219
161
  }
220
162
  from = basepath != '/' ? from.substring(basepath.length) : from;
221
163
  const baseSegments = parsePathname(from);
222
- const to = "" + ((_matchLocation$to = matchLocation.to) != null ? _matchLocation$to : '*');
164
+ const to = `${matchLocation.to ?? '*'}`;
223
165
  const routeSegments = parsePathname(to);
224
166
  const params = {};
225
167
  let isMatch = (() => {
@@ -320,135 +262,136 @@ function decode(str) {
320
262
  return out;
321
263
  }
322
264
 
323
- function _extends() {
324
- _extends = Object.assign ? Object.assign.bind() : function (target) {
325
- for (var i = 1; i < arguments.length; i++) {
326
- var source = arguments[i];
327
- for (var key in source) {
328
- if (Object.prototype.hasOwnProperty.call(source, key)) {
329
- target[key] = source[key];
330
- }
331
- }
332
- }
333
- return target;
334
- };
335
- return _extends.apply(this, arguments);
336
- }
337
-
338
- function createRoute(routeConfig, options, parent, router) {
265
+ function createRoute(routeConfig, options, originalIndex, parent, router) {
339
266
  const {
340
267
  id,
341
268
  routeId,
342
269
  path: routePath,
343
270
  fullPath
344
271
  } = routeConfig;
345
- const action = router.state.actions[id] || (() => {
346
- router.state.actions[id] = {
347
- submissions: [],
348
- submit: async (submission, actionOpts) => {
349
- var _actionOpts$invalidat;
350
- if (!route) {
351
- return;
352
- }
353
- const invalidate = (_actionOpts$invalidat = actionOpts == null ? void 0 : actionOpts.invalidate) != null ? _actionOpts$invalidat : true;
354
- if (!(actionOpts != null && actionOpts.multi)) {
355
- action.submissions = action.submissions.filter(d => d.isMulti);
356
- }
357
- const actionState = {
358
- submittedAt: Date.now(),
359
- status: 'pending',
360
- submission,
361
- isMulti: !!(actionOpts != null && actionOpts.multi)
362
- };
363
- action.current = actionState;
364
- action.latest = actionState;
365
- action.submissions.push(actionState);
366
- router.notify();
367
- try {
368
- const res = await (route.options.action == null ? void 0 : route.options.action(submission));
369
- actionState.data = res;
370
- if (invalidate) {
371
- router.invalidateRoute({
372
- to: '.',
373
- fromCurrent: true
374
- });
375
- await router.reload();
376
- }
377
- actionState.status = 'success';
378
- return res;
379
- } catch (err) {
380
- console.log('tanner');
381
- console.error(err);
382
- actionState.error = err;
383
- actionState.status = 'error';
384
- } finally {
385
- router.notify();
386
- }
387
- }
388
- };
389
- return router.state.actions[id];
390
- })();
391
- const loader = router.state.loaders[id] || (() => {
392
- router.state.loaders[id] = {
393
- pending: [],
394
- fetch: async loaderContext => {
395
- if (!route) {
396
- return;
397
- }
398
- const loaderState = {
399
- loadedAt: Date.now(),
400
- loaderContext
401
- };
402
- loader.current = loaderState;
403
- loader.latest = loaderState;
404
- loader.pending.push(loaderState);
405
-
406
- // router.state = {
407
- // ...router.state,
408
- // currentAction: loaderState,
409
- // latestAction: loaderState,
410
- // }
411
-
412
- router.notify();
413
- try {
414
- return await (route.options.loader == null ? void 0 : route.options.loader(loaderContext));
415
- } finally {
416
- loader.pending = loader.pending.filter(d => d !== loaderState);
417
- // router.removeActionQueue.push({ loader, loaderState })
418
- router.notify();
419
- }
420
- }
421
- };
422
- return router.state.loaders[id];
423
- })();
424
272
  let route = {
425
273
  routeInfo: undefined,
426
274
  routeId: id,
427
275
  routeRouteId: routeId,
276
+ originalIndex,
428
277
  routePath,
429
278
  fullPath,
430
279
  options,
431
280
  router,
432
281
  childRoutes: undefined,
433
282
  parentRoute: parent,
434
- action,
435
- loader: loader,
436
- buildLink: options => {
437
- return router.buildLink(_extends({}, options, {
438
- from: fullPath
439
- }));
440
- },
441
- navigate: options => {
442
- return router.navigate(_extends({}, options, {
443
- from: fullPath
444
- }));
283
+ get action() {
284
+ let action = router.store.actions[id] || (() => {
285
+ router.setStore(s => {
286
+ s.actions[id] = {
287
+ submissions: [],
288
+ submit: async (submission, actionOpts) => {
289
+ if (!route) {
290
+ return;
291
+ }
292
+ const invalidate = (actionOpts == null ? void 0 : actionOpts.invalidate) ?? true;
293
+ const [actionStore, setActionStore] = createStore({
294
+ submittedAt: Date.now(),
295
+ status: 'pending',
296
+ submission,
297
+ isMulti: !!(actionOpts != null && actionOpts.multi)
298
+ });
299
+ router.setStore(s => {
300
+ if (!(actionOpts != null && actionOpts.multi)) {
301
+ s.actions[id].submissions = action.submissions.filter(d => d.isMulti);
302
+ }
303
+ s.actions[id].current = actionStore;
304
+ s.actions[id].latest = actionStore;
305
+ s.actions[id].submissions.push(actionStore);
306
+ });
307
+ try {
308
+ const res = await (route.options.action == null ? void 0 : route.options.action(submission));
309
+ setActionStore(s => {
310
+ s.data = res;
311
+ });
312
+ if (invalidate) {
313
+ router.invalidateRoute({
314
+ to: '.',
315
+ fromCurrent: true
316
+ });
317
+ await router.reload();
318
+ }
319
+ setActionStore(s => {
320
+ s.status = 'success';
321
+ });
322
+ return res;
323
+ } catch (err) {
324
+ console.error(err);
325
+ setActionStore(s => {
326
+ s.error = err;
327
+ s.status = 'error';
328
+ });
329
+ }
330
+ }
331
+ };
332
+ });
333
+ return router.store.actions[id];
334
+ })();
335
+ return action;
445
336
  },
446
- matchRoute: (matchLocation, opts) => {
447
- return router.matchRoute(_extends({}, matchLocation, {
448
- from: fullPath
449
- }), opts);
337
+ get loader() {
338
+ let loader = router.store.loaders[id] || (() => {
339
+ router.setStore(s => {
340
+ s.loaders[id] = {
341
+ pending: [],
342
+ fetch: async loaderContext => {
343
+ if (!route) {
344
+ return;
345
+ }
346
+ const loaderState = {
347
+ loadedAt: Date.now(),
348
+ loaderContext
349
+ };
350
+ router.setStore(s => {
351
+ s.loaders[id].current = loaderState;
352
+ s.loaders[id].latest = loaderState;
353
+ s.loaders[id].pending.push(loaderState);
354
+ });
355
+ try {
356
+ return await (route.options.loader == null ? void 0 : route.options.loader(loaderContext));
357
+ } finally {
358
+ router.setStore(s => {
359
+ s.loaders[id].pending = s.loaders[id].pending.filter(d => d !== loaderState);
360
+ });
361
+ }
362
+ }
363
+ };
364
+ });
365
+ return router.store.loaders[id];
366
+ })();
367
+ return loader;
450
368
  }
369
+
370
+ // buildLink: (options) => {
371
+ // return router.buildLink({
372
+ // ...options,
373
+ // from: fullPath,
374
+ // } as any) as any
375
+ // },
376
+
377
+ // navigate: (options) => {
378
+ // return router.navigate({
379
+ // ...options,
380
+ // from: fullPath,
381
+ // } as any) as any
382
+ // },
383
+
384
+ // matchRoute: (matchLocation, opts) => {
385
+ // return router.matchRoute(
386
+ // {
387
+ // ...matchLocation,
388
+ // from: fullPath,
389
+ // } as any,
390
+ // opts,
391
+ // ) as any
392
+ // },
451
393
  };
394
+
452
395
  router.options.createRoute == null ? void 0 : router.options.createRoute({
453
396
  router,
454
397
  route
@@ -457,7 +400,7 @@ function createRoute(routeConfig, options, parent, router) {
457
400
  }
458
401
 
459
402
  const rootRouteId = '__root__';
460
- const createRouteConfig = function createRouteConfig(options, children, isRoot, parentId, parentPath) {
403
+ const createRouteConfig = function (options, children, isRoot, parentId, parentPath) {
461
404
  if (options === void 0) {
462
405
  options = {};
463
406
  }
@@ -497,53 +440,194 @@ const createRouteConfig = function createRouteConfig(options, children, isRoot,
497
440
  addChildren: children => createRouteConfig(options, children, false, parentId, parentPath),
498
441
  createRoute: childOptions => createRouteConfig(childOptions, undefined, false, id, fullPath),
499
442
  generate: () => {
500
- invariant(false, "routeConfig.generate() is used by TanStack Router's file-based routing code generation and should not actually be called during runtime. ");
443
+ invariant(false, `routeConfig.generate() is used by TanStack Router's file-based routing code generation and should not actually be called during runtime. `);
501
444
  }
502
445
  };
503
446
  };
504
447
 
448
+ /**
449
+ * This function returns `a` if `b` is deeply equal.
450
+ * If not, it will replace any deeply equal children of `b` with those of `a`.
451
+ * This can be used for structural sharing between JSON values for example.
452
+ */
453
+ function sharedClone(prev, next, touchAll) {
454
+ const things = new Map();
455
+ function recurse(prev, next) {
456
+ if (prev === next) {
457
+ return prev;
458
+ }
459
+ if (things.has(next)) {
460
+ return things.get(next);
461
+ }
462
+ const prevIsArray = Array.isArray(prev);
463
+ const nextIsArray = Array.isArray(next);
464
+ const prevIsObj = isPlainObject(prev);
465
+ const nextIsObj = isPlainObject(next);
466
+ const isArray = prevIsArray && nextIsArray;
467
+ const isObj = prevIsObj && nextIsObj;
468
+ const isSameStructure = isArray || isObj;
469
+
470
+ // Both are arrays or objects
471
+ if (isSameStructure) {
472
+ const aSize = isArray ? prev.length : Object.keys(prev).length;
473
+ const bItems = isArray ? next : Object.keys(next);
474
+ const bSize = bItems.length;
475
+ const copy = isArray ? [] : {};
476
+ let equalItems = 0;
477
+ for (let i = 0; i < bSize; i++) {
478
+ const key = isArray ? i : bItems[i];
479
+ if (copy[key] === prev[key]) {
480
+ equalItems++;
481
+ }
482
+ }
483
+ if (aSize === bSize && equalItems === aSize) {
484
+ things.set(next, prev);
485
+ return prev;
486
+ }
487
+ things.set(next, copy);
488
+ for (let i = 0; i < bSize; i++) {
489
+ const key = isArray ? i : bItems[i];
490
+ if (typeof bItems[i] === 'function') {
491
+ copy[key] = prev[key];
492
+ } else {
493
+ copy[key] = recurse(prev[key], next[key]);
494
+ }
495
+ if (copy[key] === prev[key]) {
496
+ equalItems++;
497
+ }
498
+ }
499
+ return copy;
500
+ }
501
+ if (nextIsArray) {
502
+ const copy = [];
503
+ things.set(next, copy);
504
+ for (let i = 0; i < next.length; i++) {
505
+ copy[i] = recurse(undefined, next[i]);
506
+ }
507
+ return copy;
508
+ }
509
+ if (nextIsObj) {
510
+ const copy = {};
511
+ things.set(next, copy);
512
+ const nextKeys = Object.keys(next);
513
+ for (let i = 0; i < nextKeys.length; i++) {
514
+ const key = nextKeys[i];
515
+ copy[key] = recurse(undefined, next[key]);
516
+ }
517
+ return copy;
518
+ }
519
+ return next;
520
+ }
521
+ return recurse(prev, next);
522
+ }
523
+
524
+ // Copied from: https://github.com/jonschlinkert/is-plain-object
525
+ function isPlainObject(o) {
526
+ if (!hasObjectPrototype(o)) {
527
+ return false;
528
+ }
529
+
530
+ // If has modified constructor
531
+ const ctor = o.constructor;
532
+ if (typeof ctor === 'undefined') {
533
+ return true;
534
+ }
535
+
536
+ // If has modified prototype
537
+ const prot = ctor.prototype;
538
+ if (!hasObjectPrototype(prot)) {
539
+ return false;
540
+ }
541
+
542
+ // If constructor does not have an Object-specific method
543
+ if (!prot.hasOwnProperty('isPrototypeOf')) {
544
+ return false;
545
+ }
546
+
547
+ // Most likely a plain Object
548
+ return true;
549
+ }
550
+ function hasObjectPrototype(o) {
551
+ return Object.prototype.toString.call(o) === '[object Object]';
552
+ }
553
+
505
554
  const componentTypes = ['component', 'errorComponent', 'pendingComponent'];
506
555
  function createRouteMatch(router, route, opts) {
507
- const routeMatch = _extends({}, route, opts, {
508
- router,
556
+ let componentsPromise;
557
+ let dataPromise;
558
+ let latestId = '';
559
+ let resolve = () => {};
560
+ function setLoaderData(loaderData) {
561
+ batch(() => {
562
+ setStore(s => {
563
+ s.routeLoaderData = sharedClone(s.routeLoaderData, loaderData);
564
+ });
565
+ updateLoaderData();
566
+ });
567
+ }
568
+ function updateLoaderData() {
569
+ setStore(s => {
570
+ var _store$parentMatch;
571
+ s.loaderData = sharedClone(s.loaderData, {
572
+ ...((_store$parentMatch = store.parentMatch) == null ? void 0 : _store$parentMatch.store.loaderData),
573
+ ...s.routeLoaderData
574
+ });
575
+ });
576
+ }
577
+ const [store, setStore] = createStore({
509
578
  routeSearch: {},
510
579
  search: {},
511
- childMatches: [],
512
580
  status: 'idle',
513
581
  routeLoaderData: {},
514
582
  loaderData: {},
515
583
  isFetching: false,
516
- isInvalid: false,
584
+ invalid: false,
517
585
  invalidAt: Infinity,
518
- // pendingActions: [],
519
- getIsInvalid: () => {
586
+ get isInvalid() {
520
587
  const now = Date.now();
521
- return routeMatch.isInvalid || routeMatch.invalidAt < now;
522
- },
588
+ return this.invalid || this.invalidAt < now;
589
+ }
590
+ });
591
+ const routeMatch = {
592
+ ...route,
593
+ ...opts,
594
+ store,
595
+ // setStore,
596
+ router,
597
+ childMatches: [],
523
598
  __: {
524
- abortController: new AbortController(),
525
- latestId: '',
526
- resolve: () => {},
527
- notify: () => {
528
- routeMatch.__.resolve();
529
- routeMatch.router.notify();
599
+ setParentMatch: parentMatch => {
600
+ batch(() => {
601
+ setStore(s => {
602
+ s.parentMatch = parentMatch;
603
+ });
604
+ updateLoaderData();
605
+ });
530
606
  },
607
+ abortController: new AbortController(),
531
608
  validate: () => {
532
- var _routeMatch$parentMat, _routeMatch$parentMat2;
609
+ var _store$parentMatch2;
533
610
  // Validate the search params and stabilize them
534
- const parentSearch = (_routeMatch$parentMat = (_routeMatch$parentMat2 = routeMatch.parentMatch) == null ? void 0 : _routeMatch$parentMat2.search) != null ? _routeMatch$parentMat : router.__location.search;
611
+ const parentSearch = ((_store$parentMatch2 = store.parentMatch) == null ? void 0 : _store$parentMatch2.store.search) ?? router.store.currentLocation.search;
535
612
  try {
536
- var _validator;
537
- const prevSearch = routeMatch.routeSearch;
613
+ const prevSearch = store.routeSearch;
538
614
  const validator = typeof routeMatch.options.validateSearch === 'object' ? routeMatch.options.validateSearch.parse : routeMatch.options.validateSearch;
539
- let nextSearch = replaceEqualDeep(prevSearch, (_validator = validator == null ? void 0 : validator(parentSearch)) != null ? _validator : {});
615
+ let nextSearch = sharedClone(prevSearch, (validator == null ? void 0 : validator(parentSearch)) ?? {});
616
+ batch(() => {
617
+ // Invalidate route matches when search param stability changes
618
+ if (prevSearch !== nextSearch) {
619
+ setStore(s => s.invalid = true);
620
+ }
540
621
 
541
- // Invalidate route matches when search param stability changes
542
- if (prevSearch !== nextSearch) {
543
- routeMatch.isInvalid = true;
544
- }
545
- routeMatch.routeSearch = nextSearch;
546
- routeMatch.search = replaceEqualDeep(parentSearch, _extends({}, parentSearch, nextSearch));
622
+ // TODO: Alright, do we need batch() here?
623
+ setStore(s => {
624
+ s.routeSearch = nextSearch;
625
+ s.search = sharedClone(parentSearch, {
626
+ ...parentSearch,
627
+ ...nextSearch
628
+ });
629
+ });
630
+ });
547
631
  componentTypes.map(async type => {
548
632
  const component = routeMatch.options[type];
549
633
  if (typeof routeMatch.__[type] !== 'function') {
@@ -556,8 +640,11 @@ function createRouteMatch(router, route, opts) {
556
640
  cause: err
557
641
  });
558
642
  error.code = 'INVALID_SEARCH_PARAMS';
559
- routeMatch.status = 'error';
560
- routeMatch.error = error;
643
+ setStore(s => {
644
+ s.status = 'error';
645
+ s.error = error;
646
+ });
647
+
561
648
  // Do not proceed with loading the route
562
649
  return;
563
650
  }
@@ -568,7 +655,7 @@ function createRouteMatch(router, route, opts) {
568
655
  (_routeMatch$__$abortC = routeMatch.__.abortController) == null ? void 0 : _routeMatch$__$abortC.abort();
569
656
  },
570
657
  invalidate: () => {
571
- routeMatch.isInvalid = true;
658
+ setStore(s => s.invalid = true);
572
659
  },
573
660
  hasLoaders: () => {
574
661
  return !!(route.options.loader || componentTypes.some(d => {
@@ -583,17 +670,17 @@ function createRouteMatch(router, route, opts) {
583
670
  // If this is a preload, add it to the preload cache
584
671
  if (loaderOpts != null && loaderOpts.preload && minMaxAge > 0) {
585
672
  // If the match is currently active, don't preload it
586
- if (router.state.matches.find(d => d.matchId === routeMatch.matchId)) {
673
+ if (router.store.currentMatches.find(d => d.matchId === routeMatch.matchId)) {
587
674
  return;
588
675
  }
589
- router.matchCache[routeMatch.matchId] = {
676
+ router.store.matchCache[routeMatch.matchId] = {
590
677
  gc: now + loaderOpts.gcMaxAge,
591
678
  match: routeMatch
592
679
  };
593
680
  }
594
681
 
595
682
  // If the match is invalid, errored or idle, trigger it to load
596
- if (routeMatch.status === 'success' && routeMatch.getIsInvalid() || routeMatch.status === 'error' || routeMatch.status === 'idle') {
683
+ if (store.status === 'success' && store.isInvalid || store.status === 'error' || store.status === 'idle') {
597
684
  const maxAge = loaderOpts != null && loaderOpts.preload ? loaderOpts == null ? void 0 : loaderOpts.maxAge : undefined;
598
685
  await routeMatch.fetch({
599
686
  maxAge
@@ -602,29 +689,30 @@ function createRouteMatch(router, route, opts) {
602
689
  },
603
690
  fetch: async opts => {
604
691
  const loadId = '' + Date.now() + Math.random();
605
- routeMatch.__.latestId = loadId;
692
+ latestId = loadId;
606
693
  const checkLatest = async () => {
607
- if (loadId !== routeMatch.__.latestId) {
694
+ if (loadId !== latestId) {
608
695
  // warning(true, 'Data loader is out of date!')
609
696
  return new Promise(() => {});
610
697
  }
611
698
  };
699
+ batch(() => {
700
+ // If the match was in an error state, set it
701
+ // to a loading state again. Otherwise, keep it
702
+ // as loading or resolved
703
+ if (store.status === 'idle') {
704
+ setStore(s => s.status = 'loading');
705
+ }
612
706
 
613
- // If the match was in an error state, set it
614
- // to a loading state again. Otherwise, keep it
615
- // as loading or resolved
616
- if (routeMatch.status === 'idle') {
617
- routeMatch.status = 'loading';
618
- }
619
-
620
- // We started loading the route, so it's no longer invalid
621
- routeMatch.isInvalid = false;
622
- routeMatch.__.loadPromise = new Promise(async resolve => {
707
+ // We started loading the route, so it's no longer invalid
708
+ setStore(s => s.invalid = false);
709
+ });
710
+ routeMatch.__.loadPromise = new Promise(async r => {
623
711
  // We are now fetching, even if it's in the background of a
624
712
  // resolved state
625
- routeMatch.isFetching = true;
626
- routeMatch.__.resolve = resolve;
627
- routeMatch.__.componentsPromise = (async () => {
713
+ setStore(s => s.isFetching = true);
714
+ resolve = r;
715
+ componentsPromise = (async () => {
628
716
  // then run all component and data loaders in parallel
629
717
  // For each component type, potentially load it asynchronously
630
718
 
@@ -636,49 +724,52 @@ function createRouteMatch(router, route, opts) {
636
724
  }
637
725
  }));
638
726
  })();
639
- routeMatch.__.dataPromise = Promise.resolve().then(async () => {
727
+ dataPromise = Promise.resolve().then(async () => {
640
728
  try {
641
- var _ref, _ref2, _opts$maxAge;
642
729
  if (routeMatch.options.loader) {
643
730
  const data = await router.loadMatchData(routeMatch);
644
731
  await checkLatest();
645
- routeMatch.routeLoaderData = replaceEqualDeep(routeMatch.routeLoaderData, data);
732
+ setLoaderData(data);
646
733
  }
647
- routeMatch.error = undefined;
648
- routeMatch.status = 'success';
649
- routeMatch.updatedAt = Date.now();
650
- routeMatch.invalidAt = routeMatch.updatedAt + ((_ref = (_ref2 = (_opts$maxAge = opts == null ? void 0 : opts.maxAge) != null ? _opts$maxAge : routeMatch.options.loaderMaxAge) != null ? _ref2 : router.options.defaultLoaderMaxAge) != null ? _ref : 0);
651
- return routeMatch.routeLoaderData;
734
+ setStore(s => {
735
+ s.error = undefined;
736
+ s.status = 'success';
737
+ s.updatedAt = Date.now();
738
+ s.invalidAt = s.updatedAt + ((opts == null ? void 0 : opts.maxAge) ?? routeMatch.options.loaderMaxAge ?? router.options.defaultLoaderMaxAge ?? 0);
739
+ });
740
+ return store.routeLoaderData;
652
741
  } catch (err) {
653
742
  await checkLatest();
654
743
  if (process.env.NODE_ENV !== 'production') {
655
744
  console.error(err);
656
745
  }
657
- routeMatch.error = err;
658
- routeMatch.status = 'error';
659
- routeMatch.updatedAt = Date.now();
746
+ setStore(s => {
747
+ s.error = err;
748
+ s.status = 'error';
749
+ s.updatedAt = Date.now();
750
+ });
660
751
  throw err;
661
752
  }
662
753
  });
663
754
  const after = async () => {
664
755
  await checkLatest();
665
- routeMatch.isFetching = false;
756
+ setStore(s => s.isFetching = false);
666
757
  delete routeMatch.__.loadPromise;
667
- routeMatch.__.notify();
758
+ resolve();
668
759
  };
669
760
  try {
670
- await Promise.all([routeMatch.__.componentsPromise, routeMatch.__.dataPromise.catch(() => {})]);
761
+ await Promise.all([componentsPromise, dataPromise.catch(() => {})]);
671
762
  after();
672
- } catch (_unused) {
763
+ } catch {
673
764
  after();
674
765
  }
675
766
  });
676
767
  await routeMatch.__.loadPromise;
677
768
  await checkLatest();
678
769
  }
679
- });
770
+ };
680
771
  if (!routeMatch.hasLoaders()) {
681
- routeMatch.status = 'success';
772
+ setStore(s => s.status = 'success');
682
773
  }
683
774
  return routeMatch;
684
775
  }
@@ -708,7 +799,9 @@ function parseSearchWith(parser) {
708
799
  }
709
800
  function stringifySearchWith(stringify) {
710
801
  return search => {
711
- search = _extends({}, search);
802
+ search = {
803
+ ...search
804
+ };
712
805
  if (search) {
713
806
  Object.keys(search).forEach(key => {
714
807
  const val = search[key];
@@ -724,7 +817,7 @@ function stringifySearchWith(stringify) {
724
817
  });
725
818
  }
726
819
  const searchStr = encode(search).toString();
727
- return searchStr ? "?" + searchStr : '';
820
+ return searchStr ? `?${searchStr}` : '';
728
821
  };
729
822
  }
730
823
 
@@ -737,216 +830,296 @@ const createDefaultHistory = () => isServer ? createMemoryHistory() : createBrow
737
830
  function getInitialRouterState() {
738
831
  return {
739
832
  status: 'idle',
740
- location: null,
741
- matches: [],
833
+ latestLocation: null,
834
+ currentLocation: null,
835
+ currentMatches: [],
742
836
  actions: {},
743
837
  loaders: {},
744
838
  lastUpdated: Date.now(),
745
- isFetching: false,
746
- isPreloading: false
839
+ matchCache: {},
840
+ get isFetching() {
841
+ return this.status === 'loading' || this.currentMatches.some(d => d.store.isFetching);
842
+ },
843
+ get isPreloading() {
844
+ return Object.values(this.matchCache).some(d => d.match.store.isFetching && !this.currentMatches.find(dd => dd.matchId === d.match.matchId));
845
+ }
747
846
  };
748
847
  }
749
848
  function createRouter(userOptions) {
750
- var _userOptions$stringif, _userOptions$parseSea;
751
- const history = (userOptions == null ? void 0 : userOptions.history) || createDefaultHistory();
752
- const originalOptions = _extends({
849
+ const originalOptions = {
753
850
  defaultLoaderGcMaxAge: 5 * 60 * 1000,
754
851
  defaultLoaderMaxAge: 0,
755
852
  defaultPreloadMaxAge: 2000,
756
853
  defaultPreloadDelay: 50,
757
- context: undefined
758
- }, userOptions, {
759
- stringifySearch: (_userOptions$stringif = userOptions == null ? void 0 : userOptions.stringifySearch) != null ? _userOptions$stringif : defaultStringifySearch,
760
- parseSearch: (_userOptions$parseSea = userOptions == null ? void 0 : userOptions.parseSearch) != null ? _userOptions$parseSea : defaultParseSearch
761
- });
762
- let router = {
854
+ context: undefined,
855
+ ...userOptions,
856
+ stringifySearch: (userOptions == null ? void 0 : userOptions.stringifySearch) ?? defaultStringifySearch,
857
+ parseSearch: (userOptions == null ? void 0 : userOptions.parseSearch) ?? defaultParseSearch
858
+ };
859
+ const [store, setStore] = createStore(getInitialRouterState());
860
+ let navigationPromise;
861
+ let startedLoadingAt = Date.now();
862
+ let resolveNavigation = () => {};
863
+ function onFocus() {
864
+ router.load();
865
+ }
866
+ function buildRouteTree(rootRouteConfig) {
867
+ const recurseRoutes = (routeConfigs, parent) => {
868
+ return routeConfigs.map((routeConfig, i) => {
869
+ const routeOptions = routeConfig.options;
870
+ const route = createRoute(routeConfig, routeOptions, i, parent, router);
871
+ const existingRoute = router.routesById[route.routeId];
872
+ if (existingRoute) {
873
+ if (process.env.NODE_ENV !== 'production') {
874
+ console.warn(`Duplicate routes found with id: ${String(route.routeId)}`, router.routesById, route);
875
+ }
876
+ throw new Error();
877
+ }
878
+ router.routesById[route.routeId] = route;
879
+ const children = routeConfig.children;
880
+ route.childRoutes = children != null && children.length ? recurseRoutes(children, route) : undefined;
881
+ return route;
882
+ });
883
+ };
884
+ const routes = recurseRoutes([rootRouteConfig]);
885
+ return routes[0];
886
+ }
887
+ function parseLocation(location, previousLocation) {
888
+ const parsedSearch = router.options.parseSearch(location.search);
889
+ return {
890
+ pathname: location.pathname,
891
+ searchStr: location.search,
892
+ search: sharedClone(previousLocation == null ? void 0 : previousLocation.search, parsedSearch),
893
+ hash: location.hash.split('#').reverse()[0] ?? '',
894
+ href: `${location.pathname}${location.search}${location.hash}`,
895
+ state: location.state,
896
+ key: location.key
897
+ };
898
+ }
899
+ function navigate(location) {
900
+ const next = router.buildNext(location);
901
+ return commitLocation(next, location.replace);
902
+ }
903
+ function buildLocation(dest) {
904
+ var _last, _dest$__preSearchFilt, _dest$__preSearchFilt2, _dest$__postSearchFil;
905
+ if (dest === void 0) {
906
+ dest = {};
907
+ }
908
+ const fromPathname = dest.fromCurrent ? store.latestLocation.pathname : dest.from ?? store.latestLocation.pathname;
909
+ let pathname = resolvePath(router.basepath ?? '/', fromPathname, `${dest.to ?? '.'}`);
910
+ const fromMatches = router.matchRoutes(store.latestLocation.pathname, {
911
+ strictParseParams: true
912
+ });
913
+ const toMatches = router.matchRoutes(pathname);
914
+ const prevParams = {
915
+ ...((_last = last(fromMatches)) == null ? void 0 : _last.params)
916
+ };
917
+ let nextParams = (dest.params ?? true) === true ? prevParams : functionalUpdate(dest.params, prevParams);
918
+ if (nextParams) {
919
+ toMatches.map(d => d.options.stringifyParams).filter(Boolean).forEach(fn => {
920
+ Object.assign({}, nextParams, fn(nextParams));
921
+ });
922
+ }
923
+ pathname = interpolatePath(pathname, nextParams ?? {});
924
+
925
+ // Pre filters first
926
+ const preFilteredSearch = (_dest$__preSearchFilt = dest.__preSearchFilters) != null && _dest$__preSearchFilt.length ? dest.__preSearchFilters.reduce((prev, next) => next(prev), store.latestLocation.search) : store.latestLocation.search;
927
+
928
+ // Then the link/navigate function
929
+ const destSearch = dest.search === true ? preFilteredSearch // Preserve resolvedFrom true
930
+ : dest.search ? functionalUpdate(dest.search, preFilteredSearch) ?? {} // Updater
931
+ : (_dest$__preSearchFilt2 = dest.__preSearchFilters) != null && _dest$__preSearchFilt2.length ? preFilteredSearch // Preserve resolvedFrom filters
932
+ : {};
933
+
934
+ // Then post filters
935
+ const postFilteredSearch = (_dest$__postSearchFil = dest.__postSearchFilters) != null && _dest$__postSearchFil.length ? dest.__postSearchFilters.reduce((prev, next) => next(prev), destSearch) : destSearch;
936
+ const search = sharedClone(store.latestLocation.search, postFilteredSearch);
937
+ const searchStr = router.options.stringifySearch(search);
938
+ let hash = dest.hash === true ? store.latestLocation.hash : functionalUpdate(dest.hash, store.latestLocation.hash);
939
+ hash = hash ? `#${hash}` : '';
940
+ return {
941
+ pathname,
942
+ search,
943
+ searchStr,
944
+ state: store.latestLocation.state,
945
+ hash,
946
+ href: `${pathname}${searchStr}${hash}`,
947
+ key: dest.key
948
+ };
949
+ }
950
+ function commitLocation(next, replace) {
951
+ const id = '' + Date.now() + Math.random();
952
+ let nextAction = 'replace';
953
+ if (!replace) {
954
+ nextAction = 'push';
955
+ }
956
+ const isSameUrl = parseLocation(router.history.location).href === next.href;
957
+ if (isSameUrl && !next.key) {
958
+ nextAction = 'replace';
959
+ }
960
+ router.history[nextAction]({
961
+ pathname: next.pathname,
962
+ hash: next.hash,
963
+ search: next.searchStr
964
+ }, {
965
+ id,
966
+ ...next.state
967
+ });
968
+ return navigationPromise = new Promise(resolve => {
969
+ const previousNavigationResolve = resolveNavigation;
970
+ resolveNavigation = () => {
971
+ previousNavigationResolve();
972
+ resolve();
973
+ };
974
+ });
975
+ }
976
+ const router = {
763
977
  types: undefined,
764
978
  // public api
765
- history,
979
+ history: (userOptions == null ? void 0 : userOptions.history) || createDefaultHistory(),
980
+ store,
981
+ setStore,
766
982
  options: originalOptions,
767
- listeners: [],
768
- // Resolved after construction
769
983
  basepath: '',
770
984
  routeTree: undefined,
771
985
  routesById: {},
772
- __location: undefined,
773
- //
774
- resolveNavigation: () => {},
775
- matchCache: {},
776
- state: getInitialRouterState(),
777
986
  reset: () => {
778
- router.state = getInitialRouterState();
779
- router.notify();
780
- },
781
- startedLoadingAt: Date.now(),
782
- subscribe: listener => {
783
- router.listeners.push(listener);
784
- return () => {
785
- router.listeners = router.listeners.filter(x => x !== listener);
786
- };
987
+ setStore(s => Object.assign(s, getInitialRouterState()));
787
988
  },
788
989
  getRoute: id => {
789
990
  return router.routesById[id];
790
991
  },
791
- notify: () => {
792
- const isFetching = router.state.status === 'loading' || router.state.matches.some(d => d.isFetching);
793
- const isPreloading = Object.values(router.matchCache).some(d => d.match.isFetching && !router.state.matches.find(dd => dd.matchId === d.match.matchId));
794
- if (router.state.isFetching !== isFetching || router.state.isPreloading !== isPreloading) {
795
- router.state = _extends({}, router.state, {
796
- isFetching,
797
- isPreloading
798
- });
799
- }
800
- cascadeLoaderData(router.state.matches);
801
- router.listeners.forEach(listener => listener(router));
802
- },
803
992
  dehydrate: () => {
804
993
  return {
805
- location: router.__location,
806
- state: _extends({}, pick(router.state, ['status', 'location', 'lastUpdated', 'location']), {
807
- matches: router.state.matches.map(match => pick(match, ['matchId', 'status', 'routeLoaderData', 'loaderData', 'isInvalid', 'invalidAt']))
808
- }),
994
+ store: {
995
+ ...pick(store, ['latestLocation', 'currentLocation', 'status', 'lastUpdated']),
996
+ currentMatches: store.currentMatches.map(match => ({
997
+ matchId: match.matchId,
998
+ store: pick(match.store, ['status', 'routeLoaderData', 'isInvalid', 'invalidAt'])
999
+ }))
1000
+ },
809
1001
  context: router.options.context
810
1002
  };
811
1003
  },
812
- hydrate: dehydratedState => {
813
- // Update the location
814
- router.__location = dehydratedState.location;
1004
+ hydrate: dehydratedRouter => {
1005
+ setStore(s => {
1006
+ // Update the context TODO: make this part of state?
1007
+ router.options.context = dehydratedRouter.context;
815
1008
 
816
- // Update the context
817
- router.options.context = dehydratedState.context;
818
-
819
- // Match the routes
820
- const matches = router.matchRoutes(router.__location.pathname, {
821
- strictParseParams: true
822
- });
823
- matches.forEach((match, index) => {
824
- const dehydratedMatch = dehydratedState.state.matches[index];
825
- invariant(dehydratedMatch, 'Oh no! Dehydrated route matches did not match the active state of the router 😬');
826
- Object.assign(match, dehydratedMatch);
827
- });
828
- matches.forEach(match => match.__.validate());
829
- router.state = _extends({}, router.state, dehydratedState, {
830
- matches
1009
+ // Match the routes
1010
+ const currentMatches = router.matchRoutes(dehydratedRouter.store.latestLocation.pathname, {
1011
+ strictParseParams: true
1012
+ });
1013
+ currentMatches.forEach((match, index) => {
1014
+ const dehydratedMatch = dehydratedRouter.store.currentMatches[index];
1015
+ invariant(dehydratedMatch && dehydratedMatch.matchId === match.matchId, 'Oh no! There was a hydration mismatch when attempting to restore the state of the router! 😬');
1016
+ Object.assign(match, dehydratedMatch);
1017
+ });
1018
+ currentMatches.forEach(match => match.__.validate());
1019
+ Object.assign(s, {
1020
+ ...dehydratedRouter.store,
1021
+ currentMatches
1022
+ });
831
1023
  });
832
1024
  },
833
1025
  mount: () => {
834
- router.__.buildLocation({
835
- to: '.',
836
- search: true,
837
- hash: true
838
- });
839
-
840
- // If the current location isn't updated, trigger a navigation
841
- // to the current location. Otherwise, load the current location.
842
- // if (next.href !== router.__location.href) {
843
- // router.__.commitLocation(next, true)
844
- // }
845
-
846
- if (!router.state.matches.length) {
847
- router.load();
848
- }
849
- const unsub = router.history.listen(event => {
850
- router.load(router.__.parseLocation(event.location, router.__location));
851
- });
1026
+ // Mount only does anything on the client
1027
+ if (!isServer) {
1028
+ // If the router matches are empty, load the matches
1029
+ if (!store.currentMatches.length) {
1030
+ router.load();
1031
+ }
1032
+ const unsub = router.history.listen(event => {
1033
+ router.load(parseLocation(event.location, store.latestLocation));
1034
+ });
852
1035
 
853
- // addEventListener does not exist in React Native, but window does
854
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
855
- if (!isServer && window.addEventListener) {
856
- // Listen to visibillitychange and focus
857
- window.addEventListener('visibilitychange', router.onFocus, false);
858
- window.addEventListener('focus', router.onFocus, false);
859
- }
860
- return () => {
861
- unsub();
862
- if (!isServer && window.removeEventListener) {
863
- // Be sure to unsubscribe if a new handler is set
864
- window.removeEventListener('visibilitychange', router.onFocus);
865
- window.removeEventListener('focus', router.onFocus);
1036
+ // addEventListener does not exist in React Native, but window does
1037
+ // In the future, we might need to invert control here for more adapters
1038
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
1039
+ if (window.addEventListener) {
1040
+ // Listen to visibilitychange and focus
1041
+ window.addEventListener('visibilitychange', onFocus, false);
1042
+ window.addEventListener('focus', onFocus, false);
866
1043
  }
867
- };
868
- },
869
- onFocus: () => {
870
- router.load();
1044
+ return () => {
1045
+ unsub();
1046
+ if (window.removeEventListener) {
1047
+ // Be sure to unsubscribe if a new handler is set
1048
+ window.removeEventListener('visibilitychange', onFocus);
1049
+ window.removeEventListener('focus', onFocus);
1050
+ }
1051
+ };
1052
+ }
1053
+ return () => {};
871
1054
  },
872
1055
  update: opts => {
873
- var _trimPath;
874
1056
  const newHistory = (opts == null ? void 0 : opts.history) !== router.history;
875
- if (!router.__location || newHistory) {
1057
+ if (!store.latestLocation || newHistory) {
876
1058
  if (opts != null && opts.history) {
877
1059
  router.history = opts.history;
878
1060
  }
879
- router.__location = router.__.parseLocation(router.history.location);
880
- router.state.location = router.__location;
1061
+ setStore(s => {
1062
+ s.latestLocation = parseLocation(router.history.location);
1063
+ s.currentLocation = s.latestLocation;
1064
+ });
881
1065
  }
882
1066
  Object.assign(router.options, opts);
883
1067
  const {
884
1068
  basepath,
885
1069
  routeConfig
886
1070
  } = router.options;
887
- router.basepath = "/" + ((_trimPath = trimPath(basepath != null ? basepath : '')) != null ? _trimPath : '');
1071
+ router.basepath = `/${trimPath(basepath ?? '') ?? ''}`;
888
1072
  if (routeConfig) {
889
1073
  router.routesById = {};
890
- router.routeTree = router.__.buildRouteTree(routeConfig);
1074
+ router.routeTree = buildRouteTree(routeConfig);
891
1075
  }
892
1076
  return router;
893
1077
  },
894
1078
  cancelMatches: () => {
895
- var _router$state$pending, _router$state$pending2;
896
- [...router.state.matches, ...((_router$state$pending = (_router$state$pending2 = router.state.pending) == null ? void 0 : _router$state$pending2.matches) != null ? _router$state$pending : [])].forEach(match => {
1079
+ [...store.currentMatches, ...(store.pendingMatches || [])].forEach(match => {
897
1080
  match.cancel();
898
1081
  });
899
1082
  },
900
1083
  load: async next => {
901
- const id = Math.random();
902
- router.startedLoadingAt = id;
903
- if (next) {
904
- // Ingest the new location
905
- router.__location = next;
906
- }
1084
+ let now = Date.now();
1085
+ const startedAt = now;
1086
+ startedLoadingAt = startedAt;
907
1087
 
908
1088
  // Cancel any pending matches
909
1089
  router.cancelMatches();
1090
+ let matches;
1091
+ batch(() => {
1092
+ if (next) {
1093
+ // Ingest the new location
1094
+ setStore(s => {
1095
+ s.latestLocation = next;
1096
+ });
1097
+ }
910
1098
 
911
- // Match the routes
912
- const matches = router.matchRoutes(router.__location.pathname, {
913
- strictParseParams: true
914
- });
915
- if (typeof document !== 'undefined') {
916
- router.state = _extends({}, router.state, {
917
- pending: {
918
- matches: matches,
919
- location: router.__location
920
- },
921
- status: 'loading'
1099
+ // Match the routes
1100
+ matches = router.matchRoutes(store.latestLocation.pathname, {
1101
+ strictParseParams: true
922
1102
  });
923
- } else {
924
- router.state = _extends({}, router.state, {
925
- matches: matches,
926
- location: router.__location,
927
- status: 'loading'
1103
+ console.log('set loading', matches);
1104
+ setStore(s => {
1105
+ s.status = 'loading';
1106
+ s.pendingMatches = matches;
1107
+ s.pendingLocation = store.latestLocation;
928
1108
  });
929
- }
1109
+ });
930
1110
 
931
- // Check if each match middleware to see if the route can be accessed
1111
+ // Load the matches
932
1112
  try {
933
- await Promise.all(matches.map(match => match.options.beforeLoad == null ? void 0 : match.options.beforeLoad({
934
- router: router,
935
- match
936
- })));
1113
+ await router.loadMatches(matches);
937
1114
  } catch (err) {
938
- console.info(err);
939
- invariant(false, "A route's beforeLoad middleware failed! \uD83D\uDC46");
1115
+ console.log(err);
1116
+ invariant(false, 'Matches failed to load due to error above ☝️. Navigation cancelled!');
940
1117
  }
941
- router.notify();
942
-
943
- // Load the matches
944
- await router.loadMatches(matches);
945
- if (router.startedLoadingAt !== id) {
946
- // Ignore side-effects of match loading
947
- return router.navigationPromise;
1118
+ if (startedLoadingAt !== startedAt) {
1119
+ // Ignore side-effects of outdated side-effects
1120
+ return navigationPromise;
948
1121
  }
949
- const previousMatches = router.state.matches;
1122
+ const previousMatches = store.currentMatches;
950
1123
  const exiting = [],
951
1124
  staying = [];
952
1125
  previousMatches.forEach(d => {
@@ -959,22 +1132,21 @@ function createRouter(userOptions) {
959
1132
  const entering = matches.filter(d => {
960
1133
  return !previousMatches.find(dd => dd.matchId === d.matchId);
961
1134
  });
962
- const now = Date.now();
1135
+ now = Date.now();
963
1136
  exiting.forEach(d => {
964
- var _ref, _d$options$loaderGcMa, _ref2, _d$options$loaderMaxA;
965
1137
  d.__.onExit == null ? void 0 : d.__.onExit({
966
1138
  params: d.params,
967
- search: d.routeSearch
1139
+ search: d.store.routeSearch
968
1140
  });
969
1141
 
970
1142
  // Clear idle error states when match leaves
971
- if (d.status === 'error' && !d.isFetching) {
972
- d.status = 'idle';
973
- d.error = undefined;
1143
+ if (d.store.status === 'error' && !d.store.isFetching) {
1144
+ d.store.status = 'idle';
1145
+ d.store.error = undefined;
974
1146
  }
975
- const gc = Math.max((_ref = (_d$options$loaderGcMa = d.options.loaderGcMaxAge) != null ? _d$options$loaderGcMa : router.options.defaultLoaderGcMaxAge) != null ? _ref : 0, (_ref2 = (_d$options$loaderMaxA = d.options.loaderMaxAge) != null ? _d$options$loaderMaxA : router.options.defaultLoaderMaxAge) != null ? _ref2 : 0);
1147
+ const gc = Math.max(d.options.loaderGcMaxAge ?? router.options.defaultLoaderGcMaxAge ?? 0, d.options.loaderMaxAge ?? router.options.defaultLoaderMaxAge ?? 0);
976
1148
  if (gc > 0) {
977
- router.matchCache[d.matchId] = {
1149
+ store.matchCache[d.matchId] = {
978
1150
  gc: gc == Infinity ? Number.MAX_SAFE_INTEGER : now + gc,
979
1151
  match: d
980
1152
  };
@@ -983,58 +1155,64 @@ function createRouter(userOptions) {
983
1155
  staying.forEach(d => {
984
1156
  d.options.onTransition == null ? void 0 : d.options.onTransition({
985
1157
  params: d.params,
986
- search: d.routeSearch
1158
+ search: d.store.routeSearch
987
1159
  });
988
1160
  });
989
1161
  entering.forEach(d => {
990
1162
  d.__.onExit = d.options.onLoaded == null ? void 0 : d.options.onLoaded({
991
1163
  params: d.params,
992
- search: d.search
1164
+ search: d.store.search
993
1165
  });
994
- delete router.matchCache[d.matchId];
1166
+ delete store.matchCache[d.matchId];
995
1167
  });
996
- if (router.startedLoadingAt !== id) {
1168
+ if (startedLoadingAt !== startedAt) {
997
1169
  // Ignore side-effects of match loading
998
1170
  return;
999
1171
  }
1000
1172
  matches.forEach(match => {
1001
1173
  // Clear actions
1002
1174
  if (match.action) {
1175
+ // TODO: Check reactivity here
1003
1176
  match.action.current = undefined;
1004
1177
  match.action.submissions = [];
1005
1178
  }
1006
1179
  });
1007
- router.state = _extends({}, router.state, {
1008
- location: router.__location,
1009
- matches,
1010
- pending: undefined,
1011
- status: 'idle'
1180
+ setStore(s => {
1181
+ console.log('set', matches);
1182
+ Object.assign(s, {
1183
+ status: 'idle',
1184
+ currentLocation: store.latestLocation,
1185
+ currentMatches: matches,
1186
+ pendingLocation: undefined,
1187
+ pendingMatches: undefined
1188
+ });
1012
1189
  });
1013
- router.notify();
1014
- router.resolveNavigation();
1190
+ resolveNavigation();
1015
1191
  },
1016
1192
  cleanMatchCache: () => {
1017
1193
  const now = Date.now();
1018
- Object.keys(router.matchCache).forEach(matchId => {
1019
- const entry = router.matchCache[matchId];
1194
+ setStore(s => {
1195
+ Object.keys(s.matchCache).forEach(matchId => {
1196
+ const entry = s.matchCache[matchId];
1020
1197
 
1021
- // Don't remove loading matches
1022
- if (entry.match.status === 'loading') {
1023
- return;
1024
- }
1198
+ // Don't remove loading matches
1199
+ if (entry.match.store.status === 'loading') {
1200
+ return;
1201
+ }
1025
1202
 
1026
- // Do not remove successful matches that are still valid
1027
- if (entry.gc > 0 && entry.gc > now) {
1028
- return;
1029
- }
1203
+ // Do not remove successful matches that are still valid
1204
+ if (entry.gc > 0 && entry.gc > now) {
1205
+ return;
1206
+ }
1030
1207
 
1031
- // Everything else gets removed
1032
- delete router.matchCache[matchId];
1208
+ // Everything else gets removed
1209
+ delete s.matchCache[matchId];
1210
+ });
1033
1211
  });
1034
1212
  },
1035
- loadRoute: async function loadRoute(navigateOpts) {
1213
+ loadRoute: async function (navigateOpts) {
1036
1214
  if (navigateOpts === void 0) {
1037
- navigateOpts = router.__location;
1215
+ navigateOpts = store.latestLocation;
1038
1216
  }
1039
1217
  const next = router.buildNext(navigateOpts);
1040
1218
  const matches = router.matchRoutes(next.pathname, {
@@ -1043,10 +1221,9 @@ function createRouter(userOptions) {
1043
1221
  await router.loadMatches(matches);
1044
1222
  return matches;
1045
1223
  },
1046
- preloadRoute: async function preloadRoute(navigateOpts, loaderOpts) {
1047
- var _ref3, _ref4, _loaderOpts$maxAge, _ref5, _ref6, _loaderOpts$gcMaxAge;
1224
+ preloadRoute: async function (navigateOpts, loaderOpts) {
1048
1225
  if (navigateOpts === void 0) {
1049
- navigateOpts = router.__location;
1226
+ navigateOpts = store.latestLocation;
1050
1227
  }
1051
1228
  const next = router.buildNext(navigateOpts);
1052
1229
  const matches = router.matchRoutes(next.pathname, {
@@ -1054,28 +1231,27 @@ function createRouter(userOptions) {
1054
1231
  });
1055
1232
  await router.loadMatches(matches, {
1056
1233
  preload: true,
1057
- maxAge: (_ref3 = (_ref4 = (_loaderOpts$maxAge = loaderOpts.maxAge) != null ? _loaderOpts$maxAge : router.options.defaultPreloadMaxAge) != null ? _ref4 : router.options.defaultLoaderMaxAge) != null ? _ref3 : 0,
1058
- gcMaxAge: (_ref5 = (_ref6 = (_loaderOpts$gcMaxAge = loaderOpts.gcMaxAge) != null ? _loaderOpts$gcMaxAge : router.options.defaultPreloadGcMaxAge) != null ? _ref6 : router.options.defaultLoaderGcMaxAge) != null ? _ref5 : 0
1234
+ maxAge: loaderOpts.maxAge ?? router.options.defaultPreloadMaxAge ?? router.options.defaultLoaderMaxAge ?? 0,
1235
+ gcMaxAge: loaderOpts.gcMaxAge ?? router.options.defaultPreloadGcMaxAge ?? router.options.defaultLoaderGcMaxAge ?? 0
1059
1236
  });
1060
1237
  return matches;
1061
1238
  },
1062
1239
  matchRoutes: (pathname, opts) => {
1063
- var _router$state$pending3, _router$state$pending4;
1064
1240
  router.cleanMatchCache();
1065
1241
  const matches = [];
1066
1242
  if (!router.routeTree) {
1067
1243
  return matches;
1068
1244
  }
1069
- const existingMatches = [...router.state.matches, ...((_router$state$pending3 = (_router$state$pending4 = router.state.pending) == null ? void 0 : _router$state$pending4.matches) != null ? _router$state$pending3 : [])];
1245
+ const existingMatches = [...store.currentMatches, ...(store.pendingMatches ?? [])];
1070
1246
  const recurse = async routes => {
1071
- var _parentMatch$params, _router$options$filte, _foundRoute$childRout;
1247
+ var _foundRoute$childRout;
1072
1248
  const parentMatch = last(matches);
1073
- let params = (_parentMatch$params = parentMatch == null ? void 0 : parentMatch.params) != null ? _parentMatch$params : {};
1074
- const filteredRoutes = (_router$options$filte = router.options.filterRoutes == null ? void 0 : router.options.filterRoutes(routes)) != null ? _router$options$filte : routes;
1249
+ let params = (parentMatch == null ? void 0 : parentMatch.params) ?? {};
1250
+ const filteredRoutes = (router.options.filterRoutes == null ? void 0 : router.options.filterRoutes(routes)) ?? routes;
1075
1251
  let foundRoutes = [];
1076
1252
  const findMatchInRoutes = (parentRoutes, routes) => {
1077
1253
  routes.some(route => {
1078
- var _route$childRoutes, _route$childRoutes2, _route$options$caseSe;
1254
+ var _route$childRoutes, _route$childRoutes2;
1079
1255
  if (!route.routePath && (_route$childRoutes = route.childRoutes) != null && _route$childRoutes.length) {
1080
1256
  return findMatchInRoutes([...foundRoutes, route], route.childRoutes);
1081
1257
  }
@@ -1083,20 +1259,21 @@ function createRouter(userOptions) {
1083
1259
  const matchParams = matchPathname(router.basepath, pathname, {
1084
1260
  to: route.fullPath,
1085
1261
  fuzzy,
1086
- caseSensitive: (_route$options$caseSe = route.options.caseSensitive) != null ? _route$options$caseSe : router.options.caseSensitive
1262
+ caseSensitive: route.options.caseSensitive ?? router.options.caseSensitive
1087
1263
  });
1088
- console.log(router.basepath, route.fullPath, fuzzy, pathname, matchParams);
1089
1264
  if (matchParams) {
1090
1265
  let parsedParams;
1091
1266
  try {
1092
- var _route$options$parseP;
1093
- parsedParams = (_route$options$parseP = route.options.parseParams == null ? void 0 : route.options.parseParams(matchParams)) != null ? _route$options$parseP : matchParams;
1267
+ parsedParams = (route.options.parseParams == null ? void 0 : route.options.parseParams(matchParams)) ?? matchParams;
1094
1268
  } catch (err) {
1095
1269
  if (opts != null && opts.strictParseParams) {
1096
1270
  throw err;
1097
1271
  }
1098
1272
  }
1099
- params = _extends({}, params, parsedParams);
1273
+ params = {
1274
+ ...params,
1275
+ ...parsedParams
1276
+ };
1100
1277
  }
1101
1278
  if (!!matchParams) {
1102
1279
  foundRoutes = [...parentRoutes, route];
@@ -1110,10 +1287,10 @@ function createRouter(userOptions) {
1110
1287
  return;
1111
1288
  }
1112
1289
  foundRoutes.forEach(foundRoute => {
1113
- var _router$matchCache$ma;
1290
+ var _store$matchCache$mat;
1114
1291
  const interpolatedPath = interpolatePath(foundRoute.routePath, params);
1115
1292
  const matchId = interpolatePath(foundRoute.routeId, params, true);
1116
- const match = existingMatches.find(d => d.matchId === matchId) || ((_router$matchCache$ma = router.matchCache[matchId]) == null ? void 0 : _router$matchCache$ma.match) || createRouteMatch(router, foundRoute, {
1293
+ const match = existingMatches.find(d => d.matchId === matchId) || ((_store$matchCache$mat = store.matchCache[matchId]) == null ? void 0 : _store$matchCache$mat.match) || createRouteMatch(router, foundRoute, {
1117
1294
  parentMatch,
1118
1295
  matchId,
1119
1296
  params,
@@ -1127,40 +1304,56 @@ function createRouter(userOptions) {
1127
1304
  }
1128
1305
  };
1129
1306
  recurse([router.routeTree]);
1130
- cascadeLoaderData(matches);
1307
+ linkMatches(matches);
1131
1308
  return matches;
1132
1309
  },
1133
1310
  loadMatches: async (resolvedMatches, loaderOpts) => {
1134
- const matchPromises = resolvedMatches.map(async match => {
1135
- var _search$__data;
1311
+ resolvedMatches.forEach(async match => {
1136
1312
  // Validate the match (loads search params etc)
1137
1313
  match.__.validate();
1138
- const search = match.search;
1314
+ });
1315
+
1316
+ // Check each match middleware to see if the route can be accessed
1317
+ await Promise.all(resolvedMatches.map(async match => {
1318
+ try {
1319
+ await (match.options.beforeLoad == null ? void 0 : match.options.beforeLoad({
1320
+ router: router,
1321
+ match
1322
+ }));
1323
+ } catch (err) {
1324
+ if (!(loaderOpts != null && loaderOpts.preload)) {
1325
+ match.options.onLoadError == null ? void 0 : match.options.onLoadError(err);
1326
+ }
1327
+ throw err;
1328
+ }
1329
+ }));
1330
+ const matchPromises = resolvedMatches.map(async match => {
1331
+ var _search$__data;
1332
+ const search = match.store.search;
1139
1333
  if ((_search$__data = search.__data) != null && _search$__data.matchId && search.__data.matchId !== match.matchId) {
1140
1334
  return;
1141
1335
  }
1142
1336
  match.load(loaderOpts);
1143
- if (match.status !== 'success' && match.__.loadPromise) {
1337
+ if (match.store.status !== 'success' && match.__.loadPromise) {
1144
1338
  // Wait for the first sign of activity from the match
1145
1339
  await match.__.loadPromise;
1146
1340
  }
1147
1341
  });
1148
- router.notify();
1149
1342
  await Promise.all(matchPromises);
1150
1343
  },
1151
1344
  loadMatchData: async routeMatch => {
1152
1345
  if (isServer || !router.options.useServerData) {
1153
- var _await$routeMatch$opt;
1154
- return (_await$routeMatch$opt = await (routeMatch.options.loader == null ? void 0 : routeMatch.options.loader({
1346
+ return (await (routeMatch.options.loader == null ? void 0 : routeMatch.options.loader({
1155
1347
  // parentLoaderPromise: routeMatch.parentMatch?.__.dataPromise,
1156
1348
  params: routeMatch.params,
1157
- search: routeMatch.routeSearch,
1349
+ search: routeMatch.store.routeSearch,
1158
1350
  signal: routeMatch.__.abortController.signal
1159
- }))) != null ? _await$routeMatch$opt : {};
1351
+ }))) || {};
1160
1352
  } else {
1161
1353
  const next = router.buildNext({
1162
1354
  to: '.',
1163
- search: d => _extends({}, d != null ? d : {}, {
1355
+ search: d => ({
1356
+ ...(d ?? {}),
1164
1357
  __data: {
1165
1358
  matchId: routeMatch.matchId
1166
1359
  }
@@ -1189,16 +1382,15 @@ function createRouter(userOptions) {
1189
1382
  }
1190
1383
  },
1191
1384
  invalidateRoute: opts => {
1192
- var _router$state$pending5, _router$state$pending6;
1193
1385
  const next = router.buildNext(opts);
1194
1386
  const unloadedMatchIds = router.matchRoutes(next.pathname).map(d => d.matchId);
1195
- [...router.state.matches, ...((_router$state$pending5 = (_router$state$pending6 = router.state.pending) == null ? void 0 : _router$state$pending6.matches) != null ? _router$state$pending5 : [])].forEach(match => {
1387
+ [...store.currentMatches, ...(store.pendingMatches ?? [])].forEach(match => {
1196
1388
  if (unloadedMatchIds.includes(match.matchId)) {
1197
1389
  match.invalidate();
1198
1390
  }
1199
1391
  });
1200
1392
  },
1201
- reload: () => router.__.navigate({
1393
+ reload: () => navigate({
1202
1394
  fromCurrent: true,
1203
1395
  replace: true,
1204
1396
  search: true
@@ -1207,27 +1399,28 @@ function createRouter(userOptions) {
1207
1399
  return resolvePath(router.basepath, from, cleanPath(path));
1208
1400
  },
1209
1401
  matchRoute: (location, opts) => {
1210
- var _location$from;
1211
1402
  // const location = router.buildNext(opts)
1212
1403
 
1213
- location = _extends({}, location, {
1214
- to: location.to ? router.resolvePath((_location$from = location.from) != null ? _location$from : '', location.to) : undefined
1215
- });
1404
+ location = {
1405
+ ...location,
1406
+ to: location.to ? router.resolvePath(location.from ?? '', location.to) : undefined
1407
+ };
1216
1408
  const next = router.buildNext(location);
1217
1409
  if (opts != null && opts.pending) {
1218
- var _router$state$pending7;
1219
- if (!((_router$state$pending7 = router.state.pending) != null && _router$state$pending7.location)) {
1410
+ if (!store.pendingLocation) {
1220
1411
  return false;
1221
1412
  }
1222
- return !!matchPathname(router.basepath, router.state.pending.location.pathname, _extends({}, opts, {
1413
+ return !!matchPathname(router.basepath, store.pendingLocation.pathname, {
1414
+ ...opts,
1223
1415
  to: next.pathname
1224
- }));
1416
+ });
1225
1417
  }
1226
- return !!matchPathname(router.basepath, router.state.location.pathname, _extends({}, opts, {
1418
+ return matchPathname(router.basepath, store.currentLocation.pathname, {
1419
+ ...opts,
1227
1420
  to: next.pathname
1228
- }));
1421
+ });
1229
1422
  },
1230
- navigate: async _ref7 => {
1423
+ navigate: async _ref => {
1231
1424
  let {
1232
1425
  from,
1233
1426
  to = '.',
@@ -1235,7 +1428,7 @@ function createRouter(userOptions) {
1235
1428
  hash,
1236
1429
  replace,
1237
1430
  params
1238
- } = _ref7;
1431
+ } = _ref;
1239
1432
  // If this link simply reloads the current route,
1240
1433
  // make sure it has a new key so it will trigger a data refresh
1241
1434
 
@@ -1245,11 +1438,11 @@ function createRouter(userOptions) {
1245
1438
  const fromString = String(from);
1246
1439
  let isExternal;
1247
1440
  try {
1248
- new URL("" + toString);
1441
+ new URL(`${toString}`);
1249
1442
  isExternal = true;
1250
1443
  } catch (e) {}
1251
1444
  invariant(!isExternal, 'Attempting to navigate to external url with router.navigate!');
1252
- return router.__.navigate({
1445
+ return navigate({
1253
1446
  from: fromString,
1254
1447
  to: toString,
1255
1448
  search,
@@ -1258,8 +1451,7 @@ function createRouter(userOptions) {
1258
1451
  params
1259
1452
  });
1260
1453
  },
1261
- buildLink: _ref8 => {
1262
- var _preload, _ref9;
1454
+ buildLink: _ref2 => {
1263
1455
  let {
1264
1456
  from,
1265
1457
  to = '.',
@@ -1274,7 +1466,7 @@ function createRouter(userOptions) {
1274
1466
  preloadGcMaxAge: userPreloadGcMaxAge,
1275
1467
  preloadDelay: userPreloadDelay,
1276
1468
  disabled
1277
- } = _ref8;
1469
+ } = _ref2;
1278
1470
  // If this link simply reloads the current route,
1279
1471
  // make sure it has a new key so it will trigger a data refresh
1280
1472
 
@@ -1282,7 +1474,7 @@ function createRouter(userOptions) {
1282
1474
  // null for LinkUtils
1283
1475
 
1284
1476
  try {
1285
- new URL("" + to);
1477
+ new URL(`${to}`);
1286
1478
  return {
1287
1479
  type: 'external',
1288
1480
  href: to
@@ -1297,15 +1489,15 @@ function createRouter(userOptions) {
1297
1489
  replace
1298
1490
  };
1299
1491
  const next = router.buildNext(nextOpts);
1300
- preload = (_preload = preload) != null ? _preload : router.options.defaultPreload;
1301
- const preloadDelay = (_ref9 = userPreloadDelay != null ? userPreloadDelay : router.options.defaultPreloadDelay) != null ? _ref9 : 0;
1492
+ preload = preload ?? router.options.defaultPreload;
1493
+ const preloadDelay = userPreloadDelay ?? router.options.defaultPreloadDelay ?? 0;
1302
1494
 
1303
1495
  // Compare path/hash for matches
1304
- const pathIsEqual = router.state.location.pathname === next.pathname;
1305
- const currentPathSplit = router.state.location.pathname.split('/');
1496
+ const pathIsEqual = store.currentLocation.pathname === next.pathname;
1497
+ const currentPathSplit = store.currentLocation.pathname.split('/');
1306
1498
  const nextPathSplit = next.pathname.split('/');
1307
1499
  const pathIsFuzzyEqual = nextPathSplit.every((d, i) => d === currentPathSplit[i]);
1308
- const hashIsEqual = router.state.location.hash === next.hash;
1500
+ const hashIsEqual = store.currentLocation.hash === next.hash;
1309
1501
  // Combine the matches based on user options
1310
1502
  const pathTest = activeOptions != null && activeOptions.exact ? pathIsEqual : pathIsFuzzyEqual;
1311
1503
  const hashTest = activeOptions != null && activeOptions.includeHash ? hashIsEqual : true;
@@ -1321,8 +1513,8 @@ function createRouter(userOptions) {
1321
1513
  router.invalidateRoute(nextOpts);
1322
1514
  }
1323
1515
 
1324
- // All is well? Navigate!)
1325
- router.__.navigate(nextOpts);
1516
+ // All is well? Navigate!
1517
+ navigate(nextOpts);
1326
1518
  }
1327
1519
  };
1328
1520
 
@@ -1332,6 +1524,9 @@ function createRouter(userOptions) {
1332
1524
  router.preloadRoute(nextOpts, {
1333
1525
  maxAge: userPreloadMaxAge,
1334
1526
  gcMaxAge: userPreloadGcMaxAge
1527
+ }).catch(err => {
1528
+ console.log(err);
1529
+ console.warn('Error preloading route! ☝️');
1335
1530
  });
1336
1531
  }
1337
1532
  };
@@ -1346,6 +1541,9 @@ function createRouter(userOptions) {
1346
1541
  router.preloadRoute(nextOpts, {
1347
1542
  maxAge: userPreloadMaxAge,
1348
1543
  gcMaxAge: userPreloadGcMaxAge
1544
+ }).catch(err => {
1545
+ console.log(err);
1546
+ console.warn('Error preloading route! ☝️');
1349
1547
  });
1350
1548
  }, preloadDelay);
1351
1549
  }
@@ -1369,143 +1567,15 @@ function createRouter(userOptions) {
1369
1567
  };
1370
1568
  },
1371
1569
  buildNext: opts => {
1372
- const next = router.__.buildLocation(opts);
1570
+ const next = buildLocation(opts);
1373
1571
  const matches = router.matchRoutes(next.pathname);
1374
- const __preSearchFilters = matches.map(match => {
1375
- var _match$options$preSea;
1376
- return (_match$options$preSea = match.options.preSearchFilters) != null ? _match$options$preSea : [];
1377
- }).flat().filter(Boolean);
1378
- const __postSearchFilters = matches.map(match => {
1379
- var _match$options$postSe;
1380
- return (_match$options$postSe = match.options.postSearchFilters) != null ? _match$options$postSe : [];
1381
- }).flat().filter(Boolean);
1382
- return router.__.buildLocation(_extends({}, opts, {
1572
+ const __preSearchFilters = matches.map(match => match.options.preSearchFilters ?? []).flat().filter(Boolean);
1573
+ const __postSearchFilters = matches.map(match => match.options.postSearchFilters ?? []).flat().filter(Boolean);
1574
+ return buildLocation({
1575
+ ...opts,
1383
1576
  __preSearchFilters,
1384
1577
  __postSearchFilters
1385
- }));
1386
- },
1387
- __: {
1388
- buildRouteTree: rootRouteConfig => {
1389
- const recurseRoutes = (routeConfigs, parent) => {
1390
- return routeConfigs.map(routeConfig => {
1391
- const routeOptions = routeConfig.options;
1392
- const route = createRoute(routeConfig, routeOptions, parent, router);
1393
- const existingRoute = router.routesById[route.routeId];
1394
- if (existingRoute) {
1395
- if (process.env.NODE_ENV !== 'production') {
1396
- console.warn("Duplicate routes found with id: " + String(route.routeId), router.routesById, route);
1397
- }
1398
- throw new Error();
1399
- }
1400
- router.routesById[route.routeId] = route;
1401
- const children = routeConfig.children;
1402
- route.childRoutes = children != null && children.length ? recurseRoutes(children, route) : undefined;
1403
- return route;
1404
- });
1405
- };
1406
- const routes = recurseRoutes([rootRouteConfig]);
1407
- return routes[0];
1408
- },
1409
- parseLocation: (location, previousLocation) => {
1410
- var _location$hash$split$;
1411
- const parsedSearch = router.options.parseSearch(location.search);
1412
- return {
1413
- pathname: location.pathname,
1414
- searchStr: location.search,
1415
- search: replaceEqualDeep(previousLocation == null ? void 0 : previousLocation.search, parsedSearch),
1416
- hash: (_location$hash$split$ = location.hash.split('#').reverse()[0]) != null ? _location$hash$split$ : '',
1417
- href: "" + location.pathname + location.search + location.hash,
1418
- state: location.state,
1419
- key: location.key
1420
- };
1421
- },
1422
- navigate: location => {
1423
- const next = router.buildNext(location);
1424
- return router.__.commitLocation(next, location.replace);
1425
- },
1426
- buildLocation: function buildLocation(dest) {
1427
- var _dest$from, _router$basepath, _dest$to, _last, _dest$params, _dest$__preSearchFilt, _functionalUpdate, _dest$__preSearchFilt2, _dest$__postSearchFil;
1428
- if (dest === void 0) {
1429
- dest = {};
1430
- }
1431
- const fromPathname = dest.fromCurrent ? router.__location.pathname : (_dest$from = dest.from) != null ? _dest$from : router.__location.pathname;
1432
- let pathname = resolvePath((_router$basepath = router.basepath) != null ? _router$basepath : '/', fromPathname, "" + ((_dest$to = dest.to) != null ? _dest$to : '.'));
1433
- const fromMatches = router.matchRoutes(router.__location.pathname, {
1434
- strictParseParams: true
1435
- });
1436
- const toMatches = router.matchRoutes(pathname);
1437
- const prevParams = _extends({}, (_last = last(fromMatches)) == null ? void 0 : _last.params);
1438
- let nextParams = ((_dest$params = dest.params) != null ? _dest$params : true) === true ? prevParams : functionalUpdate(dest.params, prevParams);
1439
- if (nextParams) {
1440
- toMatches.map(d => d.options.stringifyParams).filter(Boolean).forEach(fn => {
1441
- Object.assign({}, nextParams, fn(nextParams));
1442
- });
1443
- }
1444
- pathname = interpolatePath(pathname, nextParams != null ? nextParams : {});
1445
-
1446
- // Pre filters first
1447
- const preFilteredSearch = (_dest$__preSearchFilt = dest.__preSearchFilters) != null && _dest$__preSearchFilt.length ? dest.__preSearchFilters.reduce((prev, next) => next(prev), router.__location.search) : router.__location.search;
1448
-
1449
- // Then the link/navigate function
1450
- const destSearch = dest.search === true ? preFilteredSearch // Preserve resolvedFrom true
1451
- : dest.search ? (_functionalUpdate = functionalUpdate(dest.search, preFilteredSearch)) != null ? _functionalUpdate : {} // Updater
1452
- : (_dest$__preSearchFilt2 = dest.__preSearchFilters) != null && _dest$__preSearchFilt2.length ? preFilteredSearch // Preserve resolvedFrom filters
1453
- : {};
1454
-
1455
- // Then post filters
1456
- const postFilteredSearch = (_dest$__postSearchFil = dest.__postSearchFilters) != null && _dest$__postSearchFil.length ? dest.__postSearchFilters.reduce((prev, next) => next(prev), destSearch) : destSearch;
1457
- const search = replaceEqualDeep(router.__location.search, postFilteredSearch);
1458
- const searchStr = router.options.stringifySearch(search);
1459
- let hash = dest.hash === true ? router.__location.hash : functionalUpdate(dest.hash, router.__location.hash);
1460
- hash = hash ? "#" + hash : '';
1461
- return {
1462
- pathname,
1463
- search,
1464
- searchStr,
1465
- state: router.__location.state,
1466
- hash,
1467
- href: "" + pathname + searchStr + hash,
1468
- key: dest.key
1469
- };
1470
- },
1471
- commitLocation: (next, replace) => {
1472
- const id = '' + Date.now() + Math.random();
1473
- if (router.navigateTimeout) clearTimeout(router.navigateTimeout);
1474
- let nextAction = 'replace';
1475
- if (!replace) {
1476
- nextAction = 'push';
1477
- }
1478
- const isSameUrl = router.__.parseLocation(history.location).href === next.href;
1479
- if (isSameUrl && !next.key) {
1480
- nextAction = 'replace';
1481
- }
1482
- if (nextAction === 'replace') {
1483
- history.replace({
1484
- pathname: next.pathname,
1485
- hash: next.hash,
1486
- search: next.searchStr
1487
- }, _extends({
1488
- id
1489
- }, next.state));
1490
- } else {
1491
- history.push({
1492
- pathname: next.pathname,
1493
- hash: next.hash,
1494
- search: next.searchStr
1495
- }, {
1496
- id
1497
- });
1498
- }
1499
- router.navigationPromise = new Promise(resolve => {
1500
- const previousNavigationResolve = router.resolveNavigation;
1501
- router.resolveNavigation = () => {
1502
- previousNavigationResolve();
1503
- resolve();
1504
- delete router.navigationPromise;
1505
- };
1506
- });
1507
- return router.navigationPromise;
1508
- }
1578
+ });
1509
1579
  }
1510
1580
  };
1511
1581
  router.update(userOptions);
@@ -1517,14 +1587,16 @@ function createRouter(userOptions) {
1517
1587
  function isCtrlEvent(e) {
1518
1588
  return !!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey);
1519
1589
  }
1520
- function cascadeLoaderData(matches) {
1590
+ function linkMatches(matches) {
1521
1591
  matches.forEach((match, index) => {
1522
1592
  const parent = matches[index - 1];
1523
1593
  if (parent) {
1524
- match.loaderData = replaceEqualDeep(match.loaderData, _extends({}, parent.loaderData, match.routeLoaderData));
1594
+ match.__.setParentMatch(parent);
1595
+ } else {
1596
+ match.__.setParentMatch(undefined);
1525
1597
  }
1526
1598
  });
1527
1599
  }
1528
1600
 
1529
- export { cleanPath, createRoute, createRouteConfig, createRouteMatch, createRouter, decode, defaultParseSearch, defaultStringifySearch, encode, functionalUpdate, interpolatePath, joinPaths, last, matchByPath, matchPathname, parsePathname, parseSearchWith, pick, replaceEqualDeep, resolvePath, rootRouteId, stringifySearchWith, trimPath, trimPathLeft, trimPathRight, warning };
1601
+ export { cleanPath, createRoute, createRouteConfig, createRouteMatch, createRouter, decode, defaultParseSearch, defaultStringifySearch, encode, functionalUpdate, interpolatePath, joinPaths, last, matchByPath, matchPathname, parsePathname, parseSearchWith, pick, resolvePath, rootRouteId, sharedClone, stringifySearchWith, trimPath, trimPathLeft, trimPathRight, warning };
1530
1602
  //# sourceMappingURL=index.js.map