1import * as Namespace from "../../Namespace.ts";
2import { makeFrameworkSite, type FrameworkSiteProps } from "./FrameworkSite.ts";
3
4/** The framework-integration package that drives the React Router build. */
5export const REACT_ROUTER_FRAMEWORK_SPECIFIER =
6 "@alchemy.run/frontend-frameworks/react-router";
7
8/** The Node container deploy target for the React Router build. */
9export const REACT_ROUTER_NODE_TARGET_SPECIFIER =
10 "@alchemy.run/frontend-frameworks/react-router/node";
11
12export interface ReactRouterProps extends FrameworkSiteProps {}
13
14/**
15 * Deploy a [React Router](https://reactrouter.com) v7 app (framework mode)
16 * to a Hetzner Cloud Server: the SSR server as a systemd unit on port 3000,
17 * static assets baked into the unit.
18 *
19 * The build runs through
20 * `@alchemy.run/frontend-frameworks/react-router` with the
21 * `@alchemy.run/frontend-frameworks/react-router/node` deploy target — both
22 * must be installed in your project, alongside `@react-router/dev`,
23 * `react-router`, and `vite`.
24 *
25 * Your `vite.config.ts` needs no adapter wiring. React Router's server
26 * build is a `ServerBuild` manifest rather than a request handler, so the
27 * integration makes the server pass's build input a module that wraps the
28 * manifest with `createRequestHandler` and packages the resulting fetch
29 * handler as a Node HTTP server.
30 *
31 * React Server Components (React Router's `unstable` RSC plugin) and
32 * multi-environment builds are not supported yet — the build fails with an
33 * actionable error when more than one server entry is emitted.
34 *
35 *
36 * ### Creating React Router Sites
37 * **Example:** Basic React Router App
38 * ```typescript
39 * const site = yield* Hetzner.Website.ReactRouter("Web", {
40 * rootDir: "./app",
41 * });
42 * ```
43 *
44 * **Example:** Custom Domain
45 * ```typescript
46 * const site = yield* Hetzner.Website.ReactRouter("Web", {
47 * rootDir: "./app",
48 * domain: "app.example.com",
49 * zone,
50 * });
51 * ```
52 *
53 * ### Server Configuration
54 * **Example:** Process Environment
55 * ```typescript
56 * const site = yield* Hetzner.Website.ReactRouter("Web", {
57 * rootDir: "./app",
58 * env: {
59 * API_BASE: "https://api.example.com",
60 * },
61 * });
62 * ```
63 *
64 * **Example:** Read An Environment Variable From A Loader
65 * ```typescript
66 * // app/routes/home.tsx
67 * export function loader() {
68 * return { apiBase: process.env.API_BASE ?? "unset" };
69 * }
70 * ```
71 *
72 * @resource
73 * @product Website
74 */
75export const ReactRouter = (id: string, props: ReactRouterProps = {}) =>
76 makeFrameworkSite(id, props, {
77 name: "ReactRouter",
78 framework: REACT_ROUTER_FRAMEWORK_SPECIFIER,
79 target: REACT_ROUTER_NODE_TARGET_SPECIFIER,
80 }).pipe(Namespace.push(id));