Extensions

Extensions are user enabled file processors. Nothing loads by default. List a name or path in the extensions array and the build loads it, resolves it, and runs its hooks on matching files.

{
  "bb": {
    "extensions": ["./tools/minify-json", "my-pack-tools"]
  }
}

Resolution order

Missing or broken extensions warn and skip. The build never fails because of an extension. bb ext lists each spec with its resolved path plus ok, missing, or broken status. Missing node deps show a hint to run bb ext --install. Extensions can also report their own health checks, such as an external tool they need.

Per-extension dependencies

Each extension keeps its own node_modules. Declare what it needs in its manifest and install with one command. Core stays lean.

{
  "name": "json-cleaner",
  "entry": "index.mjs",
  "dependencies": { "jsonc-parser": "^3.3.1" }
}
bb ext --install

Installs each enabled extension's deps into its own folder with --no-save. Never touches the core package or the project root.

Processor hooks

Each processor declares a name plus a match(file) function. Four optional hooks run in order:

A processor that throws in match is skipped for that file with a warning. A processor that throws in pre, transform, or post warns and the pipeline keeps the last good bytes. Same name replaces the old entry, so an extension can override a default or another extension.

Minimal extension

export default {
  name: "minify-json",
  match: (file) => file.rel.endsWith(".json"),
  transform: (bytes) => {
    const text = new TextDecoder().decode(bytes);
    const min = JSON.stringify(JSON.parse(text));
    return new TextEncoder().encode(min);
  },
};

Save it as tools/minify-json/index.mjs, add "./tools/minify-json" to extensions, and every JSON file compacts on build. Return null from transform when the file needs no change.

Bundled samples

Each sample can carry two manifests. extension.json owns the builder entry point, package.json owns npm metadata and dependencies. Deps merge from both files, and a conflicting entry warns and keeps the extension.json value.

Copy a sample folder into your project, add its path to extensions, and it runs on matching files. Factory extensions receive helpers from bb itself, so they work with no installed dep. That covers global-only installs too.

export default function myExtension({ settings }) {
  return {
    name: "my-extension",
    match: (file) => file.rel.endsWith(".png"),
    transform: (content) => doWork(content, settings),
  };
}

Json cleaner settings

{
  "bb": {
    "extensions": [
      {
        "name": "json-cleaner",
        "settings": { "stripSchemas": true, "minify": true }
      }
    ]
  }
}

Both settings default to false. The extension runs on every JSON file and leaves unchanged files alone.

Per-extension settings

Use an object when an extension needs settings. String entries still work and use an empty settings object.

{
  "bb": {
    "extensions": [
      {
        "name": "emissive-fixer",
        "settings": { "stripPackIcons": false }
      }
    ]
  }
}

Default processors

Core texture and audio helpers stay exported for extension authors. Nothing loads them unless an extension calls them.