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