Configuration
cppjs.config.js sits at your project root and is read once by the build. It is build-time only - runtime options belong in the initNative(opts) call instead. Most projects need three lines of it; the rest of this page is what the remaining keys do when you need them.
The minimum
Identity and dependencies
| Key | What it does |
|---|---|
general.name | the logical name: output binaries (lib<name>.a), and the browser filesystem namespace /opfs/<name>/. Defaults to your package name. |
dependencies | an array of other packages' configs, imported from @cpp.js/package-*/cppjs.config.js. Transitive dependencies are flattened automatically. |
cargoDependencies | crates importable through cargo: - keys are crate names, values are Cargo dependency specs. See Rust. |
One consequence worth remembering: if any dependency is built multithreaded, your project is promoted to mt as well.
Paths
| Key | Default | What it points at |
|---|---|---|
config | - | always import.meta.url |
project | the directory of config | the project root |
base | - | an alternative root - use it in a monorepo so Docker can see the whole workspace |
native | ['src/native'] | an array of C++ source roots; order sets include precedence |
cache | .cppjs | build cache |
build | .cppjs/build | staging directory |
output | same as build | where dist artifacts are written |
header, module, bridge, cmake | derived from native | override only when your layout differs |
Docker mounts the project path as its working directory and cannot see anything above it. In a monorepo, point base at the repository root:
Target
target narrows what gets built. runtime is the one people set most - 'st' (default) or 'mt'; see Threading for what mt demands from your host.
targetSpecs
Per-target overrides, expressed as filter plus specs. Any of platform, arch, runtime, buildType and runtimeEnv may appear in the filter; an entry applies when every field you set matches.
Shipping data files is the most common use - that has its own page, Assets.
Export
Only relevant when you publish a package. It describes what consumers receive.
| Key | What it does |
|---|---|
type | 'cmake' (default, the C/C++ pipeline) or 'cargo' (a Rust crate built per platform) |
crate | cargo only: the directory holding Cargo.toml |
libName | the .a basenames produced, one per entry |
header, libPath | the include and library directory names inside dist |
bindings.vectors | extra Vec<T> classes to expose without touching Rust source |
wholeArchive | link every archive wholesale instead of eliminating dead code - only when members self-register from static initialisers |
TypeScript output
| Key | What it does |
|---|---|
dts | 'sync' (default) or 'promise', which wraps every generated return in Promise<...> for worker runtimes |
types | package authors: emit one combined .d.ts over the public headers and wire package.json so consumers importing <pkg>/<any>.h get types |
The rest
| Key | What it does |
|---|---|
ext | which extensions count as headers, sources and SWIG modules |
build | do not set this directly - build hooks live in cppjs.build.js and are merged in |
extensions | plugin objects with hooks at config-load and build-step boundaries; for sharing one override across several packages |
functions.isEnabled | override the "is this target enabled?" check, which by default asks whether the output already exists |
Reach for the highest layer that solves your problem - see the override hierarchy. Machine-wide settings (runner, Xcode team, log level, local wasi-sdk) live in ~/.cppjs.json and never belong in the project config.
Not in this file: runtime options
A frequent mistake is putting useWorker: true in cppjs.config.js, where it does nothing. Runtime options are arguments to the boot call:
The full option list is in the API reference; the ones that change behaviour most are covered in Threading and Filesystem.