react-router 0.0.0-experimental-e7e9ce6e → 0.0.0-experimental-91f2bf54

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
  import * as React from "react";
2
- import type { AgnosticRouteMatch, AgnosticIndexRouteObject, AgnosticNonIndexRouteObject, History, Location, Router, StaticHandlerContext, To, TrackedPromise } from "@remix-run/router";
2
+ import type { AgnosticRouteMatch, AgnosticIndexRouteObject, AgnosticNonIndexRouteObject, History, Location, Router, StaticHandlerContext, To, TrackedPromise, LazyRouteFunction } from "@remix-run/router";
3
3
  import type { Action as NavigationType } from "@remix-run/router";
4
4
  export interface IndexRouteObject {
5
5
  caseSensitive?: AgnosticIndexRouteObject["caseSensitive"];
@@ -14,6 +14,9 @@ export interface IndexRouteObject {
14
14
  children?: undefined;
15
15
  element?: React.ReactNode | null;
16
16
  errorElement?: React.ReactNode | null;
17
+ Component?: React.ComponentType | null;
18
+ ErrorBoundary?: React.ComponentType | null;
19
+ lazy?: LazyRouteFunction<IndexRouteObject>;
17
20
  }
18
21
  export interface NonIndexRouteObject {
19
22
  caseSensitive?: AgnosticNonIndexRouteObject["caseSensitive"];
@@ -28,6 +31,9 @@ export interface NonIndexRouteObject {
28
31
  children?: RouteObject[];
29
32
  element?: React.ReactNode | null;
30
33
  errorElement?: React.ReactNode | null;
34
+ Component?: React.ComponentType | null;
35
+ ErrorBoundary?: React.ComponentType | null;
36
+ lazy?: LazyRouteFunction<NonIndexRouteObject>;
31
37
  }
32
38
  export declare type RouteObject = IndexRouteObject | NonIndexRouteObject;
33
39
  export declare type DataRouteObject = RouteObject & {
@@ -158,7 +158,7 @@ export declare function useActionData(): unknown;
158
158
  /**
159
159
  * Returns the nearest ancestor Route error, which could be a loader/action
160
160
  * error or a render error. This is intended to be called from your
161
- * errorElement to display a proper error message.
161
+ * ErrorBoundary/errorElement to display a proper error message.
162
162
  */
163
163
  export declare function useRouteError(): unknown;
164
164
  /**
package/dist/main.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * React Router v0.0.0-experimental-e7e9ce6e
2
+ * React Router v0.0.0-experimental-91f2bf54
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * React Router v0.0.0-experimental-e7e9ce6e
2
+ * React Router v0.0.0-experimental-91f2bf54
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -8,7 +8,7 @@
8
8
  *
9
9
  * @license MIT
10
10
  */
11
- import { UNSAFE_invariant, joinPaths, matchPath, UNSAFE_getPathContributingMatches, warning, resolveTo, parsePath, matchRoutes, Action, isRouteErrorResponse, createMemoryHistory, stripBasename, AbortedDeferredError, createRouter } from '@remix-run/router';
11
+ import { UNSAFE_invariant, joinPaths, matchPath, UNSAFE_getPathContributingMatches, UNSAFE_warning, resolveTo, parsePath, matchRoutes, Action, isRouteErrorResponse, createMemoryHistory, stripBasename, AbortedDeferredError, createRouter } from '@remix-run/router';
12
12
  export { AbortedDeferredError, Action as NavigationType, createPath, defer, generatePath, isRouteErrorResponse, json, matchPath, matchRoutes, parsePath, redirect, resolvePath } from '@remix-run/router';
13
13
  import * as React from 'react';
14
14
 
@@ -354,7 +354,7 @@ function useNavigate() {
354
354
  activeRef.current = true;
355
355
  });
356
356
  let navigate = React.useCallback((to, options = {}) => {
357
- warning(activeRef.current, `You should call navigate() in a React.useEffect(), not when ` + `your component is first rendered.`) ;
357
+ UNSAFE_warning(activeRef.current, `You should call navigate() in a React.useEffect(), not when ` + `your component is first rendered.`) ;
358
358
  if (!activeRef.current) return;
359
359
 
360
360
  if (typeof to === "number") {
@@ -504,8 +504,8 @@ function useRoutes(routes, locationArg) {
504
504
  });
505
505
 
506
506
  {
507
- warning(parentRoute || matches != null, `No routes matched location "${location.pathname}${location.search}${location.hash}" `) ;
508
- warning(matches == null || matches[matches.length - 1].route.element !== undefined, `Matched leaf route at location "${location.pathname}${location.search}${location.hash}" does not have an element. ` + `This means it will render an <Outlet /> with a null value by default resulting in an "empty" page.`) ;
507
+ UNSAFE_warning(parentRoute || matches != null, `No routes matched location "${location.pathname}${location.search}${location.hash}" `) ;
508
+ UNSAFE_warning(matches == null || matches[matches.length - 1].route.element !== undefined || matches[matches.length - 1].route.Component !== undefined, `Matched leaf route at location "${location.pathname}${location.search}${location.hash}" ` + `does not have an element or Component. This means it will render an <Outlet /> with a ` + `null value by default resulting in an "empty" page.`) ;
509
509
  }
510
510
 
511
511
  let renderedMatches = _renderMatches(matches && matches.map(match => Object.assign({}, match, {
@@ -538,7 +538,7 @@ function useRoutes(routes, locationArg) {
538
538
  return renderedMatches;
539
539
  }
540
540
 
541
- function DefaultErrorElement() {
541
+ function DefaultErrorComponent() {
542
542
  let error = useRouteError();
543
543
  let message = isRouteErrorResponse(error) ? `${error.status} ${error.statusText}` : error instanceof Error ? error.message : JSON.stringify(error);
544
544
  let stack = error instanceof Error ? error.stack : null;
@@ -556,7 +556,7 @@ function DefaultErrorElement() {
556
556
  {
557
557
  devInfo = /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("p", null, "\uD83D\uDCBF Hey developer \uD83D\uDC4B"), /*#__PURE__*/React.createElement("p", null, "You can provide a way better UX than this when your app throws errors by providing your own\u00A0", /*#__PURE__*/React.createElement("code", {
558
558
  style: codeStyles
559
- }, "errorElement"), " props on\u00A0", /*#__PURE__*/React.createElement("code", {
559
+ }, "ErrorBoundary"), " prop on\u00A0", /*#__PURE__*/React.createElement("code", {
560
560
  style: codeStyles
561
561
  }, "<Route>")));
562
562
  }
@@ -634,7 +634,7 @@ function RenderedRoute({
634
634
  let dataRouterContext = React.useContext(DataRouterContext); // Track how deep we got in our render pass to emulate SSR componentDidCatch
635
635
  // in a DataStaticRouter
636
636
 
637
- if (dataRouterContext && dataRouterContext.static && dataRouterContext.staticContext && match.route.errorElement) {
637
+ if (dataRouterContext && dataRouterContext.static && dataRouterContext.staticContext && (match.route.errorElement || match.route.ErrorBoundary)) {
638
638
  dataRouterContext.staticContext._deepestRenderedBoundaryId = match.route.id;
639
639
  }
640
640
 
@@ -667,21 +667,45 @@ function _renderMatches(matches, parentMatches = [], dataRouterState) {
667
667
  return renderedMatches.reduceRight((outlet, match, index) => {
668
668
  let error = match.route.id ? errors?.[match.route.id] : null; // Only data routers handle errors
669
669
 
670
- let errorElement = dataRouterState ? match.route.errorElement || /*#__PURE__*/React.createElement(DefaultErrorElement, null) : null;
670
+ let errorElement = null;
671
+
672
+ if (dataRouterState) {
673
+ if (match.route.ErrorBoundary) {
674
+ errorElement = /*#__PURE__*/React.createElement(match.route.ErrorBoundary, null);
675
+ } else if (match.route.errorElement) {
676
+ errorElement = match.route.errorElement;
677
+ } else {
678
+ errorElement = /*#__PURE__*/React.createElement(DefaultErrorComponent, null);
679
+ }
680
+ }
681
+
671
682
  let matches = parentMatches.concat(renderedMatches.slice(0, index + 1));
672
683
 
673
- let getChildren = () => /*#__PURE__*/React.createElement(RenderedRoute, {
674
- match: match,
675
- routeContext: {
676
- outlet,
677
- matches
684
+ let getChildren = () => {
685
+ let children = outlet;
686
+
687
+ if (error) {
688
+ children = errorElement;
689
+ } else if (match.route.Component) {
690
+ children = /*#__PURE__*/React.createElement(match.route.Component, null);
691
+ } else if (match.route.element) {
692
+ children = match.route.element;
678
693
  }
679
- }, error ? errorElement : match.route.element !== undefined ? match.route.element : outlet); // Only wrap in an error boundary within data router usages when we have an
680
- // errorElement on this route. Otherwise let it bubble up to an ancestor
681
- // errorElement
682
694
 
695
+ return /*#__PURE__*/React.createElement(RenderedRoute, {
696
+ match: match,
697
+ routeContext: {
698
+ outlet,
699
+ matches
700
+ },
701
+ children: children
702
+ });
703
+ }; // Only wrap in an error boundary within data router usages when we have an
704
+ // ErrorBoundary/errorElement on this route. Otherwise let it bubble up to
705
+ // an ancestor ErrorBoundary/errorElement
683
706
 
684
- return dataRouterState && (match.route.errorElement || index === 0) ? /*#__PURE__*/React.createElement(RenderErrorBoundary, {
707
+
708
+ return dataRouterState && (match.route.ErrorBoundary || match.route.errorElement || index === 0) ? /*#__PURE__*/React.createElement(RenderErrorBoundary, {
685
709
  location: dataRouterState.location,
686
710
  component: errorElement,
687
711
  error: error,
@@ -703,6 +727,7 @@ var DataRouterHook;
703
727
  var DataRouterStateHook;
704
728
 
705
729
  (function (DataRouterStateHook) {
730
+ DataRouterStateHook["UseBlocker"] = "useBlocker";
706
731
  DataRouterStateHook["UseLoaderData"] = "useLoaderData";
707
732
  DataRouterStateHook["UseActionData"] = "useActionData";
708
733
  DataRouterStateHook["UseRouteError"] = "useRouteError";
@@ -826,7 +851,7 @@ function useActionData() {
826
851
  /**
827
852
  * Returns the nearest ancestor Route error, which could be a loader/action
828
853
  * error or a render error. This is intended to be called from your
829
- * errorElement to display a proper error message.
854
+ * ErrorBoundary/errorElement to display a proper error message.
830
855
  */
831
856
 
832
857
  function useRouteError() {
@@ -870,21 +895,24 @@ function useBlocker(shouldBlock) {
870
895
  let {
871
896
  router
872
897
  } = useDataRouterContext(DataRouterHook.UseBlocker);
898
+ let state = useDataRouterState(DataRouterStateHook.UseBlocker);
873
899
  let [blockerKey] = React.useState(() => String(++blockerId));
874
900
  let blockerFunction = React.useCallback(args => {
875
901
  return typeof shouldBlock === "function" ? !!shouldBlock(args) : !!shouldBlock;
876
902
  }, [shouldBlock]);
877
903
  let blocker = router.getBlocker(blockerKey, blockerFunction); // Cleanup on unmount
878
904
 
879
- React.useEffect(() => () => router.deleteBlocker(blockerKey), [router, blockerKey]);
880
- return blocker;
905
+ React.useEffect(() => () => router.deleteBlocker(blockerKey), [router, blockerKey]); // Prefer the blocker from state since DataRouterContext is memoized so this
906
+ // ensures we update on blocker state updates
907
+
908
+ return state.blockers.get(blockerKey) || blocker;
881
909
  }
882
910
  const alreadyWarned = {};
883
911
 
884
912
  function warningOnce(key, cond, message) {
885
913
  if (!cond && !alreadyWarned[key]) {
886
914
  alreadyWarned[key] = true;
887
- warning(false, message) ;
915
+ UNSAFE_warning(false, message) ;
888
916
  }
889
917
  }
890
918
 
@@ -896,11 +924,12 @@ function RouterProvider({
896
924
  fallbackElement,
897
925
  router
898
926
  }) {
899
- // Sync router state to our component state to force re-renders
900
- let state = useSyncExternalStore(router.subscribe, () => router.state, // We have to provide this so React@18 doesn't complain during hydration,
927
+ let getState = React.useCallback(() => router.state, [router]); // Sync router state to our component state to force re-renders
928
+
929
+ let state = useSyncExternalStore(router.subscribe, getState, // We have to provide this so React@18 doesn't complain during hydration,
901
930
  // but we pass our serialized hydration data into the router so state here
902
931
  // is already synced with what the server saw
903
- () => router.state);
932
+ getState);
904
933
  let navigator = React.useMemo(() => {
905
934
  return {
906
935
  createHref: router.createHref,
@@ -917,7 +946,13 @@ function RouterProvider({
917
946
  })
918
947
  };
919
948
  }, [router]);
920
- let basename = router.basename || "/"; // The fragment and {null} here are important! We need them to keep React 18's
949
+ let basename = router.basename || "/";
950
+ let dataRouterContext = React.useMemo(() => ({
951
+ router,
952
+ navigator,
953
+ static: false,
954
+ basename
955
+ }), [router, navigator, basename]); // The fragment and {null} here are important! We need them to keep React 18's
921
956
  // useId happy when we are server-rendering since we may have a <script> here
922
957
  // containing the hydrated server-side staticContext (from StaticRouterProvider).
923
958
  // useId relies on the component tree structure to generate deterministic id's
@@ -925,13 +960,7 @@ function RouterProvider({
925
960
  // we don't need the <script> tag
926
961
 
927
962
  return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(DataRouterContext.Provider, {
928
- value: {
929
- router,
930
- navigator,
931
- static: false,
932
- // Do we need this?
933
- basename
934
- }
963
+ value: dataRouterContext
935
964
  }, /*#__PURE__*/React.createElement(DataRouterStateContext.Provider, {
936
965
  value: state
937
966
  }, /*#__PURE__*/React.createElement(Router, {
@@ -996,7 +1025,7 @@ function Navigate({
996
1025
  !useInRouterContext() ? UNSAFE_invariant(false, // TODO: This error is probably because they somehow have 2 versions of
997
1026
  // the router loaded. We can help them understand how to avoid that.
998
1027
  `<Navigate> may be used only in the context of a <Router> component.`) : void 0;
999
- warning(!React.useContext(NavigationContext).static, `<Navigate> must not be used on the initial render in a <StaticRouter>. ` + `This is a no-op, but you should modify your code so the <Navigate> is ` + `only ever rendered in response to some user interaction or state change.`) ;
1028
+ UNSAFE_warning(!React.useContext(NavigationContext).static, `<Navigate> must not be used on the initial render in a <StaticRouter>. ` + `This is a no-op, but you should modify your code so the <Navigate> is ` + `only ever rendered in response to some user interaction or state change.`) ;
1000
1029
  let dataRouterState = React.useContext(DataRouterStateContext);
1001
1030
  let navigate = useNavigate();
1002
1031
  React.useEffect(() => {
@@ -1051,15 +1080,9 @@ function Router({
1051
1080
  navigator,
1052
1081
  static: staticProp = false
1053
1082
  }) {
1054
- // Temporarily commented out for testing of <CompatRouter> nested in a
1055
- // <RouterProvider>
1056
- // invariant(
1057
- // !useInRouterContext(),
1058
- // `You cannot render a <Router> inside another <Router>.` +
1059
- // ` You should never have more than one in your app.`
1060
- // );
1061
- // Preserve trailing slashes on basename, so we can let the user control
1083
+ !!useInRouterContext() ? UNSAFE_invariant(false, `You cannot render a <Router> inside another <Router>.` + ` You should never have more than one in your app.`) : void 0; // Preserve trailing slashes on basename, so we can let the user control
1062
1084
  // the enforcement of trailing slashes throughout the app
1085
+
1063
1086
  let basename = basenameProp.replace(/^\/*/, "/");
1064
1087
  let navigationContext = React.useMemo(() => ({
1065
1088
  basename,
@@ -1078,7 +1101,7 @@ function Router({
1078
1101
  state = null,
1079
1102
  key = "default"
1080
1103
  } = locationProp;
1081
- let location = React.useMemo(() => {
1104
+ let locationContext = React.useMemo(() => {
1082
1105
  let trailingPathname = stripBasename(pathname, basename);
1083
1106
 
1084
1107
  if (trailingPathname == null) {
@@ -1086,16 +1109,19 @@ function Router({
1086
1109
  }
1087
1110
 
1088
1111
  return {
1089
- pathname: trailingPathname,
1090
- search,
1091
- hash,
1092
- state,
1093
- key
1112
+ location: {
1113
+ pathname: trailingPathname,
1114
+ search,
1115
+ hash,
1116
+ state,
1117
+ key
1118
+ },
1119
+ navigationType
1094
1120
  };
1095
- }, [basename, pathname, search, hash, state, key]);
1096
- warning(location != null, `<Router basename="${basename}"> is not able to match the URL ` + `"${pathname}${search}${hash}" because it does not start with the ` + `basename, so the <Router> won't render anything.`) ;
1121
+ }, [basename, pathname, search, hash, state, key, navigationType]);
1122
+ UNSAFE_warning(locationContext != null, `<Router basename="${basename}"> is not able to match the URL ` + `"${pathname}${search}${hash}" because it does not start with the ` + `basename, so the <Router> won't render anything.`) ;
1097
1123
 
1098
- if (location == null) {
1124
+ if (locationContext == null) {
1099
1125
  return null;
1100
1126
  }
1101
1127
 
@@ -1103,10 +1129,7 @@ function Router({
1103
1129
  value: navigationContext
1104
1130
  }, /*#__PURE__*/React.createElement(LocationContext.Provider, {
1105
1131
  children: children,
1106
- value: {
1107
- location,
1108
- navigationType
1109
- }
1132
+ value: locationContext
1110
1133
  }));
1111
1134
  }
1112
1135
  /**
@@ -1296,14 +1319,17 @@ function createRoutesFromChildren(children, parentPath = []) {
1296
1319
  id: element.props.id || treePath.join("-"),
1297
1320
  caseSensitive: element.props.caseSensitive,
1298
1321
  element: element.props.element,
1322
+ Component: element.props.Component,
1299
1323
  index: element.props.index,
1300
1324
  path: element.props.path,
1301
1325
  loader: element.props.loader,
1302
1326
  action: element.props.action,
1303
1327
  errorElement: element.props.errorElement,
1304
- hasErrorBoundary: element.props.errorElement != null,
1328
+ ErrorBoundary: element.props.ErrorBoundary,
1329
+ hasErrorBoundary: element.props.ErrorBoundary != null || element.props.errorElement != null,
1305
1330
  shouldRevalidate: element.props.shouldRevalidate,
1306
- handle: element.props.handle
1331
+ handle: element.props.handle,
1332
+ lazy: element.props.lazy
1307
1333
  };
1308
1334
 
1309
1335
  if (element.props.children) {
@@ -1321,27 +1347,21 @@ function createRoutesFromChildren(children, parentPath = []) {
1321
1347
  function renderMatches(matches) {
1322
1348
  return _renderMatches(matches);
1323
1349
  }
1324
- /**
1325
- * @private
1326
- * Walk the route tree and add hasErrorBoundary if it's not provided, so that
1327
- * users providing manual route arrays can just specify errorElement
1328
- */
1329
-
1330
- function enhanceManualRouteObjects(routes) {
1331
- return routes.map(route => {
1332
- let routeClone = { ...route
1333
- };
1334
1350
 
1335
- if (routeClone.hasErrorBoundary == null) {
1336
- routeClone.hasErrorBoundary = routeClone.errorElement != null;
1351
+ function detectErrorBoundary(route) {
1352
+ {
1353
+ if (route.Component && route.element) {
1354
+ UNSAFE_warning(false, "You should not include both `Component` and `element` on your route - " + "`element` will be ignored.") ;
1337
1355
  }
1338
1356
 
1339
- if (routeClone.children) {
1340
- routeClone.children = enhanceManualRouteObjects(routeClone.children);
1357
+ if (route.ErrorBoundary && route.errorElement) {
1358
+ UNSAFE_warning(false, "You should not include both `ErrorBoundary` and `errorElement` on your route - " + "`errorElement` will be ignored.") ;
1341
1359
  }
1360
+ } // Note: this check also occurs in createRoutesFromChildren so update
1361
+ // there if you change this
1342
1362
 
1343
- return routeClone;
1344
- });
1363
+
1364
+ return Boolean(route.ErrorBoundary) || Boolean(route.errorElement);
1345
1365
  }
1346
1366
 
1347
1367
  function createMemoryRouter(routes, opts) {
@@ -1352,9 +1372,10 @@ function createMemoryRouter(routes, opts) {
1352
1372
  initialIndex: opts?.initialIndex
1353
1373
  }),
1354
1374
  hydrationData: opts?.hydrationData,
1355
- routes: enhanceManualRouteObjects(routes)
1375
+ routes,
1376
+ detectErrorBoundary
1356
1377
  }).initialize();
1357
1378
  } ///////////////////////////////////////////////////////////////////////////////
1358
1379
 
1359
- export { Await, MemoryRouter, Navigate, Outlet, Route, Router, RouterProvider, Routes, DataRouterContext as UNSAFE_DataRouterContext, DataRouterStateContext as UNSAFE_DataRouterStateContext, LocationContext as UNSAFE_LocationContext, NavigationContext as UNSAFE_NavigationContext, RouteContext as UNSAFE_RouteContext, enhanceManualRouteObjects as UNSAFE_enhanceManualRouteObjects, createMemoryRouter, createRoutesFromChildren, createRoutesFromChildren as createRoutesFromElements, renderMatches, useBlocker as unstable_useBlocker, useActionData, useAsyncError, useAsyncValue, useHref, useInRouterContext, useLoaderData, useLocation, useMatch, useMatches, useNavigate, useNavigation, useNavigationType, useOutlet, useOutletContext, useParams, useResolvedPath, useRevalidator, useRouteError, useRouteLoaderData, useRoutes };
1380
+ export { Await, MemoryRouter, Navigate, Outlet, Route, Router, RouterProvider, Routes, DataRouterContext as UNSAFE_DataRouterContext, DataRouterStateContext as UNSAFE_DataRouterStateContext, LocationContext as UNSAFE_LocationContext, NavigationContext as UNSAFE_NavigationContext, RouteContext as UNSAFE_RouteContext, detectErrorBoundary as UNSAFE_detectErrorBoundary, createMemoryRouter, createRoutesFromChildren, createRoutesFromChildren as createRoutesFromElements, renderMatches, useBlocker as unstable_useBlocker, useActionData, useAsyncError, useAsyncValue, useHref, useInRouterContext, useLoaderData, useLocation, useMatch, useMatches, useNavigate, useNavigation, useNavigationType, useOutlet, useOutletContext, useParams, useResolvedPath, useRevalidator, useRouteError, useRouteLoaderData, useRoutes };
1360
1381
  //# sourceMappingURL=react-router.development.js.map