UNPKG

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