crossbind
GitHub
GUIDE · CONCEPTS · CONFIGURATION

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

cppjs.config.js
export default {
paths: {
config: import.meta.url,
},
};
Always set paths.config
It anchors every relative path to this file. Without it, a build started from another working directory resolves your sources against the wrong root.

Identity and dependencies

KeyWhat it does
general.namethe logical name: output binaries (lib<name>.a), and the browser filesystem namespace /opfs/<name>/. Defaults to your package name.
dependenciesan array of other packages' configs, imported from @cpp.js/package-*/cppjs.config.js. Transitive dependencies are flattened automatically.
cargoDependenciescrates importable through cargo: - keys are crate names, values are Cargo dependency specs. See Rust.
cppjs.config.js
import gdal from '@cpp.js/package-gdal/cppjs.config.js';
 
export default {
general: { name: 'my-geo-app' },
dependencies: [gdal],
cargoDependencies: { uuid: '{ version = "1", features = ["v4"] }' },
paths: { config: import.meta.url },
};

One consequence worth remembering: if any dependency is built multithreaded, your project is promoted to mt as well.

Paths

KeyDefaultWhat it points at
config-always import.meta.url
projectthe directory of configthe 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.cppjsbuild cache
build.cppjs/buildstaging directory
outputsame as buildwhere dist artifacts are written
header, module, bridge, cmakederived from nativeoverride 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:

cppjs.config.js
export default {
paths: {
config: import.meta.url,
base: '../..',
},
};

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.

cppjs.config.js
export default {
paths: { config: import.meta.url },
target: { runtime: 'mt' },
};

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.

cppjs.config.js
targetSpecs: [
{
platform: 'wasm',
specs: {
cmake: ['-DSOMETHING=ON'],
emccFlags: ['-sINITIAL_MEMORY=128MB'],
env: { GDAL_NUM_THREADS: '0' },
data: { 'share/myapp': 'myapp/data' },
ignoreLibName: ['libtiff_legacy'],
},
},
]

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.

KeyWhat it does
type'cmake' (default, the C/C++ pipeline) or 'cargo' (a Rust crate built per platform)
cratecargo only: the directory holding Cargo.toml
libNamethe .a basenames produced, one per entry
header, libPaththe include and library directory names inside dist
bindings.vectorsextra Vec<T> classes to expose without touching Rust source
wholeArchivelink every archive wholesale instead of eliminating dead code - only when members self-register from static initialisers

TypeScript output

KeyWhat it does
dts'sync' (default) or 'promise', which wraps every generated return in Promise<...> for worker runtimes
typespackage authors: emit one combined .d.ts over the public headers and wire package.json so consumers importing <pkg>/<any>.h get types

The rest

KeyWhat it does
extwhich extensions count as headers, sources and SWIG modules
builddo not set this directly - build hooks live in cppjs.build.js and are merged in
extensionsplugin objects with hooks at config-load and build-step boundaries; for sharing one override across several packages
functions.isEnabledoverride 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:

src/main.js
const m = await initNative({
useWorker: true,
fs: { opfs: true },
env: { TMPDIR: '_CPPJS_DATA_PATH_/scratch' },
logHandler: (text) => console.debug(text),
errorHandler: (text) => console.error(text),
});

The full option list is in the API reference; the ones that change behaviour most are covered in Threading and Filesystem.

Type to search every guide page and section.
↑↓ navigate↵ openesc close