Skip to content

Build

A Matrix package builds with whichever tool its package.json:scripts.build runs. The workspace-level orchestrator is pnpm plus Turbo; individual packages call esbuild, tsc, node build.mjs, or vite build depending on the shape.

Top-level commands

From CLAUDE.md Rule 10:

bash
pnpm install              # install workspace deps
pnpm build:affected       # build only packages whose source or deps changed
pnpm build                # build everything (slower, sometimes needed)

build:affected uses Turbo's affected-detection. Use it during normal development. Use the full pnpm build after pulling a large batch of changes or when a build cache is corrupt.

Per-package build scripts

Different shapes use different build tools. The patterns in the live tree:

esbuild for headless service packages

json
// packages/system/package.json
"scripts": {
  "build": "esbuild src/index.ts --bundle --platform=node --target=node22 --format=esm --outfile=dist/index.js --external:node:* --external:@open-matrix/core ..."
}

@open-matrix/system, @matrix/mx-cli, @open-matrix/host-service all use this pattern. esbuild is fast; the externalized dependencies get resolved by the runtime importer.

node build.mjs for hybrid packages

json
// packages/director/package.json
"scripts": { "build": "node build.mjs" }

build.mjs typically chains: tsc --noEmit (type check) → vite build (browser assets) → an esbuild call (runtime entry). Look at packages/director/build.mjs and packages/chat/build.mjs for full examples.

vite build for pure web apps

Pure browser apps that lack a service factory may call vite build directly through their build script.

tsc for libraries

Pure libraries (e.g. @open-matrix/core) use tsc to emit dist/ in the layout the rest of the workspace expects.

What gets produced

The runtime entry path declared in matrix.json:runtime.entry must exist after build:

my-package/
├── matrix.json          ← runtime.entry: "dist/index.js"
└── dist/
    ├── index.js         ← built artifact
    └── ...

For a webapp package, webapp.distDir/webapp.entry must also exist. mx publish checks both before letting you publish.

Rebuild before testing

CLAUDE.md "REBUILD BEFORE TESTING": tests use built artifacts. Stale bundles produce false results. The order is always:

bash
pnpm build      # produce dist/
pnpm test       # tests run against dist/

If you change source and skip the build, your test will exercise the old code without telling you.

Rebuild order matters across packages

When you change a workspace-internal dependency, dependents need their own rebuild before you can verify them. pnpm build:affected walks the dependency graph; manual rebuilds follow the same order:

  1. @open-matrix/core
  2. Other libraries that depend on core
  3. mx-cli, host-service, host-control
  4. The package you changed
  5. hivecast wrapper if you changed any of the above and want to re-seed the install

hivecast operator refresh-host-package (from packages/hivecast/bin/hivecast.mjs:73-76) does steps 4 and 5 for you for a single package.

See also

Source: package.json:scripts.build per package. Top-level orchestration is turbo.json + package.json:scripts at the repo root.