UNPKG

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