oven-sh/bun
Extension happens at five layers. New CLI subcommands add a `Tag` variant plus a dispatch arm and an out-lined `#[cold]` body. New JS classes are declared in `*.classes.ts` and generated into paired Rust/C++ bindings, or hand-written as the three-class JSC pattern. The `Bun` global is a static hash table in `BunObject.cpp` — adding a property is a table row plus a callback. Userland extends the bundler and runtime through `Bun.plugin` (regex-filter groups of onResolve/onLoad callbacks) and native plugins via `packages/bun-native-plugin-rs`/`-api`. Frameworks extend Bake through `FrameworkRouter` and the `UserOptions` framework description. The install path is extensible through a user-supplied security scanner module and bunfig-declared registries and scopes.
- Adding a CLI command means adding a `Tag` variant, a unique crash-report `char()`, an arm in `Command::start`'s match, and an out-lined `#[cold] #[inline(never)]` exec fn — the file documents this convention so `start` stays a jump table.
- Package-manager commands are registered through a macro table mapping an exec fn to a `(Tag, handler)` pair — `exec_install => (InstallCommand, InstallCommand::exec)` and seventeen siblings — so new pm verbs plug into one list.
- New JS-visible classes are declared in a `.classes.ts` file (`define({ name, proto, values, JSType })`) and `src/codegen/generate-classes.ts` emits the prototype, constructor, subspace, and Rust binding — ~25 such definitions exist under `src/runtime/api/` alone.
- Properties on the `Bun` global are added as rows in the `BunObject.cpp` static hash table, choosing between `Function`, `PropertyCallback` (lazily constructed), and `CustomAccessor` — no separate registration call is needed.
- `Bun.plugin` is implemented as `Zig::BunPlugin` holding `Group`s of JIT-compiled `JSC::RegExp` filters paired with JS callbacks plus a `VirtualModuleMap`, targeted per `BunPluginTarget` — that is the userland hook for onResolve/onLoad.
- Native (non-JS) bundler plugins are a supported extension surface with published scaffolding: a C API in `packages/bun-native-bundler-plugin-api` and a Rust crate in `packages/bun-native-plugin-rs`.
- Bake is explicitly designed as framework infrastructure rather than a framework, with `UserOptions`/`Framework` parsed `from_js` in `bake_body.rs` and routing delegated to a pluggable `FrameworkRouter` keyed by `OpaqueFileId`.
- Reference framework integrations ship in-tree as plugin packages — `packages/bun-plugin-svelte`, `packages/bun-plugin-yaml`, and `src/runtime/bake/bun-framework-react` — which double as the worked examples for third parties.
- `bun install` accepts a user-supplied security scanner module: `scanner-entry.ts` is templated with `__SCANNER_MODULE__`/`__SUPPRESS_ERROR__`, runs the module in a child process, and returns typed `Bun.Security.Advisory[]` results.
- Registries and per-scope credentials are declarative extension points in `bunfig.toml` — `[install]` `registry` and `[install.scopes]` entries each parse into an `api::NpmRegistry` accepting either a URL string or an object with `url`/`username`/`password`/`token`.
- Node module surface is extended by adding a TypeScript file under `src/js/node/` and, when native work is needed, a paired binding in `src/runtime/node/*_binding.rs`; the bundler picks new modules up through `internal-module-registry-scanner.ts`.
- Host functions callable from bundled JS are registered through `js2native` codegen: `$rust(ffi.rs, Bun__FFI__cc)` resolves to a Rust path derived from the file basename, which is why `ffi/mod.rs` re-exports `bun__ffi__cc` under a codegen-expected module path.
- Loader/target behavior is extended through `src/options_types/` (`Loader`, `Target`, `api::Target`) rather than by branching in callers, and lower crates switch on `command_tag::Tag` without importing the CLI crate.
- Compile-time string→value tables are generated from `*.string-map.ts` files into `*.generated.rs` via `bun run codegen:string-maps`, with a `codegen:verify` script that fails CI if the generated output is stale.