# @getforma/build

The build function, application configuration, manifests and size-report types.

Use build with a BuildConfig to produce a matching asset and manifest set. Compiler, browser assets and server inputs must describe the same build. This reference documents the library API; the build guide covers commands and project setup.

Published declaration: [@getforma/build@3.0.0](https://unpkg.com/@getforma/build@3.0.0/dist/index.d.ts).

## Values and functions

### build

Function export

```ts
declare function build(userConfig: BuildConfig): Promise<BuildResult>;
```

Run the Forma build pipeline.

Orchestrates: clean, CSS generation, font copying, esbuild bundling
(with optional SSR IR emission), island registry generation,
content hashing, compression, manifest generation, service worker
generation, and budget warnings.

A production build writes into a staging directory beside `outputDir` and
swaps it in only when every step has succeeded, so a build that fails part
way leaves the previous output exactly as it was, and nothing beside it
(L-21). The application embeds `outputDir` in its binary, and used to embed
half a build after a failure. Watch mode writes in place, as before.
Verified by: packages/build/tests/build.test.ts > "leaves the previous build byte-identical when a build fails after writing files"
Verified by: packages/build/tests/build.test.ts > "a failed first build leaves no output directory and nothing beside it"

Related declarations: [`BuildConfig`](/docs/3.0/api-build/#symbol-BuildConfig), [`BuildResult`](/docs/3.0/api-build/#symbol-BuildResult)

## Exported types

### AssetManifest

Interface export

```ts
interface AssetManifest {
    version: 2;
    writer: string;
    app: string;
    build: string;
    aliases: Record<string, string>;
    assets: Record<string, AssetMetadata>;
    routes: Record<string, RouteManifest>;
}
```



Related declarations: [`AssetMetadata`](/docs/3.0/api-build/#symbol-AssetMetadata), [`RouteManifest`](/docs/3.0/api-build/#symbol-RouteManifest)

**`build`**

The deployment identity (FORMA-186, DR-29): 16 lowercase hexadecimal
characters computed from the build's own outputs and the writers'
versions before anything is named, compiled into every JavaScript bundle
(`__FORMA_BUILD__`, which core compares with the page's
`data-forma-build`), and the same for identical inputs however often they
are built, from any directory when built from the project root (esbuild
hashes input paths relative to its working directory). `dev` in watch mode.

### AssetMetadata

Interface export

```ts
interface AssetMetadata {
    immutable: boolean;
    integrity: string;
    retained?: boolean;
}
```



**`immutable`**

The build guarantees this exact filename is immutable, independent of its spelling.

**`integrity`**

Full SHA-384 of the final uncompressed file, used by SRI and the worker.

**`retained`**

A file of the previous release kept for tabs still open on it (FORMA-186, L-25).

### BuildConfig

Interface export

```ts
interface BuildConfig {
    entryPoints: BuildEntry[];
    routes: Record<string, RouteMapping>;
    cssEntries?: CssEntry[];
    outputDir: string;
    formaAlias?: string;
    codeSplitting?: boolean;
    serviceWorker?: 'on-demand';
    app?: string;
    previewFrame?: boolean;
    fontDir?: string;
    ssr?: boolean;
    ssrEntryPoints?: Record<string, string>;
    watch?: boolean;
    budgetThreshold?: number;
    serverInlined?: string[];
    strict?: boolean;
    diagnosticsJson?: string;
    previousDist?: string;
    security?: boolean;
}
```



Related declarations: [`BuildEntry`](/docs/3.0/api-build/#symbol-BuildEntry), [`CssEntry`](/docs/3.0/api-build/#symbol-CssEntry), [`RouteMapping`](/docs/3.0/api-build/#symbol-RouteMapping)

**`entryPoints`**

JavaScript entry points to build

**`routes`**

Route-to-asset mapping for manifest generation

**`cssEntries`**

CSS entries to generate (tailwind and/or concatenation)

**`outputDir`**

Output directory for all built assets

**`formaAlias`**

3.x migration bridge for 'formajs'; warns once per build() with importing files. Prefer '@getforma/core'.

**`codeSplitting`**

Bundle one shared ESM graph, preserving import() as separately loaded chunks.

**`serviceWorker`**

Production worker policy; defaults to on-demand. Only requested, manifest-listed
immutable code/style/WASM assets with verified bytes enter its cache.

**`app`**

Application/worker ID: 1–63 lowercase letters, digits or hyphens, starting
with a letter or digit. If omitted, a valid nearest Cargo package name is
used. Otherwise supply it explicitly; it is always written to manifest2.

**`previewFrame`**

Copy `@getforma/core`'s preview-frame runtime into the output, for
forma-server's preview frame (`PageResponse::frame`, FORMA-125; FORMA-175).
Off by default, where the output is what it was.

On, the build copies `dist/formajs-frame.global.js` (core 2.1.0 and later;
never core's `dist/forma-runtime.js`, the CDN alias of the auto-mounting
runtime) from the core the bundles import, byte for byte, as
`forma-runtime.<hash>.js`, and names it in the manifest under
`aliases["forma-runtime.js"]`, where `PreviewFrame::load` reads it at start
and allows it by its SHA-256 hash. Which core: before anything is
written, each entry is bundled in memory with the bundles' own esbuild
options (so `formaAlias`, resolved from the working directory, and
tsconfig `paths` lead where they lead the bundles), and every file it
loads is mapped to its package; when the bundles import none, the core
installed where the build runs. Watch mode writes it unhashed as
`forma-runtime.js`, named the same way in the dev manifest. It is
inlined, never fetched: no compressed copies, and no place in the
worker's lists. A core without it fails the build with BUILD-805, a
runtime forma-server would refuse to inline (empty, not UTF-8,
`</script`, `<!--`, a carriage return or a NUL) with BUILD-806, and
bundles that import cores whose runtimes differ with BUILD-807, before
anything is written.

**`fontDir`**

Directory containing .woff2 font files to copy

**`ssr`**

Enable SSR IR emission

**`ssrEntryPoints`**

SSR entry points for IR emission (page name -> entry path)

**`watch`**

Enable watch mode

**`budgetThreshold`**

Budget threshold in bytes (Brotli-compressed), default 200_000

**`serverInlined`**

Files that should keep unhashed copies (e.g. for server-inlined assets).
A zero-JS SSR owner cannot request executable server inlining.

**`strict`**

Legacy compatibility option. SSR publication always requires the 3.0
server-renderable subset. False and FORMA_STRICT=0 cannot downgrade errors.
Every page is checked; watch retains its previous good product on failure.

**`diagnosticsJson`**

Also write every SSR diagnostic as JSON to this path (relative to the
working directory), whether or not the build fails: the machine-readable
report an agent can act on (N12). It must be outside `outputDir`, which
the build replaces whole and serves.

**`previousDist`**

The previous release's `dist/`, which the application's pipeline keeps as a
release artifact (FORMA-186, DR-29, L-25). The build copies that release's
own hashed assets into the new `dist/`, listed in the manifest `retained`
and `immutable`, so a tab opened before a deploy still loads its chunks
from the one `dist/` the application embeds. One generation: what the
previous release itself retained is not copied again. A directory other
than `outputDir`, which the build replaces whole. Leave it out for a first
deploy, or when no release is live. The previous dist is trusted like the source: the check below catches a damaged or
altered file, not someone who can also rewrite its manifest. Every copied file is checked against
the SHA-384 the previous manifest recorded (`BUILD-821`); a path that is
not a usable previous release fails the build (`BUILD-820`). Ignored in
watch mode.

**`security`**

Mark this release as one that fixes a vulnerability (L-16, SR45): nothing is
retained from `previousDist`, so no earlier build's asset is served and a
revoked build's tabs meet 404s for their lazy chunks. The application's
deploy pipeline must mark every such release, a Forma security upgrade
included. `FORMA_SECURITY_RELEASE=1` (or `true`, any case) in the environment marks it too; any
value other than 1, true, 0, false or empty fails the build (`BUILD-822`); and a
`false` here does not unmark it.

### BuildDiagnostic

Interface export

```ts
interface BuildDiagnostic {
    code: string;
    severity: 'error' | 'warning' | 'note';
    page: string;
    file: string;
    line: number;
    column: number;
    message: string;
    why: string;
    fix: string;
    chain?: BuildDiagnosticChainLink[];
}
```

One coded SSR diagnostic (FORMA-119). The catalogue, one entry per code
with its fix, is `packages/compiler/docs/subset.md`.

Related declarations: [`BuildDiagnosticChainLink`](/docs/3.0/api-build/#symbol-BuildDiagnosticChainLink)

**`code`**

The stable code, e.g. `BUILD-101`.

**`severity`**

Unsupported SSR constructs are always `error`; `note` never fails a build.

**`page`**

The page (entry outfile without `.js`) whose module reported it.

**`file`**

The file to edit; empty for a build-level diagnostic.

**`line`**

1-based line and column in `file`, as authored; 0 when unknown.

**`message`**

The specific report: the construct, what happens, and its fix.

**`why`**

The catalogue's why and fix for the code.

**`chain`**

BUILD-403: the composition chain from the page down.

### BuildDiagnosticChainLink

Interface export

```ts
interface BuildDiagnosticChainLink {
    component: string;
    file: string;
    line: number;
    column: number;
}
```

One link of a composition chain, from the page down (BUILD-403).

### BuildEntry

Interface export

```ts
interface BuildEntry {
    entry: string;
    outfile: string;
    ssr?: boolean;
}
```

**getforma** /build — Type definitions

Configuration and result types for the Forma build pipeline.

**`entry`**

Entry point file path

**`outfile`**

Output file path (relative to outputDir)

**`ssr`**

Disable SSR for this entry in an otherwise SSR-enabled application.

### BuildResult

Interface export

```ts
interface BuildResult {
    manifest: AssetManifest;
    buildHash: string;
    warnings: string[];
    diagnostics: BuildDiagnostic[];
    sizes: RouteSize[];
    sizeReport: SizeReport;
}
```



Related declarations: [`AssetManifest`](/docs/3.0/api-build/#symbol-AssetManifest), [`BuildDiagnostic`](/docs/3.0/api-build/#symbol-BuildDiagnostic), [`RouteSize`](/docs/3.0/api-build/#symbol-RouteSize), [`SizeReport`](/docs/3.0/api-build/#symbol-SizeReport)

**`diagnostics`**

Every coded SSR diagnostic the build reported, page by page.

**`sizes`**

The size report's routes (FORMA-021, FORMA-190): per route, its class, what
it loads cold in raw, gzip -9 and Brotli bytes, Forma's code
(`@getforma/core` and `alien-signals` as bundled) beside the application's,
the service worker it registers, and its ceilings. A production build fails
with `BUILD-819` before writing its output when a route is over one:
Forma's code over N2's `FORMA_CODE_CEILING_GZIP` (28,100 B; decision brief
L-64), all of its JavaScript over its class's (none for a route without an
interactive region, 75 KiB, 365 KiB for a canvas). Empty in watch mode,
whose unminified bytes are not what a route ships. The same objects as
`sizeReport.routes`.

**`sizeReport`**

The one report (FORMA-190): `sizes` with the service worker, the preview
frame's runtime when `previewFrame` is on (the frame document row), and the
build's times by phase. A production build prints it and writes it, without
the times (so the output stays the same bytes for the same sources), as
`size-report.json` beside `manifest.json`. No routes in watch mode.

### BuildTimes

Interface export

```ts
interface BuildTimes {
    totalMs: number;
    phases: {
        bundle: number;
        hash: number;
        compress: number;
        manifest: number;
    };
}
```

Wall-clock milliseconds, per phase of a production build and in all (N7).

### ByteSizes

Interface export

```ts
interface ByteSizes {
    raw: number;
    gzip: number;
    brotli: number;
}
```

Raw (minified), gzip -9 and Brotli-11 bytes.

### CssEntry

Interface export

```ts
interface CssEntry {
    input: string | string[];
    outfile: string;
    tailwind?: boolean;
}
```



**`input`**

Glob or array of CSS files to concatenate

**`outfile`**

Output file path (relative to outputDir)

**`tailwind`**

If true, run

**tailwindcss** /cli on the first input file

### FileSize

Interface export

```ts
interface FileSize extends ByteSizes {
    file: string;
}
```

A file the build writes that is not a route's: the service worker, the preview frame's runtime.

Related declarations: [`ByteSizes`](/docs/3.0/api-build/#symbol-ByteSizes)

### FrameSize

Interface export

```ts
interface FrameSize extends FileSize {
    sourceRaw: number;
}
```

The preview frame's runtime in the output: the served file, and core's before the build minified it.

Related declarations: [`FileSize`](/docs/3.0/api-build/#symbol-FileSize)

**`sourceRaw`**

Raw bytes of core's `dist/formajs-frame.global.js`.

### RouteCeilings

Interface export

```ts
interface RouteCeilings {
    forma: number;
    javascript: number;
}
```

What a class may ship, bytes gzip -9: Forma's code, and all of the route's JavaScript.

### RouteClass

Type alias export

```ts
type RouteClass = 'no-interactive-region' | 'islands' | 'whole-page-mount';
```

How a route is classified (DR-35).

### RouteManifest

Interface export

```ts
interface RouteManifest {
    mode: 'ssr' | 'browser';
    js: string[];
    modulepreload?: string[];
    css: string[];
    fonts: string[];
    total_size_br: number;
    budget_warn_threshold: number;
    ir?: string;
}
```



**`modulepreload`**

Static ESM dependencies only; dynamic import() chunks are intentionally excluded.

### RouteMapping

Interface export

```ts
interface RouteMapping {
    js: string[];
    css: string[];
    fonts?: string[];
    canvas?: boolean;
}
```



**`js`**

Configured entry base names. The first selects the native owner before
static SSR removes its browser output; additional entries must be Browser.

**`css`**

Base names (without extension) of CSS files for this route

**`fonts`**

Font file names for this route (optional, defaults to all fonts in outputDir)

**`canvas`**

Declare the route a canvas (N2, FORMA-190): a page that hosts many modules
and is JavaScript-heavy by design. The size report holds all of its
JavaScript to 365 KiB gzip -9 instead of the 75 KiB of other pages; Forma's
own code stays under N2's 28,100 B either way. It raises nothing for a
route without JavaScript. A heavy page that is not a canvas is trimmed or
split, not declared.

### RouteSize

Interface export

```ts
interface RouteSize extends ByteSizes {
    route: string;
    class: RouteClass;
    canvas: boolean;
    files: RouteSizeFile[];
    forma: ByteSizes;
    application: ByteSizes;
    worker: ByteSizes;
    javascript: ByteSizes;
    ceilings: RouteCeilings;
    exceeded: Array<'forma' | 'javascript'>;
    over: boolean;
}
```

One route's size report.

Related declarations: [`ByteSizes`](/docs/3.0/api-build/#symbol-ByteSizes), [`RouteCeilings`](/docs/3.0/api-build/#symbol-RouteCeilings), [`RouteClass`](/docs/3.0/api-build/#symbol-RouteClass), [`RouteSizeFile`](/docs/3.0/api-build/#symbol-RouteSizeFile)

**`class`**

What the route is: the ceilings follow from it (`ceilings`).

**`canvas`**

The route is declared a canvas (`canvas: true`): 365 KiB of JavaScript instead of 75 KiB.

**`files`**

The files the route loads cold: its `js` and `modulepreload` lists. Empty for a route with no JavaScript.

**`forma`**

`@getforma/core` and `alien-signals` as bundled: N2's figure is `forma.gzip`.

**`application`**

Everything else in the route's files: the application's own code and its other dependencies.

**`worker`**

The service worker the route registers: `sw.js` for a route with JavaScript, zero for one without.

**`javascript`**

All the JavaScript the route ships: `files` and `worker`. The class's JavaScript ceiling is measured on `javascript.gzip`.

**`exceeded`**

Which ceilings the route is over: Forma's code, all of its JavaScript, or both.

**`over`**

`exceeded` is not empty: the build fails.

### RouteSizeFile

Interface export

```ts
interface RouteSizeFile extends ByteSizes {
    file: string;
    forma: ByteSizes;
}
```

One file a route loads cold, with Forma's share of it.

Related declarations: [`ByteSizes`](/docs/3.0/api-build/#symbol-ByteSizes)

### SizeReport

Interface export

```ts
interface SizeReport {
    version: 1;
    ceilings: {
        forma: number;
        page: number;
        canvas: number;
    };
    routes: RouteSize[];
    worker?: FileSize;
    frame?: FrameSize;
    times?: BuildTimes;
}
```

The one report of a production build.

Related declarations: [`BuildTimes`](/docs/3.0/api-build/#symbol-BuildTimes), [`FileSize`](/docs/3.0/api-build/#symbol-FileSize), [`FrameSize`](/docs/3.0/api-build/#symbol-FrameSize), [`RouteSize`](/docs/3.0/api-build/#symbol-RouteSize)

**`ceilings`**

The policy: Forma's code per route, then all of a page's JavaScript, then a canvas's (gzip -9 bytes).

**`worker`**

The generated `sw.js`.

**`frame`**

The preview frame's runtime, inlined into every preview document and never fetched (`previewFrame`); the frame document row.

**`times`**

In the printed report and the build result; never in the file.