Bun.build & OXC transform
How manic wires Bun.build entrypoints, oxc-transform via BunPlugin, hashing, and production minification.
Bun.build & OXC transform
Manic’s “bundler” is native Bun.build plus a thin oxc-transform adapter (packages/manic/src/cli/plugins/oxc.ts). There is no Rollup/Vite/Rspack layer — graphs, hashing, and I/O are Bun’s responsibility; JSX/TS stripping and JSX runtime injection are OXC’s.
oxcPlugin(isDev?)
Source: packages/manic/src/cli/plugins/oxc.ts
| Concern | Behavior |
|---|---|
| Files matched | *.ts, *.tsx, *.js, *.jsx outside node_modules |
| Production target | Hard-coded es2022 when isDev === false |
| Dev target | manic.config.ts → oxc.target (default esnext) |
| JSX | Automatic runtime; development: isDev |
| Fast Refresh | When isDev and oxc.refresh !== false, enables refresh + appends import.meta.hot accept shim for .tsx/.jsx |
| TypeScript | rewriteImportExtensions (default on), onlyRemoveTypeImports |
| Sourcemaps | Emitted only when isDev |
getConfig() reads the cached merged ManicConfig — during manic build this is whatever loadConfig() resolved at build start.
Client bundle (target: browser)
Implemented in packages/manic/src/cli/commands/build.ts:
| Option | Value |
|---|---|
| Entry | Resolved ./app/main via oxc-resolver (must exist as app/main.tsx / .jsx / etc.) |
| Plugins | oxcPlugin() + bun-plugin-tailwind |
define | process.env.NODE_ENV → "production" |
naming | Hashed entry, chunks/, assets/ |
Outputs drive HTML rewriting: the CLI finds the entry-point JS output and optional .css artifact, then patches app/index.html (or emits a minimal shell) into <outdir>/client/index.html.
API bundles (target: bun)
Skipped entirely when mode === 'frontend' or app/api is missing.
| Concern | Behavior |
|---|---|
| Glob | app/api/**/index.ts only — index.tsx is not picked up by this glob today |
external | All dependencies keys from root package.json |
minify | false at bundle time — compression happens in the oxc-minify pass |
| Naming | Derived from folder path → flat *.js files under <outdir>/api |
Each folder’s index.ts is its own entrypoint, which keeps serverless/external deployments aligned with small per-route graphs.
Server bundle (target: bun)
- Resolve
./~manicwithoxc-resolver. - Read source text and rewrite the
import … from './app/index.html'pattern intoBun.file("<outdir>/client/index.html").text()so production serves the built HTML string. - Emit a temporary
<outdir>/_entry.ts,Bun.buildonce withnaming.entry = server.js, delete the temp file.
Server compilation uses oxcPlugin() with isDev falsy → es2022 transforms.
Minification (oxc-minify)
After all three Bun.build graphs succeed, minifyDir walks **/*.js under:
<outdir>/client<outdir>/api(if present)<outdir>(coversserver.js)
Each file is read as text and passed through minifySync with:
compress.target: 'es2022'mangle: truecodegen.removeWhitespace: true
Runs Promise.all across files per directory sweep (three parallel top-level promises for client / api / root).
Plugin preload vs build
| Hook | Mechanism |
|---|---|
preload | Dynamically import()’d; if export is a BunPlugin, registered via Bun.plugin() before client Bun.build — affects resolution/transform for subsequent graphs |
build | Runs after client bundle + baseline HTML write; receives emitClientFile, injectHtml, pageRoutes ( discoverRoutes() snapshot ), apiRoutes: [] |
See also
- Build pipeline — ordering vs CLI logs
- OXC toolchain — lint/format/transform narrative
- manic build CLI — operator-facing summary