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
1033
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-9af8868-20230412 to 0.0.0-nightly-9bc6d9517-20241206

dist/esm/fallback.js

2

dist/_virtual/_rollupPluginBabelHelpers.js
/**
* @remix-run/react v0.0.0-nightly-9af8868-20230412
* @remix-run/react v0.0.0-nightly-9bc6d9517-20241206
*

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

@@ -1,17 +0,26 @@

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";
declare global {
var __remixContext: {
basename?: string;
state: HydrationState;
criticalCss?: string;
future: FutureConfig;
isSpaMode: boolean;
stream: ReadableStream<Uint8Array> | undefined;
streamController: ReadableStreamDefaultController<Uint8Array>;
a?: number;
dev?: {
liveReloadPort?: number;
port?: number;
hmrRuntime?: string;
};
};
var __remixRouter: Router;
var __remixRouteModules: RouteModules;
var __remixManifest: EntryContext["manifest"];
var __remixManifest: AssetsManifest;
var __remixRevalidation: number | undefined;
var __remixHdrActive: boolean;
var __remixClearCriticalCss: (() => void) | undefined;
var $RefreshRuntime$: {

@@ -23,7 +32,2 @@ performReactRefresh: () => void;

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

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

/**
* @remix-run/react v0.0.0-nightly-9af8868-20230412
* @remix-run/react v0.0.0-nightly-9bc6d9517-20241206
*

@@ -15,5 +15,6 @@ * 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');
var shim = require('use-sync-external-store/shim');
var components = require('./components.js');

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

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

@@ -47,3 +51,20 @@ function _interopNamespace(e) {

/* eslint-enable prefer-let/prefer-let */
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;
});

@@ -57,23 +78,137 @@ /**

if (!router) {
let routes$1 = routes.createClientRoutes(window.__remixManifest.routes, window.__remixRouteModules, window.__remixContext.future);
let hydrationData = window.__remixContext.state;
if (hydrationData && hydrationData.errors) {
// When single fetch is enabled, we need to suspend until the initial state
// snapshot is decoded into window.__remixContext.state
if (window.__remixContext.future.v3_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, {
// 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.v3_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,
dataStrategy: window.__remixContext.future.v3_singleFetch ? singleFetch.getSingleFetchDataStrategy(window.__remixManifest, window.__remixRouteModules, () => router) : undefined,
patchRoutesOnNavigation: fogOfWar.getPatchRoutesOnNavigationFunction(window.__remixManifest, window.__remixRouteModules, window.__remixContext.future, window.__remixContext.isSpaMode, window.__remixContext.basename)
});
// 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.
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.
let [location, setLocation] = React__namespace.useState(router.state.location);
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();
}
}, []);
React__namespace.useLayoutEffect(() => {
return router.subscribe(newState => {
if (newState.location !== location) {
setLocation(newState.location);
}
});
}, [location]);
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

@@ -83,18 +218,26 @@ // boundary also throws and we need to bubble up outside of the router entirely.

// out of there
let location = shim.useSyncExternalStore(router.subscribe, () => router.state.location, () => router.state.location);
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
})));
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.v3_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";
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;
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-9af8868-20230412
* @remix-run/react v0.0.0-nightly-9bc6d9517-20241206
*

@@ -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,83 +68,18 @@ function _interopNamespace(e) {

////////////////////////////////////////////////////////////////////////////////
// RemixRoute
function RemixRoute({
id
}) {
let {
routeModules
} = 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
} = routeModules[id];
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 ? void 0 : tError.error) instanceof 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, {
component: CatchBoundary,
catch: error
});
}
}
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
*/

@@ -163,2 +97,3 @@

} = theirElementProps;
let ref = React__namespace.useRef(null);
React__namespace.useEffect(() => {

@@ -168,2 +103,16 @@ if (prefetch === "render") {

}
if (prefetch === "viewport") {
let callback = entries => {
entries.forEach(entry => {
setShouldPrefetch(entry.isIntersecting);
});
};
let observer = new IntersectionObserver(callback, {
threshold: 0.5
});
if (ref.current) observer.observe(ref.current);
return () => {
observer.disconnect();
};
}
}, [prefetch]);

@@ -191,3 +140,3 @@ let setIntent = () => {

}, [maybePrefetch]);
return [shouldPrefetch, {
return [shouldPrefetch, ref, {
onFocus: composeEventHandlers(onFocus, setIntent),

@@ -201,5 +150,8 @@ onBlur: composeEventHandlers(onBlur, cancelIntent),

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".
*

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

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

@@ -216,7 +169,8 @@ }, forwardedRef) => {

let href = reactRouterDom.useHref(to);
let [shouldPrefetch, prefetchHandlers] = usePrefetchBehavior(prefetch, props);
return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement(reactRouterDom.NavLink, _rollupPluginBabelHelpers["extends"]({
ref: forwardedRef,
to: to
}, props, prefetchHandlers)), shouldPrefetch && !isAbsolute ? /*#__PURE__*/React__namespace.createElement(PrefetchPageLinks, {
let [shouldPrefetch, ref, prefetchHandlers] = usePrefetchBehavior(prefetch, props);
return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement(reactRouterDom.NavLink, _rollupPluginBabelHelpers["extends"]({}, props, prefetchHandlers, {
ref: mergeRefs(forwardedRef, ref),
to: to,
"data-discover": getDiscoverAttr(discover, isAbsolute, props.reloadDocument)
})), shouldPrefetch && !isAbsolute ? /*#__PURE__*/React__namespace.createElement(PrefetchPageLinks, {
page: href

@@ -236,2 +190,3 @@ }) : null);

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

@@ -241,7 +196,8 @@ }, forwardedRef) => {

let href = reactRouterDom.useHref(to);
let [shouldPrefetch, prefetchHandlers] = usePrefetchBehavior(prefetch, props);
return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement(reactRouterDom.Link, _rollupPluginBabelHelpers["extends"]({
ref: forwardedRef,
to: to
}, props, prefetchHandlers)), shouldPrefetch && !isAbsolute ? /*#__PURE__*/React__namespace.createElement(PrefetchPageLinks, {
let [shouldPrefetch, ref, prefetchHandlers] = usePrefetchBehavior(prefetch, props);
return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, /*#__PURE__*/React__namespace.createElement(reactRouterDom.Link, _rollupPluginBabelHelpers["extends"]({}, props, prefetchHandlers, {
ref: mergeRefs(forwardedRef, ref),
to: to,
"data-discover": getDiscoverAttr(discover, isAbsolute, props.reloadDocument)
})), shouldPrefetch && !isAbsolute ? /*#__PURE__*/React__namespace.createElement(PrefetchPageLinks, {
page: href

@@ -251,2 +207,19 @@ }) : null);

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) {

@@ -260,7 +233,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;
}
/**

@@ -273,53 +258,29 @@ * Renders the `<link>` tags for the current routes.

let {
isSpaMode,
manifest,
routeModules
routeModules,
criticalCss
} = useRemixContext();
let {
matches
errors,
matches: routerMatches
} = useDataRouterStateContext();
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

@@ -339,3 +300,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) {

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

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

@@ -356,7 +317,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);
}
});

@@ -367,3 +330,3 @@ return () => {

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

@@ -377,10 +340,51 @@ function PrefetchPageLinksImpl({

let {
manifest
future,
manifest,
routeModules
} = useRemixContext();
let {
loaderData,
matches
} = useDataRouterStateContext();
let newMatchesForData = React__namespace.useMemo(() => links.getNewMatchesForLinks(page, nextMatches, matches, manifest, location, "data"), [page, nextMatches, matches, manifest, location]);
let newMatchesForAssets = React__namespace.useMemo(() => links.getNewMatchesForLinks(page, nextMatches, matches, manifest, location, "assets"), [page, nextMatches, matches, manifest, location]);
let dataHrefs = React__namespace.useMemo(() => links.getDataLinkHrefs(page, newMatchesForData, manifest), [newMatchesForData, page, manifest]);
let newMatchesForData = React__namespace.useMemo(() => links.getNewMatchesForLinks(page, nextMatches, matches, manifest, location, future, "data"), [page, nextMatches, matches, manifest, location, future]);
let dataHrefs = React__namespace.useMemo(() => {
if (!future.v3_singleFetch) {
return links.getDataLinkHrefs(page, newMatchesForData, manifest);
}
if (page === location.pathname + location.search + location.hash) {
// Because we opt-into revalidation, don't compute this for the current page
// since it would always trigger a prefetch of the existing loaders
return [];
}
// Single-fetch is harder :)
// This parallels the logic in the single fetch data strategy
let routesParams = new Set();
let foundOptOutRoute = false;
nextMatches.forEach(m => {
var _routeModules$m$route;
if (!manifest.routes[m.route.id].hasLoader) {
return;
}
if (!newMatchesForData.some(m2 => m2.route.id === m.route.id) && m.route.id in loaderData && (_routeModules$m$route = routeModules[m.route.id]) !== null && _routeModules$m$route !== void 0 && _routeModules$m$route.shouldRevalidate) {
foundOptOutRoute = true;
} else if (manifest.routes[m.route.id].hasClientLoader) {
foundOptOutRoute = true;
} else {
routesParams.add(m.route.id);
}
});
if (routesParams.size === 0) {
return [];
}
let url = singleFetch.singleFetchUrl(page);
// When one or more routes have opted out, we add a _routes param to
// limit the loaders to those that have a server loader and did not
// opt out
if (foundOptOutRoute && routesParams.size > 0) {
url.searchParams.set("_routes", nextMatches.filter(m => routesParams.has(m.route.id)).map(m => m.route.id).join(","));
}
return [url.pathname + url.search];
}, [future.v3_singleFetch, loaderData, location, manifest, newMatchesForData, nextMatches, page, routeModules]);
let newMatchesForAssets = React__namespace.useMemo(() => links.getNewMatchesForLinks(page, nextMatches, matches, manifest, location, future, "assets"), [page, nextMatches, matches, manifest, location, future]);
let moduleHrefs = React__namespace.useMemo(() => links.getModuleLinkHrefs(newMatchesForAssets, manifest), [newMatchesForAssets, manifest]);

@@ -390,3 +394,3 @@

// just the manifest like the other links in here.
let styleLinks = usePrefetchedStylesheets(newMatchesForAssets);
let keyedPrefetchLinks = useKeyedPrefetchLinks(newMatchesForAssets);
return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, dataHrefs.map(href => /*#__PURE__*/React__namespace.createElement("link", _rollupPluginBabelHelpers["extends"]({

@@ -401,3 +405,6 @@ key: href,

href: href
}, linkProps))), styleLinks.map(link =>
}, linkProps))), keyedPrefetchLinks.map(({
key,
link
}) =>
/*#__PURE__*/

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

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

@@ -413,102 +420,22 @@ }

/**
* 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
} = useRemixContext();
let {
matches,
errors,
matches: routerMatches,
loaderData
} = useDataRouterStateContext();
let location = reactRouterDom.useLocation();
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 {
matches: _matches,
loaderData
} = useDataRouterStateContext();
let location = reactRouterDom.useLocation();
let meta = [];

@@ -531,10 +458,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
};

@@ -547,7 +467,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.

@@ -558,6 +479,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");
}

@@ -574,4 +492,6 @@ match.meta = routeMeta;

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

@@ -583,4 +503,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));
}

@@ -593,3 +513,3 @@ if ("title" in metaProps) {

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

@@ -604,13 +524,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;
}
}

@@ -625,8 +546,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) {

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

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

@@ -664,13 +583,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.v3_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.v3_singleFetch ? undefined : staticContext === null || staticContext === void 0 ? void 0 : staticContext.activeDeferreds;
// This sets up the __remixContext with utility functions used by the

@@ -684,3 +652,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.

@@ -697,3 +665,6 @@ // - __remixContext.a is the active number of deferred scripts that should be rendered to match

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

@@ -704,15 +675,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);
}

@@ -723,4 +684,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(",")}};

@@ -747,18 +711,10 @@

deferredScripts.push( /*#__PURE__*/React__namespace.createElement(DeferredHydrationScript, {
key: i
key: i,
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];

@@ -768,7 +724,7 @@ return (route.imports || []).concat([route.module]);

let preloads = isHydrated ? [] : manifest.entry.imports.concat(routePreloads);
return /*#__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
}), /*#__PURE__*/React__namespace.createElement("link", {
}) : null, /*#__PURE__*/React__namespace.createElement("link", {
rel: "modulepreload",

@@ -782,3 +738,3 @@ href: manifest.entry.module,

crossOrigin: props.crossOrigin
})), !isHydrated && initialScripts, !isHydrated && deferredScripts);
})), initialScripts, deferredScripts);
}

@@ -788,3 +744,6 @@ function DeferredHydrationScript({

deferredData,
routeId
routeId,
scriptProps,
serializeData,
serializeError
}) {

@@ -799,3 +758,3 @@ if (typeof document === "undefined" && deferredData && dataKey && routeId) {

// To reproduce a hydration mismatch, just render null as a fallback.
typeof document === "undefined" && deferredData && dataKey && routeId ? null : /*#__PURE__*/React__namespace.createElement("script", {
typeof document === "undefined" && deferredData && dataKey && routeId ? null : /*#__PURE__*/React__namespace.createElement("script", _rollupPluginBabelHelpers["extends"]({}, scriptProps, {
async: true,

@@ -806,3 +765,3 @@ suppressHydrationWarning: true,

}
})
}))
}, typeof document === "undefined" && deferredData && dataKey && routeId ? /*#__PURE__*/React__namespace.createElement(Await, {

@@ -812,12 +771,16 @@ resolve: deferredData.data[dataKey],

dataKey: dataKey,
routeId: routeId
routeId: routeId,
scriptProps: scriptProps,
serializeError: serializeError
}),
children: data => /*#__PURE__*/React__namespace.createElement("script", {
async: true,
suppressHydrationWarning: true,
dangerouslySetInnerHTML: {
__html: `__remixContext.r(${JSON.stringify(routeId)}, ${JSON.stringify(dataKey)}, ${markup.escapeHtml(JSON.stringify(data))});`
}
})
}) : /*#__PURE__*/React__namespace.createElement("script", {
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, {
async: true,

@@ -828,22 +791,17 @@ suppressHydrationWarning: true,

}
}));
})));
}
function ErrorDeferredHydrationScript({
dataKey,
routeId
routeId,
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", {
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)
}
});
}));
}

@@ -853,22 +811,10 @@ function dedupe(array) {

}
// 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();
}

@@ -886,195 +832,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

@@ -1085,208 +856,23 @@ * 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({
port = Number(process.env.REMIX_DEV_SERVER_WS_PORT || 8002),
process.env.NODE_ENV !== "development" ? () => null : function LiveReload({
origin,
port,
timeoutMs = 1000,
nonce = undefined
}) {
origin ??= process.env.REMIX_DEV_ORIGIN;
let js = String.raw;

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

function remixLiveReloadConnect(config) {
let protocol = location.protocol === "https:" ? "wss:" : "ws:";
let host = location.hostname;
let port = (window.__remixContext && window.__remixContext.dev && window.__remixContext.dev.liveReloadPort) || ${String(port)};
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) => {

@@ -1322,6 +915,8 @@ let event = JSON.parse(message.data);

let updateAccepted = false;
let needsRevalidation = new Set();
for (let update of event.updates) {
console.log("[HMR] " + update.reason + " [" + update.id +"]")
if (update.revalidate) {
console.log("[HMR] Revalidating [" + update.id + "]");
needsRevalidation.add(update.routeId);
console.log("[HMR] Revalidating [" + update.routeId + "]");
}

@@ -1334,3 +929,3 @@ let imported = await import(update.url + '?t=' + event.assetsManifest.hmr.timestamp);

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

@@ -1342,6 +937,6 @@ }

let accepted = window.__hmr__.contexts["remix:manifest"].emit(
event.assetsManifest
{ needsRevalidation, assetsManifest: event.assetsManifest }
);
if (accepted) {
console.log("[HMR] Updated accepted by", "remix:manifest");
console.log("[HMR] Update accepted by", "remix:manifest");
updateAccepted = true;

@@ -1351,3 +946,3 @@ }

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

@@ -1384,4 +979,16 @@ }

};
function mergeRefs(...refs) {
return value => {
refs.forEach(ref => {
if (typeof ref === "function") {
ref(value);
} else if (ref != null) {
ref.current = value;
}
});
};
}
exports.Await = Await;
exports.Form = Form;
exports.Link = Link;

@@ -1394,4 +1001,2 @@ exports.Links = Links;

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

@@ -1401,5 +1006,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 declare function isCatchResponse(response: any): boolean;
export declare function isErrorResponse(response: any): boolean;
export declare function isRedirectResponse(response: any): boolean;
export declare function isDeferredResponse(response: any): boolean;
export declare function fetchData(request: Request, routeId: string): Promise<Response | Error>;
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-9af8868-20230412
* @remix-run/react v0.0.0-nightly-9bc6d9517-20241206
*

@@ -19,22 +19,67 @@ * 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
*/
function isCatchResponse(response) {
return response instanceof Response && response.headers.get("X-Remix-Catch") != null;
return response.headers.get("X-Remix-Catch") != null;
}
function isErrorResponse(response) {
return response instanceof Response && response.headers.get("X-Remix-Error") != null;
return response.headers.get("X-Remix-Error") != null;
}
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) {
return response instanceof Response && response.headers.get("X-Remix-Redirect") != null;
return response.headers.get("X-Remix-Redirect") != null;
}
function isDeferredResponse(response) {
var _response$headers$get;
return response instanceof Response && !!((_response$headers$get = response.headers.get("Content-Type")) !== null && _response$headers$get !== void 0 && _response$headers$get.match(/text\/remix-deferred/));
return !!((_response$headers$get = response.headers.get("Content-Type")) !== null && _response$headers$get !== void 0 && _response$headers$get.match(/text\/remix-deferred/));
}
async function fetchData(request, routeId) {
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);
if (retry > 0) {
// Retry up to 3 times waiting 50, 250, 1250 ms
// between retries for a total of 1550 ms before giving up.
await new Promise(resolve => setTimeout(resolve, 5 ** retry * 10));
}
let init = await createRequestInit(request);
let revalidation = window.__remixRevalidation;
let response = await fetch(url.href, init).catch(error => {
if (typeof revalidation === "number" && revalidation === window.__remixRevalidation && (error === null || error === void 0 ? void 0 : error.name) === "TypeError" && retry < 3) {
return fetchData(request, routeId, retry + 1);
}
throw error;
});
if (isErrorResponse(response)) {
let data = await response.json();
let error = new Error(data.message);
error.stack = data.stack;
return 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 = {

@@ -46,15 +91,22 @@ signal: request.signal

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 (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();
}
}
let response = await fetch(url.href, init);
if (isErrorResponse(response)) {
let data = await response.json();
let error = new Error(data.message);
error.stack = data.stack;
return error;
}
return response;
return init;
}

@@ -100,3 +152,3 @@ const DEFERRED_VALUE_PLACEHOLDER_PREFIX = "__deferred_promise:";

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

@@ -222,7 +274,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?: {
liveReloadPort: number;
serializeError?(error: Error): SerializedError;
renderMeta?: {
didRenderScripts?: boolean;
streamCache?: Record<number, Promise<void> & {
result?: {
done: boolean;
value: string;
};
error?: unknown;
}>;
};

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

staticHandlerContext: StaticHandlerContext;
serverHandoffStream?: ReadableStream<Uint8Array>;
}
type Dev = {
port?: number;
appServerPort?: number;
remixRequestHandlerPath?: string;
rebuildPollIntervalMs?: number;
};
type VanillaExtractOptions = {
cache?: boolean;
};
export interface FutureConfig {
unstable_cssModules: boolean;
unstable_cssSideEffectImports: boolean;
unstable_dev: boolean | Dev;
/** @deprecated Use the `postcss` config option instead */
unstable_postcss: boolean;
/** @deprecated Use the `tailwind` config option instead */
unstable_tailwind: boolean;
unstable_vanillaExtract: boolean | VanillaExtractOptions;
v2_errorBoundary: boolean;
v2_meta: boolean;
v2_normalizeFormMethod: boolean;
v2_routeConvention: boolean;
v3_fetcherPersist: boolean;
v3_relativeSplatPath: boolean;
v3_lazyRouteDiscovery: boolean;
v3_singleFetch: boolean;
}

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

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

@@ -53,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-9af8868-20230412
* @remix-run/react v0.0.0-nightly-9bc6d9517-20241206
*

@@ -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,22 +96,39 @@ } else {

function RemixRootDefaultErrorBoundary({
error
error,
isOutsideRemixApp
}) {
console.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"
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"), /*#__PURE__*/React__default["default"].createElement("pre", {
}, "Application Error"), /*#__PURE__*/React__namespace.createElement("pre", {
style: {

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

}
}, error.stack)), /*#__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", {
}), /*#__PURE__*/React__namespace.createElement("title", null, title)), /*#__PURE__*/React__namespace.createElement("body", null, /*#__PURE__*/React__namespace.createElement("main", {
style: {

@@ -182,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-9af8868-20230412
* @remix-run/react v0.0.0-nightly-9bc6d9517-20241206
*

@@ -25,7 +25,23 @@ * Copyright (c) Remix Software Inc.

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 {

@@ -32,0 +48,0 @@ serialized[key] = val;

/**
* @remix-run/react v0.0.0-nightly-9af8868-20230412
* @remix-run/react v0.0.0-nightly-9bc6d9517-20241206
*

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

/**
* @remix-run/react v0.0.0-nightly-9af8868-20230412
* @remix-run/react v0.0.0-nightly-9bc6d9517-20241206
*

@@ -11,25 +11,63 @@ * 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 { useSyncExternalStore } from 'use-sync-external-store/shim';
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 { createClientRoutes } from './routes.js';
import { createClientRoutesWithHMRRevalidationOptOut, createClientRoutes, shouldHydrateRouteLoader } from './routes.js';
import { decodeViaTurboStream, getSingleFetchDataStrategy } from './single-fetch.js';
import invariant from './invariant.js';
import { getPatchRoutesOnNavigationFunction, useFogOFWarDiscovery } from './fog-of-war.js';
/* eslint-disable prefer-let/prefer-let */
/* eslint-enable 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) {
import.meta.hot.accept("remix:manifest", async newManifest => {
// @ts-expect-error
import.meta.hot.accept("remix:manifest", async ({
assetsManifest,
needsRevalidation
}) => {
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)))];
if (hmrAbortController) {
hmrAbortController.abort();
}
hmrAbortController = new AbortController();
let signal = hmrAbortController.signal;
// Load new route modules that we've seen.
let newRouteModules = Object.assign({}, window.__remixRouteModules, Object.fromEntries((await Promise.all(routeIds.map(async id => {
var _newManifest$hmr, _window$__remixRouteM, _window$__remixRouteM2, _window$__remixRouteM3;
if (!newManifest.routes[id]) {
var _assetsManifest$hmr, _window$__remixRouteM, _window$__remixRouteM2, _window$__remixRouteM3;
if (!assetsManifest.routes[id]) {
return null;
}
let imported = await import(newManifest.routes[id].module + `?t=${(_newManifest$hmr = newManifest.hmr) === null || _newManifest$hmr === void 0 ? void 0 : _newManifest$hmr.timestamp}`);
let imported = await import(assetsManifest.routes[id].module + `?t=${(_assetsManifest$hmr = assetsManifest.hmr) === null || _assetsManifest$hmr === void 0 ? void 0 : _assetsManifest$hmr.timestamp}`);
return [id, {

@@ -40,4 +78,4 @@ ...imported,

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
}];

@@ -47,22 +85,23 @@ }))).filter(Boolean)));

// Create new routes
let routes = createClientRoutes(newManifest.routes, window.__remixRouteModules, window.__remixContext.future);
let routes = createClientRoutesWithHMRRevalidationOptOut(needsRevalidation, assetsManifest.routes, window.__remixRouteModules, window.__remixContext.state, window.__remixContext.future, window.__remixContext.isSpaMode);
// This is temporary API and will be more granular before release
router._internalSetRoutes(routes);
if (hmrAbortController) {
hmrAbortController.abort();
}
hmrAbortController = new AbortController();
let signal = hmrAbortController.signal;
// Wait for router to be idle before updating the manifest and route modules
// and triggering a react-refresh
let unsub = router.subscribe(state => {
if (state.revalidation === "idle" && !signal.aborted) {
if (state.revalidation === "idle") {
unsub();
// TODO: Handle race conditions here. Should abort if a new update
// comes in while we're waiting for the router to be idle.
Object.assign(window.__remixManifest, newManifest);
window.$RefreshRuntime$.performReactRefresh();
// Abort if a new update comes in while we're waiting for the
// router to be idle.
if (signal.aborted) return;
// Ensure RouterProvider setState has flushed before re-rendering
setTimeout(() => {
Object.assign(window.__remixManifest, assetsManifest);
window.$RefreshRuntime$.performReactRefresh();
}, 1);
}
});
window.__remixRevalidation = (window.__remixRevalidation || 0) + 1;
router.revalidate();

@@ -79,23 +118,137 @@ });

if (!router) {
let routes = createClientRoutes(window.__remixManifest.routes, window.__remixRouteModules, window.__remixContext.future);
let hydrationData = window.__remixContext.state;
if (hydrationData && hydrationData.errors) {
// When single fetch is enabled, we need to suspend until the initial state
// snapshot is decoded into window.__remixContext.state
if (window.__remixContext.future.v3_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, {
// 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.v3_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,
dataStrategy: window.__remixContext.future.v3_singleFetch ? getSingleFetchDataStrategy(window.__remixManifest, window.__remixRouteModules, () => router) : undefined,
patchRoutesOnNavigation: getPatchRoutesOnNavigationFunction(window.__remixManifest, window.__remixRouteModules, window.__remixContext.future, window.__remixContext.isSpaMode, window.__remixContext.basename)
});
// 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.
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.
let [location, setLocation] = React.useState(router.state.location);
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();
}
}, []);
React.useLayoutEffect(() => {
return router.subscribe(newState => {
if (newState.location !== location) {
setLocation(newState.location);
}
});
}, [location]);
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

@@ -105,18 +258,26 @@ // boundary also throws and we need to bubble up outside of the router entirely.

// out of there
let location = useSyncExternalStore(router.subscribe, () => router.state.location, () => router.state.location);
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
})));
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.v3_singleFetch ? /*#__PURE__*/React.createElement(React.Fragment, null) : null)
);
}
export { RemixBrowser };
/**
* @remix-run/react v0.0.0-nightly-9af8868-20230412
* @remix-run/react v0.0.0-nightly-9bc6d9517-20241206
*

@@ -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, 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 { singleFetchUrl } from './single-fetch.js';
import { isFogOfWarEnabled, getPartialManifest } from './fog-of-war.js';

@@ -45,83 +44,18 @@ function useDataRouterContext() {

////////////////////////////////////////////////////////////////////////////////
// RemixRoute
function RemixRoute({
id
}) {
let {
routeModules
} = 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
} = routeModules[id];
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 ? void 0 : tError.error) instanceof 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, {
component: CatchBoundary,
catch: error
});
}
}
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
*/

@@ -139,2 +73,3 @@

} = theirElementProps;
let ref = React.useRef(null);
React.useEffect(() => {

@@ -144,2 +79,16 @@ if (prefetch === "render") {

}
if (prefetch === "viewport") {
let callback = entries => {
entries.forEach(entry => {
setShouldPrefetch(entry.isIntersecting);
});
};
let observer = new IntersectionObserver(callback, {
threshold: 0.5
});
if (ref.current) observer.observe(ref.current);
return () => {
observer.disconnect();
};
}
}, [prefetch]);

@@ -167,3 +116,3 @@ let setIntent = () => {

}, [maybePrefetch]);
return [shouldPrefetch, {
return [shouldPrefetch, ref, {
onFocus: composeEventHandlers(onFocus, setIntent),

@@ -177,5 +126,8 @@ onBlur: composeEventHandlers(onBlur, cancelIntent),

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".
*

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

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

@@ -192,7 +145,8 @@ }, forwardedRef) => {

let href = useHref(to);
let [shouldPrefetch, prefetchHandlers] = usePrefetchBehavior(prefetch, props);
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(NavLink$1, _extends({
ref: forwardedRef,
to: to
}, props, prefetchHandlers)), shouldPrefetch && !isAbsolute ? /*#__PURE__*/React.createElement(PrefetchPageLinks, {
let [shouldPrefetch, ref, prefetchHandlers] = usePrefetchBehavior(prefetch, props);
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(NavLink$1, _extends({}, props, prefetchHandlers, {
ref: mergeRefs(forwardedRef, ref),
to: to,
"data-discover": getDiscoverAttr(discover, isAbsolute, props.reloadDocument)
})), shouldPrefetch && !isAbsolute ? /*#__PURE__*/React.createElement(PrefetchPageLinks, {
page: href

@@ -212,2 +166,3 @@ }) : null);

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

@@ -217,7 +172,8 @@ }, forwardedRef) => {

let href = useHref(to);
let [shouldPrefetch, prefetchHandlers] = usePrefetchBehavior(prefetch, props);
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Link$1, _extends({
ref: forwardedRef,
to: to
}, props, prefetchHandlers)), shouldPrefetch && !isAbsolute ? /*#__PURE__*/React.createElement(PrefetchPageLinks, {
let [shouldPrefetch, ref, prefetchHandlers] = usePrefetchBehavior(prefetch, props);
return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Link$1, _extends({}, props, prefetchHandlers, {
ref: mergeRefs(forwardedRef, ref),
to: to,
"data-discover": getDiscoverAttr(discover, isAbsolute, props.reloadDocument)
})), shouldPrefetch && !isAbsolute ? /*#__PURE__*/React.createElement(PrefetchPageLinks, {
page: href

@@ -227,2 +183,19 @@ }) : null);

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) {

@@ -236,7 +209,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;
}
/**

@@ -249,53 +234,29 @@ * Renders the `<link>` tags for the current routes.

let {
isSpaMode,
manifest,
routeModules
routeModules,
criticalCss
} = useRemixContext();
let {
matches
errors,
matches: routerMatches
} = useDataRouterStateContext();
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

@@ -315,3 +276,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) {

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

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

@@ -332,7 +293,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);
}
});

@@ -343,3 +306,3 @@ return () => {

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

@@ -353,10 +316,51 @@ function PrefetchPageLinksImpl({

let {
manifest
future,
manifest,
routeModules
} = useRemixContext();
let {
loaderData,
matches
} = useDataRouterStateContext();
let newMatchesForData = React.useMemo(() => getNewMatchesForLinks(page, nextMatches, matches, manifest, location, "data"), [page, nextMatches, matches, manifest, location]);
let newMatchesForAssets = React.useMemo(() => getNewMatchesForLinks(page, nextMatches, matches, manifest, location, "assets"), [page, nextMatches, matches, manifest, location]);
let dataHrefs = React.useMemo(() => getDataLinkHrefs(page, newMatchesForData, manifest), [newMatchesForData, page, manifest]);
let newMatchesForData = React.useMemo(() => getNewMatchesForLinks(page, nextMatches, matches, manifest, location, future, "data"), [page, nextMatches, matches, manifest, location, future]);
let dataHrefs = React.useMemo(() => {
if (!future.v3_singleFetch) {
return getDataLinkHrefs(page, newMatchesForData, manifest);
}
if (page === location.pathname + location.search + location.hash) {
// Because we opt-into revalidation, don't compute this for the current page
// since it would always trigger a prefetch of the existing loaders
return [];
}
// Single-fetch is harder :)
// This parallels the logic in the single fetch data strategy
let routesParams = new Set();
let foundOptOutRoute = false;
nextMatches.forEach(m => {
var _routeModules$m$route;
if (!manifest.routes[m.route.id].hasLoader) {
return;
}
if (!newMatchesForData.some(m2 => m2.route.id === m.route.id) && m.route.id in loaderData && (_routeModules$m$route = routeModules[m.route.id]) !== null && _routeModules$m$route !== void 0 && _routeModules$m$route.shouldRevalidate) {
foundOptOutRoute = true;
} else if (manifest.routes[m.route.id].hasClientLoader) {
foundOptOutRoute = true;
} else {
routesParams.add(m.route.id);
}
});
if (routesParams.size === 0) {
return [];
}
let url = singleFetchUrl(page);
// When one or more routes have opted out, we add a _routes param to
// limit the loaders to those that have a server loader and did not
// opt out
if (foundOptOutRoute && routesParams.size > 0) {
url.searchParams.set("_routes", nextMatches.filter(m => routesParams.has(m.route.id)).map(m => m.route.id).join(","));
}
return [url.pathname + url.search];
}, [future.v3_singleFetch, loaderData, location, manifest, newMatchesForData, nextMatches, page, routeModules]);
let newMatchesForAssets = React.useMemo(() => getNewMatchesForLinks(page, nextMatches, matches, manifest, location, future, "assets"), [page, nextMatches, matches, manifest, location, future]);
let moduleHrefs = React.useMemo(() => getModuleLinkHrefs(newMatchesForAssets, manifest), [newMatchesForAssets, manifest]);

@@ -366,3 +370,3 @@

// just the manifest like the other links in here.
let styleLinks = usePrefetchedStylesheets(newMatchesForAssets);
let keyedPrefetchLinks = useKeyedPrefetchLinks(newMatchesForAssets);
return /*#__PURE__*/React.createElement(React.Fragment, null, dataHrefs.map(href => /*#__PURE__*/React.createElement("link", _extends({

@@ -377,3 +381,6 @@ key: href,

href: href
}, linkProps))), styleLinks.map(link =>
}, linkProps))), keyedPrefetchLinks.map(({
key,
link
}) =>
/*#__PURE__*/

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

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

@@ -389,102 +396,22 @@ }

/**
* 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
} = useRemixContext();
let {
matches,
errors,
matches: routerMatches,
loaderData
} = useDataRouterStateContext();
let location = useLocation();
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 {
matches: _matches,
loaderData
} = useDataRouterStateContext();
let location = useLocation();
let meta = [];

@@ -507,10 +434,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
};

@@ -523,7 +443,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.

@@ -534,6 +455,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");
}

@@ -550,4 +468,6 @@ match.meta = routeMeta;

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

@@ -559,4 +479,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));
}

@@ -569,3 +489,3 @@ if ("title" in metaProps) {

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

@@ -580,13 +500,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;
}
}

@@ -601,8 +522,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) {

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

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

@@ -640,13 +559,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.v3_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.v3_singleFetch ? undefined : staticContext === null || staticContext === void 0 ? void 0 : staticContext.activeDeferreds;
// This sets up the __remixContext with utility functions used by the

@@ -660,3 +628,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.

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

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

@@ -680,15 +651,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);
}

@@ -699,4 +660,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(",")}};

@@ -723,18 +687,10 @@

deferredScripts.push( /*#__PURE__*/React.createElement(DeferredHydrationScript, {
key: i
key: i,
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];

@@ -744,7 +700,7 @@ return (route.imports || []).concat([route.module]);

let preloads = isHydrated ? [] : manifest.entry.imports.concat(routePreloads);
return /*#__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
}), /*#__PURE__*/React.createElement("link", {
}) : null, /*#__PURE__*/React.createElement("link", {
rel: "modulepreload",

@@ -758,3 +714,3 @@ href: manifest.entry.module,

crossOrigin: props.crossOrigin
})), !isHydrated && initialScripts, !isHydrated && deferredScripts);
})), initialScripts, deferredScripts);
}

@@ -764,3 +720,6 @@ function DeferredHydrationScript({

deferredData,
routeId
routeId,
scriptProps,
serializeData,
serializeError
}) {

@@ -775,3 +734,3 @@ if (typeof document === "undefined" && deferredData && dataKey && routeId) {

// To reproduce a hydration mismatch, just render null as a fallback.
typeof document === "undefined" && deferredData && dataKey && routeId ? null : /*#__PURE__*/React.createElement("script", {
typeof document === "undefined" && deferredData && dataKey && routeId ? null : /*#__PURE__*/React.createElement("script", _extends({}, scriptProps, {
async: true,

@@ -782,3 +741,3 @@ suppressHydrationWarning: true,

}
})
}))
}, typeof document === "undefined" && deferredData && dataKey && routeId ? /*#__PURE__*/React.createElement(Await, {

@@ -788,12 +747,16 @@ resolve: deferredData.data[dataKey],

dataKey: dataKey,
routeId: routeId
routeId: routeId,
scriptProps: scriptProps,
serializeError: serializeError
}),
children: data => /*#__PURE__*/React.createElement("script", {
async: true,
suppressHydrationWarning: true,
dangerouslySetInnerHTML: {
__html: `__remixContext.r(${JSON.stringify(routeId)}, ${JSON.stringify(dataKey)}, ${escapeHtml(JSON.stringify(data))});`
}
})
}) : /*#__PURE__*/React.createElement("script", {
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, {
async: true,

@@ -804,22 +767,17 @@ suppressHydrationWarning: true,

}
}));
})));
}
function ErrorDeferredHydrationScript({
dataKey,
routeId
routeId,
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", {
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)
}
});
}));
}

@@ -829,22 +787,10 @@ function dedupe(array) {

}
// 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();
}

@@ -862,195 +808,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

@@ -1061,208 +832,29 @@ * 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({
port = Number(process.env.REMIX_DEV_SERVER_WS_PORT || 8002),
process.env.NODE_ENV !== "development" ? () => null : function LiveReload({
origin,
port,
timeoutMs = 1000,
nonce = undefined
}) {
// @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;

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

function remixLiveReloadConnect(config) {
let protocol = location.protocol === "https:" ? "wss:" : "ws:";
let host = location.hostname;
let port = (window.__remixContext && window.__remixContext.dev && window.__remixContext.dev.liveReloadPort) || ${String(port)};
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) => {

@@ -1298,6 +897,8 @@ let event = JSON.parse(message.data);

let updateAccepted = false;
let needsRevalidation = new Set();
for (let update of event.updates) {
console.log("[HMR] " + update.reason + " [" + update.id +"]")
if (update.revalidate) {
console.log("[HMR] Revalidating [" + update.id + "]");
needsRevalidation.add(update.routeId);
console.log("[HMR] Revalidating [" + update.routeId + "]");
}

@@ -1310,3 +911,3 @@ let imported = await import(update.url + '?t=' + event.assetsManifest.hmr.timestamp);

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

@@ -1318,6 +919,6 @@ }

let accepted = window.__hmr__.contexts["remix:manifest"].emit(
event.assetsManifest
{ needsRevalidation, assetsManifest: event.assetsManifest }
);
if (accepted) {
console.log("[HMR] Updated accepted by", "remix:manifest");
console.log("[HMR] Update accepted by", "remix:manifest");
updateAccepted = true;

@@ -1327,3 +928,3 @@ }

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

@@ -1360,3 +961,14 @@ }

};
function mergeRefs(...refs) {
return value => {
refs.forEach(ref => {
if (typeof ref === "function") {
ref(value);
} else if (ref != null) {
ref.current = value;
}
});
};
}
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-9af8868-20230412
* @remix-run/react v0.0.0-nightly-9bc6d9517-20241206
*

@@ -15,22 +15,67 @@ * 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
*/
function isCatchResponse(response) {
return response instanceof Response && response.headers.get("X-Remix-Catch") != null;
return response.headers.get("X-Remix-Catch") != null;
}
function isErrorResponse(response) {
return response instanceof Response && response.headers.get("X-Remix-Error") != null;
return response.headers.get("X-Remix-Error") != null;
}
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) {
return response instanceof Response && response.headers.get("X-Remix-Redirect") != null;
return response.headers.get("X-Remix-Redirect") != null;
}
function isDeferredResponse(response) {
var _response$headers$get;
return response instanceof Response && !!((_response$headers$get = response.headers.get("Content-Type")) !== null && _response$headers$get !== void 0 && _response$headers$get.match(/text\/remix-deferred/));
return !!((_response$headers$get = response.headers.get("Content-Type")) !== null && _response$headers$get !== void 0 && _response$headers$get.match(/text\/remix-deferred/));
}
async function fetchData(request, routeId) {
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);
if (retry > 0) {
// Retry up to 3 times waiting 50, 250, 1250 ms
// between retries for a total of 1550 ms before giving up.
await new Promise(resolve => setTimeout(resolve, 5 ** retry * 10));
}
let init = await createRequestInit(request);
let revalidation = window.__remixRevalidation;
let response = await fetch(url.href, init).catch(error => {
if (typeof revalidation === "number" && revalidation === window.__remixRevalidation && (error === null || error === void 0 ? void 0 : error.name) === "TypeError" && retry < 3) {
return fetchData(request, routeId, retry + 1);
}
throw error;
});
if (isErrorResponse(response)) {
let data = await response.json();
let error = new Error(data.message);
error.stack = data.stack;
return 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 = {

@@ -42,15 +87,22 @@ signal: request.signal

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 (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();
}
}
let response = await fetch(url.href, init);
if (isErrorResponse(response)) {
let data = await response.json();
let error = new Error(data.message);
error.stack = data.stack;
return error;
}
return response;
return init;
}

@@ -96,3 +148,3 @@ const DEFERRED_VALUE_PLACEHOLDER_PREFIX = "__deferred_promise:";

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

@@ -218,2 +270,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-9af8868-20230412
* @remix-run/react v0.0.0-nightly-9bc6d9517-20241206
*

@@ -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,22 +72,39 @@ } else {

function RemixRootDefaultErrorBoundary({
error
error,
isOutsideRemixApp
}) {
console.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"
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"), /*#__PURE__*/React__default.createElement("pre", {
}, "Application Error"), /*#__PURE__*/React.createElement("pre", {
style: {

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

}
}, error.stack)), /*#__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", {
}), /*#__PURE__*/React.createElement("title", null, title)), /*#__PURE__*/React.createElement("body", null, /*#__PURE__*/React.createElement("main", {
style: {

@@ -174,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-9af8868-20230412
* @remix-run/react v0.0.0-nightly-9bc6d9517-20241206
*

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

*/
import { ErrorResponse } from '@remix-run/router';
import { UNSAFE_ErrorResponseImpl } from '@remix-run/router';

@@ -22,7 +22,23 @@ function deserializeErrors(errors) {

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 {

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

/**
* @remix-run/react v0.0.0-nightly-9af8868-20230412
* @remix-run/react v0.0.0-nightly-9bc6d9517-20241206
*

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

*/
export { Navigate, NavigationType, Outlet, Route, Routes, createPath, createRoutesFromChildren, createRoutesFromElements, createSearchParams, generatePath, isRouteErrorResponse, matchPath, matchRoutes, parsePath, renderMatches, resolvePath, unstable_usePrompt, useAsyncError, useAsyncValue, useBeforeUnload, useBlocker, useFetchers, useFormAction, useHref, useInRouterContext, useLinkClickHandler, useLocation, useMatch, useNavigate, useNavigation, useNavigationType, useOutlet, useOutletContext, useParams, useResolvedPath, useRevalidator, useRouteError, useRoutes, useSearchParams, useSubmit, useViewTransitionState } from 'react-router-dom';
export { data, defer, json, redirect, redirectDocument, replace } from '@remix-run/server-runtime';
export { RemixBrowser } from './browser.js';
export { Form, Outlet, isRouteErrorResponse, unstable_useBlocker, unstable_usePrompt, useAsyncError, useAsyncValue, useBeforeUnload, useFormAction, useHref, useLocation, 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';
/**
* @remix-run/react v0.0.0-nightly-9af8868-20230412
* @remix-run/react v0.0.0-nightly-9bc6d9517-20241206
*

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

/**
* @remix-run/react v0.0.0-nightly-9af8868-20230412
* @remix-run/react v0.0.0-nightly-9bc6d9517-20241206
*

@@ -14,2 +14,8 @@ * Copyright (c) Remix Software Inc.

/**
* Represents a `<link>` element.
*
* WHATWG Specification: https://html.spec.whatwg.org/multipage/semantics.html#the-link-element
*/
////////////////////////////////////////////////////////////////////////////////

@@ -21,15 +27,23 @@

*/
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 = [];

@@ -46,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));

@@ -80,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 => {

@@ -96,14 +113,14 @@ 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"
}));
}
// This is ridiculously identical to transition.ts `filterMatchesToLoad`
function getNewMatchesForLinks(page, nextMatches, currentMatches, manifest, location, mode) {
function getNewMatchesForLinks(page, nextMatches, currentMatches, manifest, location, future, mode) {
let path = parsePathPatch(page);

@@ -127,3 +144,3 @@ let isNew = (match, index) => {

// version doesn't care about submissions
let newMatches = mode === "data" && location.search !== path.search ?
let newMatches = mode === "data" && (future.v3_singleFetch || location.search !== path.search) ?
// this is really similar to stuff in transition.ts, maybe somebody smarter

@@ -139,2 +156,7 @@ // than me (or in less of a hurry) can share some of it. You're the best.

}
// For reused routes on GET navigations, by default:
// - Single fetch always revalidates
// - Multi fetch revalidates if search params changed
let defaultShouldRevalidate = future.v3_singleFetch || location.search !== path.search;
if (match.route.shouldRevalidate) {

@@ -147,3 +169,3 @@ var _currentMatches$;

nextParams: match.params,
defaultShouldRevalidate: true
defaultShouldRevalidate
});

@@ -154,3 +176,3 @@ if (typeof routeChoice === "boolean") {

}
return true;
return defaultShouldRevalidate;
}) : nextMatches.filter((match, index) => {

@@ -164,3 +186,3 @@ let manifestRoute = manifest.routes[match.route.id];

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 {

@@ -202,14 +224,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
});
}

@@ -227,2 +260,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-9af8868-20230412
* @remix-run/react v0.0.0-nightly-9bc6d9517-20241206
*

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

/**
* @remix-run/react v0.0.0-nightly-9af8868-20230412
* @remix-run/react v0.0.0-nightly-9bc6d9517-20241206
*

@@ -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-9af8868-20230412
* @remix-run/react v0.0.0-nightly-9bc6d9517-20241206
*

@@ -12,9 +12,15 @@ * 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';
// NOTE: make sure to change the Route in server-runtime if you change this
// NOTE: make sure to change the EntryRoute in server-runtime if you change this
// Create a map of routes by parentId to use recursively instead of

@@ -33,22 +39,61 @@ // repeatedly filtering the manifest.

}
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;

@@ -58,24 +103,213 @@ return dataRoute;

}
function createClientRoutes(manifest, routeModulesCache, future, parentId = "", routesByParentId = groupRoutesByParentId(manifest)) {
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 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)
path: route.path
};
let children = createClientRoutes(manifest, routeModulesCache, future, route.id);
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: getShouldRevalidateFunction(future, routeModule, route.id, needsRevalidation)
});
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 (initialData !== undefined) {
return initialData;
}
if (initialError !== undefined) {
throw initialError;
}
return null;
}
// 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);
}
});
}
return {
...(lazyRoute.loader ? {
loader: lazyRoute.loader
} : {}),
...(lazyRoute.action ? {
action: lazyRoute.action
} : {}),
hasErrorBoundary: lazyRoute.hasErrorBoundary,
shouldRevalidate: getShouldRevalidateFunction(future, lazyRoute, route.id, needsRevalidation),
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;

@@ -85,10 +319,30 @@ return dataRoute;

}
function createShouldRevalidate(route, routeModules) {
return function (arg) {
let module = routeModules[route.id];
invariant(module, `Expected route module to be loaded for ${route.id}`);
if (module.shouldRevalidate) {
return module.shouldRevalidate(arg);
function getShouldRevalidateFunction(future, route, routeId, needsRevalidation) {
// During HDR we force revalidation for updated routes
if (needsRevalidation) {
return wrapShouldRevalidateForHdr(routeId, route.shouldRevalidate, needsRevalidation);
}
// Single fetch revalidates by default, so override the RR default value which
// matches the multi-fetch behavior with `true`
if (future.v3_singleFetch && route.shouldRevalidate) {
let fn = route.shouldRevalidate;
return opts => fn({
...opts,
defaultShouldRevalidate: true
});
}
return route.shouldRevalidate;
}
// 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 arg => {
if (!handledRevalidation) {
handledRevalidation = true;
return needsRevalidation.has(routeId);
}
return arg.defaultShouldRevalidate;
return routeShouldRevalidate ? routeShouldRevalidate(arg) : arg.defaultShouldRevalidate;
};

@@ -98,36 +352,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;
}

@@ -142,2 +408,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, {

@@ -149,2 +423,17 @@ status,

export { createClientRoutes, 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-9af8868-20230412
* @remix-run/react v0.0.0-nightly-9bc6d9517-20241206
*

@@ -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-9af8868-20230412
* @remix-run/react v0.0.0-nightly-9bc6d9517-20241206
*

@@ -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.v3_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,14 @@

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, useViewTransitionState, unstable_usePrompt, } from "react-router-dom";
export { defer, json, redirect, redirectDocument, replace, 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, 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 { 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-9af8868-20230412
* @remix-run/react v0.0.0-nightly-9bc6d9517-20241206
*

@@ -15,6 +15,6 @@ * 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');

@@ -25,7 +25,10 @@ var server = require('./server.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', {

@@ -35,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', {

@@ -40,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', {

@@ -61,2 +108,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 +125,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', {

@@ -75,2 +138,6 @@ enumerable: true,

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

@@ -112,5 +179,5 @@ enumerable: true,

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

@@ -125,3 +192,33 @@ Object.defineProperty(exports, 'useSearchParams', {

});
Object.defineProperty(exports, 'useViewTransitionState', {
enumerable: true,
get: function () { return reactRouterDom.useViewTransitionState; }
});
Object.defineProperty(exports, 'data', {
enumerable: true,
get: function () { return serverRuntime.data; }
});
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; }
});
exports.RemixBrowser = browser.RemixBrowser;
exports.Await = components.Await;
exports.Form = components.Form;
exports.Link = components.Link;

@@ -137,8 +234,6 @@ 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;
/**
* @remix-run/react v0.0.0-nightly-9af8868-20230412
* @remix-run/react v0.0.0-nightly-9bc6d9517-20241206
*

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

import type { AgnosticDataRouteMatch } from "@remix-run/router";
import type { Location } from "react-router-dom";
import type { AssetsManifest } from "./entry";
import type { AssetsManifest, FutureConfig } from "./entry";
import type { RouteModules, RouteModule } from "./routeModules";
import type { EntryRoute } from "./routes";
type Primitive = null | undefined | string | number | boolean | symbol | bigint;

@@ -98,16 +99,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.

@@ -122,11 +114,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 declare function getNewMatchesForLinks(page: string, nextMatches: AgnosticDataRouteMatch[], currentMatches: AgnosticDataRouteMatch[], manifest: AssetsManifest, location: Location, mode: "data" | "assets"): AgnosticDataRouteMatch[];
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, future: FutureConfig, 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-9af8868-20230412
* @remix-run/react v0.0.0-nightly-9bc6d9517-20241206
*

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

/**
* Represents a `<link>` element.
*
* WHATWG Specification: https://html.spec.whatwg.org/multipage/semantics.html#the-link-element
*/
////////////////////////////////////////////////////////////////////////////////

@@ -25,15 +31,23 @@

*/
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 = [];

@@ -50,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));

@@ -84,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 => {

@@ -100,14 +117,14 @@ 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"
}));
}
// This is ridiculously identical to transition.ts `filterMatchesToLoad`
function getNewMatchesForLinks(page, nextMatches, currentMatches, manifest, location, mode) {
function getNewMatchesForLinks(page, nextMatches, currentMatches, manifest, location, future, mode) {
let path = parsePathPatch(page);

@@ -131,3 +148,3 @@ let isNew = (match, index) => {

// version doesn't care about submissions
let newMatches = mode === "data" && location.search !== path.search ?
let newMatches = mode === "data" && (future.v3_singleFetch || location.search !== path.search) ?
// this is really similar to stuff in transition.ts, maybe somebody smarter

@@ -143,2 +160,7 @@ // than me (or in less of a hurry) can share some of it. You're the best.

}
// For reused routes on GET navigations, by default:
// - Single fetch always revalidates
// - Multi fetch revalidates if search params changed
let defaultShouldRevalidate = future.v3_singleFetch || location.search !== path.search;
if (match.route.shouldRevalidate) {

@@ -151,3 +173,3 @@ var _currentMatches$;

nextParams: match.params,
defaultShouldRevalidate: true
defaultShouldRevalidate
});

@@ -158,3 +180,3 @@ if (typeof routeChoice === "boolean") {

}
return true;
return defaultShouldRevalidate;
}) : nextMatches.filter((match, index) => {

@@ -168,3 +190,3 @@ let manifestRoute = manifest.routes[match.route.id];

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 {

@@ -206,14 +228,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
});
}

@@ -231,10 +264,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-9af8868-20230412
* @remix-run/react v0.0.0-nightly-9bc6d9517-20241206
*

@@ -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>> {
data: Loader extends LoaderFunction ? SerializeFrom<Loader> : AppData;
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-9af8868-20230412
* @remix-run/react v0.0.0-nightly-9bc6d9517-20241206
*

@@ -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,10 +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 createClientRoutes(manifest: RouteManifest<EntryRoute>, routeModulesCache: RouteModules, future: FutureConfig, parentId?: string, routesByParentId?: Record<string, Omit<EntryRoute, "children">[]>): 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-9af8868-20230412
* @remix-run/react v0.0.0-nightly-9bc6d9517-20241206
*

@@ -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');

@@ -45,2 +47,6 @@ function _interopNamespace(e) {

// NOTE: make sure to change the Route in server-runtime if you change this
// NOTE: make sure to change the EntryRoute in server-runtime if you change this
// Create a map of routes by parentId to use recursively instead of

@@ -59,22 +65,61 @@ // repeatedly filtering the manifest.

}
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;

@@ -84,24 +129,213 @@ return dataRoute;

}
function createClientRoutes(manifest, routeModulesCache, future, parentId = "", routesByParentId = groupRoutesByParentId(manifest)) {
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)
path: route.path
};
let children = createClientRoutes(manifest, routeModulesCache, future, route.id);
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: getShouldRevalidateFunction(future, routeModule, route.id, needsRevalidation)
});
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 (initialData !== undefined) {
return initialData;
}
if (initialError !== undefined) {
throw initialError;
}
return null;
}
// 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);
}
});
}
return {
...(lazyRoute.loader ? {
loader: lazyRoute.loader
} : {}),
...(lazyRoute.action ? {
action: lazyRoute.action
} : {}),
hasErrorBoundary: lazyRoute.hasErrorBoundary,
shouldRevalidate: getShouldRevalidateFunction(future, lazyRoute, route.id, needsRevalidation),
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;

@@ -111,10 +345,30 @@ return dataRoute;

}
function createShouldRevalidate(route, routeModules) {
return function (arg) {
let module = routeModules[route.id];
invariant(module, `Expected route module to be loaded for ${route.id}`);
if (module.shouldRevalidate) {
return module.shouldRevalidate(arg);
function getShouldRevalidateFunction(future, route, routeId, needsRevalidation) {
// During HDR we force revalidation for updated routes
if (needsRevalidation) {
return wrapShouldRevalidateForHdr(routeId, route.shouldRevalidate, needsRevalidation);
}
// Single fetch revalidates by default, so override the RR default value which
// matches the multi-fetch behavior with `true`
if (future.v3_singleFetch && route.shouldRevalidate) {
let fn = route.shouldRevalidate;
return opts => fn({
...opts,
defaultShouldRevalidate: true
});
}
return route.shouldRevalidate;
}
// 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 arg => {
if (!handledRevalidation) {
handledRevalidation = true;
return needsRevalidation.has(routeId);
}
return arg.defaultShouldRevalidate;
return routeShouldRevalidate ? routeShouldRevalidate(arg) : arg.defaultShouldRevalidate;
};

@@ -124,36 +378,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;
}

@@ -168,2 +434,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, {

@@ -175,3 +449,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-9af8868-20230412
* @remix-run/react v0.0.0-nightly-9bc6d9517-20241206
*

@@ -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-9af8868-20230412
* @remix-run/react v0.0.0-nightly-9bc6d9517-20241206
*

@@ -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.v3_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;

@@ -1,7 +0,22 @@

Copyright 2021 Remix Software Inc.
MIT License
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
Copyright (c) Remix Software Inc. 2020-2021
Copyright (c) Shopify Inc. 2022-2024
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
{
"name": "@remix-run/react",
"version": "0.0.0-nightly-9af8868-20230412",
"version": "0.0.0-nightly-9bc6d9517-20241206",
"description": "React DOM bindings for Remix",

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

"dependencies": {
"@remix-run/router": "1.5.0",
"react-router-dom": "6.10.0",
"use-sync-external-store": "1.2.0"
"@remix-run/router": "1.21.0",
"@remix-run/server-runtime": "0.0.0-nightly-9bc6d9517-20241206",
"react-router": "6.28.0",
"react-router-dom": "6.28.0",
"turbo-stream": "2.4.0"
},
"devDependencies": {
"@remix-run/server-runtime": "0.0.0-nightly-9af8868-20230412",
"@testing-library/jest-dom": "^5.16.2",
"@remix-run/node": "0.0.0-nightly-9bc6d9517-20241206",
"@remix-run/react": "0.0.0-nightly-9bc6d9517-20241206",
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.3.0",
"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",
"react-dom": ">=16.8"
"react": "^18.0.0",
"react-dom": "^18.0.0",
"typescript": "^5.1.0"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
},
"engines": {
"node": ">=14"
"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