Skip to content

chant build

chant build [path] [flags]
chant build [path] --components --generate <lexicon> [--env <name>] [-o <file>]

chant build loads the project at the given path, parses all TypeScript source files, evaluates resource definitions, and serializes the output.

It reaches no network. Every schema it needs is generated into the lexicon packages ahead of time, and a guard test runs the build over every example project with the socket layer replaced by a throwing stub — see Network Egress.

FlagTypeDefaultDescription
-o, --outputstringstdoutWrite output to file
-f, --formatstringjsonOutput format: json or yaml
-d, --lexiconstringallBuild only the specified lexicon (e.g. aws, gitlab)
-w, --watchboolfalseWatch for file changes and rebuild
--fold / --no-foldbooltrueFold source files statically instead of importing and running them. On by default since #1134; --no-fold runs every file instead. Folding falls back to running a file, per file, for anything outside the fold subset — summarized as fold: N files folded, M ran, with the per-file [fold:fold] / [fold:run] <reason> lines under --verbose
-v, --verboseboolfalseList every resolved build parameter and every per-file fold decision, and print a success line with the resource count. Without it each group is one summary line
--sandboxboolfalseRun run-fallback source files (or every file, without --fold) together, isolated, in one sandboxed child process instead of in-process (opt-in). No filesystem write, no process spawn, no worker threads, no ambient environment visible to project source; network egress is not blocked — see Sandboxed Execution
--param <name=value>stringBind a declared build-time parameter (chant.config.ts’s buildParams) to a value; repeatable. See Build-Time Parameters
--params-file <path>stringJSON file of { "name": value } build-time parameter values; second precedence, after --param
--env <name>stringActive environment. Sets CHANT_ENV so env-aware source re-evaluates for that environment, and selects the organizational policy that applies. Must be declared under environments in chant.config.ts when that list exists
--components --generate <lexicon>stringGenerate mode. Synthesize a CI pipeline from the discovered *.component.ts declarations instead of running a lexicon build. See Generate mode
--fold-rank [path]bool or stringoffAfter a folded build, print the run-fallback blockers ranked by how many files each one keeps from folding. With a path, also write the ranking in collapsed-stack format to that file for a flame-graph viewer. No-op with --no-fold
Terminal window
# Build from current directory
chant build
# Build from specific directory
chant build ./infra
# Output to file
chant build --output stack.json
# YAML output
chant build --format yaml --output stack.yaml
# Watch mode
chant build --watch
# Import and run every file instead of folding (pre-#1134 behavior)
chant build --no-fold
# Isolate run-fallback files in a sandboxed child process (opt-in)
chant build --sandbox
# Bind a declared build-time parameter (repeatable)
chant build --param tier=production --param env=staging
# Or from a JSON file
chant build --params-file ./params.prod.json
# Re-evaluate env-aware source for one environment
chant build --env prod
# Rank what keeps files from folding; also export a flame-graph input
chant build --fold-rank
chant build --fold-rank ./fold-blockers.collapsed
# Generate a CI pipeline from components instead of building resources
chant build --components --generate github --env staging --output .github/workflows/components.yml

chant build reduces each file’s AST directly to the resource spec by default, with no module execution — a file outside the supported subset (see Folded vs Run) falls back to being imported and run, transparently, per file. Nothing about how you write resources changes.

Pass --no-fold to import and run every file instead, which is the pre-#1134 behavior. The same switch is settable project-wide:

chant.config.ts
export default {
build: { fold: false },
};

An explicit --fold/--no-fold flag always wins over build.fold when both are present.

Output goes to stdout. Everything else goes to stderr, in this order: a one-line count of resolved build parameters, a one-line fold summary, then any warnings and errors, so the lines you can act on are at the bottom.

ℹ 23 build parameters resolved (4 from env, 19 default); --verbose to list
ℹ fold: 8 files folded, 13 ran (--verbose for reasons)
⚠ [apiDeployment] ...

A file that ran instead of folding is not a problem. It is the expected outcome for source that computes values in functions, and the build output is the same either way. Pass --verbose to see each [param] name = value (source) line and each [fold:fold] / [fold:run] <reason> line.

--sandbox runs every run-fallback file for the build together, isolated, in one child process with the filesystem, process-spawn, worker-thread, and environment access locked down — see Sandboxed Execution for exactly what that isolates (and, importantly, what it doesn’t: Node has no way to block network egress, so that’s a documented residual risk with its own deployment guidance, not something this flag claims to close). It composes with --fold: the run-fallback remainder is what gets sandboxed.

With --sandbox, no project source executes in the CLI’s process at all. That is slightly stricter than folding alone: folding a SomeComposite({...}) call or a new SomeType({...}) has to invoke the real factory or constructor, so when those are imported from a project file rather than a lexicon package, the file falls back to the sandboxed run path instead of folding. Sandboxed Execution reports what that costs across the example corpus.

chant.config.ts
export default {
build: { fold: true, sandbox: true },
};

The --sandbox flag always wins over build.sandbox when both are present.

Generate mode: CI pipelines from components

Section titled “Generate mode: CI pipelines from components”

--components --generate <lexicon> switches the build to the component discovery path. It reads every *.component.ts under the path, orders them by dependsOn into waves, and asks the named lexicon’s generateComponentPipeline to render those waves as CI stages and jobs. The github, gitlab and forgejo lexicons implement it; naming any other lexicon fails with a message saying so.

A components-only project need not declare a lexicon plugin at all. Lexicons are loaded best-effort here, so a project whose chant.config.ts lists none still generates.

--env <name> is threaded into the generated pipeline as the environment it deploys, and --param / --params-file bind build-time parameters the same way they do for a resource build. With --format json the result is { stages, jobs, yaml, env } on stdout, where env is the generator’s own resolved environment (present whenever the generator returns one; omitted otherwise) rather than something a consumer has to re-derive by parsing the YAML back. The same stages, jobs and env are available as graph IR through chant graph --components --format ir --projection <lexicon>.

--param name=value (repeatable) and --params-file <path> bind declared build-time parameters — chant.config.ts’s buildParams — so source can read params.<name> (import { params } from "@intentius/chant/params") instead of process.env. Unlike the AWS lexicon’s deploy-time Parameter(), a build-time parameter resolves before synthesis, so its value can change which resources are produced at all. An unknown name, a missing required value, or a value outside a declared enum is reported as a build error naming the parameter — never a thrown error from inside project source. The resolution is summarized on one line (23 build parameters resolved (4 from env, 19 default)), listed one per line as [param] name = value (source) under --verbose, and recorded on the build result for provenance. See Build-Time Parameters for the full picture, including precedence and why it folds to a literal.

CodeMeaning
0Success
1Errors found