@remix-run/router 1.7.2 → 1.8.0-pre.0

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/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # `@remix-run/router`
2
2
 
3
+ ## 1.8.0-pre.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Add's a new `redirectDocument()` function which allows users to specify that a redirect from a `loader`/`action` should trigger a document reload (via `window.location`) instead of attempting to navigate to the redirected location via React Router ([#10705](https://github.com/remix-run/react-router/pull/10705))
8
+
9
+ ### Patch Changes
10
+
11
+ - Fix an issue in `queryRoute` that was not always identifying thrown `Response` instances ([#10717](https://github.com/remix-run/react-router/pull/10717))
12
+ - Ensure hash history always includes a leading slash on hash pathnames ([#10753](https://github.com/remix-run/react-router/pull/10753))
13
+
3
14
  ## 1.7.2
4
15
 
5
16
  ### Patch Changes
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export type { ActionFunction, ActionFunctionArgs, AgnosticDataIndexRouteObject, AgnosticDataNonIndexRouteObject, AgnosticDataRouteMatch, AgnosticDataRouteObject, AgnosticIndexRouteObject, AgnosticNonIndexRouteObject, AgnosticRouteMatch, AgnosticRouteObject, LazyRouteFunction, TrackedPromise, FormEncType, FormMethod, HTMLFormMethod, JsonFunction, LoaderFunction, LoaderFunctionArgs, ParamParseKey, Params, PathMatch, PathPattern, RedirectFunction, ShouldRevalidateFunction, V7_FormMethod, } from "./utils";
2
- export { AbortedDeferredError, ErrorResponse, defer, generatePath, getToPathname, isRouteErrorResponse, joinPaths, json, matchPath, matchRoutes, normalizePathname, redirect, resolvePath, resolveTo, stripBasename, } from "./utils";
2
+ export { AbortedDeferredError, ErrorResponse, defer, generatePath, getToPathname, isRouteErrorResponse, joinPaths, json, matchPath, matchRoutes, normalizePathname, redirect, redirectDocument, resolvePath, resolveTo, stripBasename, } from "./utils";
3
3
  export type { BrowserHistory, BrowserHistoryOptions, HashHistory, HashHistoryOptions, History, InitialEntry, Location, MemoryHistory, MemoryHistoryOptions, Path, To, } from "./history";
4
4
  export { Action, createBrowserHistory, createPath, createHashHistory, createMemoryHistory, parsePath, } from "./history";
5
5
  export * from "./router";
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @remix-run/router v1.7.2
2
+ * @remix-run/router v1.8.0-pre.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -34,31 +34,36 @@ function _extends() {
34
34
  /**
35
35
  * Actions represent the type of change to a location value.
36
36
  */
37
- exports.Action = void 0;
37
+ let Action = /*#__PURE__*/function (Action) {
38
+ Action["Pop"] = "POP";
39
+ Action["Push"] = "PUSH";
40
+ Action["Replace"] = "REPLACE";
41
+ return Action;
42
+ }({});
38
43
 
39
44
  /**
40
45
  * The pathname, search, and hash values of a URL.
41
46
  */
42
- (function (Action) {
43
- Action["Pop"] = "POP";
44
- Action["Push"] = "PUSH";
45
- Action["Replace"] = "REPLACE";
46
- })(exports.Action || (exports.Action = {}));
47
+
47
48
  /**
48
49
  * An entry in a history stack. A location contains information about the
49
50
  * URL path, as well as possibly some arbitrary state and a key.
50
51
  */
52
+
51
53
  /**
52
54
  * A change to the current location.
53
55
  */
56
+
54
57
  /**
55
58
  * A function that receives notifications about location changes.
56
59
  */
60
+
57
61
  /**
58
62
  * Describes a location that is the destination of some navigation, either via
59
63
  * `history.push` or `history.replace`. May be either a URL or the pieces of a
60
64
  * URL path.
61
65
  */
66
+
62
67
  /**
63
68
  * A history is an interface to the navigation stack. The history serves as the
64
69
  * source of truth for the current location, as well as provides a set of
@@ -67,6 +72,7 @@ exports.Action = void 0;
67
72
  * It is similar to the DOM's `window.history` object, but with a smaller, more
68
73
  * focused API.
69
74
  */
75
+
70
76
  const PopStateEventType = "popstate";
71
77
  //#endregion
72
78
 
@@ -99,7 +105,7 @@ function createMemoryHistory(options) {
99
105
  let entries; // Declare so we can access from createMemoryLocation
100
106
  entries = initialEntries.map((entry, index) => createMemoryLocation(entry, typeof entry === "string" ? null : entry.state, index === 0 ? "default" : undefined));
101
107
  let index = clampIndex(initialIndex == null ? entries.length - 1 : initialIndex);
102
- let action = exports.Action.Pop;
108
+ let action = Action.Pop;
103
109
  let listener = null;
104
110
  function clampIndex(n) {
105
111
  return Math.min(Math.max(n, 0), entries.length - 1);
@@ -141,7 +147,7 @@ function createMemoryHistory(options) {
141
147
  };
142
148
  },
143
149
  push(to, state) {
144
- action = exports.Action.Push;
150
+ action = Action.Push;
145
151
  let nextLocation = createMemoryLocation(to, state);
146
152
  index += 1;
147
153
  entries.splice(index, entries.length, nextLocation);
@@ -154,7 +160,7 @@ function createMemoryHistory(options) {
154
160
  }
155
161
  },
156
162
  replace(to, state) {
157
- action = exports.Action.Replace;
163
+ action = Action.Replace;
158
164
  let nextLocation = createMemoryLocation(to, state);
159
165
  entries[index] = nextLocation;
160
166
  if (v5Compat && listener) {
@@ -166,7 +172,7 @@ function createMemoryHistory(options) {
166
172
  }
167
173
  },
168
174
  go(delta) {
169
- action = exports.Action.Pop;
175
+ action = Action.Pop;
170
176
  let nextIndex = clampIndex(index + delta);
171
177
  let nextLocation = entries[nextIndex];
172
178
  index = nextIndex;
@@ -265,6 +271,16 @@ function createHashHistory(options) {
265
271
  search = "",
266
272
  hash = ""
267
273
  } = parsePath(window.location.hash.substr(1));
274
+
275
+ // Hash URL should always have a leading / just like window.location.pathname
276
+ // does, so if an app ends up at a route like /#something then we add a
277
+ // leading slash so all of our path-matching behaves the same as if it would
278
+ // in a browser router. This is particularly important when there exists a
279
+ // root splat route (<Route path="*">) since that matches internally against
280
+ // "/*" and we'd expect /#something to 404 in a hash router app.
281
+ if (!pathname.startsWith("/") && !pathname.startsWith(".")) {
282
+ pathname = "/" + pathname;
283
+ }
268
284
  return createLocation("", {
269
285
  pathname,
270
286
  search,
@@ -399,7 +415,7 @@ function getUrlBasedHistory(getLocation, createHref, validateLocation, options)
399
415
  v5Compat = false
400
416
  } = options;
401
417
  let globalHistory = window.history;
402
- let action = exports.Action.Pop;
418
+ let action = Action.Pop;
403
419
  let listener = null;
404
420
  let index = getIndex();
405
421
  // Index should only be null when we initialize. If not, it's because the
@@ -418,7 +434,7 @@ function getUrlBasedHistory(getLocation, createHref, validateLocation, options)
418
434
  return state.idx;
419
435
  }
420
436
  function handlePop() {
421
- action = exports.Action.Pop;
437
+ action = Action.Pop;
422
438
  let nextIndex = getIndex();
423
439
  let delta = nextIndex == null ? null : nextIndex - index;
424
440
  index = nextIndex;
@@ -431,7 +447,7 @@ function getUrlBasedHistory(getLocation, createHref, validateLocation, options)
431
447
  }
432
448
  }
433
449
  function push(to, state) {
434
- action = exports.Action.Push;
450
+ action = Action.Push;
435
451
  let location = createLocation(history.location, to, state);
436
452
  if (validateLocation) validateLocation(location, to);
437
453
  index = getIndex() + 1;
@@ -462,7 +478,7 @@ function getUrlBasedHistory(getLocation, createHref, validateLocation, options)
462
478
  }
463
479
  }
464
480
  function replace(to, state) {
465
- action = exports.Action.Replace;
481
+ action = Action.Replace;
466
482
  let location = createLocation(history.location, to, state);
467
483
  if (validateLocation) validateLocation(location, to);
468
484
  index = getIndex();
@@ -532,69 +548,85 @@ function getUrlBasedHistory(getLocation, createHref, validateLocation, options)
532
548
  * Map of routeId -> data returned from a loader/action/error
533
549
  */
534
550
 
535
- let ResultType;
536
-
537
- /**
538
- * Successful result from a loader or action
539
- */
540
- (function (ResultType) {
551
+ let ResultType = /*#__PURE__*/function (ResultType) {
541
552
  ResultType["data"] = "data";
542
553
  ResultType["deferred"] = "deferred";
543
554
  ResultType["redirect"] = "redirect";
544
555
  ResultType["error"] = "error";
545
- })(ResultType || (ResultType = {}));
556
+ return ResultType;
557
+ }({});
558
+
559
+ /**
560
+ * Successful result from a loader or action
561
+ */
562
+
546
563
  /**
547
564
  * Successful defer() result from a loader or action
548
565
  */
566
+
549
567
  /**
550
568
  * Redirect result from a loader or action
551
569
  */
570
+
552
571
  /**
553
572
  * Unsuccessful result from a loader or action
554
573
  */
574
+
555
575
  /**
556
576
  * Result from a loader or action - potentially successful or unsuccessful
557
577
  */
578
+
558
579
  /**
559
580
  * Users can specify either lowercase or uppercase form methods on <Form>,
560
581
  * useSubmit(), <fetcher.Form>, etc.
561
582
  */
583
+
562
584
  /**
563
585
  * Active navigation/fetcher form methods are exposed in lowercase on the
564
586
  * RouterState
565
587
  */
588
+
566
589
  /**
567
590
  * In v7, active navigation/fetcher form methods are exposed in uppercase on the
568
591
  * RouterState. This is to align with the normalization done via fetch().
569
592
  */
593
+
570
594
  // Thanks https://github.com/sindresorhus/type-fest!
595
+
571
596
  /**
572
597
  * @private
573
598
  * Internal interface to pass around for action submissions, not intended for
574
599
  * external consumption
575
600
  */
601
+
576
602
  /**
577
603
  * @private
578
604
  * Arguments passed to route loader/action functions. Same for now but we keep
579
605
  * this as a private implementation detail in case they diverge in the future.
580
606
  */
607
+
581
608
  /**
582
609
  * Arguments passed to loader functions
583
610
  */
611
+
584
612
  /**
585
613
  * Arguments passed to action functions
586
614
  */
615
+
587
616
  /**
588
617
  * Loaders and actions can return anything except `undefined` (`null` is a
589
618
  * valid return value if there is no data to return). Responses are preferred
590
619
  * and will ease any future migration to Remix
591
620
  */
621
+
592
622
  /**
593
623
  * Route loader function signature
594
624
  */
625
+
595
626
  /**
596
627
  * Route action function signature
597
628
  */
629
+
598
630
  /**
599
631
  * Route shouldRevalidate function signature. This runs after any submission
600
632
  * (navigation or fetcher), so we flatten the navigation/fetcher submission
@@ -602,21 +634,25 @@ let ResultType;
602
634
  * or a fetcher, what really matters is the URLs and the formData since loaders
603
635
  * have to re-run based on the data models that were potentially mutated.
604
636
  */
637
+
605
638
  /**
606
639
  * Function provided by the framework-aware layers to set `hasErrorBoundary`
607
640
  * from the framework-aware `errorElement` prop
608
641
  *
609
642
  * @deprecated Use `mapRouteProperties` instead
610
643
  */
644
+
611
645
  /**
612
646
  * Function provided by the framework-aware layers to set any framework-specific
613
647
  * properties from framework-agnostic properties
614
648
  */
649
+
615
650
  /**
616
651
  * Keys we cannot change from within a lazy() function. We spread all other keys
617
652
  * onto the route. Either they're meaningful to the router, or they'll get
618
653
  * ignored.
619
654
  */
655
+
620
656
  const immutableRouteKeys = new Set(["lazy", "caseSensitive", "path", "id", "index", "children"]);
621
657
 
622
658
  /**
@@ -1414,6 +1450,17 @@ const redirect = function redirect(url, init) {
1414
1450
  }));
1415
1451
  };
1416
1452
 
1453
+ /**
1454
+ * A redirect response that will force a document reload to the new location.
1455
+ * Sets the status code and the `Location` header.
1456
+ * Defaults to "302 Found".
1457
+ */
1458
+ const redirectDocument = (url, init) => {
1459
+ let response = redirect(url, init);
1460
+ response.headers.set("X-Remix-Reload-Document", "true");
1461
+ return response;
1462
+ };
1463
+
1417
1464
  /**
1418
1465
  * @private
1419
1466
  * Utility class we use to hold auto-unwrapped 4xx/5xx Response bodies
@@ -1653,7 +1700,7 @@ function createRouter(init) {
1653
1700
 
1654
1701
  // -- Stateful internal variables to manage navigations --
1655
1702
  // Current navigation in progress (to be committed in completeNavigation)
1656
- let pendingAction = exports.Action.Pop;
1703
+ let pendingAction = Action.Pop;
1657
1704
 
1658
1705
  // Should the current navigation prevent the scroll reset if scroll cannot
1659
1706
  // be restored?
@@ -1776,7 +1823,7 @@ function createRouter(init) {
1776
1823
  // resolved prior to router creation since we can't go into a fallbackElement
1777
1824
  // UI for SSR'd apps
1778
1825
  if (!state.initialized) {
1779
- startNavigation(exports.Action.Pop, state.location);
1826
+ startNavigation(Action.Pop, state.location);
1780
1827
  }
1781
1828
  return router;
1782
1829
  }
@@ -1851,9 +1898,9 @@ function createRouter(init) {
1851
1898
  dataRoutes = inFlightDataRoutes;
1852
1899
  inFlightDataRoutes = undefined;
1853
1900
  }
1854
- if (isUninterruptedRevalidation) ; else if (pendingAction === exports.Action.Pop) ; else if (pendingAction === exports.Action.Push) {
1901
+ if (isUninterruptedRevalidation) ; else if (pendingAction === Action.Pop) ; else if (pendingAction === Action.Push) {
1855
1902
  init.history.push(location, location.state);
1856
- } else if (pendingAction === exports.Action.Replace) {
1903
+ } else if (pendingAction === Action.Replace) {
1857
1904
  init.history.replace(location, location.state);
1858
1905
  }
1859
1906
  updateState(_extends({}, newState, {
@@ -1871,7 +1918,7 @@ function createRouter(init) {
1871
1918
  }));
1872
1919
 
1873
1920
  // Reset stateful navigation vars
1874
- pendingAction = exports.Action.Pop;
1921
+ pendingAction = Action.Pop;
1875
1922
  pendingPreventScrollReset = false;
1876
1923
  isUninterruptedRevalidation = false;
1877
1924
  isRevalidationRequired = false;
@@ -1902,15 +1949,15 @@ function createRouter(init) {
1902
1949
  // without having to touch history
1903
1950
  nextLocation = _extends({}, nextLocation, init.history.encodeLocation(nextLocation));
1904
1951
  let userReplace = opts && opts.replace != null ? opts.replace : undefined;
1905
- let historyAction = exports.Action.Push;
1952
+ let historyAction = Action.Push;
1906
1953
  if (userReplace === true) {
1907
- historyAction = exports.Action.Replace;
1954
+ historyAction = Action.Replace;
1908
1955
  } else if (userReplace === false) ; else if (submission != null && isMutationMethod(submission.formMethod) && submission.formAction === state.location.pathname + state.location.search) {
1909
1956
  // By default on submissions to the current location we REPLACE so that
1910
1957
  // users don't have to double-click the back button to get to the prior
1911
1958
  // location. If the user redirects to a different location from the
1912
1959
  // action/loader this will be ignored and the redirect will be a PUSH
1913
- historyAction = exports.Action.Replace;
1960
+ historyAction = Action.Replace;
1914
1961
  }
1915
1962
  let preventScrollReset = opts && "preventScrollReset" in opts ? opts.preventScrollReset === true : undefined;
1916
1963
  let blockerKey = shouldBlockNavigation({
@@ -2157,7 +2204,7 @@ function createRouter(init) {
2157
2204
  // back to PUSH so that the user can use the back button to get back to
2158
2205
  // the pre-submission form location to try again
2159
2206
  if ((opts && opts.replace) !== true) {
2160
- pendingAction = exports.Action.Push;
2207
+ pendingAction = Action.Push;
2161
2208
  }
2162
2209
  return {
2163
2210
  // Send back an empty object we can use to clear out any prior actionData
@@ -2648,11 +2695,20 @@ function createRouter(init) {
2648
2695
  _isFetchActionRedirect: true
2649
2696
  } : {}));
2650
2697
  invariant(redirectLocation, "Expected a location on the redirect navigation");
2651
- // Check if this an absolute external redirect that goes to a new origin
2652
- if (ABSOLUTE_URL_REGEX.test(redirect.location) && isBrowser) {
2653
- let url = init.history.createURL(redirect.location);
2654
- let isDifferentBasename = stripBasename(url.pathname, basename) == null;
2655
- if (routerWindow.location.origin !== url.origin || isDifferentBasename) {
2698
+ if (isBrowser) {
2699
+ let isDocumentReload = false;
2700
+ if (redirect.reloadDocument) {
2701
+ // Hard reload if the response contained X-Remix-Reload-Document
2702
+ isDocumentReload = true;
2703
+ } else if (ABSOLUTE_URL_REGEX.test(redirect.location)) {
2704
+ const url = init.history.createURL(redirect.location);
2705
+ isDocumentReload =
2706
+ // Hard reload if it's an absolute URL to a new origin
2707
+ url.origin !== routerWindow.location.origin ||
2708
+ // Hard reload if it's an absolute URL that does not match our basename
2709
+ stripBasename(url.pathname, basename) == null;
2710
+ }
2711
+ if (isDocumentReload) {
2656
2712
  if (replace) {
2657
2713
  routerWindow.location.replace(redirect.location);
2658
2714
  } else {
@@ -2665,7 +2721,7 @@ function createRouter(init) {
2665
2721
  // There's no need to abort on redirects, since we don't detect the
2666
2722
  // redirect until the action/loaders have settled
2667
2723
  pendingNavigationController = null;
2668
- let redirectHistoryAction = replace === true ? exports.Action.Replace : exports.Action.Push;
2724
+ let redirectHistoryAction = replace === true ? Action.Replace : Action.Push;
2669
2725
 
2670
2726
  // Use the incoming submission if provided, fallback on the active one in
2671
2727
  // state.navigation
@@ -3178,7 +3234,7 @@ function createStaticHandler(routes, opts) {
3178
3234
  // it to bail out and then return or throw here based on whether the user
3179
3235
  // returned or threw
3180
3236
  if (isQueryRouteResponse(e)) {
3181
- if (e.type === ResultType.error && !isRedirectResponse(e.response)) {
3237
+ if (e.type === ResultType.error) {
3182
3238
  throw e.response;
3183
3239
  }
3184
3240
  return e.response;
@@ -3871,7 +3927,8 @@ async function callLoaderOrAction(type, request, match, matches, manifest, mapRo
3871
3927
  type: ResultType.redirect,
3872
3928
  status,
3873
3929
  location,
3874
- revalidate: result.headers.get("X-Remix-Revalidate") !== null
3930
+ revalidate: result.headers.get("X-Remix-Revalidate") !== null,
3931
+ reloadDocument: result.headers.get("X-Remix-Reload-Document") !== null
3875
3932
  };
3876
3933
  }
3877
3934
 
@@ -3879,11 +3936,11 @@ async function callLoaderOrAction(type, request, match, matches, manifest, mapRo
3879
3936
  // without unwrapping. We do this with the QueryRouteResponse wrapper
3880
3937
  // interface so we can know whether it was returned or thrown
3881
3938
  if (opts.isRouteRequest) {
3882
- // eslint-disable-next-line no-throw-literal
3883
- throw {
3884
- type: resultType || ResultType.data,
3939
+ let queryRouteResponse = {
3940
+ type: resultType === ResultType.error ? ResultType.error : ResultType.data,
3885
3941
  response: result
3886
3942
  };
3943
+ throw queryRouteResponse;
3887
3944
  }
3888
3945
  let data;
3889
3946
  let contentType = result.headers.get("Content-Type");
@@ -4241,7 +4298,7 @@ function isRedirectResponse(result) {
4241
4298
  return status >= 300 && status <= 399 && location != null;
4242
4299
  }
4243
4300
  function isQueryRouteResponse(obj) {
4244
- return obj && isResponse(obj.response) && (obj.type === ResultType.data || ResultType.error);
4301
+ return obj && isResponse(obj.response) && (obj.type === ResultType.data || obj.type === ResultType.error);
4245
4302
  }
4246
4303
  function isValidMethod(method) {
4247
4304
  return validRequestMethods.has(method.toLowerCase());
@@ -4474,6 +4531,7 @@ function getDoneFetcher(data) {
4474
4531
  //#endregion
4475
4532
 
4476
4533
  exports.AbortedDeferredError = AbortedDeferredError;
4534
+ exports.Action = Action;
4477
4535
  exports.ErrorResponse = ErrorResponse;
4478
4536
  exports.IDLE_BLOCKER = IDLE_BLOCKER;
4479
4537
  exports.IDLE_FETCHER = IDLE_FETCHER;
@@ -4503,6 +4561,7 @@ exports.matchRoutes = matchRoutes;
4503
4561
  exports.normalizePathname = normalizePathname;
4504
4562
  exports.parsePath = parsePath;
4505
4563
  exports.redirect = redirect;
4564
+ exports.redirectDocument = redirectDocument;
4506
4565
  exports.resolvePath = resolvePath;
4507
4566
  exports.resolveTo = resolveTo;
4508
4567
  exports.stripBasename = stripBasename;