INDIE UNPKG

33.1 kBJavaScriptView Raw
1/**
2 * react-router v8.5.0
3 *
4 * Copyright (c) Remix Software Inc.
5 *
6 * This source code is licensed under the MIT license found in the
7 * LICENSE.md file in the root directory of this source tree.
8 *
9 * @license MIT
10 */
11import { createMemoryHistory, createPath, invariant, parsePath, warning } from "./router/history.js";
12import { defaultMapRouteProperties, getResolveToMatches, getRoutePattern, resolveTo, stripBasename } from "./router/utils.js";
13import { getNavigatorCurrentUrl, validateNavigationTarget } from "./router/navigation.js";
14import { clientErrors, createRouter } from "./router/router.js";
15import { AwaitContext, DataRouterContext, DataRouterDataContext, DataRouterNavigationContext, DataRouterStateContext, FetchersContext, LocationContext, NavigationContext, RouteContext, ViewTransitionContext, useIsRSCRouterContext } from "./context.js";
16import { _renderMatches, useActionData, useAsyncValue, useInRouterContext, useLoaderData, useLocation, useMatches, useNavigate, useOutlet, useParams, useRouteError, useRoutes, useRoutesImpl } from "./hooks.js";
17import { warnOnce } from "./server-runtime/warnings.js";
18import { FrameworkContext } from "./dom/ssr/components.js";
19import * as React$1 from "react";
20import { useOptimistic } from "react";
21//#region lib/components.tsx
22const hydrationRouteProperties = ["HydrateFallback", "hydrateFallbackElement"];
23/**
24* Create a new {@link DataRouter} that manages the application path using an
25* in-memory [`History`](https://developer.mozilla.org/en-US/docs/Web/API/History)
26* stack. Useful for non-browser environments without a DOM API.
27*
28* Data Routers should not be held in React state. You should create your router
29* once outside of the React tree and pass it to {@link RouterProvider | `<RouterProvider>`}.
30* You can use `patchRoutesOnNavigation` to add additional routes programmatically.
31*
32* @public
33* @category Data Routers
34* @mode data
35* @param routes Application routes
36* @param opts Options
37* @param {MemoryRouterOpts.basename} opts.basename n/a
38* @param {MemoryRouterOpts.dataStrategy} opts.dataStrategy n/a
39* @param {MemoryRouterOpts.future} opts.future n/a
40* @param {MemoryRouterOpts.getContext} opts.getContext n/a
41* @param {MemoryRouterOpts.hydrationData} opts.hydrationData n/a
42* @param {MemoryRouterOpts.initialEntries} opts.initialEntries n/a
43* @param {MemoryRouterOpts.initialIndex} opts.initialIndex n/a
44* @param {MemoryRouterOpts.instrumentations} opts.instrumentations n/a
45* @param {MemoryRouterOpts.patchRoutesOnNavigation} opts.patchRoutesOnNavigation n/a
46* @returns An initialized {@link DataRouter} to pass to {@link RouterProvider | `<RouterProvider>`}
47*/
48function createMemoryRouter(routes, opts) {
49 return createRouter({
50 basename: opts?.basename,
51 getContext: opts?.getContext,
52 future: opts?.future,
53 history: createMemoryHistory({
54 initialEntries: opts?.initialEntries,
55 initialIndex: opts?.initialIndex
56 }),
57 hydrationData: opts?.hydrationData,
58 routes,
59 mapRouteProperties: defaultMapRouteProperties,
60 hydrationRouteProperties,
61 dataStrategy: opts?.dataStrategy,
62 patchRoutesOnNavigation: opts?.patchRoutesOnNavigation,
63 instrumentations: opts?.instrumentations
64 }).initialize();
65}
66var Deferred = class {
67 status = "pending";
68 promise;
69 resolve;
70 reject;
71 constructor() {
72 this.promise = new Promise((resolve, reject) => {
73 this.resolve = (value) => {
74 if (this.status === "pending") {
75 this.status = "resolved";
76 resolve(value);
77 }
78 };
79 this.reject = (reason) => {
80 if (this.status === "pending") {
81 this.status = "rejected";
82 reject(reason);
83 }
84 };
85 });
86 }
87};
88/**
89* Render the UI for the given {@link DataRouter}. This component should
90* typically be at the top of an app's element tree. The router prop should
91* be a single router instance created outside of the React tree. Avoid
92* creating new routers during React renders/re-renders.
93*
94* ```tsx
95* import { createBrowserRouter } from "react-router";
96* import { RouterProvider } from "react-router/dom";
97* import { createRoot } from "react-dom/client";
98*
99* const router = createBrowserRouter(routes);
100* createRoot(document.getElementById("root")).render(
101* <RouterProvider router={router} />
102* );
103* ```
104*
105* <docs-info>Please note that this component is exported both from
106* `react-router` and `react-router/dom` with the only difference being that the
107* latter automatically wires up `react-dom`'s [`flushSync`](https://react.dev/reference/react-dom/flushSync)
108* implementation. You _almost always_ want to use the version from
109* `react-router/dom` unless you're running in a non-DOM environment.</docs-info>
110*
111*
112* @public
113* @category Data Routers
114* @mode data
115* @param props Props
116* @param {RouterProviderProps.flushSync} props.flushSync n/a
117* @param {RouterProviderProps.onError} props.onError n/a
118* @param {RouterProviderProps.router} props.router n/a
119* @param {RouterProviderProps.useTransitions} props.useTransitions n/a
120* @returns React element for the rendered router
121*/
122function RouterProvider({ router, flushSync: reactDomFlushSyncImpl, onError, useTransitions }) {
123 useTransitions = useIsRSCRouterContext() || useTransitions;
124 let [_state, setStateImpl] = React$1.useState(router.state);
125 let [state, setOptimisticState] = useOptimistic(_state);
126 let lastState = React$1.useRef(state);
127 let [pendingState, setPendingState] = React$1.useState();
128 let [vtContext, setVtContext] = React$1.useState({ isTransitioning: false });
129 let [renderDfd, setRenderDfd] = React$1.useState();
130 let [transition, setTransition] = React$1.useState();
131 let [interruption, setInterruption] = React$1.useState();
132 let fetcherData = React$1.useRef(/* @__PURE__ */ new Map());
133 let handledErrors = React$1.useRef(/* @__PURE__ */ new WeakSet());
134 let setState = React$1.useCallback((newState, { deletedFetchers, newErrors, flushSync, viewTransitionOpts }) => {
135 lastState.current = newState;
136 if (newErrors && newState.errors && clientErrors.has(newState.errors)) if (handledErrors.current.has(newState.errors)) newErrors = null;
137 else handledErrors.current.add(newState.errors);
138 if (newErrors && onError) Object.values(newErrors).forEach((error) => onError(error, {
139 location: newState.location,
140 params: newState.matches[0]?.params ?? {},
141 pattern: getRoutePattern(newState.matches)
142 }));
143 newState.fetchers.forEach((fetcher, key) => {
144 if (fetcher.data !== void 0) fetcherData.current.set(key, fetcher.data);
145 });
146 deletedFetchers.forEach((key) => fetcherData.current.delete(key));
147 warnOnce(flushSync === false || reactDomFlushSyncImpl != null, "You provided the `flushSync` option to a router update, but you are not using the `<RouterProvider>` from `react-router/dom` so `ReactDOM.flushSync()` is unavailable. Please update your app to `import { RouterProvider } from \"react-router/dom\"` and ensure you have `react-dom` installed as a dependency to use the `flushSync` option.");
148 let isViewTransitionAvailable = router.window != null && router.window.document != null && typeof router.window.document.startViewTransition === "function";
149 warnOnce(viewTransitionOpts == null || isViewTransitionAvailable, "You provided the `viewTransition` option to a router update, but you do not appear to be running in a DOM environment as `window.startViewTransition` is not available.");
150 if (!viewTransitionOpts || !isViewTransitionAvailable) {
151 if (reactDomFlushSyncImpl && flushSync) reactDomFlushSyncImpl(() => setStateImpl(newState));
152 else if (useTransitions === false) setStateImpl(newState);
153 else React$1.startTransition(() => {
154 if (useTransitions === true) setOptimisticState((s) => getOptimisticRouterState(s, newState));
155 setStateImpl(newState);
156 });
157 return;
158 }
159 if (reactDomFlushSyncImpl && flushSync) {
160 reactDomFlushSyncImpl(() => {
161 if (transition) {
162 renderDfd?.resolve();
163 transition.skipTransition();
164 }
165 setVtContext({
166 isTransitioning: true,
167 flushSync: true,
168 currentLocation: viewTransitionOpts.currentLocation,
169 nextLocation: viewTransitionOpts.nextLocation
170 });
171 });
172 let t = router.window.document.startViewTransition(() => {
173 reactDomFlushSyncImpl(() => setStateImpl(newState));
174 });
175 t.finished.finally(() => {
176 reactDomFlushSyncImpl(() => {
177 setRenderDfd(void 0);
178 setTransition(void 0);
179 setPendingState(void 0);
180 setVtContext({ isTransitioning: false });
181 });
182 });
183 reactDomFlushSyncImpl(() => setTransition(t));
184 return;
185 }
186 if (transition) {
187 renderDfd?.resolve();
188 transition.skipTransition();
189 setInterruption({
190 state: newState,
191 currentLocation: viewTransitionOpts.currentLocation,
192 nextLocation: viewTransitionOpts.nextLocation
193 });
194 } else {
195 setPendingState(newState);
196 setVtContext({
197 isTransitioning: true,
198 flushSync: false,
199 currentLocation: viewTransitionOpts.currentLocation,
200 nextLocation: viewTransitionOpts.nextLocation
201 });
202 }
203 }, [
204 router.window,
205 reactDomFlushSyncImpl,
206 transition,
207 renderDfd,
208 useTransitions,
209 setOptimisticState,
210 onError
211 ]);
212 React$1.useLayoutEffect(() => router.subscribe(setState), [router, setState]);
213 React$1.useLayoutEffect(() => {
214 let errors = router.state.errors;
215 let newErrors = errors ? clientErrors.get(errors) : null;
216 if (lastState.current !== router.state || errors && newErrors && !handledErrors.current.has(errors)) setState(router.state, {
217 deletedFetchers: [],
218 flushSync: false,
219 newErrors: newErrors ?? null
220 });
221 }, [router, setState]);
222 React$1.useEffect(() => {
223 if (vtContext.isTransitioning && !vtContext.flushSync) setRenderDfd(new Deferred());
224 }, [vtContext]);
225 React$1.useEffect(() => {
226 if (renderDfd && pendingState && router.window) {
227 let newState = pendingState;
228 let renderPromise = renderDfd.promise;
229 let transition = router.window.document.startViewTransition(async () => {
230 if (useTransitions === false) setStateImpl(newState);
231 else React$1.startTransition(() => {
232 if (useTransitions === true) setOptimisticState((s) => getOptimisticRouterState(s, newState));
233 setStateImpl(newState);
234 });
235 await renderPromise;
236 });
237 transition.finished.finally(() => {
238 setRenderDfd(void 0);
239 setTransition(void 0);
240 setPendingState(void 0);
241 setVtContext({ isTransitioning: false });
242 });
243 setTransition(transition);
244 }
245 }, [
246 pendingState,
247 renderDfd,
248 router.window,
249 useTransitions,
250 setOptimisticState
251 ]);
252 React$1.useEffect(() => {
253 if (renderDfd && pendingState && state.location.key === pendingState.location.key) renderDfd.resolve();
254 }, [
255 renderDfd,
256 transition,
257 state.location,
258 pendingState
259 ]);
260 React$1.useEffect(() => {
261 if (!vtContext.isTransitioning && interruption) {
262 setPendingState(interruption.state);
263 setVtContext({
264 isTransitioning: true,
265 flushSync: false,
266 currentLocation: interruption.currentLocation,
267 nextLocation: interruption.nextLocation
268 });
269 setInterruption(void 0);
270 }
271 }, [vtContext.isTransitioning, interruption]);
272 let navigator = React$1.useMemo(() => {
273 return {
274 createHref: router.createHref,
275 createURL: router.createURL,
276 encodeLocation: router.encodeLocation,
277 go: (n) => router.navigate(n),
278 push: (to, state, opts) => router.navigate(to, {
279 state,
280 preventScrollReset: opts?.preventScrollReset
281 }),
282 replace: (to, state, opts) => router.navigate(to, {
283 replace: true,
284 state,
285 preventScrollReset: opts?.preventScrollReset
286 })
287 };
288 }, [router]);
289 let basename = router.basename || "/";
290 let dataRouterContext = React$1.useMemo(() => ({
291 router,
292 navigator,
293 static: false,
294 basename,
295 onError
296 }), [
297 router,
298 navigator,
299 basename,
300 onError
301 ]);
302 let dataRouterState = React$1.useMemo(() => ({
303 historyAction: state.historyAction,
304 location: state.location,
305 matches: state.matches,
306 initialized: state.initialized,
307 renderFallback: state.renderFallback,
308 restoreScrollPosition: state.restoreScrollPosition,
309 preventScrollReset: state.preventScrollReset,
310 blockers: state.blockers
311 }), [
312 state.historyAction,
313 state.location,
314 state.matches,
315 state.initialized,
316 state.renderFallback,
317 state.restoreScrollPosition,
318 state.preventScrollReset,
319 state.blockers
320 ]);
321 let dataRouterNavigation = React$1.useMemo(() => ({
322 navigation: state.navigation,
323 revalidation: state.revalidation
324 }), [state.navigation, state.revalidation]);
325 let dataRouterData = React$1.useMemo(() => ({
326 loaderData: state.loaderData,
327 actionData: state.actionData,
328 errors: state.errors
329 }), [
330 state.loaderData,
331 state.actionData,
332 state.errors
333 ]);
334 let fetchersContext = React$1.useMemo(() => ({
335 fetchers: state.fetchers,
336 fetcherData: fetcherData.current
337 }), [state.fetchers]);
338 return /* @__PURE__ */ React$1.createElement(React$1.Fragment, null, /* @__PURE__ */ React$1.createElement(DataRouterContext.Provider, { value: dataRouterContext }, /* @__PURE__ */ React$1.createElement(DataRouterStateContext.Provider, { value: dataRouterState }, /* @__PURE__ */ React$1.createElement(DataRouterNavigationContext.Provider, { value: dataRouterNavigation }, /* @__PURE__ */ React$1.createElement(DataRouterDataContext.Provider, { value: dataRouterData }, /* @__PURE__ */ React$1.createElement(FetchersContext.Provider, { value: fetchersContext }, /* @__PURE__ */ React$1.createElement(ViewTransitionContext.Provider, { value: vtContext }, /* @__PURE__ */ React$1.createElement(Router, {
339 basename,
340 location: state.location,
341 navigationType: state.historyAction,
342 navigator,
343 useTransitions
344 }, /* @__PURE__ */ React$1.createElement(MemoizedDataRoutes, {
345 routes: router.routes,
346 manifest: router.manifest,
347 state,
348 isStatic: false,
349 onError
350 })))))))), null);
351}
352function getOptimisticRouterState(currentState, newState) {
353 return {
354 ...currentState,
355 navigation: newState.navigation.state !== "idle" ? newState.navigation : currentState.navigation,
356 revalidation: newState.revalidation !== "idle" ? newState.revalidation : currentState.revalidation,
357 actionData: newState.navigation.state !== "submitting" ? newState.actionData : currentState.actionData,
358 fetchers: newState.fetchers
359 };
360}
361const MemoizedDataRoutes = React$1.memo(DataRoutes);
362function DataRoutes({ routes, manifest, state, isStatic, onError }) {
363 let dataRouterContext = React$1.useContext(DataRouterContext);
364 invariant(dataRouterContext, "You must render this element inside a <DataRouterContext.Provider> element");
365 let discoveryRuntime = React$1.useContext(FrameworkContext)?.routeDiscoveryRuntime;
366 React$1.useEffect(() => {
367 if (state.initialized) discoveryRuntime?.markReady();
368 }, [state.initialized, discoveryRuntime]);
369 return useRoutesImpl(routes, void 0, {
370 router: dataRouterContext.router,
371 manifest,
372 state,
373 isStatic,
374 onError
375 });
376}
377/**
378* A declarative {@link Router | `<Router>`} that stores all entries in memory.
379*
380* @public
381* @category Declarative Routers
382* @mode declarative
383* @param props Props
384* @param {MemoryRouterProps.basename} props.basename n/a
385* @param {MemoryRouterProps.children} props.children n/a
386* @param {MemoryRouterProps.initialEntries} props.initialEntries n/a
387* @param {MemoryRouterProps.initialIndex} props.initialIndex n/a
388* @param {MemoryRouterProps.useTransitions} props.useTransitions n/a
389* @returns A declarative in-memory {@link Router | `<Router>`} for client-side
390* routing.
391*/
392function MemoryRouter({ basename, children, initialEntries, initialIndex, useTransitions }) {
393 let historyRef = React$1.useRef(null);
394 if (historyRef.current == null) historyRef.current = createMemoryHistory({
395 initialEntries,
396 initialIndex,
397 v5Compat: true
398 });
399 let history = historyRef.current;
400 let [state, setStateImpl] = React$1.useState({
401 action: history.action,
402 location: history.location
403 });
404 let setState = React$1.useCallback((newState) => {
405 if (useTransitions === false) setStateImpl(newState);
406 else React$1.startTransition(() => setStateImpl(newState));
407 }, [useTransitions]);
408 React$1.useLayoutEffect(() => history.listen(setState), [history, setState]);
409 return /* @__PURE__ */ React$1.createElement(Router, {
410 basename,
411 children,
412 location: state.location,
413 navigationType: state.action,
414 navigator: history,
415 useTransitions
416 });
417}
418/**
419* A component-based version of {@link useNavigate} to use in a
420* [`React.Component` class](https://react.dev/reference/react/Component) where
421* hooks cannot be used.
422*
423* It's recommended to avoid using this component in favor of {@link useNavigate}.
424*
425* @example
426* <Navigate to="/tasks" />
427*
428* @public
429* @category Components
430* @param props Props
431* @param {NavigateProps.relative} props.relative n/a
432* @param {NavigateProps.replace} props.replace n/a
433* @param {NavigateProps.state} props.state n/a
434* @param {NavigateProps.to} props.to n/a
435* @returns {void}
436*
437*/
438function Navigate({ to, replace, state, relative }) {
439 invariant(useInRouterContext(), `<Navigate> may be used only in the context of a <Router> component.`);
440 let { static: isStatic, navigator } = React$1.useContext(NavigationContext);
441 warning(!isStatic, "<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.");
442 let { matches } = React$1.useContext(RouteContext);
443 let { pathname: locationPathname } = useLocation();
444 let navigate = useNavigate();
445 let path = resolveTo(to, getResolveToMatches(matches), locationPathname, relative === "path");
446 validateNavigationTarget(typeof to === "string" ? to : createPath(to), navigator.createHref(path), getNavigatorCurrentUrl(navigator), "reject");
447 let jsonPath = JSON.stringify(path);
448 React$1.useEffect(() => {
449 navigate(JSON.parse(jsonPath), {
450 replace,
451 state,
452 relative
453 });
454 }, [
455 navigate,
456 jsonPath,
457 relative,
458 replace,
459 state
460 ]);
461 return null;
462}
463/**
464* Renders the matching child route of a parent route or nothing if no child
465* route matches.
466*
467* @example
468* import { Outlet } from "react-router";
469*
470* export default function SomeParent() {
471* return (
472* <div>
473* <h1>Parent Content</h1>
474* <Outlet />
475* </div>
476* );
477* }
478*
479* @public
480* @category Components
481* @param props Props
482* @param {OutletProps.context} props.context n/a
483* @returns React element for the rendered outlet or `null` if no child route matches.
484*/
485function Outlet(props) {
486 return useOutlet(props.context);
487}
488/**
489* Configures an element to render when a pattern matches the current location.
490* It must be rendered within a {@link Routes} element. Note that these routes
491* do not participate in data loading, actions, code splitting, or any other
492* route module features.
493*
494* @example
495* // Usually used in a declarative router
496* function App() {
497* return (
498* <BrowserRouter>
499* <Routes>
500* <Route index element={<StepOne />} />
501* <Route path="step-2" element={<StepTwo />} />
502* <Route path="step-3" element={<StepThree />} />
503* </Routes>
504* </BrowserRouter>
505* );
506* }
507*
508* // But can be used with a data router as well if you prefer the JSX notation
509* const routes = createRoutesFromElements(
510* <>
511* <Route index loader={step1Loader} Component={StepOne} />
512* <Route path="step-2" loader={step2Loader} Component={StepTwo} />
513* <Route path="step-3" loader={step3Loader} Component={StepThree} />
514* </>
515* );
516*
517* const router = createBrowserRouter(routes);
518*
519* function App() {
520* return <RouterProvider router={router} />;
521* }
522*
523* @public
524* @category Components
525* @param props Props
526* @param {PathRouteProps.action} props.action n/a
527* @param {PathRouteProps.caseSensitive} props.caseSensitive n/a
528* @param {PathRouteProps.Component} props.Component n/a
529* @param {PathRouteProps.children} props.children n/a
530* @param {PathRouteProps.element} props.element n/a
531* @param {PathRouteProps.ErrorBoundary} props.ErrorBoundary n/a
532* @param {PathRouteProps.errorElement} props.errorElement n/a
533* @param {PathRouteProps.handle} props.handle n/a
534* @param {PathRouteProps.HydrateFallback} props.HydrateFallback n/a
535* @param {PathRouteProps.hydrateFallbackElement} props.hydrateFallbackElement n/a
536* @param {PathRouteProps.id} props.id n/a
537* @param {PathRouteProps.index} props.index n/a
538* @param {PathRouteProps.lazy} props.lazy n/a
539* @param {PathRouteProps.loader} props.loader n/a
540* @param {PathRouteProps.path} props.path n/a
541* @param {PathRouteProps.shouldRevalidate} props.shouldRevalidate n/a
542* @returns {void}
543*/
544function Route(props) {
545 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>.");
546}
547/**
548* Provides location context for the rest of the app.
549*
550* Note: You usually won't render a `<Router>` directly. Instead, you'll render a
551* router that is more specific to your environment such as a {@link BrowserRouter}
552* in web browsers or a {@link ServerRouter} for server rendering.
553*
554* @public
555* @category Declarative Routers
556* @mode declarative
557* @param props Props
558* @param {RouterProps.basename} props.basename n/a
559* @param {RouterProps.children} props.children n/a
560* @param {RouterProps.location} props.location n/a
561* @param {RouterProps.navigationType} props.navigationType n/a
562* @param {RouterProps.navigator} props.navigator n/a
563* @param {RouterProps.static} props.static n/a
564* @param {RouterProps.useTransitions} props.useTransitions n/a
565* @returns React element for the rendered router or `null` if the location does
566* not match the {@link props.basename}
567*/
568function Router({ basename: basenameProp = "/", children = null, location: locationProp, navigationType = "POP", navigator, static: staticProp = false, useTransitions }) {
569 invariant(!useInRouterContext(), "You cannot render a <Router> inside another <Router>. You should never have more than one in your app.");
570 let basename = basenameProp.replace(/^\/*/, "/");
571 let navigationContext = React$1.useMemo(() => ({
572 basename,
573 navigator,
574 static: staticProp,
575 useTransitions,
576 future: {}
577 }), [
578 basename,
579 navigator,
580 staticProp,
581 useTransitions
582 ]);
583 if (typeof locationProp === "string") locationProp = parsePath(locationProp);
584 let { pathname = "/", search = "", hash = "", state = null, key = "default", mask } = locationProp;
585 let locationContext = React$1.useMemo(() => {
586 let trailingPathname = stripBasename(pathname, basename);
587 if (trailingPathname == null) return null;
588 return {
589 location: {
590 pathname: trailingPathname,
591 search,
592 hash,
593 state,
594 key,
595 mask
596 },
597 navigationType
598 };
599 }, [
600 basename,
601 pathname,
602 search,
603 hash,
604 state,
605 key,
606 navigationType,
607 mask
608 ]);
609 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.`);
610 if (locationContext == null) return null;
611 return /* @__PURE__ */ React$1.createElement(NavigationContext.Provider, { value: navigationContext }, /* @__PURE__ */ React$1.createElement(LocationContext.Provider, {
612 children,
613 value: locationContext
614 }));
615}
616/**
617* Renders a branch of {@link Route | `<Route>`s} that best matches the current
618* location. Note that these routes do not participate in [data loading](../../start/framework/route-module#loader),
619* [`action`](../../start/framework/route-module#action), code splitting, or
620* any other [route module](../../start/framework/route-module) features.
621*
622* @example
623* import { Route, Routes } from "react-router";
624*
625* <Routes>
626* <Route index element={<StepOne />} />
627* <Route path="step-2" element={<StepTwo />} />
628* <Route path="step-3" element={<StepThree />} />
629* </Routes>
630*
631* @public
632* @category Components
633* @param props Props
634* @param {RoutesProps.children} props.children n/a
635* @param {RoutesProps.location} props.location n/a
636* @returns React element for the rendered routes or `null` if no route matches
637*/
638function Routes({ children, location }) {
639 return useRoutes(createRoutesFromChildren(children), location);
640}
641/**
642* Used to render promise values with automatic error handling.
643*
644* **Note:** `<Await>` expects to be rendered inside a [`<React.Suspense>`](https://react.dev/reference/react/Suspense)
645*
646* @example
647* import { Await, useLoaderData } from "react-router";
648*
649* export async function loader() {
650* // not awaited
651* const reviews = getReviews();
652* // awaited (blocks the transition)
653* const book = await fetch("/api/book").then((res) => res.json());
654* return { book, reviews };
655* }
656*
657* function Book() {
658* const { book, reviews } = useLoaderData();
659* return (
660* <div>
661* <h1>{book.title}</h1>
662* <p>{book.description}</p>
663* <React.Suspense fallback={<ReviewsSkeleton />}>
664* <Await
665* resolve={reviews}
666* errorElement={
667* <div>Could not load reviews 😬</div>
668* }
669* children={(resolvedReviews) => (
670* <Reviews items={resolvedReviews} />
671* )}
672* />
673* </React.Suspense>
674* </div>
675* );
676* }
677*
678* @public
679* @category Components
680* @mode framework
681* @mode data
682* @param props Props
683* @param {AwaitProps.children} props.children n/a
684* @param {AwaitProps.errorElement} props.errorElement n/a
685* @param {AwaitProps.resolve} props.resolve n/a
686* @returns React element for the rendered awaited value
687*/
688function Await({ children, errorElement, resolve }) {
689 let dataRouterContext = React$1.useContext(DataRouterContext);
690 let dataRouterStateContext = React$1.useContext(DataRouterStateContext);
691 let onError = React$1.useCallback((error, errorInfo) => {
692 if (dataRouterContext && dataRouterContext.onError && dataRouterStateContext) dataRouterContext.onError(error, {
693 location: dataRouterStateContext.location,
694 params: dataRouterStateContext.matches[0]?.params || {},
695 pattern: getRoutePattern(dataRouterStateContext.matches),
696 errorInfo
697 });
698 }, [dataRouterContext, dataRouterStateContext]);
699 return /* @__PURE__ */ React$1.createElement(AwaitErrorBoundary, {
700 resolve,
701 errorElement,
702 onError
703 }, /* @__PURE__ */ React$1.createElement(ResolveAwait, null, children));
704}
705var AwaitErrorBoundary = class extends React$1.Component {
706 constructor(props) {
707 super(props);
708 this.state = { error: null };
709 }
710 static getDerivedStateFromError(error) {
711 return { error };
712 }
713 componentDidCatch(error, errorInfo) {
714 if (this.props.onError) this.props.onError(error, errorInfo);
715 else console.error("<Await> caught the following error during render", error, errorInfo);
716 }
717 render() {
718 let { children, errorElement, resolve } = this.props;
719 let promise = null;
720 let status = 0;
721 if (!(resolve instanceof Promise)) {
722 status = 1;
723 promise = Promise.resolve();
724 Object.defineProperty(promise, "_tracked", { get: () => true });
725 Object.defineProperty(promise, "_data", { get: () => resolve });
726 } else if (this.state.error) {
727 status = 2;
728 let renderError = this.state.error;
729 promise = Promise.reject().catch(() => {});
730 Object.defineProperty(promise, "_tracked", { get: () => true });
731 Object.defineProperty(promise, "_error", { get: () => renderError });
732 } else if (resolve._tracked) {
733 promise = resolve;
734 status = "_error" in promise ? 2 : "_data" in promise ? 1 : 0;
735 } else {
736 status = 0;
737 Object.defineProperty(resolve, "_tracked", { get: () => true });
738 promise = Promise.resolve(resolve).then((data) => Object.defineProperty(resolve, "_data", { get: () => data }), (error) => {
739 this.props.onError?.(error);
740 Object.defineProperty(resolve, "_error", { get: () => error });
741 });
742 }
743 if (status === 2 && !errorElement) throw promise._error;
744 if (status === 2) return /* @__PURE__ */ React$1.createElement(AwaitContext.Provider, {
745 value: promise,
746 children: errorElement
747 });
748 if (status === 1) return /* @__PURE__ */ React$1.createElement(AwaitContext.Provider, {
749 value: promise,
750 children
751 });
752 throw promise;
753 }
754};
755function ResolveAwait({ children }) {
756 let data = useAsyncValue();
757 let toRender = typeof children === "function" ? children(data) : children;
758 return /* @__PURE__ */ React$1.createElement(React$1.Fragment, null, toRender);
759}
760/**
761* Creates a route config from a React "children" object, which is usually
762* either a `<Route>` element or an array of them. Used internally by
763* `<Routes>` to create a route config from its children.
764*
765* @category Utils
766* @mode data
767* @param children The React children to convert into a route config
768* @param parentPath The path of the parent route, used to generate unique IDs.
769* @returns An array of {@link RouteObject}s that can be used with a {@link DataRouter}
770*/
771function createRoutesFromChildren(children, parentPath = []) {
772 let routes = [];
773 React$1.Children.forEach(children, (element, index) => {
774 if (!React$1.isValidElement(element)) return;
775 let treePath = [...parentPath, index];
776 if (element.type === React$1.Fragment) {
777 routes.push.apply(routes, createRoutesFromChildren(element.props.children, treePath));
778 return;
779 }
780 invariant(element.type === Route, `[${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>`);
781 let props = element.props;
782 invariant(!props.index || !props.children, "An index route cannot have child routes.");
783 let route = {
784 id: props.id || treePath.join("-"),
785 caseSensitive: props.caseSensitive,
786 element: props.element,
787 Component: props.Component,
788 index: props.index,
789 path: props.path,
790 middleware: props.middleware,
791 loader: props.loader,
792 action: props.action,
793 hydrateFallbackElement: props.hydrateFallbackElement,
794 HydrateFallback: props.HydrateFallback,
795 errorElement: props.errorElement,
796 ErrorBoundary: props.ErrorBoundary,
797 shouldRevalidate: props.shouldRevalidate,
798 handle: props.handle,
799 lazy: props.lazy
800 };
801 if (props.children) route.children = createRoutesFromChildren(props.children, treePath);
802 routes.push(route);
803 });
804 return routes;
805}
806/**
807* Create route objects from JSX elements instead of arrays of objects.
808*
809* @example
810* const routes = createRoutesFromElements(
811* <>
812* <Route index loader={step1Loader} Component={StepOne} />
813* <Route path="step-2" loader={step2Loader} Component={StepTwo} />
814* <Route path="step-3" loader={step3Loader} Component={StepThree} />
815* </>
816* );
817*
818* const router = createBrowserRouter(routes);
819*
820* function App() {
821* return <RouterProvider router={router} />;
822* }
823*
824* @name createRoutesFromElements
825* @public
826* @category Utils
827* @mode data
828* @param children The React children to convert into a route config
829* @param parentPath The path of the parent route, used to generate unique IDs.
830* This is used for internal recursion and is not intended to be used by the
831* application developer.
832* @returns An array of {@link RouteObject}s that can be used with a {@link DataRouter}
833*/
834const createRoutesFromElements = createRoutesFromChildren;
835/**
836* Renders the result of {@link matchRoutes} into a React element.
837*
838* @public
839* @category Utils
840* @param matches The array of {@link RouteMatch | route matches} to render
841* @returns A React element that renders the matched routes or `null` if no matches
842*/
843function renderMatches(matches) {
844 return _renderMatches(matches);
845}
846function useRouteComponentProps() {
847 return {
848 params: useParams(),
849 loaderData: useLoaderData(),
850 actionData: useActionData(),
851 matches: useMatches()
852 };
853}
854function WithComponentProps({ children }) {
855 const props = useRouteComponentProps();
856 return React$1.cloneElement(children, props);
857}
858function withComponentProps(Component) {
859 return function WithComponentProps() {
860 const props = useRouteComponentProps();
861 return React$1.createElement(Component, props);
862 };
863}
864function useHydrateFallbackProps() {
865 return {
866 params: useParams(),
867 loaderData: useLoaderData(),
868 actionData: useActionData()
869 };
870}
871function WithHydrateFallbackProps({ children }) {
872 const props = useHydrateFallbackProps();
873 return React$1.cloneElement(children, props);
874}
875function withHydrateFallbackProps(HydrateFallback) {
876 return function WithHydrateFallbackProps() {
877 const props = useHydrateFallbackProps();
878 return React$1.createElement(HydrateFallback, props);
879 };
880}
881function useErrorBoundaryProps() {
882 return {
883 params: useParams(),
884 loaderData: useLoaderData(),
885 actionData: useActionData(),
886 error: useRouteError()
887 };
888}
889function WithErrorBoundaryProps({ children }) {
890 const props = useErrorBoundaryProps();
891 return React$1.cloneElement(children, props);
892}
893function withErrorBoundaryProps(ErrorBoundary) {
894 return function WithErrorBoundaryProps() {
895 const props = useErrorBoundaryProps();
896 return React$1.createElement(ErrorBoundary, props);
897 };
898}
899//#endregion
900export { Await, DataRoutes, MemoryRouter, Navigate, Outlet, Route, Router, RouterProvider, Routes, WithComponentProps, WithErrorBoundaryProps, WithHydrateFallbackProps, createMemoryRouter, createRoutesFromChildren, createRoutesFromElements, hydrationRouteProperties, renderMatches, withComponentProps, withErrorBoundaryProps, withHydrateFallbackProps };