@tanstack/react-router 0.0.1-beta.61 → 0.0.1-beta.63
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/cjs/index.js +37 -29
- package/build/cjs/index.js.map +1 -1
- package/build/esm/index.js +37 -29
- package/build/esm/index.js.map +1 -1
- package/build/stats-html.html +1 -1
- package/build/stats-react.json +52 -52
- package/build/types/index.d.ts +16 -13
- package/build/umd/index.development.js +210 -167
- package/build/umd/index.development.js.map +1 -1
- package/build/umd/index.production.js +3 -3
- package/build/umd/index.production.js.map +1 -1
- package/package.json +3 -3
- package/src/index.tsx +74 -54
|
@@ -89,9 +89,9 @@
|
|
|
89
89
|
const previous = this.state;
|
|
90
90
|
this.state = this.options?.updateFn ? this.options.updateFn(previous)(updater) : updater(previous);
|
|
91
91
|
if (this.state === previous) return;
|
|
92
|
+
this.options?.onUpdate?.(this.state, previous);
|
|
92
93
|
this.queue.push(() => {
|
|
93
94
|
this.listeners.forEach(listener => listener(this.state, previous));
|
|
94
|
-
this.options?.onUpdate?.(this.state, previous);
|
|
95
95
|
});
|
|
96
96
|
this.#flush();
|
|
97
97
|
};
|
|
@@ -345,6 +345,21 @@
|
|
|
345
345
|
function hasObjectPrototype(o) {
|
|
346
346
|
return Object.prototype.toString.call(o) === '[object Object]';
|
|
347
347
|
}
|
|
348
|
+
function partialDeepEqual(a, b) {
|
|
349
|
+
if (a === b) {
|
|
350
|
+
return true;
|
|
351
|
+
}
|
|
352
|
+
if (typeof a !== typeof b) {
|
|
353
|
+
return false;
|
|
354
|
+
}
|
|
355
|
+
if (isPlainObject(a) && isPlainObject(b)) {
|
|
356
|
+
return !Object.keys(b).some(key => !partialDeepEqual(a[key], b[key]));
|
|
357
|
+
}
|
|
358
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
359
|
+
return a.length === b.length && a.every((item, index) => partialDeepEqual(item, b[index]));
|
|
360
|
+
}
|
|
361
|
+
return false;
|
|
362
|
+
}
|
|
348
363
|
|
|
349
364
|
function joinPaths(paths) {
|
|
350
365
|
return cleanPath(paths.filter(Boolean).join('/'));
|
|
@@ -577,7 +592,9 @@
|
|
|
577
592
|
this.options = options || {};
|
|
578
593
|
this.isRoot = !options?.getParentRoute;
|
|
579
594
|
}
|
|
580
|
-
init =
|
|
595
|
+
init = opts => {
|
|
596
|
+
this.originalIndex = opts.originalIndex;
|
|
597
|
+
this.router = opts.router;
|
|
581
598
|
const allOptions = this.options;
|
|
582
599
|
const isRoot = !allOptions?.path && !allOptions?.id;
|
|
583
600
|
const parent = this.options?.getParentRoute?.();
|
|
@@ -612,19 +629,17 @@
|
|
|
612
629
|
this.children = children;
|
|
613
630
|
return this;
|
|
614
631
|
};
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
// false,
|
|
619
|
-
// `routeConfig.generate() is used by TanStack Router's file-based routing code generation and should not actually be called during runtime. `,
|
|
620
|
-
// )
|
|
621
|
-
// },
|
|
632
|
+
generate = options => {
|
|
633
|
+
invariant(false, `route.generate() is used by TanStack Router's file-based routing code generation and should not actually be called during runtime. `);
|
|
634
|
+
};
|
|
622
635
|
}
|
|
623
|
-
|
|
624
636
|
class RootRoute extends Route {
|
|
625
637
|
constructor(options) {
|
|
626
638
|
super(options);
|
|
627
639
|
}
|
|
640
|
+
static withRouterContext = () => {
|
|
641
|
+
return options => new RootRoute(options);
|
|
642
|
+
};
|
|
628
643
|
}
|
|
629
644
|
|
|
630
645
|
// const rootRoute = new RootRoute({
|
|
@@ -689,8 +704,13 @@
|
|
|
689
704
|
routeSearch: {},
|
|
690
705
|
search: {},
|
|
691
706
|
status: 'idle'
|
|
707
|
+
}, {
|
|
708
|
+
onUpdate: next => {
|
|
709
|
+
this.state = next;
|
|
710
|
+
}
|
|
692
711
|
})
|
|
693
712
|
});
|
|
713
|
+
this.state = this.store.state;
|
|
694
714
|
if (!this.#hasLoaders()) {
|
|
695
715
|
this.store.setState(s => ({
|
|
696
716
|
...s,
|
|
@@ -698,12 +718,66 @@
|
|
|
698
718
|
}));
|
|
699
719
|
}
|
|
700
720
|
}
|
|
721
|
+
#hasLoaders = () => {
|
|
722
|
+
return !!(this.route.options.onLoad || componentTypes.some(d => this.route.options[d]?.preload));
|
|
723
|
+
};
|
|
724
|
+
__init = opts => {
|
|
725
|
+
// Validate the search params and stabilize them
|
|
726
|
+
this.parentMatch = opts.parentMatch;
|
|
727
|
+
const parentSearch = this.parentMatch?.state.search ?? this.router.state.latestLocation.search;
|
|
728
|
+
try {
|
|
729
|
+
const validator = typeof this.route.options.validateSearch === 'object' ? this.route.options.validateSearch.parse : this.route.options.validateSearch;
|
|
730
|
+
let nextSearch = validator?.(parentSearch) ?? {};
|
|
731
|
+
this.store.setState(s => ({
|
|
732
|
+
...s,
|
|
733
|
+
routeSearch: nextSearch,
|
|
734
|
+
search: {
|
|
735
|
+
...parentSearch,
|
|
736
|
+
...nextSearch
|
|
737
|
+
}
|
|
738
|
+
}));
|
|
739
|
+
componentTypes.map(async type => {
|
|
740
|
+
const component = this.route.options[type];
|
|
741
|
+
if (typeof this[type] !== 'function') {
|
|
742
|
+
this[type] = component;
|
|
743
|
+
}
|
|
744
|
+
});
|
|
745
|
+
const parent = this.parentMatch;
|
|
746
|
+
this.routeContext = this.route.options.getContext?.({
|
|
747
|
+
parentContext: parent?.routeContext,
|
|
748
|
+
context: parent?.context,
|
|
749
|
+
params: this.params,
|
|
750
|
+
search: this.state.search
|
|
751
|
+
}) || {};
|
|
752
|
+
this.context = parent ? {
|
|
753
|
+
...parent.context,
|
|
754
|
+
...this.routeContext
|
|
755
|
+
} : {
|
|
756
|
+
...this.router?.options.context,
|
|
757
|
+
...this.routeContext
|
|
758
|
+
};
|
|
759
|
+
} catch (err) {
|
|
760
|
+
console.error(err);
|
|
761
|
+
const error = new Error('Invalid search params found', {
|
|
762
|
+
cause: err
|
|
763
|
+
});
|
|
764
|
+
error.code = 'INVALID_SEARCH_PARAMS';
|
|
765
|
+
this.store.setState(s => ({
|
|
766
|
+
...s,
|
|
767
|
+
status: 'error',
|
|
768
|
+
error: error
|
|
769
|
+
}));
|
|
770
|
+
|
|
771
|
+
// Do not proceed with loading the route
|
|
772
|
+
return;
|
|
773
|
+
}
|
|
774
|
+
};
|
|
701
775
|
cancel = () => {
|
|
702
776
|
this.abortController?.abort();
|
|
703
777
|
};
|
|
704
778
|
load = async opts => {
|
|
705
779
|
// If the match is invalid, errored or idle, trigger it to load
|
|
706
|
-
if (this.
|
|
780
|
+
if (this.state.status !== 'pending') {
|
|
707
781
|
await this.fetch(opts);
|
|
708
782
|
}
|
|
709
783
|
};
|
|
@@ -720,7 +794,7 @@
|
|
|
720
794
|
// If the match was in an error state, set it
|
|
721
795
|
// to a loading state again. Otherwise, keep it
|
|
722
796
|
// as loading or resolved
|
|
723
|
-
if (this.
|
|
797
|
+
if (this.state.status === 'idle') {
|
|
724
798
|
this.store.setState(s => ({
|
|
725
799
|
...s,
|
|
726
800
|
status: 'pending'
|
|
@@ -742,9 +816,11 @@
|
|
|
742
816
|
if (this.route.options.onLoad) {
|
|
743
817
|
return this.route.options.onLoad({
|
|
744
818
|
params: this.params,
|
|
745
|
-
search: this.
|
|
819
|
+
search: this.state.search,
|
|
746
820
|
signal: this.abortController.signal,
|
|
747
|
-
preload: !!opts?.preload
|
|
821
|
+
preload: !!opts?.preload,
|
|
822
|
+
routeContext: this.routeContext,
|
|
823
|
+
context: this.context
|
|
748
824
|
});
|
|
749
825
|
}
|
|
750
826
|
return;
|
|
@@ -772,50 +848,6 @@
|
|
|
772
848
|
});
|
|
773
849
|
return this.__loadPromise;
|
|
774
850
|
};
|
|
775
|
-
#hasLoaders = () => {
|
|
776
|
-
return !!(this.route.options.onLoad || componentTypes.some(d => this.route.options[d]?.preload));
|
|
777
|
-
};
|
|
778
|
-
__setParentMatch = parentMatch => {
|
|
779
|
-
if (!this.parentMatch && parentMatch) {
|
|
780
|
-
this.parentMatch = parentMatch;
|
|
781
|
-
}
|
|
782
|
-
};
|
|
783
|
-
__validate = () => {
|
|
784
|
-
// Validate the search params and stabilize them
|
|
785
|
-
const parentSearch = this.parentMatch?.store.state.search ?? this.router.store.state.latestLocation.search;
|
|
786
|
-
try {
|
|
787
|
-
const validator = typeof this.route.options.validateSearch === 'object' ? this.route.options.validateSearch.parse : this.route.options.validateSearch;
|
|
788
|
-
let nextSearch = validator?.(parentSearch) ?? {};
|
|
789
|
-
this.store.setState(s => ({
|
|
790
|
-
...s,
|
|
791
|
-
routeSearch: nextSearch,
|
|
792
|
-
search: {
|
|
793
|
-
...parentSearch,
|
|
794
|
-
...nextSearch
|
|
795
|
-
}
|
|
796
|
-
}));
|
|
797
|
-
componentTypes.map(async type => {
|
|
798
|
-
const component = this.route.options[type];
|
|
799
|
-
if (typeof this[type] !== 'function') {
|
|
800
|
-
this[type] = component;
|
|
801
|
-
}
|
|
802
|
-
});
|
|
803
|
-
} catch (err) {
|
|
804
|
-
console.error(err);
|
|
805
|
-
const error = new Error('Invalid search params found', {
|
|
806
|
-
cause: err
|
|
807
|
-
});
|
|
808
|
-
error.code = 'INVALID_SEARCH_PARAMS';
|
|
809
|
-
this.store.setState(s => ({
|
|
810
|
-
...s,
|
|
811
|
-
status: 'error',
|
|
812
|
-
error: error
|
|
813
|
-
}));
|
|
814
|
-
|
|
815
|
-
// Do not proceed with loading the route
|
|
816
|
-
return;
|
|
817
|
-
}
|
|
818
|
-
};
|
|
819
851
|
}
|
|
820
852
|
|
|
821
853
|
const defaultParseSearch = parseSearchWith(JSON.parse);
|
|
@@ -900,7 +932,12 @@
|
|
|
900
932
|
parseSearch: options?.parseSearch ?? defaultParseSearch,
|
|
901
933
|
fetchServerDataFn: options?.fetchServerDataFn ?? defaultFetchServerDataFn
|
|
902
934
|
};
|
|
903
|
-
this.store = new Store(getInitialRouterState()
|
|
935
|
+
this.store = new Store(getInitialRouterState(), {
|
|
936
|
+
onUpdate: state => {
|
|
937
|
+
this.state = state;
|
|
938
|
+
}
|
|
939
|
+
});
|
|
940
|
+
this.state = this.store.state;
|
|
904
941
|
this.basepath = '';
|
|
905
942
|
this.update(options);
|
|
906
943
|
|
|
@@ -914,7 +951,7 @@
|
|
|
914
951
|
// Mount only does anything on the client
|
|
915
952
|
if (!isServer) {
|
|
916
953
|
// If the router matches are empty, load the matches
|
|
917
|
-
if (!this.
|
|
954
|
+
if (!this.state.currentMatches.length) {
|
|
918
955
|
this.load();
|
|
919
956
|
}
|
|
920
957
|
const visibilityChangeEvent = 'visibilitychange';
|
|
@@ -953,7 +990,9 @@
|
|
|
953
990
|
currentLocation: parsedLocation
|
|
954
991
|
}));
|
|
955
992
|
this.#unsubHistory = this.history.listen(() => {
|
|
956
|
-
this.load(
|
|
993
|
+
this.load({
|
|
994
|
+
next: this.#parseLocation(this.state.latestLocation)
|
|
995
|
+
});
|
|
957
996
|
});
|
|
958
997
|
}
|
|
959
998
|
const {
|
|
@@ -979,11 +1018,11 @@
|
|
|
979
1018
|
});
|
|
980
1019
|
};
|
|
981
1020
|
cancelMatches = () => {
|
|
982
|
-
[...this.
|
|
1021
|
+
[...this.state.currentMatches, ...(this.state.pendingMatches || [])].forEach(match => {
|
|
983
1022
|
match.cancel();
|
|
984
1023
|
});
|
|
985
1024
|
};
|
|
986
|
-
load = async
|
|
1025
|
+
load = async opts => {
|
|
987
1026
|
let now = Date.now();
|
|
988
1027
|
const startedAt = now;
|
|
989
1028
|
this.startedLoadingAt = startedAt;
|
|
@@ -992,29 +1031,31 @@
|
|
|
992
1031
|
this.cancelMatches();
|
|
993
1032
|
let matches;
|
|
994
1033
|
this.store.batch(() => {
|
|
995
|
-
if (next) {
|
|
1034
|
+
if (opts?.next) {
|
|
996
1035
|
// Ingest the new location
|
|
997
1036
|
this.store.setState(s => ({
|
|
998
1037
|
...s,
|
|
999
|
-
latestLocation: next
|
|
1038
|
+
latestLocation: opts.next
|
|
1000
1039
|
}));
|
|
1001
1040
|
}
|
|
1002
1041
|
|
|
1003
1042
|
// Match the routes
|
|
1004
|
-
matches = this.matchRoutes(this.
|
|
1043
|
+
matches = this.matchRoutes(this.state.latestLocation.pathname, {
|
|
1005
1044
|
strictParseParams: true
|
|
1006
1045
|
});
|
|
1007
1046
|
this.store.setState(s => ({
|
|
1008
1047
|
...s,
|
|
1009
1048
|
status: 'pending',
|
|
1010
1049
|
pendingMatches: matches,
|
|
1011
|
-
pendingLocation: this.
|
|
1050
|
+
pendingLocation: this.state.latestLocation
|
|
1012
1051
|
}));
|
|
1013
1052
|
});
|
|
1014
1053
|
|
|
1015
1054
|
// Load the matches
|
|
1016
1055
|
try {
|
|
1017
|
-
await this.loadMatches(matches
|
|
1056
|
+
await this.loadMatches(matches
|
|
1057
|
+
// opts
|
|
1058
|
+
);
|
|
1018
1059
|
} catch (err) {
|
|
1019
1060
|
console.warn(err);
|
|
1020
1061
|
invariant(false, 'Matches failed to load due to error above ☝️. Navigation cancelled!');
|
|
@@ -1023,7 +1064,7 @@
|
|
|
1023
1064
|
// Ignore side-effects of outdated side-effects
|
|
1024
1065
|
return this.navigationPromise;
|
|
1025
1066
|
}
|
|
1026
|
-
const previousMatches = this.
|
|
1067
|
+
const previousMatches = this.state.currentMatches;
|
|
1027
1068
|
const exiting = [],
|
|
1028
1069
|
staying = [];
|
|
1029
1070
|
previousMatches.forEach(d => {
|
|
@@ -1040,11 +1081,11 @@
|
|
|
1040
1081
|
exiting.forEach(d => {
|
|
1041
1082
|
d.__onExit?.({
|
|
1042
1083
|
params: d.params,
|
|
1043
|
-
search: d.
|
|
1084
|
+
search: d.state.routeSearch
|
|
1044
1085
|
});
|
|
1045
1086
|
|
|
1046
1087
|
// Clear non-loading error states when match leaves
|
|
1047
|
-
if (d.
|
|
1088
|
+
if (d.state.status === 'error') {
|
|
1048
1089
|
this.store.setState(s => ({
|
|
1049
1090
|
...s,
|
|
1050
1091
|
status: 'idle',
|
|
@@ -1055,21 +1096,19 @@
|
|
|
1055
1096
|
staying.forEach(d => {
|
|
1056
1097
|
d.route.options.onTransition?.({
|
|
1057
1098
|
params: d.params,
|
|
1058
|
-
search: d.
|
|
1099
|
+
search: d.state.routeSearch
|
|
1059
1100
|
});
|
|
1060
1101
|
});
|
|
1061
1102
|
entering.forEach(d => {
|
|
1062
1103
|
d.__onExit = d.route.options.onLoaded?.({
|
|
1063
1104
|
params: d.params,
|
|
1064
|
-
search: d.
|
|
1105
|
+
search: d.state.search
|
|
1065
1106
|
});
|
|
1066
|
-
// delete this.store.state.matchCache[d.id] // TODO:
|
|
1067
1107
|
});
|
|
1068
|
-
|
|
1069
1108
|
this.store.setState(s => ({
|
|
1070
1109
|
...s,
|
|
1071
1110
|
status: 'idle',
|
|
1072
|
-
currentLocation: this.
|
|
1111
|
+
currentLocation: this.state.latestLocation,
|
|
1073
1112
|
currentMatches: matches,
|
|
1074
1113
|
pendingLocation: undefined,
|
|
1075
1114
|
pendingMatches: undefined
|
|
@@ -1082,7 +1121,7 @@
|
|
|
1082
1121
|
invariant(route, `Route with id "${id}" not found`);
|
|
1083
1122
|
return route;
|
|
1084
1123
|
};
|
|
1085
|
-
loadRoute = async (navigateOpts = this.
|
|
1124
|
+
loadRoute = async (navigateOpts = this.state.latestLocation) => {
|
|
1086
1125
|
const next = this.buildNext(navigateOpts);
|
|
1087
1126
|
const matches = this.matchRoutes(next.pathname, {
|
|
1088
1127
|
strictParseParams: true
|
|
@@ -1090,7 +1129,7 @@
|
|
|
1090
1129
|
await this.loadMatches(matches);
|
|
1091
1130
|
return matches;
|
|
1092
1131
|
};
|
|
1093
|
-
preloadRoute = async (navigateOpts = this.
|
|
1132
|
+
preloadRoute = async (navigateOpts = this.state.latestLocation) => {
|
|
1094
1133
|
const next = this.buildNext(navigateOpts);
|
|
1095
1134
|
const matches = this.matchRoutes(next.pathname, {
|
|
1096
1135
|
strictParseParams: true
|
|
@@ -1105,7 +1144,7 @@
|
|
|
1105
1144
|
if (!this.routeTree) {
|
|
1106
1145
|
return matches;
|
|
1107
1146
|
}
|
|
1108
|
-
const existingMatches = [...this.
|
|
1147
|
+
const existingMatches = [...this.state.currentMatches, ...(this.state.pendingMatches ?? [])];
|
|
1109
1148
|
const findInRouteTree = async routes => {
|
|
1110
1149
|
const parentMatch = last(matches);
|
|
1111
1150
|
let params = parentMatch?.params ?? {};
|
|
@@ -1151,9 +1190,7 @@
|
|
|
1151
1190
|
matchingRoutes.forEach(foundRoute => {
|
|
1152
1191
|
const interpolatedPath = interpolatePath(foundRoute.path, params);
|
|
1153
1192
|
const matchId = interpolatePath(foundRoute.id, params, true);
|
|
1154
|
-
const match = existingMatches.find(d => d.id === matchId) ||
|
|
1155
|
-
// this.store.state.matchCache[matchId]?.match || // TODO:
|
|
1156
|
-
new RouteMatch(this, foundRoute, {
|
|
1193
|
+
const match = existingMatches.find(d => d.id === matchId) || new RouteMatch(this, foundRoute, {
|
|
1157
1194
|
id: matchId,
|
|
1158
1195
|
params,
|
|
1159
1196
|
pathname: joinPaths([this.basepath, interpolatedPath])
|
|
@@ -1169,14 +1206,8 @@
|
|
|
1169
1206
|
findInRouteTree([this.routeTree]);
|
|
1170
1207
|
return matches;
|
|
1171
1208
|
};
|
|
1172
|
-
loadMatches = async (resolvedMatches,
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
// this.cleanMatchCache()
|
|
1176
|
-
resolvedMatches.forEach(async match => {
|
|
1177
|
-
// Validate the match (loads search params etc)
|
|
1178
|
-
match.__validate();
|
|
1179
|
-
});
|
|
1209
|
+
loadMatches = async (resolvedMatches, opts) => {
|
|
1210
|
+
initMatches(resolvedMatches);
|
|
1180
1211
|
|
|
1181
1212
|
// Check each match middleware to see if the route can be accessed
|
|
1182
1213
|
await Promise.all(resolvedMatches.map(async match => {
|
|
@@ -1186,7 +1217,7 @@
|
|
|
1186
1217
|
match
|
|
1187
1218
|
});
|
|
1188
1219
|
} catch (err) {
|
|
1189
|
-
if (!
|
|
1220
|
+
if (!opts?.preload) {
|
|
1190
1221
|
match.route.options.onLoadError?.(err);
|
|
1191
1222
|
}
|
|
1192
1223
|
throw err;
|
|
@@ -1194,14 +1225,16 @@
|
|
|
1194
1225
|
}));
|
|
1195
1226
|
const matchPromises = resolvedMatches.map(async (match, index) => {
|
|
1196
1227
|
const prevMatch = resolvedMatches[1];
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1228
|
+
match.state.search;
|
|
1229
|
+
|
|
1230
|
+
// if (opts?.filter && !opts.filter(match)) {
|
|
1231
|
+
// return
|
|
1232
|
+
// }
|
|
1233
|
+
|
|
1201
1234
|
match.load({
|
|
1202
|
-
preload:
|
|
1235
|
+
preload: opts?.preload
|
|
1203
1236
|
});
|
|
1204
|
-
if (match.
|
|
1237
|
+
if (match.state.status !== 'success' && match.__loadPromise) {
|
|
1205
1238
|
// Wait for the first sign of activity from the match
|
|
1206
1239
|
await match.__loadPromise;
|
|
1207
1240
|
}
|
|
@@ -1223,7 +1256,7 @@
|
|
|
1223
1256
|
};
|
|
1224
1257
|
navigate = async ({
|
|
1225
1258
|
from,
|
|
1226
|
-
to = '
|
|
1259
|
+
to = '',
|
|
1227
1260
|
search,
|
|
1228
1261
|
hash,
|
|
1229
1262
|
replace,
|
|
@@ -1257,19 +1290,22 @@
|
|
|
1257
1290
|
to: location.to ? this.resolvePath(location.from ?? '', location.to) : undefined
|
|
1258
1291
|
};
|
|
1259
1292
|
const next = this.buildNext(location);
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
}
|
|
1264
|
-
return matchPathname(this.basepath, this.store.state.pendingLocation.pathname, {
|
|
1265
|
-
...opts,
|
|
1266
|
-
to: next.pathname
|
|
1267
|
-
});
|
|
1293
|
+
const baseLocation = opts?.pending ? this.state.pendingLocation : this.state.currentLocation;
|
|
1294
|
+
if (!baseLocation) {
|
|
1295
|
+
return false;
|
|
1268
1296
|
}
|
|
1269
|
-
|
|
1297
|
+
const match = matchPathname(this.basepath, baseLocation.pathname, {
|
|
1270
1298
|
...opts,
|
|
1271
1299
|
to: next.pathname
|
|
1272
1300
|
});
|
|
1301
|
+
if (!match) {
|
|
1302
|
+
return false;
|
|
1303
|
+
}
|
|
1304
|
+
if (opts?.includeSearch ?? true) {
|
|
1305
|
+
console.log(baseLocation.search, next.search, partialDeepEqual(baseLocation.search, next.search));
|
|
1306
|
+
return partialDeepEqual(baseLocation.search, next.search) ? match : false;
|
|
1307
|
+
}
|
|
1308
|
+
return match;
|
|
1273
1309
|
};
|
|
1274
1310
|
buildLink = ({
|
|
1275
1311
|
from,
|
|
@@ -1312,17 +1348,16 @@
|
|
|
1312
1348
|
const preloadDelay = userPreloadDelay ?? this.options.defaultPreloadDelay ?? 0;
|
|
1313
1349
|
|
|
1314
1350
|
// Compare path/hash for matches
|
|
1315
|
-
const
|
|
1316
|
-
const currentPathSplit = this.store.state.currentLocation.pathname.split('/');
|
|
1351
|
+
const currentPathSplit = this.state.currentLocation.pathname.split('/');
|
|
1317
1352
|
const nextPathSplit = next.pathname.split('/');
|
|
1318
1353
|
const pathIsFuzzyEqual = nextPathSplit.every((d, i) => d === currentPathSplit[i]);
|
|
1319
|
-
const hashIsEqual = this.store.state.currentLocation.hash === next.hash;
|
|
1320
1354
|
// Combine the matches based on user options
|
|
1321
|
-
const pathTest = activeOptions?.exact ?
|
|
1322
|
-
const hashTest = activeOptions?.includeHash ?
|
|
1355
|
+
const pathTest = activeOptions?.exact ? this.state.currentLocation.pathname === next.pathname : pathIsFuzzyEqual;
|
|
1356
|
+
const hashTest = activeOptions?.includeHash ? this.state.currentLocation.hash === next.hash : true;
|
|
1357
|
+
const searchTest = activeOptions?.includeSearch ?? true ? partialDeepEqual(this.state.currentLocation.search, next.search) : true;
|
|
1323
1358
|
|
|
1324
1359
|
// The final "active" test
|
|
1325
|
-
const isActive = pathTest && hashTest;
|
|
1360
|
+
const isActive = pathTest && hashTest && searchTest;
|
|
1326
1361
|
|
|
1327
1362
|
// The click handler
|
|
1328
1363
|
const handleClick = e => {
|
|
@@ -1379,21 +1414,18 @@
|
|
|
1379
1414
|
dehydrate = () => {
|
|
1380
1415
|
return {
|
|
1381
1416
|
state: {
|
|
1382
|
-
...pick(this.
|
|
1383
|
-
currentMatches: this.
|
|
1417
|
+
...pick(this.state, ['latestLocation', 'currentLocation', 'status', 'lastUpdated']),
|
|
1418
|
+
currentMatches: this.state.currentMatches.map(match => ({
|
|
1384
1419
|
id: match.id,
|
|
1385
1420
|
state: {
|
|
1386
|
-
|
|
1421
|
+
status: match.state.status
|
|
1387
1422
|
}
|
|
1388
1423
|
}))
|
|
1389
|
-
}
|
|
1390
|
-
context: this.options.context
|
|
1424
|
+
}
|
|
1391
1425
|
};
|
|
1392
1426
|
};
|
|
1393
1427
|
hydrate = dehydratedRouter => {
|
|
1394
1428
|
this.store.setState(s => {
|
|
1395
|
-
this.options.context = dehydratedRouter.context;
|
|
1396
|
-
|
|
1397
1429
|
// Match the routes
|
|
1398
1430
|
const currentMatches = this.matchRoutes(dehydratedRouter.state.latestLocation.pathname, {
|
|
1399
1431
|
strictParseParams: true
|
|
@@ -1406,7 +1438,7 @@
|
|
|
1406
1438
|
...dehydratedMatch.state
|
|
1407
1439
|
}));
|
|
1408
1440
|
});
|
|
1409
|
-
currentMatches
|
|
1441
|
+
initMatches(currentMatches);
|
|
1410
1442
|
return {
|
|
1411
1443
|
...s,
|
|
1412
1444
|
...dehydratedRouter.state,
|
|
@@ -1417,9 +1449,10 @@
|
|
|
1417
1449
|
#buildRouteTree = routeTree => {
|
|
1418
1450
|
const recurseRoutes = routes => {
|
|
1419
1451
|
routes.forEach((route, i) => {
|
|
1420
|
-
route.init(
|
|
1421
|
-
|
|
1422
|
-
|
|
1452
|
+
route.init({
|
|
1453
|
+
originalIndex: i,
|
|
1454
|
+
router: this
|
|
1455
|
+
});
|
|
1423
1456
|
const existingRoute = this.routesById[route.id];
|
|
1424
1457
|
if (existingRoute) {
|
|
1425
1458
|
{
|
|
@@ -1457,9 +1490,10 @@
|
|
|
1457
1490
|
this.load();
|
|
1458
1491
|
};
|
|
1459
1492
|
#buildLocation = (dest = {}) => {
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1493
|
+
dest.fromCurrent = dest.fromCurrent ?? dest.to === '';
|
|
1494
|
+
const fromPathname = dest.fromCurrent ? this.state.latestLocation.pathname : dest.from ?? this.state.latestLocation.pathname;
|
|
1495
|
+
let pathname = resolvePath(this.basepath ?? '/', fromPathname, `${dest.to ?? ''}`);
|
|
1496
|
+
const fromMatches = this.matchRoutes(this.state.latestLocation.pathname, {
|
|
1463
1497
|
strictParseParams: true
|
|
1464
1498
|
});
|
|
1465
1499
|
const toMatches = this.matchRoutes(pathname);
|
|
@@ -1475,7 +1509,7 @@
|
|
|
1475
1509
|
pathname = interpolatePath(pathname, nextParams ?? {});
|
|
1476
1510
|
|
|
1477
1511
|
// Pre filters first
|
|
1478
|
-
const preFilteredSearch = dest.__preSearchFilters?.length ? dest.__preSearchFilters?.reduce((prev, next) => next(prev), this.
|
|
1512
|
+
const preFilteredSearch = dest.__preSearchFilters?.length ? dest.__preSearchFilters?.reduce((prev, next) => next(prev), this.state.latestLocation.search) : this.state.latestLocation.search;
|
|
1479
1513
|
|
|
1480
1514
|
// Then the link/navigate function
|
|
1481
1515
|
const destSearch = dest.search === true ? preFilteredSearch // Preserve resolvedFrom true
|
|
@@ -1485,15 +1519,15 @@
|
|
|
1485
1519
|
|
|
1486
1520
|
// Then post filters
|
|
1487
1521
|
const postFilteredSearch = dest.__postSearchFilters?.length ? dest.__postSearchFilters.reduce((prev, next) => next(prev), destSearch) : destSearch;
|
|
1488
|
-
const search = replaceEqualDeep(this.
|
|
1522
|
+
const search = replaceEqualDeep(this.state.latestLocation.search, postFilteredSearch);
|
|
1489
1523
|
const searchStr = this.options.stringifySearch(search);
|
|
1490
|
-
let hash = dest.hash === true ? this.
|
|
1524
|
+
let hash = dest.hash === true ? this.state.latestLocation.hash : functionalUpdate(dest.hash, this.state.latestLocation.hash);
|
|
1491
1525
|
hash = hash ? `#${hash}` : '';
|
|
1492
1526
|
return {
|
|
1493
1527
|
pathname,
|
|
1494
1528
|
search,
|
|
1495
1529
|
searchStr,
|
|
1496
|
-
state: this.
|
|
1530
|
+
state: this.state.latestLocation.state,
|
|
1497
1531
|
hash,
|
|
1498
1532
|
href: `${pathname}${searchStr}${hash}`,
|
|
1499
1533
|
key: dest.key
|
|
@@ -1507,7 +1541,7 @@
|
|
|
1507
1541
|
if (!location.replace) {
|
|
1508
1542
|
nextAction = 'push';
|
|
1509
1543
|
}
|
|
1510
|
-
const isSameUrl = this.
|
|
1544
|
+
const isSameUrl = this.state.latestLocation.href === next.href;
|
|
1511
1545
|
if (isSameUrl && !next.key) {
|
|
1512
1546
|
nextAction = 'replace';
|
|
1513
1547
|
}
|
|
@@ -1517,7 +1551,7 @@
|
|
|
1517
1551
|
...next.state
|
|
1518
1552
|
});
|
|
1519
1553
|
|
|
1520
|
-
// this.load(this.#parseLocation(this.
|
|
1554
|
+
// this.load(this.#parseLocation(this.state.latestLocation))
|
|
1521
1555
|
|
|
1522
1556
|
return this.navigationPromise = new Promise(resolve => {
|
|
1523
1557
|
const previousNavigationResolve = this.resolveNavigation;
|
|
@@ -1543,12 +1577,12 @@
|
|
|
1543
1577
|
function isCtrlEvent(e) {
|
|
1544
1578
|
return !!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey);
|
|
1545
1579
|
}
|
|
1546
|
-
function
|
|
1580
|
+
function initMatches(matches) {
|
|
1547
1581
|
matches.forEach((match, index) => {
|
|
1548
|
-
const
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
}
|
|
1582
|
+
const parentMatch = matches[index - 1];
|
|
1583
|
+
match.__init({
|
|
1584
|
+
parentMatch
|
|
1585
|
+
});
|
|
1552
1586
|
});
|
|
1553
1587
|
}
|
|
1554
1588
|
|
|
@@ -1624,7 +1658,7 @@
|
|
|
1624
1658
|
//
|
|
1625
1659
|
|
|
1626
1660
|
function useLinkProps(options) {
|
|
1627
|
-
const router =
|
|
1661
|
+
const router = useRouterContext();
|
|
1628
1662
|
const {
|
|
1629
1663
|
// custom props
|
|
1630
1664
|
type,
|
|
@@ -1749,7 +1783,7 @@
|
|
|
1749
1783
|
...rest
|
|
1750
1784
|
}) {
|
|
1751
1785
|
router.update(rest);
|
|
1752
|
-
const currentMatches = useStore(router.store, s => s.currentMatches
|
|
1786
|
+
const currentMatches = useStore(router.store, s => s.currentMatches);
|
|
1753
1787
|
React__namespace.useEffect(router.mount, [router]);
|
|
1754
1788
|
return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement(routerContext.Provider, {
|
|
1755
1789
|
value: {
|
|
@@ -1759,22 +1793,24 @@
|
|
|
1759
1793
|
value: [undefined, ...currentMatches]
|
|
1760
1794
|
}, /*#__PURE__*/React__namespace.createElement(Outlet, null))));
|
|
1761
1795
|
}
|
|
1762
|
-
function
|
|
1796
|
+
function useRouterContext() {
|
|
1763
1797
|
const value = React__namespace.useContext(routerContext);
|
|
1764
1798
|
warning(!value, 'useRouter must be used inside a <Router> component!');
|
|
1799
|
+
useStore(value.router.store);
|
|
1765
1800
|
return value.router;
|
|
1766
1801
|
}
|
|
1767
|
-
function
|
|
1768
|
-
const router =
|
|
1769
|
-
|
|
1802
|
+
function useRouter(track, shallow) {
|
|
1803
|
+
const router = useRouterContext();
|
|
1804
|
+
useStore(router.store, track, shallow);
|
|
1805
|
+
return router;
|
|
1770
1806
|
}
|
|
1771
1807
|
function useMatches() {
|
|
1772
1808
|
return React__namespace.useContext(matchesContext);
|
|
1773
1809
|
}
|
|
1774
1810
|
function useMatch(opts) {
|
|
1775
|
-
const router =
|
|
1811
|
+
const router = useRouterContext();
|
|
1776
1812
|
const nearestMatch = useMatches()[0];
|
|
1777
|
-
const match = opts?.from ? router.
|
|
1813
|
+
const match = opts?.from ? router.state.currentMatches.find(d => d.route.id === opts?.from) : nearestMatch;
|
|
1778
1814
|
invariant(match, `Could not find ${opts?.from ? `an active match from "${opts.from}"` : 'a nearest match!'}`);
|
|
1779
1815
|
if (opts?.strict ?? true) {
|
|
1780
1816
|
invariant(nearestMatch.route.id == match?.route.id, `useMatch("${match?.route.id}") is being called in a component that is meant to render the '${nearestMatch.route.id}' route. Did you mean to 'useMatch("${match?.route.id}", { strict: false })' or 'useRoute("${match?.route.id}")' instead?`);
|
|
@@ -1783,26 +1819,26 @@
|
|
|
1783
1819
|
return match;
|
|
1784
1820
|
}
|
|
1785
1821
|
function useRoute(routeId) {
|
|
1786
|
-
const router =
|
|
1822
|
+
const router = useRouterContext();
|
|
1787
1823
|
const resolvedRoute = router.getRoute(routeId);
|
|
1788
1824
|
invariant(resolvedRoute, `Could not find a route for route "${routeId}"! Did you forget to add it to your route config?`);
|
|
1789
1825
|
return resolvedRoute;
|
|
1790
1826
|
}
|
|
1791
1827
|
function useSearch(opts) {
|
|
1792
1828
|
const match = useMatch(opts);
|
|
1793
|
-
useStore(match.store, d => opts?.track?.(d.search) ?? d.search);
|
|
1794
|
-
return match.
|
|
1829
|
+
useStore(match.store, d => opts?.track?.(d.search) ?? d.search, true);
|
|
1830
|
+
return match.state.search;
|
|
1795
1831
|
}
|
|
1796
1832
|
function useParams(opts) {
|
|
1797
|
-
const router =
|
|
1833
|
+
const router = useRouterContext();
|
|
1798
1834
|
useStore(router.store, d => {
|
|
1799
1835
|
const params = last(d.currentMatches)?.params;
|
|
1800
1836
|
return opts?.track?.(params) ?? params;
|
|
1801
|
-
});
|
|
1802
|
-
return last(router.
|
|
1837
|
+
}, true);
|
|
1838
|
+
return last(router.state.currentMatches)?.params;
|
|
1803
1839
|
}
|
|
1804
1840
|
function useNavigate(defaultOpts) {
|
|
1805
|
-
const router =
|
|
1841
|
+
const router = useRouterContext();
|
|
1806
1842
|
return opts => {
|
|
1807
1843
|
return router.navigate({
|
|
1808
1844
|
...defaultOpts,
|
|
@@ -1811,7 +1847,7 @@
|
|
|
1811
1847
|
};
|
|
1812
1848
|
}
|
|
1813
1849
|
function useMatchRoute() {
|
|
1814
|
-
const router =
|
|
1850
|
+
const router = useRouterContext();
|
|
1815
1851
|
return opts => {
|
|
1816
1852
|
const {
|
|
1817
1853
|
pending,
|
|
@@ -1850,17 +1886,17 @@
|
|
|
1850
1886
|
matches,
|
|
1851
1887
|
match
|
|
1852
1888
|
}) {
|
|
1853
|
-
const router =
|
|
1854
|
-
useStore(match.store);
|
|
1889
|
+
const router = useRouterContext();
|
|
1890
|
+
useStore(match.store, store => [store.status, store.error], true);
|
|
1855
1891
|
const defaultPending = React__namespace.useCallback(() => null, []);
|
|
1856
1892
|
const Inner = React__namespace.useCallback(props => {
|
|
1857
|
-
if (props.match.
|
|
1858
|
-
throw props.match.
|
|
1893
|
+
if (props.match.state.status === 'error') {
|
|
1894
|
+
throw props.match.state.error;
|
|
1859
1895
|
}
|
|
1860
|
-
if (props.match.
|
|
1896
|
+
if (props.match.state.status === 'success') {
|
|
1861
1897
|
return /*#__PURE__*/React__namespace.createElement(props.match.component ?? router.options.defaultComponent ?? Outlet);
|
|
1862
1898
|
}
|
|
1863
|
-
if (props.match.
|
|
1899
|
+
if (props.match.state.status === 'pending') {
|
|
1864
1900
|
throw props.match.__loadPromise;
|
|
1865
1901
|
}
|
|
1866
1902
|
invariant(false, 'Idle routeMatch status encountered during rendering! You should never see this. File an issue!');
|
|
@@ -1869,7 +1905,7 @@
|
|
|
1869
1905
|
const errorComponent = match.errorComponent ?? router.options.defaultErrorComponent;
|
|
1870
1906
|
return /*#__PURE__*/React__namespace.createElement(matchesContext.Provider, {
|
|
1871
1907
|
value: matches
|
|
1872
|
-
}, /*#__PURE__*/React__namespace.createElement(React__namespace.Suspense, {
|
|
1908
|
+
}, match.route.options.wrapInSuspense ?? true ? /*#__PURE__*/React__namespace.createElement(React__namespace.Suspense, {
|
|
1873
1909
|
fallback: /*#__PURE__*/React__namespace.createElement(PendingComponent, null)
|
|
1874
1910
|
}, /*#__PURE__*/React__namespace.createElement(CatchBoundary, {
|
|
1875
1911
|
key: match.route.id,
|
|
@@ -1877,7 +1913,13 @@
|
|
|
1877
1913
|
match: match
|
|
1878
1914
|
}, /*#__PURE__*/React__namespace.createElement(Inner, {
|
|
1879
1915
|
match: match
|
|
1880
|
-
})))
|
|
1916
|
+
}))) : /*#__PURE__*/React__namespace.createElement(CatchBoundary, {
|
|
1917
|
+
key: match.route.id,
|
|
1918
|
+
errorComponent: errorComponent,
|
|
1919
|
+
match: match
|
|
1920
|
+
}, /*#__PURE__*/React__namespace.createElement(Inner, {
|
|
1921
|
+
match: match
|
|
1922
|
+
})));
|
|
1881
1923
|
}
|
|
1882
1924
|
|
|
1883
1925
|
// This is the messiest thing ever... I'm either seriously tired (likely) or
|
|
@@ -1906,17 +1948,17 @@
|
|
|
1906
1948
|
}
|
|
1907
1949
|
function CatchBoundaryInner(props) {
|
|
1908
1950
|
const [activeErrorState, setActiveErrorState] = React__namespace.useState(props.errorState);
|
|
1909
|
-
const router =
|
|
1951
|
+
const router = useRouterContext();
|
|
1910
1952
|
const errorComponent = props.errorComponent ?? DefaultErrorBoundary;
|
|
1911
1953
|
const prevKeyRef = React__namespace.useRef('');
|
|
1912
1954
|
React__namespace.useEffect(() => {
|
|
1913
1955
|
if (activeErrorState) {
|
|
1914
|
-
if (router.
|
|
1956
|
+
if (router.state.currentLocation.key !== prevKeyRef.current) {
|
|
1915
1957
|
setActiveErrorState({});
|
|
1916
1958
|
}
|
|
1917
1959
|
}
|
|
1918
|
-
prevKeyRef.current = router.
|
|
1919
|
-
}, [activeErrorState, router.
|
|
1960
|
+
prevKeyRef.current = router.state.currentLocation.key;
|
|
1961
|
+
}, [activeErrorState, router.state.currentLocation.key]);
|
|
1920
1962
|
React__namespace.useEffect(() => {
|
|
1921
1963
|
if (props.errorState.error) {
|
|
1922
1964
|
setActiveErrorState(props.errorState);
|
|
@@ -2013,6 +2055,7 @@
|
|
|
2013
2055
|
exports.matchesContext = matchesContext;
|
|
2014
2056
|
exports.parsePathname = parsePathname;
|
|
2015
2057
|
exports.parseSearchWith = parseSearchWith;
|
|
2058
|
+
exports.partialDeepEqual = partialDeepEqual;
|
|
2016
2059
|
exports.pick = pick;
|
|
2017
2060
|
exports.replaceEqualDeep = replaceEqualDeep;
|
|
2018
2061
|
exports.resolvePath = resolvePath;
|
|
@@ -2030,7 +2073,7 @@
|
|
|
2030
2073
|
exports.useParams = useParams;
|
|
2031
2074
|
exports.useRoute = useRoute;
|
|
2032
2075
|
exports.useRouter = useRouter;
|
|
2033
|
-
exports.
|
|
2076
|
+
exports.useRouterContext = useRouterContext;
|
|
2034
2077
|
exports.useSearch = useSearch;
|
|
2035
2078
|
exports.useStore = useStore;
|
|
2036
2079
|
exports.warning = warning;
|