@tanstack/router-core 0.0.1-beta.161 → 0.0.1-beta.163

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.
@@ -33,7 +33,7 @@ const stopBlocking = () => {
33
33
  function createHistory(opts) {
34
34
  let location = opts.getLocation();
35
35
  let unsub = () => {};
36
- let listeners = new Set();
36
+ let subscribers = new Set();
37
37
  let blockers = [];
38
38
  let queue = [];
39
39
  const tryFlush = () => {
@@ -47,7 +47,7 @@ function createHistory(opts) {
47
47
  while (queue.length) {
48
48
  queue.shift()?.();
49
49
  }
50
- if (!opts.listener) {
50
+ if (!opts.subscriber) {
51
51
  onUpdate();
52
52
  }
53
53
  };
@@ -57,20 +57,20 @@ function createHistory(opts) {
57
57
  };
58
58
  const onUpdate = () => {
59
59
  location = opts.getLocation();
60
- listeners.forEach(listener => listener());
60
+ subscribers.forEach(subscriber => subscriber());
61
61
  };
62
62
  return {
63
63
  get location() {
64
64
  return location;
65
65
  },
66
- listen: cb => {
67
- if (listeners.size === 0) {
68
- unsub = typeof opts.listener === 'function' ? opts.listener(onUpdate) : () => {};
66
+ subscribe: cb => {
67
+ if (subscribers.size === 0) {
68
+ unsub = typeof opts.subscriber === 'function' ? opts.subscriber(onUpdate) : () => {};
69
69
  }
70
- listeners.add(cb);
70
+ subscribers.add(cb);
71
71
  return () => {
72
- listeners.delete(cb);
73
- if (listeners.size === 0) {
72
+ subscribers.delete(cb);
73
+ if (subscribers.size === 0) {
74
74
  unsub();
75
75
  }
76
76
  };
@@ -123,7 +123,7 @@ function createBrowserHistory(opts) {
123
123
  const getLocation = () => parseLocation(getHref(), history.state);
124
124
  return createHistory({
125
125
  getLocation,
126
- listener: onUpdate => {
126
+ subscriber: onUpdate => {
127
127
  window.addEventListener(pushStateEvent, onUpdate);
128
128
  window.addEventListener(popStateEvent, onUpdate);
129
129
  var pushState = window.history.pushState;
@@ -178,7 +178,7 @@ function createMemoryHistory(opts = {
178
178
  const getLocation = () => parseLocation(entries[index], currentState);
179
179
  return createHistory({
180
180
  getLocation,
181
- listener: false,
181
+ subscriber: false,
182
182
  pushState: (path, state) => {
183
183
  currentState = {
184
184
  ...state,
@@ -753,6 +753,24 @@ class Router {
753
753
  });
754
754
  }
755
755
  }
756
+ subscribers = new Set();
757
+ subscribe = (eventType, fn) => {
758
+ const listener = {
759
+ eventType,
760
+ fn
761
+ };
762
+ this.subscribers.add(listener);
763
+ return () => {
764
+ this.subscribers.delete(listener);
765
+ };
766
+ };
767
+ #emit = routerEvent => {
768
+ this.subscribers.forEach(listener => {
769
+ if (listener.eventType === routerEvent.type) {
770
+ listener.fn(routerEvent);
771
+ }
772
+ });
773
+ };
756
774
  reset = () => {
757
775
  this.__store.setState(s => Object.assign(s, getInitialRouterState()));
758
776
  };
@@ -783,7 +801,7 @@ class Router {
783
801
  resolvedLocation: parsedLocation,
784
802
  location: parsedLocation
785
803
  }));
786
- this.#unsubHistory = this.history.listen(() => {
804
+ this.#unsubHistory = this.history.subscribe(() => {
787
805
  this.safeLoad({
788
806
  next: this.#parseLocation(this.state.location)
789
807
  });
@@ -825,6 +843,8 @@ class Router {
825
843
  latestLoadPromise = Promise.resolve();
826
844
  load = async opts => {
827
845
  const promise = new Promise(async (resolve, reject) => {
846
+ const prevLocation = this.state.resolvedLocation;
847
+ const pathDidChange = !!(opts?.next && prevLocation.href !== opts.next.href);
828
848
  let latestPromise;
829
849
  const checkLatest = () => {
830
850
  return this.latestLoadPromise !== promise ? this.latestLoadPromise : undefined;
@@ -834,6 +854,12 @@ class Router {
834
854
  // this.cancelMatches()
835
855
 
836
856
  let pendingMatches;
857
+ this.#emit({
858
+ type: 'onBeforeLoad',
859
+ from: prevLocation,
860
+ to: opts?.next ?? this.state.location,
861
+ pathChanged: pathDidChange
862
+ });
837
863
  this.__store.batch(() => {
838
864
  if (opts?.next) {
839
865
  // Ingest the new location
@@ -868,7 +894,6 @@ class Router {
868
894
  if (latestPromise = checkLatest()) {
869
895
  return latestPromise;
870
896
  }
871
- const prevLocation = this.state.resolvedLocation;
872
897
  this.__store.setState(s => ({
873
898
  ...s,
874
899
  status: 'idle',
@@ -876,9 +901,12 @@ class Router {
876
901
  matchIds: s.pendingMatchIds,
877
902
  pendingMatchIds: []
878
903
  }));
879
- if (prevLocation.href !== this.state.location.href) {
880
- this.options.onRouteChange?.();
881
- }
904
+ this.#emit({
905
+ type: 'onLoad',
906
+ from: prevLocation,
907
+ to: this.state.location,
908
+ pathChanged: pathDidChange
909
+ });
882
910
  resolve();
883
911
  } catch (err) {
884
912
  // Only apply the latest transition
@@ -1128,14 +1156,14 @@ class Router {
1128
1156
  try {
1129
1157
  for (const [index, match] of resolvedMatches.entries()) {
1130
1158
  const route = this.getRoute(match.routeId);
1131
- const handleError = (err, handler) => {
1159
+ const handleError = (err, code) => {
1160
+ err.routerCode = code;
1132
1161
  firstBadMatchIndex = firstBadMatchIndex ?? index;
1133
- handler = handler || route.options.onError;
1134
1162
  if (isRedirect(err)) {
1135
1163
  throw err;
1136
1164
  }
1137
1165
  try {
1138
- handler?.(err);
1166
+ route.options.onError?.(err);
1139
1167
  } catch (errorHandlerErr) {
1140
1168
  err = errorHandlerErr;
1141
1169
  if (isRedirect(errorHandlerErr)) {
@@ -1150,10 +1178,10 @@ class Router {
1150
1178
  }));
1151
1179
  };
1152
1180
  if (match.paramsError) {
1153
- handleError(match.paramsError, route.options.onParseParamsError);
1181
+ handleError(match.paramsError, 'PARSE_PARAMS');
1154
1182
  }
1155
1183
  if (match.searchError) {
1156
- handleError(match.searchError, route.options.onValidateSearchError);
1184
+ handleError(match.searchError, 'VALIDATE_SEARCH');
1157
1185
  }
1158
1186
  let didError = false;
1159
1187
  try {
@@ -1162,7 +1190,7 @@ class Router {
1162
1190
  preload: !!opts?.preload
1163
1191
  });
1164
1192
  } catch (err) {
1165
- handleError(err, route.options.onBeforeLoadError);
1193
+ handleError(err, 'BEFORE_LOAD');
1166
1194
  didError = true;
1167
1195
  }
1168
1196
 
@@ -1220,28 +1248,18 @@ class Router {
1220
1248
  const [_, loader] = await Promise.all([componentsPromise, loaderPromise]);
1221
1249
  if (latestPromise = checkLatest()) return await latestPromise;
1222
1250
  this.setRouteMatchData(match.id, () => loader, opts);
1223
- } catch (loaderError) {
1224
- let latestError = loaderError;
1251
+ } catch (error) {
1225
1252
  if (latestPromise = checkLatest()) return await latestPromise;
1226
- if (handleIfRedirect(loaderError)) return;
1227
- if (route.options.onLoadError) {
1228
- try {
1229
- route.options.onLoadError(loaderError);
1230
- } catch (onLoadError) {
1231
- latestError = onLoadError;
1232
- if (handleIfRedirect(onLoadError)) return;
1233
- }
1234
- }
1235
- if ((!route.options.onLoadError || latestError !== loaderError) && route.options.onError) {
1236
- try {
1237
- route.options.onError(latestError);
1238
- } catch (onErrorError) {
1239
- if (handleIfRedirect(onErrorError)) return;
1240
- }
1253
+ if (handleIfRedirect(error)) return;
1254
+ try {
1255
+ route.options.onError?.(error);
1256
+ } catch (onErrorError) {
1257
+ error = onErrorError;
1258
+ if (handleIfRedirect(onErrorError)) return;
1241
1259
  }
1242
1260
  this.setRouteMatch(match.id, s => ({
1243
1261
  ...s,
1244
- error: loaderError,
1262
+ error,
1245
1263
  status: 'error',
1246
1264
  isFetching: false,
1247
1265
  updatedAt: Date.now()