Skip to content

Commit

Permalink
chore: re-add RemixContext
Browse files Browse the repository at this point in the history
Signed-off-by: Logan McAnsh <logan@mcan.sh>
  • Loading branch information
mcansh committed Dec 22, 2022
1 parent 5d25c61 commit 6749331
Show file tree
Hide file tree
Showing 2 changed files with 125 additions and 22 deletions.
17 changes: 15 additions & 2 deletions packages/remix-react/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export {
useLoaderData,
useMatches,
useActionData,
RemixContext as UNSAFE_RemixContext,
} from "./components";

export type { FormMethod, FormEncType } from "./data";
Expand All @@ -58,7 +59,10 @@ export type { ThrownResponse } from "./errors";
export { useCatch } from "./errorBoundaries";

export type { HtmlLinkDescriptor } from "./links";
export type { HtmlMetaDescriptor } from "./routeModules";
export type {
HtmlMetaDescriptor,
RouteModules as UNSAFE_RouteModules,
} from "./routeModules";

export { ScrollRestoration } from "./scroll-restoration";

Expand All @@ -67,4 +71,13 @@ export { RemixServer } from "./server";

export type { Fetcher } from "./transition";

export type { RouteData as UNSAFE_RouteData } from "./routeData";
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";
130 changes: 110 additions & 20 deletions packages/remix-testing/create-remix-stub.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import * as React from "react";
import type { UNSAFE_RouteData as RouteData } from "@remix-run/react";
import type { InitialEntry, Router } from "@remix-run/router";
import type { HydrationState, InitialEntry, Router } from "@remix-run/router";
import { UNSAFE_RemixContext as RemixContext } from "@remix-run/react";
import type {
UNSAFE_FutureConfig as FutureConfig,
UNSAFE_AssetsManifest as AssetsManifest,
UNSAFE_EntryRoute as EntryRoute,
UNSAFE_RouteManifest as RouteManifest,
UNSAFE_RouteModules as RouteModules,
UNSAFE_RemixContextObject as RemixContextObject,
} from "@remix-run/react";
import type { RouteObject } from "react-router-dom";
import { createMemoryRouter, RouterProvider } from "react-router-dom";

Expand All @@ -14,16 +22,13 @@ type RemixStubOptions = {
initialEntries?: InitialEntry[];

/**
* Used to set the route's initial loader data.
* e.g. initialLoaderData={{ "/contact": { locale: "en-US" } }}
* Used to set the route's initial loader and action data.
* e.g. hydrationData={{
* loaderData: { "/contact": { locale: "en-US" } },
* actionData: { "/login": { errors: { email: "invalid email" } }}
* }}
*/
initialLoaderData?: RouteData;

/**
* Used to set the route's initial action data.
* e.g. initialActionData={{ "/login": { errors: { email: "invalid email" } }}
*/
initialActionData?: RouteData;
hydrationData?: HydrationState;

/**
* The initial index in the history stack to render. This allows you to start a test at a specific entry.
Expand All @@ -33,27 +38,112 @@ type RemixStubOptions = {
* initialIndex: 1 // start at "/events/123"
*/
initialIndex?: number;

remixConfigFuture?: Partial<FutureConfig>;
};

export function createRemixStub(routes: RouteObject[]) {
type StubRouteObject = Omit<
RouteObject,
"id" | "handle" | "shouldRevalidate" | "errorElement" | "hasErrorBoundary"
>;

export function createRemixStub(routes: StubRouteObject[]) {
return function RemixStub({
initialEntries,
initialIndex,
initialActionData,
initialLoaderData,
hydrationData,
remixConfigFuture,
}: RemixStubOptions) {
let routerRef = React.useRef<Router>();
let remixContextRef = React.useRef<RemixContextObject>();

if (routerRef.current == null) {
routerRef.current = createMemoryRouter(routes, {
routerRef.current = createMemoryRouter(routes as RouteObject[], {
initialEntries,
initialIndex,
hydrationData: {
actionData: initialActionData,
loaderData: initialLoaderData,
},
hydrationData,
});
}

return <RouterProvider router={routerRef.current} />;
if (remixContextRef.current == null) {
remixContextRef.current = {
future: {
v2_meta: false,
...remixConfigFuture,
},
manifest: createManifest(routes as RouteObject[]),
routeModules: createRouteModules(routes as RouteObject[]),
};
}

return (
<RemixContext.Provider value={remixContextRef.current}>
<RouterProvider router={routerRef.current} />
</RemixContext.Provider>
);
};
}

function createManifest(routes: RouteObject[]): AssetsManifest {
return {
routes: createRouteManifest(routes),
entry: { imports: [], module: "" },
url: "",
version: "",
};
}

function createRouteManifest(
routes: RouteObject[],
manifest?: RouteManifest<EntryRoute>,
parentId?: string
): RouteManifest<EntryRoute> {
return routes.reduce((manifest, route) => {
if (route.children) {
createRouteManifest(route.children, manifest, route.id);
}
manifest[route.id!] = convertToEntryRoute(route, parentId);
return manifest;
}, manifest || {});
}

function createRouteModules(
routes: RouteObject[],
routeModules?: RouteModules
): RouteModules {
return routes.reduce((modules, route) => {
if (route.children) {
createRouteModules(route.children, modules);
}

modules[route.id!] = {
CatchBoundary: undefined,
ErrorBoundary: undefined,
// @ts-expect-error - types are still `agnostic` here
default: () => route.element,
handle: route.handle,
links: undefined,
meta: undefined,
shouldRevalidate: undefined,
};
return modules;
}, routeModules || {});
}

function convertToEntryRoute(
route: RouteObject,
parentId?: string
): EntryRoute {
return {
id: route.id!,
index: route.index,
caseSensitive: route.caseSensitive,
path: route.path,
parentId,
hasAction: !!route.action,
hasLoader: !!route.loader,
module: "",
hasCatchBoundary: false,
hasErrorBoundary: false,
};
}

0 comments on commit 6749331

Please sign in to comment.