Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@remix-run/react

Package Overview
Dependencies
Maintainers
2
Versions
1041
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@remix-run/react - npm Package Compare versions

Comparing version 0.0.0-nightly-ccefed3-20230621 to 0.0.0-nightly-cd403b516-20240809

dist/esm/fallback.js

2

dist/_virtual/_rollupPluginBabelHelpers.js
/**
* @remix-run/react v0.0.0-nightly-ccefed3-20230621
* @remix-run/react v0.0.0-nightly-cd403b516-20240809
*

@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc.

@@ -1,4 +0,4 @@

import type { HydrationState } from "@remix-run/router";
import type { HydrationState, Router } from "@remix-run/router";
import type { ReactElement } from "react";
import type { EntryContext, FutureConfig } from "./entry";
import type { AssetsManifest, FutureConfig } from "./entry";
import type { RouteModules } from "./routeModules";

@@ -8,4 +8,9 @@ declare global {

url: string;
basename?: string;
state: HydrationState;
criticalCss?: string;
future: FutureConfig;
isSpaMode: boolean;
stream: ReadableStream<Uint8Array> | undefined;
streamController: ReadableStreamDefaultController<Uint8Array>;
a?: number;

@@ -17,5 +22,7 @@ dev?: {

};
var __remixRouter: Router;
var __remixRouteModules: RouteModules;
var __remixManifest: EntryContext["manifest"];
var __remixManifest: AssetsManifest;
var __remixRevalidation: number | undefined;
var __remixClearCriticalCss: (() => void) | undefined;
var $RefreshRuntime$: {

@@ -27,7 +34,2 @@ performReactRefresh: () => void;

}
declare global {
interface ImportMeta {
hot: any;
}
}
/**

@@ -34,0 +36,0 @@ * The entry point for a Remix app when it is rendered in the browser (in

/**
* @remix-run/react v0.0.0-nightly-ccefed3-20230621
* @remix-run/react v0.0.0-nightly-cd403b516-20240809
*

@@ -15,3 +15,5 @@ * Copyright (c) Remix Software Inc.

var router$1 = require('@remix-run/router');
var React = require('react');
var reactRouter = require('react-router');
var reactRouterDom = require('react-router-dom');

@@ -22,2 +24,5 @@ var components = require('./components.js');

var routes = require('./routes.js');
var singleFetch = require('./single-fetch.js');
var invariant = require('./invariant.js');
var fogOfWar = require('./fog-of-war.js');

@@ -48,3 +53,18 @@ function _interopNamespace(e) {

let stateDecodingPromise;
let router;
let routerInitialized = false;
let hmrRouterReadyResolve;
// There's a race condition with HMR where the remix:manifest is signaled before
// the router is assigned in the RemixBrowser component. This promise gates the
// HMR handler until the router is ready
new Promise(resolve => {
// body of a promise is executed immediately, so this can be resolved outside
// of the promise body
hmrRouterReadyResolve = resolve;
}).catch(() => {
// This is a noop catch handler to avoid unhandled promise rejection warnings
// in the console. The promise is never rejected.
return undefined;
});

@@ -58,37 +78,157 @@ /**

if (!router) {
let routes$1 = routes.createClientRoutes(window.__remixManifest.routes, window.__remixRouteModules, window.__remixContext.future);
let hydrationData = window.__remixContext.state;
if (hydrationData && hydrationData.errors) {
// Hard reload if the path we tried to load is not the current path.
// This is usually the result of 2 rapid back/forward clicks from an
// external site into a Remix app, where we initially start the load for
// one URL and while the JS chunks are loading a second forward click moves
// us to a new URL. Avoid comparing search params because of CDNs which
// can be configured to ignore certain params and only pathname is relevant
// towards determining the route matches.
let initialPathname = window.__remixContext.url;
let hydratedPathname = window.location.pathname;
if (initialPathname !== hydratedPathname && !window.__remixContext.isSpaMode) {
let errorMsg = `Initial URL (${initialPathname}) does not match URL at time of hydration ` + `(${hydratedPathname}), reloading page...`;
console.error(errorMsg);
window.location.reload();
// Get out of here so the reload can happen - don't create the router
// since it'll then kick off unnecessary route.lazy() loads
return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null);
}
// When single fetch is enabled, we need to suspend until the initial state
// snapshot is decoded into window.__remixContext.state
if (window.__remixContext.future.unstable_singleFetch) {
// Note: `stateDecodingPromise` is not coupled to `router` - we'll reach this
// code potentially many times waiting for our state to arrive, but we'll
// then only get past here and create the `router` one time
if (!stateDecodingPromise) {
let stream = window.__remixContext.stream;
invariant(stream, "No stream found for single fetch decoding");
window.__remixContext.stream = undefined;
stateDecodingPromise = singleFetch.decodeViaTurboStream(stream, window).then(value => {
window.__remixContext.state = value.value;
stateDecodingPromise.value = true;
}).catch(e => {
stateDecodingPromise.error = e;
});
}
if (stateDecodingPromise.error) {
throw stateDecodingPromise.error;
}
if (!stateDecodingPromise.value) {
throw stateDecodingPromise;
}
}
let routes$1 = routes.createClientRoutes(window.__remixManifest.routes, window.__remixRouteModules, window.__remixContext.state, window.__remixContext.future, window.__remixContext.isSpaMode);
let hydrationData = undefined;
if (!window.__remixContext.isSpaMode) {
// Create a shallow clone of `loaderData` we can mutate for partial hydration.
// When a route exports a `clientLoader` and a `HydrateFallback`, the SSR will
// render the fallback so we need the client to do the same for hydration.
// The server loader data has already been exposed to these route `clientLoader`'s
// in `createClientRoutes` above, so we need to clear out the version we pass to
// `createBrowserRouter` so it initializes and runs the client loaders.
hydrationData = {
...hydrationData,
errors: errors.deserializeErrors(hydrationData.errors)
...window.__remixContext.state,
loaderData: {
...window.__remixContext.state.loaderData
}
};
let initialMatches = reactRouterDom.matchRoutes(routes$1, window.location, window.__remixContext.basename);
if (initialMatches) {
for (let match of initialMatches) {
let routeId = match.route.id;
let route = window.__remixRouteModules[routeId];
let manifestRoute = window.__remixManifest.routes[routeId];
// Clear out the loaderData to avoid rendering the route component when the
// route opted into clientLoader hydration and either:
// * gave us a HydrateFallback
// * or doesn't have a server loader and we have no data to render
if (route && routes.shouldHydrateRouteLoader(manifestRoute, route, window.__remixContext.isSpaMode) && (route.HydrateFallback || !manifestRoute.hasLoader)) {
hydrationData.loaderData[routeId] = undefined;
} else if (manifestRoute && !manifestRoute.hasLoader) {
// Since every Remix route gets a `loader` on the client side to load
// the route JS module, we need to add a `null` value to `loaderData`
// for any routes that don't have server loaders so our partial
// hydration logic doesn't kick off the route module loaders during
// hydration
hydrationData.loaderData[routeId] = null;
}
}
}
if (hydrationData && hydrationData.errors) {
hydrationData.errors = errors.deserializeErrors(hydrationData.errors);
}
}
router = reactRouterDom.createBrowserRouter(routes$1, {
let {
enabled: isFogOfWarEnabled,
patchRoutesOnMiss
} = fogOfWar.initFogOfWar(window.__remixManifest, window.__remixRouteModules, window.__remixContext.future, window.__remixContext.isSpaMode, window.__remixContext.basename);
// We don't use createBrowserRouter here because we need fine-grained control
// over initialization to support synchronous `clientLoader` flows.
router = router$1.createRouter({
routes: routes$1,
history: router$1.createBrowserHistory(),
basename: window.__remixContext.basename,
future: {
v7_normalizeFormMethod: true,
v7_fetcherPersist: window.__remixContext.future.v3_fetcherPersist,
v7_partialHydration: true,
v7_prependBasename: true,
v7_relativeSplatPath: window.__remixContext.future.v3_relativeSplatPath,
// Single fetch enables this underlying behavior
v7_skipActionErrorRevalidation: window.__remixContext.future.unstable_singleFetch === true
},
hydrationData,
future: {
// Pass through the Remix future flag to avoid a v1 breaking change in
// useNavigation() - users can control the casing via the flag in v1.
// useFetcher still always uppercases in the back-compat layer in v1.
// In v2 we can just always pass true here and remove the back-compat
// layer
v7_normalizeFormMethod: window.__remixContext.future.v2_normalizeFormMethod
}
mapRouteProperties: reactRouter.UNSAFE_mapRouteProperties,
unstable_dataStrategy: window.__remixContext.future.unstable_singleFetch ? singleFetch.getSingleFetchDataStrategy(window.__remixManifest, window.__remixRouteModules) : undefined,
...(isFogOfWarEnabled ? {
unstable_patchRoutesOnMiss: patchRoutesOnMiss
} : {})
});
// Hard reload if the URL we tried to load is not the current URL.
// This is usually the result of 2 rapid backwards/forward clicks from an
// external site into a Remix app, where we initially start the load for
// one URL and while the JS chunks are loading a second forward click moves
// us to a new URL
let initialUrl = window.__remixContext.url;
let hydratedUrl = window.location.pathname + window.location.search;
if (initialUrl !== hydratedUrl) {
let errorMsg = `Initial URL (${initialUrl}) does not match URL at time of hydration ` + `(${hydratedUrl}), reloading page...`;
console.error(errorMsg);
window.location.reload();
// We can call initialize() immediately if the router doesn't have any
// loaders to run on hydration
if (router.state.initialized) {
routerInitialized = true;
router.initialize();
}
// @ts-ignore
router.createRoutesForHMR = routes.createClientRoutesWithHMRRevalidationOptOut;
window.__remixRouter = router;
// Notify that the router is ready for HMR
if (hmrRouterReadyResolve) {
hmrRouterReadyResolve(router);
}
}
// Critical CSS can become stale after code changes, e.g. styles might be
// removed from a component, but the styles will still be present in the
// server HTML. This allows our HMR logic to clear the critical CSS state.
// eslint-disable-next-line react-hooks/rules-of-hooks
let [criticalCss, setCriticalCss] = React__namespace.useState(process.env.NODE_ENV === "development" ? window.__remixContext.criticalCss : undefined);
if (process.env.NODE_ENV === "development") {
window.__remixClearCriticalCss = () => setCriticalCss(undefined);
}
// This is due to the short circuit return above when the pathname doesn't
// match and we force a hard reload. This is an exceptional scenario in which
// we can't hydrate anyway.
// eslint-disable-next-line react-hooks/rules-of-hooks
let [location, setLocation] = React__namespace.useState(router.state.location);
// eslint-disable-next-line react-hooks/rules-of-hooks
React__namespace.useLayoutEffect(() => {
// If we had to run clientLoaders on hydration, we delay initialization until
// after we've hydrated to avoid hydration issues from synchronous client loaders
if (!routerInitialized) {
routerInitialized = true;
router.initialize();
}
}, []);
// eslint-disable-next-line react-hooks/rules-of-hooks
React__namespace.useLayoutEffect(() => {
return router.subscribe(newState => {

@@ -101,2 +241,5 @@ if (newState.location !== location) {

// eslint-disable-next-line react-hooks/rules-of-hooks
fogOfWar.useFogOFWarDiscovery(router, window.__remixManifest, window.__remixRouteModules, window.__remixContext.future, window.__remixContext.isSpaMode);
// We need to include a wrapper RemixErrorBoundary here in case the root error

@@ -106,20 +249,26 @@ // boundary also throws and we need to bubble up outside of the router entirely.

// out of there
return /*#__PURE__*/React__namespace.createElement(components.RemixContext.Provider, {
value: {
manifest: window.__remixManifest,
routeModules: window.__remixRouteModules,
future: window.__remixContext.future
}
}, /*#__PURE__*/React__namespace.createElement(errorBoundaries.RemixErrorBoundary, {
location: location,
component: errorBoundaries.RemixRootDefaultErrorBoundary
}, /*#__PURE__*/React__namespace.createElement(reactRouterDom.RouterProvider, {
router: router,
fallbackElement: null,
future: {
v7_startTransition: true
}
})));
return (
/*#__PURE__*/
// This fragment is important to ensure we match the <RemixServer> JSX
// structure so that useId values hydrate correctly
React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement(components.RemixContext.Provider, {
value: {
manifest: window.__remixManifest,
routeModules: window.__remixRouteModules,
future: window.__remixContext.future,
criticalCss,
isSpaMode: window.__remixContext.isSpaMode
}
}, /*#__PURE__*/React__namespace.createElement(errorBoundaries.RemixErrorBoundary, {
location: location
}, /*#__PURE__*/React__namespace.createElement(reactRouterDom.RouterProvider, {
router: router,
fallbackElement: null,
future: {
v7_startTransition: true
}
}))), window.__remixContext.future.unstable_singleFetch ? /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null) : null)
);
}
exports.RemixBrowser = RemixBrowser;
import * as React from "react";
import type { LinkProps, NavLinkProps, FormProps, Params, SubmitFunction } from "react-router-dom";
import type { UIMatch as UIMatchRR } from "@remix-run/router";
import type { FetcherWithComponents, FormProps, LinkProps, NavLinkProps } from "react-router-dom";
import { useFetcher as useFetcherRR } from "react-router-dom";
import type { SerializeFrom } from "@remix-run/server-runtime";

@@ -7,26 +9,31 @@ import type { AppData } from "./data";

import type { PrefetchPageDescriptor } from "./links";
import type { Transition, Fetcher } from "./transition";
import type { RouteHandle } from "./routeModules";
export declare const RemixContext: React.Context<RemixContextObject | undefined>;
export declare function RemixRoute({ id }: {
id: string;
}): JSX.Element;
export declare function RemixRouteError({ id }: {
id: string;
}): JSX.Element;
export declare function useRemixContext(): RemixContextObject;
/**
* Defines the discovery behavior of the link:
*
* - "render": Eagerly discover when the link is rendered (default)
* - "none": No eager discovery - discover when the link is clicked
*/
export type DiscoverBehavior = "render" | "none";
/**
* Defines the prefetching behavior of the link:
*
* - "none": Never fetched
* - "intent": Fetched when the user focuses or hovers the link
* - "render": Fetched when the link is rendered
* - "none": Never fetched
* - "viewport": Fetched when the link is in the viewport
*/
type PrefetchBehavior = "intent" | "render" | "none" | "viewport";
export interface RemixLinkProps extends LinkProps {
discover?: DiscoverBehavior;
prefetch?: PrefetchBehavior;
}
export interface RemixNavLinkProps extends NavLinkProps {
discover?: DiscoverBehavior;
prefetch?: PrefetchBehavior;
}
/**
* A special kind of `<Link>` that knows whether or not it is "active".
* A special kind of `<Link>` that knows whether it is "active".
*

@@ -45,2 +52,13 @@ * @see https://remix.run/components/nav-link

export { Link };
export interface RemixFormProps extends FormProps {
discover?: DiscoverBehavior;
}
/**
* This component renders a form tag and is the primary way the user will
* submit information via your website.
*
* @see https://remix.run/components/form
*/
declare let Form: React.ForwardRefExoticComponent<RemixFormProps & React.RefAttributes<HTMLFormElement>>;
export { Form };
export declare function composeEventHandlers<EventType extends React.SyntheticEvent | Event>(theirHandler: ((event: EventType) => any) | undefined, ourHandler: (event: EventType) => any): (event: EventType) => any;

@@ -52,5 +70,5 @@ /**

*/
export declare function Links(): JSX.Element;
export declare function Links(): React.JSX.Element;
/**
* This component renders all of the `<link rel="prefetch">` and
* This component renders all the `<link rel="prefetch">` and
* `<link rel="modulepreload"/>` tags for all the assets (data, modules, css) of

@@ -63,4 +81,9 @@ * a given page.

*/
export declare function PrefetchPageLinks({ page, ...dataLinkProps }: PrefetchPageDescriptor): JSX.Element | null;
export declare function Meta(): JSX.Element;
export declare function PrefetchPageLinks({ page, ...dataLinkProps }: PrefetchPageDescriptor): React.JSX.Element | null;
/**
* Renders HTML tags related to metadata for the current route.
*
* @see https://remix.run/components/meta
*/
export declare function Meta(): React.JSX.Element;
export interface AwaitProps<Resolve> {

@@ -71,3 +94,3 @@ children: React.ReactNode | ((value: Awaited<Resolve>) => React.ReactNode);

}
export declare function Await<Resolve>(props: AwaitProps<Resolve>): JSX.Element;
export declare function Await<Resolve>(props: AwaitProps<Resolve>): React.JSX.Element;
export type ScriptProps = Omit<React.HTMLProps<HTMLScriptElement>, "children" | "async" | "defer" | "src" | "type" | "noModule" | "dangerouslySetInnerHTML" | "suppressHydrationWarning">;

@@ -84,33 +107,12 @@ /**

*/
export declare function Scripts(props: ScriptProps): JSX.Element | null;
export interface RouteMatch {
/**
* The id of the matched route
*/
id: string;
/**
* The pathname of the matched route
*/
pathname: string;
/**
* The dynamic parameters of the matched route
*
* @see https://remix.run/file-conventions/routes-files#dynamic-route-parameters
*/
params: Params<string>;
/**
* Any route data associated with the matched route
*/
data: any;
/**
* The exported `handle` object of the matched route.
*
* @see https://remix.run/route/handle
*/
handle: undefined | {
[key: string]: any;
};
}
export declare function useMatches(): RouteMatch[];
export declare function Scripts(props: ScriptProps): React.JSX.Element | null;
export type UIMatch<D = AppData, H = RouteHandle> = UIMatchRR<SerializeFrom<D>, H>;
/**
* Returns the active route matches, useful for accessing loaderData for
* parent/child routes or the route "handle" property
*
* @see https://remix.run/hooks/use-matches
*/
export declare function useMatches(): UIMatch[];
/**
* Returns the JSON parsed data from the current route's `loader`.

@@ -122,2 +124,8 @@ *

/**
* Returns the loaderData for the given routeId.
*
* @see https://remix.run/hooks/use-route-loader-data
*/
export declare function useRouteLoaderData<T = AppData>(routeId: string): SerializeFrom<T> | undefined;
/**
* Returns the JSON parsed data from the current route's `action`.

@@ -129,33 +137,20 @@ *

/**
* Returns everything you need to know about a page transition to build pending
* navigation indicators and optimistic UI on data mutations.
* Interacts with route loaders and actions without causing a navigation. Great
* for any interaction that stays on the same page.
*
* @deprecated in favor of useNavigation
*
* @see https://remix.run/hooks/use-transition
* @see https://remix.run/hooks/use-fetcher
*/
export declare function useTransition(): Transition;
export declare function useFetcher<TData = AppData>(opts?: Parameters<typeof useFetcherRR>[0]): FetcherWithComponents<SerializeFrom<TData>>;
/**
* Provides all fetchers currently on the page. Useful for layouts and parent
* routes that need to provide pending/optimistic UI regarding the fetch.
* This component connects your app to the Remix asset server and
* automatically reloads the page when files change in development.
* In production, it renders null, so you can safely render it always in your root route.
*
* @see https://remix.run/api/remix#usefetchers
* @see https://remix.run/docs/components/live-reload
*/
export declare function useFetchers(): Fetcher[];
export type FetcherWithComponents<TData> = Fetcher<TData> & {
Form: React.ForwardRefExoticComponent<FormProps & React.RefAttributes<HTMLFormElement>>;
submit: SubmitFunction;
load: (href: string) => void;
};
/**
* Interacts with route loaders and actions without causing a navigation. Great
* for any interaction that stays on the same page.
*
* @see https://remix.run/hooks/use-fetcher
*/
export declare function useFetcher<TData = any>(): FetcherWithComponents<SerializeFrom<TData>>;
export declare const LiveReload: (() => null) | (({ port, timeoutMs, nonce, }: {
export declare const LiveReload: ({ origin, port, timeoutMs, nonce, }: {
origin?: string | undefined;
port?: number | undefined;
timeoutMs?: number | undefined;
nonce?: string | undefined;
}) => JSX.Element);
}) => React.JSX.Element | null;
/**
* @remix-run/react v0.0.0-nightly-ccefed3-20230621
* @remix-run/react v0.0.0-nightly-cd403b516-20240809
*

@@ -18,8 +18,7 @@ * Copyright (c) Remix Software Inc.

var reactRouterDom = require('react-router-dom');
var errorBoundaries = require('./errorBoundaries.js');
var invariant = require('./invariant.js');
var links = require('./links.js');
var markup = require('./markup.js');
var transition = require('./transition.js');
var warnings = require('./warnings.js');
var singleFetch = require('./single-fetch.js');
var fogOfWar = require('./fog-of-war.js');

@@ -69,92 +68,20 @@ function _interopNamespace(e) {

////////////////////////////////////////////////////////////////////////////////
// RemixRoute
function RemixRoute({
id
}) {
let {
routeModules,
future
} = useRemixContext();
invariant(routeModules, "Cannot initialize 'routeModules'. This normally occurs when you have server code in your client modules.\n" + "Check this link for more details:\nhttps://remix.run/pages/gotchas#server-code-in-client-bundles");
let {
default: Component,
ErrorBoundary,
CatchBoundary
} = routeModules[id];
// Default Component to Outlet if we expose boundary UI components
if (!Component && (ErrorBoundary || !future.v2_errorBoundary && CatchBoundary)) {
Component = reactRouterDom.Outlet;
}
invariant(Component, `Route "${id}" has no component! Please go add a \`default\` export in the route module file.\n` + "If you were trying to navigate or submit to a resource route, use `<a>` instead of `<Link>` or `<Form reloadDocument>`.");
return /*#__PURE__*/React__namespace.createElement(Component, null);
}
function RemixRouteError({
id
}) {
let {
future,
routeModules
} = useRemixContext();
// This checks prevent cryptic error messages such as: 'Cannot read properties of undefined (reading 'root')'
invariant(routeModules, "Cannot initialize 'routeModules'. This normally occurs when you have server code in your client modules.\n" + "Check this link for more details:\nhttps://remix.run/pages/gotchas#server-code-in-client-bundles");
let error = reactRouterDom.useRouteError();
let {
CatchBoundary,
ErrorBoundary
} = routeModules[id];
if (future.v2_errorBoundary) {
// Provide defaults for the root route if they are not present
if (id === "root") {
ErrorBoundary || (ErrorBoundary = errorBoundaries.V2_RemixRootDefaultErrorBoundary);
}
if (ErrorBoundary) {
// TODO: Unsure if we can satisfy the typings here
// @ts-expect-error
return /*#__PURE__*/React__namespace.createElement(ErrorBoundary, null);
}
throw error;
}
// Provide defaults for the root route if they are not present
if (id === "root") {
CatchBoundary || (CatchBoundary = errorBoundaries.RemixRootDefaultCatchBoundary);
ErrorBoundary || (ErrorBoundary = errorBoundaries.RemixRootDefaultErrorBoundary);
}
if (reactRouterDom.isRouteErrorResponse(error)) {
let tError = error;
if (!!(tError !== null && tError !== void 0 && tError.error) && tError.status !== 404 && ErrorBoundary) {
// Internal framework-thrown ErrorResponses
return /*#__PURE__*/React__namespace.createElement(ErrorBoundary, {
error: tError.error
});
}
if (CatchBoundary) {
// User-thrown ErrorResponses
return /*#__PURE__*/React__namespace.createElement(errorBoundaries.RemixCatchBoundary, {
catch: error,
component: CatchBoundary
});
}
}
if (error instanceof Error && ErrorBoundary) {
// User- or framework-thrown Errors
return /*#__PURE__*/React__namespace.createElement(ErrorBoundary, {
error: error
});
}
throw error;
}
////////////////////////////////////////////////////////////////////////////////
// Public API
/**
* Defines the discovery behavior of the link:
*
* - "render": Eagerly discover when the link is rendered (default)
* - "none": No eager discovery - discover when the link is clicked
*/
/**
* Defines the prefetching behavior of the link:
*
* - "none": Never fetched
* - "intent": Fetched when the user focuses or hovers the link
* - "render": Fetched when the link is rendered
* - "none": Never fetched
* - "viewport": Fetched when the link is in the viewport
*/
function usePrefetchBehavior(prefetch, theirElementProps) {

@@ -220,5 +147,8 @@ let [maybePrefetch, setMaybePrefetch] = React__namespace.useState(false);

const ABSOLUTE_URL_REGEX = /^(?:[a-z][a-z0-9+.-]*:|\/\/)/i;
function getDiscoverAttr(discover, isAbsolute, reloadDocument) {
return discover === "render" && !isAbsolute && !reloadDocument ? "true" : undefined;
}
/**
* A special kind of `<Link>` that knows whether or not it is "active".
* A special kind of `<Link>` that knows whether it is "active".
*

@@ -230,2 +160,3 @@ * @see https://remix.run/components/nav-link

prefetch = "none",
discover = "render",
...props

@@ -238,3 +169,4 @@ }, forwardedRef) => {

ref: mergeRefs(forwardedRef, ref),
to: to
to: to,
"data-discover": getDiscoverAttr(discover, isAbsolute, props.reloadDocument)
})), shouldPrefetch && !isAbsolute ? /*#__PURE__*/React__namespace.createElement(PrefetchPageLinks, {

@@ -255,2 +187,3 @@ page: href

prefetch = "none",
discover = "render",
...props

@@ -263,3 +196,4 @@ }, forwardedRef) => {

ref: mergeRefs(forwardedRef, ref),
to: to
to: to,
"data-discover": getDiscoverAttr(discover, isAbsolute, props.reloadDocument)
})), shouldPrefetch && !isAbsolute ? /*#__PURE__*/React__namespace.createElement(PrefetchPageLinks, {

@@ -270,2 +204,19 @@ page: href

Link.displayName = "Link";
/**
* This component renders a form tag and is the primary way the user will
* submit information via your website.
*
* @see https://remix.run/components/form
*/
let Form = /*#__PURE__*/React__namespace.forwardRef(({
discover = "render",
...props
}, forwardedRef) => {
let isAbsolute = typeof props.action === "string" && ABSOLUTE_URL_REGEX.test(props.action);
return /*#__PURE__*/React__namespace.createElement(reactRouterDom.Form, _rollupPluginBabelHelpers["extends"]({}, props, {
ref: forwardedRef,
"data-discover": getDiscoverAttr(discover, isAbsolute, props.reloadDocument)
}));
});
Form.displayName = "Form";
function composeEventHandlers(theirHandler, ourHandler) {

@@ -279,7 +230,19 @@ return event => {

}
let linksWarning = "⚠️ REMIX FUTURE CHANGE: The behavior of links `imagesizes` and `imagesrcset` will be changing in v2. " + "Only the React camel case versions will be valid. Please change to `imageSizes` and `imageSrcSet`. " + "For instructions on making this change see " + "https://remix.run/docs/en/v1.15.0/pages/v2#links-imagesizes-and-imagesrcset";
let useTransitionWarning = "⚠️ REMIX FUTURE CHANGE: `useTransition` will be removed in v2 in favor of `useNavigation`. " + "You can prepare for this change at your convenience by updating to `useNavigation`. " + "For instructions on making this change see " + "https://remix.run/docs/en/v1.15.0/pages/v2#usetransition";
let fetcherTypeWarning = "⚠️ REMIX FUTURE CHANGE: `fetcher.type` will be removed in v2. " + "Please use `fetcher.state`, `fetcher.formData`, and `fetcher.data` to achieve the same UX. " + "For instructions on making this change see " + "https://remix.run/docs/en/v1.15.0/pages/v2#usefetcher";
let fetcherSubmissionWarning = "⚠️ REMIX FUTURE CHANGE : `fetcher.submission` will be removed in v2. " + "The submission fields are now part of the fetcher object itself (`fetcher.formData`). " + "For instructions on making this change see " + "https://remix.run/docs/en/v1.15.0/pages/v2#usefetcher";
// Return the matches actively being displayed:
// - In SPA Mode we only SSR/hydrate the root match, and include all matches
// after hydration. This lets the router handle initial match loads via lazy().
// - When an error boundary is rendered, we slice off matches up to the
// boundary for <Links>/<Meta>
function getActiveMatches(matches, errors, isSpaMode) {
if (isSpaMode && !isHydrated) {
return [matches[0]];
}
if (errors) {
let errorIdx = matches.findIndex(m => errors[m.route.id] !== undefined);
return matches.slice(0, errorIdx + 1);
}
return matches;
}
/**

@@ -292,4 +255,6 @@ * Renders the `<link>` tags for the current routes.

let {
isSpaMode,
manifest,
routeModules
routeModules,
criticalCss
} = useRemixContext();

@@ -300,48 +265,20 @@ let {

} = useDataRouterStateContext();
let matches = errors ? routerMatches.slice(0, routerMatches.findIndex(m => errors[m.route.id]) + 1) : routerMatches;
let links$1 = React__namespace.useMemo(() => links.getLinksForMatches(matches, routeModules, manifest), [matches, routeModules, manifest]);
React__namespace.useEffect(() => {
if (links$1.some(link => "imagesizes" in link || "imagesrcset" in link)) {
warnings.logDeprecationOnce(linksWarning);
let matches = getActiveMatches(routerMatches, errors, isSpaMode);
let keyedLinks = React__namespace.useMemo(() => links.getKeyedLinksForMatches(matches, routeModules, manifest), [matches, routeModules, manifest]);
return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, criticalCss ? /*#__PURE__*/React__namespace.createElement("style", {
dangerouslySetInnerHTML: {
__html: criticalCss
}
}, [links$1]);
return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, links$1.map(link => {
if (links.isPageLinkDescriptor(link)) {
return /*#__PURE__*/React__namespace.createElement(PrefetchPageLinks, _rollupPluginBabelHelpers["extends"]({
key: link.page
}, link));
}
let imageSrcSet = null;
// In React 17, <link imageSrcSet> and <link imageSizes> will warn
// because the DOM attributes aren't recognized, so users need to pass
// them in all lowercase to forward the attributes to the node without a
// warning. Normalize so that either property can be used in Remix.
if ("useId" in React__namespace) {
if (link.imagesrcset) {
link.imageSrcSet = imageSrcSet = link.imagesrcset;
delete link.imagesrcset;
}
if (link.imagesizes) {
link.imageSizes = link.imagesizes;
delete link.imagesizes;
}
} else {
if (link.imageSrcSet) {
link.imagesrcset = imageSrcSet = link.imageSrcSet;
delete link.imageSrcSet;
}
if (link.imageSizes) {
link.imagesizes = link.imageSizes;
delete link.imageSizes;
}
}
return /*#__PURE__*/React__namespace.createElement("link", _rollupPluginBabelHelpers["extends"]({
key: link.rel + (link.href || "") + (imageSrcSet || "")
}, link));
}));
}) : null, keyedLinks.map(({
key,
link
}) => links.isPageLinkDescriptor(link) ? /*#__PURE__*/React__namespace.createElement(PrefetchPageLinks, _rollupPluginBabelHelpers["extends"]({
key: key
}, link)) : /*#__PURE__*/React__namespace.createElement("link", _rollupPluginBabelHelpers["extends"]({
key: key
}, link))));
}
/**
* This component renders all of the `<link rel="prefetch">` and
* This component renders all the `<link rel="prefetch">` and
* `<link rel="modulepreload"/>` tags for all the assets (data, modules, css) of

@@ -361,3 +298,3 @@ * a given page.

} = useDataRouterContext();
let matches = React__namespace.useMemo(() => reactRouterDom.matchRoutes(router.routes, page), [router.routes, page]);
let matches = React__namespace.useMemo(() => reactRouterDom.matchRoutes(router.routes, page, router.basename), [router.routes, page, router.basename]);
if (!matches) {

@@ -372,3 +309,3 @@ console.warn(`Tried to prefetch ${page} but no routes matched.`);

}
function usePrefetchedStylesheets(matches) {
function useKeyedPrefetchLinks(matches) {
let {

@@ -378,7 +315,9 @@ manifest,

} = useRemixContext();
let [styleLinks, setStyleLinks] = React__namespace.useState([]);
let [keyedPrefetchLinks, setKeyedPrefetchLinks] = React__namespace.useState([]);
React__namespace.useEffect(() => {
let interrupted = false;
links.getStylesheetPrefetchLinks(matches, manifest, routeModules).then(links => {
if (!interrupted) setStyleLinks(links);
void links.getKeyedPrefetchLinks(matches, manifest, routeModules).then(links => {
if (!interrupted) {
setKeyedPrefetchLinks(links);
}
});

@@ -389,3 +328,3 @@ return () => {

}, [matches, manifest, routeModules]);
return styleLinks;
return keyedPrefetchLinks;
}

@@ -399,3 +338,5 @@ function PrefetchPageLinksImpl({

let {
manifest
future,
manifest,
routeModules
} = useRemixContext();

@@ -412,13 +353,32 @@ let {

// just the manifest like the other links in here.
let styleLinks = usePrefetchedStylesheets(newMatchesForAssets);
return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, dataHrefs.map(href => /*#__PURE__*/React__namespace.createElement("link", _rollupPluginBabelHelpers["extends"]({
let keyedPrefetchLinks = useKeyedPrefetchLinks(newMatchesForAssets);
let linksToRender = null;
if (!future.unstable_singleFetch) {
// Non-single-fetch prefetching
linksToRender = dataHrefs.map(href => /*#__PURE__*/React__namespace.createElement("link", _rollupPluginBabelHelpers["extends"]({
key: href,
rel: "prefetch",
as: "fetch",
href: href
}, linkProps)));
} else if (newMatchesForData.length > 0) {
// Single-fetch with routes that require data
let url = singleFetch.addRevalidationParam(manifest, routeModules, nextMatches.map(m => m.route), newMatchesForData.map(m => m.route), singleFetch.singleFetchUrl(page));
if (url.searchParams.get("_routes") !== "") {
linksToRender = /*#__PURE__*/React__namespace.createElement("link", _rollupPluginBabelHelpers["extends"]({
key: url.pathname + url.search,
rel: "prefetch",
as: "fetch",
href: url.pathname + url.search
}, linkProps));
}
} else ;
return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, linksToRender, moduleHrefs.map(href => /*#__PURE__*/React__namespace.createElement("link", _rollupPluginBabelHelpers["extends"]({
key: href,
rel: "prefetch",
as: "fetch",
href: href
}, linkProps))), moduleHrefs.map(href => /*#__PURE__*/React__namespace.createElement("link", _rollupPluginBabelHelpers["extends"]({
key: href,
rel: "modulepreload",
href: href
}, linkProps))), styleLinks.map(link =>
}, linkProps))), keyedPrefetchLinks.map(({
key,
link
}) =>
/*#__PURE__*/

@@ -428,3 +388,3 @@ // these don't spread `linkProps` because they are full link descriptors

React__namespace.createElement("link", _rollupPluginBabelHelpers["extends"]({
key: link.href
key: key
}, link))));

@@ -434,8 +394,9 @@ }

/**
* Renders the `<title>` and `<meta>` tags for the current routes.
* Renders HTML tags related to metadata for the current route.
*
* @see https://remix.run/components/meta
*/
function V1Meta() {
function Meta() {
let {
isSpaMode,
routeModules

@@ -449,92 +410,7 @@ } = useRemixContext();

let location = reactRouterDom.useLocation();
let matches = errors ? routerMatches.slice(0, routerMatches.findIndex(m => errors[m.route.id]) + 1) : routerMatches;
let meta = {};
let parentsData = {};
for (let match of matches) {
let routeId = match.route.id;
let data = loaderData[routeId];
let params = match.params;
let routeModule = routeModules[routeId];
if (routeModule.meta) {
let routeMeta = typeof routeModule.meta === "function" ? routeModule.meta({
data,
parentsData,
params,
location
}) : routeModule.meta;
if (routeMeta && Array.isArray(routeMeta)) {
throw new Error("The route at " + match.route.path + " returns an array. This is only supported with the `v2_meta` future flag " + "in the Remix config. Either set the flag to `true` or update the route's " + "meta function to return an object." + "\n\nTo reference the v1 meta function API, see https://remix.run/route/meta"
// TODO: Add link to the docs once they are written
// + "\n\nTo reference future flags and the v2 meta API, see https://remix.run/file-conventions/remix-config#future-v2-meta."
);
}
Object.assign(meta, routeMeta);
}
parentsData[routeId] = data;
let _matches = getActiveMatches(routerMatches, errors, isSpaMode);
let error = null;
if (errors) {
error = errors[_matches[_matches.length - 1].route.id];
}
return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, Object.entries(meta).map(([name, value]) => {
if (!value) {
return null;
}
if (["charset", "charSet"].includes(name)) {
return /*#__PURE__*/React__namespace.createElement("meta", {
key: "charSet",
charSet: value
});
}
if (name === "title") {
return /*#__PURE__*/React__namespace.createElement("title", {
key: "title"
}, String(value));
}
// Open Graph tags use the `property` attribute, while other meta tags
// use `name`. See https://ogp.me/
//
// Namespaced attributes:
// - https://ogp.me/#type_music
// - https://ogp.me/#type_video
// - https://ogp.me/#type_article
// - https://ogp.me/#type_book
// - https://ogp.me/#type_profile
//
// Facebook specific tags begin with `fb:` and also use the `property`
// attribute.
//
// Twitter specific tags begin with `twitter:` but they use `name`, so
// they are excluded.
let isOpenGraphTag = /^(og|music|video|article|book|profile|fb):.+$/.test(name);
return [value].flat().map(content => {
if (isOpenGraphTag) {
return /*#__PURE__*/React__namespace.createElement("meta", {
property: name,
content: content,
key: name + content
});
}
if (typeof content === "string") {
return /*#__PURE__*/React__namespace.createElement("meta", {
name: name,
content: content,
key: name + content
});
}
return /*#__PURE__*/React__namespace.createElement("meta", _rollupPluginBabelHelpers["extends"]({
key: name + JSON.stringify(content)
}, content));
});
}));
}
function V2Meta() {
let {
routeModules
} = useRemixContext();
let {
errors,
matches: routerMatches,
loaderData
} = useDataRouterStateContext();
let location = reactRouterDom.useLocation();
let _matches = errors ? routerMatches.slice(0, routerMatches.findIndex(m => errors[m.route.id]) + 1) : routerMatches;
let meta = [];

@@ -557,10 +433,3 @@ let leafMeta = null;

handle: _match.route.handle,
// TODO: Remove in v2. Only leaving it for now because we used it in
// examples and there's no reason to crash someone's build for one line.
// They'll get a TS error from the type updates anyway.
// @ts-expect-error
get route() {
console.warn("The meta function in " + _match.route.path + " accesses the `route` property on `matches`. This is deprecated and will be removed in Remix version 2. See");
return _match.route;
}
error
};

@@ -573,7 +442,8 @@ matches[i] = match;

location,
matches
matches,
error
}) : Array.isArray(routeModule.meta) ? [...routeModule.meta] : routeModule.meta;
} else if (leafMeta) {
// We only assign the route's meta to the nearest leaf if there is no meta
// export in the route. The meta function may return a falsey value which
// export in the route. The meta function may return a falsy value which
// is effectively the same as an empty array.

@@ -584,6 +454,3 @@ routeMeta = [...leafMeta];

if (!Array.isArray(routeMeta)) {
throw new Error("The `v2_meta` API is enabled in the Remix config, but the route at " + _match.route.path + " returns an invalid value. In v2, all route meta functions must " + "return an array of meta objects." +
// TODO: Add link to the docs once they are written
// "\n\nTo reference future flags and the v2 meta API, see https://remix.run/file-conventions/remix-config#future-v2-meta." +
"\n\nTo reference the v1 meta function API, see https://remix.run/route/meta");
throw new Error("The route at " + _match.route.path + " returns an invalid value. All route meta functions must " + "return an array of meta objects." + "\n\nTo reference the meta function API, see https://remix.run/route/meta");
}

@@ -600,4 +467,6 @@ match.meta = routeMeta;

if ("tagName" in metaProps) {
let tagName = metaProps.tagName;
delete metaProps.tagName;
let {
tagName,
...rest
} = metaProps;
if (!isValidMetaTag(tagName)) {

@@ -609,4 +478,4 @@ console.warn(`A meta object uses an invalid tagName: ${tagName}. Expected either 'link' or 'meta'`);

return /*#__PURE__*/React__namespace.createElement(Comp, _rollupPluginBabelHelpers["extends"]({
key: JSON.stringify(metaProps)
}, metaProps));
key: JSON.stringify(rest)
}, rest));
}

@@ -619,3 +488,3 @@ if ("title" in metaProps) {

if ("charset" in metaProps) {
metaProps.charSet ?? (metaProps.charSet = metaProps.charset);
metaProps.charSet ??= metaProps.charset;
delete metaProps.charset;

@@ -630,13 +499,14 @@ }

if ("script:ld+json" in metaProps) {
let json = null;
try {
json = JSON.stringify(metaProps["script:ld+json"]);
} catch (err) {}
return json != null && /*#__PURE__*/React__namespace.createElement("script", {
key: "script:ld+json",
type: "application/ld+json",
dangerouslySetInnerHTML: {
__html: JSON.stringify(metaProps["script:ld+json"])
}
});
let json = JSON.stringify(metaProps["script:ld+json"]);
return /*#__PURE__*/React__namespace.createElement("script", {
key: `script:ld+json:${json}`,
type: "application/ld+json",
dangerouslySetInnerHTML: {
__html: json
}
});
} catch (err) {
return null;
}
}

@@ -651,8 +521,2 @@ return /*#__PURE__*/React__namespace.createElement("meta", _rollupPluginBabelHelpers["extends"]({

}
function Meta() {
let {
future
} = useRemixContext();
return future !== null && future !== void 0 && future.v2_meta ? /*#__PURE__*/React__namespace.createElement(V2Meta, null) : /*#__PURE__*/React__namespace.createElement(V1Meta, null);
}
function Await(props) {

@@ -681,3 +545,7 @@ return /*#__PURE__*/React__namespace.createElement(reactRouterDom.Await, props);

serverHandoffString,
abortDelay
abortDelay,
serializeError,
isSpaMode,
future,
renderMeta
} = useRemixContext();

@@ -690,13 +558,62 @@ let {

let {
matches
matches: routerMatches
} = useDataRouterStateContext();
let navigation = reactRouterDom.useNavigation();
let enableFogOfWar = fogOfWar.isFogOfWarEnabled(future, isSpaMode);
// Let <RemixServer> know that we hydrated and we should render the single
// fetch streaming scripts
if (renderMeta) {
renderMeta.didRenderScripts = true;
}
let matches = getActiveMatches(routerMatches, null, isSpaMode);
React__namespace.useEffect(() => {
isHydrated = true;
}, []);
let serializePreResolvedErrorImp = (key, error) => {
let toSerialize;
if (serializeError && error instanceof Error) {
toSerialize = serializeError(error);
} else {
toSerialize = error;
}
return `${JSON.stringify(key)}:__remixContext.p(!1, ${markup.escapeHtml(JSON.stringify(toSerialize))})`;
};
let serializePreresolvedDataImp = (routeId, key, data) => {
let serializedData;
try {
serializedData = JSON.stringify(data);
} catch (error) {
return serializePreResolvedErrorImp(key, error);
}
return `${JSON.stringify(key)}:__remixContext.p(${markup.escapeHtml(serializedData)})`;
};
let serializeErrorImp = (routeId, key, error) => {
let toSerialize;
if (serializeError && error instanceof Error) {
toSerialize = serializeError(error);
} else {
toSerialize = error;
}
return `__remixContext.r(${JSON.stringify(routeId)}, ${JSON.stringify(key)}, !1, ${markup.escapeHtml(JSON.stringify(toSerialize))})`;
};
let serializeDataImp = (routeId, key, data) => {
let serializedData;
try {
serializedData = JSON.stringify(data);
} catch (error) {
return serializeErrorImp(routeId, key, error);
}
return `__remixContext.r(${JSON.stringify(routeId)}, ${JSON.stringify(key)}, ${markup.escapeHtml(serializedData)})`;
};
let deferredScripts = [];
let initialScripts = React__namespace.useMemo(() => {
var _manifest$hmr;
let contextScript = staticContext ? `window.__remixContext = ${serverHandoffString};` : " ";
let activeDeferreds = staticContext === null || staticContext === void 0 ? void 0 : staticContext.activeDeferreds;
let streamScript = future.unstable_singleFetch ?
// prettier-ignore
"window.__remixContext.stream = new ReadableStream({" + "start(controller){" + "window.__remixContext.streamController = controller;" + "}" + "}).pipeThrough(new TextEncoderStream());" : "";
let contextScript = staticContext ? `window.__remixContext = ${serverHandoffString};${streamScript}` : " ";
// When single fetch is enabled, deferred is handled by turbo-stream
let activeDeferreds = future.unstable_singleFetch ? undefined : staticContext === null || staticContext === void 0 ? void 0 : staticContext.activeDeferreds;
// This sets up the __remixContext with utility functions used by the

@@ -710,3 +627,3 @@ // deferred scripts.

// the promise created by __remixContext.n.
// - __remixContext.t is a a map or routeId to keys to an object containing `e` and `r` methods
// - __remixContext.t is a map or routeId to keys to an object containing `e` and `r` methods
// to resolve or reject the promise created by __remixContext.n.

@@ -724,3 +641,5 @@ // - __remixContext.a is the active number of deferred scripts that should be rendered to match

dataKey: key,
scriptProps: props
scriptProps: props,
serializeData: serializeDataImp,
serializeError: serializeErrorImp
}));

@@ -731,15 +650,5 @@ return `${JSON.stringify(key)}:__remixContext.n(${JSON.stringify(routeId)}, ${JSON.stringify(key)})`;

if (typeof trackedPromise._error !== "undefined") {
let toSerialize = process.env.NODE_ENV === "development" ? {
message: trackedPromise._error.message,
stack: trackedPromise._error.stack
} : {
message: "Unexpected Server Error",
stack: undefined
};
return `${JSON.stringify(key)}:__remixContext.p(!1, ${markup.escapeHtml(JSON.stringify(toSerialize))})`;
return serializePreResolvedErrorImp(key, trackedPromise._error);
} else {
if (typeof trackedPromise._data === "undefined") {
throw new Error(`The deferred data for ${key} was not resolved, did you forget to return data from a deferred promise?`);
}
return `${JSON.stringify(key)}:__remixContext.p(${markup.escapeHtml(JSON.stringify(trackedPromise._data))})`;
return serializePreresolvedDataImp(routeId, key, trackedPromise._data);
}

@@ -750,4 +659,7 @@ }

}).join("\n") + (deferredScripts.length > 0 ? `__remixContext.a=${deferredScripts.length};` : "");
let routeModulesScript = !isStatic ? " " : `${(_manifest$hmr = manifest.hmr) !== null && _manifest$hmr !== void 0 && _manifest$hmr.runtime ? `import ${JSON.stringify(manifest.hmr.runtime)};` : ""}import ${JSON.stringify(manifest.url)};
let routeModulesScript = !isStatic ? " " : `${(_manifest$hmr = manifest.hmr) !== null && _manifest$hmr !== void 0 && _manifest$hmr.runtime ? `import ${JSON.stringify(manifest.hmr.runtime)};` : ""}${enableFogOfWar ? "" : `import ${JSON.stringify(manifest.url)}`};
${matches.map((match, index) => `import * as route${index} from ${JSON.stringify(manifest.routes[match.route.id].module)};`).join("\n")}
${enableFogOfWar ?
// Inline a minimal manifest with the SSR matches
`window.__remixManifest = ${JSON.stringify(fogOfWar.getPartialManifest(manifest, router), null, 2)};` : ""}
window.__remixRouteModules = {${matches.map((match, index) => `${JSON.stringify(match.route.id)}:route${index}`).join(",")}};

@@ -775,18 +687,9 @@

key: i,
scriptProps: props
scriptProps: props,
serializeData: serializeDataImp,
serializeError: serializeErrorImp
}));
}
}
// avoid waterfall when importing the next route module
let nextMatches = React__namespace.useMemo(() => {
if (navigation.location) {
// FIXME: can probably use transitionManager `nextMatches`
let matches = reactRouterDom.matchRoutes(router.routes, navigation.location);
invariant(matches, `No routes match path "${navigation.location.pathname}"`);
return matches;
}
return [];
}, [navigation.location, router.routes]);
let routePreloads = matches.concat(nextMatches).map(match => {
let routePreloads = matches.map(match => {
let route = manifest.routes[match.route.id];

@@ -796,4 +699,8 @@ return (route.imports || []).concat([route.module]);

let preloads = isHydrated ? [] : manifest.entry.imports.concat(routePreloads);
return isHydrated ? null : /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement("link", {
return isHydrated ? null : /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, !enableFogOfWar ? /*#__PURE__*/React__namespace.createElement("link", {
rel: "modulepreload",
href: manifest.url,
crossOrigin: props.crossOrigin
}) : null, /*#__PURE__*/React__namespace.createElement("link", {
rel: "modulepreload",
href: manifest.entry.module,

@@ -812,3 +719,5 @@ crossOrigin: props.crossOrigin

routeId,
scriptProps
scriptProps,
serializeData,
serializeError
}) {

@@ -835,11 +744,14 @@ if (typeof document === "undefined" && deferredData && dataKey && routeId) {

routeId: routeId,
scriptProps: scriptProps
scriptProps: scriptProps,
serializeError: serializeError
}),
children: data => /*#__PURE__*/React__namespace.createElement("script", _rollupPluginBabelHelpers["extends"]({}, scriptProps, {
async: true,
suppressHydrationWarning: true,
dangerouslySetInnerHTML: {
__html: `__remixContext.r(${JSON.stringify(routeId)}, ${JSON.stringify(dataKey)}, ${markup.escapeHtml(JSON.stringify(data))});`
}
}))
children: data => {
return /*#__PURE__*/React__namespace.createElement("script", _rollupPluginBabelHelpers["extends"]({}, scriptProps, {
async: true,
suppressHydrationWarning: true,
dangerouslySetInnerHTML: {
__html: serializeData(routeId, dataKey, data)
}
}));
}
}) : /*#__PURE__*/React__namespace.createElement("script", _rollupPluginBabelHelpers["extends"]({}, scriptProps, {

@@ -856,16 +768,10 @@ async: true,

routeId,
scriptProps
scriptProps,
serializeError
}) {
let error = reactRouterDom.useAsyncError();
let toSerialize = process.env.NODE_ENV === "development" ? {
message: error.message,
stack: error.stack
} : {
message: "Unexpected Server Error",
stack: undefined
};
return /*#__PURE__*/React__namespace.createElement("script", _rollupPluginBabelHelpers["extends"]({}, scriptProps, {
suppressHydrationWarning: true,
dangerouslySetInnerHTML: {
__html: `__remixContext.r(${JSON.stringify(routeId)}, ${JSON.stringify(dataKey)}, !1, ${markup.escapeHtml(JSON.stringify(toSerialize))});`
__html: serializeError(routeId, dataKey, error)
}

@@ -877,22 +783,10 @@ }));

}
// TODO: Can this be re-exported from RR?
/**
* Returns the active route matches, useful for accessing loaderData for
* parent/child routes or the route "handle" property
*
* @see https://remix.run/hooks/use-matches
*/
function useMatches() {
let {
routeModules
} = useRemixContext();
let matches = reactRouterDom.useMatches();
return React__namespace.useMemo(() => matches.map(match => {
let remixMatch = {
id: match.id,
pathname: match.pathname,
params: match.params,
data: match.data,
// Need to grab handle here since we don't have it at client-side route
// creation time
handle: routeModules[match.id].handle
};
return remixMatch;
}), [matches, routeModules]);
return reactRouterDom.useMatches();
}

@@ -910,195 +804,20 @@

/**
* Returns the JSON parsed data from the current route's `action`.
* Returns the loaderData for the given routeId.
*
* @see https://remix.run/hooks/use-action-data
* @see https://remix.run/hooks/use-route-loader-data
*/
function useActionData() {
return reactRouterDom.useActionData();
function useRouteLoaderData(routeId) {
return reactRouterDom.useRouteLoaderData(routeId);
}
/**
* Returns everything you need to know about a page transition to build pending
* navigation indicators and optimistic UI on data mutations.
* Returns the JSON parsed data from the current route's `action`.
*
* @deprecated in favor of useNavigation
*
* @see https://remix.run/hooks/use-transition
* @see https://remix.run/hooks/use-action-data
*/
function useTransition() {
let navigation = reactRouterDom.useNavigation();
React__namespace.useEffect(() => {
warnings.logDeprecationOnce(useTransitionWarning);
}, []);
return React__namespace.useMemo(() => convertNavigationToTransition(navigation), [navigation]);
function useActionData() {
return reactRouterDom.useActionData();
}
function convertNavigationToTransition(navigation) {
let {
location,
state,
formMethod,
formAction,
formEncType,
formData
} = navigation;
if (!location) {
return transition.IDLE_TRANSITION;
}
let isActionSubmission = formMethod != null && ["POST", "PUT", "PATCH", "DELETE"].includes(formMethod.toUpperCase());
if (state === "submitting" && formMethod && formAction && formEncType && formData) {
if (isActionSubmission) {
// Actively submitting to an action
let transition = {
location,
state,
submission: {
method: formMethod.toUpperCase(),
action: formAction,
encType: formEncType,
formData: formData,
key: ""
},
type: "actionSubmission"
};
return transition;
} else {
// @remix-run/router doesn't mark loader submissions as state: "submitting"
invariant(false, "Encountered an unexpected navigation scenario in useTransition()");
}
}
if (state === "loading") {
let {
_isRedirect,
_isFetchActionRedirect
} = location.state || {};
if (formMethod && formAction && formEncType && formData) {
if (!_isRedirect) {
if (isActionSubmission) {
// We're reloading the same location after an action submission
let transition = {
location,
state,
submission: {
method: formMethod.toUpperCase(),
action: formAction,
encType: formEncType,
formData: formData,
key: ""
},
type: "actionReload"
};
return transition;
} else {
// The new router fixes a bug in useTransition where the submission
// "action" represents the request URL not the state of the <form> in
// the DOM. Back-port it here to maintain behavior, but useNavigation
// will fix this bug.
let url = new URL(formAction, window.location.origin);
// This typing override should be safe since this is only running for
// GET submissions and over in @remix-run/router we have an invariant
// if you have any non-string values in your FormData when we attempt
// to convert them to URLSearchParams
url.search = new URLSearchParams(formData.entries()).toString();
// Actively "submitting" to a loader
let transition = {
location,
state: "submitting",
submission: {
method: formMethod.toUpperCase(),
action: url.pathname + url.search,
encType: formEncType,
formData: formData,
key: ""
},
type: "loaderSubmission"
};
return transition;
}
} else {
// Redirecting after a submission
if (isActionSubmission) {
let transition = {
location,
state,
submission: {
method: formMethod.toUpperCase(),
action: formAction,
encType: formEncType,
formData: formData,
key: ""
},
type: "actionRedirect"
};
return transition;
} else {
let transition = {
location,
state,
submission: {
method: formMethod.toUpperCase(),
action: formAction,
encType: formEncType,
formData: formData,
key: ""
},
type: "loaderSubmissionRedirect"
};
return transition;
}
}
} else if (_isRedirect) {
if (_isFetchActionRedirect) {
let transition = {
location,
state,
submission: undefined,
type: "fetchActionRedirect"
};
return transition;
} else {
let transition = {
location,
state,
submission: undefined,
type: "normalRedirect"
};
return transition;
}
}
}
// If no scenarios above match, then it's a normal load!
let transition$1 = {
location,
state: "loading",
submission: undefined,
type: "normalLoad"
};
return transition$1;
}
/**
* Provides all fetchers currently on the page. Useful for layouts and parent
* routes that need to provide pending/optimistic UI regarding the fetch.
*
* @see https://remix.run/api/remix#usefetchers
*/
function useFetchers() {
let fetchers = reactRouterDom.useFetchers();
return fetchers.map(f => {
let fetcher = convertRouterFetcherToRemixFetcher({
state: f.state,
data: f.data,
formMethod: f.formMethod,
formAction: f.formAction,
formData: f.formData,
formEncType: f.formEncType,
" _hasFetcherDoneAnything ": f[" _hasFetcherDoneAnything "]
});
addFetcherDeprecationWarnings(fetcher);
return fetcher;
});
}
/**
* Interacts with route loaders and actions without causing a navigation. Great

@@ -1109,205 +828,18 @@ * for any interaction that stays on the same page.

*/
function useFetcher() {
let fetcherRR = reactRouterDom.useFetcher();
return React__namespace.useMemo(() => {
let remixFetcher = convertRouterFetcherToRemixFetcher({
state: fetcherRR.state,
data: fetcherRR.data,
formMethod: fetcherRR.formMethod,
formAction: fetcherRR.formAction,
formData: fetcherRR.formData,
formEncType: fetcherRR.formEncType,
" _hasFetcherDoneAnything ": fetcherRR[" _hasFetcherDoneAnything "]
});
let fetcherWithComponents = {
...remixFetcher,
load: fetcherRR.load,
submit: fetcherRR.submit,
Form: fetcherRR.Form
};
addFetcherDeprecationWarnings(fetcherWithComponents);
return fetcherWithComponents;
}, [fetcherRR]);
function useFetcher(opts = {}) {
return reactRouterDom.useFetcher(opts);
}
function addFetcherDeprecationWarnings(fetcher) {
let type = fetcher.type;
Object.defineProperty(fetcher, "type", {
get() {
warnings.logDeprecationOnce(fetcherTypeWarning);
return type;
},
set(value) {
// Devs should *not* be doing this but we don't want to break their
// current app if they are
type = value;
},
// These settings should make this behave like a normal object `type` field
configurable: true,
enumerable: true
});
let submission = fetcher.submission;
Object.defineProperty(fetcher, "submission", {
get() {
warnings.logDeprecationOnce(fetcherSubmissionWarning);
return submission;
},
set(value) {
// Devs should *not* be doing this but we don't want to break their
// current app if they are
submission = value;
},
// These settings should make this behave like a normal object `type` field
configurable: true,
enumerable: true
});
}
function convertRouterFetcherToRemixFetcher(fetcherRR) {
let {
state,
formMethod,
formAction,
formEncType,
formData,
data
} = fetcherRR;
let isActionSubmission = formMethod != null && ["POST", "PUT", "PATCH", "DELETE"].includes(formMethod.toUpperCase());
if (state === "idle") {
if (fetcherRR[" _hasFetcherDoneAnything "] === true) {
let fetcher = {
state: "idle",
type: "done",
formMethod: undefined,
formAction: undefined,
formData: undefined,
formEncType: undefined,
submission: undefined,
data
};
return fetcher;
} else {
let fetcher = transition.IDLE_FETCHER;
return fetcher;
}
}
if (state === "submitting" && formMethod && formAction && formEncType && formData) {
if (isActionSubmission) {
// Actively submitting to an action
let fetcher = {
state,
type: "actionSubmission",
formMethod: formMethod.toUpperCase(),
formAction: formAction,
formEncType: formEncType,
formData: formData,
submission: {
method: formMethod.toUpperCase(),
action: formAction,
encType: formEncType,
formData: formData,
key: ""
},
data
};
return fetcher;
} else {
// @remix-run/router doesn't mark loader submissions as state: "submitting"
invariant(false, "Encountered an unexpected fetcher scenario in useFetcher()");
}
}
if (state === "loading") {
if (formMethod && formAction && formEncType && formData) {
if (isActionSubmission) {
if (data) {
// In a loading state but we have data - must be an actionReload
let fetcher = {
state,
type: "actionReload",
formMethod: formMethod.toUpperCase(),
formAction: formAction,
formEncType: formEncType,
formData: formData,
submission: {
method: formMethod.toUpperCase(),
action: formAction,
encType: formEncType,
formData: formData,
key: ""
},
data
};
return fetcher;
} else {
let fetcher = {
state,
type: "actionRedirect",
formMethod: formMethod.toUpperCase(),
formAction: formAction,
formEncType: formEncType,
formData: formData,
submission: {
method: formMethod.toUpperCase(),
action: formAction,
encType: formEncType,
formData: formData,
key: ""
},
data: undefined
};
return fetcher;
}
} else {
// The new router fixes a bug in useTransition where the submission
// "action" represents the request URL not the state of the <form> in
// the DOM. Back-port it here to maintain behavior, but useNavigation
// will fix this bug.
let url = new URL(formAction, window.location.origin);
// This typing override should be safe since this is only running for
// GET submissions and over in @remix-run/router we have an invariant
// if you have any non-string values in your FormData when we attempt
// to convert them to URLSearchParams
url.search = new URLSearchParams(formData.entries()).toString();
// Actively "submitting" to a loader
let fetcher = {
state: "submitting",
type: "loaderSubmission",
formMethod: formMethod.toUpperCase(),
formAction: formAction,
formEncType: formEncType,
formData: formData,
submission: {
method: formMethod.toUpperCase(),
action: url.pathname + url.search,
encType: formEncType,
formData: formData,
key: ""
},
data
};
return fetcher;
}
}
}
// If all else fails, it's a normal load!
let fetcher = {
state: "loading",
type: "normalLoad",
formMethod: undefined,
formAction: undefined,
formData: undefined,
formEncType: undefined,
submission: undefined,
data
};
return fetcher;
}
/**
* This component connects your app to the Remix asset server and
* automatically reloads the page when files change in development.
* In production, it renders null, so you can safely render it always in your root route.
*
* @see https://remix.run/docs/components/live-reload
*/
const LiveReload =
// Dead Code Elimination magic for production builds.
// This way devs don't have to worry about doing the NODE_ENV check themselves.
// If running an un-bundled server outside of `remix dev` you will still need
// to set the REMIX_DEV_SERVER_WS_PORT manually.
const LiveReload = process.env.NODE_ENV !== "development" ? () => null : function LiveReload({
// TODO: remove REMIX_DEV_SERVER_WS_PORT in v2
process.env.NODE_ENV !== "development" ? () => null : function LiveReload({
origin,
port,

@@ -1317,2 +849,3 @@ timeoutMs = 1000,

}) {
origin ??= process.env.REMIX_DEV_ORIGIN;
let js = String.raw;

@@ -1325,7 +858,14 @@ return /*#__PURE__*/React__namespace.createElement("script", {

function remixLiveReloadConnect(config) {
let protocol = location.protocol === "https:" ? "wss:" : "ws:";
let host = location.hostname;
let port = ${port} || (window.__remixContext && window.__remixContext.dev && window.__remixContext.dev.port) || ${Number(process.env.REMIX_DEV_SERVER_WS_PORT || 8002)};
let socketPath = protocol + "//" + host + ":" + port + "/socket";
let ws = new WebSocket(socketPath);
let LIVE_RELOAD_ORIGIN = ${JSON.stringify(origin)};
let protocol =
LIVE_RELOAD_ORIGIN ? new URL(LIVE_RELOAD_ORIGIN).protocol.replace(/^http/, "ws") :
location.protocol === "https:" ? "wss:" : "ws:"; // remove in v2?
let hostname = LIVE_RELOAD_ORIGIN ? new URL(LIVE_RELOAD_ORIGIN).hostname : location.hostname;
let url = new URL(protocol + "//" + hostname + "/socket");
url.port =
${port} ||
(LIVE_RELOAD_ORIGIN ? new URL(LIVE_RELOAD_ORIGIN).port : 8002);
let ws = new WebSocket(url.href);
ws.onmessage = async (message) => {

@@ -1361,3 +901,3 @@ let event = JSON.parse(message.data);

if (accepted) {
console.log("[HMR] Updated accepted by", update.id);
console.log("[HMR] Update accepted by", update.id);
updateAccepted = true;

@@ -1372,3 +912,3 @@ }

if (accepted) {
console.log("[HMR] Updated accepted by", "remix:manifest");
console.log("[HMR] Update accepted by", "remix:manifest");
updateAccepted = true;

@@ -1378,3 +918,3 @@ }

if (!updateAccepted) {
console.log("[HMR] Updated rejected, reloading...");
console.log("[HMR] Update rejected, reloading...");
window.location.reload();

@@ -1424,2 +964,3 @@ }

exports.Await = Await;
exports.Form = Form;
exports.Link = Link;

@@ -1432,4 +973,2 @@ exports.Links = Links;

exports.RemixContext = RemixContext;
exports.RemixRoute = RemixRoute;
exports.RemixRouteError = RemixRouteError;
exports.Scripts = Scripts;

@@ -1439,5 +978,5 @@ exports.composeEventHandlers = composeEventHandlers;

exports.useFetcher = useFetcher;
exports.useFetchers = useFetchers;
exports.useLoaderData = useLoaderData;
exports.useMatches = useMatches;
exports.useTransition = useTransition;
exports.useRemixContext = useRemixContext;
exports.useRouteLoaderData = useRouteLoaderData;
import { UNSAFE_DeferredData as DeferredData } from "@remix-run/router";
/**
* Data for a route that was returned from a `loader()`.
*
* Note: This moves to unknown in ReactRouter and eventually likely in Remix
*/
export type AppData = any;
export type AppData = unknown;
export declare function isCatchResponse(response: Response): boolean;
export declare function isErrorResponse(response: any): response is Response;
export declare function isNetworkErrorResponse(response: any): response is Response;
export declare function isRedirectResponse(response: Response): boolean;
export declare function isDeferredResponse(response: Response): boolean;
export declare function isResponse(value: any): value is Response;
export declare function isDeferredData(value: any): value is DeferredData;
export declare function fetchData(request: Request, routeId: string, retry?: number): Promise<Response | Error>;
export declare function createRequestInit(request: Request): Promise<RequestInit>;
export declare function parseDeferredReadableStream(stream: ReadableStream<Uint8Array>): Promise<DeferredData>;
/**
* @remix-run/react v0.0.0-nightly-ccefed3-20230621
* @remix-run/react v0.0.0-nightly-cd403b516-20240809
*

@@ -19,4 +19,2 @@ * Copyright (c) Remix Software Inc.

* Data for a route that was returned from a `loader()`.
*
* Note: This moves to unknown in ReactRouter and eventually likely in Remix
*/

@@ -30,2 +28,14 @@

}
function isNetworkErrorResponse(response) {
// If we reach the Remix server, we can safely identify response types via the
// X-Remix-Error/X-Remix-Catch headers. However, if we never reach the Remix
// server, and instead receive a 4xx/5xx from somewhere in between (like
// Cloudflare), then we get a false negative in the isErrorResponse check and
// we incorrectly assume that the user returns the 4xx/5xx response and
// consider it successful. To alleviate this, we add X-Remix-Response to any
// non-Error/non-Catch responses coming back from the server. If we don't
// see this, we can conclude that a 4xx/5xx response never actually reached
// the Remix server and we can bubble it up as an error.
return isResponse(response) && response.status >= 400 && response.headers.get("X-Remix-Error") == null && response.headers.get("X-Remix-Catch") == null && response.headers.get("X-Remix-Response") == null;
}
function isRedirectResponse(response) {

@@ -38,16 +48,12 @@ return response.headers.get("X-Remix-Redirect") != null;

}
function isResponse(value) {
return value != null && typeof value.status === "number" && typeof value.statusText === "string" && typeof value.headers === "object" && typeof value.body !== "undefined";
}
function isDeferredData(value) {
let deferred = value;
return deferred && typeof deferred === "object" && typeof deferred.data === "object" && typeof deferred.subscribe === "function" && typeof deferred.cancel === "function" && typeof deferred.resolveData === "function";
}
async function fetchData(request, routeId, retry = 0) {
let url = new URL(request.url);
url.searchParams.set("_data", routeId);
let init = {
signal: request.signal
};
if (request.method !== "GET") {
init.method = request.method;
let contentType = request.headers.get("Content-Type");
init.body =
// Check between word boundaries instead of startsWith() due to the last
// paragraph of https://httpwg.org/specs/rfc9110.html#field.content-type
contentType && /\bapplication\/x-www-form-urlencoded\b/.test(contentType) ? new URLSearchParams(await request.text()) : await request.formData();
}
if (retry > 0) {

@@ -58,2 +64,3 @@ // Retry up to 3 times waiting 50, 250, 1250 ms

}
let init = await createRequestInit(request);
let revalidation = window.__remixRevalidation;

@@ -72,4 +79,38 @@ let response = await fetch(url.href, init).catch(error => {

}
if (isNetworkErrorResponse(response)) {
let text = await response.text();
let error = new Error(text);
error.stack = undefined;
return error;
}
return response;
}
async function createRequestInit(request) {
let init = {
signal: request.signal
};
if (request.method !== "GET") {
init.method = request.method;
let contentType = request.headers.get("Content-Type");
// Check between word boundaries instead of startsWith() due to the last
// paragraph of https://httpwg.org/specs/rfc9110.html#field.content-type
if (contentType && /\bapplication\/json\b/.test(contentType)) {
init.headers = {
"Content-Type": contentType
};
init.body = JSON.stringify(await request.json());
} else if (contentType && /\btext\/plain\b/.test(contentType)) {
init.headers = {
"Content-Type": contentType
};
init.body = await request.text();
} else if (contentType && /\bapplication\/x-www-form-urlencoded\b/.test(contentType)) {
init.body = new URLSearchParams(await request.text());
} else {
init.body = await request.formData();
}
}
return init;
}
const DEFERRED_VALUE_PLACEHOLDER_PREFIX = "__deferred_promise:";

@@ -114,3 +155,3 @@ async function parseDeferredReadableStream(stream) {

// Read the rest of the stream and resolve deferred promises
(async () => {
void (async () => {
try {

@@ -236,7 +277,11 @@ for await (let section of sectionReader) {

exports.createRequestInit = createRequestInit;
exports.fetchData = fetchData;
exports.isCatchResponse = isCatchResponse;
exports.isDeferredData = isDeferredData;
exports.isDeferredResponse = isDeferredResponse;
exports.isErrorResponse = isErrorResponse;
exports.isNetworkErrorResponse = isNetworkErrorResponse;
exports.isRedirectResponse = isRedirectResponse;
exports.isResponse = isResponse;
exports.parseDeferredReadableStream = parseDeferredReadableStream;
import type { StaticHandlerContext } from "@remix-run/router";
import type { RouteManifest, EntryRoute } from "./routes";
import type { RouteModules } from "./routeModules";
type SerializedError = {
message: string;
stack?: string;
};
export interface RemixContextObject {
manifest: AssetsManifest;
routeModules: RouteModules;
criticalCss?: string;
serverHandoffString?: string;
future: FutureConfig;
isSpaMode: boolean;
abortDelay?: number;
dev?: {
port: number;
serializeError?(error: Error): SerializedError;
renderMeta?: {
didRenderScripts?: boolean;
streamCache?: Record<number, Promise<void> & {
result?: {
done: boolean;
value: string;
};
error?: unknown;
}>;
};

@@ -16,20 +30,9 @@ }

staticHandlerContext: StaticHandlerContext;
serverHandoffStream?: ReadableStream<Uint8Array>;
}
type Dev = {
port?: number;
appServerPort?: number;
remixRequestHandlerPath?: string;
rebuildPollIntervalMs?: number;
};
export interface FutureConfig {
v2_dev: boolean | Dev;
/** @deprecated Use the `postcss` config option instead */
unstable_postcss: boolean;
/** @deprecated Use the `tailwind` config option instead */
unstable_tailwind: boolean;
v2_errorBoundary: boolean;
v2_headers: boolean;
v2_meta: boolean;
v2_normalizeFormMethod: boolean;
v2_routeConvention: boolean;
v3_fetcherPersist: boolean;
v3_relativeSplatPath: boolean;
unstable_lazyRouteDiscovery: boolean;
unstable_singleFetch: boolean;
}

@@ -45,3 +48,3 @@ export interface AssetsManifest {

hmr?: {
timestamp: number;
timestamp?: number;
runtime: string;

@@ -48,0 +51,0 @@ };

@@ -1,8 +0,6 @@

import React from "react";
import type { ErrorResponse, Location } from "@remix-run/router";
import type { CatchBoundaryComponent, ErrorBoundaryComponent } from "./routeModules";
import type { ThrownResponse } from "./errors";
import * as React from "react";
import type { Location } from "@remix-run/router";
type RemixErrorBoundaryProps = React.PropsWithChildren<{
location: Location;
component: ErrorBoundaryComponent;
isOutsideRemixApp?: boolean;
error?: Error;

@@ -21,5 +19,5 @@ }>;

error: Error | null;
location: Location;
location: Location<any>;
};
render(): string | number | boolean | React.ReactFragment | JSX.Element | null | undefined;
render(): string | number | boolean | Iterable<React.ReactNode> | React.JSX.Element | null | undefined;
}

@@ -29,23 +27,12 @@ /**

*/
export declare function RemixRootDefaultErrorBoundary({ error }: {
error: Error;
}): JSX.Element;
export declare function V2_RemixRootDefaultErrorBoundary(): JSX.Element;
/**
* Returns the status code and thrown response data.
*
* @deprecated Please enable the v2_errorBoundary flag
*
* @see https://remix.run/route/catch-boundary
*/
export declare function useCatch<Result extends ThrownResponse = ThrownResponse>(): Result;
type RemixCatchBoundaryProps = React.PropsWithChildren<{
component: CatchBoundaryComponent;
catch?: ErrorResponse;
}>;
export declare function RemixCatchBoundary({ catch: catchVal, component: Component, children, }: RemixCatchBoundaryProps): JSX.Element;
/**
* When app's don't provide a root level CatchBoundary, we default to this.
*/
export declare function RemixRootDefaultCatchBoundary(): JSX.Element;
export declare function RemixRootDefaultErrorBoundary({ error, isOutsideRemixApp, }: {
error: unknown;
isOutsideRemixApp?: boolean;
}): React.JSX.Element;
export declare function BoundaryShell({ title, renderScripts, isOutsideRemixApp, children, }: {
title: string;
renderScripts?: boolean;
isOutsideRemixApp?: boolean;
children: React.ReactNode | React.ReactNode[];
}): string | number | boolean | Iterable<React.ReactNode> | React.JSX.Element | null | undefined;
export {};
/**
* @remix-run/react v0.0.0-nightly-ccefed3-20230621
* @remix-run/react v0.0.0-nightly-cd403b516-20240809
*

@@ -17,8 +17,25 @@ * Copyright (c) Remix Software Inc.

var reactRouterDom = require('react-router-dom');
var components = require('./components.js');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
function _interopNamespace(e) {
if (e && e.__esModule) return e;
var n = Object.create(null);
if (e) {
Object.keys(e).forEach(function (k) {
if (k !== 'default') {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () { return e[k]; }
});
}
});
}
n["default"] = e;
return Object.freeze(n);
}
var React__default = /*#__PURE__*/_interopDefaultLegacy(React);
var React__namespace = /*#__PURE__*/_interopNamespace(React);
class RemixErrorBoundary extends React__default["default"].Component {
class RemixErrorBoundary extends React__namespace.Component {
constructor(props) {

@@ -64,4 +81,5 @@ super(props);

if (this.state.error) {
return /*#__PURE__*/React__default["default"].createElement(this.props.component, {
error: this.state.error
return /*#__PURE__*/React__namespace.createElement(RemixRootDefaultErrorBoundary, {
error: this.state.error,
isOutsideRemixApp: true
});

@@ -78,25 +96,39 @@ } else {

function RemixRootDefaultErrorBoundary({
error
error,
isOutsideRemixApp
}) {
// Only log client side to avoid double-logging on the server
React__default["default"].useEffect(() => {
console.error(error);
}, [error]);
return /*#__PURE__*/React__default["default"].createElement("html", {
lang: "en"
}, /*#__PURE__*/React__default["default"].createElement("head", null, /*#__PURE__*/React__default["default"].createElement("meta", {
charSet: "utf-8"
}), /*#__PURE__*/React__default["default"].createElement("meta", {
name: "viewport",
content: "width=device-width, initial-scale=1, viewport-fit=cover"
}), /*#__PURE__*/React__default["default"].createElement("title", null, "Application Error!")), /*#__PURE__*/React__default["default"].createElement("body", null, /*#__PURE__*/React__default["default"].createElement("main", {
style: {
fontFamily: "system-ui, sans-serif",
padding: "2rem"
console.error(error);
let heyDeveloper = /*#__PURE__*/React__namespace.createElement("script", {
dangerouslySetInnerHTML: {
__html: `
console.log(
"💿 Hey developer 👋. You can provide a way better UX than this when your app throws errors. Check out https://remix.run/guides/errors for more information."
);
`
}
}, /*#__PURE__*/React__default["default"].createElement("h1", {
});
if (reactRouterDom.isRouteErrorResponse(error)) {
return /*#__PURE__*/React__namespace.createElement(BoundaryShell, {
title: "Unhandled Thrown Response!"
}, /*#__PURE__*/React__namespace.createElement("h1", {
style: {
fontSize: "24px"
}
}, error.status, " ", error.statusText), heyDeveloper);
}
let errorInstance;
if (error instanceof Error) {
errorInstance = error;
} else {
let errorString = error == null ? "Unknown Error" : typeof error === "object" && "toString" in error ? error.toString() : JSON.stringify(error);
errorInstance = new Error(errorString);
}
return /*#__PURE__*/React__namespace.createElement(BoundaryShell, {
title: "Application Error!",
isOutsideRemixApp: isOutsideRemixApp
}, /*#__PURE__*/React__namespace.createElement("h1", {
style: {
fontSize: "24px"
}
}, "Application Error"), error.stack ? /*#__PURE__*/React__default["default"].createElement("pre", {
}, "Application Error"), /*#__PURE__*/React__namespace.createElement("pre", {
style: {

@@ -108,74 +140,42 @@ padding: "2rem",

}
}, error.stack) : null), /*#__PURE__*/React__default["default"].createElement("script", {
dangerouslySetInnerHTML: {
__html: `
console.log(
"💿 Hey developer👋. You can provide a way better UX than this when your app throws errors. Check out https://remix.run/guides/errors for more information."
);
`
}
})));
}, errorInstance.stack), heyDeveloper);
}
function V2_RemixRootDefaultErrorBoundary() {
let error = reactRouterDom.useRouteError();
if (reactRouterDom.isRouteErrorResponse(error)) {
return /*#__PURE__*/React__default["default"].createElement(RemixRootDefaultCatchBoundaryImpl, {
caught: error
});
} else if (error instanceof Error) {
return /*#__PURE__*/React__default["default"].createElement(RemixRootDefaultErrorBoundary, {
error: error
});
} else {
let errorString = error == null ? "Unknown Error" : typeof error === "object" && "toString" in error ? error.toString() : JSON.stringify(error);
return /*#__PURE__*/React__default["default"].createElement(RemixRootDefaultErrorBoundary, {
error: new Error(errorString)
});
}
}
let RemixCatchContext = /*#__PURE__*/React__default["default"].createContext(undefined);
/**
* Returns the status code and thrown response data.
*
* @deprecated Please enable the v2_errorBoundary flag
*
* @see https://remix.run/route/catch-boundary
*/
function useCatch() {
return React.useContext(RemixCatchContext);
}
function RemixCatchBoundary({
catch: catchVal,
component: Component,
function BoundaryShell({
title,
renderScripts,
isOutsideRemixApp,
children
}) {
if (catchVal) {
return /*#__PURE__*/React__default["default"].createElement(RemixCatchContext.Provider, {
value: catchVal
}, /*#__PURE__*/React__default["default"].createElement(Component, null));
var _routeModules$root;
let {
routeModules
} = components.useRemixContext();
// Generally speaking, when the root route has a Layout we want to use that
// as the app shell instead of the default `BoundaryShell` wrapper markup below.
// This is true for `loader`/`action` errors, most render errors, and
// `HydrateFallback` scenarios.
// However, render errors thrown from the `Layout` present a bit of an issue
// because if the `Layout` itself throws during the `ErrorBoundary` pass and
// we bubble outside the `RouterProvider` to the wrapping `RemixErrorBoundary`,
// by returning only `children` here we'll be trying to append a `<div>` to
// the `document` and the DOM will throw, putting React into an error/hydration
// loop.
// Instead, if we're ever rendering from the outermost `RemixErrorBoundary`
// during hydration that wraps `RouterProvider`, then we can't trust the
// `Layout` and should fallback to the default app shell so we're always
// returning an `<html>` document.
if ((_routeModules$root = routeModules.root) !== null && _routeModules$root !== void 0 && _routeModules$root.Layout && !isOutsideRemixApp) {
return children;
}
return /*#__PURE__*/React__default["default"].createElement(React__default["default"].Fragment, null, children);
}
/**
* When app's don't provide a root level CatchBoundary, we default to this.
*/
function RemixRootDefaultCatchBoundary() {
let caught = useCatch();
return /*#__PURE__*/React__default["default"].createElement(RemixRootDefaultCatchBoundaryImpl, {
caught: caught
});
}
function RemixRootDefaultCatchBoundaryImpl({
caught
}) {
return /*#__PURE__*/React__default["default"].createElement("html", {
return /*#__PURE__*/React__namespace.createElement("html", {
lang: "en"
}, /*#__PURE__*/React__default["default"].createElement("head", null, /*#__PURE__*/React__default["default"].createElement("meta", {
}, /*#__PURE__*/React__namespace.createElement("head", null, /*#__PURE__*/React__namespace.createElement("meta", {
charSet: "utf-8"
}), /*#__PURE__*/React__default["default"].createElement("meta", {
}), /*#__PURE__*/React__namespace.createElement("meta", {
name: "viewport",
content: "width=device-width, initial-scale=1, viewport-fit=cover"
}), /*#__PURE__*/React__default["default"].createElement("title", null, "Unhandled Thrown Response!")), /*#__PURE__*/React__default["default"].createElement("body", null, /*#__PURE__*/React__default["default"].createElement("h1", {
content: "width=device-width,initial-scale=1,viewport-fit=cover"
}), /*#__PURE__*/React__namespace.createElement("title", null, title)), /*#__PURE__*/React__namespace.createElement("body", null, /*#__PURE__*/React__namespace.createElement("main", {
style: {

@@ -185,18 +185,7 @@ fontFamily: "system-ui, sans-serif",

}
}, caught.status, " ", caught.statusText), /*#__PURE__*/React__default["default"].createElement("script", {
dangerouslySetInnerHTML: {
__html: `
console.log(
"💿 Hey developer👋. You can provide a way better UX than this when your app throws 404s (and other responses). Check out https://remix.run/guides/not-found for more information."
);
`
}
})));
}, children, renderScripts ? /*#__PURE__*/React__namespace.createElement(components.Scripts, null) : null)));
}
exports.RemixCatchBoundary = RemixCatchBoundary;
exports.BoundaryShell = BoundaryShell;
exports.RemixErrorBoundary = RemixErrorBoundary;
exports.RemixRootDefaultCatchBoundary = RemixRootDefaultCatchBoundary;
exports.RemixRootDefaultErrorBoundary = RemixRootDefaultErrorBoundary;
exports.V2_RemixRootDefaultErrorBoundary = V2_RemixRootDefaultErrorBoundary;
exports.useCatch = useCatch;
import type { Router as RemixRouter } from "@remix-run/router";
import type { AppData } from "./data";
/**
* @deprecated in favor of the `ErrorResponse` class in React Router. Please
* enable the `future.v2_errorBoundary` flag to ease your migration to Remix v2.
*/
export interface ThrownResponse<Status extends number = number, Data = AppData> {
status: Status;
statusText: string;
data: Data;
}
export declare function deserializeErrors(errors: RemixRouter["state"]["errors"]): RemixRouter["state"]["errors"];
/**
* @remix-run/react v0.0.0-nightly-ccefed3-20230621
* @remix-run/react v0.0.0-nightly-cd403b516-20240809
*

@@ -17,7 +17,2 @@ * Copyright (c) Remix Software Inc.

/**
* @deprecated in favor of the `ErrorResponse` class in React Router. Please
* enable the `future.v2_errorBoundary` flag to ease your migration to Remix v2.
*/
function deserializeErrors(errors) {

@@ -31,7 +26,23 @@ if (!errors) return null;

if (val && val.__type === "RouteErrorResponse") {
serialized[key] = new router.ErrorResponse(val.status, val.statusText, val.data, val.internal === true);
serialized[key] = new router.UNSAFE_ErrorResponseImpl(val.status, val.statusText, val.data, val.internal === true);
} else if (val && val.__type === "Error") {
let error = new Error(val.message);
error.stack = val.stack;
serialized[key] = error;
// Attempt to reconstruct the right type of Error (i.e., ReferenceError)
if (val.__subType) {
let ErrorConstructor = window[val.__subType];
if (typeof ErrorConstructor === "function") {
try {
// @ts-expect-error
let error = new ErrorConstructor(val.message);
error.stack = val.stack;
serialized[key] = error;
} catch (e) {
// no-op - fall through and create a normal Error
}
}
}
if (serialized[key] == null) {
let error = new Error(val.message);
error.stack = val.stack;
serialized[key] = error;
}
} else {

@@ -38,0 +49,0 @@ serialized[key] = val;

/**
* @remix-run/react v0.0.0-nightly-ccefed3-20230621
* @remix-run/react v0.0.0-nightly-cd403b516-20240809
*

@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc.

/**
* @remix-run/react v0.0.0-nightly-ccefed3-20230621
* @remix-run/react v0.0.0-nightly-cd403b516-20240809
*

@@ -11,8 +11,13 @@ * Copyright (c) Remix Software Inc.

*/
import { createRouter, createBrowserHistory } from '@remix-run/router';
import * as React from 'react';
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
import { UNSAFE_mapRouteProperties } from 'react-router';
import { matchRoutes, RouterProvider } from 'react-router-dom';
import { RemixContext } from './components.js';
import { RemixErrorBoundary, RemixRootDefaultErrorBoundary } from './errorBoundaries.js';
import { RemixErrorBoundary } from './errorBoundaries.js';
import { deserializeErrors } from './errors.js';
import { createClientRoutesWithHMRRevalidationOptOut, createClientRoutes } from './routes.js';
import { createClientRoutesWithHMRRevalidationOptOut, createClientRoutes, shouldHydrateRouteLoader } from './routes.js';
import { decodeViaTurboStream, getSingleFetchDataStrategy } from './single-fetch.js';
import invariant from './invariant.js';
import { initFogOfWar, useFogOFWarDiscovery } from './fog-of-war.js';

@@ -23,5 +28,23 @@ /* eslint-disable prefer-let/prefer-let */

let stateDecodingPromise;
let router;
let routerInitialized = false;
let hmrAbortController;
let hmrRouterReadyResolve;
// There's a race condition with HMR where the remix:manifest is signaled before
// the router is assigned in the RemixBrowser component. This promise gates the
// HMR handler until the router is ready
let hmrRouterReadyPromise = new Promise(resolve => {
// body of a promise is executed immediately, so this can be resolved outside
// of the promise body
hmrRouterReadyResolve = resolve;
}).catch(() => {
// This is a noop catch handler to avoid unhandled promise rejection warnings
// in the console. The promise is never rejected.
return undefined;
});
// @ts-expect-error
if (import.meta && import.meta.hot) {
// @ts-expect-error
import.meta.hot.accept("remix:manifest", async ({

@@ -31,2 +54,8 @@ assetsManifest,

}) => {
let router = await hmrRouterReadyPromise;
// This should never happen, but just in case...
if (!router) {
console.error("Failed to accept HMR update because the router was not ready.");
return;
}
let routeIds = [...new Set(router.state.matches.map(m => m.route.id).concat(Object.keys(window.__remixRouteModules)))];

@@ -51,4 +80,4 @@ if (hmrAbortController) {

default: imported.default ? ((_window$__remixRouteM = window.__remixRouteModules[id]) === null || _window$__remixRouteM === void 0 ? void 0 : _window$__remixRouteM.default) ?? imported.default : imported.default,
CatchBoundary: imported.CatchBoundary ? ((_window$__remixRouteM2 = window.__remixRouteModules[id]) === null || _window$__remixRouteM2 === void 0 ? void 0 : _window$__remixRouteM2.CatchBoundary) ?? imported.CatchBoundary : imported.CatchBoundary,
ErrorBoundary: imported.ErrorBoundary ? ((_window$__remixRouteM3 = window.__remixRouteModules[id]) === null || _window$__remixRouteM3 === void 0 ? void 0 : _window$__remixRouteM3.ErrorBoundary) ?? imported.ErrorBoundary : imported.ErrorBoundary
ErrorBoundary: imported.ErrorBoundary ? ((_window$__remixRouteM2 = window.__remixRouteModules[id]) === null || _window$__remixRouteM2 === void 0 ? void 0 : _window$__remixRouteM2.ErrorBoundary) ?? imported.ErrorBoundary : imported.ErrorBoundary,
HydrateFallback: imported.HydrateFallback ? ((_window$__remixRouteM3 = window.__remixRouteModules[id]) === null || _window$__remixRouteM3 === void 0 ? void 0 : _window$__remixRouteM3.HydrateFallback) ?? imported.HydrateFallback : imported.HydrateFallback
}];

@@ -58,3 +87,3 @@ }))).filter(Boolean)));

// Create new routes
let routes = createClientRoutesWithHMRRevalidationOptOut(needsRevalidation, assetsManifest.routes, window.__remixRouteModules, window.__remixContext.future);
let routes = createClientRoutesWithHMRRevalidationOptOut(needsRevalidation, assetsManifest.routes, window.__remixRouteModules, window.__remixContext.state, window.__remixContext.future, window.__remixContext.isSpaMode);

@@ -91,37 +120,157 @@ // This is temporary API and will be more granular before release

if (!router) {
let routes = createClientRoutes(window.__remixManifest.routes, window.__remixRouteModules, window.__remixContext.future);
let hydrationData = window.__remixContext.state;
if (hydrationData && hydrationData.errors) {
// Hard reload if the path we tried to load is not the current path.
// This is usually the result of 2 rapid back/forward clicks from an
// external site into a Remix app, where we initially start the load for
// one URL and while the JS chunks are loading a second forward click moves
// us to a new URL. Avoid comparing search params because of CDNs which
// can be configured to ignore certain params and only pathname is relevant
// towards determining the route matches.
let initialPathname = window.__remixContext.url;
let hydratedPathname = window.location.pathname;
if (initialPathname !== hydratedPathname && !window.__remixContext.isSpaMode) {
let errorMsg = `Initial URL (${initialPathname}) does not match URL at time of hydration ` + `(${hydratedPathname}), reloading page...`;
console.error(errorMsg);
window.location.reload();
// Get out of here so the reload can happen - don't create the router
// since it'll then kick off unnecessary route.lazy() loads
return /*#__PURE__*/React.createElement(React.Fragment, null);
}
// When single fetch is enabled, we need to suspend until the initial state
// snapshot is decoded into window.__remixContext.state
if (window.__remixContext.future.unstable_singleFetch) {
// Note: `stateDecodingPromise` is not coupled to `router` - we'll reach this
// code potentially many times waiting for our state to arrive, but we'll
// then only get past here and create the `router` one time
if (!stateDecodingPromise) {
let stream = window.__remixContext.stream;
invariant(stream, "No stream found for single fetch decoding");
window.__remixContext.stream = undefined;
stateDecodingPromise = decodeViaTurboStream(stream, window).then(value => {
window.__remixContext.state = value.value;
stateDecodingPromise.value = true;
}).catch(e => {
stateDecodingPromise.error = e;
});
}
if (stateDecodingPromise.error) {
throw stateDecodingPromise.error;
}
if (!stateDecodingPromise.value) {
throw stateDecodingPromise;
}
}
let routes = createClientRoutes(window.__remixManifest.routes, window.__remixRouteModules, window.__remixContext.state, window.__remixContext.future, window.__remixContext.isSpaMode);
let hydrationData = undefined;
if (!window.__remixContext.isSpaMode) {
// Create a shallow clone of `loaderData` we can mutate for partial hydration.
// When a route exports a `clientLoader` and a `HydrateFallback`, the SSR will
// render the fallback so we need the client to do the same for hydration.
// The server loader data has already been exposed to these route `clientLoader`'s
// in `createClientRoutes` above, so we need to clear out the version we pass to
// `createBrowserRouter` so it initializes and runs the client loaders.
hydrationData = {
...hydrationData,
errors: deserializeErrors(hydrationData.errors)
...window.__remixContext.state,
loaderData: {
...window.__remixContext.state.loaderData
}
};
let initialMatches = matchRoutes(routes, window.location, window.__remixContext.basename);
if (initialMatches) {
for (let match of initialMatches) {
let routeId = match.route.id;
let route = window.__remixRouteModules[routeId];
let manifestRoute = window.__remixManifest.routes[routeId];
// Clear out the loaderData to avoid rendering the route component when the
// route opted into clientLoader hydration and either:
// * gave us a HydrateFallback
// * or doesn't have a server loader and we have no data to render
if (route && shouldHydrateRouteLoader(manifestRoute, route, window.__remixContext.isSpaMode) && (route.HydrateFallback || !manifestRoute.hasLoader)) {
hydrationData.loaderData[routeId] = undefined;
} else if (manifestRoute && !manifestRoute.hasLoader) {
// Since every Remix route gets a `loader` on the client side to load
// the route JS module, we need to add a `null` value to `loaderData`
// for any routes that don't have server loaders so our partial
// hydration logic doesn't kick off the route module loaders during
// hydration
hydrationData.loaderData[routeId] = null;
}
}
}
if (hydrationData && hydrationData.errors) {
hydrationData.errors = deserializeErrors(hydrationData.errors);
}
}
router = createBrowserRouter(routes, {
let {
enabled: isFogOfWarEnabled,
patchRoutesOnMiss
} = initFogOfWar(window.__remixManifest, window.__remixRouteModules, window.__remixContext.future, window.__remixContext.isSpaMode, window.__remixContext.basename);
// We don't use createBrowserRouter here because we need fine-grained control
// over initialization to support synchronous `clientLoader` flows.
router = createRouter({
routes,
history: createBrowserHistory(),
basename: window.__remixContext.basename,
future: {
v7_normalizeFormMethod: true,
v7_fetcherPersist: window.__remixContext.future.v3_fetcherPersist,
v7_partialHydration: true,
v7_prependBasename: true,
v7_relativeSplatPath: window.__remixContext.future.v3_relativeSplatPath,
// Single fetch enables this underlying behavior
v7_skipActionErrorRevalidation: window.__remixContext.future.unstable_singleFetch === true
},
hydrationData,
future: {
// Pass through the Remix future flag to avoid a v1 breaking change in
// useNavigation() - users can control the casing via the flag in v1.
// useFetcher still always uppercases in the back-compat layer in v1.
// In v2 we can just always pass true here and remove the back-compat
// layer
v7_normalizeFormMethod: window.__remixContext.future.v2_normalizeFormMethod
}
mapRouteProperties: UNSAFE_mapRouteProperties,
unstable_dataStrategy: window.__remixContext.future.unstable_singleFetch ? getSingleFetchDataStrategy(window.__remixManifest, window.__remixRouteModules) : undefined,
...(isFogOfWarEnabled ? {
unstable_patchRoutesOnMiss: patchRoutesOnMiss
} : {})
});
// Hard reload if the URL we tried to load is not the current URL.
// This is usually the result of 2 rapid backwards/forward clicks from an
// external site into a Remix app, where we initially start the load for
// one URL and while the JS chunks are loading a second forward click moves
// us to a new URL
let initialUrl = window.__remixContext.url;
let hydratedUrl = window.location.pathname + window.location.search;
if (initialUrl !== hydratedUrl) {
let errorMsg = `Initial URL (${initialUrl}) does not match URL at time of hydration ` + `(${hydratedUrl}), reloading page...`;
console.error(errorMsg);
window.location.reload();
// We can call initialize() immediately if the router doesn't have any
// loaders to run on hydration
if (router.state.initialized) {
routerInitialized = true;
router.initialize();
}
// @ts-ignore
router.createRoutesForHMR = createClientRoutesWithHMRRevalidationOptOut;
window.__remixRouter = router;
// Notify that the router is ready for HMR
if (hmrRouterReadyResolve) {
hmrRouterReadyResolve(router);
}
}
// Critical CSS can become stale after code changes, e.g. styles might be
// removed from a component, but the styles will still be present in the
// server HTML. This allows our HMR logic to clear the critical CSS state.
// eslint-disable-next-line react-hooks/rules-of-hooks
let [criticalCss, setCriticalCss] = React.useState(process.env.NODE_ENV === "development" ? window.__remixContext.criticalCss : undefined);
if (process.env.NODE_ENV === "development") {
window.__remixClearCriticalCss = () => setCriticalCss(undefined);
}
// This is due to the short circuit return above when the pathname doesn't
// match and we force a hard reload. This is an exceptional scenario in which
// we can't hydrate anyway.
// eslint-disable-next-line react-hooks/rules-of-hooks
let [location, setLocation] = React.useState(router.state.location);
// eslint-disable-next-line react-hooks/rules-of-hooks
React.useLayoutEffect(() => {
// If we had to run clientLoaders on hydration, we delay initialization until
// after we've hydrated to avoid hydration issues from synchronous client loaders
if (!routerInitialized) {
routerInitialized = true;
router.initialize();
}
}, []);
// eslint-disable-next-line react-hooks/rules-of-hooks
React.useLayoutEffect(() => {
return router.subscribe(newState => {

@@ -134,2 +283,5 @@ if (newState.location !== location) {

// eslint-disable-next-line react-hooks/rules-of-hooks
useFogOFWarDiscovery(router, window.__remixManifest, window.__remixRouteModules, window.__remixContext.future, window.__remixContext.isSpaMode);
// We need to include a wrapper RemixErrorBoundary here in case the root error

@@ -139,20 +291,26 @@ // boundary also throws and we need to bubble up outside of the router entirely.

// out of there
return /*#__PURE__*/React.createElement(RemixContext.Provider, {
value: {
manifest: window.__remixManifest,
routeModules: window.__remixRouteModules,
future: window.__remixContext.future
}
}, /*#__PURE__*/React.createElement(RemixErrorBoundary, {
location: location,
component: RemixRootDefaultErrorBoundary
}, /*#__PURE__*/React.createElement(RouterProvider, {
router: router,
fallbackElement: null,
future: {
v7_startTransition: true
}
})));
return (
/*#__PURE__*/
// This fragment is important to ensure we match the <RemixServer> JSX
// structure so that useId values hydrate correctly
React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(RemixContext.Provider, {
value: {
manifest: window.__remixManifest,
routeModules: window.__remixRouteModules,
future: window.__remixContext.future,
criticalCss,
isSpaMode: window.__remixContext.isSpaMode
}
}, /*#__PURE__*/React.createElement(RemixErrorBoundary, {
location: location
}, /*#__PURE__*/React.createElement(RouterProvider, {
router: router,
fallbackElement: null,
future: {
v7_startTransition: true
}
}))), window.__remixContext.future.unstable_singleFetch ? /*#__PURE__*/React.createElement(React.Fragment, null) : null)
);
}
export { RemixBrowser };
/**
* @remix-run/react v0.0.0-nightly-ccefed3-20230621
* @remix-run/react v0.0.0-nightly-cd403b516-20240809
*

@@ -13,9 +13,8 @@ * Copyright (c) Remix Software Inc.

import * as React from 'react';
import { useHref, NavLink as NavLink$1, Link as Link$1, matchRoutes, useLocation, Await as Await$1, useNavigation, useAsyncError, useMatches as useMatches$1, useLoaderData as useLoaderData$1, useActionData as useActionData$1, useFetchers as useFetchers$1, useFetcher as useFetcher$1, UNSAFE_DataRouterContext, UNSAFE_DataRouterStateContext, Outlet, useRouteError, isRouteErrorResponse } from 'react-router-dom';
import { RemixRootDefaultErrorBoundary, RemixCatchBoundary, V2_RemixRootDefaultErrorBoundary, RemixRootDefaultCatchBoundary } from './errorBoundaries.js';
import { useHref, NavLink as NavLink$1, Link as Link$1, Form as Form$1, matchRoutes, useLocation, Await as Await$1, useAsyncError, useMatches as useMatches$1, useLoaderData as useLoaderData$1, useRouteLoaderData as useRouteLoaderData$1, useActionData as useActionData$1, useFetcher as useFetcher$1, UNSAFE_DataRouterContext, UNSAFE_DataRouterStateContext } from 'react-router-dom';
import invariant from './invariant.js';
import { getLinksForMatches, isPageLinkDescriptor, getNewMatchesForLinks, getDataLinkHrefs, getModuleLinkHrefs, getStylesheetPrefetchLinks } from './links.js';
import { getKeyedLinksForMatches, isPageLinkDescriptor, getNewMatchesForLinks, getDataLinkHrefs, getModuleLinkHrefs, getKeyedPrefetchLinks } from './links.js';
import { escapeHtml, createHtml } from './markup.js';
import { IDLE_TRANSITION, IDLE_FETCHER } from './transition.js';
import { logDeprecationOnce } from './warnings.js';
import { addRevalidationParam, singleFetchUrl } from './single-fetch.js';
import { isFogOfWarEnabled, getPartialManifest } from './fog-of-war.js';

@@ -45,92 +44,20 @@ function useDataRouterContext() {

////////////////////////////////////////////////////////////////////////////////
// RemixRoute
function RemixRoute({
id
}) {
let {
routeModules,
future
} = useRemixContext();
invariant(routeModules, "Cannot initialize 'routeModules'. This normally occurs when you have server code in your client modules.\n" + "Check this link for more details:\nhttps://remix.run/pages/gotchas#server-code-in-client-bundles");
let {
default: Component,
ErrorBoundary,
CatchBoundary
} = routeModules[id];
// Default Component to Outlet if we expose boundary UI components
if (!Component && (ErrorBoundary || !future.v2_errorBoundary && CatchBoundary)) {
Component = Outlet;
}
invariant(Component, `Route "${id}" has no component! Please go add a \`default\` export in the route module file.\n` + "If you were trying to navigate or submit to a resource route, use `<a>` instead of `<Link>` or `<Form reloadDocument>`.");
return /*#__PURE__*/React.createElement(Component, null);
}
function RemixRouteError({
id
}) {
let {
future,
routeModules
} = useRemixContext();
// This checks prevent cryptic error messages such as: 'Cannot read properties of undefined (reading 'root')'
invariant(routeModules, "Cannot initialize 'routeModules'. This normally occurs when you have server code in your client modules.\n" + "Check this link for more details:\nhttps://remix.run/pages/gotchas#server-code-in-client-bundles");
let error = useRouteError();
let {
CatchBoundary,
ErrorBoundary
} = routeModules[id];
if (future.v2_errorBoundary) {
// Provide defaults for the root route if they are not present
if (id === "root") {
ErrorBoundary || (ErrorBoundary = V2_RemixRootDefaultErrorBoundary);
}
if (ErrorBoundary) {
// TODO: Unsure if we can satisfy the typings here
// @ts-expect-error
return /*#__PURE__*/React.createElement(ErrorBoundary, null);
}
throw error;
}
// Provide defaults for the root route if they are not present
if (id === "root") {
CatchBoundary || (CatchBoundary = RemixRootDefaultCatchBoundary);
ErrorBoundary || (ErrorBoundary = RemixRootDefaultErrorBoundary);
}
if (isRouteErrorResponse(error)) {
let tError = error;
if (!!(tError !== null && tError !== void 0 && tError.error) && tError.status !== 404 && ErrorBoundary) {
// Internal framework-thrown ErrorResponses
return /*#__PURE__*/React.createElement(ErrorBoundary, {
error: tError.error
});
}
if (CatchBoundary) {
// User-thrown ErrorResponses
return /*#__PURE__*/React.createElement(RemixCatchBoundary, {
catch: error,
component: CatchBoundary
});
}
}
if (error instanceof Error && ErrorBoundary) {
// User- or framework-thrown Errors
return /*#__PURE__*/React.createElement(ErrorBoundary, {
error: error
});
}
throw error;
}
////////////////////////////////////////////////////////////////////////////////
// Public API
/**
* Defines the discovery behavior of the link:
*
* - "render": Eagerly discover when the link is rendered (default)
* - "none": No eager discovery - discover when the link is clicked
*/
/**
* Defines the prefetching behavior of the link:
*
* - "none": Never fetched
* - "intent": Fetched when the user focuses or hovers the link
* - "render": Fetched when the link is rendered
* - "none": Never fetched
* - "viewport": Fetched when the link is in the viewport
*/
function usePrefetchBehavior(prefetch, theirElementProps) {

@@ -196,5 +123,8 @@ let [maybePrefetch, setMaybePrefetch] = React.useState(false);

const ABSOLUTE_URL_REGEX = /^(?:[a-z][a-z0-9+.-]*:|\/\/)/i;
function getDiscoverAttr(discover, isAbsolute, reloadDocument) {
return discover === "render" && !isAbsolute && !reloadDocument ? "true" : undefined;
}
/**
* A special kind of `<Link>` that knows whether or not it is "active".
* A special kind of `<Link>` that knows whether it is "active".
*

@@ -206,2 +136,3 @@ * @see https://remix.run/components/nav-link

prefetch = "none",
discover = "render",
...props

@@ -214,3 +145,4 @@ }, forwardedRef) => {

ref: mergeRefs(forwardedRef, ref),
to: to
to: to,
"data-discover": getDiscoverAttr(discover, isAbsolute, props.reloadDocument)
})), shouldPrefetch && !isAbsolute ? /*#__PURE__*/React.createElement(PrefetchPageLinks, {

@@ -231,2 +163,3 @@ page: href

prefetch = "none",
discover = "render",
...props

@@ -239,3 +172,4 @@ }, forwardedRef) => {

ref: mergeRefs(forwardedRef, ref),
to: to
to: to,
"data-discover": getDiscoverAttr(discover, isAbsolute, props.reloadDocument)
})), shouldPrefetch && !isAbsolute ? /*#__PURE__*/React.createElement(PrefetchPageLinks, {

@@ -246,2 +180,19 @@ page: href

Link.displayName = "Link";
/**
* This component renders a form tag and is the primary way the user will
* submit information via your website.
*
* @see https://remix.run/components/form
*/
let Form = /*#__PURE__*/React.forwardRef(({
discover = "render",
...props
}, forwardedRef) => {
let isAbsolute = typeof props.action === "string" && ABSOLUTE_URL_REGEX.test(props.action);
return /*#__PURE__*/React.createElement(Form$1, _extends({}, props, {
ref: forwardedRef,
"data-discover": getDiscoverAttr(discover, isAbsolute, props.reloadDocument)
}));
});
Form.displayName = "Form";
function composeEventHandlers(theirHandler, ourHandler) {

@@ -255,7 +206,19 @@ return event => {

}
let linksWarning = "⚠️ REMIX FUTURE CHANGE: The behavior of links `imagesizes` and `imagesrcset` will be changing in v2. " + "Only the React camel case versions will be valid. Please change to `imageSizes` and `imageSrcSet`. " + "For instructions on making this change see " + "https://remix.run/docs/en/v1.15.0/pages/v2#links-imagesizes-and-imagesrcset";
let useTransitionWarning = "⚠️ REMIX FUTURE CHANGE: `useTransition` will be removed in v2 in favor of `useNavigation`. " + "You can prepare for this change at your convenience by updating to `useNavigation`. " + "For instructions on making this change see " + "https://remix.run/docs/en/v1.15.0/pages/v2#usetransition";
let fetcherTypeWarning = "⚠️ REMIX FUTURE CHANGE: `fetcher.type` will be removed in v2. " + "Please use `fetcher.state`, `fetcher.formData`, and `fetcher.data` to achieve the same UX. " + "For instructions on making this change see " + "https://remix.run/docs/en/v1.15.0/pages/v2#usefetcher";
let fetcherSubmissionWarning = "⚠️ REMIX FUTURE CHANGE : `fetcher.submission` will be removed in v2. " + "The submission fields are now part of the fetcher object itself (`fetcher.formData`). " + "For instructions on making this change see " + "https://remix.run/docs/en/v1.15.0/pages/v2#usefetcher";
// Return the matches actively being displayed:
// - In SPA Mode we only SSR/hydrate the root match, and include all matches
// after hydration. This lets the router handle initial match loads via lazy().
// - When an error boundary is rendered, we slice off matches up to the
// boundary for <Links>/<Meta>
function getActiveMatches(matches, errors, isSpaMode) {
if (isSpaMode && !isHydrated) {
return [matches[0]];
}
if (errors) {
let errorIdx = matches.findIndex(m => errors[m.route.id] !== undefined);
return matches.slice(0, errorIdx + 1);
}
return matches;
}
/**

@@ -268,4 +231,6 @@ * Renders the `<link>` tags for the current routes.

let {
isSpaMode,
manifest,
routeModules
routeModules,
criticalCss
} = useRemixContext();

@@ -276,48 +241,20 @@ let {

} = useDataRouterStateContext();
let matches = errors ? routerMatches.slice(0, routerMatches.findIndex(m => errors[m.route.id]) + 1) : routerMatches;
let links = React.useMemo(() => getLinksForMatches(matches, routeModules, manifest), [matches, routeModules, manifest]);
React.useEffect(() => {
if (links.some(link => "imagesizes" in link || "imagesrcset" in link)) {
logDeprecationOnce(linksWarning);
let matches = getActiveMatches(routerMatches, errors, isSpaMode);
let keyedLinks = React.useMemo(() => getKeyedLinksForMatches(matches, routeModules, manifest), [matches, routeModules, manifest]);
return /*#__PURE__*/React.createElement(React.Fragment, null, criticalCss ? /*#__PURE__*/React.createElement("style", {
dangerouslySetInnerHTML: {
__html: criticalCss
}
}, [links]);
return /*#__PURE__*/React.createElement(React.Fragment, null, links.map(link => {
if (isPageLinkDescriptor(link)) {
return /*#__PURE__*/React.createElement(PrefetchPageLinks, _extends({
key: link.page
}, link));
}
let imageSrcSet = null;
// In React 17, <link imageSrcSet> and <link imageSizes> will warn
// because the DOM attributes aren't recognized, so users need to pass
// them in all lowercase to forward the attributes to the node without a
// warning. Normalize so that either property can be used in Remix.
if ("useId" in React) {
if (link.imagesrcset) {
link.imageSrcSet = imageSrcSet = link.imagesrcset;
delete link.imagesrcset;
}
if (link.imagesizes) {
link.imageSizes = link.imagesizes;
delete link.imagesizes;
}
} else {
if (link.imageSrcSet) {
link.imagesrcset = imageSrcSet = link.imageSrcSet;
delete link.imageSrcSet;
}
if (link.imageSizes) {
link.imagesizes = link.imageSizes;
delete link.imageSizes;
}
}
return /*#__PURE__*/React.createElement("link", _extends({
key: link.rel + (link.href || "") + (imageSrcSet || "")
}, link));
}));
}) : null, keyedLinks.map(({
key,
link
}) => isPageLinkDescriptor(link) ? /*#__PURE__*/React.createElement(PrefetchPageLinks, _extends({
key: key
}, link)) : /*#__PURE__*/React.createElement("link", _extends({
key: key
}, link))));
}
/**
* This component renders all of the `<link rel="prefetch">` and
* This component renders all the `<link rel="prefetch">` and
* `<link rel="modulepreload"/>` tags for all the assets (data, modules, css) of

@@ -337,3 +274,3 @@ * a given page.

} = useDataRouterContext();
let matches = React.useMemo(() => matchRoutes(router.routes, page), [router.routes, page]);
let matches = React.useMemo(() => matchRoutes(router.routes, page, router.basename), [router.routes, page, router.basename]);
if (!matches) {

@@ -348,3 +285,3 @@ console.warn(`Tried to prefetch ${page} but no routes matched.`);

}
function usePrefetchedStylesheets(matches) {
function useKeyedPrefetchLinks(matches) {
let {

@@ -354,7 +291,9 @@ manifest,

} = useRemixContext();
let [styleLinks, setStyleLinks] = React.useState([]);
let [keyedPrefetchLinks, setKeyedPrefetchLinks] = React.useState([]);
React.useEffect(() => {
let interrupted = false;
getStylesheetPrefetchLinks(matches, manifest, routeModules).then(links => {
if (!interrupted) setStyleLinks(links);
void getKeyedPrefetchLinks(matches, manifest, routeModules).then(links => {
if (!interrupted) {
setKeyedPrefetchLinks(links);
}
});

@@ -365,3 +304,3 @@ return () => {

}, [matches, manifest, routeModules]);
return styleLinks;
return keyedPrefetchLinks;
}

@@ -375,3 +314,5 @@ function PrefetchPageLinksImpl({

let {
manifest
future,
manifest,
routeModules
} = useRemixContext();

@@ -388,13 +329,32 @@ let {

// just the manifest like the other links in here.
let styleLinks = usePrefetchedStylesheets(newMatchesForAssets);
return /*#__PURE__*/React.createElement(React.Fragment, null, dataHrefs.map(href => /*#__PURE__*/React.createElement("link", _extends({
let keyedPrefetchLinks = useKeyedPrefetchLinks(newMatchesForAssets);
let linksToRender = null;
if (!future.unstable_singleFetch) {
// Non-single-fetch prefetching
linksToRender = dataHrefs.map(href => /*#__PURE__*/React.createElement("link", _extends({
key: href,
rel: "prefetch",
as: "fetch",
href: href
}, linkProps)));
} else if (newMatchesForData.length > 0) {
// Single-fetch with routes that require data
let url = addRevalidationParam(manifest, routeModules, nextMatches.map(m => m.route), newMatchesForData.map(m => m.route), singleFetchUrl(page));
if (url.searchParams.get("_routes") !== "") {
linksToRender = /*#__PURE__*/React.createElement("link", _extends({
key: url.pathname + url.search,
rel: "prefetch",
as: "fetch",
href: url.pathname + url.search
}, linkProps));
}
} else ;
return /*#__PURE__*/React.createElement(React.Fragment, null, linksToRender, moduleHrefs.map(href => /*#__PURE__*/React.createElement("link", _extends({
key: href,
rel: "prefetch",
as: "fetch",
href: href
}, linkProps))), moduleHrefs.map(href => /*#__PURE__*/React.createElement("link", _extends({
key: href,
rel: "modulepreload",
href: href
}, linkProps))), styleLinks.map(link =>
}, linkProps))), keyedPrefetchLinks.map(({
key,
link
}) =>
/*#__PURE__*/

@@ -404,3 +364,3 @@ // these don't spread `linkProps` because they are full link descriptors

React.createElement("link", _extends({
key: link.href
key: key
}, link))));

@@ -410,8 +370,9 @@ }

/**
* Renders the `<title>` and `<meta>` tags for the current routes.
* Renders HTML tags related to metadata for the current route.
*
* @see https://remix.run/components/meta
*/
function V1Meta() {
function Meta() {
let {
isSpaMode,
routeModules

@@ -425,92 +386,7 @@ } = useRemixContext();

let location = useLocation();
let matches = errors ? routerMatches.slice(0, routerMatches.findIndex(m => errors[m.route.id]) + 1) : routerMatches;
let meta = {};
let parentsData = {};
for (let match of matches) {
let routeId = match.route.id;
let data = loaderData[routeId];
let params = match.params;
let routeModule = routeModules[routeId];
if (routeModule.meta) {
let routeMeta = typeof routeModule.meta === "function" ? routeModule.meta({
data,
parentsData,
params,
location
}) : routeModule.meta;
if (routeMeta && Array.isArray(routeMeta)) {
throw new Error("The route at " + match.route.path + " returns an array. This is only supported with the `v2_meta` future flag " + "in the Remix config. Either set the flag to `true` or update the route's " + "meta function to return an object." + "\n\nTo reference the v1 meta function API, see https://remix.run/route/meta"
// TODO: Add link to the docs once they are written
// + "\n\nTo reference future flags and the v2 meta API, see https://remix.run/file-conventions/remix-config#future-v2-meta."
);
}
Object.assign(meta, routeMeta);
}
parentsData[routeId] = data;
let _matches = getActiveMatches(routerMatches, errors, isSpaMode);
let error = null;
if (errors) {
error = errors[_matches[_matches.length - 1].route.id];
}
return /*#__PURE__*/React.createElement(React.Fragment, null, Object.entries(meta).map(([name, value]) => {
if (!value) {
return null;
}
if (["charset", "charSet"].includes(name)) {
return /*#__PURE__*/React.createElement("meta", {
key: "charSet",
charSet: value
});
}
if (name === "title") {
return /*#__PURE__*/React.createElement("title", {
key: "title"
}, String(value));
}
// Open Graph tags use the `property` attribute, while other meta tags
// use `name`. See https://ogp.me/
//
// Namespaced attributes:
// - https://ogp.me/#type_music
// - https://ogp.me/#type_video
// - https://ogp.me/#type_article
// - https://ogp.me/#type_book
// - https://ogp.me/#type_profile
//
// Facebook specific tags begin with `fb:` and also use the `property`
// attribute.
//
// Twitter specific tags begin with `twitter:` but they use `name`, so
// they are excluded.
let isOpenGraphTag = /^(og|music|video|article|book|profile|fb):.+$/.test(name);
return [value].flat().map(content => {
if (isOpenGraphTag) {
return /*#__PURE__*/React.createElement("meta", {
property: name,
content: content,
key: name + content
});
}
if (typeof content === "string") {
return /*#__PURE__*/React.createElement("meta", {
name: name,
content: content,
key: name + content
});
}
return /*#__PURE__*/React.createElement("meta", _extends({
key: name + JSON.stringify(content)
}, content));
});
}));
}
function V2Meta() {
let {
routeModules
} = useRemixContext();
let {
errors,
matches: routerMatches,
loaderData
} = useDataRouterStateContext();
let location = useLocation();
let _matches = errors ? routerMatches.slice(0, routerMatches.findIndex(m => errors[m.route.id]) + 1) : routerMatches;
let meta = [];

@@ -533,10 +409,3 @@ let leafMeta = null;

handle: _match.route.handle,
// TODO: Remove in v2. Only leaving it for now because we used it in
// examples and there's no reason to crash someone's build for one line.
// They'll get a TS error from the type updates anyway.
// @ts-expect-error
get route() {
console.warn("The meta function in " + _match.route.path + " accesses the `route` property on `matches`. This is deprecated and will be removed in Remix version 2. See");
return _match.route;
}
error
};

@@ -549,7 +418,8 @@ matches[i] = match;

location,
matches
matches,
error
}) : Array.isArray(routeModule.meta) ? [...routeModule.meta] : routeModule.meta;
} else if (leafMeta) {
// We only assign the route's meta to the nearest leaf if there is no meta
// export in the route. The meta function may return a falsey value which
// export in the route. The meta function may return a falsy value which
// is effectively the same as an empty array.

@@ -560,6 +430,3 @@ routeMeta = [...leafMeta];

if (!Array.isArray(routeMeta)) {
throw new Error("The `v2_meta` API is enabled in the Remix config, but the route at " + _match.route.path + " returns an invalid value. In v2, all route meta functions must " + "return an array of meta objects." +
// TODO: Add link to the docs once they are written
// "\n\nTo reference future flags and the v2 meta API, see https://remix.run/file-conventions/remix-config#future-v2-meta." +
"\n\nTo reference the v1 meta function API, see https://remix.run/route/meta");
throw new Error("The route at " + _match.route.path + " returns an invalid value. All route meta functions must " + "return an array of meta objects." + "\n\nTo reference the meta function API, see https://remix.run/route/meta");
}

@@ -576,4 +443,6 @@ match.meta = routeMeta;

if ("tagName" in metaProps) {
let tagName = metaProps.tagName;
delete metaProps.tagName;
let {
tagName,
...rest
} = metaProps;
if (!isValidMetaTag(tagName)) {

@@ -585,4 +454,4 @@ console.warn(`A meta object uses an invalid tagName: ${tagName}. Expected either 'link' or 'meta'`);

return /*#__PURE__*/React.createElement(Comp, _extends({
key: JSON.stringify(metaProps)
}, metaProps));
key: JSON.stringify(rest)
}, rest));
}

@@ -595,3 +464,3 @@ if ("title" in metaProps) {

if ("charset" in metaProps) {
metaProps.charSet ?? (metaProps.charSet = metaProps.charset);
metaProps.charSet ??= metaProps.charset;
delete metaProps.charset;

@@ -606,13 +475,14 @@ }

if ("script:ld+json" in metaProps) {
let json = null;
try {
json = JSON.stringify(metaProps["script:ld+json"]);
} catch (err) {}
return json != null && /*#__PURE__*/React.createElement("script", {
key: "script:ld+json",
type: "application/ld+json",
dangerouslySetInnerHTML: {
__html: JSON.stringify(metaProps["script:ld+json"])
}
});
let json = JSON.stringify(metaProps["script:ld+json"]);
return /*#__PURE__*/React.createElement("script", {
key: `script:ld+json:${json}`,
type: "application/ld+json",
dangerouslySetInnerHTML: {
__html: json
}
});
} catch (err) {
return null;
}
}

@@ -627,8 +497,2 @@ return /*#__PURE__*/React.createElement("meta", _extends({

}
function Meta() {
let {
future
} = useRemixContext();
return future !== null && future !== void 0 && future.v2_meta ? /*#__PURE__*/React.createElement(V2Meta, null) : /*#__PURE__*/React.createElement(V1Meta, null);
}
function Await(props) {

@@ -657,3 +521,7 @@ return /*#__PURE__*/React.createElement(Await$1, props);

serverHandoffString,
abortDelay
abortDelay,
serializeError,
isSpaMode,
future,
renderMeta
} = useRemixContext();

@@ -666,13 +534,62 @@ let {

let {
matches
matches: routerMatches
} = useDataRouterStateContext();
let navigation = useNavigation();
let enableFogOfWar = isFogOfWarEnabled(future, isSpaMode);
// Let <RemixServer> know that we hydrated and we should render the single
// fetch streaming scripts
if (renderMeta) {
renderMeta.didRenderScripts = true;
}
let matches = getActiveMatches(routerMatches, null, isSpaMode);
React.useEffect(() => {
isHydrated = true;
}, []);
let serializePreResolvedErrorImp = (key, error) => {
let toSerialize;
if (serializeError && error instanceof Error) {
toSerialize = serializeError(error);
} else {
toSerialize = error;
}
return `${JSON.stringify(key)}:__remixContext.p(!1, ${escapeHtml(JSON.stringify(toSerialize))})`;
};
let serializePreresolvedDataImp = (routeId, key, data) => {
let serializedData;
try {
serializedData = JSON.stringify(data);
} catch (error) {
return serializePreResolvedErrorImp(key, error);
}
return `${JSON.stringify(key)}:__remixContext.p(${escapeHtml(serializedData)})`;
};
let serializeErrorImp = (routeId, key, error) => {
let toSerialize;
if (serializeError && error instanceof Error) {
toSerialize = serializeError(error);
} else {
toSerialize = error;
}
return `__remixContext.r(${JSON.stringify(routeId)}, ${JSON.stringify(key)}, !1, ${escapeHtml(JSON.stringify(toSerialize))})`;
};
let serializeDataImp = (routeId, key, data) => {
let serializedData;
try {
serializedData = JSON.stringify(data);
} catch (error) {
return serializeErrorImp(routeId, key, error);
}
return `__remixContext.r(${JSON.stringify(routeId)}, ${JSON.stringify(key)}, ${escapeHtml(serializedData)})`;
};
let deferredScripts = [];
let initialScripts = React.useMemo(() => {
var _manifest$hmr;
let contextScript = staticContext ? `window.__remixContext = ${serverHandoffString};` : " ";
let activeDeferreds = staticContext === null || staticContext === void 0 ? void 0 : staticContext.activeDeferreds;
let streamScript = future.unstable_singleFetch ?
// prettier-ignore
"window.__remixContext.stream = new ReadableStream({" + "start(controller){" + "window.__remixContext.streamController = controller;" + "}" + "}).pipeThrough(new TextEncoderStream());" : "";
let contextScript = staticContext ? `window.__remixContext = ${serverHandoffString};${streamScript}` : " ";
// When single fetch is enabled, deferred is handled by turbo-stream
let activeDeferreds = future.unstable_singleFetch ? undefined : staticContext === null || staticContext === void 0 ? void 0 : staticContext.activeDeferreds;
// This sets up the __remixContext with utility functions used by the

@@ -686,3 +603,3 @@ // deferred scripts.

// the promise created by __remixContext.n.
// - __remixContext.t is a a map or routeId to keys to an object containing `e` and `r` methods
// - __remixContext.t is a map or routeId to keys to an object containing `e` and `r` methods
// to resolve or reject the promise created by __remixContext.n.

@@ -700,3 +617,5 @@ // - __remixContext.a is the active number of deferred scripts that should be rendered to match

dataKey: key,
scriptProps: props
scriptProps: props,
serializeData: serializeDataImp,
serializeError: serializeErrorImp
}));

@@ -707,15 +626,5 @@ return `${JSON.stringify(key)}:__remixContext.n(${JSON.stringify(routeId)}, ${JSON.stringify(key)})`;

if (typeof trackedPromise._error !== "undefined") {
let toSerialize = process.env.NODE_ENV === "development" ? {
message: trackedPromise._error.message,
stack: trackedPromise._error.stack
} : {
message: "Unexpected Server Error",
stack: undefined
};
return `${JSON.stringify(key)}:__remixContext.p(!1, ${escapeHtml(JSON.stringify(toSerialize))})`;
return serializePreResolvedErrorImp(key, trackedPromise._error);
} else {
if (typeof trackedPromise._data === "undefined") {
throw new Error(`The deferred data for ${key} was not resolved, did you forget to return data from a deferred promise?`);
}
return `${JSON.stringify(key)}:__remixContext.p(${escapeHtml(JSON.stringify(trackedPromise._data))})`;
return serializePreresolvedDataImp(routeId, key, trackedPromise._data);
}

@@ -726,4 +635,7 @@ }

}).join("\n") + (deferredScripts.length > 0 ? `__remixContext.a=${deferredScripts.length};` : "");
let routeModulesScript = !isStatic ? " " : `${(_manifest$hmr = manifest.hmr) !== null && _manifest$hmr !== void 0 && _manifest$hmr.runtime ? `import ${JSON.stringify(manifest.hmr.runtime)};` : ""}import ${JSON.stringify(manifest.url)};
let routeModulesScript = !isStatic ? " " : `${(_manifest$hmr = manifest.hmr) !== null && _manifest$hmr !== void 0 && _manifest$hmr.runtime ? `import ${JSON.stringify(manifest.hmr.runtime)};` : ""}${enableFogOfWar ? "" : `import ${JSON.stringify(manifest.url)}`};
${matches.map((match, index) => `import * as route${index} from ${JSON.stringify(manifest.routes[match.route.id].module)};`).join("\n")}
${enableFogOfWar ?
// Inline a minimal manifest with the SSR matches
`window.__remixManifest = ${JSON.stringify(getPartialManifest(manifest, router), null, 2)};` : ""}
window.__remixRouteModules = {${matches.map((match, index) => `${JSON.stringify(match.route.id)}:route${index}`).join(",")}};

@@ -751,18 +663,9 @@

key: i,
scriptProps: props
scriptProps: props,
serializeData: serializeDataImp,
serializeError: serializeErrorImp
}));
}
}
// avoid waterfall when importing the next route module
let nextMatches = React.useMemo(() => {
if (navigation.location) {
// FIXME: can probably use transitionManager `nextMatches`
let matches = matchRoutes(router.routes, navigation.location);
invariant(matches, `No routes match path "${navigation.location.pathname}"`);
return matches;
}
return [];
}, [navigation.location, router.routes]);
let routePreloads = matches.concat(nextMatches).map(match => {
let routePreloads = matches.map(match => {
let route = manifest.routes[match.route.id];

@@ -772,4 +675,8 @@ return (route.imports || []).concat([route.module]);

let preloads = isHydrated ? [] : manifest.entry.imports.concat(routePreloads);
return isHydrated ? null : /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("link", {
return isHydrated ? null : /*#__PURE__*/React.createElement(React.Fragment, null, !enableFogOfWar ? /*#__PURE__*/React.createElement("link", {
rel: "modulepreload",
href: manifest.url,
crossOrigin: props.crossOrigin
}) : null, /*#__PURE__*/React.createElement("link", {
rel: "modulepreload",
href: manifest.entry.module,

@@ -788,3 +695,5 @@ crossOrigin: props.crossOrigin

routeId,
scriptProps
scriptProps,
serializeData,
serializeError
}) {

@@ -811,11 +720,14 @@ if (typeof document === "undefined" && deferredData && dataKey && routeId) {

routeId: routeId,
scriptProps: scriptProps
scriptProps: scriptProps,
serializeError: serializeError
}),
children: data => /*#__PURE__*/React.createElement("script", _extends({}, scriptProps, {
async: true,
suppressHydrationWarning: true,
dangerouslySetInnerHTML: {
__html: `__remixContext.r(${JSON.stringify(routeId)}, ${JSON.stringify(dataKey)}, ${escapeHtml(JSON.stringify(data))});`
}
}))
children: data => {
return /*#__PURE__*/React.createElement("script", _extends({}, scriptProps, {
async: true,
suppressHydrationWarning: true,
dangerouslySetInnerHTML: {
__html: serializeData(routeId, dataKey, data)
}
}));
}
}) : /*#__PURE__*/React.createElement("script", _extends({}, scriptProps, {

@@ -832,16 +744,10 @@ async: true,

routeId,
scriptProps
scriptProps,
serializeError
}) {
let error = useAsyncError();
let toSerialize = process.env.NODE_ENV === "development" ? {
message: error.message,
stack: error.stack
} : {
message: "Unexpected Server Error",
stack: undefined
};
return /*#__PURE__*/React.createElement("script", _extends({}, scriptProps, {
suppressHydrationWarning: true,
dangerouslySetInnerHTML: {
__html: `__remixContext.r(${JSON.stringify(routeId)}, ${JSON.stringify(dataKey)}, !1, ${escapeHtml(JSON.stringify(toSerialize))});`
__html: serializeError(routeId, dataKey, error)
}

@@ -853,22 +759,10 @@ }));

}
// TODO: Can this be re-exported from RR?
/**
* Returns the active route matches, useful for accessing loaderData for
* parent/child routes or the route "handle" property
*
* @see https://remix.run/hooks/use-matches
*/
function useMatches() {
let {
routeModules
} = useRemixContext();
let matches = useMatches$1();
return React.useMemo(() => matches.map(match => {
let remixMatch = {
id: match.id,
pathname: match.pathname,
params: match.params,
data: match.data,
// Need to grab handle here since we don't have it at client-side route
// creation time
handle: routeModules[match.id].handle
};
return remixMatch;
}), [matches, routeModules]);
return useMatches$1();
}

@@ -886,195 +780,20 @@

/**
* Returns the JSON parsed data from the current route's `action`.
* Returns the loaderData for the given routeId.
*
* @see https://remix.run/hooks/use-action-data
* @see https://remix.run/hooks/use-route-loader-data
*/
function useActionData() {
return useActionData$1();
function useRouteLoaderData(routeId) {
return useRouteLoaderData$1(routeId);
}
/**
* Returns everything you need to know about a page transition to build pending
* navigation indicators and optimistic UI on data mutations.
* Returns the JSON parsed data from the current route's `action`.
*
* @deprecated in favor of useNavigation
*
* @see https://remix.run/hooks/use-transition
* @see https://remix.run/hooks/use-action-data
*/
function useTransition() {
let navigation = useNavigation();
React.useEffect(() => {
logDeprecationOnce(useTransitionWarning);
}, []);
return React.useMemo(() => convertNavigationToTransition(navigation), [navigation]);
function useActionData() {
return useActionData$1();
}
function convertNavigationToTransition(navigation) {
let {
location,
state,
formMethod,
formAction,
formEncType,
formData
} = navigation;
if (!location) {
return IDLE_TRANSITION;
}
let isActionSubmission = formMethod != null && ["POST", "PUT", "PATCH", "DELETE"].includes(formMethod.toUpperCase());
if (state === "submitting" && formMethod && formAction && formEncType && formData) {
if (isActionSubmission) {
// Actively submitting to an action
let transition = {
location,
state,
submission: {
method: formMethod.toUpperCase(),
action: formAction,
encType: formEncType,
formData: formData,
key: ""
},
type: "actionSubmission"
};
return transition;
} else {
// @remix-run/router doesn't mark loader submissions as state: "submitting"
invariant(false, "Encountered an unexpected navigation scenario in useTransition()");
}
}
if (state === "loading") {
let {
_isRedirect,
_isFetchActionRedirect
} = location.state || {};
if (formMethod && formAction && formEncType && formData) {
if (!_isRedirect) {
if (isActionSubmission) {
// We're reloading the same location after an action submission
let transition = {
location,
state,
submission: {
method: formMethod.toUpperCase(),
action: formAction,
encType: formEncType,
formData: formData,
key: ""
},
type: "actionReload"
};
return transition;
} else {
// The new router fixes a bug in useTransition where the submission
// "action" represents the request URL not the state of the <form> in
// the DOM. Back-port it here to maintain behavior, but useNavigation
// will fix this bug.
let url = new URL(formAction, window.location.origin);
// This typing override should be safe since this is only running for
// GET submissions and over in @remix-run/router we have an invariant
// if you have any non-string values in your FormData when we attempt
// to convert them to URLSearchParams
url.search = new URLSearchParams(formData.entries()).toString();
// Actively "submitting" to a loader
let transition = {
location,
state: "submitting",
submission: {
method: formMethod.toUpperCase(),
action: url.pathname + url.search,
encType: formEncType,
formData: formData,
key: ""
},
type: "loaderSubmission"
};
return transition;
}
} else {
// Redirecting after a submission
if (isActionSubmission) {
let transition = {
location,
state,
submission: {
method: formMethod.toUpperCase(),
action: formAction,
encType: formEncType,
formData: formData,
key: ""
},
type: "actionRedirect"
};
return transition;
} else {
let transition = {
location,
state,
submission: {
method: formMethod.toUpperCase(),
action: formAction,
encType: formEncType,
formData: formData,
key: ""
},
type: "loaderSubmissionRedirect"
};
return transition;
}
}
} else if (_isRedirect) {
if (_isFetchActionRedirect) {
let transition = {
location,
state,
submission: undefined,
type: "fetchActionRedirect"
};
return transition;
} else {
let transition = {
location,
state,
submission: undefined,
type: "normalRedirect"
};
return transition;
}
}
}
// If no scenarios above match, then it's a normal load!
let transition = {
location,
state: "loading",
submission: undefined,
type: "normalLoad"
};
return transition;
}
/**
* Provides all fetchers currently on the page. Useful for layouts and parent
* routes that need to provide pending/optimistic UI regarding the fetch.
*
* @see https://remix.run/api/remix#usefetchers
*/
function useFetchers() {
let fetchers = useFetchers$1();
return fetchers.map(f => {
let fetcher = convertRouterFetcherToRemixFetcher({
state: f.state,
data: f.data,
formMethod: f.formMethod,
formAction: f.formAction,
formData: f.formData,
formEncType: f.formEncType,
" _hasFetcherDoneAnything ": f[" _hasFetcherDoneAnything "]
});
addFetcherDeprecationWarnings(fetcher);
return fetcher;
});
}
/**
* Interacts with route loaders and actions without causing a navigation. Great

@@ -1085,205 +804,18 @@ * for any interaction that stays on the same page.

*/
function useFetcher() {
let fetcherRR = useFetcher$1();
return React.useMemo(() => {
let remixFetcher = convertRouterFetcherToRemixFetcher({
state: fetcherRR.state,
data: fetcherRR.data,
formMethod: fetcherRR.formMethod,
formAction: fetcherRR.formAction,
formData: fetcherRR.formData,
formEncType: fetcherRR.formEncType,
" _hasFetcherDoneAnything ": fetcherRR[" _hasFetcherDoneAnything "]
});
let fetcherWithComponents = {
...remixFetcher,
load: fetcherRR.load,
submit: fetcherRR.submit,
Form: fetcherRR.Form
};
addFetcherDeprecationWarnings(fetcherWithComponents);
return fetcherWithComponents;
}, [fetcherRR]);
function useFetcher(opts = {}) {
return useFetcher$1(opts);
}
function addFetcherDeprecationWarnings(fetcher) {
let type = fetcher.type;
Object.defineProperty(fetcher, "type", {
get() {
logDeprecationOnce(fetcherTypeWarning);
return type;
},
set(value) {
// Devs should *not* be doing this but we don't want to break their
// current app if they are
type = value;
},
// These settings should make this behave like a normal object `type` field
configurable: true,
enumerable: true
});
let submission = fetcher.submission;
Object.defineProperty(fetcher, "submission", {
get() {
logDeprecationOnce(fetcherSubmissionWarning);
return submission;
},
set(value) {
// Devs should *not* be doing this but we don't want to break their
// current app if they are
submission = value;
},
// These settings should make this behave like a normal object `type` field
configurable: true,
enumerable: true
});
}
function convertRouterFetcherToRemixFetcher(fetcherRR) {
let {
state,
formMethod,
formAction,
formEncType,
formData,
data
} = fetcherRR;
let isActionSubmission = formMethod != null && ["POST", "PUT", "PATCH", "DELETE"].includes(formMethod.toUpperCase());
if (state === "idle") {
if (fetcherRR[" _hasFetcherDoneAnything "] === true) {
let fetcher = {
state: "idle",
type: "done",
formMethod: undefined,
formAction: undefined,
formData: undefined,
formEncType: undefined,
submission: undefined,
data
};
return fetcher;
} else {
let fetcher = IDLE_FETCHER;
return fetcher;
}
}
if (state === "submitting" && formMethod && formAction && formEncType && formData) {
if (isActionSubmission) {
// Actively submitting to an action
let fetcher = {
state,
type: "actionSubmission",
formMethod: formMethod.toUpperCase(),
formAction: formAction,
formEncType: formEncType,
formData: formData,
submission: {
method: formMethod.toUpperCase(),
action: formAction,
encType: formEncType,
formData: formData,
key: ""
},
data
};
return fetcher;
} else {
// @remix-run/router doesn't mark loader submissions as state: "submitting"
invariant(false, "Encountered an unexpected fetcher scenario in useFetcher()");
}
}
if (state === "loading") {
if (formMethod && formAction && formEncType && formData) {
if (isActionSubmission) {
if (data) {
// In a loading state but we have data - must be an actionReload
let fetcher = {
state,
type: "actionReload",
formMethod: formMethod.toUpperCase(),
formAction: formAction,
formEncType: formEncType,
formData: formData,
submission: {
method: formMethod.toUpperCase(),
action: formAction,
encType: formEncType,
formData: formData,
key: ""
},
data
};
return fetcher;
} else {
let fetcher = {
state,
type: "actionRedirect",
formMethod: formMethod.toUpperCase(),
formAction: formAction,
formEncType: formEncType,
formData: formData,
submission: {
method: formMethod.toUpperCase(),
action: formAction,
encType: formEncType,
formData: formData,
key: ""
},
data: undefined
};
return fetcher;
}
} else {
// The new router fixes a bug in useTransition where the submission
// "action" represents the request URL not the state of the <form> in
// the DOM. Back-port it here to maintain behavior, but useNavigation
// will fix this bug.
let url = new URL(formAction, window.location.origin);
// This typing override should be safe since this is only running for
// GET submissions and over in @remix-run/router we have an invariant
// if you have any non-string values in your FormData when we attempt
// to convert them to URLSearchParams
url.search = new URLSearchParams(formData.entries()).toString();
// Actively "submitting" to a loader
let fetcher = {
state: "submitting",
type: "loaderSubmission",
formMethod: formMethod.toUpperCase(),
formAction: formAction,
formEncType: formEncType,
formData: formData,
submission: {
method: formMethod.toUpperCase(),
action: url.pathname + url.search,
encType: formEncType,
formData: formData,
key: ""
},
data
};
return fetcher;
}
}
}
// If all else fails, it's a normal load!
let fetcher = {
state: "loading",
type: "normalLoad",
formMethod: undefined,
formAction: undefined,
formData: undefined,
formEncType: undefined,
submission: undefined,
data
};
return fetcher;
}
/**
* This component connects your app to the Remix asset server and
* automatically reloads the page when files change in development.
* In production, it renders null, so you can safely render it always in your root route.
*
* @see https://remix.run/docs/components/live-reload
*/
const LiveReload =
// Dead Code Elimination magic for production builds.
// This way devs don't have to worry about doing the NODE_ENV check themselves.
// If running an un-bundled server outside of `remix dev` you will still need
// to set the REMIX_DEV_SERVER_WS_PORT manually.
const LiveReload = process.env.NODE_ENV !== "development" ? () => null : function LiveReload({
// TODO: remove REMIX_DEV_SERVER_WS_PORT in v2
process.env.NODE_ENV !== "development" ? () => null : function LiveReload({
origin,
port,

@@ -1293,2 +825,9 @@ timeoutMs = 1000,

}) {
// @ts-expect-error
let isViteClient = import.meta && import.meta.env !== undefined;
if (isViteClient) {
console.warn(["`<LiveReload />` is obsolete when using Vite and can conflict with Vite's built-in HMR runtime.", "", "Remove `<LiveReload />` from your code and instead only use `<Scripts />`.", "Then refresh the page to remove lingering scripts from `<LiveReload />`."].join("\n"));
return null;
}
origin ??= process.env.REMIX_DEV_ORIGIN;
let js = String.raw;

@@ -1301,7 +840,14 @@ return /*#__PURE__*/React.createElement("script", {

function remixLiveReloadConnect(config) {
let protocol = location.protocol === "https:" ? "wss:" : "ws:";
let host = location.hostname;
let port = ${port} || (window.__remixContext && window.__remixContext.dev && window.__remixContext.dev.port) || ${Number(process.env.REMIX_DEV_SERVER_WS_PORT || 8002)};
let socketPath = protocol + "//" + host + ":" + port + "/socket";
let ws = new WebSocket(socketPath);
let LIVE_RELOAD_ORIGIN = ${JSON.stringify(origin)};
let protocol =
LIVE_RELOAD_ORIGIN ? new URL(LIVE_RELOAD_ORIGIN).protocol.replace(/^http/, "ws") :
location.protocol === "https:" ? "wss:" : "ws:"; // remove in v2?
let hostname = LIVE_RELOAD_ORIGIN ? new URL(LIVE_RELOAD_ORIGIN).hostname : location.hostname;
let url = new URL(protocol + "//" + hostname + "/socket");
url.port =
${port} ||
(LIVE_RELOAD_ORIGIN ? new URL(LIVE_RELOAD_ORIGIN).port : 8002);
let ws = new WebSocket(url.href);
ws.onmessage = async (message) => {

@@ -1337,3 +883,3 @@ let event = JSON.parse(message.data);

if (accepted) {
console.log("[HMR] Updated accepted by", update.id);
console.log("[HMR] Update accepted by", update.id);
updateAccepted = true;

@@ -1348,3 +894,3 @@ }

if (accepted) {
console.log("[HMR] Updated accepted by", "remix:manifest");
console.log("[HMR] Update accepted by", "remix:manifest");
updateAccepted = true;

@@ -1354,3 +900,3 @@ }

if (!updateAccepted) {
console.log("[HMR] Updated rejected, reloading...");
console.log("[HMR] Update rejected, reloading...");
window.location.reload();

@@ -1399,2 +945,2 @@ }

export { Await, Link, Links, LiveReload, Meta, NavLink, PrefetchPageLinks, RemixContext, RemixRoute, RemixRouteError, Scripts, composeEventHandlers, useActionData, useFetcher, useFetchers, useLoaderData, useMatches, useTransition };
export { Await, Form, Link, Links, LiveReload, Meta, NavLink, PrefetchPageLinks, RemixContext, Scripts, composeEventHandlers, useActionData, useFetcher, useLoaderData, useMatches, useRemixContext, useRouteLoaderData };
/**
* @remix-run/react v0.0.0-nightly-ccefed3-20230621
* @remix-run/react v0.0.0-nightly-cd403b516-20240809
*

@@ -15,4 +15,2 @@ * Copyright (c) Remix Software Inc.

* Data for a route that was returned from a `loader()`.
*
* Note: This moves to unknown in ReactRouter and eventually likely in Remix
*/

@@ -26,2 +24,14 @@

}
function isNetworkErrorResponse(response) {
// If we reach the Remix server, we can safely identify response types via the
// X-Remix-Error/X-Remix-Catch headers. However, if we never reach the Remix
// server, and instead receive a 4xx/5xx from somewhere in between (like
// Cloudflare), then we get a false negative in the isErrorResponse check and
// we incorrectly assume that the user returns the 4xx/5xx response and
// consider it successful. To alleviate this, we add X-Remix-Response to any
// non-Error/non-Catch responses coming back from the server. If we don't
// see this, we can conclude that a 4xx/5xx response never actually reached
// the Remix server and we can bubble it up as an error.
return isResponse(response) && response.status >= 400 && response.headers.get("X-Remix-Error") == null && response.headers.get("X-Remix-Catch") == null && response.headers.get("X-Remix-Response") == null;
}
function isRedirectResponse(response) {

@@ -34,16 +44,12 @@ return response.headers.get("X-Remix-Redirect") != null;

}
function isResponse(value) {
return value != null && typeof value.status === "number" && typeof value.statusText === "string" && typeof value.headers === "object" && typeof value.body !== "undefined";
}
function isDeferredData(value) {
let deferred = value;
return deferred && typeof deferred === "object" && typeof deferred.data === "object" && typeof deferred.subscribe === "function" && typeof deferred.cancel === "function" && typeof deferred.resolveData === "function";
}
async function fetchData(request, routeId, retry = 0) {
let url = new URL(request.url);
url.searchParams.set("_data", routeId);
let init = {
signal: request.signal
};
if (request.method !== "GET") {
init.method = request.method;
let contentType = request.headers.get("Content-Type");
init.body =
// Check between word boundaries instead of startsWith() due to the last
// paragraph of https://httpwg.org/specs/rfc9110.html#field.content-type
contentType && /\bapplication\/x-www-form-urlencoded\b/.test(contentType) ? new URLSearchParams(await request.text()) : await request.formData();
}
if (retry > 0) {

@@ -54,2 +60,3 @@ // Retry up to 3 times waiting 50, 250, 1250 ms

}
let init = await createRequestInit(request);
let revalidation = window.__remixRevalidation;

@@ -68,4 +75,38 @@ let response = await fetch(url.href, init).catch(error => {

}
if (isNetworkErrorResponse(response)) {
let text = await response.text();
let error = new Error(text);
error.stack = undefined;
return error;
}
return response;
}
async function createRequestInit(request) {
let init = {
signal: request.signal
};
if (request.method !== "GET") {
init.method = request.method;
let contentType = request.headers.get("Content-Type");
// Check between word boundaries instead of startsWith() due to the last
// paragraph of https://httpwg.org/specs/rfc9110.html#field.content-type
if (contentType && /\bapplication\/json\b/.test(contentType)) {
init.headers = {
"Content-Type": contentType
};
init.body = JSON.stringify(await request.json());
} else if (contentType && /\btext\/plain\b/.test(contentType)) {
init.headers = {
"Content-Type": contentType
};
init.body = await request.text();
} else if (contentType && /\bapplication\/x-www-form-urlencoded\b/.test(contentType)) {
init.body = new URLSearchParams(await request.text());
} else {
init.body = await request.formData();
}
}
return init;
}
const DEFERRED_VALUE_PLACEHOLDER_PREFIX = "__deferred_promise:";

@@ -110,3 +151,3 @@ async function parseDeferredReadableStream(stream) {

// Read the rest of the stream and resolve deferred promises
(async () => {
void (async () => {
try {

@@ -232,2 +273,2 @@ for await (let section of sectionReader) {

export { fetchData, isCatchResponse, isDeferredResponse, isErrorResponse, isRedirectResponse, parseDeferredReadableStream };
export { createRequestInit, fetchData, isCatchResponse, isDeferredData, isDeferredResponse, isErrorResponse, isNetworkErrorResponse, isRedirectResponse, isResponse, parseDeferredReadableStream };
/**
* @remix-run/react v0.0.0-nightly-ccefed3-20230621
* @remix-run/react v0.0.0-nightly-cd403b516-20240809
*

@@ -11,6 +11,7 @@ * Copyright (c) Remix Software Inc.

*/
import React__default, { useContext } from 'react';
import { useRouteError, isRouteErrorResponse } from 'react-router-dom';
import * as React from 'react';
import { isRouteErrorResponse } from 'react-router-dom';
import { useRemixContext, Scripts } from './components.js';
class RemixErrorBoundary extends React__default.Component {
class RemixErrorBoundary extends React.Component {
constructor(props) {

@@ -56,4 +57,5 @@ super(props);

if (this.state.error) {
return /*#__PURE__*/React__default.createElement(this.props.component, {
error: this.state.error
return /*#__PURE__*/React.createElement(RemixRootDefaultErrorBoundary, {
error: this.state.error,
isOutsideRemixApp: true
});

@@ -70,25 +72,39 @@ } else {

function RemixRootDefaultErrorBoundary({
error
error,
isOutsideRemixApp
}) {
// Only log client side to avoid double-logging on the server
React__default.useEffect(() => {
console.error(error);
}, [error]);
return /*#__PURE__*/React__default.createElement("html", {
lang: "en"
}, /*#__PURE__*/React__default.createElement("head", null, /*#__PURE__*/React__default.createElement("meta", {
charSet: "utf-8"
}), /*#__PURE__*/React__default.createElement("meta", {
name: "viewport",
content: "width=device-width, initial-scale=1, viewport-fit=cover"
}), /*#__PURE__*/React__default.createElement("title", null, "Application Error!")), /*#__PURE__*/React__default.createElement("body", null, /*#__PURE__*/React__default.createElement("main", {
style: {
fontFamily: "system-ui, sans-serif",
padding: "2rem"
console.error(error);
let heyDeveloper = /*#__PURE__*/React.createElement("script", {
dangerouslySetInnerHTML: {
__html: `
console.log(
"💿 Hey developer 👋. You can provide a way better UX than this when your app throws errors. Check out https://remix.run/guides/errors for more information."
);
`
}
}, /*#__PURE__*/React__default.createElement("h1", {
});
if (isRouteErrorResponse(error)) {
return /*#__PURE__*/React.createElement(BoundaryShell, {
title: "Unhandled Thrown Response!"
}, /*#__PURE__*/React.createElement("h1", {
style: {
fontSize: "24px"
}
}, error.status, " ", error.statusText), heyDeveloper);
}
let errorInstance;
if (error instanceof Error) {
errorInstance = error;
} else {
let errorString = error == null ? "Unknown Error" : typeof error === "object" && "toString" in error ? error.toString() : JSON.stringify(error);
errorInstance = new Error(errorString);
}
return /*#__PURE__*/React.createElement(BoundaryShell, {
title: "Application Error!",
isOutsideRemixApp: isOutsideRemixApp
}, /*#__PURE__*/React.createElement("h1", {
style: {
fontSize: "24px"
}
}, "Application Error"), error.stack ? /*#__PURE__*/React__default.createElement("pre", {
}, "Application Error"), /*#__PURE__*/React.createElement("pre", {
style: {

@@ -100,74 +116,42 @@ padding: "2rem",

}
}, error.stack) : null), /*#__PURE__*/React__default.createElement("script", {
dangerouslySetInnerHTML: {
__html: `
console.log(
"💿 Hey developer👋. You can provide a way better UX than this when your app throws errors. Check out https://remix.run/guides/errors for more information."
);
`
}
})));
}, errorInstance.stack), heyDeveloper);
}
function V2_RemixRootDefaultErrorBoundary() {
let error = useRouteError();
if (isRouteErrorResponse(error)) {
return /*#__PURE__*/React__default.createElement(RemixRootDefaultCatchBoundaryImpl, {
caught: error
});
} else if (error instanceof Error) {
return /*#__PURE__*/React__default.createElement(RemixRootDefaultErrorBoundary, {
error: error
});
} else {
let errorString = error == null ? "Unknown Error" : typeof error === "object" && "toString" in error ? error.toString() : JSON.stringify(error);
return /*#__PURE__*/React__default.createElement(RemixRootDefaultErrorBoundary, {
error: new Error(errorString)
});
}
}
let RemixCatchContext = /*#__PURE__*/React__default.createContext(undefined);
/**
* Returns the status code and thrown response data.
*
* @deprecated Please enable the v2_errorBoundary flag
*
* @see https://remix.run/route/catch-boundary
*/
function useCatch() {
return useContext(RemixCatchContext);
}
function RemixCatchBoundary({
catch: catchVal,
component: Component,
function BoundaryShell({
title,
renderScripts,
isOutsideRemixApp,
children
}) {
if (catchVal) {
return /*#__PURE__*/React__default.createElement(RemixCatchContext.Provider, {
value: catchVal
}, /*#__PURE__*/React__default.createElement(Component, null));
var _routeModules$root;
let {
routeModules
} = useRemixContext();
// Generally speaking, when the root route has a Layout we want to use that
// as the app shell instead of the default `BoundaryShell` wrapper markup below.
// This is true for `loader`/`action` errors, most render errors, and
// `HydrateFallback` scenarios.
// However, render errors thrown from the `Layout` present a bit of an issue
// because if the `Layout` itself throws during the `ErrorBoundary` pass and
// we bubble outside the `RouterProvider` to the wrapping `RemixErrorBoundary`,
// by returning only `children` here we'll be trying to append a `<div>` to
// the `document` and the DOM will throw, putting React into an error/hydration
// loop.
// Instead, if we're ever rendering from the outermost `RemixErrorBoundary`
// during hydration that wraps `RouterProvider`, then we can't trust the
// `Layout` and should fallback to the default app shell so we're always
// returning an `<html>` document.
if ((_routeModules$root = routeModules.root) !== null && _routeModules$root !== void 0 && _routeModules$root.Layout && !isOutsideRemixApp) {
return children;
}
return /*#__PURE__*/React__default.createElement(React__default.Fragment, null, children);
}
/**
* When app's don't provide a root level CatchBoundary, we default to this.
*/
function RemixRootDefaultCatchBoundary() {
let caught = useCatch();
return /*#__PURE__*/React__default.createElement(RemixRootDefaultCatchBoundaryImpl, {
caught: caught
});
}
function RemixRootDefaultCatchBoundaryImpl({
caught
}) {
return /*#__PURE__*/React__default.createElement("html", {
return /*#__PURE__*/React.createElement("html", {
lang: "en"
}, /*#__PURE__*/React__default.createElement("head", null, /*#__PURE__*/React__default.createElement("meta", {
}, /*#__PURE__*/React.createElement("head", null, /*#__PURE__*/React.createElement("meta", {
charSet: "utf-8"
}), /*#__PURE__*/React__default.createElement("meta", {
}), /*#__PURE__*/React.createElement("meta", {
name: "viewport",
content: "width=device-width, initial-scale=1, viewport-fit=cover"
}), /*#__PURE__*/React__default.createElement("title", null, "Unhandled Thrown Response!")), /*#__PURE__*/React__default.createElement("body", null, /*#__PURE__*/React__default.createElement("h1", {
content: "width=device-width,initial-scale=1,viewport-fit=cover"
}), /*#__PURE__*/React.createElement("title", null, title)), /*#__PURE__*/React.createElement("body", null, /*#__PURE__*/React.createElement("main", {
style: {

@@ -177,13 +161,5 @@ fontFamily: "system-ui, sans-serif",

}
}, caught.status, " ", caught.statusText), /*#__PURE__*/React__default.createElement("script", {
dangerouslySetInnerHTML: {
__html: `
console.log(
"💿 Hey developer👋. You can provide a way better UX than this when your app throws 404s (and other responses). Check out https://remix.run/guides/not-found for more information."
);
`
}
})));
}, children, renderScripts ? /*#__PURE__*/React.createElement(Scripts, null) : null)));
}
export { RemixCatchBoundary, RemixErrorBoundary, RemixRootDefaultCatchBoundary, RemixRootDefaultErrorBoundary, V2_RemixRootDefaultErrorBoundary, useCatch };
export { BoundaryShell, RemixErrorBoundary, RemixRootDefaultErrorBoundary };
/**
* @remix-run/react v0.0.0-nightly-ccefed3-20230621
* @remix-run/react v0.0.0-nightly-cd403b516-20240809
*

@@ -11,9 +11,4 @@ * Copyright (c) Remix Software Inc.

*/
import { ErrorResponse } from '@remix-run/router';
import { UNSAFE_ErrorResponseImpl } from '@remix-run/router';
/**
* @deprecated in favor of the `ErrorResponse` class in React Router. Please
* enable the `future.v2_errorBoundary` flag to ease your migration to Remix v2.
*/
function deserializeErrors(errors) {

@@ -27,7 +22,23 @@ if (!errors) return null;

if (val && val.__type === "RouteErrorResponse") {
serialized[key] = new ErrorResponse(val.status, val.statusText, val.data, val.internal === true);
serialized[key] = new UNSAFE_ErrorResponseImpl(val.status, val.statusText, val.data, val.internal === true);
} else if (val && val.__type === "Error") {
let error = new Error(val.message);
error.stack = val.stack;
serialized[key] = error;
// Attempt to reconstruct the right type of Error (i.e., ReferenceError)
if (val.__subType) {
let ErrorConstructor = window[val.__subType];
if (typeof ErrorConstructor === "function") {
try {
// @ts-expect-error
let error = new ErrorConstructor(val.message);
error.stack = val.stack;
serialized[key] = error;
} catch (e) {
// no-op - fall through and create a normal Error
}
}
}
if (serialized[key] == null) {
let error = new Error(val.message);
error.stack = val.stack;
serialized[key] = error;
}
} else {

@@ -34,0 +45,0 @@ serialized[key] = val;

/**
* @remix-run/react v0.0.0-nightly-ccefed3-20230621
* @remix-run/react v0.0.0-nightly-cd403b516-20240809
*

@@ -11,7 +11,8 @@ * Copyright (c) Remix Software Inc.

*/
export { Navigate, NavigationType, Outlet, Route, Routes, createPath, createRoutesFromChildren, createRoutesFromElements, createSearchParams, generatePath, isRouteErrorResponse, matchPath, matchRoutes, parsePath, renderMatches, resolvePath, unstable_usePrompt, unstable_useViewTransitionState, useAsyncError, useAsyncValue, useBeforeUnload, useBlocker, useFetchers, useFormAction, useHref, useInRouterContext, useLinkClickHandler, useLocation, useMatch, useNavigate, useNavigation, useNavigationType, useOutlet, useOutletContext, useParams, useResolvedPath, useRevalidator, useRouteError, useRoutes, useSearchParams, useSubmit } from 'react-router-dom';
export { defer, json, redirect, redirectDocument, replace, unstable_data } from '@remix-run/server-runtime';
export { RemixBrowser } from './browser.js';
export { Form, Outlet, isRouteErrorResponse, unstable_useBlocker, unstable_usePrompt, useAsyncError, useAsyncValue, useBeforeUnload, useFormAction, useHref, useLocation, useMatch, useNavigate, useNavigation, useNavigationType, useOutlet, useOutletContext, useParams, useResolvedPath, useRevalidator, useRouteError, useRouteLoaderData, useSearchParams, useSubmit } from 'react-router-dom';
export { Await, Link, Links, LiveReload, Meta, NavLink, PrefetchPageLinks, Scripts, RemixContext as UNSAFE_RemixContext, useActionData, useFetcher, useFetchers, useLoaderData, useMatches, useTransition } from './components.js';
export { useCatch } from './errorBoundaries.js';
export { Await, Form, Link, Links, LiveReload, Meta, NavLink, PrefetchPageLinks, Scripts, RemixContext as UNSAFE_RemixContext, useActionData, useFetcher, useLoaderData, useMatches, useRouteLoaderData } from './components.js';
export { ScrollRestoration } from './scroll-restoration.js';
export { RemixServer } from './server.js';
export { defineClientAction as unstable_defineClientAction, defineClientLoader as unstable_defineClientLoader } from './single-fetch.js';
/**
* @remix-run/react v0.0.0-nightly-ccefed3-20230621
* @remix-run/react v0.0.0-nightly-cd403b516-20240809
*

@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc.

/**
* @remix-run/react v0.0.0-nightly-ccefed3-20230621
* @remix-run/react v0.0.0-nightly-cd403b516-20240809
*

@@ -21,2 +21,3 @@ * Copyright (c) Remix Software Inc.

////////////////////////////////////////////////////////////////////////////////
/**

@@ -26,15 +27,23 @@ * Gets all the links for a set of matches. The modules are assumed to have been

*/
function getLinksForMatches(matches, routeModules, manifest) {
function getKeyedLinksForMatches(matches, routeModules, manifest) {
let descriptors = matches.map(match => {
var _module$links;
let module = routeModules[match.route.id];
return ((_module$links = module.links) === null || _module$links === void 0 ? void 0 : _module$links.call(module)) || [];
}).flat(1);
let route = manifest.routes[match.route.id];
return [route.css ? route.css.map(href => ({
rel: "stylesheet",
href
})) : [], (module === null || module === void 0 ? void 0 : (_module$links = module.links) === null || _module$links === void 0 ? void 0 : _module$links.call(module)) || []];
}).flat(2);
let preloads = getCurrentPageModulePreloadHrefs(matches, manifest);
return dedupe(descriptors, preloads);
return dedupeLinkDescriptors(descriptors, preloads);
}
async function prefetchStyleLinks(routeModule) {
if (!routeModule.links) return;
let descriptors = routeModule.links();
if (!descriptors) return;
async function prefetchStyleLinks(route, routeModule) {
var _route$css, _routeModule$links;
if (!route.css && !routeModule.links || !isPreloadSupported()) return;
let descriptors = [((_route$css = route.css) === null || _route$css === void 0 ? void 0 : _route$css.map(href => ({
rel: "stylesheet",
href
}))) ?? [], ((_routeModule$links = routeModule.links) === null || _routeModule$links === void 0 ? void 0 : _routeModule$links.call(routeModule)) ?? []].flat(1);
if (descriptors.length === 0) return;
let styleLinks = [];

@@ -51,4 +60,5 @@ for (let descriptor of descriptors) {

// don't block for non-matching media queries
let matchingLinks = styleLinks.filter(link => !link.media || window.matchMedia(link.media).matches);
// don't block for non-matching media queries, or for stylesheets that are
// already in the DOM (active route revalidations)
let matchingLinks = styleLinks.filter(link => (!link.media || window.matchMedia(link.media).matches) && !document.querySelector(`link[rel="stylesheet"][href="${link.href}"]`));
await Promise.all(matchingLinks.map(prefetchStyleLink));

@@ -85,13 +95,15 @@ }

function isHtmlLinkDescriptor(object) {
if (object == null) return false;
if (object == null) {
return false;
}
// <link> may not have an href if <link rel="preload"> is used with imagesrcset + imagesizes
// <link> may not have an href if <link rel="preload"> is used with imageSrcSet + imageSizes
// https://github.com/remix-run/remix/issues/184
// https://html.spec.whatwg.org/commit-snapshots/cb4f5ff75de5f4cbd7013c4abad02f21c77d4d1c/#attr-link-imagesrcset
if (object.href == null) {
return object.rel === "preload" && (typeof object.imageSrcSet === "string" || typeof object.imagesrcset === "string") && (typeof object.imageSizes === "string" || typeof object.imagesizes === "string");
return object.rel === "preload" && typeof object.imageSrcSet === "string" && typeof object.imageSizes === "string";
}
return typeof object.rel === "string" && typeof object.href === "string";
}
async function getStylesheetPrefetchLinks(matches, manifest, routeModules) {
async function getKeyedPrefetchLinks(matches, manifest, routeModules) {
let links = await Promise.all(matches.map(async match => {

@@ -101,10 +113,10 @@ let mod = await loadRouteModule(manifest.routes[match.route.id], routeModules);

}));
return links.flat(1).filter(isHtmlLinkDescriptor).filter(link => link.rel === "stylesheet" || link.rel === "preload").map(link => link.rel === "preload" ? {
return dedupeLinkDescriptors(links.flat(1).filter(isHtmlLinkDescriptor).filter(link => link.rel === "stylesheet" || link.rel === "preload").map(link => link.rel === "stylesheet" ? {
...link,
rel: "prefetch"
rel: "prefetch",
as: "style"
} : {
...link,
rel: "prefetch",
as: "style"
});
rel: "prefetch"
}));
}

@@ -165,3 +177,3 @@

let path = parsePathPatch(page);
return dedupeHrefs(matches.filter(match => manifest.routes[match.route.id].hasLoader).map(match => {
return dedupeHrefs(matches.filter(match => manifest.routes[match.route.id].hasLoader && !manifest.routes[match.route.id].hasClientLoader).map(match => {
let {

@@ -203,14 +215,25 @@ pathname,

}
function dedupe(descriptors, preloads) {
function sortKeys(obj) {
let sorted = {};
let keys = Object.keys(obj).sort();
for (let key of keys) {
sorted[key] = obj[key];
}
return sorted;
}
function dedupeLinkDescriptors(descriptors, preloads) {
let set = new Set();
let preloadsSet = new Set(preloads);
return descriptors.reduce((deduped, descriptor) => {
let alreadyModulePreload = !isPageLinkDescriptor(descriptor) && descriptor.as === "script" && descriptor.href && preloadsSet.has(descriptor.href);
let alreadyModulePreload = preloads && !isPageLinkDescriptor(descriptor) && descriptor.as === "script" && descriptor.href && preloadsSet.has(descriptor.href);
if (alreadyModulePreload) {
return deduped;
}
let str = JSON.stringify(descriptor);
if (!set.has(str)) {
set.add(str);
deduped.push(descriptor);
let key = JSON.stringify(sortKeys(descriptor));
if (!set.has(key)) {
set.add(key);
deduped.push({
key,
link: descriptor
});
}

@@ -228,2 +251,16 @@ return deduped;

export { dedupe, getDataLinkHrefs, getLinksForMatches, getModuleLinkHrefs, getNewMatchesForLinks, getStylesheetPrefetchLinks, isHtmlLinkDescriptor, isPageLinkDescriptor, prefetchStyleLinks };
// Detect if this browser supports <link rel="preload"> (or has it enabled).
// Originally added to handle the firefox `network.preload` config:
// https://bugzilla.mozilla.org/show_bug.cgi?id=1847811
let _isPreloadSupported;
function isPreloadSupported() {
if (_isPreloadSupported !== undefined) {
return _isPreloadSupported;
}
let el = document.createElement("link");
_isPreloadSupported = el.relList.supports("preload");
el = null;
return _isPreloadSupported;
}
export { getDataLinkHrefs, getKeyedLinksForMatches, getKeyedPrefetchLinks, getModuleLinkHrefs, getNewMatchesForLinks, isPageLinkDescriptor, prefetchStyleLinks };
/**
* @remix-run/react v0.0.0-nightly-ccefed3-20230621
* @remix-run/react v0.0.0-nightly-cd403b516-20240809
*

@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc.

/**
* @remix-run/react v0.0.0-nightly-ccefed3-20230621
* @remix-run/react v0.0.0-nightly-cd403b516-20240809
*

@@ -12,48 +12,38 @@ * Copyright (c) Remix Software Inc.

/**
* A React component that is rendered when the server throws a Response.
*
* @deprecated Please enable the v2_errorBoundary flag
*
* @see https://remix.run/route/catch-boundary
* A function that handles data mutations for a route on the client
*/
/**
* A React component that is rendered when there is an error on a route.
*
* @deprecated Please enable the v2_errorBoundary flag
*
* @see https://remix.run/route/error-boundary
* Arguments passed to a route `clientAction` function
*/
/**
* V2 version of the ErrorBoundary that eliminates the distinction between
* Error and Catch Boundaries and behaves like RR 6.4 errorElement and captures
* errors with useRouteError()
* A function that loads data for a route on the client
*/
/**
* A function that defines `<link>` tags to be inserted into the `<head>` of
* the document on route transitions.
*
* @see https://remix.run/route/meta
* Arguments passed to a route `clientLoader` function
*/
/**
* A function that returns an object of name + content pairs to use for
* `<meta>` tags for a route. These tags will be merged with (and take
* precedence over) tags from parent routes.
*
* @see https://remix.run/route/meta
* ErrorBoundary to display for this route
*/
// TODO: Replace in v2
/**
* `<Route HydrateFallback>` component to render on initial loads
* when client loaders are present
*/
/**
* A name/content pair used to render `<meta>` tags in a meta function for a
* route. The value can be either a string, which will render a single `<meta>`
* tag, or an array of strings that will render multiple tags with the same
* `name` attribute.
* Optional, root-only `<Route Layout>` component to wrap the root content in.
* Useful for defining the <html>/<head>/<body> document shell shared by the
* Component, HydrateFallback, and ErrorBoundary
*/
// TODO: Replace in v2
/**
* A function that defines `<link>` tags to be inserted into the `<head>` of
* the document on route transitions.
*
* @see https://remix.run/route/meta
*/

@@ -79,6 +69,23 @@ /**

} catch (error) {
// User got caught in the middle of a deploy and the CDN no longer has the
// asset we're trying to import! Reload from the server and the user
// (should) get the new manifest--unless the developer purged the static
// assets, the manifest path, but not the documents 😬
// If we can't load the route it's likely one of 2 things:
// - User got caught in the middle of a deploy and the CDN no longer has the
// asset we're trying to import! Reload from the server and the user
// (should) get the new manifest--unless the developer purged the static
// assets, the manifest path, but not the documents 😬
// - Or, the asset trying to be imported has an error (usually in vite dev
// mode), so the best we can do here is log the error for visibility
// (via `Preserve log`) and reload
// Log the error so it can be accessed via the `Preserve Log` setting
console.error(`Error loading route module \`${route.module}\`, reloading page...`);
console.error(error);
if (window.__remixContext.isSpaMode &&
// @ts-expect-error
typeof import.meta.hot !== "undefined") {
// In SPA Mode (which implies vite) we don't want to perform a hard reload
// on dev-time errors since it's a vite compilation error and a reload is
// just going to fail with the same issue. Let the UI bubble to the error
// boundary and let them see the error in the overlay or the dev server log
throw error;
}
window.location.reload();

@@ -91,9 +98,2 @@ return new Promise(() => {

/**
* @deprecated The `unstable_shouldReload` function has been removed, so this
* function will never run and route data will be revalidated on every request.
* Please update the function name to `shouldRevalidate` and use the
* `ShouldRevalidateFunction` interface.
*/
export { loadRouteModule };
/**
* @remix-run/react v0.0.0-nightly-ccefed3-20230621
* @remix-run/react v0.0.0-nightly-cd403b516-20240809
*

@@ -12,8 +12,10 @@ * Copyright (c) Remix Software Inc.

import * as React from 'react';
import { redirect } from 'react-router-dom';
import { UNSAFE_ErrorResponseImpl } from '@remix-run/router';
import { useRouteError, redirect } from 'react-router-dom';
import { loadRouteModule } from './routeModules.js';
import { fetchData, isRedirectResponse, isCatchResponse, isDeferredResponse, parseDeferredReadableStream } from './data.js';
import { fetchData, isRedirectResponse, isCatchResponse, isDeferredResponse, parseDeferredReadableStream, isDeferredData, isResponse } from './data.js';
import { prefetchStyleLinks } from './links.js';
import { RemixRootDefaultErrorBoundary } from './errorBoundaries.js';
import { RemixRootDefaultHydrateFallback } from './fallback.js';
import invariant from './invariant.js';
import { RemixRoute, RemixRouteError } from './components.js';

@@ -37,22 +39,61 @@ // NOTE: make sure to change the Route in server-runtime if you change this

}
function createServerRoutes(manifest, routeModules, future, parentId = "", routesByParentId = groupRoutesByParentId(manifest)) {
function getRouteComponents(route, routeModule, isSpaMode) {
let Component = getRouteModuleComponent(routeModule);
// HydrateFallback can only exist on the root route in SPA Mode
let HydrateFallback = routeModule.HydrateFallback && (!isSpaMode || route.id === "root") ? routeModule.HydrateFallback : route.id === "root" ? RemixRootDefaultHydrateFallback : undefined;
let ErrorBoundary = routeModule.ErrorBoundary ? routeModule.ErrorBoundary : route.id === "root" ? () => /*#__PURE__*/React.createElement(RemixRootDefaultErrorBoundary, {
error: useRouteError()
}) : undefined;
if (route.id === "root" && routeModule.Layout) {
return {
...(Component ? {
element: /*#__PURE__*/React.createElement(routeModule.Layout, null, /*#__PURE__*/React.createElement(Component, null))
} : {
Component
}),
...(ErrorBoundary ? {
errorElement: /*#__PURE__*/React.createElement(routeModule.Layout, null, /*#__PURE__*/React.createElement(ErrorBoundary, null))
} : {
ErrorBoundary
}),
...(HydrateFallback ? {
hydrateFallbackElement: /*#__PURE__*/React.createElement(routeModule.Layout, null, /*#__PURE__*/React.createElement(HydrateFallback, null))
} : {
HydrateFallback
})
};
}
return {
Component,
ErrorBoundary,
HydrateFallback
};
}
function createServerRoutes(manifest, routeModules, future, isSpaMode, parentId = "", routesByParentId = groupRoutesByParentId(manifest), spaModeLazyPromise = Promise.resolve({
Component: () => null
})) {
return (routesByParentId[parentId] || []).map(route => {
let hasErrorBoundary = future.v2_errorBoundary === true ? route.id === "root" || route.hasErrorBoundary : route.id === "root" || route.hasCatchBoundary || route.hasErrorBoundary;
let routeModule = routeModules[route.id];
invariant(routeModule, "No `routeModule` available to create server routes");
let dataRoute = {
...getRouteComponents(route, routeModule, isSpaMode),
caseSensitive: route.caseSensitive,
element: /*#__PURE__*/React.createElement(RemixRoute, {
id: route.id
}),
errorElement: hasErrorBoundary ? /*#__PURE__*/React.createElement(RemixRouteError, {
id: route.id
}) : undefined,
id: route.id,
index: route.index,
path: route.path,
handle: routeModules[route.id].handle
// Note: we don't need loader/action/shouldRevalidate on these routes
// since they're for a static render
handle: routeModule.handle,
// For SPA Mode, all routes are lazy except root. However we tell the
// router root is also lazy here too since we don't need a full
// implementation - we just need a `lazy` prop to tell the RR rendering
// where to stop which is always at the root route in SPA mode
lazy: isSpaMode ? () => spaModeLazyPromise : undefined,
// For partial hydration rendering, we need to indicate when the route
// has a loader/clientLoader, but it won't ever be called during the static
// render, so just give it a no-op function so we can render down to the
// proper fallback
loader: route.hasLoader || route.hasClientLoader ? () => null : undefined
// We don't need action/shouldRevalidate on these routes since they're
// for a static render
};
let children = createServerRoutes(manifest, routeModules, future, route.id, routesByParentId);
let children = createServerRoutes(manifest, routeModules, future, isSpaMode, route.id, routesByParentId, spaModeLazyPromise);
if (children.length > 0) dataRoute.children = children;

@@ -62,27 +103,213 @@ return dataRoute;

}
function createClientRoutesWithHMRRevalidationOptOut(needsRevalidation, manifest, routeModulesCache, future) {
return createClientRoutes(manifest, routeModulesCache, future, "", groupRoutesByParentId(manifest), needsRevalidation);
function createClientRoutesWithHMRRevalidationOptOut(needsRevalidation, manifest, routeModulesCache, initialState, future, isSpaMode) {
return createClientRoutes(manifest, routeModulesCache, initialState, future, isSpaMode, "", groupRoutesByParentId(manifest), needsRevalidation);
}
function createClientRoutes(manifest, routeModulesCache, future, parentId = "", routesByParentId = groupRoutesByParentId(manifest), needsRevalidation) {
function preventInvalidServerHandlerCall(type, route, isSpaMode) {
if (isSpaMode) {
let fn = type === "action" ? "serverAction()" : "serverLoader()";
let msg = `You cannot call ${fn} in SPA Mode (routeId: "${route.id}")`;
console.error(msg);
throw new UNSAFE_ErrorResponseImpl(400, "Bad Request", new Error(msg), true);
}
let fn = type === "action" ? "serverAction()" : "serverLoader()";
let msg = `You are trying to call ${fn} on a route that does not have a server ` + `${type} (routeId: "${route.id}")`;
if (type === "loader" && !route.hasLoader || type === "action" && !route.hasAction) {
console.error(msg);
throw new UNSAFE_ErrorResponseImpl(400, "Bad Request", new Error(msg), true);
}
}
function noActionDefinedError(type, routeId) {
let article = type === "clientAction" ? "a" : "an";
let msg = `Route "${routeId}" does not have ${article} ${type}, but you are trying to ` + `submit to it. To fix this, please add ${article} \`${type}\` function to the route`;
console.error(msg);
throw new UNSAFE_ErrorResponseImpl(405, "Method Not Allowed", new Error(msg), true);
}
function createClientRoutes(manifest, routeModulesCache, initialState, future, isSpaMode, parentId = "", routesByParentId = groupRoutesByParentId(manifest), needsRevalidation) {
return (routesByParentId[parentId] || []).map(route => {
let hasErrorBoundary = future.v2_errorBoundary === true ? route.id === "root" || route.hasErrorBoundary : route.id === "root" || route.hasCatchBoundary || route.hasErrorBoundary;
let routeModule = routeModulesCache[route.id];
// Fetch data from the server either via single fetch or the standard `?_data`
// request. Unwrap it when called via `serverLoader`/`serverAction` in a
// client handler, otherwise return the raw response for the router to unwrap
async function fetchServerHandlerAndMaybeUnwrap(request, unwrap, singleFetch) {
if (typeof singleFetch === "function") {
let result = await singleFetch();
return result;
}
let result = await fetchServerHandler(request, route);
return unwrap ? unwrapServerResponse(result) : result;
}
function fetchServerLoader(request, unwrap, singleFetch) {
if (!route.hasLoader) return Promise.resolve(null);
return fetchServerHandlerAndMaybeUnwrap(request, unwrap, singleFetch);
}
function fetchServerAction(request, unwrap, singleFetch) {
if (!route.hasAction) {
throw noActionDefinedError("action", route.id);
}
return fetchServerHandlerAndMaybeUnwrap(request, unwrap, singleFetch);
}
async function prefetchStylesAndCallHandler(handler) {
// Only prefetch links if we exist in the routeModulesCache (critical modules
// and navigating back to pages previously loaded via route.lazy). Initial
// execution of route.lazy (when the module is not in the cache) will handle
// prefetching style links via loadRouteModuleWithBlockingLinks.
let cachedModule = routeModulesCache[route.id];
let linkPrefetchPromise = cachedModule ? prefetchStyleLinks(route, cachedModule) : Promise.resolve();
try {
return handler();
} finally {
await linkPrefetchPromise;
}
}
let dataRoute = {
caseSensitive: route.caseSensitive,
element: /*#__PURE__*/React.createElement(RemixRoute, {
id: route.id
}),
errorElement: hasErrorBoundary ? /*#__PURE__*/React.createElement(RemixRouteError, {
id: route.id
}) : undefined,
id: route.id,
index: route.index,
path: route.path,
// handle gets added in via useMatches since we aren't guaranteed to
// have the route module available here
handle: undefined,
loader: createDataFunction(route, routeModulesCache, false),
action: createDataFunction(route, routeModulesCache, true),
shouldRevalidate: createShouldRevalidate(route, routeModulesCache, needsRevalidation)
path: route.path
};
let children = createClientRoutes(manifest, routeModulesCache, future, route.id, routesByParentId, needsRevalidation);
if (routeModule) {
var _initialState$loaderD, _initialState$errors, _routeModule$clientLo;
// Use critical path modules directly
Object.assign(dataRoute, {
...dataRoute,
...getRouteComponents(route, routeModule, isSpaMode),
handle: routeModule.handle,
shouldRevalidate: needsRevalidation ? wrapShouldRevalidateForHdr(route.id, routeModule.shouldRevalidate, needsRevalidation) : routeModule.shouldRevalidate
});
let initialData = initialState === null || initialState === void 0 ? void 0 : (_initialState$loaderD = initialState.loaderData) === null || _initialState$loaderD === void 0 ? void 0 : _initialState$loaderD[route.id];
let initialError = initialState === null || initialState === void 0 ? void 0 : (_initialState$errors = initialState.errors) === null || _initialState$errors === void 0 ? void 0 : _initialState$errors[route.id];
let isHydrationRequest = needsRevalidation == null && (((_routeModule$clientLo = routeModule.clientLoader) === null || _routeModule$clientLo === void 0 ? void 0 : _routeModule$clientLo.hydrate) === true || !route.hasLoader);
dataRoute.loader = async ({
request,
params
}, singleFetch) => {
try {
let result = await prefetchStylesAndCallHandler(async () => {
invariant(routeModule, "No `routeModule` available for critical-route loader");
if (!routeModule.clientLoader) {
if (isSpaMode) return null;
// Call the server when no client loader exists
return fetchServerLoader(request, false, singleFetch);
}
return routeModule.clientLoader({
request,
params,
async serverLoader() {
preventInvalidServerHandlerCall("loader", route, isSpaMode);
// On the first call, resolve with the server result
if (isHydrationRequest) {
if (initialError !== undefined) {
throw initialError;
}
return initialData;
}
// Call the server loader for client-side navigations
return fetchServerLoader(request, true, singleFetch);
}
});
});
return result;
} finally {
// Whether or not the user calls `serverLoader`, we only let this
// stick around as true for one loader call
isHydrationRequest = false;
}
};
// Let React Router know whether to run this on hydration
dataRoute.loader.hydrate = shouldHydrateRouteLoader(route, routeModule, isSpaMode);
dataRoute.action = ({
request,
params
}, singleFetch) => {
return prefetchStylesAndCallHandler(async () => {
invariant(routeModule, "No `routeModule` available for critical-route action");
if (!routeModule.clientAction) {
if (isSpaMode) {
throw noActionDefinedError("clientAction", route.id);
}
return fetchServerAction(request, false, singleFetch);
}
return routeModule.clientAction({
request,
params,
async serverAction() {
preventInvalidServerHandlerCall("action", route, isSpaMode);
return fetchServerAction(request, true, singleFetch);
}
});
});
};
} else {
// If the lazy route does not have a client loader/action we want to call
// the server loader/action in parallel with the module load so we add
// loader/action as static props on the route
if (!route.hasClientLoader) {
dataRoute.loader = ({
request
}, singleFetch) => prefetchStylesAndCallHandler(() => {
if (isSpaMode) return Promise.resolve(null);
return fetchServerLoader(request, false, singleFetch);
});
}
if (!route.hasClientAction) {
dataRoute.action = ({
request
}, singleFetch) => prefetchStylesAndCallHandler(() => {
if (isSpaMode) {
throw noActionDefinedError("clientAction", route.id);
}
return fetchServerAction(request, false, singleFetch);
});
}
// Load all other modules via route.lazy()
dataRoute.lazy = async () => {
let mod = await loadRouteModuleWithBlockingLinks(route, routeModulesCache);
let lazyRoute = {
...mod
};
if (mod.clientLoader) {
let clientLoader = mod.clientLoader;
lazyRoute.loader = (args, singleFetch) => clientLoader({
...args,
async serverLoader() {
preventInvalidServerHandlerCall("loader", route, isSpaMode);
return fetchServerLoader(args.request, true, singleFetch);
}
});
}
if (mod.clientAction) {
let clientAction = mod.clientAction;
lazyRoute.action = (args, singleFetch) => clientAction({
...args,
async serverAction() {
preventInvalidServerHandlerCall("action", route, isSpaMode);
return fetchServerAction(args.request, true, singleFetch);
}
});
}
if (needsRevalidation) {
lazyRoute.shouldRevalidate = wrapShouldRevalidateForHdr(route.id, mod.shouldRevalidate, needsRevalidation);
}
return {
...(lazyRoute.loader ? {
loader: lazyRoute.loader
} : {}),
...(lazyRoute.action ? {
action: lazyRoute.action
} : {}),
hasErrorBoundary: lazyRoute.hasErrorBoundary,
shouldRevalidate: lazyRoute.shouldRevalidate,
handle: lazyRoute.handle,
// No need to wrap these in layout since the root route is never
// loaded via route.lazy()
Component: lazyRoute.Component,
ErrorBoundary: lazyRoute.ErrorBoundary
};
};
}
let children = createClientRoutes(manifest, routeModulesCache, initialState, future, isSpaMode, route.id, routesByParentId, needsRevalidation);
if (children.length > 0) dataRoute.children = children;

@@ -92,19 +319,13 @@ return dataRoute;

}
function createShouldRevalidate(route, routeModules, needsRevalidation) {
// When an HMR / HDR update happens we opt out of all user-defined
// revalidation logic and force a revalidation on the first call
function wrapShouldRevalidateForHdr(routeId, routeShouldRevalidate, needsRevalidation) {
let handledRevalidation = false;
return function (arg) {
let module = routeModules[route.id];
invariant(module, `Expected route module to be loaded for ${route.id}`);
// When an HMR / HDR update happens we opt out of all user-defined
// revalidation logic and the do as the dev server tells us the first
// time router.revalidate() is called.
if (needsRevalidation !== undefined && !handledRevalidation) {
return arg => {
if (!handledRevalidation) {
handledRevalidation = true;
return needsRevalidation.has(route.id);
return needsRevalidation.has(routeId);
}
if (module.shouldRevalidate) {
return module.shouldRevalidate(arg);
}
return arg.defaultShouldRevalidate;
return routeShouldRevalidate ? routeShouldRevalidate(arg) : arg.defaultShouldRevalidate;
};

@@ -114,36 +335,48 @@ }

let routeModule = await loadRouteModule(route, routeModules);
await prefetchStyleLinks(routeModule);
return routeModule;
await prefetchStyleLinks(route, routeModule);
// Include all `browserSafeRouteExports` fields, except `HydrateFallback`
// since those aren't used on lazily loaded routes
return {
Component: getRouteModuleComponent(routeModule),
ErrorBoundary: routeModule.ErrorBoundary,
clientAction: routeModule.clientAction,
clientLoader: routeModule.clientLoader,
handle: routeModule.handle,
links: routeModule.links,
meta: routeModule.meta,
shouldRevalidate: routeModule.shouldRevalidate
};
}
function createDataFunction(route, routeModules, isAction) {
return async ({
request
}) => {
let routeModulePromise = loadRouteModuleWithBlockingLinks(route, routeModules);
try {
if (isAction && !route.hasAction) {
let msg = `Route "${route.id}" does not have an action, but you are trying ` + `to submit to it. To fix this, please add an \`action\` function to the route`;
console.error(msg);
throw new Error(msg);
} else if (!isAction && !route.hasLoader) {
return null;
}
let result = await fetchData(request, route.id);
if (result instanceof Error) {
throw result;
}
if (isRedirectResponse(result)) {
throw getRedirect(result);
}
if (isCatchResponse(result)) {
throw result;
}
if (isDeferredResponse(result) && result.body) {
return await parseDeferredReadableStream(result.body);
}
return result;
} finally {
await routeModulePromise;
async function fetchServerHandler(request, route) {
let result = await fetchData(request, route.id);
if (result instanceof Error) {
throw result;
}
if (isRedirectResponse(result)) {
throw getRedirect(result);
}
if (isCatchResponse(result)) {
throw result;
}
if (isDeferredResponse(result) && result.body) {
return await parseDeferredReadableStream(result.body);
}
return result;
}
function unwrapServerResponse(result) {
if (isDeferredData(result)) {
return result.data;
}
if (isResponse(result)) {
let contentType = result.headers.get("Content-Type");
// Check between word boundaries instead of startsWith() due to the last
// paragraph of https://httpwg.org/specs/rfc9110.html#field.content-type
if (contentType && /\bapplication\/json\b/.test(contentType)) {
return result.json();
} else {
return result.text();
}
};
}
return result;
}

@@ -158,2 +391,10 @@ function getRedirect(response) {

}
let reloadDocument = response.headers.get("X-Remix-Reload-Document");
if (reloadDocument) {
headers["X-Remix-Reload-Document"] = reloadDocument;
}
let replace = response.headers.get("X-Remix-Replace");
if (replace) {
headers["X-Remix-Replace"] = replace;
}
return redirect(url, {

@@ -165,2 +406,17 @@ status,

export { createClientRoutes, createClientRoutesWithHMRRevalidationOptOut, createServerRoutes };
// Our compiler generates the default export as `{}` when no default is provided,
// which can lead us to trying to use that as a Component in RR and calling
// createElement on it. Patching here as a quick fix and hoping it's no longer
// an issue in Vite.
function getRouteModuleComponent(routeModule) {
if (routeModule.default == null) return undefined;
let isEmptyObject = typeof routeModule.default === "object" && Object.keys(routeModule.default).length === 0;
if (!isEmptyObject) {
return routeModule.default;
}
}
function shouldHydrateRouteLoader(route, routeModule, isSpaMode) {
return isSpaMode && route.id !== "root" || routeModule.clientLoader != null && (routeModule.clientLoader.hydrate === true || route.hasLoader !== true);
}
export { createClientRoutes, createClientRoutesWithHMRRevalidationOptOut, createServerRoutes, shouldHydrateRouteLoader };
/**
* @remix-run/react v0.0.0-nightly-ccefed3-20230621
* @remix-run/react v0.0.0-nightly-cd403b516-20240809
*

@@ -13,4 +13,4 @@ * Copyright (c) Remix Software Inc.

import * as React from 'react';
import { useLocation, UNSAFE_useScrollRestoration } from 'react-router-dom';
import { useMatches } from './components.js';
import { useLocation, useMatches, UNSAFE_useScrollRestoration } from 'react-router-dom';
import { useRemixContext } from './components.js';

@@ -29,2 +29,5 @@ let STORAGE_KEY = "positions";

}) {
let {
isSpaMode
} = useRemixContext();
let location = useLocation();

@@ -51,2 +54,8 @@ let matches = useMatches();

[]);
// In SPA Mode, there's nothing to restore on initial render since we didn't
// render anything on the server
if (isSpaMode) {
return null;
}
let restoreScroll = ((STORAGE_KEY, restoreKey) => {

@@ -53,0 +62,0 @@ if (!window.history.state || !window.history.state.key) {

/**
* @remix-run/react v0.0.0-nightly-ccefed3-20230621
* @remix-run/react v0.0.0-nightly-cd403b516-20240809
*

@@ -14,4 +14,5 @@ * Copyright (c) Remix Software Inc.

import { RemixContext } from './components.js';
import { RemixErrorBoundary, RemixRootDefaultErrorBoundary } from './errorBoundaries.js';
import { createServerRoutes } from './routes.js';
import { RemixErrorBoundary } from './errorBoundaries.js';
import { createServerRoutes, shouldHydrateRouteLoader } from './routes.js';
import { StreamTransfer } from './single-fetch.js';

@@ -26,3 +27,4 @@ /**

url,
abortDelay
abortDelay,
nonce
}) {

@@ -35,17 +37,47 @@ if (typeof url === "string") {

routeModules,
criticalCss,
serverHandoffString
} = context;
let routes = createServerRoutes(manifest.routes, routeModules, context.future);
let router = createStaticRouter(routes, context.staticHandlerContext);
return /*#__PURE__*/React.createElement(RemixContext.Provider, {
let routes = createServerRoutes(manifest.routes, routeModules, context.future, context.isSpaMode);
// Create a shallow clone of `loaderData` we can mutate for partial hydration.
// When a route exports a `clientLoader` and a `HydrateFallback`, we want to
// render the fallback on the server so we clear our the `loaderData` during SSR.
// Is it important not to change the `context` reference here since we use it
// for context._deepestRenderedBoundaryId tracking
context.staticHandlerContext.loaderData = {
...context.staticHandlerContext.loaderData
};
for (let match of context.staticHandlerContext.matches) {
let routeId = match.route.id;
let route = routeModules[routeId];
let manifestRoute = context.manifest.routes[routeId];
// Clear out the loaderData to avoid rendering the route component when the
// route opted into clientLoader hydration and either:
// * gave us a HydrateFallback
// * or doesn't have a server loader and we have no data to render
if (route && shouldHydrateRouteLoader(manifestRoute, route, context.isSpaMode) && (route.HydrateFallback || !manifestRoute.hasLoader)) {
context.staticHandlerContext.loaderData[routeId] = undefined;
}
}
let router = createStaticRouter(routes, context.staticHandlerContext, {
future: {
v7_partialHydration: true,
v7_relativeSplatPath: context.future.v3_relativeSplatPath
}
});
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(RemixContext.Provider, {
value: {
manifest,
routeModules,
criticalCss,
serverHandoffString,
future: context.future,
abortDelay
isSpaMode: context.isSpaMode,
serializeError: context.serializeError,
abortDelay,
renderMeta: context.renderMeta
}
}, /*#__PURE__*/React.createElement(RemixErrorBoundary, {
location: router.state.location,
component: RemixRootDefaultErrorBoundary
location: router.state.location
}, /*#__PURE__*/React.createElement(StaticRouterProvider, {

@@ -55,5 +87,11 @@ router: router,

hydrate: false
})));
}))), context.future.unstable_singleFetch && context.serverHandoffStream ? /*#__PURE__*/React.createElement(React.Suspense, null, /*#__PURE__*/React.createElement(StreamTransfer, {
context: context,
identifier: 0,
reader: context.serverHandoffStream.getReader(),
textDecoder: new TextDecoder(),
nonce: nonce
})) : null);
}
export { RemixServer };

@@ -0,16 +1,16 @@

export type { ErrorResponse, Fetcher, FetcherWithComponents, FormEncType, FormMethod, Location, NavigateFunction, Navigation, Params, Path, ShouldRevalidateFunction, ShouldRevalidateFunctionArgs, SubmitFunction, SubmitOptions, Blocker, BlockerFunction, } from "react-router-dom";
export { createPath, createRoutesFromChildren, createRoutesFromElements, createSearchParams, generatePath, matchPath, matchRoutes, parsePath, renderMatches, resolvePath, Navigate, NavigationType, Outlet, Route, Routes, useAsyncError, useAsyncValue, isRouteErrorResponse, useBeforeUnload, useFetchers, useFormAction, useHref, useInRouterContext, useLinkClickHandler, useLocation, useMatch, useNavigate, useNavigation, useNavigationType, useOutlet, useOutletContext, useParams, useResolvedPath, useRevalidator, useRouteError, useRoutes, useSearchParams, useSubmit, useBlocker, unstable_usePrompt, unstable_useViewTransitionState, } from "react-router-dom";
export { defer, json, redirect, redirectDocument, replace, unstable_data, } from "@remix-run/server-runtime";
export type { RemixBrowserProps } from "./browser";
export { RemixBrowser } from "./browser";
export type { FormEncType, FormMethod, FormProps, Location, NavigateFunction, Params, Path, ShouldRevalidateFunction, SubmitFunction, SubmitOptions, unstable_Blocker, unstable_BlockerFunction, } from "react-router-dom";
export { Form, Outlet, useAsyncError, useAsyncValue, isRouteErrorResponse, useBeforeUnload, useFormAction, useHref, useLocation, useMatch, useNavigate, useNavigation, useNavigationType, useOutlet, useOutletContext, useParams, useResolvedPath, useRevalidator, useRouteError, useRouteLoaderData, useSearchParams, useSubmit, unstable_useBlocker, unstable_usePrompt, } from "react-router-dom";
export type { AwaitProps, FetcherWithComponents, RouteMatch, RemixNavLinkProps as NavLinkProps, RemixLinkProps as LinkProps, } from "./components";
export { Await, Meta, Links, Scripts, Link, NavLink, PrefetchPageLinks, LiveReload, useTransition, useFetcher, useFetchers, useLoaderData, useMatches, useActionData, RemixContext as UNSAFE_RemixContext, } from "./components";
export type { ThrownResponse } from "./errors";
export { useCatch } from "./errorBoundaries";
export type { AwaitProps, RemixFormProps as FormProps, RemixNavLinkProps as NavLinkProps, RemixLinkProps as LinkProps, UIMatch, } from "./components";
export { Await, Meta, Links, Scripts, Form, Link, NavLink, PrefetchPageLinks, LiveReload, useFetcher, useLoaderData, useRouteLoaderData, useActionData, useMatches, RemixContext as UNSAFE_RemixContext, } from "./components";
export type { HtmlLinkDescriptor } from "./links";
export type { CatchBoundaryComponent, HtmlMetaDescriptor, V2_MetaArgs, V2_MetaDescriptor, V2_MetaFunction, RouteModules as UNSAFE_RouteModules, ShouldReloadFunction, } from "./routeModules";
export type { ClientActionFunction, ClientActionFunctionArgs, ClientLoaderFunction, ClientLoaderFunctionArgs, MetaArgs, MetaMatch as UNSAFE_MetaMatch, MetaDescriptor, MetaFunction, RouteModules as UNSAFE_RouteModules, } from "./routeModules";
export { ScrollRestoration } from "./scroll-restoration";
export type { RemixServerProps } from "./server";
export { RemixServer } from "./server";
export type { Fetcher } from "./transition";
export type { ClientAction as unstable_ClientAction, ClientLoader as unstable_ClientLoader, } from "./single-fetch";
export { defineClientAction as unstable_defineClientAction, defineClientLoader as unstable_defineClientLoader, } from "./single-fetch";
export type { FutureConfig as UNSAFE_FutureConfig, AssetsManifest as UNSAFE_AssetsManifest, RemixContextObject as UNSAFE_RemixContextObject, } from "./entry";
export type { EntryRoute as UNSAFE_EntryRoute, RouteManifest as UNSAFE_RouteManifest, } from "./routes";
/**
* @remix-run/react v0.0.0-nightly-ccefed3-20230621
* @remix-run/react v0.0.0-nightly-cd403b516-20240809
*

@@ -15,16 +15,20 @@ * Copyright (c) Remix Software Inc.

var reactRouterDom = require('react-router-dom');
var serverRuntime = require('@remix-run/server-runtime');
var browser = require('./browser.js');
var reactRouterDom = require('react-router-dom');
var components = require('./components.js');
var errorBoundaries = require('./errorBoundaries.js');
var scrollRestoration = require('./scroll-restoration.js');
var server = require('./server.js');
var singleFetch = require('./single-fetch.js');
exports.RemixBrowser = browser.RemixBrowser;
Object.defineProperty(exports, 'Form', {
Object.defineProperty(exports, 'Navigate', {
enumerable: true,
get: function () { return reactRouterDom.Form; }
get: function () { return reactRouterDom.Navigate; }
});
Object.defineProperty(exports, 'NavigationType', {
enumerable: true,
get: function () { return reactRouterDom.NavigationType; }
});
Object.defineProperty(exports, 'Outlet', {

@@ -34,2 +38,30 @@ enumerable: true,

});
Object.defineProperty(exports, 'Route', {
enumerable: true,
get: function () { return reactRouterDom.Route; }
});
Object.defineProperty(exports, 'Routes', {
enumerable: true,
get: function () { return reactRouterDom.Routes; }
});
Object.defineProperty(exports, 'createPath', {
enumerable: true,
get: function () { return reactRouterDom.createPath; }
});
Object.defineProperty(exports, 'createRoutesFromChildren', {
enumerable: true,
get: function () { return reactRouterDom.createRoutesFromChildren; }
});
Object.defineProperty(exports, 'createRoutesFromElements', {
enumerable: true,
get: function () { return reactRouterDom.createRoutesFromElements; }
});
Object.defineProperty(exports, 'createSearchParams', {
enumerable: true,
get: function () { return reactRouterDom.createSearchParams; }
});
Object.defineProperty(exports, 'generatePath', {
enumerable: true,
get: function () { return reactRouterDom.generatePath; }
});
Object.defineProperty(exports, 'isRouteErrorResponse', {

@@ -39,6 +71,22 @@ enumerable: true,

});
Object.defineProperty(exports, 'unstable_useBlocker', {
Object.defineProperty(exports, 'matchPath', {
enumerable: true,
get: function () { return reactRouterDom.unstable_useBlocker; }
get: function () { return reactRouterDom.matchPath; }
});
Object.defineProperty(exports, 'matchRoutes', {
enumerable: true,
get: function () { return reactRouterDom.matchRoutes; }
});
Object.defineProperty(exports, 'parsePath', {
enumerable: true,
get: function () { return reactRouterDom.parsePath; }
});
Object.defineProperty(exports, 'renderMatches', {
enumerable: true,
get: function () { return reactRouterDom.renderMatches; }
});
Object.defineProperty(exports, 'resolvePath', {
enumerable: true,
get: function () { return reactRouterDom.resolvePath; }
});
Object.defineProperty(exports, 'unstable_usePrompt', {

@@ -48,2 +96,6 @@ enumerable: true,

});
Object.defineProperty(exports, 'unstable_useViewTransitionState', {
enumerable: true,
get: function () { return reactRouterDom.unstable_useViewTransitionState; }
});
Object.defineProperty(exports, 'useAsyncError', {

@@ -61,2 +113,10 @@ enumerable: true,

});
Object.defineProperty(exports, 'useBlocker', {
enumerable: true,
get: function () { return reactRouterDom.useBlocker; }
});
Object.defineProperty(exports, 'useFetchers', {
enumerable: true,
get: function () { return reactRouterDom.useFetchers; }
});
Object.defineProperty(exports, 'useFormAction', {

@@ -70,2 +130,10 @@ enumerable: true,

});
Object.defineProperty(exports, 'useInRouterContext', {
enumerable: true,
get: function () { return reactRouterDom.useInRouterContext; }
});
Object.defineProperty(exports, 'useLinkClickHandler', {
enumerable: true,
get: function () { return reactRouterDom.useLinkClickHandler; }
});
Object.defineProperty(exports, 'useLocation', {

@@ -115,5 +183,5 @@ enumerable: true,

});
Object.defineProperty(exports, 'useRouteLoaderData', {
Object.defineProperty(exports, 'useRoutes', {
enumerable: true,
get: function () { return reactRouterDom.useRouteLoaderData; }
get: function () { return reactRouterDom.useRoutes; }
});

@@ -128,3 +196,29 @@ Object.defineProperty(exports, 'useSearchParams', {

});
Object.defineProperty(exports, 'defer', {
enumerable: true,
get: function () { return serverRuntime.defer; }
});
Object.defineProperty(exports, 'json', {
enumerable: true,
get: function () { return serverRuntime.json; }
});
Object.defineProperty(exports, 'redirect', {
enumerable: true,
get: function () { return serverRuntime.redirect; }
});
Object.defineProperty(exports, 'redirectDocument', {
enumerable: true,
get: function () { return serverRuntime.redirectDocument; }
});
Object.defineProperty(exports, 'replace', {
enumerable: true,
get: function () { return serverRuntime.replace; }
});
Object.defineProperty(exports, 'unstable_data', {
enumerable: true,
get: function () { return serverRuntime.unstable_data; }
});
exports.RemixBrowser = browser.RemixBrowser;
exports.Await = components.Await;
exports.Form = components.Form;
exports.Link = components.Link;

@@ -140,8 +234,8 @@ exports.Links = components.Links;

exports.useFetcher = components.useFetcher;
exports.useFetchers = components.useFetchers;
exports.useLoaderData = components.useLoaderData;
exports.useMatches = components.useMatches;
exports.useTransition = components.useTransition;
exports.useCatch = errorBoundaries.useCatch;
exports.useRouteLoaderData = components.useRouteLoaderData;
exports.ScrollRestoration = scrollRestoration.ScrollRestoration;
exports.RemixServer = server.RemixServer;
exports.unstable_defineClientAction = singleFetch.defineClientAction;
exports.unstable_defineClientLoader = singleFetch.defineClientLoader;
/**
* @remix-run/react v0.0.0-nightly-ccefed3-20230621
* @remix-run/react v0.0.0-nightly-cd403b516-20240809
*

@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc.

@@ -5,2 +5,3 @@ import type { AgnosticDataRouteMatch } from "@remix-run/router";

import type { RouteModules, RouteModule } from "./routeModules";
import type { EntryRoute } from "./routes";
type Primitive = null | undefined | string | number | boolean | symbol | bigint;

@@ -99,16 +100,7 @@ type LiteralUnion<LiteralType, BaseType extends Primitive> = LiteralType | (BaseType & Record<never, never>);

*/
export type HtmlLinkDescriptor = ((HtmlLinkProps & Pick<Required<HtmlLinkProps>, "href">) | (HtmlLinkPreloadImage & Pick<Required<HtmlLinkPreloadImage>, "imageSizes">) | (HtmlLinkPreloadImage & Pick<Required<HtmlLinkPreloadImage>, "href"> & {
export type HtmlLinkDescriptor = (HtmlLinkProps & Pick<Required<HtmlLinkProps>, "href">) | (HtmlLinkPreloadImage & Pick<Required<HtmlLinkPreloadImage>, "imageSizes">) | (HtmlLinkPreloadImage & Pick<Required<HtmlLinkPreloadImage>, "href"> & {
imageSizes?: never;
})) & {
});
export interface PrefetchPageDescriptor extends Omit<HtmlLinkDescriptor, "href" | "rel" | "type" | "sizes" | "imageSrcSet" | "imageSizes" | "as" | "color" | "title"> {
/**
* @deprecated Use `imageSrcSet` instead.
*/
imagesrcset?: string;
/**
* @deprecated Use `imageSizes` instead.
*/
imagesizes?: string;
};
export interface PrefetchPageDescriptor extends Omit<HtmlLinkDescriptor, "href" | "rel" | "type" | "sizes" | "imageSrcSet" | "imageSizes" | "imagesrcset" | "imagesizes" | "as" | "color" | "title"> {
/**
* The absolute path of the page to prefetch.

@@ -123,11 +115,17 @@ */

*/
export declare function getLinksForMatches(matches: AgnosticDataRouteMatch[], routeModules: RouteModules, manifest: AssetsManifest): LinkDescriptor[];
export declare function prefetchStyleLinks(routeModule: RouteModule): Promise<void>;
export declare function getKeyedLinksForMatches(matches: AgnosticDataRouteMatch[], routeModules: RouteModules, manifest: AssetsManifest): KeyedLinkDescriptor[];
export declare function prefetchStyleLinks(route: EntryRoute, routeModule: RouteModule): Promise<void>;
export declare function isPageLinkDescriptor(object: any): object is PrefetchPageDescriptor;
export declare function isHtmlLinkDescriptor(object: any): object is HtmlLinkDescriptor;
export declare function getStylesheetPrefetchLinks(matches: AgnosticDataRouteMatch[], manifest: AssetsManifest, routeModules: RouteModules): Promise<HtmlLinkDescriptor[]>;
export type KeyedHtmlLinkDescriptor = {
key: string;
link: HtmlLinkDescriptor;
};
export declare function getKeyedPrefetchLinks(matches: AgnosticDataRouteMatch[], manifest: AssetsManifest, routeModules: RouteModules): Promise<KeyedHtmlLinkDescriptor[]>;
export declare function getNewMatchesForLinks(page: string, nextMatches: AgnosticDataRouteMatch[], currentMatches: AgnosticDataRouteMatch[], manifest: AssetsManifest, location: Location, mode: "data" | "assets"): AgnosticDataRouteMatch[];
export declare function getDataLinkHrefs(page: string, matches: AgnosticDataRouteMatch[], manifest: AssetsManifest): string[];
export declare function getModuleLinkHrefs(matches: AgnosticDataRouteMatch[], manifestPatch: AssetsManifest): string[];
export declare function dedupe(descriptors: LinkDescriptor[], preloads: string[]): LinkDescriptor[];
type KeyedLinkDescriptor<Descriptor extends LinkDescriptor = LinkDescriptor> = {
key: string;
link: Descriptor;
};
export {};
/**
* @remix-run/react v0.0.0-nightly-ccefed3-20230621
* @remix-run/react v0.0.0-nightly-cd403b516-20240809
*

@@ -25,2 +25,3 @@ * Copyright (c) Remix Software Inc.

////////////////////////////////////////////////////////////////////////////////
/**

@@ -30,15 +31,23 @@ * Gets all the links for a set of matches. The modules are assumed to have been

*/
function getLinksForMatches(matches, routeModules, manifest) {
function getKeyedLinksForMatches(matches, routeModules, manifest) {
let descriptors = matches.map(match => {
var _module$links;
let module = routeModules[match.route.id];
return ((_module$links = module.links) === null || _module$links === void 0 ? void 0 : _module$links.call(module)) || [];
}).flat(1);
let route = manifest.routes[match.route.id];
return [route.css ? route.css.map(href => ({
rel: "stylesheet",
href
})) : [], (module === null || module === void 0 ? void 0 : (_module$links = module.links) === null || _module$links === void 0 ? void 0 : _module$links.call(module)) || []];
}).flat(2);
let preloads = getCurrentPageModulePreloadHrefs(matches, manifest);
return dedupe(descriptors, preloads);
return dedupeLinkDescriptors(descriptors, preloads);
}
async function prefetchStyleLinks(routeModule) {
if (!routeModule.links) return;
let descriptors = routeModule.links();
if (!descriptors) return;
async function prefetchStyleLinks(route, routeModule) {
var _route$css, _routeModule$links;
if (!route.css && !routeModule.links || !isPreloadSupported()) return;
let descriptors = [((_route$css = route.css) === null || _route$css === void 0 ? void 0 : _route$css.map(href => ({
rel: "stylesheet",
href
}))) ?? [], ((_routeModule$links = routeModule.links) === null || _routeModule$links === void 0 ? void 0 : _routeModule$links.call(routeModule)) ?? []].flat(1);
if (descriptors.length === 0) return;
let styleLinks = [];

@@ -55,4 +64,5 @@ for (let descriptor of descriptors) {

// don't block for non-matching media queries
let matchingLinks = styleLinks.filter(link => !link.media || window.matchMedia(link.media).matches);
// don't block for non-matching media queries, or for stylesheets that are
// already in the DOM (active route revalidations)
let matchingLinks = styleLinks.filter(link => (!link.media || window.matchMedia(link.media).matches) && !document.querySelector(`link[rel="stylesheet"][href="${link.href}"]`));
await Promise.all(matchingLinks.map(prefetchStyleLink));

@@ -89,13 +99,15 @@ }

function isHtmlLinkDescriptor(object) {
if (object == null) return false;
if (object == null) {
return false;
}
// <link> may not have an href if <link rel="preload"> is used with imagesrcset + imagesizes
// <link> may not have an href if <link rel="preload"> is used with imageSrcSet + imageSizes
// https://github.com/remix-run/remix/issues/184
// https://html.spec.whatwg.org/commit-snapshots/cb4f5ff75de5f4cbd7013c4abad02f21c77d4d1c/#attr-link-imagesrcset
if (object.href == null) {
return object.rel === "preload" && (typeof object.imageSrcSet === "string" || typeof object.imagesrcset === "string") && (typeof object.imageSizes === "string" || typeof object.imagesizes === "string");
return object.rel === "preload" && typeof object.imageSrcSet === "string" && typeof object.imageSizes === "string";
}
return typeof object.rel === "string" && typeof object.href === "string";
}
async function getStylesheetPrefetchLinks(matches, manifest, routeModules$1) {
async function getKeyedPrefetchLinks(matches, manifest, routeModules$1) {
let links = await Promise.all(matches.map(async match => {

@@ -105,10 +117,10 @@ let mod = await routeModules.loadRouteModule(manifest.routes[match.route.id], routeModules$1);

}));
return links.flat(1).filter(isHtmlLinkDescriptor).filter(link => link.rel === "stylesheet" || link.rel === "preload").map(link => link.rel === "preload" ? {
return dedupeLinkDescriptors(links.flat(1).filter(isHtmlLinkDescriptor).filter(link => link.rel === "stylesheet" || link.rel === "preload").map(link => link.rel === "stylesheet" ? {
...link,
rel: "prefetch"
rel: "prefetch",
as: "style"
} : {
...link,
rel: "prefetch",
as: "style"
});
rel: "prefetch"
}));
}

@@ -169,3 +181,3 @@

let path = parsePathPatch(page);
return dedupeHrefs(matches.filter(match => manifest.routes[match.route.id].hasLoader).map(match => {
return dedupeHrefs(matches.filter(match => manifest.routes[match.route.id].hasLoader && !manifest.routes[match.route.id].hasClientLoader).map(match => {
let {

@@ -207,14 +219,25 @@ pathname,

}
function dedupe(descriptors, preloads) {
function sortKeys(obj) {
let sorted = {};
let keys = Object.keys(obj).sort();
for (let key of keys) {
sorted[key] = obj[key];
}
return sorted;
}
function dedupeLinkDescriptors(descriptors, preloads) {
let set = new Set();
let preloadsSet = new Set(preloads);
return descriptors.reduce((deduped, descriptor) => {
let alreadyModulePreload = !isPageLinkDescriptor(descriptor) && descriptor.as === "script" && descriptor.href && preloadsSet.has(descriptor.href);
let alreadyModulePreload = preloads && !isPageLinkDescriptor(descriptor) && descriptor.as === "script" && descriptor.href && preloadsSet.has(descriptor.href);
if (alreadyModulePreload) {
return deduped;
}
let str = JSON.stringify(descriptor);
if (!set.has(str)) {
set.add(str);
deduped.push(descriptor);
let key = JSON.stringify(sortKeys(descriptor));
if (!set.has(key)) {
set.add(key);
deduped.push({
key,
link: descriptor
});
}

@@ -232,10 +255,22 @@ return deduped;

exports.dedupe = dedupe;
// Detect if this browser supports <link rel="preload"> (or has it enabled).
// Originally added to handle the firefox `network.preload` config:
// https://bugzilla.mozilla.org/show_bug.cgi?id=1847811
let _isPreloadSupported;
function isPreloadSupported() {
if (_isPreloadSupported !== undefined) {
return _isPreloadSupported;
}
let el = document.createElement("link");
_isPreloadSupported = el.relList.supports("preload");
el = null;
return _isPreloadSupported;
}
exports.getDataLinkHrefs = getDataLinkHrefs;
exports.getLinksForMatches = getLinksForMatches;
exports.getKeyedLinksForMatches = getKeyedLinksForMatches;
exports.getKeyedPrefetchLinks = getKeyedPrefetchLinks;
exports.getModuleLinkHrefs = getModuleLinkHrefs;
exports.getNewMatchesForLinks = getNewMatchesForLinks;
exports.getStylesheetPrefetchLinks = getStylesheetPrefetchLinks;
exports.isHtmlLinkDescriptor = isHtmlLinkDescriptor;
exports.isPageLinkDescriptor = isPageLinkDescriptor;
exports.prefetchStyleLinks = prefetchStyleLinks;
/**
* @remix-run/react v0.0.0-nightly-ccefed3-20230621
* @remix-run/react v0.0.0-nightly-cd403b516-20240809
*

@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc.

@@ -1,4 +0,3 @@

import type { ComponentType } from "react";
import type { RouterState } from "@remix-run/router";
import type { DataRouteMatch, Params, Location, ShouldRevalidateFunction } from "react-router-dom";
import type { ComponentType, ReactElement } from "react";
import type { ActionFunction as RRActionFunction, ActionFunctionArgs as RRActionFunctionArgs, LoaderFunction as RRLoaderFunction, LoaderFunctionArgs as RRLoaderFunctionArgs, DataRouteMatch, Params, Location, ShouldRevalidateFunction } from "react-router-dom";
import type { LoaderFunction, SerializeFrom } from "@remix-run/server-runtime";

@@ -8,40 +7,57 @@ import type { AppData } from "./data";

import type { EntryRoute } from "./routes";
type RouteData = RouterState["loaderData"];
export interface RouteModules {
[routeId: string]: RouteModule;
[routeId: string]: RouteModule | undefined;
}
export interface RouteModule {
CatchBoundary?: CatchBoundaryComponent;
ErrorBoundary?: ErrorBoundaryComponent | V2_ErrorBoundaryComponent;
clientAction?: ClientActionFunction;
clientLoader?: ClientLoaderFunction;
ErrorBoundary?: ErrorBoundaryComponent;
HydrateFallback?: HydrateFallbackComponent;
Layout?: LayoutComponent;
default: RouteComponent;
handle?: RouteHandle;
links?: LinksFunction;
meta?: V1_MetaFunction | V1_HtmlMetaDescriptor | V2_MetaFunction | V2_MetaDescriptor[];
meta?: MetaFunction;
shouldRevalidate?: ShouldRevalidateFunction;
}
/**
* A React component that is rendered when the server throws a Response.
*
* @deprecated Please enable the v2_errorBoundary flag
*
* @see https://remix.run/route/catch-boundary
* A function that handles data mutations for a route on the client
*/
export type CatchBoundaryComponent = ComponentType<{}>;
export type ClientActionFunction = (args: ClientActionFunctionArgs) => ReturnType<RRActionFunction>;
/**
* A React component that is rendered when there is an error on a route.
*
* @deprecated Please enable the v2_errorBoundary flag
*
* @see https://remix.run/route/error-boundary
* Arguments passed to a route `clientAction` function
*/
export type ErrorBoundaryComponent = ComponentType<{
error: Error;
}>;
export type ClientActionFunctionArgs = RRActionFunctionArgs<undefined> & {
serverAction: <T = AppData>() => Promise<SerializeFrom<T>>;
};
/**
* V2 version of the ErrorBoundary that eliminates the distinction between
* Error and Catch Boundaries and behaves like RR 6.4 errorElement and captures
* errors with useRouteError()
* A function that loads data for a route on the client
*/
export type V2_ErrorBoundaryComponent = ComponentType;
export type ClientLoaderFunction = ((args: ClientLoaderFunctionArgs) => ReturnType<RRLoaderFunction>) & {
hydrate?: boolean;
};
/**
* Arguments passed to a route `clientLoader` function
*/
export type ClientLoaderFunctionArgs = RRLoaderFunctionArgs<undefined> & {
serverLoader: <T = AppData>() => Promise<SerializeFrom<T>>;
};
/**
* ErrorBoundary to display for this route
*/
export type ErrorBoundaryComponent = ComponentType;
/**
* `<Route HydrateFallback>` component to render on initial loads
* when client loaders are present
*/
export type HydrateFallbackComponent = ComponentType;
/**
* Optional, root-only `<Route Layout>` component to wrap the root content in.
* Useful for defining the <html>/<head>/<body> document shell shared by the
* Component, HydrateFallback, and ErrorBoundary
*/
export type LayoutComponent = ComponentType<{
children: ReactElement<unknown, ErrorBoundaryComponent | HydrateFallbackComponent | RouteComponent>;
}>;
/**
* A function that defines `<link>` tags to be inserted into the `<head>` of

@@ -55,55 +71,25 @@ * the document on route transitions.

}
/**
* A function that returns an object of name + content pairs to use for
* `<meta>` tags for a route. These tags will be merged with (and take
* precedence over) tags from parent routes.
*
* @see https://remix.run/route/meta
*/
export interface V1_MetaFunction {
(args: {
data: AppData;
parentsData: RouteData;
params: Params;
location: Location;
}): HtmlMetaDescriptor;
}
export type MetaFunction = V1_MetaFunction;
export interface RouteMatchWithMeta extends DataRouteMatch {
meta: V2_MetaDescriptor[];
}
export interface V2_MetaMatch<RouteId extends string = string, Loader extends LoaderFunction | unknown = unknown> {
export interface MetaMatch<RouteId extends string = string, Loader extends LoaderFunction | unknown = unknown> {
id: RouteId;
pathname: DataRouteMatch["pathname"];
data: Loader extends LoaderFunction ? SerializeFrom<Loader> : unknown;
handle?: unknown;
handle?: RouteHandle;
params: DataRouteMatch["params"];
meta: V2_MetaDescriptor[];
meta: MetaDescriptor[];
error?: unknown;
}
export type V2_MetaMatches<MatchLoaders extends Record<string, unknown> = Record<string, unknown>> = Array<{
[K in keyof MatchLoaders]: V2_MetaMatch<Exclude<K, number | symbol>, MatchLoaders[K]>;
export type MetaMatches<MatchLoaders extends Record<string, LoaderFunction | unknown> = Record<string, unknown>> = Array<{
[K in keyof MatchLoaders]: MetaMatch<Exclude<K, number | symbol>, MatchLoaders[K]>;
}[keyof MatchLoaders]>;
export interface V2_MetaArgs<Loader extends LoaderFunction | unknown = unknown, MatchLoaders extends Record<string, unknown> = Record<string, unknown>> {
export interface MetaArgs<Loader extends LoaderFunction | unknown = unknown, MatchLoaders extends Record<string, LoaderFunction | unknown> = Record<string, unknown>> {
data: (Loader extends LoaderFunction ? SerializeFrom<Loader> : AppData) | undefined;
params: Params;
location: Location;
matches: V2_MetaMatches<MatchLoaders>;
matches: MetaMatches<MatchLoaders>;
error?: unknown;
}
export interface V2_MetaFunction<Loader extends LoaderFunction | unknown = unknown, MatchLoaders extends Record<string, unknown> = Record<string, unknown>> {
(args: V2_MetaArgs<Loader, MatchLoaders>): V2_MetaDescriptor[] | undefined;
export interface MetaFunction<Loader extends LoaderFunction | unknown = unknown, MatchLoaders extends Record<string, LoaderFunction | unknown> = Record<string, unknown>> {
(args: MetaArgs<Loader, MatchLoaders>): MetaDescriptor[] | undefined;
}
/**
* A name/content pair used to render `<meta>` tags in a meta function for a
* route. The value can be either a string, which will render a single `<meta>`
* tag, or an array of strings that will render multiple tags with the same
* `name` attribute.
*/
export interface V1_HtmlMetaDescriptor {
charset?: "utf-8";
charSet?: "utf-8";
title?: string;
[name: string]: null | string | undefined | Record<string, string> | Array<Record<string, string> | string>;
}
export type HtmlMetaDescriptor = V1_HtmlMetaDescriptor;
export type V2_MetaDescriptor = {
export type MetaDescriptor = {
charSet: "utf-8";

@@ -146,25 +132,4 @@ } | {

*/
export type RouteHandle = any;
export type RouteHandle = unknown;
export declare function loadRouteModule(route: EntryRoute, routeModulesCache: RouteModules): Promise<RouteModule>;
/**
* @deprecated The `unstable_shouldReload` function has been removed, so this
* function will never run and route data will be revalidated on every request.
* Please update the function name to `shouldRevalidate` and use the
* `ShouldRevalidateFunction` interface.
*/
export interface ShouldReloadFunction {
(args: {
url: URL;
prevUrl: URL;
params: Params;
submission?: Submission;
}): boolean;
}
interface Submission {
action: string;
method: string;
formData: FormData;
encType: string;
key: string;
}
export {};
/**
* @remix-run/react v0.0.0-nightly-ccefed3-20230621
* @remix-run/react v0.0.0-nightly-cd403b516-20240809
*

@@ -34,48 +34,38 @@ * Copyright (c) Remix Software Inc.

/**
* A React component that is rendered when the server throws a Response.
*
* @deprecated Please enable the v2_errorBoundary flag
*
* @see https://remix.run/route/catch-boundary
* A function that handles data mutations for a route on the client
*/
/**
* A React component that is rendered when there is an error on a route.
*
* @deprecated Please enable the v2_errorBoundary flag
*
* @see https://remix.run/route/error-boundary
* Arguments passed to a route `clientAction` function
*/
/**
* V2 version of the ErrorBoundary that eliminates the distinction between
* Error and Catch Boundaries and behaves like RR 6.4 errorElement and captures
* errors with useRouteError()
* A function that loads data for a route on the client
*/
/**
* A function that defines `<link>` tags to be inserted into the `<head>` of
* the document on route transitions.
*
* @see https://remix.run/route/meta
* Arguments passed to a route `clientLoader` function
*/
/**
* A function that returns an object of name + content pairs to use for
* `<meta>` tags for a route. These tags will be merged with (and take
* precedence over) tags from parent routes.
*
* @see https://remix.run/route/meta
* ErrorBoundary to display for this route
*/
// TODO: Replace in v2
/**
* `<Route HydrateFallback>` component to render on initial loads
* when client loaders are present
*/
/**
* A name/content pair used to render `<meta>` tags in a meta function for a
* route. The value can be either a string, which will render a single `<meta>`
* tag, or an array of strings that will render multiple tags with the same
* `name` attribute.
* Optional, root-only `<Route Layout>` component to wrap the root content in.
* Useful for defining the <html>/<head>/<body> document shell shared by the
* Component, HydrateFallback, and ErrorBoundary
*/
// TODO: Replace in v2
/**
* A function that defines `<link>` tags to be inserted into the `<head>` of
* the document on route transitions.
*
* @see https://remix.run/route/meta
*/

@@ -101,6 +91,23 @@ /**

} catch (error) {
// User got caught in the middle of a deploy and the CDN no longer has the
// asset we're trying to import! Reload from the server and the user
// (should) get the new manifest--unless the developer purged the static
// assets, the manifest path, but not the documents 😬
// If we can't load the route it's likely one of 2 things:
// - User got caught in the middle of a deploy and the CDN no longer has the
// asset we're trying to import! Reload from the server and the user
// (should) get the new manifest--unless the developer purged the static
// assets, the manifest path, but not the documents 😬
// - Or, the asset trying to be imported has an error (usually in vite dev
// mode), so the best we can do here is log the error for visibility
// (via `Preserve log`) and reload
// Log the error so it can be accessed via the `Preserve Log` setting
console.error(`Error loading route module \`${route.module}\`, reloading page...`);
console.error(error);
if (window.__remixContext.isSpaMode &&
// @ts-expect-error
typeof undefined !== "undefined") {
// In SPA Mode (which implies vite) we don't want to perform a hard reload
// on dev-time errors since it's a vite compilation error and a reload is
// just going to fail with the same issue. Let the UI bubble to the error
// boundary and let them see the error in the overlay or the dev server log
throw error;
}
window.location.reload();

@@ -113,9 +120,2 @@ return new Promise(() => {

/**
* @deprecated The `unstable_shouldReload` function has been removed, so this
* function will never run and route data will be revalidated on every request.
* Please update the function name to `shouldRevalidate` and use the
* `ShouldRevalidateFunction` interface.
*/
exports.loadRouteModule = loadRouteModule;

@@ -0,3 +1,4 @@

import type { HydrationState } from "@remix-run/router";
import type { DataRouteObject } from "react-router-dom";
import type { RouteModules } from "./routeModules";
import type { RouteModule, RouteModules } from "./routeModules";
import type { FutureConfig } from "./entry";

@@ -17,11 +18,16 @@ export interface RouteManifest<Route> {

hasLoader: boolean;
hasCatchBoundary: boolean;
hasClientAction: boolean;
hasClientLoader: boolean;
hasErrorBoundary: boolean;
imports?: string[];
css?: string[];
module: string;
parentId?: string;
}
export declare function createServerRoutes(manifest: RouteManifest<EntryRoute>, routeModules: RouteModules, future: FutureConfig, parentId?: string, routesByParentId?: Record<string, Omit<EntryRoute, "children">[]>): DataRouteObject[];
export declare function createClientRoutesWithHMRRevalidationOptOut(needsRevalidation: Set<string>, manifest: RouteManifest<EntryRoute>, routeModulesCache: RouteModules, future: FutureConfig): DataRouteObject[];
export declare function createClientRoutes(manifest: RouteManifest<EntryRoute>, routeModulesCache: RouteModules, future: FutureConfig, parentId?: string, routesByParentId?: Record<string, Omit<EntryRoute, "children">[]>, needsRevalidation?: Set<string>): DataRouteObject[];
export declare function createServerRoutes(manifest: RouteManifest<EntryRoute>, routeModules: RouteModules, future: FutureConfig, isSpaMode: boolean, parentId?: string, routesByParentId?: Record<string, Omit<EntryRoute, "children">[]>, spaModeLazyPromise?: Promise<{
Component: () => null;
}>): DataRouteObject[];
export declare function createClientRoutesWithHMRRevalidationOptOut(needsRevalidation: Set<string>, manifest: RouteManifest<EntryRoute>, routeModulesCache: RouteModules, initialState: HydrationState, future: FutureConfig, isSpaMode: boolean): DataRouteObject[];
export declare function createClientRoutes(manifest: RouteManifest<EntryRoute>, routeModulesCache: RouteModules, initialState: HydrationState | null, future: FutureConfig, isSpaMode: boolean, parentId?: string, routesByParentId?: Record<string, Omit<EntryRoute, "children">[]>, needsRevalidation?: Set<string>): DataRouteObject[];
export declare function shouldHydrateRouteLoader(route: EntryRoute, routeModule: RouteModule, isSpaMode: boolean): boolean;
export {};
/**
* @remix-run/react v0.0.0-nightly-ccefed3-20230621
* @remix-run/react v0.0.0-nightly-cd403b516-20240809
*

@@ -16,2 +16,3 @@ * Copyright (c) Remix Software Inc.

var React = require('react');
var router = require('@remix-run/router');
var reactRouterDom = require('react-router-dom');

@@ -21,4 +22,5 @@ var routeModules = require('./routeModules.js');

var links = require('./links.js');
var errorBoundaries = require('./errorBoundaries.js');
var fallback = require('./fallback.js');
var invariant = require('./invariant.js');
var components = require('./components.js');

@@ -62,22 +64,61 @@ function _interopNamespace(e) {

}
function createServerRoutes(manifest, routeModules, future, parentId = "", routesByParentId = groupRoutesByParentId(manifest)) {
function getRouteComponents(route, routeModule, isSpaMode) {
let Component = getRouteModuleComponent(routeModule);
// HydrateFallback can only exist on the root route in SPA Mode
let HydrateFallback = routeModule.HydrateFallback && (!isSpaMode || route.id === "root") ? routeModule.HydrateFallback : route.id === "root" ? fallback.RemixRootDefaultHydrateFallback : undefined;
let ErrorBoundary = routeModule.ErrorBoundary ? routeModule.ErrorBoundary : route.id === "root" ? () => /*#__PURE__*/React__namespace.createElement(errorBoundaries.RemixRootDefaultErrorBoundary, {
error: reactRouterDom.useRouteError()
}) : undefined;
if (route.id === "root" && routeModule.Layout) {
return {
...(Component ? {
element: /*#__PURE__*/React__namespace.createElement(routeModule.Layout, null, /*#__PURE__*/React__namespace.createElement(Component, null))
} : {
Component
}),
...(ErrorBoundary ? {
errorElement: /*#__PURE__*/React__namespace.createElement(routeModule.Layout, null, /*#__PURE__*/React__namespace.createElement(ErrorBoundary, null))
} : {
ErrorBoundary
}),
...(HydrateFallback ? {
hydrateFallbackElement: /*#__PURE__*/React__namespace.createElement(routeModule.Layout, null, /*#__PURE__*/React__namespace.createElement(HydrateFallback, null))
} : {
HydrateFallback
})
};
}
return {
Component,
ErrorBoundary,
HydrateFallback
};
}
function createServerRoutes(manifest, routeModules, future, isSpaMode, parentId = "", routesByParentId = groupRoutesByParentId(manifest), spaModeLazyPromise = Promise.resolve({
Component: () => null
})) {
return (routesByParentId[parentId] || []).map(route => {
let hasErrorBoundary = future.v2_errorBoundary === true ? route.id === "root" || route.hasErrorBoundary : route.id === "root" || route.hasCatchBoundary || route.hasErrorBoundary;
let routeModule = routeModules[route.id];
invariant(routeModule, "No `routeModule` available to create server routes");
let dataRoute = {
...getRouteComponents(route, routeModule, isSpaMode),
caseSensitive: route.caseSensitive,
element: /*#__PURE__*/React__namespace.createElement(components.RemixRoute, {
id: route.id
}),
errorElement: hasErrorBoundary ? /*#__PURE__*/React__namespace.createElement(components.RemixRouteError, {
id: route.id
}) : undefined,
id: route.id,
index: route.index,
path: route.path,
handle: routeModules[route.id].handle
// Note: we don't need loader/action/shouldRevalidate on these routes
// since they're for a static render
handle: routeModule.handle,
// For SPA Mode, all routes are lazy except root. However we tell the
// router root is also lazy here too since we don't need a full
// implementation - we just need a `lazy` prop to tell the RR rendering
// where to stop which is always at the root route in SPA mode
lazy: isSpaMode ? () => spaModeLazyPromise : undefined,
// For partial hydration rendering, we need to indicate when the route
// has a loader/clientLoader, but it won't ever be called during the static
// render, so just give it a no-op function so we can render down to the
// proper fallback
loader: route.hasLoader || route.hasClientLoader ? () => null : undefined
// We don't need action/shouldRevalidate on these routes since they're
// for a static render
};
let children = createServerRoutes(manifest, routeModules, future, route.id, routesByParentId);
let children = createServerRoutes(manifest, routeModules, future, isSpaMode, route.id, routesByParentId, spaModeLazyPromise);
if (children.length > 0) dataRoute.children = children;

@@ -87,24 +128,213 @@ return dataRoute;

}
function createClientRoutes(manifest, routeModulesCache, future, parentId = "", routesByParentId = groupRoutesByParentId(manifest), needsRevalidation) {
function createClientRoutesWithHMRRevalidationOptOut(needsRevalidation, manifest, routeModulesCache, initialState, future, isSpaMode) {
return createClientRoutes(manifest, routeModulesCache, initialState, future, isSpaMode, "", groupRoutesByParentId(manifest), needsRevalidation);
}
function preventInvalidServerHandlerCall(type, route, isSpaMode) {
if (isSpaMode) {
let fn = type === "action" ? "serverAction()" : "serverLoader()";
let msg = `You cannot call ${fn} in SPA Mode (routeId: "${route.id}")`;
console.error(msg);
throw new router.UNSAFE_ErrorResponseImpl(400, "Bad Request", new Error(msg), true);
}
let fn = type === "action" ? "serverAction()" : "serverLoader()";
let msg = `You are trying to call ${fn} on a route that does not have a server ` + `${type} (routeId: "${route.id}")`;
if (type === "loader" && !route.hasLoader || type === "action" && !route.hasAction) {
console.error(msg);
throw new router.UNSAFE_ErrorResponseImpl(400, "Bad Request", new Error(msg), true);
}
}
function noActionDefinedError(type, routeId) {
let article = type === "clientAction" ? "a" : "an";
let msg = `Route "${routeId}" does not have ${article} ${type}, but you are trying to ` + `submit to it. To fix this, please add ${article} \`${type}\` function to the route`;
console.error(msg);
throw new router.UNSAFE_ErrorResponseImpl(405, "Method Not Allowed", new Error(msg), true);
}
function createClientRoutes(manifest, routeModulesCache, initialState, future, isSpaMode, parentId = "", routesByParentId = groupRoutesByParentId(manifest), needsRevalidation) {
return (routesByParentId[parentId] || []).map(route => {
let hasErrorBoundary = future.v2_errorBoundary === true ? route.id === "root" || route.hasErrorBoundary : route.id === "root" || route.hasCatchBoundary || route.hasErrorBoundary;
let routeModule = routeModulesCache[route.id];
// Fetch data from the server either via single fetch or the standard `?_data`
// request. Unwrap it when called via `serverLoader`/`serverAction` in a
// client handler, otherwise return the raw response for the router to unwrap
async function fetchServerHandlerAndMaybeUnwrap(request, unwrap, singleFetch) {
if (typeof singleFetch === "function") {
let result = await singleFetch();
return result;
}
let result = await fetchServerHandler(request, route);
return unwrap ? unwrapServerResponse(result) : result;
}
function fetchServerLoader(request, unwrap, singleFetch) {
if (!route.hasLoader) return Promise.resolve(null);
return fetchServerHandlerAndMaybeUnwrap(request, unwrap, singleFetch);
}
function fetchServerAction(request, unwrap, singleFetch) {
if (!route.hasAction) {
throw noActionDefinedError("action", route.id);
}
return fetchServerHandlerAndMaybeUnwrap(request, unwrap, singleFetch);
}
async function prefetchStylesAndCallHandler(handler) {
// Only prefetch links if we exist in the routeModulesCache (critical modules
// and navigating back to pages previously loaded via route.lazy). Initial
// execution of route.lazy (when the module is not in the cache) will handle
// prefetching style links via loadRouteModuleWithBlockingLinks.
let cachedModule = routeModulesCache[route.id];
let linkPrefetchPromise = cachedModule ? links.prefetchStyleLinks(route, cachedModule) : Promise.resolve();
try {
return handler();
} finally {
await linkPrefetchPromise;
}
}
let dataRoute = {
caseSensitive: route.caseSensitive,
element: /*#__PURE__*/React__namespace.createElement(components.RemixRoute, {
id: route.id
}),
errorElement: hasErrorBoundary ? /*#__PURE__*/React__namespace.createElement(components.RemixRouteError, {
id: route.id
}) : undefined,
id: route.id,
index: route.index,
path: route.path,
// handle gets added in via useMatches since we aren't guaranteed to
// have the route module available here
handle: undefined,
loader: createDataFunction(route, routeModulesCache, false),
action: createDataFunction(route, routeModulesCache, true),
shouldRevalidate: createShouldRevalidate(route, routeModulesCache, needsRevalidation)
path: route.path
};
let children = createClientRoutes(manifest, routeModulesCache, future, route.id, routesByParentId, needsRevalidation);
if (routeModule) {
var _initialState$loaderD, _initialState$errors, _routeModule$clientLo;
// Use critical path modules directly
Object.assign(dataRoute, {
...dataRoute,
...getRouteComponents(route, routeModule, isSpaMode),
handle: routeModule.handle,
shouldRevalidate: needsRevalidation ? wrapShouldRevalidateForHdr(route.id, routeModule.shouldRevalidate, needsRevalidation) : routeModule.shouldRevalidate
});
let initialData = initialState === null || initialState === void 0 ? void 0 : (_initialState$loaderD = initialState.loaderData) === null || _initialState$loaderD === void 0 ? void 0 : _initialState$loaderD[route.id];
let initialError = initialState === null || initialState === void 0 ? void 0 : (_initialState$errors = initialState.errors) === null || _initialState$errors === void 0 ? void 0 : _initialState$errors[route.id];
let isHydrationRequest = needsRevalidation == null && (((_routeModule$clientLo = routeModule.clientLoader) === null || _routeModule$clientLo === void 0 ? void 0 : _routeModule$clientLo.hydrate) === true || !route.hasLoader);
dataRoute.loader = async ({
request,
params
}, singleFetch) => {
try {
let result = await prefetchStylesAndCallHandler(async () => {
invariant(routeModule, "No `routeModule` available for critical-route loader");
if (!routeModule.clientLoader) {
if (isSpaMode) return null;
// Call the server when no client loader exists
return fetchServerLoader(request, false, singleFetch);
}
return routeModule.clientLoader({
request,
params,
async serverLoader() {
preventInvalidServerHandlerCall("loader", route, isSpaMode);
// On the first call, resolve with the server result
if (isHydrationRequest) {
if (initialError !== undefined) {
throw initialError;
}
return initialData;
}
// Call the server loader for client-side navigations
return fetchServerLoader(request, true, singleFetch);
}
});
});
return result;
} finally {
// Whether or not the user calls `serverLoader`, we only let this
// stick around as true for one loader call
isHydrationRequest = false;
}
};
// Let React Router know whether to run this on hydration
dataRoute.loader.hydrate = shouldHydrateRouteLoader(route, routeModule, isSpaMode);
dataRoute.action = ({
request,
params
}, singleFetch) => {
return prefetchStylesAndCallHandler(async () => {
invariant(routeModule, "No `routeModule` available for critical-route action");
if (!routeModule.clientAction) {
if (isSpaMode) {
throw noActionDefinedError("clientAction", route.id);
}
return fetchServerAction(request, false, singleFetch);
}
return routeModule.clientAction({
request,
params,
async serverAction() {
preventInvalidServerHandlerCall("action", route, isSpaMode);
return fetchServerAction(request, true, singleFetch);
}
});
});
};
} else {
// If the lazy route does not have a client loader/action we want to call
// the server loader/action in parallel with the module load so we add
// loader/action as static props on the route
if (!route.hasClientLoader) {
dataRoute.loader = ({
request
}, singleFetch) => prefetchStylesAndCallHandler(() => {
if (isSpaMode) return Promise.resolve(null);
return fetchServerLoader(request, false, singleFetch);
});
}
if (!route.hasClientAction) {
dataRoute.action = ({
request
}, singleFetch) => prefetchStylesAndCallHandler(() => {
if (isSpaMode) {
throw noActionDefinedError("clientAction", route.id);
}
return fetchServerAction(request, false, singleFetch);
});
}
// Load all other modules via route.lazy()
dataRoute.lazy = async () => {
let mod = await loadRouteModuleWithBlockingLinks(route, routeModulesCache);
let lazyRoute = {
...mod
};
if (mod.clientLoader) {
let clientLoader = mod.clientLoader;
lazyRoute.loader = (args, singleFetch) => clientLoader({
...args,
async serverLoader() {
preventInvalidServerHandlerCall("loader", route, isSpaMode);
return fetchServerLoader(args.request, true, singleFetch);
}
});
}
if (mod.clientAction) {
let clientAction = mod.clientAction;
lazyRoute.action = (args, singleFetch) => clientAction({
...args,
async serverAction() {
preventInvalidServerHandlerCall("action", route, isSpaMode);
return fetchServerAction(args.request, true, singleFetch);
}
});
}
if (needsRevalidation) {
lazyRoute.shouldRevalidate = wrapShouldRevalidateForHdr(route.id, mod.shouldRevalidate, needsRevalidation);
}
return {
...(lazyRoute.loader ? {
loader: lazyRoute.loader
} : {}),
...(lazyRoute.action ? {
action: lazyRoute.action
} : {}),
hasErrorBoundary: lazyRoute.hasErrorBoundary,
shouldRevalidate: lazyRoute.shouldRevalidate,
handle: lazyRoute.handle,
// No need to wrap these in layout since the root route is never
// loaded via route.lazy()
Component: lazyRoute.Component,
ErrorBoundary: lazyRoute.ErrorBoundary
};
};
}
let children = createClientRoutes(manifest, routeModulesCache, initialState, future, isSpaMode, route.id, routesByParentId, needsRevalidation);
if (children.length > 0) dataRoute.children = children;

@@ -114,19 +344,13 @@ return dataRoute;

}
function createShouldRevalidate(route, routeModules, needsRevalidation) {
// When an HMR / HDR update happens we opt out of all user-defined
// revalidation logic and force a revalidation on the first call
function wrapShouldRevalidateForHdr(routeId, routeShouldRevalidate, needsRevalidation) {
let handledRevalidation = false;
return function (arg) {
let module = routeModules[route.id];
invariant(module, `Expected route module to be loaded for ${route.id}`);
// When an HMR / HDR update happens we opt out of all user-defined
// revalidation logic and the do as the dev server tells us the first
// time router.revalidate() is called.
if (needsRevalidation !== undefined && !handledRevalidation) {
return arg => {
if (!handledRevalidation) {
handledRevalidation = true;
return needsRevalidation.has(route.id);
return needsRevalidation.has(routeId);
}
if (module.shouldRevalidate) {
return module.shouldRevalidate(arg);
}
return arg.defaultShouldRevalidate;
return routeShouldRevalidate ? routeShouldRevalidate(arg) : arg.defaultShouldRevalidate;
};

@@ -136,36 +360,48 @@ }

let routeModule = await routeModules.loadRouteModule(route, routeModules$1);
await links.prefetchStyleLinks(routeModule);
return routeModule;
await links.prefetchStyleLinks(route, routeModule);
// Include all `browserSafeRouteExports` fields, except `HydrateFallback`
// since those aren't used on lazily loaded routes
return {
Component: getRouteModuleComponent(routeModule),
ErrorBoundary: routeModule.ErrorBoundary,
clientAction: routeModule.clientAction,
clientLoader: routeModule.clientLoader,
handle: routeModule.handle,
links: routeModule.links,
meta: routeModule.meta,
shouldRevalidate: routeModule.shouldRevalidate
};
}
function createDataFunction(route, routeModules, isAction) {
return async ({
request
}) => {
let routeModulePromise = loadRouteModuleWithBlockingLinks(route, routeModules);
try {
if (isAction && !route.hasAction) {
let msg = `Route "${route.id}" does not have an action, but you are trying ` + `to submit to it. To fix this, please add an \`action\` function to the route`;
console.error(msg);
throw new Error(msg);
} else if (!isAction && !route.hasLoader) {
return null;
}
let result = await data.fetchData(request, route.id);
if (result instanceof Error) {
throw result;
}
if (data.isRedirectResponse(result)) {
throw getRedirect(result);
}
if (data.isCatchResponse(result)) {
throw result;
}
if (data.isDeferredResponse(result) && result.body) {
return await data.parseDeferredReadableStream(result.body);
}
return result;
} finally {
await routeModulePromise;
async function fetchServerHandler(request, route) {
let result = await data.fetchData(request, route.id);
if (result instanceof Error) {
throw result;
}
if (data.isRedirectResponse(result)) {
throw getRedirect(result);
}
if (data.isCatchResponse(result)) {
throw result;
}
if (data.isDeferredResponse(result) && result.body) {
return await data.parseDeferredReadableStream(result.body);
}
return result;
}
function unwrapServerResponse(result) {
if (data.isDeferredData(result)) {
return result.data;
}
if (data.isResponse(result)) {
let contentType = result.headers.get("Content-Type");
// Check between word boundaries instead of startsWith() due to the last
// paragraph of https://httpwg.org/specs/rfc9110.html#field.content-type
if (contentType && /\bapplication\/json\b/.test(contentType)) {
return result.json();
} else {
return result.text();
}
};
}
return result;
}

@@ -180,2 +416,10 @@ function getRedirect(response) {

}
let reloadDocument = response.headers.get("X-Remix-Reload-Document");
if (reloadDocument) {
headers["X-Remix-Reload-Document"] = reloadDocument;
}
let replace = response.headers.get("X-Remix-Replace");
if (replace) {
headers["X-Remix-Replace"] = replace;
}
return reactRouterDom.redirect(url, {

@@ -187,3 +431,20 @@ status,

// Our compiler generates the default export as `{}` when no default is provided,
// which can lead us to trying to use that as a Component in RR and calling
// createElement on it. Patching here as a quick fix and hoping it's no longer
// an issue in Vite.
function getRouteModuleComponent(routeModule) {
if (routeModule.default == null) return undefined;
let isEmptyObject = typeof routeModule.default === "object" && Object.keys(routeModule.default).length === 0;
if (!isEmptyObject) {
return routeModule.default;
}
}
function shouldHydrateRouteLoader(route, routeModule, isSpaMode) {
return isSpaMode && route.id !== "root" || routeModule.clientLoader != null && (routeModule.clientLoader.hydrate === true || route.hasLoader !== true);
}
exports.createClientRoutes = createClientRoutes;
exports.createClientRoutesWithHMRRevalidationOptOut = createClientRoutesWithHMRRevalidationOptOut;
exports.createServerRoutes = createServerRoutes;
exports.shouldHydrateRouteLoader = shouldHydrateRouteLoader;

@@ -0,1 +1,2 @@

import * as React from "react";
import type { ScrollRestorationProps as ScrollRestorationPropsRR } from "react-router-dom";

@@ -11,2 +12,2 @@ import type { ScriptProps } from "./components";

getKey?: ScrollRestorationPropsRR["getKey"];
}): JSX.Element;
}): React.JSX.Element | null;
/**
* @remix-run/react v0.0.0-nightly-ccefed3-20230621
* @remix-run/react v0.0.0-nightly-cd403b516-20240809
*

@@ -52,4 +52,7 @@ * Copyright (c) Remix Software Inc.

}) {
let {
isSpaMode
} = components.useRemixContext();
let location = reactRouterDom.useLocation();
let matches = components.useMatches();
let matches = reactRouterDom.useMatches();
reactRouterDom.UNSAFE_useScrollRestoration({

@@ -74,2 +77,8 @@ getKey,

[]);
// In SPA Mode, there's nothing to restore on initial render since we didn't
// render anything on the server
if (isSpaMode) {
return null;
}
let restoreScroll = ((STORAGE_KEY, restoreKey) => {

@@ -76,0 +85,0 @@ if (!window.history.state || !window.history.state.key) {

@@ -7,2 +7,3 @@ import type { ReactElement } from "react";

abortDelay?: number;
nonce?: string;
}

@@ -14,2 +15,2 @@ /**

*/
export declare function RemixServer({ context, url, abortDelay, }: RemixServerProps): ReactElement;
export declare function RemixServer({ context, url, abortDelay, nonce, }: RemixServerProps): ReactElement;
/**
* @remix-run/react v0.0.0-nightly-ccefed3-20230621
* @remix-run/react v0.0.0-nightly-cd403b516-20240809
*

@@ -20,2 +20,3 @@ * Copyright (c) Remix Software Inc.

var routes = require('./routes.js');
var singleFetch = require('./single-fetch.js');

@@ -50,3 +51,4 @@ function _interopNamespace(e) {

url,
abortDelay
abortDelay,
nonce
}) {

@@ -59,17 +61,47 @@ if (typeof url === "string") {

routeModules,
criticalCss,
serverHandoffString
} = context;
let routes$1 = routes.createServerRoutes(manifest.routes, routeModules, context.future);
let router = server.createStaticRouter(routes$1, context.staticHandlerContext);
return /*#__PURE__*/React__namespace.createElement(components.RemixContext.Provider, {
let routes$1 = routes.createServerRoutes(manifest.routes, routeModules, context.future, context.isSpaMode);
// Create a shallow clone of `loaderData` we can mutate for partial hydration.
// When a route exports a `clientLoader` and a `HydrateFallback`, we want to
// render the fallback on the server so we clear our the `loaderData` during SSR.
// Is it important not to change the `context` reference here since we use it
// for context._deepestRenderedBoundaryId tracking
context.staticHandlerContext.loaderData = {
...context.staticHandlerContext.loaderData
};
for (let match of context.staticHandlerContext.matches) {
let routeId = match.route.id;
let route = routeModules[routeId];
let manifestRoute = context.manifest.routes[routeId];
// Clear out the loaderData to avoid rendering the route component when the
// route opted into clientLoader hydration and either:
// * gave us a HydrateFallback
// * or doesn't have a server loader and we have no data to render
if (route && routes.shouldHydrateRouteLoader(manifestRoute, route, context.isSpaMode) && (route.HydrateFallback || !manifestRoute.hasLoader)) {
context.staticHandlerContext.loaderData[routeId] = undefined;
}
}
let router = server.createStaticRouter(routes$1, context.staticHandlerContext, {
future: {
v7_partialHydration: true,
v7_relativeSplatPath: context.future.v3_relativeSplatPath
}
});
return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement(components.RemixContext.Provider, {
value: {
manifest,
routeModules,
criticalCss,
serverHandoffString,
future: context.future,
abortDelay
isSpaMode: context.isSpaMode,
serializeError: context.serializeError,
abortDelay,
renderMeta: context.renderMeta
}
}, /*#__PURE__*/React__namespace.createElement(errorBoundaries.RemixErrorBoundary, {
location: router.state.location,
component: errorBoundaries.RemixRootDefaultErrorBoundary
location: router.state.location
}, /*#__PURE__*/React__namespace.createElement(server.StaticRouterProvider, {

@@ -79,5 +111,11 @@ router: router,

hydrate: false
})));
}))), context.future.unstable_singleFetch && context.serverHandoffStream ? /*#__PURE__*/React__namespace.createElement(React__namespace.Suspense, null, /*#__PURE__*/React__namespace.createElement(singleFetch.StreamTransfer, {
context: context,
identifier: 0,
reader: context.serverHandoffStream.getReader(),
textDecoder: new TextDecoder(),
nonce: nonce
})) : null);
}
exports.RemixServer = RemixServer;
export declare function warnOnce(condition: boolean, message: string): void;
export declare function logDeprecationOnce(message: string, key?: string): void;
MIT License
Copyright (c) Remix Software Inc. 2020-2021
Copyright (c) Shopify Inc. 2022-2023
Copyright (c) Shopify Inc. 2022-2024

@@ -6,0 +6,0 @@ Permission is hereby granted, free of charge, to any person obtaining a copy

{
"name": "@remix-run/react",
"version": "0.0.0-nightly-ccefed3-20230621",
"version": "0.0.0-nightly-cd403b516-20240809",
"description": "React DOM bindings for Remix",

@@ -19,27 +19,42 @@ "bugs": {

"dependencies": {
"@remix-run/router": "1.6.3",
"react-router-dom": "6.13.0"
"@remix-run/router": "1.19.0",
"@remix-run/server-runtime": "0.0.0-nightly-cd403b516-20240809",
"react-router": "6.26.0",
"react-router-dom": "6.26.0",
"turbo-stream": "2.2.0"
},
"devDependencies": {
"@remix-run/server-runtime": "0.0.0-nightly-ccefed3-20230621",
"@testing-library/jest-dom": "^5.16.2",
"@remix-run/node": "0.0.0-nightly-cd403b516-20240809",
"@remix-run/react": "0.0.0-nightly-cd403b516-20240809",
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.3.0",
"@types/react": "^18.0.15",
"abort-controller": "^3.0.0",
"@types/react": "^18.2.20",
"jest-environment-jsdom": "^29.6.4",
"react": "^18.2.0",
"react-dom": "^18.2.0"
"react-dom": "^18.2.0",
"typescript": "^5.1.6"
},
"peerDependencies": {
"react": ">=16.8.0",
"react-dom": ">=16.8.0"
"react": "^18.0.0",
"react-dom": "^18.0.0",
"typescript": "^5.1.0"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
},
"engines": {
"node": ">=14.0.0"
"node": ">=18.0.0"
},
"files": [
"dist/",
"future/",
"CHANGELOG.md",
"LICENSE.md",
"README.md"
]
}
],
"scripts": {
"tsc": "tsc"
}
}
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc