@remix-run/router 1.2.1 → 1.3.0-pre.1

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.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @remix-run/router v1.2.1
2
+ * @remix-run/router v1.3.0-pre.1
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -96,6 +96,10 @@ function createMemoryHistory(options) {
96
96
  return location;
97
97
  }
98
98
 
99
+ function createHref(to) {
100
+ return typeof to === "string" ? to : createPath(to);
101
+ }
102
+
99
103
  let history = {
100
104
  get index() {
101
105
  return index;
@@ -109,8 +113,10 @@ function createMemoryHistory(options) {
109
113
  return getCurrentLocation();
110
114
  },
111
115
 
112
- createHref(to) {
113
- return typeof to === "string" ? to : createPath(to);
116
+ createHref,
117
+
118
+ createURL(to) {
119
+ return new URL(createHref(to), "http://localhost");
114
120
  },
115
121
 
116
122
  encodeLocation(to) {
@@ -389,15 +395,6 @@ function parsePath(path) {
389
395
 
390
396
  return parsedPath;
391
397
  }
392
- function createClientSideURL(location) {
393
- // window.location.origin is "null" (the literal string value) in Firefox
394
- // under certain conditions, notably when serving from a local HTML file
395
- // See https://bugzilla.mozilla.org/show_bug.cgi?id=878297
396
- let base = typeof window !== "undefined" && typeof window.location !== "undefined" && window.location.origin !== "null" ? window.location.origin : window.location.href;
397
- let href = typeof location === "string" ? location : createPath(location);
398
- invariant(base, "No window.location.(origin|href) available to create URL for href: " + href);
399
- return new URL(href, base);
400
- }
401
398
 
402
399
  function getUrlBasedHistory(getLocation, createHref, validateLocation, options) {
403
400
  if (options === void 0) {
@@ -462,6 +459,16 @@ function getUrlBasedHistory(getLocation, createHref, validateLocation, options)
462
459
  }
463
460
  }
464
461
 
462
+ function createURL(to) {
463
+ // window.location.origin is "null" (the literal string value) in Firefox
464
+ // under certain conditions, notably when serving from a local HTML file
465
+ // See https://bugzilla.mozilla.org/show_bug.cgi?id=878297
466
+ let base = window.location.origin !== "null" ? window.location.origin : window.location.href;
467
+ let href = typeof to === "string" ? to : createPath(to);
468
+ invariant(base, "No window.location.(origin|href) available to create URL for href: " + href);
469
+ return new URL(href, base);
470
+ }
471
+
465
472
  let history = {
466
473
  get action() {
467
474
  return action;
@@ -488,9 +495,11 @@ function getUrlBasedHistory(getLocation, createHref, validateLocation, options)
488
495
  return createHref(window, to);
489
496
  },
490
497
 
498
+ createURL,
499
+
491
500
  encodeLocation(to) {
492
501
  // Encode a Location the same way window.location would
493
- let url = createClientSideURL(typeof to === "string" ? to : createPath(to));
502
+ let url = createURL(to);
494
503
  return {
495
504
  pathname: url.pathname,
496
505
  search: url.search,
@@ -807,13 +816,32 @@ function generatePath(originalPath, params) {
807
816
  path = path.replace(/\*$/, "/*");
808
817
  }
809
818
 
810
- return path.replace(/^:(\w+)/g, (_, key) => {
811
- invariant(params[key] != null, "Missing \":" + key + "\" param");
812
- return params[key];
813
- }).replace(/\/:(\w+)/g, (_, key) => {
814
- invariant(params[key] != null, "Missing \":" + key + "\" param");
815
- return "/" + params[key];
816
- }).replace(/(\/?)\*/, (_, prefix, __, str) => {
819
+ return path.replace(/^:(\w+)(\??)/g, (_, key, optional) => {
820
+ let param = params[key];
821
+
822
+ if (optional === "?") {
823
+ return param == null ? "" : param;
824
+ }
825
+
826
+ if (param == null) {
827
+ invariant(false, "Missing \":" + key + "\" param");
828
+ }
829
+
830
+ return param;
831
+ }).replace(/\/:(\w+)(\??)/g, (_, key, optional) => {
832
+ let param = params[key];
833
+
834
+ if (optional === "?") {
835
+ return param == null ? "" : "/" + param;
836
+ }
837
+
838
+ if (param == null) {
839
+ invariant(false, "Missing \":" + key + "\" param");
840
+ }
841
+
842
+ return "/" + param;
843
+ }) // Remove any optional markers from optional static segments
844
+ .replace(/\?/g, "").replace(/(\/?)\*/, (_, prefix, __, str) => {
817
845
  const star = "*";
818
846
 
819
847
  if (params[star] == null) {
@@ -1160,9 +1188,10 @@ const json = function json(data, init) {
1160
1188
  };
1161
1189
  class AbortedDeferredError extends Error {}
1162
1190
  class DeferredData {
1163
- constructor(data) {
1164
- this.pendingKeys = new Set();
1165
- this.subscriber = undefined;
1191
+ constructor(data, responseInit) {
1192
+ this.pendingKeysSet = new Set();
1193
+ this.subscribers = new Set();
1194
+ this.deferredKeys = [];
1166
1195
  invariant(data && typeof data === "object" && !Array.isArray(data), "defer() only accepts plain objects"); // Set up an AbortController + Promise we can race against to exit early
1167
1196
  // cancellation
1168
1197
 
@@ -1181,6 +1210,7 @@ class DeferredData {
1181
1210
  [key]: this.trackPromise(key, value)
1182
1211
  });
1183
1212
  }, {});
1213
+ this.init = responseInit;
1184
1214
  }
1185
1215
 
1186
1216
  trackPromise(key, value) {
@@ -1188,7 +1218,8 @@ class DeferredData {
1188
1218
  return value;
1189
1219
  }
1190
1220
 
1191
- this.pendingKeys.add(key); // We store a little wrapper promise that will be extended with
1221
+ this.deferredKeys.push(key);
1222
+ this.pendingKeysSet.add(key); // We store a little wrapper promise that will be extended with
1192
1223
  // _data/_error props upon resolve/reject
1193
1224
 
1194
1225
  let promise = Promise.race([value, this.abortPromise]).then(data => this.onSettle(promise, key, null, data), error => this.onSettle(promise, key, error)); // Register rejection listeners to avoid uncaught promise rejections on
@@ -1210,39 +1241,41 @@ class DeferredData {
1210
1241
  return Promise.reject(error);
1211
1242
  }
1212
1243
 
1213
- this.pendingKeys.delete(key);
1244
+ this.pendingKeysSet.delete(key);
1214
1245
 
1215
1246
  if (this.done) {
1216
1247
  // Nothing left to abort!
1217
1248
  this.unlistenAbortSignal();
1218
1249
  }
1219
1250
 
1220
- const subscriber = this.subscriber;
1221
-
1222
1251
  if (error) {
1223
1252
  Object.defineProperty(promise, "_error", {
1224
1253
  get: () => error
1225
1254
  });
1226
- subscriber && subscriber(false);
1255
+ this.emit(false, key);
1227
1256
  return Promise.reject(error);
1228
1257
  }
1229
1258
 
1230
1259
  Object.defineProperty(promise, "_data", {
1231
1260
  get: () => data
1232
1261
  });
1233
- subscriber && subscriber(false);
1262
+ this.emit(false, key);
1234
1263
  return data;
1235
1264
  }
1236
1265
 
1266
+ emit(aborted, settledKey) {
1267
+ this.subscribers.forEach(subscriber => subscriber(aborted, settledKey));
1268
+ }
1269
+
1237
1270
  subscribe(fn) {
1238
- this.subscriber = fn;
1271
+ this.subscribers.add(fn);
1272
+ return () => this.subscribers.delete(fn);
1239
1273
  }
1240
1274
 
1241
1275
  cancel() {
1242
1276
  this.controller.abort();
1243
- this.pendingKeys.forEach((v, k) => this.pendingKeys.delete(k));
1244
- let subscriber = this.subscriber;
1245
- subscriber && subscriber(true);
1277
+ this.pendingKeysSet.forEach((v, k) => this.pendingKeysSet.delete(k));
1278
+ this.emit(true);
1246
1279
  }
1247
1280
 
1248
1281
  async resolveData(signal) {
@@ -1267,7 +1300,7 @@ class DeferredData {
1267
1300
  }
1268
1301
 
1269
1302
  get done() {
1270
- return this.pendingKeys.size === 0;
1303
+ return this.pendingKeysSet.size === 0;
1271
1304
  }
1272
1305
 
1273
1306
  get unwrappedData() {
@@ -1280,6 +1313,10 @@ class DeferredData {
1280
1313
  }, {});
1281
1314
  }
1282
1315
 
1316
+ get pendingKeys() {
1317
+ return Array.from(this.pendingKeysSet);
1318
+ }
1319
+
1283
1320
  }
1284
1321
 
1285
1322
  function isTrackedPromise(value) {
@@ -1298,9 +1335,16 @@ function unwrapTrackedPromise(value) {
1298
1335
  return value._data;
1299
1336
  }
1300
1337
 
1301
- function defer(data) {
1302
- return new DeferredData(data);
1303
- }
1338
+ const defer = function defer(data, init) {
1339
+ if (init === void 0) {
1340
+ init = {};
1341
+ }
1342
+
1343
+ let responseInit = typeof init === "number" ? {
1344
+ status: init
1345
+ } : init;
1346
+ return new DeferredData(data, responseInit);
1347
+ };
1304
1348
 
1305
1349
  /**
1306
1350
  * A redirect response. Sets the status code and the `Location` header.
@@ -1546,7 +1590,7 @@ function createRouter(init) {
1546
1590
 
1547
1591
 
1548
1592
  function completeNavigation(location, newState) {
1549
- var _location$state;
1593
+ var _location$state, _location$state2;
1550
1594
 
1551
1595
  // Deduce if we're in a loading/actionReload state:
1552
1596
  // - We have committed actionData in the store
@@ -1572,7 +1616,10 @@ function createRouter(init) {
1572
1616
  } // Always preserve any existing loaderData from re-used routes
1573
1617
 
1574
1618
 
1575
- let loaderData = newState.loaderData ? mergeLoaderData(state.loaderData, newState.loaderData, newState.matches || [], newState.errors) : state.loaderData;
1619
+ let loaderData = newState.loaderData ? mergeLoaderData(state.loaderData, newState.loaderData, newState.matches || [], newState.errors) : state.loaderData; // Always respect the user flag. Otherwise don't reset on mutation
1620
+ // submission navigations unless they redirect
1621
+
1622
+ let preventScrollReset = pendingPreventScrollReset === true || state.navigation.formMethod != null && isMutationMethod(state.navigation.formMethod) && ((_location$state2 = location.state) == null ? void 0 : _location$state2._isRedirect) !== true;
1576
1623
  updateState(_extends({}, newState, {
1577
1624
  // matches, errors, fetchers go through as-is
1578
1625
  actionData,
@@ -1582,9 +1629,8 @@ function createRouter(init) {
1582
1629
  initialized: true,
1583
1630
  navigation: IDLE_NAVIGATION,
1584
1631
  revalidation: "idle",
1585
- // Don't restore on submission navigations
1586
- restoreScrollPosition: state.navigation.formData ? false : getSavedScrollPosition(location, newState.matches || state.matches),
1587
- preventScrollReset: pendingPreventScrollReset
1632
+ restoreScrollPosition: getSavedScrollPosition(location, newState.matches || state.matches),
1633
+ preventScrollReset
1588
1634
  }));
1589
1635
 
1590
1636
  if (isUninterruptedRevalidation) ; else if (pendingAction === exports.Action.Pop) ; else if (pendingAction === exports.Action.Push) {
@@ -1726,7 +1772,7 @@ function createRouter(init) {
1726
1772
 
1727
1773
 
1728
1774
  pendingNavigationController = new AbortController();
1729
- let request = createClientSideRequest(location, pendingNavigationController.signal, opts && opts.submission);
1775
+ let request = createClientSideRequest(init.history, location, pendingNavigationController.signal, opts && opts.submission);
1730
1776
  let pendingActionData;
1731
1777
  let pendingError;
1732
1778
 
@@ -1867,7 +1913,9 @@ function createRouter(init) {
1867
1913
  }
1868
1914
 
1869
1915
  if (isDeferredResult(result)) {
1870
- throw new Error("defer() is not supported in actions");
1916
+ throw getInternalRouterError(400, {
1917
+ type: "defer-action"
1918
+ });
1871
1919
  }
1872
1920
 
1873
1921
  return {
@@ -1904,7 +1952,7 @@ function createRouter(init) {
1904
1952
  formData: loadingNavigation.formData,
1905
1953
  formEncType: loadingNavigation.formEncType
1906
1954
  } : undefined;
1907
- let [matchesToLoad, revalidatingFetchers] = getMatchesToLoad(state, matches, activeSubmission, location, isRevalidationRequired, cancelledDeferredRoutes, cancelledFetcherLoads, pendingActionData, pendingError, fetchLoadMatches); // Cancel pending deferreds for no-longer-matched routes or routes we're
1955
+ let [matchesToLoad, revalidatingFetchers] = getMatchesToLoad(init.history, state, matches, activeSubmission, location, isRevalidationRequired, cancelledDeferredRoutes, cancelledFetcherLoads, pendingActionData, pendingError, fetchLoadMatches); // Cancel pending deferreds for no-longer-matched routes or routes we're
1908
1956
  // about to reload. Note that if this is an action reload we would have
1909
1957
  // already cancelled all pending deferreds so this would be a no-op
1910
1958
 
@@ -2086,7 +2134,7 @@ function createRouter(init) {
2086
2134
  }); // Call the action for the fetcher
2087
2135
 
2088
2136
  let abortController = new AbortController();
2089
- let fetchRequest = createClientSideRequest(path, abortController.signal, submission);
2137
+ let fetchRequest = createClientSideRequest(init.history, path, abortController.signal, submission);
2090
2138
  fetchControllers.set(key, abortController);
2091
2139
  let actionResult = await callLoaderOrAction("action", fetchRequest, match, requestMatches, router.basename);
2092
2140
 
@@ -2127,13 +2175,15 @@ function createRouter(init) {
2127
2175
  }
2128
2176
 
2129
2177
  if (isDeferredResult(actionResult)) {
2130
- invariant(false, "defer() is not supported in actions");
2178
+ throw getInternalRouterError(400, {
2179
+ type: "defer-action"
2180
+ });
2131
2181
  } // Start the data load for current matches, or the next location if we're
2132
2182
  // in the middle of a navigation
2133
2183
 
2134
2184
 
2135
2185
  let nextLocation = state.navigation.location || state.location;
2136
- let revalidationRequest = createClientSideRequest(nextLocation, abortController.signal);
2186
+ let revalidationRequest = createClientSideRequest(init.history, nextLocation, abortController.signal);
2137
2187
  let matches = state.navigation.state !== "idle" ? matchRoutes(dataRoutes, state.navigation.location, init.basename) : state.matches;
2138
2188
  invariant(matches, "Didn't find any matches after fetcher action");
2139
2189
  let loadId = ++incrementingLoadId;
@@ -2147,7 +2197,7 @@ function createRouter(init) {
2147
2197
  });
2148
2198
 
2149
2199
  state.fetchers.set(key, loadFetcher);
2150
- let [matchesToLoad, revalidatingFetchers] = getMatchesToLoad(state, matches, submission, nextLocation, isRevalidationRequired, cancelledDeferredRoutes, cancelledFetcherLoads, {
2200
+ let [matchesToLoad, revalidatingFetchers] = getMatchesToLoad(init.history, state, matches, submission, nextLocation, isRevalidationRequired, cancelledDeferredRoutes, cancelledFetcherLoads, {
2151
2201
  [match.route.id]: actionResult.data
2152
2202
  }, undefined, // No need to send through errors since we short circuit above
2153
2203
  fetchLoadMatches); // Put all revalidating fetchers into the loading state, except for the
@@ -2260,9 +2310,9 @@ function createRouter(init) {
2260
2310
  }); // Call the loader for this fetcher route match
2261
2311
 
2262
2312
  let abortController = new AbortController();
2263
- let fetchRequest = createClientSideRequest(path, abortController.signal);
2313
+ let fetchRequest = createClientSideRequest(init.history, path, abortController.signal);
2264
2314
  fetchControllers.set(key, abortController);
2265
- let result = await callLoaderOrAction("loader", fetchRequest, match, matches, router.basename); // Deferred isn't supported or fetcher loads, await everything and treat it
2315
+ let result = await callLoaderOrAction("loader", fetchRequest, match, matches, router.basename); // Deferred isn't supported for fetcher loads, await everything and treat it
2266
2316
  // as a normal load. resolveDeferredData will return undefined if this
2267
2317
  // fetcher gets aborted, so we just leave result untouched and short circuit
2268
2318
  // below if that happens
@@ -2362,7 +2412,7 @@ function createRouter(init) {
2362
2412
  invariant(redirectLocation, "Expected a location on the redirect navigation"); // Check if this an external redirect that goes to a new origin
2363
2413
 
2364
2414
  if (typeof ((_window = window) == null ? void 0 : _window.location) !== "undefined") {
2365
- let newOrigin = createClientSideURL(redirect.location).origin;
2415
+ let newOrigin = init.history.createURL(redirect.location).origin;
2366
2416
 
2367
2417
  if (window.location.origin !== newOrigin) {
2368
2418
  if (replace) {
@@ -2404,7 +2454,9 @@ function createRouter(init) {
2404
2454
  await startNavigation(redirectHistoryAction, redirectLocation, {
2405
2455
  submission: _extends({}, submission, {
2406
2456
  formAction: redirect.location
2407
- })
2457
+ }),
2458
+ // Preserve this flag across redirects
2459
+ preventScrollReset: pendingPreventScrollReset
2408
2460
  });
2409
2461
  } else {
2410
2462
  // Otherwise, we kick off a new loading navigation, preserving the
@@ -2417,7 +2469,9 @@ function createRouter(init) {
2417
2469
  formAction: submission ? submission.formAction : undefined,
2418
2470
  formEncType: submission ? submission.formEncType : undefined,
2419
2471
  formData: submission ? submission.formData : undefined
2420
- }
2472
+ },
2473
+ // Preserve this flag across redirects
2474
+ preventScrollReset: pendingPreventScrollReset
2421
2475
  });
2422
2476
  }
2423
2477
  }
@@ -2428,7 +2482,7 @@ function createRouter(init) {
2428
2482
  // accordingly
2429
2483
  let results = await Promise.all([...matchesToLoad.map(match => callLoaderOrAction("loader", request, match, matches, router.basename)), ...fetchersToLoad.map(_ref8 => {
2430
2484
  let [, href, match, fetchMatches] = _ref8;
2431
- return callLoaderOrAction("loader", createClientSideRequest(href, request.signal), match, fetchMatches, router.basename);
2485
+ return callLoaderOrAction("loader", createClientSideRequest(init.history, href, request.signal), match, fetchMatches, router.basename);
2432
2486
  })]);
2433
2487
  let loaderResults = results.slice(0, matchesToLoad.length);
2434
2488
  let fetcherResults = results.slice(matchesToLoad.length);
@@ -2637,6 +2691,7 @@ function createRouter(init) {
2637
2691
  //#region createStaticHandler
2638
2692
  ////////////////////////////////////////////////////////////////////////////////
2639
2693
 
2694
+ const UNSAFE_DEFERRED_SYMBOL = Symbol("deferred");
2640
2695
  function createStaticHandler(routes, opts) {
2641
2696
  invariant(routes.length > 0, "You must provide a non-empty routes array to createStaticHandler");
2642
2697
  let dataRoutes = convertRoutesToDataRoutes(routes);
@@ -2689,7 +2744,8 @@ function createStaticHandler(routes, opts) {
2689
2744
  },
2690
2745
  statusCode: error.status,
2691
2746
  loaderHeaders: {},
2692
- actionHeaders: {}
2747
+ actionHeaders: {},
2748
+ activeDeferreds: null
2693
2749
  };
2694
2750
  } else if (!matches) {
2695
2751
  let error = getInternalRouterError(404, {
@@ -2710,7 +2766,8 @@ function createStaticHandler(routes, opts) {
2710
2766
  },
2711
2767
  statusCode: error.status,
2712
2768
  loaderHeaders: {},
2713
- actionHeaders: {}
2769
+ actionHeaders: {},
2770
+ activeDeferreds: null
2714
2771
  };
2715
2772
  }
2716
2773
 
@@ -2801,8 +2858,23 @@ function createStaticHandler(routes, opts) {
2801
2858
  } // Pick off the right state value to return
2802
2859
 
2803
2860
 
2804
- let routeData = [result.actionData, result.loaderData].find(v => v);
2805
- return Object.values(routeData || {})[0];
2861
+ if (result.actionData) {
2862
+ return Object.values(result.actionData)[0];
2863
+ }
2864
+
2865
+ if (result.loaderData) {
2866
+ var _result$activeDeferre;
2867
+
2868
+ let data = Object.values(result.loaderData)[0];
2869
+
2870
+ if ((_result$activeDeferre = result.activeDeferreds) != null && _result$activeDeferre[match.route.id]) {
2871
+ data[UNSAFE_DEFERRED_SYMBOL] = result.activeDeferreds[match.route.id];
2872
+ }
2873
+
2874
+ return data;
2875
+ }
2876
+
2877
+ return undefined;
2806
2878
  }
2807
2879
 
2808
2880
  async function queryImpl(request, location, matches, requestContext, routeMatch) {
@@ -2882,7 +2954,18 @@ function createStaticHandler(routes, opts) {
2882
2954
  }
2883
2955
 
2884
2956
  if (isDeferredResult(result)) {
2885
- throw new Error("defer() is not supported in actions");
2957
+ let error = getInternalRouterError(400, {
2958
+ type: "defer-action"
2959
+ });
2960
+
2961
+ if (isRouteRequest) {
2962
+ throw error;
2963
+ }
2964
+
2965
+ result = {
2966
+ type: ResultType.error,
2967
+ error
2968
+ };
2886
2969
  }
2887
2970
 
2888
2971
  if (isRouteRequest) {
@@ -2903,7 +2986,8 @@ function createStaticHandler(routes, opts) {
2903
2986
  // return the raw Response or value
2904
2987
  statusCode: 200,
2905
2988
  loaderHeaders: {},
2906
- actionHeaders: {}
2989
+ actionHeaders: {},
2990
+ activeDeferreds: null
2907
2991
  };
2908
2992
  }
2909
2993
 
@@ -2966,7 +3050,8 @@ function createStaticHandler(routes, opts) {
2966
3050
  }), {}),
2967
3051
  errors: pendingActionError || null,
2968
3052
  statusCode: 200,
2969
- loaderHeaders: {}
3053
+ loaderHeaders: {},
3054
+ activeDeferreds: null
2970
3055
  };
2971
3056
  }
2972
3057
 
@@ -2975,27 +3060,21 @@ function createStaticHandler(routes, opts) {
2975
3060
  if (request.signal.aborted) {
2976
3061
  let method = isRouteRequest ? "queryRoute" : "query";
2977
3062
  throw new Error(method + "() call aborted");
2978
- }
2979
-
2980
- let executedLoaders = new Set();
2981
- results.forEach((result, i) => {
2982
- executedLoaders.add(matchesToLoad[i].route.id); // Can't do anything with these without the Remix side of things, so just
2983
- // cancel them for now
3063
+ } // Process and commit output from loaders
2984
3064
 
2985
- if (isDeferredResult(result)) {
2986
- result.deferredData.cancel();
2987
- }
2988
- }); // Process and commit output from loaders
2989
3065
 
2990
- let context = processRouteLoaderData(matches, matchesToLoad, results, pendingActionError); // Add a null for any non-loader matches for proper revalidation on the client
3066
+ let activeDeferreds = new Map();
3067
+ let context = processRouteLoaderData(matches, matchesToLoad, results, pendingActionError, activeDeferreds); // Add a null for any non-loader matches for proper revalidation on the client
2991
3068
 
3069
+ let executedLoaders = new Set(matchesToLoad.map(match => match.route.id));
2992
3070
  matches.forEach(match => {
2993
3071
  if (!executedLoaders.has(match.route.id)) {
2994
3072
  context.loaderData[match.route.id] = null;
2995
3073
  }
2996
3074
  });
2997
3075
  return _extends({}, context, {
2998
- matches
3076
+ matches,
3077
+ activeDeferreds: activeDeferreds.size > 0 ? Object.fromEntries(activeDeferreds.entries()) : null
2999
3078
  });
3000
3079
  }
3001
3080
 
@@ -3114,13 +3193,13 @@ function getLoaderMatchesUntilBoundary(matches, boundaryId) {
3114
3193
  return boundaryMatches;
3115
3194
  }
3116
3195
 
3117
- function getMatchesToLoad(state, matches, submission, location, isRevalidationRequired, cancelledDeferredRoutes, cancelledFetcherLoads, pendingActionData, pendingError, fetchLoadMatches) {
3196
+ function getMatchesToLoad(history, state, matches, submission, location, isRevalidationRequired, cancelledDeferredRoutes, cancelledFetcherLoads, pendingActionData, pendingError, fetchLoadMatches) {
3118
3197
  let actionResult = pendingError ? Object.values(pendingError)[0] : pendingActionData ? Object.values(pendingActionData)[0] : undefined; // Pick navigation matches that are net-new or qualify for revalidation
3119
3198
 
3120
3199
  let boundaryId = pendingError ? Object.keys(pendingError)[0] : undefined;
3121
3200
  let boundaryMatches = getLoaderMatchesUntilBoundary(matches, boundaryId);
3122
3201
  let navigationMatches = boundaryMatches.filter((match, index) => match.route.loader != null && (isNewLoader(state.loaderData, state.matches[index], match) || // If this route had a pending deferred cancelled it must be revalidated
3123
- cancelledDeferredRoutes.some(id => id === match.route.id) || shouldRevalidateLoader(state.location, state.matches[index], submission, location, match, isRevalidationRequired, actionResult))); // Pick fetcher.loads that need to be revalidated
3202
+ cancelledDeferredRoutes.some(id => id === match.route.id) || shouldRevalidateLoader(history, state.location, state.matches[index], submission, location, match, isRevalidationRequired, actionResult))); // Pick fetcher.loads that need to be revalidated
3124
3203
 
3125
3204
  let revalidatingFetchers = [];
3126
3205
  fetchLoadMatches && fetchLoadMatches.forEach((_ref10, key) => {
@@ -3130,7 +3209,7 @@ function getMatchesToLoad(state, matches, submission, location, isRevalidationRe
3130
3209
  if (cancelledFetcherLoads.includes(key)) {
3131
3210
  revalidatingFetchers.push([key, href, match, fetchMatches]);
3132
3211
  } else if (isRevalidationRequired) {
3133
- let shouldRevalidate = shouldRevalidateLoader(href, match, submission, href, match, isRevalidationRequired, actionResult);
3212
+ let shouldRevalidate = shouldRevalidateLoader(history, href, match, submission, href, match, isRevalidationRequired, actionResult);
3134
3213
 
3135
3214
  if (shouldRevalidate) {
3136
3215
  revalidatingFetchers.push([key, href, match, fetchMatches]);
@@ -3160,10 +3239,10 @@ function isNewRouteInstance(currentMatch, match) {
3160
3239
  );
3161
3240
  }
3162
3241
 
3163
- function shouldRevalidateLoader(currentLocation, currentMatch, submission, location, match, isRevalidationRequired, actionResult) {
3164
- let currentUrl = createClientSideURL(currentLocation);
3242
+ function shouldRevalidateLoader(history, currentLocation, currentMatch, submission, location, match, isRevalidationRequired, actionResult) {
3243
+ let currentUrl = history.createURL(currentLocation);
3165
3244
  let currentParams = currentMatch.params;
3166
- let nextUrl = createClientSideURL(location);
3245
+ let nextUrl = history.createURL(location);
3167
3246
  let nextParams = match.params; // This is the default implementation as to when we revalidate. If the route
3168
3247
  // provides it's own implementation, then we give them full control but
3169
3248
  // provide this value so they can leverage it if needed after they check
@@ -3240,7 +3319,7 @@ async function callLoaderOrAction(type, request, match, matches, basename, isSta
3240
3319
  if (redirectStatusCodes.has(status)) {
3241
3320
  let location = result.headers.get("Location");
3242
3321
  invariant(location, "Redirects returned/thrown from loaders/actions must have a Location header");
3243
- let isAbsolute = /^[a-z+]+:\/\//i.test(location) || location.startsWith("//"); // Support relative routing in internal redirects
3322
+ let isAbsolute = /^(?:[a-z][a-z0-9+.-]*:|\/\/)/i.test(location); // Support relative routing in internal redirects
3244
3323
 
3245
3324
  if (!isAbsolute) {
3246
3325
  let activeMatches = matches.slice(0, matches.indexOf(match) + 1);
@@ -3333,8 +3412,8 @@ async function callLoaderOrAction(type, request, match, matches, basename, isSta
3333
3412
  // Request instance from the static handler (query/queryRoute)
3334
3413
 
3335
3414
 
3336
- function createClientSideRequest(location, signal, submission) {
3337
- let url = createClientSideURL(stripHashFromPath(location)).toString();
3415
+ function createClientSideRequest(history, location, signal, submission) {
3416
+ let url = history.createURL(stripHashFromPath(location)).toString();
3338
3417
  let init = {
3339
3418
  signal
3340
3419
  };
@@ -3407,13 +3486,16 @@ function processRouteLoaderData(matches, matchesToLoad, results, pendingError, a
3407
3486
  if (result.headers) {
3408
3487
  loaderHeaders[id] = result.headers;
3409
3488
  }
3410
- } else if (isDeferredResult(result)) {
3411
- activeDeferreds && activeDeferreds.set(id, result.deferredData);
3412
- loaderData[id] = result.deferredData.data; // TODO: Add statusCode/headers once we wire up streaming in Remix
3413
3489
  } else {
3414
- loaderData[id] = result.data; // Error status codes always override success status codes, but if all
3490
+ if (isDeferredResult(result)) {
3491
+ activeDeferreds.set(id, result.deferredData);
3492
+ loaderData[id] = result.deferredData.data;
3493
+ } else {
3494
+ loaderData[id] = result.data;
3495
+ } // Error status codes always override success status codes, but if all
3415
3496
  // loaders are successful we take the deepest status code.
3416
3497
 
3498
+
3417
3499
  if (result.statusCode != null && result.statusCode !== 200 && !foundError) {
3418
3500
  statusCode = result.statusCode;
3419
3501
  }
@@ -3463,11 +3545,11 @@ function processLoaderData(state, matches, matchesToLoad, results, pendingError,
3463
3545
  } else if (isRedirectResult(result)) {
3464
3546
  // Should never get here, redirects should get processed above, but we
3465
3547
  // keep this to type narrow to a success result in the else
3466
- throw new Error("Unhandled fetcher revalidation redirect");
3548
+ invariant(false, "Unhandled fetcher revalidation redirect");
3467
3549
  } else if (isDeferredResult(result)) {
3468
3550
  // Should never get here, deferred data should be awaited for fetchers
3469
3551
  // in resolveDeferredResults
3470
- throw new Error("Unhandled fetcher deferred data");
3552
+ invariant(false, "Unhandled fetcher deferred data");
3471
3553
  } else {
3472
3554
  let doneFetcher = {
3473
3555
  state: "idle",
@@ -3539,7 +3621,8 @@ function getInternalRouterError(status, _temp4) {
3539
3621
  let {
3540
3622
  pathname,
3541
3623
  routeId,
3542
- method
3624
+ method,
3625
+ type
3543
3626
  } = _temp4 === void 0 ? {} : _temp4;
3544
3627
  let statusText = "Unknown Server Error";
3545
3628
  let errorMessage = "Unknown @remix-run/router error";
@@ -3549,6 +3632,8 @@ function getInternalRouterError(status, _temp4) {
3549
3632
 
3550
3633
  if (method && pathname && routeId) {
3551
3634
  errorMessage = "You made a " + method + " request to \"" + pathname + "\" but " + ("did not provide a `loader` for route \"" + routeId + "\", ") + "so there is no way to handle the request.";
3635
+ } else if (type === "defer-action") {
3636
+ errorMessage = "defer() is not supported in actions";
3552
3637
  } else {
3553
3638
  errorMessage = "Cannot submit binary form data using GET";
3554
3639
  }
@@ -3722,6 +3807,8 @@ exports.AbortedDeferredError = AbortedDeferredError;
3722
3807
  exports.ErrorResponse = ErrorResponse;
3723
3808
  exports.IDLE_FETCHER = IDLE_FETCHER;
3724
3809
  exports.IDLE_NAVIGATION = IDLE_NAVIGATION;
3810
+ exports.UNSAFE_DEFERRED_SYMBOL = UNSAFE_DEFERRED_SYMBOL;
3811
+ exports.UNSAFE_DeferredData = DeferredData;
3725
3812
  exports.UNSAFE_convertRoutesToDataRoutes = convertRoutesToDataRoutes;
3726
3813
  exports.UNSAFE_getPathContributingMatches = getPathContributingMatches;
3727
3814
  exports.createBrowserHistory = createBrowserHistory;