react-router-dom-v5-compat 6.13.0 → 6.14.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,14 @@
1
1
  # `react-router-dom-v5-compat`
2
2
 
3
+ ## 6.14.0-pre.0
4
+
5
+ ### Patch Changes
6
+
7
+ - upgrade typescript to 5.1 ([#10581](https://github.com/remix-run/react-router/pull/10581))
8
+ - Updated dependencies:
9
+ - `react-router@6.14.0-pre.0`
10
+ - `react-router-dom@6.14.0-pre.0`
11
+
3
12
  ## 6.13.0
4
13
 
5
14
  ### Patch Changes
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * React Router DOM v5 Compat v6.13.0
2
+ * React Router DOM v5 Compat v6.14.0-pre.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -9,9 +9,9 @@
9
9
  * @license MIT
10
10
  */
11
11
  import * as React from 'react';
12
- import { UNSAFE_mapRouteProperties, UNSAFE_startTransitionImpl, Router, UNSAFE_NavigationContext, useHref, useResolvedPath, useLocation, UNSAFE_DataRouterStateContext, useNavigate, createPath, UNSAFE_useRouteId, UNSAFE_RouteContext, useMatches, useNavigation, unstable_useBlocker, UNSAFE_DataRouterContext, Routes, Route } from 'react-router';
12
+ import { UNSAFE_mapRouteProperties, Router, UNSAFE_NavigationContext, useHref, useResolvedPath, useLocation, UNSAFE_DataRouterStateContext, useNavigate, createPath, UNSAFE_useRouteId, UNSAFE_RouteContext, useMatches, useNavigation, unstable_useBlocker, UNSAFE_DataRouterContext, Routes, Route } from 'react-router';
13
13
  export { AbortedDeferredError, Await, MemoryRouter, Navigate, NavigationType, Outlet, Route, Router, RouterProvider, Routes, UNSAFE_DataRouterContext, UNSAFE_DataRouterStateContext, UNSAFE_LocationContext, UNSAFE_NavigationContext, UNSAFE_RouteContext, UNSAFE_useRouteId, createMemoryRouter, createPath, createRoutesFromChildren, createRoutesFromElements, defer, generatePath, isRouteErrorResponse, json, matchPath, matchRoutes, parsePath, redirect, renderMatches, resolvePath, unstable_useBlocker, useActionData, useAsyncError, useAsyncValue, useHref, useInRouterContext, useLoaderData, useLocation, useMatch, useMatches, useNavigate, useNavigation, useNavigationType, useOutlet, useOutletContext, useParams, useResolvedPath, useRevalidator, useRouteError, useRouteLoaderData, useRoutes } from 'react-router';
14
- import { stripBasename, createRouter, createBrowserHistory, createHashHistory, ErrorResponse, UNSAFE_warning, UNSAFE_invariant, joinPaths } from '@remix-run/router';
14
+ import { stripBasename, UNSAFE_warning, createRouter, createBrowserHistory, createHashHistory, ErrorResponse, UNSAFE_invariant, joinPaths } from '@remix-run/router';
15
15
  import { parsePath, Action, createPath as createPath$1 } from 'history';
16
16
  import { Route as Route$1, useHistory } from 'react-router-dom';
17
17
 
@@ -110,83 +110,95 @@ function getSearchParamsForLocation(locationSearch, defaultSearchParams) {
110
110
  }
111
111
  return searchParams;
112
112
  }
113
- function getFormSubmissionInfo(target, options, basename) {
113
+ // One-time check for submitter support
114
+ let formDataSupportsSubmitter = false;
115
+ try {
116
+ // @ts-expect-error if FormData supports the submitter parameter, this will throw
117
+ new FormData(undefined, 0);
118
+ } catch (e) {
119
+ formDataSupportsSubmitter = true;
120
+ }
121
+ const supportedFormEncTypes = new Set(["application/x-www-form-urlencoded", "multipart/form-data", "text/plain"]);
122
+ function getFormEncType(encType) {
123
+ if (encType != null && !supportedFormEncTypes.has(encType)) {
124
+ process.env.NODE_ENV !== "production" ? UNSAFE_warning(false, "\"" + encType + "\" is not a valid `encType` for `<Form>`/`<fetcher.Form>` " + ("and will default to \"" + defaultEncType + "\"")) : void 0;
125
+ return null;
126
+ }
127
+ return encType;
128
+ }
129
+ function getFormSubmissionInfo(target, basename) {
114
130
  let method;
115
- let action = null;
131
+ let action;
116
132
  let encType;
117
133
  let formData;
134
+ let body;
118
135
  if (isFormElement(target)) {
119
- let submissionTrigger = options.submissionTrigger;
120
- if (options.action) {
121
- action = options.action;
122
- } else {
123
- // When grabbing the action from the element, it will have had the basename
124
- // prefixed to ensure non-JS scenarios work, so strip it since we'll
125
- // re-prefix in the router
126
- let attr = target.getAttribute("action");
127
- action = attr ? stripBasename(attr, basename) : null;
128
- }
129
- method = options.method || target.getAttribute("method") || defaultMethod;
130
- encType = options.encType || target.getAttribute("enctype") || defaultEncType;
136
+ // When grabbing the action from the element, it will have had the basename
137
+ // prefixed to ensure non-JS scenarios work, so strip it since we'll
138
+ // re-prefix in the router
139
+ let attr = target.getAttribute("action");
140
+ action = attr ? stripBasename(attr, basename) : null;
141
+ method = target.getAttribute("method") || defaultMethod;
142
+ encType = getFormEncType(target.getAttribute("enctype")) || defaultEncType;
131
143
  formData = new FormData(target);
132
- if (submissionTrigger && submissionTrigger.name) {
133
- formData.append(submissionTrigger.name, submissionTrigger.value);
134
- }
135
144
  } else if (isButtonElement(target) || isInputElement(target) && (target.type === "submit" || target.type === "image")) {
136
145
  let form = target.form;
137
146
  if (form == null) {
138
147
  throw new Error("Cannot submit a <button> or <input type=\"submit\"> without a <form>");
139
148
  }
140
149
  // <button>/<input type="submit"> may override attributes of <form>
141
- if (options.action) {
142
- action = options.action;
143
- } else {
144
- // When grabbing the action from the element, it will have had the basename
145
- // prefixed to ensure non-JS scenarios work, so strip it since we'll
146
- // re-prefix in the router
147
- let attr = target.getAttribute("formaction") || form.getAttribute("action");
148
- action = attr ? stripBasename(attr, basename) : null;
149
- }
150
- method = options.method || target.getAttribute("formmethod") || form.getAttribute("method") || defaultMethod;
151
- encType = options.encType || target.getAttribute("formenctype") || form.getAttribute("enctype") || defaultEncType;
152
- formData = new FormData(form);
153
- // Include name + value from a <button>, appending in case the button name
154
- // matches an existing input name
155
- if (target.name) {
156
- formData.append(target.name, target.value);
150
+ // When grabbing the action from the element, it will have had the basename
151
+ // prefixed to ensure non-JS scenarios work, so strip it since we'll
152
+ // re-prefix in the router
153
+ let attr = target.getAttribute("formaction") || form.getAttribute("action");
154
+ action = attr ? stripBasename(attr, basename) : null;
155
+ method = target.getAttribute("formmethod") || form.getAttribute("method") || defaultMethod;
156
+ encType = getFormEncType(target.getAttribute("formenctype")) || getFormEncType(form.getAttribute("enctype")) || defaultEncType;
157
+ // Build a FormData object populated from a form and submitter
158
+ formData = new FormData(form, target);
159
+ // If this browser doesn't support the `FormData(el, submitter)` format,
160
+ // then tack on the submitter value at the end. This is a lightweight
161
+ // solution that is not 100% spec compliant. For complete support in older
162
+ // browsers, consider using the `formdata-submitter-polyfill` package
163
+ if (!formDataSupportsSubmitter) {
164
+ let {
165
+ name,
166
+ type,
167
+ value
168
+ } = target;
169
+ if (type === "image") {
170
+ let prefix = name ? name + "." : "";
171
+ formData.append(prefix + "x", "0");
172
+ formData.append(prefix + "y", "0");
173
+ } else if (name) {
174
+ formData.append(name, value);
175
+ }
157
176
  }
158
177
  } else if (isHtmlElement(target)) {
159
178
  throw new Error("Cannot submit element that is not <form>, <button>, or " + "<input type=\"submit|image\">");
160
179
  } else {
161
- method = options.method || defaultMethod;
162
- action = options.action || null;
163
- encType = options.encType || defaultEncType;
164
- if (target instanceof FormData) {
165
- formData = target;
166
- } else {
167
- formData = new FormData();
168
- if (target instanceof URLSearchParams) {
169
- for (let [name, value] of target) {
170
- formData.append(name, value);
171
- }
172
- } else if (target != null) {
173
- for (let name of Object.keys(target)) {
174
- formData.append(name, target[name]);
175
- }
176
- }
177
- }
180
+ method = defaultMethod;
181
+ action = null;
182
+ encType = defaultEncType;
183
+ body = target;
184
+ }
185
+ // Send body for <Form encType="text/plain" so we encode it into text
186
+ if (formData && encType === "text/plain") {
187
+ body = formData;
188
+ formData = undefined;
178
189
  }
179
190
  return {
180
191
  action,
181
192
  method: method.toLowerCase(),
182
193
  encType,
183
- formData
194
+ formData,
195
+ body
184
196
  };
185
197
  }
186
198
 
187
199
  const _excluded = ["onClick", "relative", "reloadDocument", "replace", "state", "target", "to", "preventScrollReset"],
188
200
  _excluded2 = ["aria-current", "caseSensitive", "className", "end", "style", "to", "children"],
189
- _excluded3 = ["reloadDocument", "replace", "method", "action", "onSubmit", "fetcherKey", "routeId", "relative", "preventScrollReset"];
201
+ _excluded3 = ["reloadDocument", "replace", "method", "action", "onSubmit", "submit", "relative", "preventScrollReset"];
190
202
  function createBrowserRouter(routes, opts) {
191
203
  return createRouter({
192
204
  basename: opts == null ? void 0 : opts.basename,
@@ -246,6 +258,33 @@ function deserializeErrors(errors) {
246
258
  }
247
259
  return serialized;
248
260
  }
261
+ //#endregion
262
+ ////////////////////////////////////////////////////////////////////////////////
263
+ //#region Components
264
+ ////////////////////////////////////////////////////////////////////////////////
265
+ /**
266
+ Webpack + React 17 fails to compile on any of the following because webpack
267
+ complains that `startTransition` doesn't exist in `React`:
268
+ * import { startTransition } from "react"
269
+ * import * as React from from "react";
270
+ "startTransition" in React ? React.startTransition(() => setState()) : setState()
271
+ * import * as React from from "react";
272
+ "startTransition" in React ? React["startTransition"](() => setState()) : setState()
273
+
274
+ Moving it to a constant such as the following solves the Webpack/React 17 issue:
275
+ * import * as React from from "react";
276
+ const START_TRANSITION = "startTransition";
277
+ START_TRANSITION in React ? React[START_TRANSITION](() => setState()) : setState()
278
+
279
+ However, that introduces webpack/terser minification issues in production builds
280
+ in React 18 where minification/obfuscation ends up removing the call of
281
+ React.startTransition entirely from the first half of the ternary. Grabbing
282
+ this exported reference once up front resolves that issue.
283
+
284
+ See https://github.com/remix-run/react-router/issues/10579
285
+ */
286
+ const START_TRANSITION = "startTransition";
287
+ const startTransitionImpl = React[START_TRANSITION];
249
288
  /**
250
289
  * A `<Router>` for use in web browsers. Provides the cleanest URLs.
251
290
  */
@@ -272,7 +311,7 @@ function BrowserRouter(_ref) {
272
311
  v7_startTransition
273
312
  } = future || {};
274
313
  let setState = React.useCallback(newState => {
275
- v7_startTransition && UNSAFE_startTransitionImpl ? UNSAFE_startTransitionImpl(() => setStateImpl(newState)) : setStateImpl(newState);
314
+ v7_startTransition && startTransitionImpl ? startTransitionImpl(() => setStateImpl(newState)) : setStateImpl(newState);
276
315
  }, [setStateImpl, v7_startTransition]);
277
316
  React.useLayoutEffect(() => history.listen(setState), [history, setState]);
278
317
  return /*#__PURE__*/React.createElement(Router, {
@@ -310,7 +349,7 @@ function HashRouter(_ref2) {
310
349
  v7_startTransition
311
350
  } = future || {};
312
351
  let setState = React.useCallback(newState => {
313
- v7_startTransition && UNSAFE_startTransitionImpl ? UNSAFE_startTransitionImpl(() => setStateImpl(newState)) : setStateImpl(newState);
352
+ v7_startTransition && startTransitionImpl ? startTransitionImpl(() => setStateImpl(newState)) : setStateImpl(newState);
314
353
  }, [setStateImpl, v7_startTransition]);
315
354
  React.useLayoutEffect(() => history.listen(setState), [history, setState]);
316
355
  return /*#__PURE__*/React.createElement(Router, {
@@ -342,7 +381,7 @@ function HistoryRouter(_ref3) {
342
381
  v7_startTransition
343
382
  } = future || {};
344
383
  let setState = React.useCallback(newState => {
345
- v7_startTransition && UNSAFE_startTransitionImpl ? UNSAFE_startTransitionImpl(() => setStateImpl(newState)) : setStateImpl(newState);
384
+ v7_startTransition && startTransitionImpl ? startTransitionImpl(() => setStateImpl(newState)) : setStateImpl(newState);
346
385
  }, [setStateImpl, v7_startTransition]);
347
386
  React.useLayoutEffect(() => history.listen(setState), [history, setState]);
348
387
  return /*#__PURE__*/React.createElement(Router, {
@@ -503,7 +542,9 @@ if (process.env.NODE_ENV !== "production") {
503
542
  * submitted and returns with data.
504
543
  */
505
544
  const Form = /*#__PURE__*/React.forwardRef((props, ref) => {
545
+ let submit = useSubmit();
506
546
  return /*#__PURE__*/React.createElement(FormImpl, _extends({}, props, {
547
+ submit: submit,
507
548
  ref: ref
508
549
  }));
509
550
  });
@@ -517,13 +558,11 @@ const FormImpl = /*#__PURE__*/React.forwardRef((_ref6, forwardedRef) => {
517
558
  method = defaultMethod,
518
559
  action,
519
560
  onSubmit,
520
- fetcherKey,
521
- routeId,
561
+ submit,
522
562
  relative,
523
563
  preventScrollReset
524
564
  } = _ref6,
525
565
  props = _objectWithoutPropertiesLoose(_ref6, _excluded3);
526
- let submit = useSubmitImpl(fetcherKey, routeId);
527
566
  let formMethod = method.toLowerCase() === "get" ? "get" : "post";
528
567
  let formAction = useFormAction(action, {
529
568
  relative
@@ -576,7 +615,8 @@ if (process.env.NODE_ENV !== "production") {
576
615
  var DataRouterHook;
577
616
  (function (DataRouterHook) {
578
617
  DataRouterHook["UseScrollRestoration"] = "useScrollRestoration";
579
- DataRouterHook["UseSubmitImpl"] = "useSubmitImpl";
618
+ DataRouterHook["UseSubmit"] = "useSubmit";
619
+ DataRouterHook["UseSubmitFetcher"] = "useSubmitFetcher";
580
620
  DataRouterHook["UseFetcher"] = "useFetcher";
581
621
  })(DataRouterHook || (DataRouterHook = {}));
582
622
  var DataRouterStateHook;
@@ -652,17 +692,19 @@ function useSearchParams(defaultInit) {
652
692
  }, [navigate, searchParams]);
653
693
  return [searchParams, setSearchParams];
654
694
  }
695
+ function validateClientSideSubmission() {
696
+ if (typeof document === "undefined") {
697
+ throw new Error("You are calling submit during the server render. " + "Try calling submit within a `useEffect` or callback instead.");
698
+ }
699
+ }
655
700
  /**
656
701
  * Returns a function that may be used to programmatically submit a form (or
657
702
  * some arbitrary data) to the server.
658
703
  */
659
704
  function useSubmit() {
660
- return useSubmitImpl();
661
- }
662
- function useSubmitImpl(fetcherKey, fetcherRouteId) {
663
705
  let {
664
706
  router
665
- } = useDataRouterContext(DataRouterHook.UseSubmitImpl);
707
+ } = useDataRouterContext(DataRouterHook.UseSubmit);
666
708
  let {
667
709
  basename
668
710
  } = React.useContext(UNSAFE_NavigationContext);
@@ -671,32 +713,56 @@ function useSubmitImpl(fetcherKey, fetcherRouteId) {
671
713
  if (options === void 0) {
672
714
  options = {};
673
715
  }
674
- if (typeof document === "undefined") {
675
- throw new Error("You are calling submit during the server render. " + "Try calling submit within a `useEffect` or callback instead.");
676
- }
716
+ validateClientSideSubmission();
677
717
  let {
678
718
  action,
679
719
  method,
680
720
  encType,
681
- formData
682
- } = getFormSubmissionInfo(target, options, basename);
683
- // Base options shared between fetch() and navigate()
684
- let opts = {
721
+ formData,
722
+ body
723
+ } = getFormSubmissionInfo(target, basename);
724
+ router.navigate(options.action || action, {
685
725
  preventScrollReset: options.preventScrollReset,
686
726
  formData,
687
- formMethod: method,
688
- formEncType: encType
689
- };
690
- if (fetcherKey) {
691
- !(fetcherRouteId != null) ? process.env.NODE_ENV !== "production" ? UNSAFE_invariant(false, "No routeId available for useFetcher()") : UNSAFE_invariant(false) : void 0;
692
- router.fetch(fetcherKey, fetcherRouteId, action, opts);
693
- } else {
694
- router.navigate(action, _extends({}, opts, {
695
- replace: options.replace,
696
- fromRouteId: currentRouteId
697
- }));
727
+ body,
728
+ formMethod: options.method || method,
729
+ formEncType: options.encType || encType,
730
+ replace: options.replace,
731
+ fromRouteId: currentRouteId
732
+ });
733
+ }, [router, basename, currentRouteId]);
734
+ }
735
+ /**
736
+ * Returns the implementation for fetcher.submit
737
+ */
738
+ function useSubmitFetcher(fetcherKey, fetcherRouteId) {
739
+ let {
740
+ router
741
+ } = useDataRouterContext(DataRouterHook.UseSubmitFetcher);
742
+ let {
743
+ basename
744
+ } = React.useContext(UNSAFE_NavigationContext);
745
+ return React.useCallback(function (target, options) {
746
+ if (options === void 0) {
747
+ options = {};
698
748
  }
699
- }, [router, basename, fetcherKey, fetcherRouteId, currentRouteId]);
749
+ validateClientSideSubmission();
750
+ let {
751
+ action,
752
+ method,
753
+ encType,
754
+ formData,
755
+ body
756
+ } = getFormSubmissionInfo(target, basename);
757
+ !(fetcherRouteId != null) ? process.env.NODE_ENV !== "production" ? UNSAFE_invariant(false, "No routeId available for useFetcher()") : UNSAFE_invariant(false) : void 0;
758
+ router.fetch(fetcherKey, fetcherRouteId, options.action || action, {
759
+ preventScrollReset: options.preventScrollReset,
760
+ formData,
761
+ body,
762
+ formMethod: options.method || method,
763
+ formEncType: options.encType || encType
764
+ });
765
+ }, [router, basename, fetcherKey, fetcherRouteId]);
700
766
  }
701
767
  // v7: Eventually we should deprecate this entirely in favor of using the
702
768
  // router method directly?
@@ -750,10 +816,10 @@ function useFormAction(action, _temp2) {
750
816
  }
751
817
  function createFetcherForm(fetcherKey, routeId) {
752
818
  let FetcherForm = /*#__PURE__*/React.forwardRef((props, ref) => {
819
+ let submit = useSubmitFetcher(fetcherKey, routeId);
753
820
  return /*#__PURE__*/React.createElement(FormImpl, _extends({}, props, {
754
821
  ref: ref,
755
- fetcherKey: fetcherKey,
756
- routeId: routeId
822
+ submit: submit
757
823
  }));
758
824
  });
759
825
  if (process.env.NODE_ENV !== "production") {
@@ -785,7 +851,7 @@ function useFetcher() {
785
851
  !routeId ? process.env.NODE_ENV !== "production" ? UNSAFE_invariant(false, "No routeId available for fetcher.load()") : UNSAFE_invariant(false) : void 0;
786
852
  router.fetch(fetcherKey, routeId, href);
787
853
  });
788
- let submit = useSubmitImpl(fetcherKey, routeId);
854
+ let submit = useSubmitFetcher(fetcherKey, routeId);
789
855
  let fetcher = router.getFetcher(fetcherKey);
790
856
  let fetcherWithComponents = React.useMemo(() => _extends({
791
857
  Form,
@@ -831,6 +897,9 @@ function useScrollRestoration(_temp3) {
831
897
  restoreScrollPosition,
832
898
  preventScrollReset
833
899
  } = useDataRouterState(DataRouterStateHook.UseScrollRestoration);
900
+ let {
901
+ basename
902
+ } = React.useContext(UNSAFE_NavigationContext);
834
903
  let location = useLocation();
835
904
  let matches = useMatches();
836
905
  let navigation = useNavigation();
@@ -866,9 +935,13 @@ function useScrollRestoration(_temp3) {
866
935
  // Enable scroll restoration in the router
867
936
  // eslint-disable-next-line react-hooks/rules-of-hooks
868
937
  React.useLayoutEffect(() => {
869
- let disableScrollRestoration = router == null ? void 0 : router.enableScrollRestoration(savedScrollPositions, () => window.scrollY, getKey);
938
+ let getKeyWithoutBasename = getKey && basename !== "/" ? (location, matches) => getKey( // Strip the basename to match useLocation()
939
+ _extends({}, location, {
940
+ pathname: stripBasename(location.pathname, basename) || location.pathname
941
+ }), matches) : getKey;
942
+ let disableScrollRestoration = router == null ? void 0 : router.enableScrollRestoration(savedScrollPositions, () => window.scrollY, getKeyWithoutBasename);
870
943
  return () => disableScrollRestoration && disableScrollRestoration();
871
- }, [router, getKey]);
944
+ }, [router, basename, getKey]);
872
945
  // Restore scrolling when state.restoreScrollPosition changes
873
946
  // eslint-disable-next-line react-hooks/rules-of-hooks
874
947
  React.useLayoutEffect(() => {