@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
  *
@@ -98,6 +98,10 @@
98
98
  return location;
99
99
  }
100
100
 
101
+ function createHref(to) {
102
+ return typeof to === "string" ? to : createPath(to);
103
+ }
104
+
101
105
  let history = {
102
106
  get index() {
103
107
  return index;
@@ -111,8 +115,10 @@
111
115
  return getCurrentLocation();
112
116
  },
113
117
 
114
- createHref(to) {
115
- return typeof to === "string" ? to : createPath(to);
118
+ createHref,
119
+
120
+ createURL(to) {
121
+ return new URL(createHref(to), "http://localhost");
116
122
  },
117
123
 
118
124
  encodeLocation(to) {
@@ -391,15 +397,6 @@
391
397
 
392
398
  return parsedPath;
393
399
  }
394
- function createClientSideURL(location) {
395
- // window.location.origin is "null" (the literal string value) in Firefox
396
- // under certain conditions, notably when serving from a local HTML file
397
- // See https://bugzilla.mozilla.org/show_bug.cgi?id=878297
398
- let base = typeof window !== "undefined" && typeof window.location !== "undefined" && window.location.origin !== "null" ? window.location.origin : window.location.href;
399
- let href = typeof location === "string" ? location : createPath(location);
400
- invariant(base, "No window.location.(origin|href) available to create URL for href: " + href);
401
- return new URL(href, base);
402
- }
403
400
 
404
401
  function getUrlBasedHistory(getLocation, createHref, validateLocation, options) {
405
402
  if (options === void 0) {
@@ -464,6 +461,16 @@
464
461
  }
465
462
  }
466
463
 
464
+ function createURL(to) {
465
+ // window.location.origin is "null" (the literal string value) in Firefox
466
+ // under certain conditions, notably when serving from a local HTML file
467
+ // See https://bugzilla.mozilla.org/show_bug.cgi?id=878297
468
+ let base = window.location.origin !== "null" ? window.location.origin : window.location.href;
469
+ let href = typeof to === "string" ? to : createPath(to);
470
+ invariant(base, "No window.location.(origin|href) available to create URL for href: " + href);
471
+ return new URL(href, base);
472
+ }
473
+
467
474
  let history = {
468
475
  get action() {
469
476
  return action;
@@ -490,9 +497,11 @@
490
497
  return createHref(window, to);
491
498
  },
492
499
 
500
+ createURL,
501
+
493
502
  encodeLocation(to) {
494
503
  // Encode a Location the same way window.location would
495
- let url = createClientSideURL(typeof to === "string" ? to : createPath(to));
504
+ let url = createURL(to);
496
505
  return {
497
506
  pathname: url.pathname,
498
507
  search: url.search,
@@ -809,13 +818,32 @@
809
818
  path = path.replace(/\*$/, "/*");
810
819
  }
811
820
 
812
- return path.replace(/^:(\w+)/g, (_, key) => {
813
- invariant(params[key] != null, "Missing \":" + key + "\" param");
814
- return params[key];
815
- }).replace(/\/:(\w+)/g, (_, key) => {
816
- invariant(params[key] != null, "Missing \":" + key + "\" param");
817
- return "/" + params[key];
818
- }).replace(/(\/?)\*/, (_, prefix, __, str) => {
821
+ return path.replace(/^:(\w+)(\??)/g, (_, key, optional) => {
822
+ let param = params[key];
823
+
824
+ if (optional === "?") {
825
+ return param == null ? "" : param;
826
+ }
827
+
828
+ if (param == null) {
829
+ invariant(false, "Missing \":" + key + "\" param");
830
+ }
831
+
832
+ return param;
833
+ }).replace(/\/:(\w+)(\??)/g, (_, key, optional) => {
834
+ let param = params[key];
835
+
836
+ if (optional === "?") {
837
+ return param == null ? "" : "/" + param;
838
+ }
839
+
840
+ if (param == null) {
841
+ invariant(false, "Missing \":" + key + "\" param");
842
+ }
843
+
844
+ return "/" + param;
845
+ }) // Remove any optional markers from optional static segments
846
+ .replace(/\?/g, "").replace(/(\/?)\*/, (_, prefix, __, str) => {
819
847
  const star = "*";
820
848
 
821
849
  if (params[star] == null) {
@@ -1162,9 +1190,10 @@
1162
1190
  };
1163
1191
  class AbortedDeferredError extends Error {}
1164
1192
  class DeferredData {
1165
- constructor(data) {
1166
- this.pendingKeys = new Set();
1167
- this.subscriber = undefined;
1193
+ constructor(data, responseInit) {
1194
+ this.pendingKeysSet = new Set();
1195
+ this.subscribers = new Set();
1196
+ this.deferredKeys = [];
1168
1197
  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
1169
1198
  // cancellation
1170
1199
 
@@ -1183,6 +1212,7 @@
1183
1212
  [key]: this.trackPromise(key, value)
1184
1213
  });
1185
1214
  }, {});
1215
+ this.init = responseInit;
1186
1216
  }
1187
1217
 
1188
1218
  trackPromise(key, value) {
@@ -1190,7 +1220,8 @@
1190
1220
  return value;
1191
1221
  }
1192
1222
 
1193
- this.pendingKeys.add(key); // We store a little wrapper promise that will be extended with
1223
+ this.deferredKeys.push(key);
1224
+ this.pendingKeysSet.add(key); // We store a little wrapper promise that will be extended with
1194
1225
  // _data/_error props upon resolve/reject
1195
1226
 
1196
1227
  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
@@ -1212,39 +1243,41 @@
1212
1243
  return Promise.reject(error);
1213
1244
  }
1214
1245
 
1215
- this.pendingKeys.delete(key);
1246
+ this.pendingKeysSet.delete(key);
1216
1247
 
1217
1248
  if (this.done) {
1218
1249
  // Nothing left to abort!
1219
1250
  this.unlistenAbortSignal();
1220
1251
  }
1221
1252
 
1222
- const subscriber = this.subscriber;
1223
-
1224
1253
  if (error) {
1225
1254
  Object.defineProperty(promise, "_error", {
1226
1255
  get: () => error
1227
1256
  });
1228
- subscriber && subscriber(false);
1257
+ this.emit(false, key);
1229
1258
  return Promise.reject(error);
1230
1259
  }
1231
1260
 
1232
1261
  Object.defineProperty(promise, "_data", {
1233
1262
  get: () => data
1234
1263
  });
1235
- subscriber && subscriber(false);
1264
+ this.emit(false, key);
1236
1265
  return data;
1237
1266
  }
1238
1267
 
1268
+ emit(aborted, settledKey) {
1269
+ this.subscribers.forEach(subscriber => subscriber(aborted, settledKey));
1270
+ }
1271
+
1239
1272
  subscribe(fn) {
1240
- this.subscriber = fn;
1273
+ this.subscribers.add(fn);
1274
+ return () => this.subscribers.delete(fn);
1241
1275
  }
1242
1276
 
1243
1277
  cancel() {
1244
1278
  this.controller.abort();
1245
- this.pendingKeys.forEach((v, k) => this.pendingKeys.delete(k));
1246
- let subscriber = this.subscriber;
1247
- subscriber && subscriber(true);
1279
+ this.pendingKeysSet.forEach((v, k) => this.pendingKeysSet.delete(k));
1280
+ this.emit(true);
1248
1281
  }
1249
1282
 
1250
1283
  async resolveData(signal) {
@@ -1269,7 +1302,7 @@
1269
1302
  }
1270
1303
 
1271
1304
  get done() {
1272
- return this.pendingKeys.size === 0;
1305
+ return this.pendingKeysSet.size === 0;
1273
1306
  }
1274
1307
 
1275
1308
  get unwrappedData() {
@@ -1282,6 +1315,10 @@
1282
1315
  }, {});
1283
1316
  }
1284
1317
 
1318
+ get pendingKeys() {
1319
+ return Array.from(this.pendingKeysSet);
1320
+ }
1321
+
1285
1322
  }
1286
1323
 
1287
1324
  function isTrackedPromise(value) {
@@ -1300,9 +1337,16 @@
1300
1337
  return value._data;
1301
1338
  }
1302
1339
 
1303
- function defer(data) {
1304
- return new DeferredData(data);
1305
- }
1340
+ const defer = function defer(data, init) {
1341
+ if (init === void 0) {
1342
+ init = {};
1343
+ }
1344
+
1345
+ let responseInit = typeof init === "number" ? {
1346
+ status: init
1347
+ } : init;
1348
+ return new DeferredData(data, responseInit);
1349
+ };
1306
1350
 
1307
1351
  /**
1308
1352
  * A redirect response. Sets the status code and the `Location` header.
@@ -1548,7 +1592,7 @@
1548
1592
 
1549
1593
 
1550
1594
  function completeNavigation(location, newState) {
1551
- var _location$state;
1595
+ var _location$state, _location$state2;
1552
1596
 
1553
1597
  // Deduce if we're in a loading/actionReload state:
1554
1598
  // - We have committed actionData in the store
@@ -1574,7 +1618,10 @@
1574
1618
  } // Always preserve any existing loaderData from re-used routes
1575
1619
 
1576
1620
 
1577
- let loaderData = newState.loaderData ? mergeLoaderData(state.loaderData, newState.loaderData, newState.matches || [], newState.errors) : state.loaderData;
1621
+ 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
1622
+ // submission navigations unless they redirect
1623
+
1624
+ let preventScrollReset = pendingPreventScrollReset === true || state.navigation.formMethod != null && isMutationMethod(state.navigation.formMethod) && ((_location$state2 = location.state) == null ? void 0 : _location$state2._isRedirect) !== true;
1578
1625
  updateState(_extends({}, newState, {
1579
1626
  // matches, errors, fetchers go through as-is
1580
1627
  actionData,
@@ -1584,9 +1631,8 @@
1584
1631
  initialized: true,
1585
1632
  navigation: IDLE_NAVIGATION,
1586
1633
  revalidation: "idle",
1587
- // Don't restore on submission navigations
1588
- restoreScrollPosition: state.navigation.formData ? false : getSavedScrollPosition(location, newState.matches || state.matches),
1589
- preventScrollReset: pendingPreventScrollReset
1634
+ restoreScrollPosition: getSavedScrollPosition(location, newState.matches || state.matches),
1635
+ preventScrollReset
1590
1636
  }));
1591
1637
 
1592
1638
  if (isUninterruptedRevalidation) ; else if (pendingAction === exports.Action.Pop) ; else if (pendingAction === exports.Action.Push) {
@@ -1728,7 +1774,7 @@
1728
1774
 
1729
1775
 
1730
1776
  pendingNavigationController = new AbortController();
1731
- let request = createClientSideRequest(location, pendingNavigationController.signal, opts && opts.submission);
1777
+ let request = createClientSideRequest(init.history, location, pendingNavigationController.signal, opts && opts.submission);
1732
1778
  let pendingActionData;
1733
1779
  let pendingError;
1734
1780
 
@@ -1869,7 +1915,9 @@
1869
1915
  }
1870
1916
 
1871
1917
  if (isDeferredResult(result)) {
1872
- throw new Error("defer() is not supported in actions");
1918
+ throw getInternalRouterError(400, {
1919
+ type: "defer-action"
1920
+ });
1873
1921
  }
1874
1922
 
1875
1923
  return {
@@ -1906,7 +1954,7 @@
1906
1954
  formData: loadingNavigation.formData,
1907
1955
  formEncType: loadingNavigation.formEncType
1908
1956
  } : undefined;
1909
- 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
1957
+ 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
1910
1958
  // about to reload. Note that if this is an action reload we would have
1911
1959
  // already cancelled all pending deferreds so this would be a no-op
1912
1960
 
@@ -2088,7 +2136,7 @@
2088
2136
  }); // Call the action for the fetcher
2089
2137
 
2090
2138
  let abortController = new AbortController();
2091
- let fetchRequest = createClientSideRequest(path, abortController.signal, submission);
2139
+ let fetchRequest = createClientSideRequest(init.history, path, abortController.signal, submission);
2092
2140
  fetchControllers.set(key, abortController);
2093
2141
  let actionResult = await callLoaderOrAction("action", fetchRequest, match, requestMatches, router.basename);
2094
2142
 
@@ -2129,13 +2177,15 @@
2129
2177
  }
2130
2178
 
2131
2179
  if (isDeferredResult(actionResult)) {
2132
- invariant(false, "defer() is not supported in actions");
2180
+ throw getInternalRouterError(400, {
2181
+ type: "defer-action"
2182
+ });
2133
2183
  } // Start the data load for current matches, or the next location if we're
2134
2184
  // in the middle of a navigation
2135
2185
 
2136
2186
 
2137
2187
  let nextLocation = state.navigation.location || state.location;
2138
- let revalidationRequest = createClientSideRequest(nextLocation, abortController.signal);
2188
+ let revalidationRequest = createClientSideRequest(init.history, nextLocation, abortController.signal);
2139
2189
  let matches = state.navigation.state !== "idle" ? matchRoutes(dataRoutes, state.navigation.location, init.basename) : state.matches;
2140
2190
  invariant(matches, "Didn't find any matches after fetcher action");
2141
2191
  let loadId = ++incrementingLoadId;
@@ -2149,7 +2199,7 @@
2149
2199
  });
2150
2200
 
2151
2201
  state.fetchers.set(key, loadFetcher);
2152
- let [matchesToLoad, revalidatingFetchers] = getMatchesToLoad(state, matches, submission, nextLocation, isRevalidationRequired, cancelledDeferredRoutes, cancelledFetcherLoads, {
2202
+ let [matchesToLoad, revalidatingFetchers] = getMatchesToLoad(init.history, state, matches, submission, nextLocation, isRevalidationRequired, cancelledDeferredRoutes, cancelledFetcherLoads, {
2153
2203
  [match.route.id]: actionResult.data
2154
2204
  }, undefined, // No need to send through errors since we short circuit above
2155
2205
  fetchLoadMatches); // Put all revalidating fetchers into the loading state, except for the
@@ -2262,9 +2312,9 @@
2262
2312
  }); // Call the loader for this fetcher route match
2263
2313
 
2264
2314
  let abortController = new AbortController();
2265
- let fetchRequest = createClientSideRequest(path, abortController.signal);
2315
+ let fetchRequest = createClientSideRequest(init.history, path, abortController.signal);
2266
2316
  fetchControllers.set(key, abortController);
2267
- let result = await callLoaderOrAction("loader", fetchRequest, match, matches, router.basename); // Deferred isn't supported or fetcher loads, await everything and treat it
2317
+ let result = await callLoaderOrAction("loader", fetchRequest, match, matches, router.basename); // Deferred isn't supported for fetcher loads, await everything and treat it
2268
2318
  // as a normal load. resolveDeferredData will return undefined if this
2269
2319
  // fetcher gets aborted, so we just leave result untouched and short circuit
2270
2320
  // below if that happens
@@ -2364,7 +2414,7 @@
2364
2414
  invariant(redirectLocation, "Expected a location on the redirect navigation"); // Check if this an external redirect that goes to a new origin
2365
2415
 
2366
2416
  if (typeof ((_window = window) == null ? void 0 : _window.location) !== "undefined") {
2367
- let newOrigin = createClientSideURL(redirect.location).origin;
2417
+ let newOrigin = init.history.createURL(redirect.location).origin;
2368
2418
 
2369
2419
  if (window.location.origin !== newOrigin) {
2370
2420
  if (replace) {
@@ -2406,7 +2456,9 @@
2406
2456
  await startNavigation(redirectHistoryAction, redirectLocation, {
2407
2457
  submission: _extends({}, submission, {
2408
2458
  formAction: redirect.location
2409
- })
2459
+ }),
2460
+ // Preserve this flag across redirects
2461
+ preventScrollReset: pendingPreventScrollReset
2410
2462
  });
2411
2463
  } else {
2412
2464
  // Otherwise, we kick off a new loading navigation, preserving the
@@ -2419,7 +2471,9 @@
2419
2471
  formAction: submission ? submission.formAction : undefined,
2420
2472
  formEncType: submission ? submission.formEncType : undefined,
2421
2473
  formData: submission ? submission.formData : undefined
2422
- }
2474
+ },
2475
+ // Preserve this flag across redirects
2476
+ preventScrollReset: pendingPreventScrollReset
2423
2477
  });
2424
2478
  }
2425
2479
  }
@@ -2430,7 +2484,7 @@
2430
2484
  // accordingly
2431
2485
  let results = await Promise.all([...matchesToLoad.map(match => callLoaderOrAction("loader", request, match, matches, router.basename)), ...fetchersToLoad.map(_ref8 => {
2432
2486
  let [, href, match, fetchMatches] = _ref8;
2433
- return callLoaderOrAction("loader", createClientSideRequest(href, request.signal), match, fetchMatches, router.basename);
2487
+ return callLoaderOrAction("loader", createClientSideRequest(init.history, href, request.signal), match, fetchMatches, router.basename);
2434
2488
  })]);
2435
2489
  let loaderResults = results.slice(0, matchesToLoad.length);
2436
2490
  let fetcherResults = results.slice(matchesToLoad.length);
@@ -2639,6 +2693,7 @@
2639
2693
  //#region createStaticHandler
2640
2694
  ////////////////////////////////////////////////////////////////////////////////
2641
2695
 
2696
+ const UNSAFE_DEFERRED_SYMBOL = Symbol("deferred");
2642
2697
  function createStaticHandler(routes, opts) {
2643
2698
  invariant(routes.length > 0, "You must provide a non-empty routes array to createStaticHandler");
2644
2699
  let dataRoutes = convertRoutesToDataRoutes(routes);
@@ -2691,7 +2746,8 @@
2691
2746
  },
2692
2747
  statusCode: error.status,
2693
2748
  loaderHeaders: {},
2694
- actionHeaders: {}
2749
+ actionHeaders: {},
2750
+ activeDeferreds: null
2695
2751
  };
2696
2752
  } else if (!matches) {
2697
2753
  let error = getInternalRouterError(404, {
@@ -2712,7 +2768,8 @@
2712
2768
  },
2713
2769
  statusCode: error.status,
2714
2770
  loaderHeaders: {},
2715
- actionHeaders: {}
2771
+ actionHeaders: {},
2772
+ activeDeferreds: null
2716
2773
  };
2717
2774
  }
2718
2775
 
@@ -2803,8 +2860,23 @@
2803
2860
  } // Pick off the right state value to return
2804
2861
 
2805
2862
 
2806
- let routeData = [result.actionData, result.loaderData].find(v => v);
2807
- return Object.values(routeData || {})[0];
2863
+ if (result.actionData) {
2864
+ return Object.values(result.actionData)[0];
2865
+ }
2866
+
2867
+ if (result.loaderData) {
2868
+ var _result$activeDeferre;
2869
+
2870
+ let data = Object.values(result.loaderData)[0];
2871
+
2872
+ if ((_result$activeDeferre = result.activeDeferreds) != null && _result$activeDeferre[match.route.id]) {
2873
+ data[UNSAFE_DEFERRED_SYMBOL] = result.activeDeferreds[match.route.id];
2874
+ }
2875
+
2876
+ return data;
2877
+ }
2878
+
2879
+ return undefined;
2808
2880
  }
2809
2881
 
2810
2882
  async function queryImpl(request, location, matches, requestContext, routeMatch) {
@@ -2884,7 +2956,18 @@
2884
2956
  }
2885
2957
 
2886
2958
  if (isDeferredResult(result)) {
2887
- throw new Error("defer() is not supported in actions");
2959
+ let error = getInternalRouterError(400, {
2960
+ type: "defer-action"
2961
+ });
2962
+
2963
+ if (isRouteRequest) {
2964
+ throw error;
2965
+ }
2966
+
2967
+ result = {
2968
+ type: ResultType.error,
2969
+ error
2970
+ };
2888
2971
  }
2889
2972
 
2890
2973
  if (isRouteRequest) {
@@ -2905,7 +2988,8 @@
2905
2988
  // return the raw Response or value
2906
2989
  statusCode: 200,
2907
2990
  loaderHeaders: {},
2908
- actionHeaders: {}
2991
+ actionHeaders: {},
2992
+ activeDeferreds: null
2909
2993
  };
2910
2994
  }
2911
2995
 
@@ -2968,7 +3052,8 @@
2968
3052
  }), {}),
2969
3053
  errors: pendingActionError || null,
2970
3054
  statusCode: 200,
2971
- loaderHeaders: {}
3055
+ loaderHeaders: {},
3056
+ activeDeferreds: null
2972
3057
  };
2973
3058
  }
2974
3059
 
@@ -2977,27 +3062,21 @@
2977
3062
  if (request.signal.aborted) {
2978
3063
  let method = isRouteRequest ? "queryRoute" : "query";
2979
3064
  throw new Error(method + "() call aborted");
2980
- }
2981
-
2982
- let executedLoaders = new Set();
2983
- results.forEach((result, i) => {
2984
- executedLoaders.add(matchesToLoad[i].route.id); // Can't do anything with these without the Remix side of things, so just
2985
- // cancel them for now
3065
+ } // Process and commit output from loaders
2986
3066
 
2987
- if (isDeferredResult(result)) {
2988
- result.deferredData.cancel();
2989
- }
2990
- }); // Process and commit output from loaders
2991
3067
 
2992
- let context = processRouteLoaderData(matches, matchesToLoad, results, pendingActionError); // Add a null for any non-loader matches for proper revalidation on the client
3068
+ let activeDeferreds = new Map();
3069
+ let context = processRouteLoaderData(matches, matchesToLoad, results, pendingActionError, activeDeferreds); // Add a null for any non-loader matches for proper revalidation on the client
2993
3070
 
3071
+ let executedLoaders = new Set(matchesToLoad.map(match => match.route.id));
2994
3072
  matches.forEach(match => {
2995
3073
  if (!executedLoaders.has(match.route.id)) {
2996
3074
  context.loaderData[match.route.id] = null;
2997
3075
  }
2998
3076
  });
2999
3077
  return _extends({}, context, {
3000
- matches
3078
+ matches,
3079
+ activeDeferreds: activeDeferreds.size > 0 ? Object.fromEntries(activeDeferreds.entries()) : null
3001
3080
  });
3002
3081
  }
3003
3082
 
@@ -3116,13 +3195,13 @@
3116
3195
  return boundaryMatches;
3117
3196
  }
3118
3197
 
3119
- function getMatchesToLoad(state, matches, submission, location, isRevalidationRequired, cancelledDeferredRoutes, cancelledFetcherLoads, pendingActionData, pendingError, fetchLoadMatches) {
3198
+ function getMatchesToLoad(history, state, matches, submission, location, isRevalidationRequired, cancelledDeferredRoutes, cancelledFetcherLoads, pendingActionData, pendingError, fetchLoadMatches) {
3120
3199
  let actionResult = pendingError ? Object.values(pendingError)[0] : pendingActionData ? Object.values(pendingActionData)[0] : undefined; // Pick navigation matches that are net-new or qualify for revalidation
3121
3200
 
3122
3201
  let boundaryId = pendingError ? Object.keys(pendingError)[0] : undefined;
3123
3202
  let boundaryMatches = getLoaderMatchesUntilBoundary(matches, boundaryId);
3124
3203
  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
3125
- 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
3204
+ 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
3126
3205
 
3127
3206
  let revalidatingFetchers = [];
3128
3207
  fetchLoadMatches && fetchLoadMatches.forEach((_ref10, key) => {
@@ -3132,7 +3211,7 @@
3132
3211
  if (cancelledFetcherLoads.includes(key)) {
3133
3212
  revalidatingFetchers.push([key, href, match, fetchMatches]);
3134
3213
  } else if (isRevalidationRequired) {
3135
- let shouldRevalidate = shouldRevalidateLoader(href, match, submission, href, match, isRevalidationRequired, actionResult);
3214
+ let shouldRevalidate = shouldRevalidateLoader(history, href, match, submission, href, match, isRevalidationRequired, actionResult);
3136
3215
 
3137
3216
  if (shouldRevalidate) {
3138
3217
  revalidatingFetchers.push([key, href, match, fetchMatches]);
@@ -3162,10 +3241,10 @@
3162
3241
  );
3163
3242
  }
3164
3243
 
3165
- function shouldRevalidateLoader(currentLocation, currentMatch, submission, location, match, isRevalidationRequired, actionResult) {
3166
- let currentUrl = createClientSideURL(currentLocation);
3244
+ function shouldRevalidateLoader(history, currentLocation, currentMatch, submission, location, match, isRevalidationRequired, actionResult) {
3245
+ let currentUrl = history.createURL(currentLocation);
3167
3246
  let currentParams = currentMatch.params;
3168
- let nextUrl = createClientSideURL(location);
3247
+ let nextUrl = history.createURL(location);
3169
3248
  let nextParams = match.params; // This is the default implementation as to when we revalidate. If the route
3170
3249
  // provides it's own implementation, then we give them full control but
3171
3250
  // provide this value so they can leverage it if needed after they check
@@ -3242,7 +3321,7 @@
3242
3321
  if (redirectStatusCodes.has(status)) {
3243
3322
  let location = result.headers.get("Location");
3244
3323
  invariant(location, "Redirects returned/thrown from loaders/actions must have a Location header");
3245
- let isAbsolute = /^[a-z+]+:\/\//i.test(location) || location.startsWith("//"); // Support relative routing in internal redirects
3324
+ let isAbsolute = /^(?:[a-z][a-z0-9+.-]*:|\/\/)/i.test(location); // Support relative routing in internal redirects
3246
3325
 
3247
3326
  if (!isAbsolute) {
3248
3327
  let activeMatches = matches.slice(0, matches.indexOf(match) + 1);
@@ -3335,8 +3414,8 @@
3335
3414
  // Request instance from the static handler (query/queryRoute)
3336
3415
 
3337
3416
 
3338
- function createClientSideRequest(location, signal, submission) {
3339
- let url = createClientSideURL(stripHashFromPath(location)).toString();
3417
+ function createClientSideRequest(history, location, signal, submission) {
3418
+ let url = history.createURL(stripHashFromPath(location)).toString();
3340
3419
  let init = {
3341
3420
  signal
3342
3421
  };
@@ -3409,13 +3488,16 @@
3409
3488
  if (result.headers) {
3410
3489
  loaderHeaders[id] = result.headers;
3411
3490
  }
3412
- } else if (isDeferredResult(result)) {
3413
- activeDeferreds && activeDeferreds.set(id, result.deferredData);
3414
- loaderData[id] = result.deferredData.data; // TODO: Add statusCode/headers once we wire up streaming in Remix
3415
3491
  } else {
3416
- loaderData[id] = result.data; // Error status codes always override success status codes, but if all
3492
+ if (isDeferredResult(result)) {
3493
+ activeDeferreds.set(id, result.deferredData);
3494
+ loaderData[id] = result.deferredData.data;
3495
+ } else {
3496
+ loaderData[id] = result.data;
3497
+ } // Error status codes always override success status codes, but if all
3417
3498
  // loaders are successful we take the deepest status code.
3418
3499
 
3500
+
3419
3501
  if (result.statusCode != null && result.statusCode !== 200 && !foundError) {
3420
3502
  statusCode = result.statusCode;
3421
3503
  }
@@ -3465,11 +3547,11 @@
3465
3547
  } else if (isRedirectResult(result)) {
3466
3548
  // Should never get here, redirects should get processed above, but we
3467
3549
  // keep this to type narrow to a success result in the else
3468
- throw new Error("Unhandled fetcher revalidation redirect");
3550
+ invariant(false, "Unhandled fetcher revalidation redirect");
3469
3551
  } else if (isDeferredResult(result)) {
3470
3552
  // Should never get here, deferred data should be awaited for fetchers
3471
3553
  // in resolveDeferredResults
3472
- throw new Error("Unhandled fetcher deferred data");
3554
+ invariant(false, "Unhandled fetcher deferred data");
3473
3555
  } else {
3474
3556
  let doneFetcher = {
3475
3557
  state: "idle",
@@ -3541,7 +3623,8 @@
3541
3623
  let {
3542
3624
  pathname,
3543
3625
  routeId,
3544
- method
3626
+ method,
3627
+ type
3545
3628
  } = _temp4 === void 0 ? {} : _temp4;
3546
3629
  let statusText = "Unknown Server Error";
3547
3630
  let errorMessage = "Unknown @remix-run/router error";
@@ -3551,6 +3634,8 @@
3551
3634
 
3552
3635
  if (method && pathname && routeId) {
3553
3636
  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.";
3637
+ } else if (type === "defer-action") {
3638
+ errorMessage = "defer() is not supported in actions";
3554
3639
  } else {
3555
3640
  errorMessage = "Cannot submit binary form data using GET";
3556
3641
  }
@@ -3724,6 +3809,8 @@
3724
3809
  exports.ErrorResponse = ErrorResponse;
3725
3810
  exports.IDLE_FETCHER = IDLE_FETCHER;
3726
3811
  exports.IDLE_NAVIGATION = IDLE_NAVIGATION;
3812
+ exports.UNSAFE_DEFERRED_SYMBOL = UNSAFE_DEFERRED_SYMBOL;
3813
+ exports.UNSAFE_DeferredData = DeferredData;
3727
3814
  exports.UNSAFE_convertRoutesToDataRoutes = convertRoutesToDataRoutes;
3728
3815
  exports.UNSAFE_getPathContributingMatches = getPathContributingMatches;
3729
3816
  exports.createBrowserHistory = createBrowserHistory;