Bun builds a Rust crate into a WebAssembly package that you import like any ES module. It runs cargo build for a wasm32 target, then wasm-bindgen, wasm-opt and writes the .js, .d.ts, .wasm and package.json files. It replaces wasm-pack.
bun wasm build ./crates/hello --outdir ./pkg
import init, { greet } from "./pkg/hello.js";
await init();
console.log(greet("Bun"));
bun build --target=wasm is the same command:
bun build --target=wasm ./crates/hello --outdir ./pkg
Requirements
- A Rust toolchain with the target installed:
rustup target add wasm32-unknown-unknown(orwasm32-wasip1,wasm32-wasip2for WASI). wasm-bindgenandwasm-optare used fromPATHwhen present. Otherwise Bun installs them once into$BUN_INSTALL/tools:wasm-bindgen-cliwithcargo, at the version the crate'sCargo.lockpins, andwasm-opt(binaryen) withbun x.
Import a crate directly
The bun:wasm plugin builds a crate when a module imports its Cargo.toml or one of its .rs files. The package is built into the crate's Cargo target directory and reused while the artifact is unchanged.
import { plugin } from "bun:wasm";
Bun.plugin(plugin());
preload = ["./preload.ts"]
import init, { greet } from "./crates/hello/src/lib.rs";
await init();
console.log(greet("Bun"));
The same plugin works with Bun.build:
import { plugin } from "bun:wasm";
await Bun.build({
entrypoints: ["./index.ts"],
outdir: "./dist",
plugins: [plugin()],
});
Build from JavaScript
import { build, optimize } from "bun:wasm";
const result = await build({
crate: "./crates/hello", // a directory, its Cargo.toml or a .rs file in it
outdir: "./pkg",
target: "web",
optimize: "Oz",
maxBytes: 200_000,
});
console.log(result.js, result.bytes, result.rawBytes);
// run wasm-opt on any module; a larger result is discarded
await optimize("./module.wasm", { level: "O3" });
outdir is replaced only when the build succeeds, so a failed build keeps the last usable package.
Cargo workspaces and prebuilt artifacts
package selects a member of a Cargo workspace, like cargo build -p. artifact packages a .wasm that cargo already built, for example on a build server, without running cargo.
bun wasm build --package hello --outdir ./pkg
bun wasm build --package hello --artifact ./target/wasm32-unknown-unknown/release/hello.wasm --outdir ./pkg
cargo replaces the command that runs cargo metadata and cargo build, for example a wrapper script or cross. In code it is a path or an array of words; on the command line, quote words that contain spaces. Bun does not run rustup target add for a custom cargo.
bun wasm build --package hello --cargo=./scripts/cargo.sh --outdir ./pkg
WASI
--wasi=p1 builds for wasm32-wasip1. The generated module's default export instantiates the module with node:wasi:
import init from "./pkg/tool.js";
const { exports } = await init({ args: ["tool"], env: {}, preopens: { "/": "." } });
--wasi=p2 builds a wasm32-wasip2 component and transpiles it to JavaScript with jco, run through bun x. wasm-opt does not read components, so p2 output is not optimized.
Flags
| Flag | Description |
|---|---|
-o, --outdir=<dir> | Output package. Defaults to pkg in the crate |
-p, --package=<name> | A package of the Cargo workspace |
--artifact=<file> | Package a .wasm cargo already built |
--cargo=<cmd> | Run <cmd> instead of cargo |
--target=<t> | wasm-bindgen target: web (default), bundler, nodejs, deno, no-modules |
--wasi=<p1|p2> | Build for WASI preview 1 or preview 2 |
--triple=<triple> | Any Rust target triple |
--dev | Debug profile, no wasm-opt |
--profile=<name> | Cargo profile. Defaults to release |
--opt-level=<level> | wasm-opt level: Oz (default), Os, O0 to O4 |
--no-opt | Skip wasm-opt |
--name=<name> | Base name of the generated files |
--features=<a,b> | Cargo features |
--no-default-features | Disable the crate's default features |
--locked | Pass --locked to cargo |
--no-typescript | Do not emit .d.ts files |
--no-pack | Do not write package.json |
--max-bytes=<n> | Fail when the optimized .wasm is larger |
-- <cargo args> | Passed to cargo build |
bun wasm opt <in.wasm> runs wasm-opt (-Oz by default) on a module.
If package.json has a wasm script, bun wasm runs that script. Inside the script, bun wasm is the command.