Skip to main content

state and target shapes — what build hooks receive

When you write a cppjs.build.js hook (prepare(state), build(state), getBuildParams(state, target), etc.), or an extensions[] plugin, you receive a state object and a target object. This doc enumerates every key on both. Source: cppjs-core/cpp.js/src/state/index.js.

state — top-level keys

state = {
targets: Target[], // 20 built-in build targets (see § Target inventory)
config: ResolvedConfig, // merged cppjs.config.js + cppjs.build.js + system
cache: BuildCache, // persisted to .cppjs/cache.json
system: SystemConfig, // ~/.cppjs.json + defaults from systemKeys
}

state.config (the resolved configuration)

The output of loadConfig() in src/state/loadConfig.js. Every key:

state.config = {
general: { name: string }, // package name; OPFS namespace = /opfs/<name>/

dependencies: ResolvedConfig[], // immediate cpp.js deps (each is its own resolved config)
allDependencies: ResolvedConfig[], // flattened transitive deps, deduped by paths.project

paths: {
config: string, // import.meta.url of cppjs.config.js
project: string, // absolute project root
base: string, // = project unless overridden
cache: string, // .cppjs (build cache root)
build: string, // .cppjs/build (staging dir for sources)
output: string, // dist artifacts dir
native: string[], // C++ source roots (ARRAY)
module: string[], // SWIG .i source roots
header: string[], // C++ header roots
bridge: string[], // bridge code roots = [...native, build]
cmake: string, // resolved CMakeLists.txt path
cmakeDir: string, // dirname of cmake
cli: string, // path to cpp.js CLI install
cliCMakeListsTxt: string, // bundled fallback CMakeLists.txt
systemConfig: string, // = ~/.cppjs.json
},

ext: {
header: string[], // ['h','hpp','hxx','hh']
source: string[], // ['c','cpp','cxx','cc']
module: string[], // ['i'] — SWIG interface extensions
},

export: {
type: 'cmake' | 'source', // distribution shape
header: string, // include dir name in dist
libPath: string, // .a output dir
libName: string[], // .a basenames (one per item; e.g. ['ssl','crypto'])
binHeaders: string[], // headers to ship as raw binary blobs
},

target: { // FILTER (restrict which targets are built)
platform?: 'wasm' | 'android' | 'ios',
arch?: string, // 'wasm32' | 'wasm64' | 'arm64-v8a' | …
runtime?: 'st' | 'mt',
buildType?: 'release' | 'debug',
runtimeEnv?: 'browser' | 'node' | 'edge',
},

targetSpecs: TargetSpec[], // per-target overrides (see § targetSpecs)

build: { // merged from cppjs.build.js (package authors only)
withBuildConfig: boolean, // true if a cppjs.build.js was loaded
buildType?: 'cmake' | 'configure',
setState?: (state) => void,
beforeRun?: (cmakeDir) => Array<{program, parameters}>,
getBuildParams?: (state, target) => string[],
getExtraLibs?: (target) => string[],
sourceReplaceList?: (target, depPaths) => Array<{regex, replacement, paths}>,
env?: ((target) => string[]) | string[],
copyToSource?: Record<string, string[]>,
copyToDist?: Record<string, string[]>,
},

extensions: Extension[], // plugin hooks (see § Extensions)

functions: {
isEnabled: (target) => boolean, // default: checks ${cmakeDir}/{target.path} or .xcframework exists
},

package: object | null, // parsed package.json of paths.project

allDependencyPaths: { // per-target dependency lookup
[target.path]: {
cmake: { ... },
[libName]: { root, header, libPath, lib, bin },
},
},

dependencyParameters: ..., // CMake -D injection from calculateDependencyParameters()
}

state.cache

Persisted to .cppjs/cache.json between builds:

state.cache = {
hashes: Record<string, string>, // file hash → seen?
interfaces: Record<string, string>, // SWIG .i hash cache
bridges: Record<string, string>, // bridge code cache
}

isSourceNewer checks mtime against this cache to decide whether to rebuild a target.

state.system

Loaded from ~/.cppjs.json, merged with defaults from cppjs-core/cpp.js/src/utils/systemKeys.js:

state.system = {
XCODE_DEVELOPMENT_TEAM: string, // default '' (required for iOS device builds)
RUNNER: 'DOCKER_RUN' | 'DOCKER_EXEC' | 'LOCAL', // default 'DOCKER_RUN'
LOG_LEVEL: 'DEBUG' | 'INFO' | 'WARN' | 'ERROR', // default 'INFO'
}

target — single build target

A single entry from state.targets[]. Every key:

target = {
platform: 'wasm' | 'android' | 'ios',
arch: string, // 'wasm32' | 'wasm64' | 'arm64-v8a' | 'x86_64' | 'iphoneos' | 'iphonesimulator'
runtime: 'st' | 'mt',
buildType: 'release' | 'debug',
runtimeEnv?: 'browser' | 'node' | 'edge', // wasm only

path: string, // computed: '{platform}-{arch}-{runtime}-{buildType}'
releasePath: string, // = path with buildType→'release' (always release variant)

// wasm-only computed names:
rawJsName?: string, // pre-bundled emcc output: '{name}-{path}.{runtimeEnv}.js'
jsName?: string, // final bundled JS: '{name}-{path}.{runtimeEnv}.js'
wasmName?: string, // wasm binary: '{name}-{path}.{runtimeEnv}.wasm'
dataName?: string, // emcc preload: '{name}-{path}.{runtimeEnv}.data'
dataTxtName?: string, // preload manifest: '{name}-{path}.{runtimeEnv}.data.txt'
}

Target inventory — 20 built-in targets

From cppjs-core/cpp.js/src/state/index.js lines 8–197:

Wasm32

platformarchruntimebuildTyperuntimeEnv
wasmwasm32streleasebrowser
wasmwasm32streleaseedge
wasmwasm32streleasenode
wasmwasm32stdebugbrowser
wasmwasm32stdebugedge
wasmwasm32stdebugnode
wasmwasm32mtreleasebrowser
wasmwasm32mtreleasenode
wasmwasm32mtdebugbrowser
wasmwasm32mtdebugnode

mt has no edge runtimeEnv. Edge runtimes (Cloudflare Workers, Deno Deploy) don't expose Web Workers / SharedArrayBuffer. See threading.md.

Wasm64 (memory-64; same axes)

Same shape as wasm32 above, but arch: 'wasm64'. Used when you need >4GB linear memory. Browser support is partial (Chrome 119+).

Android

platformarchruntimebuildType
androidarm64-v8amtrelease
androidarm64-v8amtdebug
androidx86_64mtrelease
androidx86_64mtdebug

Android is always mt. No single-thread variant. NDK API 33, NDK 27.3.13750724.

iOS

platformarchruntimebuildType
iosiphoneosmtrelease
iosiphoneosmtdebug
iosiphonesimulatormtrelease
iosiphonesimulatormtdebug

iOS is always mt. No single-thread variant. iOS deployment target 13.0; bitcode embedded.

targetSpecs[] — per-target override entries

When you want to override defaults for a specific subset of targets, push entries to cppjs.config.js targetSpecs[]:

type TargetSpec = {
// Filter (any combination — entries match if all set fields match)
platform?: 'wasm' | 'android' | 'ios',
arch?: string,
runtime?: 'st' | 'mt',
buildType?: 'release' | 'debug',
runtimeEnv?: 'browser' | 'node' | 'edge',

// Overrides
specs: {
cmake?: string[], // extra -D flags appended to cmake configure
emccFlags?: string[], // extra -s/-O flags appended to emcc command
env?: Record<string, string>, // env vars passed to Wasm process at runtime (or via CFLAGS/LDFLAGS at build)
data?: Record<string, string>, // bundle data files: { 'src-dir': 'dest-dir' }
ignoreLibName?: string[], // suppress specific .a names from being linked
},
}

Example (wasm-only SIMD, all archs):

targetSpecs: [{
platform: 'wasm',
specs: { emccFlags: ['-msimd128', '-DUSE_SIMD'] },
}]

Example (gdal-wasm: disable threading on st runtime via env):

targetSpecs: [{
platform: 'wasm',
runtime: 'st',
specs: { env: { GDAL_NUM_THREADS: '0' } },
}]

extensions[] — plugin hooks

type Extension = {
loadConfig?: {
after?: (config) => void, // mutate resolved config after load
},
buildWasm?: {
beforeBuild?: (emccFlags) => void, // all wasm targets
beforeBuildBrowser?: (emccFlags) => void, // browser only
beforeBuildEdge?: (emccFlags) => void, // edge only
beforeBuildNodeJS?: (emccFlags) => void, // node only
},
createLib?: {
setFlagWithBuildConfig?: (buildEnv, cFlags, ldFlags) => void,
setFlagWithoutBuildConfig?: (buildEnv) => void,
},
}

Used internally by built-in extensions (e.g. for OpenSSL Android cert injection). Most users never write extensions; prefer targetSpecs first.

When you write a hook, here's what to do

You want toUse this
Read which target is being builttarget.platform, .arch, .runtime, .buildType, .runtimeEnv
Find where source code isstate.config.paths.build (extracted upstream sources land here)
Find where artifacts gostate.config.paths.output
Find a dep's installed headers/libsstate.allDependencyPaths[target.path][libName].header / .lib
Add a CMake flagtargetSpecs[].specs.cmake (preferred) or getBuildParams return value
Add an emcc flagtargetSpecs[].specs.emccFlags
Inject env to the running WasmtargetSpecs[].specs.env or cppjs.config.js env: {}
Patch upstream sourcecppjs.build.js replaceList or sourceReplaceList(target, depPaths) hook
Bundle data files into the .data preloadtargetSpecs[].specs.data or sub-arch data: {}
Run something before cmakecppjs.build.js beforeRun(cmakeDir)

See also

  • overrides.md — full catalog of override mechanisms with priority order.
  • cppjs-config.md — consumer-side config field-by-field.
  • cppjs-build.md — package-author hooks (setState, beforeRun, getExtraLibs, sourceReplaceList, env, copyToSource, copyToDist, prepare, build).
  • Source: cppjs-core/cpp.js/src/state/index.js, loadConfig.js, actions/target.js.