crossbind
GitHub
GUIDE · CONCEPTS · PACKAGES

Packages

Native libraries travel through npm like any other dependency. 16 are already published prebuilt - GDAL, OpenSSL, SQLite, GEOS, PROJ, libTIFF and more - and the same mechanism packages your own C++, whether it ships as binaries, as sources, as a CMake project or as a Rust crate.

Using a prebuilt library

Install the package, declare it as a dependency in cppjs.config.js, and import its header. Nothing is compiled on your machine - the binaries are in the package.

shell
npm install @cpp.js/package-gdal
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 },
};
src/main.js
import { initNative, GDALVersionInfo } from '@cpp.js/package-gdal/gdal.h';
 
await initNative();
console.log(GDALVersionInfo('RELEASE_NAME'));

Headers live under the package's dist/prebuilt/<target>/include, and the import path is relative to that - @cpp.js/package-gdal/gdal.h is GDAL's own gdal.h.

Meta package, platform variants

A package family is a thin meta package plus one package per platform, so you only download artifacts for the platforms you build. @cpp.js/package-gdal depends on -wasm, -android and -ios; a -wasi variant carries the WASI prebuilt. Importing the meta package pulls in the right variant for the target.

PackageCarries
@cpp.js/package-gdalthe meta package - depend on this
@cpp.js/package-gdal-wasmthe WebAssembly prebuilt (browser, Node, edge)
@cpp.js/package-gdal-android / -iosthe native mobile libraries
@cpp.js/package-gdal-wasithe wasm32-wasip3 prebuilt
@cpp.js/package-gdal-bin-wasithe upstream CLI tools as npm commands

Those -bin-wasi packages are prebuilt command-line tools - gdalinfo-wasi, ogr2ogr-wasi, sqlite3-wasi and friends. See WASI commands.

Auditable by construction
Every upstream source is sha256-pinned, and each published package ships its third-party notices, a CycloneDX SBOM and a provenance block naming the sources, toolchain and environment that produced the binaries.

The three package types

Typeexport.typeWhat consumers get
Prebuiltcmake (default)compiled libraries per platform - nothing to build
Sourcesourceraw C++ compiled during the consumer's build
CMakecmakesources plus a CMakeLists.txt for custom build systems
Cargocargoa Rust crate built per platform - see Rust

Prebuilt is the default and what almost every published package is. Source and CMake packages trade build time for control - the consumer compiles them, so platform-specific tweaks are possible.

Publishing your own

Point the config at your sources, name the library, and declare where the build output goes. That is the whole contract:

cppjs.config.js
export default {
general: { name: 'mylib' },
paths: {
config: import.meta.url,
native: ['src/native'],
output: 'dist',
},
export: {
type: 'cmake',
libName: ['mylib'],
},
};

Build once per platform and publish the result. The layout the CLI produces is what consumers rely on:

dist/
dist/
├── mylib-wasm-wasm32-st-release.browser.js
├── mylib-wasm-wasm32-st-release.browser.wasm
└── prebuilt/
├── wasm-wasm32-st-release/{include,lib}
├── android-arm64-v8a-mt-release/{include,lib}
└── ios-iphoneos-mt-release/{include,lib}

Wrapping an external project rather than your own sources? Add a cppjs.build.js next to the config: it fetches the upstream release, patches it if needed, and passes build parameters to cmake or configure. Every published library in the registry is built exactly that way.

Declare your C++ dependencies in package.json too
Build order is derived from the npm dependency graph. A library that links against another package must list it in dependencies, or the linker will run before that package has been built.
Type to search every guide page and section.
↑↓ navigate↵ openesc close