cppjs.config.js — Build-time Configuration
Build-time only. Read once by the
cppjs buildCLI. NOT consumed at runtime. Runtime configuration lives ininit.md.
Every consumer (app or library) writes a cppjs.config.js at the project root. It declares dependencies, source paths, build target, and export shape — everything the CLI needs to compile and package your C++.
Shape
export default {
// ─────────────────────────────────────────────────────────────
// General identity
// ─────────────────────────────────────────────────────────────
general: {
name: 'myapp',
// Logical app/lib name. Used for:
// - Output binary names (lib<name>.a, <name>.xcframework)
// - Browser fs namespace: /opfs/<name>/, /memfs/<name>/
// Defaults to fixPackageName(package.json.name) or 'cppjssample'.
},
// ─────────────────────────────────────────────────────────────
// Other cpp.js packages this project depends on
// ─────────────────────────────────────────────────────────────
dependencies: [],
// Array of cppjs.config.js values, imported from @cpp.js/package-*.
// Example:
// import gdal from '@cpp.js/package-gdal/cppjs.config.js'
// dependencies: [gdal]
//
// Transitive deps are automatically flattened into config.allDependencies.
// If ANY dep declares target.runtime === 'mt', this project auto-promotes
// to 'mt' too.
// ─────────────────────────────────────────────────────────────
// Paths (all relative to paths.project; see resolution rules below)
// ─────────────────────────────────────────────────────────────
paths: {
config: import.meta.url,
// ALWAYS SET THIS. Anchors all relative paths to this file's location.
project: undefined,
// Project root. Defaults to the dir of paths.config (or cwd if neither set).
base: undefined,
// Alternative project root override. Used by samples to point above
// the workspace boundary in the monorepo.
cache: '.cppjs', // build cache root
build: '.cppjs/build', // staging dir
output: '.cppjs/build', // dist artifacts (defaults to build)
native: ['src/native'],
// ARRAY. C++ source roots. Order matters for include precedence.
module: undefined, // SWIG .i interfaces; defaults to native
header: undefined, // headers; defaults to native
bridge: undefined, // bridge code; defaults to [...native, build]
cmake: undefined, // override CMakeLists.txt path
},
// ─────────────────────────────────────────────────────────────
// File extension filters
// ─────────────────────────────────────────────────────────────
ext: {
header: ['h', 'hpp', 'hxx', 'hh'],
source: ['c', 'cpp', 'cxx', 'cc'],
module: ['i'], // SWIG interfaces
},
// ─────────────────────────────────────────────────────────────
// Export (output) settings
// ─────────────────────────────────────────────────────────────
export: {
type: 'cmake',
// Currently 'cmake' is the only fully-supported value.
header: 'include', // include dir name in dist
libPath: 'lib', // .a output dir
libName: ['<general.name>'], // .a basenames; one per item
binHeaders: [], // headers to ship as raw binary blobs
},
// ─────────────────────────────────────────────────────────────
// Build target / runtime
// ─────────────────────────────────────────────────────────────
target: {
runtime: 'st',
// 'st' (single-threaded) | 'mt' (multi-threaded WASM)
// 'mt' requires SharedArrayBuffer + COOP/COEP in browser production.
// See threading.md for the full requirements.
//
// Auto-promotes to 'mt' if any item in `dependencies` is 'mt'.
},
targetSpecs: [
{
// Filter (any combination — entry matches if all set fields match)
platform: 'wasm' | 'android' | 'ios', // optional
arch: 'wasm32' | 'wasm64' | 'arm64-v8a' | 'x86_64' | 'iphoneos' | 'iphonesimulator',
runtime: 'st' | 'mt',
buildType: 'release' | 'debug',
runtimeEnv: 'browser' | 'node' | 'edge',
// Overrides (apply when filter matches)
specs: {
cmake: ['-DSOMETHING=ON'], // -D flags appended to cmake configure
emccFlags: ['-sINITIAL_MEMORY=64MB'], // -s/-O flags appended to emcc (wasm only)
env: { GDAL_NUM_THREADS: '0' }, // env vars passed to running Wasm + build env
data: { 'share/myapp': 'myapp/data' }, // bundle data files into .data preload
ignoreLibName: ['libtiff_legacy'], // suppress these .a names from link line
},
},
],
// See `overrides.md` for the catalog of override mechanisms and when
// to reach for `targetSpecs` vs `cppjs.build.js` hooks vs `extensions`.
// ─────────────────────────────────────────────────────────────
// Build hooks (merged from cppjs.build.js if present)
// ─────────────────────────────────────────────────────────────
build: {},
// Don't write this here directly. Put hooks in cppjs.build.js — the
// CLI auto-merges. See cppjs-build.md.
// ─────────────────────────────────────────────────────────────
// Plugin / extension system
// ─────────────────────────────────────────────────────────────
extensions: [
{
loadConfig: {
after: (config) => { /* mutate resolved config after load */ },
},
buildWasm: {
beforeBuild: (emccFlags) => {}, // all wasm targets — mutate emccFlags array
beforeBuildBrowser: (emccFlags) => {}, // browser runtimeEnv only
beforeBuildEdge: (emccFlags) => {}, // edge runtimeEnv only
beforeBuildNodeJS: (emccFlags) => {}, // node runtimeEnv only
},
createLib: {
setFlagWithBuildConfig: (buildEnv, cFlags, ldFlags) => {}, // tweak CFLAGS/LDFLAGS
setFlagWithoutBuildConfig: (buildEnv) => {}, // env-level override
},
},
],
// Plugin objects with hooks at config-load and build-step boundaries.
// Use only when sharing an override across multiple cpp.js packages —
// for single-package needs, prefer `targetSpecs` or `cppjs.build.js`.
// Real example: OpenSSL Android cert-injection extension.
// ─────────────────────────────────────────────────────────────
// Custom helper functions
// ─────────────────────────────────────────────────────────────
functions: {
isEnabled: (target) => boolean,
// Override the default "is this build target enabled?" check.
// Default: returns true if the target's output binary already exists.
},
}
Path resolution rules
Paths are resolved in this order (from loadConfig.js):
paths.configis set →paths.projectdefaults to its parent dir.paths.projectis set → resolved as absolute viagetAbsolutePath.- Neither set → falls back to
process.cwd().
Then everything else (base, cache, build, output, native, module, header, bridge, cmake) is resolved against paths.project using getAbsolutePath(project, value).
This means: always set paths.config: import.meta.url at minimum. Without it, cppjs build invoked from a different cwd will resolve paths against the wrong root.
Examples
Minimal app (consumes a prebuilt package)
// cppjs.config.js
import gdal from '@cpp.js/package-gdal/cppjs.config.js'
export default {
general: { name: 'my-geo-app' },
dependencies: [gdal],
paths: { config: import.meta.url },
}
Multithread browser app
import gdal from '@cpp.js/package-gdal/cppjs.config.js'
export default {
general: { name: 'my-fast-app' },
dependencies: [gdal],
paths: { config: import.meta.url },
target: { runtime: 'mt' },
// Remember: prod host needs COOP/COEP headers.
}
Library wrapping your own C++
export default {
general: { name: 'mylib' },
paths: {
config: import.meta.url,
native: ['src/native'],
output: 'dist',
},
export: {
type: 'cmake',
libName: ['mylib'],
},
}
Monorepo sample (above-workspace project root)
import Matrix from '@cpp.js/sample-lib-prebuilt-matrix/cppjs.config.js'
export default {
general: { name: 'cppjs-sample-web-vue-vite' },
dependencies: [Matrix],
paths: {
config: import.meta.url,
base: '../..', // points above the workspace boundary
},
}
See also
init.md— runtime API.cppjs.config.jsproduces the artifacts thatinitCppJs(opts)loads.cppjs-build.md— sibling file used by package authors only.threading.md—target.runtime: 'mt'requirements.- ADR-0002 — pnpm topological build order via
dependencies. - ADR-0003 — function-typed env values.