Files

108 lines
6.0 KiB
Markdown
Raw Permalink Normal View History

2026-10-12 08:23:33 +02:00
<!-- SPDX-License-Identifier: Apache-2.0 -->
# `@aphrody/m3-bun`
The one Bun-native tool for M3 apps: a single Bun plugin and the `m3-bun` CLI. It builds, minifies, serves with HMR,
compiles to one executable, packages and generates deploy configs. One Bun file server serves the UI files and the API
from the same process. Full guide: `docs/guides/M3-BUN.md`.
```sh
m3-bun dev # HMR dev server: UI and API in one process (bunfig [serve.static] plugins)
m3-bun build [--minify none|basic|full] [--graph[=<registry>]]
m3-bun graph [file] # recorded builds (bun:graph), or what depends on a file
m3-bun serve # production server of the build directory
m3-bun compile [--target linux-x64-musl|windows-x64|darwin-arm64|...]
m3-bun package [--rootfs] [--deploy]
m3-bun deploy docker systemd nginx vercel fly cloudflare coolify vscode windows linux-image # or: all
m3-bun smoke # run the executable (yolo runtime when installed), GET /, /healthz, /api/m3/info
```
Inside the Aphrody checkout the same commands run as `yolo m3 bun <command>`.
## The plugin
`@aphrody/m3-bun/plugin` is `@aphrody/m3`'s plugin (Tailwind v4, Sass, `m3:*` token sheets, `?url`) plus what a
standalone checkout needs: the token sheets from `@aphrody/m3-tokens` when the Rust library is absent, the font sheets from
`@aphrody/m3-fonts`, `m3:icons/[style/]name[?fill=1&wght=300]` (Material Symbols SVG markup from the forked catalogue, inlined
at build time) and `m3:optional/<specifier>` (a module that may not be installed: its namespace and `resolved: true`, or
`null` and `resolved: false`).
```toml
# bunfig.toml
[serve.static]
plugins = ["@aphrody/m3-bun/plugin"]
```
```ts
// m3.config.ts
export default defineM3Config({ entry: "src/index.html", theme: { seed: "#6750a4" } });
export const bun = defineM3Bun({ api: "src/api/index.ts", port: 3000, compile: { bytecode: true } });
```
The fork's Bun plugins compose behind options of `m3Bun` (`src/plugins.ts`): `yaml`, `svelte` (on by default), `mdx`
(`@aphrody/bun-mdx-rs`, native `onBeforeParse`), `n2b` and `oxc`. Each one is imported on the first module its hooks
match. The m3 packages are modules too (`src/plugins-m3.ts`): `m3:icon/<style>/<name>` (`@aphrody/m3-icons`),
`m3:theme/{tokens,tailwind,spaceui}.css` (`@aphrody/m3-theme`) and `m3:assets/<path>[?url]` (`@aphrody/assets`).
## Build graph (`bun:graph`)
`m3-bun build --graph` (or `bun: { graph: true }` in `m3.config.ts`) records the build in the `bun:graph` SQLite registry of
the Aphrody fork of Bun, `.m3-bun/graph.sqlite` by default: the app is a repository, the build a run with its duration
sample and one artifact (path, SHA-256, bytes) per output file, and the `Bun.build` metafile a graph snapshot. Its nodes
are the inputs (virtual `m3:` modules included), the outputs and the external imports; its edges the imports, keyed by
import kind, and `bundled-into` from an input to each output it contributed to. `m3-bun graph` prints the `bun:graphx`
markdown report; `m3-bun graph src/main.ts` lists the modules that import the file, transitively, and the outputs it went
into. Runtimes without `bun:graph` fail with an explicit error.
## One process, UI and API
`src/api` exports Bun routes (`defineM3Api`); `Bun.serve` runs them natively next to the file server (cache headers,
ETag, brotli and gzip, SPA fallback, `/healthz`, `/api/m3/info`, security headers, traversal guard). `routesToFetch` turns the
same routes into one `fetch` handler for Cloudflare Workers and Vercel functions.
The server's optional parts load on first use (`src/server-*.ts`), on Bun APIs only:
| Module | What it does |
| --- | --- |
| HTML | `HTMLRewriter`: CSP nonce on every script, and the request's theme |
| Markdown | `Bun.markdown` pages |
| Crawl | `sitemap.xml`, `robots.txt` and `llms.txt` |
| Realtime | WebSocket topics |
| Data | `bun:sqlite` with migrations |
| Secrets | `Bun.secrets` |
| Auth | `Bun.password` and sessions |
| Cache | `Bun.RedisClient` |
| MCP | `@aphrody/m3-mcp` tools |
| AG-UI | `@aphrody/m3-ai` |
| Next | `@aphrody/next-bun` behind a proxy |
| m3 routes | `@aphrody/m3/server` routes |
`m3-bun dev --inspect [--inspector-ui]` runs the dev server in a child Bun with its inspector on, relays its console and
exceptions, and serves the WebKit Web Inspector (`@aphrody/web-inspector-bun`). API load and handler errors go to Bun's
error overlay.
## Tools
`m3-bun lint` (eslint-plugin-m3 through oxlint), `migrate` (m3-codemods, `--next` for m3-next-migrate), `create`
(scaffold), `design` (m3-design), `og` (canvas Open Graph image) and `lib` (m3-config library build). Each tool package
loads on first use (`src/tools-*.ts`). If a package is missing, the error names it.
Deploy targets also include `tauri`, `webos`, `lambda`, `os-themes`, `release`, `next` and `windows-inspect`.
## Compile: bytecode and the limits
`m3-bun compile` embeds the built files and the API in a generated entry and links it with the Bun runtime. It passes
`bytecode: true` to `Bun.build` (with `minify` as configured and `sourcemap: "none"`): JavaScriptCore bytecode is stored in
the executable, so start-up skips parsing. Measured on the fixture app (medians, same host, interleaved runs):
`--version` 4.3 ms instead of 7.5 ms, first `/healthz` 5.8 ms instead of 9.3 ms; the executable grows by about 1.5 MB.
Bytecode output is CommonJS, so the generated entry has no top-level await. `compile: { bytecode: false }` turns it off.
The executable is not static: glibc targets link libc, libm, libdl, libpthread and the loader, musl targets link musl and
libstdc++. Images carry those libraries (`m3-bun package --rootfs`, the `linux-image` target); the `docker` target
compiles and runs on `ghcr.io/aphrody-labs/alpine:3.24-runtime` (musl, libstdc++, CA certificates, tzdata). Windows
metadata (`compile.windows`) is applied only when compiling on Windows; elsewhere the report says so.
`m3 compile` of `@aphrody/m3` runs this compiler (an optional peer): compiling an HTML entry directly yields an HTML
file, not an executable.
Deploy targets are files; nothing here applies, pushes or reloads anything.