1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354 |
- /**
- * React Router v6.8.1
- *
- * Copyright (c) Remix Software Inc.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE.md file in the root directory of this source tree.
- *
- * @license MIT
- */
- import { invariant, joinPaths, matchPath, UNSAFE_getPathContributingMatches, warning, resolveTo, parsePath, matchRoutes, Action, isRouteErrorResponse, createMemoryHistory, stripBasename, AbortedDeferredError, createRouter } from '@remix-run/router';
- export { AbortedDeferredError, Action as NavigationType, createPath, defer, generatePath, isRouteErrorResponse, json, matchPath, matchRoutes, parsePath, redirect, resolvePath } from '@remix-run/router';
- import * as React from 'react';
- /**
- * Copyright (c) Facebook, Inc. and its affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
- /**
- * inlined Object.is polyfill to avoid requiring consumers ship their own
- * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is
- */
- function isPolyfill(x, y) {
- return x === y && (x !== 0 || 1 / x === 1 / y) || x !== x && y !== y // eslint-disable-line no-self-compare
- ;
- }
- const is = typeof Object.is === "function" ? Object.is : isPolyfill; // Intentionally not using named imports because Rollup uses dynamic
- // dispatch for CommonJS interop named imports.
- const {
- useState,
- useEffect,
- useLayoutEffect,
- useDebugValue
- } = React;
- let didWarnOld18Alpha = false;
- let didWarnUncachedGetSnapshot = false; // Disclaimer: This shim breaks many of the rules of React, and only works
- // because of a very particular set of implementation details and assumptions
- // -- change any one of them and it will break. The most important assumption
- // is that updates are always synchronous, because concurrent rendering is
- // only available in versions of React that also have a built-in
- // useSyncExternalStore API. And we only use this shim when the built-in API
- // does not exist.
- //
- // Do not assume that the clever hacks used by this hook also work in general.
- // The point of this shim is to replace the need for hacks by other libraries.
- function useSyncExternalStore$2(subscribe, getSnapshot, // Note: The shim does not use getServerSnapshot, because pre-18 versions of
- // React do not expose a way to check if we're hydrating. So users of the shim
- // will need to track that themselves and return the correct value
- // from `getSnapshot`.
- getServerSnapshot) {
- {
- if (!didWarnOld18Alpha) {
- if ("startTransition" in React) {
- didWarnOld18Alpha = true;
- console.error("You are using an outdated, pre-release alpha of React 18 that " + "does not support useSyncExternalStore. The " + "use-sync-external-store shim will not work correctly. Upgrade " + "to a newer pre-release.");
- }
- }
- } // Read the current snapshot from the store on every render. Again, this
- // breaks the rules of React, and only works here because of specific
- // implementation details, most importantly that updates are
- // always synchronous.
- const value = getSnapshot();
- {
- if (!didWarnUncachedGetSnapshot) {
- const cachedValue = getSnapshot();
- if (!is(value, cachedValue)) {
- console.error("The result of getSnapshot should be cached to avoid an infinite loop");
- didWarnUncachedGetSnapshot = true;
- }
- }
- } // Because updates are synchronous, we don't queue them. Instead we force a
- // re-render whenever the subscribed state changes by updating an some
- // arbitrary useState hook. Then, during render, we call getSnapshot to read
- // the current value.
- //
- // Because we don't actually use the state returned by the useState hook, we
- // can save a bit of memory by storing other stuff in that slot.
- //
- // To implement the early bailout, we need to track some things on a mutable
- // object. Usually, we would put that in a useRef hook, but we can stash it in
- // our useState hook instead.
- //
- // To force a re-render, we call forceUpdate({inst}). That works because the
- // new object always fails an equality check.
- const [{
- inst
- }, forceUpdate] = useState({
- inst: {
- value,
- getSnapshot
- }
- }); // Track the latest getSnapshot function with a ref. This needs to be updated
- // in the layout phase so we can access it during the tearing check that
- // happens on subscribe.
- useLayoutEffect(() => {
- inst.value = value;
- inst.getSnapshot = getSnapshot; // Whenever getSnapshot or subscribe changes, we need to check in the
- // commit phase if there was an interleaved mutation. In concurrent mode
- // this can happen all the time, but even in synchronous mode, an earlier
- // effect may have mutated the store.
- if (checkIfSnapshotChanged(inst)) {
- // Force a re-render.
- forceUpdate({
- inst
- });
- } // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [subscribe, value, getSnapshot]);
- useEffect(() => {
- // Check for changes right before subscribing. Subsequent changes will be
- // detected in the subscription handler.
- if (checkIfSnapshotChanged(inst)) {
- // Force a re-render.
- forceUpdate({
- inst
- });
- }
- const handleStoreChange = () => {
- // TODO: Because there is no cross-renderer API for batching updates, it's
- // up to the consumer of this library to wrap their subscription event
- // with unstable_batchedUpdates. Should we try to detect when this isn't
- // the case and print a warning in development?
- // The store changed. Check if the snapshot changed since the last time we
- // read from the store.
- if (checkIfSnapshotChanged(inst)) {
- // Force a re-render.
- forceUpdate({
- inst
- });
- }
- }; // Subscribe to the store and return a clean-up function.
- return subscribe(handleStoreChange); // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [subscribe]);
- useDebugValue(value);
- return value;
- }
- function checkIfSnapshotChanged(inst) {
- const latestGetSnapshot = inst.getSnapshot;
- const prevValue = inst.value;
- try {
- const nextValue = latestGetSnapshot();
- return !is(prevValue, nextValue);
- } catch (error) {
- return true;
- }
- }
- /**
- * Copyright (c) Facebook, Inc. and its affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- *
- * @flow
- */
- function useSyncExternalStore$1(subscribe, getSnapshot, getServerSnapshot) {
- // Note: The shim does not use getServerSnapshot, because pre-18 versions of
- // React do not expose a way to check if we're hydrating. So users of the shim
- // will need to track that themselves and return the correct value
- // from `getSnapshot`.
- return getSnapshot();
- }
- /**
- * Inlined into the react-router repo since use-sync-external-store does not
- * provide a UMD-compatible package, so we need this to be able to distribute
- * UMD react-router bundles
- */
- const canUseDOM = !!(typeof window !== "undefined" && typeof window.document !== "undefined" && typeof window.document.createElement !== "undefined");
- const isServerEnvironment = !canUseDOM;
- const shim = isServerEnvironment ? useSyncExternalStore$1 : useSyncExternalStore$2;
- const useSyncExternalStore = "useSyncExternalStore" in React ? (module => module.useSyncExternalStore)(React) : shim;
- const DataRouterContext = /*#__PURE__*/React.createContext(null);
- {
- DataRouterContext.displayName = "DataRouter";
- }
- const DataRouterStateContext = /*#__PURE__*/React.createContext(null);
- {
- DataRouterStateContext.displayName = "DataRouterState";
- }
- const AwaitContext = /*#__PURE__*/React.createContext(null);
- {
- AwaitContext.displayName = "Await";
- }
- const NavigationContext = /*#__PURE__*/React.createContext(null);
- {
- NavigationContext.displayName = "Navigation";
- }
- const LocationContext = /*#__PURE__*/React.createContext(null);
- {
- LocationContext.displayName = "Location";
- }
- const RouteContext = /*#__PURE__*/React.createContext({
- outlet: null,
- matches: []
- });
- {
- RouteContext.displayName = "Route";
- }
- const RouteErrorContext = /*#__PURE__*/React.createContext(null);
- {
- RouteErrorContext.displayName = "RouteError";
- }
- /**
- * Returns the full href for the given "to" value. This is useful for building
- * custom links that are also accessible and preserve right-click behavior.
- *
- * @see https://reactrouter.com/hooks/use-href
- */
- function useHref(to, {
- relative
- } = {}) {
- !useInRouterContext() ? invariant(false, // TODO: This error is probably because they somehow have 2 versions of the
- // router loaded. We can help them understand how to avoid that.
- `useHref() may be used only in the context of a <Router> component.`) : void 0;
- let {
- basename,
- navigator
- } = React.useContext(NavigationContext);
- let {
- hash,
- pathname,
- search
- } = useResolvedPath(to, {
- relative
- });
- let joinedPathname = pathname; // If we're operating within a basename, prepend it to the pathname prior
- // to creating the href. If this is a root navigation, then just use the raw
- // basename which allows the basename to have full control over the presence
- // of a trailing slash on root links
- if (basename !== "/") {
- joinedPathname = pathname === "/" ? basename : joinPaths([basename, pathname]);
- }
- return navigator.createHref({
- pathname: joinedPathname,
- search,
- hash
- });
- }
- /**
- * Returns true if this component is a descendant of a <Router>.
- *
- * @see https://reactrouter.com/hooks/use-in-router-context
- */
- function useInRouterContext() {
- return React.useContext(LocationContext) != null;
- }
- /**
- * Returns the current location object, which represents the current URL in web
- * browsers.
- *
- * Note: If you're using this it may mean you're doing some of your own
- * "routing" in your app, and we'd like to know what your use case is. We may
- * be able to provide something higher-level to better suit your needs.
- *
- * @see https://reactrouter.com/hooks/use-location
- */
- function useLocation() {
- !useInRouterContext() ? invariant(false, // TODO: This error is probably because they somehow have 2 versions of the
- // router loaded. We can help them understand how to avoid that.
- `useLocation() may be used only in the context of a <Router> component.`) : void 0;
- return React.useContext(LocationContext).location;
- }
- /**
- * Returns the current navigation action which describes how the router came to
- * the current location, either by a pop, push, or replace on the history stack.
- *
- * @see https://reactrouter.com/hooks/use-navigation-type
- */
- function useNavigationType() {
- return React.useContext(LocationContext).navigationType;
- }
- /**
- * Returns a PathMatch object if the given pattern matches the current URL.
- * This is useful for components that need to know "active" state, e.g.
- * <NavLink>.
- *
- * @see https://reactrouter.com/hooks/use-match
- */
- function useMatch(pattern) {
- !useInRouterContext() ? invariant(false, // TODO: This error is probably because they somehow have 2 versions of the
- // router loaded. We can help them understand how to avoid that.
- `useMatch() may be used only in the context of a <Router> component.`) : void 0;
- let {
- pathname
- } = useLocation();
- return React.useMemo(() => matchPath(pattern, pathname), [pathname, pattern]);
- }
- /**
- * Returns an imperative method for changing the location. Used by <Link>s, but
- * may also be used by other elements to change the location.
- *
- * @see https://reactrouter.com/hooks/use-navigate
- */
- function useNavigate() {
- !useInRouterContext() ? invariant(false, // TODO: This error is probably because they somehow have 2 versions of the
- // router loaded. We can help them understand how to avoid that.
- `useNavigate() may be used only in the context of a <Router> component.`) : void 0;
- let {
- basename,
- navigator
- } = React.useContext(NavigationContext);
- let {
- matches
- } = React.useContext(RouteContext);
- let {
- pathname: locationPathname
- } = useLocation();
- let routePathnamesJson = JSON.stringify(UNSAFE_getPathContributingMatches(matches).map(match => match.pathnameBase));
- let activeRef = React.useRef(false);
- React.useEffect(() => {
- activeRef.current = true;
- });
- let navigate = React.useCallback((to, options = {}) => {
- warning(activeRef.current, `You should call navigate() in a React.useEffect(), not when ` + `your component is first rendered.`) ;
- if (!activeRef.current) return;
- if (typeof to === "number") {
- navigator.go(to);
- return;
- }
- let path = resolveTo(to, JSON.parse(routePathnamesJson), locationPathname, options.relative === "path"); // If we're operating within a basename, prepend it to the pathname prior
- // to handing off to history. If this is a root navigation, then we
- // navigate to the raw basename which allows the basename to have full
- // control over the presence of a trailing slash on root links
- if (basename !== "/") {
- path.pathname = path.pathname === "/" ? basename : joinPaths([basename, path.pathname]);
- }
- (!!options.replace ? navigator.replace : navigator.push)(path, options.state, options);
- }, [basename, navigator, routePathnamesJson, locationPathname]);
- return navigate;
- }
- const OutletContext = /*#__PURE__*/React.createContext(null);
- /**
- * Returns the context (if provided) for the child route at this level of the route
- * hierarchy.
- * @see https://reactrouter.com/hooks/use-outlet-context
- */
- function useOutletContext() {
- return React.useContext(OutletContext);
- }
- /**
- * Returns the element for the child route at this level of the route
- * hierarchy. Used internally by <Outlet> to render child routes.
- *
- * @see https://reactrouter.com/hooks/use-outlet
- */
- function useOutlet(context) {
- let outlet = React.useContext(RouteContext).outlet;
- if (outlet) {
- return /*#__PURE__*/React.createElement(OutletContext.Provider, {
- value: context
- }, outlet);
- }
- return outlet;
- }
- /**
- * Returns an object of key/value pairs of the dynamic params from the current
- * URL that were matched by the route path.
- *
- * @see https://reactrouter.com/hooks/use-params
- */
- function useParams() {
- let {
- matches
- } = React.useContext(RouteContext);
- let routeMatch = matches[matches.length - 1];
- return routeMatch ? routeMatch.params : {};
- }
- /**
- * Resolves the pathname of the given `to` value against the current location.
- *
- * @see https://reactrouter.com/hooks/use-resolved-path
- */
- function useResolvedPath(to, {
- relative
- } = {}) {
- let {
- matches
- } = React.useContext(RouteContext);
- let {
- pathname: locationPathname
- } = useLocation();
- let routePathnamesJson = JSON.stringify(UNSAFE_getPathContributingMatches(matches).map(match => match.pathnameBase));
- return React.useMemo(() => resolveTo(to, JSON.parse(routePathnamesJson), locationPathname, relative === "path"), [to, routePathnamesJson, locationPathname, relative]);
- }
- /**
- * Returns the element of the route that matched the current location, prepared
- * with the correct context to render the remainder of the route tree. Route
- * elements in the tree must render an <Outlet> to render their child route's
- * element.
- *
- * @see https://reactrouter.com/hooks/use-routes
- */
- function useRoutes(routes, locationArg) {
- !useInRouterContext() ? invariant(false, // TODO: This error is probably because they somehow have 2 versions of the
- // router loaded. We can help them understand how to avoid that.
- `useRoutes() may be used only in the context of a <Router> component.`) : void 0;
- let {
- navigator
- } = React.useContext(NavigationContext);
- let dataRouterStateContext = React.useContext(DataRouterStateContext);
- let {
- matches: parentMatches
- } = React.useContext(RouteContext);
- let routeMatch = parentMatches[parentMatches.length - 1];
- let parentParams = routeMatch ? routeMatch.params : {};
- let parentPathname = routeMatch ? routeMatch.pathname : "/";
- let parentPathnameBase = routeMatch ? routeMatch.pathnameBase : "/";
- let parentRoute = routeMatch && routeMatch.route;
- {
- // You won't get a warning about 2 different <Routes> under a <Route>
- // without a trailing *, but this is a best-effort warning anyway since we
- // cannot even give the warning unless they land at the parent route.
- //
- // Example:
- //
- // <Routes>
- // {/* This route path MUST end with /* because otherwise
- // it will never match /blog/post/123 */}
- // <Route path="blog" element={<Blog />} />
- // <Route path="blog/feed" element={<BlogFeed />} />
- // </Routes>
- //
- // function Blog() {
- // return (
- // <Routes>
- // <Route path="post/:id" element={<Post />} />
- // </Routes>
- // );
- // }
- let parentPath = parentRoute && parentRoute.path || "";
- warningOnce(parentPathname, !parentRoute || parentPath.endsWith("*"), `You rendered descendant <Routes> (or called \`useRoutes()\`) at ` + `"${parentPathname}" (under <Route path="${parentPath}">) but the ` + `parent route path has no trailing "*". This means if you navigate ` + `deeper, the parent won't match anymore and therefore the child ` + `routes will never render.\n\n` + `Please change the parent <Route path="${parentPath}"> to <Route ` + `path="${parentPath === "/" ? "*" : `${parentPath}/*`}">.`);
- }
- let locationFromContext = useLocation();
- let location;
- if (locationArg) {
- let parsedLocationArg = typeof locationArg === "string" ? parsePath(locationArg) : locationArg;
- !(parentPathnameBase === "/" || parsedLocationArg.pathname?.startsWith(parentPathnameBase)) ? invariant(false, `When overriding the location using \`<Routes location>\` or \`useRoutes(routes, location)\`, ` + `the location pathname must begin with the portion of the URL pathname that was ` + `matched by all parent routes. The current pathname base is "${parentPathnameBase}" ` + `but pathname "${parsedLocationArg.pathname}" was given in the \`location\` prop.`) : void 0;
- location = parsedLocationArg;
- } else {
- location = locationFromContext;
- }
- let pathname = location.pathname || "/";
- let remainingPathname = parentPathnameBase === "/" ? pathname : pathname.slice(parentPathnameBase.length) || "/";
- let matches = matchRoutes(routes, {
- pathname: remainingPathname
- });
- {
- warning(parentRoute || matches != null, `No routes matched location "${location.pathname}${location.search}${location.hash}" `) ;
- 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.`) ;
- }
- let renderedMatches = _renderMatches(matches && matches.map(match => Object.assign({}, match, {
- params: Object.assign({}, parentParams, match.params),
- pathname: joinPaths([parentPathnameBase, // Re-encode pathnames that were decoded inside matchRoutes
- navigator.encodeLocation ? navigator.encodeLocation(match.pathname).pathname : match.pathname]),
- pathnameBase: match.pathnameBase === "/" ? parentPathnameBase : joinPaths([parentPathnameBase, // Re-encode pathnames that were decoded inside matchRoutes
- navigator.encodeLocation ? navigator.encodeLocation(match.pathnameBase).pathname : match.pathnameBase])
- })), parentMatches, dataRouterStateContext || undefined); // When a user passes in a `locationArg`, the associated routes need to
- // be wrapped in a new `LocationContext.Provider` in order for `useLocation`
- // to use the scoped location instead of the global location.
- if (locationArg && renderedMatches) {
- return /*#__PURE__*/React.createElement(LocationContext.Provider, {
- value: {
- location: {
- pathname: "/",
- search: "",
- hash: "",
- state: null,
- key: "default",
- ...location
- },
- navigationType: Action.Pop
- }
- }, renderedMatches);
- }
- return renderedMatches;
- }
- function DefaultErrorElement() {
- let error = useRouteError();
- let message = isRouteErrorResponse(error) ? `${error.status} ${error.statusText}` : error instanceof Error ? error.message : JSON.stringify(error);
- let stack = error instanceof Error ? error.stack : null;
- let lightgrey = "rgba(200,200,200, 0.5)";
- let preStyles = {
- padding: "0.5rem",
- backgroundColor: lightgrey
- };
- let codeStyles = {
- padding: "2px 4px",
- backgroundColor: lightgrey
- };
- let devInfo = null;
- {
- 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", {
- style: codeStyles
- }, "errorElement"), " props on\u00A0", /*#__PURE__*/React.createElement("code", {
- style: codeStyles
- }, "<Route>")));
- }
- return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("h2", null, "Unexpected Application Error!"), /*#__PURE__*/React.createElement("h3", {
- style: {
- fontStyle: "italic"
- }
- }, message), stack ? /*#__PURE__*/React.createElement("pre", {
- style: preStyles
- }, stack) : null, devInfo);
- }
- class RenderErrorBoundary extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- location: props.location,
- error: props.error
- };
- }
- static getDerivedStateFromError(error) {
- return {
- error: error
- };
- }
- static getDerivedStateFromProps(props, state) {
- // When we get into an error state, the user will likely click "back" to the
- // previous page that didn't have an error. Because this wraps the entire
- // application, that will have no effect--the error page continues to display.
- // This gives us a mechanism to recover from the error when the location changes.
- //
- // Whether we're in an error state or not, we update the location in state
- // so that when we are in an error state, it gets reset when a new location
- // comes in and the user recovers from the error.
- if (state.location !== props.location) {
- return {
- error: props.error,
- location: props.location
- };
- } // If we're not changing locations, preserve the location but still surface
- // any new errors that may come through. We retain the existing error, we do
- // this because the error provided from the app state may be cleared without
- // the location changing.
- return {
- error: props.error || state.error,
- location: state.location
- };
- }
- componentDidCatch(error, errorInfo) {
- console.error("React Router caught the following error during render", error, errorInfo);
- }
- render() {
- return this.state.error ? /*#__PURE__*/React.createElement(RouteContext.Provider, {
- value: this.props.routeContext
- }, /*#__PURE__*/React.createElement(RouteErrorContext.Provider, {
- value: this.state.error,
- children: this.props.component
- })) : this.props.children;
- }
- }
- function RenderedRoute({
- routeContext,
- match,
- children
- }) {
- let dataRouterContext = React.useContext(DataRouterContext); // Track how deep we got in our render pass to emulate SSR componentDidCatch
- // in a DataStaticRouter
- if (dataRouterContext && dataRouterContext.static && dataRouterContext.staticContext && match.route.errorElement) {
- dataRouterContext.staticContext._deepestRenderedBoundaryId = match.route.id;
- }
- return /*#__PURE__*/React.createElement(RouteContext.Provider, {
- value: routeContext
- }, children);
- }
- function _renderMatches(matches, parentMatches = [], dataRouterState) {
- if (matches == null) {
- if (dataRouterState?.errors) {
- // Don't bail if we have data router errors so we can render them in the
- // boundary. Use the pre-matched (or shimmed) matches
- matches = dataRouterState.matches;
- } else {
- return null;
- }
- }
- let renderedMatches = matches; // If we have data errors, trim matches to the highest error boundary
- let errors = dataRouterState?.errors;
- if (errors != null) {
- let errorIndex = renderedMatches.findIndex(m => m.route.id && errors?.[m.route.id]);
- !(errorIndex >= 0) ? invariant(false, `Could not find a matching route for the current errors: ${errors}`) : void 0;
- renderedMatches = renderedMatches.slice(0, Math.min(renderedMatches.length, errorIndex + 1));
- }
- return renderedMatches.reduceRight((outlet, match, index) => {
- let error = match.route.id ? errors?.[match.route.id] : null; // Only data routers handle errors
- let errorElement = dataRouterState ? match.route.errorElement || /*#__PURE__*/React.createElement(DefaultErrorElement, null) : null;
- let matches = parentMatches.concat(renderedMatches.slice(0, index + 1));
- let getChildren = () => /*#__PURE__*/React.createElement(RenderedRoute, {
- match: match,
- routeContext: {
- outlet,
- matches
- }
- }, error ? errorElement : match.route.element !== undefined ? match.route.element : outlet); // Only wrap in an error boundary within data router usages when we have an
- // errorElement on this route. Otherwise let it bubble up to an ancestor
- // errorElement
- return dataRouterState && (match.route.errorElement || index === 0) ? /*#__PURE__*/React.createElement(RenderErrorBoundary, {
- location: dataRouterState.location,
- component: errorElement,
- error: error,
- children: getChildren(),
- routeContext: {
- outlet: null,
- matches
- }
- }) : getChildren();
- }, null);
- }
- var DataRouterHook;
- (function (DataRouterHook) {
- DataRouterHook["UseBlocker"] = "useBlocker";
- DataRouterHook["UseRevalidator"] = "useRevalidator";
- })(DataRouterHook || (DataRouterHook = {}));
- var DataRouterStateHook;
- (function (DataRouterStateHook) {
- DataRouterStateHook["UseLoaderData"] = "useLoaderData";
- DataRouterStateHook["UseActionData"] = "useActionData";
- DataRouterStateHook["UseRouteError"] = "useRouteError";
- DataRouterStateHook["UseNavigation"] = "useNavigation";
- DataRouterStateHook["UseRouteLoaderData"] = "useRouteLoaderData";
- DataRouterStateHook["UseMatches"] = "useMatches";
- DataRouterStateHook["UseRevalidator"] = "useRevalidator";
- })(DataRouterStateHook || (DataRouterStateHook = {}));
- function getDataRouterConsoleError(hookName) {
- return `${hookName} must be used within a data router. See https://reactrouter.com/routers/picking-a-router.`;
- }
- function useDataRouterContext(hookName) {
- let ctx = React.useContext(DataRouterContext);
- !ctx ? invariant(false, getDataRouterConsoleError(hookName)) : void 0;
- return ctx;
- }
- function useDataRouterState(hookName) {
- let state = React.useContext(DataRouterStateContext);
- !state ? invariant(false, getDataRouterConsoleError(hookName)) : void 0;
- return state;
- }
- function useRouteContext(hookName) {
- let route = React.useContext(RouteContext);
- !route ? invariant(false, getDataRouterConsoleError(hookName)) : void 0;
- return route;
- }
- function useCurrentRouteId(hookName) {
- let route = useRouteContext(hookName);
- let thisRoute = route.matches[route.matches.length - 1];
- !thisRoute.route.id ? invariant(false, `${hookName} can only be used on routes that contain a unique "id"`) : void 0;
- return thisRoute.route.id;
- }
- /**
- * Returns the current navigation, defaulting to an "idle" navigation when
- * no navigation is in progress
- */
- function useNavigation() {
- let state = useDataRouterState(DataRouterStateHook.UseNavigation);
- return state.navigation;
- }
- /**
- * Returns a revalidate function for manually triggering revalidation, as well
- * as the current state of any manual revalidations
- */
- function useRevalidator() {
- let dataRouterContext = useDataRouterContext(DataRouterHook.UseRevalidator);
- let state = useDataRouterState(DataRouterStateHook.UseRevalidator);
- return {
- revalidate: dataRouterContext.router.revalidate,
- state: state.revalidation
- };
- }
- /**
- * Returns the active route matches, useful for accessing loaderData for
- * parent/child routes or the route "handle" property
- */
- function useMatches() {
- let {
- matches,
- loaderData
- } = useDataRouterState(DataRouterStateHook.UseMatches);
- return React.useMemo(() => matches.map(match => {
- let {
- pathname,
- params
- } = match; // Note: This structure matches that created by createUseMatchesMatch
- // in the @remix-run/router , so if you change this please also change
- // that :) Eventually we'll DRY this up
- return {
- id: match.route.id,
- pathname,
- params,
- data: loaderData[match.route.id],
- handle: match.route.handle
- };
- }), [matches, loaderData]);
- }
- /**
- * Returns the loader data for the nearest ancestor Route loader
- */
- function useLoaderData() {
- let state = useDataRouterState(DataRouterStateHook.UseLoaderData);
- let routeId = useCurrentRouteId(DataRouterStateHook.UseLoaderData);
- if (state.errors && state.errors[routeId] != null) {
- console.error(`You cannot \`useLoaderData\` in an errorElement (routeId: ${routeId})`);
- return undefined;
- }
- return state.loaderData[routeId];
- }
- /**
- * Returns the loaderData for the given routeId
- */
- function useRouteLoaderData(routeId) {
- let state = useDataRouterState(DataRouterStateHook.UseRouteLoaderData);
- return state.loaderData[routeId];
- }
- /**
- * Returns the action data for the nearest ancestor Route action
- */
- function useActionData() {
- let state = useDataRouterState(DataRouterStateHook.UseActionData);
- let route = React.useContext(RouteContext);
- !route ? invariant(false, `useActionData must be used inside a RouteContext`) : void 0;
- return Object.values(state?.actionData || {})[0];
- }
- /**
- * Returns the nearest ancestor Route error, which could be a loader/action
- * error or a render error. This is intended to be called from your
- * errorElement to display a proper error message.
- */
- function useRouteError() {
- let error = React.useContext(RouteErrorContext);
- let state = useDataRouterState(DataRouterStateHook.UseRouteError);
- let routeId = useCurrentRouteId(DataRouterStateHook.UseRouteError); // If this was a render error, we put it in a RouteError context inside
- // of RenderErrorBoundary
- if (error) {
- return error;
- } // Otherwise look for errors from our data router state
- return state.errors?.[routeId];
- }
- /**
- * Returns the happy-path data from the nearest ancestor <Await /> value
- */
- function useAsyncValue() {
- let value = React.useContext(AwaitContext);
- return value?._data;
- }
- /**
- * Returns the error from the nearest ancestor <Await /> value
- */
- function useAsyncError() {
- let value = React.useContext(AwaitContext);
- return value?._error;
- }
- let blockerId = 0;
- /**
- * Allow the application to block navigations within the SPA and present the
- * user a confirmation dialog to confirm the navigation. Mostly used to avoid
- * using half-filled form data. This does not handle hard-reloads or
- * cross-origin navigations.
- */
- function useBlocker(shouldBlock) {
- let {
- router
- } = useDataRouterContext(DataRouterHook.UseBlocker);
- let [blockerKey] = React.useState(() => String(++blockerId));
- let blockerFunction = React.useCallback(args => {
- return typeof shouldBlock === "function" ? !!shouldBlock(args) : !!shouldBlock;
- }, [shouldBlock]);
- let blocker = router.getBlocker(blockerKey, blockerFunction); // Cleanup on unmount
- React.useEffect(() => () => router.deleteBlocker(blockerKey), [router, blockerKey]);
- return blocker;
- }
- const alreadyWarned = {};
- function warningOnce(key, cond, message) {
- if (!cond && !alreadyWarned[key]) {
- alreadyWarned[key] = true;
- warning(false, message) ;
- }
- }
- /**
- * Given a Remix Router instance, render the appropriate UI
- */
- function RouterProvider({
- fallbackElement,
- router
- }) {
- // Sync router state to our component state to force re-renders
- let state = useSyncExternalStore(router.subscribe, () => router.state, // We have to provide this so React@18 doesn't complain during hydration,
- // but we pass our serialized hydration data into the router so state here
- // is already synced with what the server saw
- () => router.state);
- let navigator = React.useMemo(() => {
- return {
- createHref: router.createHref,
- encodeLocation: router.encodeLocation,
- go: n => router.navigate(n),
- push: (to, state, opts) => router.navigate(to, {
- state,
- preventScrollReset: opts?.preventScrollReset
- }),
- replace: (to, state, opts) => router.navigate(to, {
- replace: true,
- state,
- preventScrollReset: opts?.preventScrollReset
- })
- };
- }, [router]);
- let basename = router.basename || "/"; // The fragment and {null} here are important! We need them to keep React 18's
- // useId happy when we are server-rendering since we may have a <script> here
- // containing the hydrated server-side staticContext (from StaticRouterProvider).
- // useId relies on the component tree structure to generate deterministic id's
- // so we need to ensure it remains the same on the client even though
- // we don't need the <script> tag
- return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(DataRouterContext.Provider, {
- value: {
- router,
- navigator,
- static: false,
- // Do we need this?
- basename
- }
- }, /*#__PURE__*/React.createElement(DataRouterStateContext.Provider, {
- value: state
- }, /*#__PURE__*/React.createElement(Router, {
- basename: router.basename,
- location: router.state.location,
- navigationType: router.state.historyAction,
- navigator: navigator
- }, router.state.initialized ? /*#__PURE__*/React.createElement(Routes, null) : fallbackElement))), null);
- }
- /**
- * A <Router> that stores all entries in memory.
- *
- * @see https://reactrouter.com/router-components/memory-router
- */
- function MemoryRouter({
- basename,
- children,
- initialEntries,
- initialIndex
- }) {
- let historyRef = React.useRef();
- if (historyRef.current == null) {
- historyRef.current = createMemoryHistory({
- initialEntries,
- initialIndex,
- v5Compat: true
- });
- }
- let history = historyRef.current;
- let [state, setState] = React.useState({
- action: history.action,
- location: history.location
- });
- React.useLayoutEffect(() => history.listen(setState), [history]);
- return /*#__PURE__*/React.createElement(Router, {
- basename: basename,
- children: children,
- location: state.location,
- navigationType: state.action,
- navigator: history
- });
- }
- /**
- * Changes the current location.
- *
- * Note: This API is mostly useful in React.Component subclasses that are not
- * able to use hooks. In functional components, we recommend you use the
- * `useNavigate` hook instead.
- *
- * @see https://reactrouter.com/components/navigate
- */
- function Navigate({
- to,
- replace,
- state,
- relative
- }) {
- !useInRouterContext() ? invariant(false, // TODO: This error is probably because they somehow have 2 versions of
- // the router loaded. We can help them understand how to avoid that.
- `<Navigate> may be used only in the context of a <Router> component.`) : void 0;
- 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.`) ;
- let dataRouterState = React.useContext(DataRouterStateContext);
- let navigate = useNavigate();
- React.useEffect(() => {
- // Avoid kicking off multiple navigations if we're in the middle of a
- // data-router navigation, since components get re-rendered when we enter
- // a submitting/loading state
- if (dataRouterState && dataRouterState.navigation.state !== "idle") {
- return;
- }
- navigate(to, {
- replace,
- state,
- relative
- });
- });
- return null;
- }
- /**
- * Renders the child route's element, if there is one.
- *
- * @see https://reactrouter.com/components/outlet
- */
- function Outlet(props) {
- return useOutlet(props.context);
- }
- /**
- * Declares an element that should be rendered at a certain URL path.
- *
- * @see https://reactrouter.com/components/route
- */
- function Route(_props) {
- invariant(false, `A <Route> is only ever to be used as the child of <Routes> element, ` + `never rendered directly. Please wrap your <Route> in a <Routes>.`) ;
- }
- /**
- * Provides location context for the rest of the app.
- *
- * Note: You usually won't render a <Router> directly. Instead, you'll render a
- * router that is more specific to your environment such as a <BrowserRouter>
- * in web browsers or a <StaticRouter> for server rendering.
- *
- * @see https://reactrouter.com/router-components/router
- */
- function Router({
- basename: basenameProp = "/",
- children = null,
- location: locationProp,
- navigationType = Action.Pop,
- navigator,
- static: staticProp = false
- }) {
- !!useInRouterContext() ? 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
- // the enforcement of trailing slashes throughout the app
- let basename = basenameProp.replace(/^\/*/, "/");
- let navigationContext = React.useMemo(() => ({
- basename,
- navigator,
- static: staticProp
- }), [basename, navigator, staticProp]);
- if (typeof locationProp === "string") {
- locationProp = parsePath(locationProp);
- }
- let {
- pathname = "/",
- search = "",
- hash = "",
- state = null,
- key = "default"
- } = locationProp;
- let location = React.useMemo(() => {
- let trailingPathname = stripBasename(pathname, basename);
- if (trailingPathname == null) {
- return null;
- }
- return {
- pathname: trailingPathname,
- search,
- hash,
- state,
- key
- };
- }, [basename, pathname, search, hash, state, key]);
- 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.`) ;
- if (location == null) {
- return null;
- }
- return /*#__PURE__*/React.createElement(NavigationContext.Provider, {
- value: navigationContext
- }, /*#__PURE__*/React.createElement(LocationContext.Provider, {
- children: children,
- value: {
- location,
- navigationType
- }
- }));
- }
- /**
- * A container for a nested tree of <Route> elements that renders the branch
- * that best matches the current location.
- *
- * @see https://reactrouter.com/components/routes
- */
- function Routes({
- children,
- location
- }) {
- let dataRouterContext = React.useContext(DataRouterContext); // When in a DataRouterContext _without_ children, we use the router routes
- // directly. If we have children, then we're in a descendant tree and we
- // need to use child routes.
- let routes = dataRouterContext && !children ? dataRouterContext.router.routes : createRoutesFromChildren(children);
- return useRoutes(routes, location);
- }
- /**
- * Component to use for rendering lazily loaded data from returning defer()
- * in a loader function
- */
- function Await({
- children,
- errorElement,
- resolve
- }) {
- return /*#__PURE__*/React.createElement(AwaitErrorBoundary, {
- resolve: resolve,
- errorElement: errorElement
- }, /*#__PURE__*/React.createElement(ResolveAwait, null, children));
- }
- var AwaitRenderStatus;
- (function (AwaitRenderStatus) {
- AwaitRenderStatus[AwaitRenderStatus["pending"] = 0] = "pending";
- AwaitRenderStatus[AwaitRenderStatus["success"] = 1] = "success";
- AwaitRenderStatus[AwaitRenderStatus["error"] = 2] = "error";
- })(AwaitRenderStatus || (AwaitRenderStatus = {}));
- const neverSettledPromise = new Promise(() => {});
- class AwaitErrorBoundary extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- error: null
- };
- }
- static getDerivedStateFromError(error) {
- return {
- error
- };
- }
- componentDidCatch(error, errorInfo) {
- console.error("<Await> caught the following error during render", error, errorInfo);
- }
- render() {
- let {
- children,
- errorElement,
- resolve
- } = this.props;
- let promise = null;
- let status = AwaitRenderStatus.pending;
- if (!(resolve instanceof Promise)) {
- // Didn't get a promise - provide as a resolved promise
- status = AwaitRenderStatus.success;
- promise = Promise.resolve();
- Object.defineProperty(promise, "_tracked", {
- get: () => true
- });
- Object.defineProperty(promise, "_data", {
- get: () => resolve
- });
- } else if (this.state.error) {
- // Caught a render error, provide it as a rejected promise
- status = AwaitRenderStatus.error;
- let renderError = this.state.error;
- promise = Promise.reject().catch(() => {}); // Avoid unhandled rejection warnings
- Object.defineProperty(promise, "_tracked", {
- get: () => true
- });
- Object.defineProperty(promise, "_error", {
- get: () => renderError
- });
- } else if (resolve._tracked) {
- // Already tracked promise - check contents
- promise = resolve;
- status = promise._error !== undefined ? AwaitRenderStatus.error : promise._data !== undefined ? AwaitRenderStatus.success : AwaitRenderStatus.pending;
- } else {
- // Raw (untracked) promise - track it
- status = AwaitRenderStatus.pending;
- Object.defineProperty(resolve, "_tracked", {
- get: () => true
- });
- promise = resolve.then(data => Object.defineProperty(resolve, "_data", {
- get: () => data
- }), error => Object.defineProperty(resolve, "_error", {
- get: () => error
- }));
- }
- if (status === AwaitRenderStatus.error && promise._error instanceof AbortedDeferredError) {
- // Freeze the UI by throwing a never resolved promise
- throw neverSettledPromise;
- }
- if (status === AwaitRenderStatus.error && !errorElement) {
- // No errorElement, throw to the nearest route-level error boundary
- throw promise._error;
- }
- if (status === AwaitRenderStatus.error) {
- // Render via our errorElement
- return /*#__PURE__*/React.createElement(AwaitContext.Provider, {
- value: promise,
- children: errorElement
- });
- }
- if (status === AwaitRenderStatus.success) {
- // Render children with resolved value
- return /*#__PURE__*/React.createElement(AwaitContext.Provider, {
- value: promise,
- children: children
- });
- } // Throw to the suspense boundary
- throw promise;
- }
- }
- /**
- * @private
- * Indirection to leverage useAsyncValue for a render-prop API on <Await>
- */
- function ResolveAwait({
- children
- }) {
- let data = useAsyncValue();
- let toRender = typeof children === "function" ? children(data) : children;
- return /*#__PURE__*/React.createElement(React.Fragment, null, toRender);
- } ///////////////////////////////////////////////////////////////////////////////
- // UTILS
- ///////////////////////////////////////////////////////////////////////////////
- /**
- * Creates a route config from a React "children" object, which is usually
- * either a `<Route>` element or an array of them. Used internally by
- * `<Routes>` to create a route config from its children.
- *
- * @see https://reactrouter.com/utils/create-routes-from-children
- */
- function createRoutesFromChildren(children, parentPath = []) {
- let routes = [];
- React.Children.forEach(children, (element, index) => {
- if (! /*#__PURE__*/React.isValidElement(element)) {
- // Ignore non-elements. This allows people to more easily inline
- // conditionals in their route config.
- return;
- }
- if (element.type === React.Fragment) {
- // Transparently support React.Fragment and its children.
- routes.push.apply(routes, createRoutesFromChildren(element.props.children, parentPath));
- return;
- }
- !(element.type === Route) ? invariant(false, `[${typeof element.type === "string" ? element.type : element.type.name}] is not a <Route> component. All component children of <Routes> must be a <Route> or <React.Fragment>`) : void 0;
- !(!element.props.index || !element.props.children) ? invariant(false, "An index route cannot have child routes.") : void 0;
- let treePath = [...parentPath, index];
- let route = {
- id: element.props.id || treePath.join("-"),
- caseSensitive: element.props.caseSensitive,
- element: element.props.element,
- index: element.props.index,
- path: element.props.path,
- loader: element.props.loader,
- action: element.props.action,
- errorElement: element.props.errorElement,
- hasErrorBoundary: element.props.errorElement != null,
- shouldRevalidate: element.props.shouldRevalidate,
- handle: element.props.handle
- };
- if (element.props.children) {
- route.children = createRoutesFromChildren(element.props.children, treePath);
- }
- routes.push(route);
- });
- return routes;
- }
- /**
- * Renders the result of `matchRoutes()` into a React element.
- */
- function renderMatches(matches) {
- return _renderMatches(matches);
- }
- /**
- * @private
- * Walk the route tree and add hasErrorBoundary if it's not provided, so that
- * users providing manual route arrays can just specify errorElement
- */
- function enhanceManualRouteObjects(routes) {
- return routes.map(route => {
- let routeClone = { ...route
- };
- if (routeClone.hasErrorBoundary == null) {
- routeClone.hasErrorBoundary = routeClone.errorElement != null;
- }
- if (routeClone.children) {
- routeClone.children = enhanceManualRouteObjects(routeClone.children);
- }
- return routeClone;
- });
- }
- function createMemoryRouter(routes, opts) {
- return createRouter({
- basename: opts?.basename,
- history: createMemoryHistory({
- initialEntries: opts?.initialEntries,
- initialIndex: opts?.initialIndex
- }),
- hydrationData: opts?.hydrationData,
- routes: enhanceManualRouteObjects(routes)
- }).initialize();
- } ///////////////////////////////////////////////////////////////////////////////
- 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 };
- //# sourceMappingURL=react-router.development.js.map
|