1import * as Namespace from "../../Namespace.ts";
2import { makeFrameworkSite, type FrameworkSiteProps } from "./FrameworkSite.ts";
3
4/**
5 * The framework-integration module that drives `next build` plus a Node
6 * `next({ dev: false })` serve entry. This module IS the Node pipeline
7 * (not OpenNext).
8 */
9export const NEXTJS_NODE_FRAMEWORK_SPECIFIER =
10 "@alchemy.run/frontend-frameworks/nextjs/node";
11
12export interface NextjsProps extends FrameworkSiteProps {}
13
14/**
15 * Deploy a Next.js application to a Hetzner Cloud Server: `next build`
16 * then a long-running `next({ dev: false })` systemd unit on port 3000.
17 * Does **not** use OpenNext (those wrappers are Lambda/workerd).
18 *
19 * The `.next` output (and `public/` when present) is packed into the
20 * unit archive. `next`, `react`, and `react-dom` are installed on the
21 * unit with `npm install` rather than bundled.
22 *
23 * During `alchemy dev` the site is `next dev` and no cloud resources
24 * are declared; `Alchemy.remote()` opts back into the live Service.
25 *
26 *
27 * ### Creating Next.js Sites
28 * **Example:** Basic Next.js App
29 * ```typescript
30 * const site = yield* Hetzner.Website.Nextjs("Web", {
31 * rootDir: "./app",
32 * });
33 * ```
34 *
35 * **Example:** Custom Domain
36 * ```typescript
37 * const site = yield* Hetzner.Website.Nextjs("Web", {
38 * rootDir: "./app",
39 * domain: "app.example.com",
40 * zone,
41 * });
42 * ```
43 *
44 * @resource
45 * @product Website
46 */
47export const Nextjs = (id: string, props: NextjsProps = {}) =>
48 makeFrameworkSite(id, props, {
49 name: "Nextjs",
50 framework: NEXTJS_NODE_FRAMEWORK_SPECIFIER,
51 target: NEXTJS_NODE_FRAMEWORK_SPECIFIER,
52 skipClientAssets: true,
53 install: ["next", "react", "react-dom"],
54 }).pipe(Namespace.push(id));