# Deploy on Vercel

What `recit build` writes is static: an `index.html`, its `assets/`, nothing to run on a server. Any
static host serves it; this page walks through Vercel, from a Git repository or from your machine.

## What gets deployed

| You built                                | Vercel receives                                                     |
| ---------------------------------------- | ------------------------------------------------------------------- |
| a deck or a story, `recit build talk.md` | the `dist/` folder, served as static files                          |
| a site on the Récit site framework       | `.vercel/output` (Build Output API v3), written by the build itself |

A site on the Récit site framework detects Vercel by itself: when the `VERCEL` environment variable
is set (Vercel sets it, and so does `vercel build`), the build writes `.vercel/output` next to
`dist/`, with the prerendered pages, the redirects of `recit.config.ts`, clean URLs (`/guide` rather
than `/guide.html`, with a 308 from the old form) and the Markdown version of each page for clients
that ask for it. Nothing to configure.

## `vercel.json`

One file at the root of the project tells Vercel how to install and build. For a deck:

```json
{
  "$schema": "https://openapi.vercel.sh/vercel.json",
  "framework": null,
  "installCommand": "pnpm install",
  "buildCommand": "pnpm exec recit build talk.md -o dist",
  "outputDirectory": "dist"
}
```

For a site on the Récit site framework, leave `outputDirectory` out (`null`): the build hands Vercel
its own `.vercel/output`.

```json
{
  "$schema": "https://openapi.vercel.sh/vercel.json",
  "framework": null,
  "installCommand": "pnpm install",
  "buildCommand": "pnpm run build",
  "outputDirectory": null
}
```

`framework: null` stops Vercel from guessing a framework and overriding these commands.

## From Git

1. Push the project to GitHub, GitLab or Bitbucket.
2. On [vercel.com/new](https://vercel.com/new), import the repository. In a monorepo, set **Root
   Directory** to the folder of the deck or site (`talks/intro`, `sites/docs`); `pnpm install` run
   from there installs the whole workspace.
3. Leave **Framework Preset** on "Other": `vercel.json` gives the commands.
4. Deploy. Every push to the production branch updates the site; every other branch or pull request
   gets a preview URL.

In a monorepo with several sites, add an `ignoreCommand` to each `vercel.json` so that a push
rebuilds only the sites it touched, for example `"ignoreCommand": "npx nx-ignore <project>"` with
Nx.

## From your machine

The [Vercel CLI](https://vercel.com/docs/cli) builds on your machine and uploads the result. Run it
from the folder of the deck or site:

```bash
pnpm dlx vercel login                       # once
pnpm dlx vercel link                        # once: picks or creates the Vercel project
pnpm dlx vercel pull --yes                  # the project's settings and environment variables
pnpm dlx vercel build                       # runs vercel.json's build, writes .vercel/output
pnpm dlx vercel deploy --prebuilt           # uploads it: a preview URL
```

For production, pull its environment and add `--prod` to the last two:

```bash
pnpm dlx vercel pull --yes --environment=production
pnpm dlx vercel build --prod
pnpm dlx vercel deploy --prebuilt --prod
```

`--prebuilt` uploads what you built, so the deployment is exactly what you checked locally. A plain
`vercel deploy --prod` uploads the sources and lets Vercel build them instead.

In a monorepo with one Vercel project per site, link the repository once from its root with
`vercel link --repo`: the CLI then picks the right project from the folder you run it in.

Add `.vercel` to `.gitignore`: it holds the link and the build output, never the sources.

## Domains

In the project's **Settings → Domains**, add the domain, then at your registrar point a subdomain to
Vercel with a `CNAME` to `cname.vercel-dns.com` (an apex domain takes the `A` record Vercel shows).
Vercel issues the certificate. Another name that should land on the same site (`www`, an old domain)
is added as a redirect to the main one.

## Récit's own sites

The sites of the Récit repository (`recit.dev`, `docs.recit.dev`…) deploy this way, one Vercel
project per site, with commands that wrap the Vercel CLI. From the repository root:

```bash
pnpm vercel:login                 # once per machine
export VERCEL_SCOPE=recit         # the team
pnpm vercel:project --domain      # once: create the projects, connect Git, add the domains
pnpm vercel:domains               # check the DNS of every domain
pnpm vercel:deploy docs           # a preview of docs.recit.dev
pnpm vercel:deploy:prod docs www  # production
```

`vercel:project` can be replayed: it creates a missing project and updates an existing one (Root
Directory, Node version, link, Git connection). Without a site name, every command acts on every
site that has a `vercel.json`; `--dry-run` prints the calls without running them. A production
deployment refuses uncommitted changes unless given `--allow-dirty`. `.github/VERCEL.md` in the
repository lists the whole procedure, down to the DNS records.
