1import type { InputProps } from "../../Input.ts";
2import * as Namespace from "../../Namespace.ts";
3import { makeFrameworkSite, type FrameworkSiteProps } from "./FrameworkSite.ts";
4
5/** The framework-integration package that drives the React Router build. */
6export const REACT_ROUTER_FRAMEWORK_SPECIFIER =
7 "@alchemy.run/frontend-frameworks/react-router";
8
9/** The AWS Lambda deploy target for the React Router build. */
10export const REACT_ROUTER_AWS_TARGET_SPECIFIER =
11 "@alchemy.run/frontend-frameworks/react-router/aws";
12
13export interface ReactRouterProps extends FrameworkSiteProps {}
14
15/**
16 * Deploy a [React Router](https://reactrouter.com) v7 app (framework mode)
17 * to AWS: the SSR server on a streaming Lambda Function URL, client assets
18 * in S3, and a CloudFront distribution whose edge router serves uploaded
19 * files from S3 and forwards everything else to the server.
20 *
21 * The build runs through
22 * `@alchemy.run/frontend-frameworks/react-router` with the
23 * `@alchemy.run/frontend-frameworks/react-router/aws` deploy target — both
24 * must be installed in your project, alongside `@react-router/dev`,
25 * `react-router`, and `vite`.
26 *
27 * Your `vite.config.ts` needs no adapter wiring. React Router's server
28 * build is a `ServerBuild` manifest rather than a request handler, so the
29 * integration makes the server pass's build input a module that wraps the
30 * manifest with `createRequestHandler`, forces the SSR bundle to be
31 * self-contained, and packages the resulting fetch handler as a streaming
32 * Lambda handler.
33 *
34 * React Server Components (React Router's `unstable` RSC plugin) and
35 * multi-environment builds are not supported yet — the build fails with an
36 * actionable error when more than one server entry is emitted.
37 *
38 * ### Creating React Router Sites
39 * **Example:** Basic React Router App
40 * ```typescript
41 * const site = yield* AWS.Website.ReactRouter("Web", {
42 * rootDir: "./app",
43 * });
44 * ```
45 *
46 * **Example:** Custom Domain
47 * ```typescript
48 * const site = yield* AWS.Website.ReactRouter("Web", {
49 * rootDir: "./app",
50 * domain: {
51 * name: "app.example.com",
52 * hostedZoneId: zone.hostedZoneId,
53 * },
54 * });
55 * ```
56 *
57 * ### Server Configuration
58 * **Example:** Tune The Server Function
59 * ```typescript
60 * const site = yield* AWS.Website.ReactRouter("Web", {
61 * rootDir: "./app",
62 * memorySize: 2048,
63 * env: {
64 * API_BASE: api.url,
65 * },
66 * });
67 * ```
68 *
69 * **Example:** Read An Environment Variable From A Loader
70 * ```typescript
71 * // app/routes/home.tsx
72 * export function loader() {
73 * return { apiBase: process.env.API_BASE ?? "unset" };
74 * }
75 * ```
76 *
77 * ### Shared Router
78 * **Example:** Serve Through An Existing Router
79 * ```typescript
80 * const router = yield* AWS.Website.Router("FrontDoor", {});
81 *
82 * const site = yield* AWS.Website.ReactRouter("Web", {
83 * rootDir: "./app",
84 * domain: { router },
85 * });
86 * ```
87 *
88 * @resource
89 */
90export const ReactRouter = (
91 id: string,
92 props: InputProps<ReactRouterProps> = {},
93) =>
94 makeFrameworkSite(id, props, {
95 name: "ReactRouter",
96 framework: REACT_ROUTER_FRAMEWORK_SPECIFIER,
97 target: REACT_ROUTER_AWS_TARGET_SPECIFIER,
98 }).pipe(Namespace.push(id));