OpenCascade.js

YAML schema

Every key the build YAML accepts — mainBuild, extraBuilds, additionalCppCode, additionalCppFiles, additionalBindCode.

The build YAML controls which OCCT classes get bound, which C++ wrapper code is injected, and which emscripten linker flags drive the final wasm.

Top-level shape

mainBuild:
  name: <string>
  bindings:
    - symbol: <ClassName>
  emccFlags:
    - <flag>
  additionalBindCode: |
    EMSCRIPTEN_BINDINGS(custom) { ... }

extraBuilds:
  - name: <string>
    bindings: [...]
    emccFlags: [...]
    additionalBindCode: |
      ...

additionalCppCode: |
  typedef opencascade::handle<Geom_Curve> Handle_Geom_Curve;
  class MyWrapper { ... };

additionalCppFiles:
  - path/to/extra.cpp

generateTypescriptDefinitions: true

The canonical Cerberus definition lives in src/customBuildSchema.py.

mainBuild

The primary wasm artifact produced by the YAML.

mainBuild.name

Output filename without extension. name: my-occt produces my-occt.wasm, my-occt.js, my-occt.d.ts, and my-occt.build-manifest.json.

mainBuild.bindings

Allowlist of OCCT classes to expose to JS via embind. Only classes listed here (and their transitive base classes) are accessible at runtime.

bindings:
  - symbol: BRepPrimAPI_MakeBox
  - symbol: TopoDS_Shape
  - symbol: gp_Pnt

The symbol name must match exactly the C++ class name in the generated binding .cpp files under build/bindings/. Base classes are auto-included if missing from the list.

mainBuild.emccFlags

Emscripten linker flags. See Custom emcc flags for the recommended baseline and per-flag rationale.

mainBuild.additionalBindCode

Per-build raw EMSCRIPTEN_BINDINGS(...) registrations compiled with <emscripten/bind.h> already included. See Extend with C++.

Symbol resolution classes

Every YAML-requested symbol becomes a linked binding through exactly one of four mechanisms. The post-link build-manifest.json (schema build-manifest-v2) buckets each requested symbol into one of these categories under symbols:

  1. Direct compilation. bindings: - symbol: gp_Pnt causes the generator to emit build/bindings/gp_Pnt.cpp, which compileBindings.py compiles into build/compiled-bindings/gp_Pnt.cpp.o. Detected by ocjs_bindgen.link.manifest_registry.collect_compiled_symbols. Reported as satisfied_by_compiled (count surfaces as symbols.compiled).
  2. NCollection typedef alias. bindings: - symbol: TColgp_Array1OfPnt resolves via the canonical mangled spelling NCollection_Array1_gp_Pnt; the linker substitutes the typedef at link time. Mapping lives in build/ncollection-manifest.json. Detected by manifest_registry.load_ncollection_alias_index. Reported under symbols.alias_resolved as {alias, canonical} entries.
  3. Embind builtin. OCJS's BUILTIN_ADDITIONAL_BIND_CODE block (OCJS, TopoDS, TColStd_IndexedDataMapOfStringString) registers Embind class wrappers with no .cpp.o of their own. Detected by manifest_registry.builtin_binding_symbols reading build/additional-bind-symbols.json.
  4. Consumer additionalBindCode. YAML's own mainBuild.additionalBindCode block undergoes the same Embind pathway. The link stage compiles BUILTIN_ADDITIONAL_BIND_CODE + consumer additionalBindCode into ONE translation unit so the AST producer emits a single additional-bind-symbols.json manifest covering both sources. Reported under symbols.builtin (no separate bucket).

Anything that survives all four lookups lands in symbols.missing and triggers validation_passed=false. The link step also raises immediately via yaml_build.verifyBindings (no env-var gate) so a YAML asking for a symbol the toolchain cannot provide fails the link, not just the post-link audit.

Auto-discovered NCollection canonicals (entries the YAML never named directly, but that became reachable from the YAML's scope) are tracked separately in <variant>.provenance.json::nCollectionManifest.{linked, total, dropped} (schema wasm-build-provenance-v1.1). They never appear in symbols.requested because they're produced by the discovery pass, not requested by the operator.

Producer-side manifest contract

Every mechanism above has exactly one producer — a pipeline stage with the semantic knowledge to compute it — that writes a JSON manifest in build/ or the dist sidecar. Every downstream consumer (link-time verifyBindings, post-link validate-build.py, generate-docs.mjs, docker-e2e-validate.sh) reads the manifest through the corresponding manifest_registry loader. No consumer re-parses C++, runs regex against source, or re-derives set-difference math.

ManifestProducerConsumer loader
build/ncollection-manifest.jsonocjs_bindgen.discovermanifest_registry.load_ncollection_alias_index
build/additional-bind-symbols.jsonrunBuild::getAdditionalBindCodeO() (libclang AST via ocjs_bindgen.ast.parse_additional_bind_code + ocjs_bindgen.ast.walker.extract_class_registrations)manifest_registry.builtin_binding_symbols
build/compiled-bindings/*.cpp.ocompileBindings.pymanifest_registry.collect_compiled_symbols
build/compiled-bindings/binding-report.jsoncompileBindings.pyvalidate-build.py::validate_binding_report
<variant>.provenance.json::nCollectionManifestyaml_build.main via provenance.add_linking(ncollection_linked=, ncollection_total=, ncollection_dropped=)generate-docs.mjs, scripts/docker-e2e-validate.sh
build/any-type-report.jsongenerate.pyvalidate-build.py::merge_any_reasons

When a manifest is missing, consumers fail loudly with a pointer at pnpm nx run ocjs:build. Stale artifacts are stale by definition; rendering them with degraded math produces docs whose numbers contradict the build that produced them.

extraBuilds

Same schema as mainBuild. Each entry produces a sibling wasm artifact. Useful for shipping multiple variants (e.g. baseline + relaxed-SIMD) from one YAML pass.

additionalCppCode

Inline C++ injected into a generated .cpp file compiled with the same flags as the bindings TU. Common uses:

  1. Handle typedefs (typedef opencascade::handle<T> Handle_T;).
  2. Free-function helpers reachable from additionalBindCode.
  3. Small value_object<T> POD definitions.

additionalCppFiles

List of .cpp files concatenated onto additionalCppCode before custom-binding generation runs.

additionalCppCode: |
  typedef opencascade::handle<Geom_BSplineSurface> Handle_Geom_BSplineSurface;

additionalCppFiles:
  - wrappers/fair-curve.cpp
  - wrappers/shape-cast.cpp
  • Paths resolve relative to the YAML file's directory; absolute paths also accepted.
  • File contents are appended in declaration order.
  • additionalCppCode wins on conflict because it precedes file contents in the TU.
  • A missing file fails-loud at validate time.

generateTypescriptDefinitions

Default true. Set to false to skip .d.ts generation (rare — useful only for ultra-fast iteration builds).

Validation

./build-wasm.sh validate build-configs/my-config.yml

validate checks the YAML against the schema and exits non-zero on any malformed entry, unknown key, duplicated symbol, or missing additionalCppFiles path. It does not run the C++ pipeline — pair it with ./build-wasm.sh link to actually produce the wasm.

On this page