--- title: Contributing description: Contributing to Bun --- Configuring a development environment for Bun can take 10-30 minutes depending on your internet connection and computer speed. You will need ~10GB of free disk space for the repository and build artifacts. If you are using Windows, see [Building Windows](/project/building-windows). ## Using Nix (Alternative) The repository includes a Nix flake as an alternative to installing dependencies manually: ```bash nix develop bun bd ``` `nix develop` provides all dependencies in an isolated, reproducible environment without requiring sudo. ## Install Dependencies (Manual) Using your system's package manager, install Bun's dependencies: ```bash macOS (Homebrew) brew install automake ccache cmake coreutils gnu-sed go icu4c libiconv libtool ninja pkg-config rustup-init ruby ``` ```bash Ubuntu/Debian sudo apt install curl wget lsb-release software-properties-common cmake git golang libtool ninja-build pkg-config ruby-full xz-utils ``` ```bash Arch sudo pacman -S base-devel cmake git go libiconv libtool make ninja pkg-config python rustup sed unzip ruby ``` ```bash Fedora sudo dnf install clang21 llvm21 lld21 cmake git golang libtool ninja-build pkg-config ruby libatomic-static libstdc++-static sed unzip which libicu-devel 'perl(Math::BigInt)' ``` ```bash openSUSE Tumbleweed sudo zypper install go cmake ninja automake git icu rustup ``` Bun is written in Rust and requires a specific nightly toolchain (pinned in `rust-toolchain.toml`). Install Rust with [rustup](https://rustup.rs) rather than your distro's `rust`/`cargo` packages — the build scripts use rustup to automatically install and update the pinned nightly: ```bash curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh ``` Before starting, install a release build of Bun: the build uses Bun's bundler to transpile and minify code, and to run the code generation scripts. ```bash Native curl -fsSL https://bun.com/install | bash ``` ```bash npm npm install -g bun ``` ```bash Homebrew brew tap oven-sh/bun brew install bun ``` ### Optional: Install `ccache` `ccache` caches compilation artifacts, which speeds up rebuilds: ```bash # For macOS brew install ccache # For Ubuntu/Debian sudo apt install ccache # For Arch sudo pacman -S ccache # For Fedora sudo dnf install ccache # For openSUSE sudo zypper install ccache ``` The build scripts detect and use `ccache` automatically if it's available. Check cache statistics with `ccache --show-stats`. ## Install LLVM Bun requires LLVM 21.1.8 (`clang` is part of LLVM). The build system enforces this version: a mismatched version causes memory allocation failures at runtime. In most cases, you can install LLVM through your system package manager: ```bash macOS (Homebrew) brew install llvm@21 ``` ```bash Ubuntu/Debian # LLVM has an automatic installation script that is compatible with all versions of Ubuntu wget https://apt.llvm.org/llvm.sh -O - | sudo bash -s -- 21 all ``` ```bash Arch sudo pacman -S llvm clang lld ``` ```bash Fedora sudo dnf install llvm clang lld-devel ``` ```bash openSUSE Tumbleweed sudo zypper install clang21 lld21 llvm21 ``` If none of these work, install it [manually](https://github.com/llvm/llvm-project/releases/tag/llvmorg-21.1.8). Make sure Clang/LLVM 21 is in your path: ```bash which clang-21 ``` If not, add it manually: ```bash macOS (Homebrew) # use fish_add_path if you're using fish # use path+="$(brew --prefix llvm@21)/bin" if you are using zsh export PATH="$(brew --prefix llvm@21)/bin:$PATH" ``` ```bash Arch # use fish_add_path if you're using fish export PATH="$PATH:/usr/lib/llvm21/bin" ``` ⚠️ On Ubuntu \<= 20.04, you may need to install the C++ standard library separately. See the [troubleshooting section](#span-file-not-found-on-ubuntu). ## Building Bun After cloning the repository, run the following command to build. This can take a while: it downloads and builds dependencies. ```bash bun run build ``` The binary is at `./build/debug/bun-debug`. It is recommended to add this to your `$PATH`. To verify the build worked, print its version: ```bash build/debug/bun-debug --version x.y.z_debug ``` ## VSCode VSCode is the recommended IDE for working on Bun; the repository includes configuration for it. After opening the repository, run `Extensions: Show Recommended Extensions` to install the recommended extensions for Rust and C++. rust-analyzer picks up the workspace `Cargo.toml` automatically and uses the pinned toolchain in `rust-toolchain.toml` for analysis, so diagnostics match the build. If you use a different editor, point rust-analyzer (or your editor's Rust plugin) at the repo root — the Cargo workspace and `rust-toolchain.toml` are discovered automatically. We recommend adding `./build/debug` to your `$PATH` so that you can run `bun-debug` in your terminal: ```sh bun-debug ``` ## Running debug builds The `bd` package.json script compiles and runs a debug build of Bun, only printing the output of the build process if it fails. ```sh bun bd bun bd test foo.test.ts bun bd ./foo.ts ``` A full debug build can take a few minutes when Rust or C++ has changed; cargo's incremental compilation makes subsequent Rust-only rebuilds much faster. If your development workflow is "change one line, save, rebuild", you will still spend too much time waiting for the link step. Instead: - Batch up your changes - Use `cargo check -p ` (or `bun run rust:check` for the whole workspace) to type-check Rust changes without linking. `bun run watch` runs `cargo check` on every save. - Ensure rust-analyzer is running for inline diagnostics (the recommended VSCode extensions set this up) - Prefer using the debugger ("CodeLLDB" in VSCode) to step through the code. - Use debug logs. `BUN_DEBUG_=1` enables debug logging for the corresponding `declare_scope!(, ...)` / `scoped_log!(, ...)` logs. Set `BUN_DEBUG_QUIET_LOGS=1` to disable all debug logging that isn't explicitly enabled. To dump debug logs into a file, set `BUN_DEBUG=.log`. Debug logs are removed in release builds. - src/js/\*\*.ts changes rebuild almost instantly. Single-crate Rust changes and C++ changes are incremental; only the final link is unavoidable. ## Code generation scripts Bun's build process runs several code generation scripts automatically when certain files change: - `./src/codegen/generate-jssink.ts` -- Generates `build/debug/codegen/JSSink.cpp`, `build/debug/codegen/JSSink.h` which implement various classes for interfacing with `ReadableStream`. This is internally how `FileSink`, `ArrayBufferSink`, `"type": "direct"` streams and other code related to streams work. - `./src/codegen/generate-classes.ts` -- Generates Rust & C++ bindings for JavaScriptCore classes implemented in Rust. `**/*.classes.ts` files define the interfaces for classes, methods, prototypes, and getters/setters; the code generator reads them to generate the boilerplate that implements the JavaScript objects in C++ and wires them up to Rust. - `./src/codegen/cppbind.ts` -- Scans the C++ bindings for functions marked with an export attribute and generates automatic Rust FFI wrappers (`cpp.rs`) for them. - `./src/codegen/bundle-modules.ts` -- Bundles built-in modules like `node:fs`, `bun:ffi` into files included in the final binary. In development, these can be reloaded without rebuilding native code (you still need to run `bun run build`, but it re-reads the transpiled files from disk afterwards). In release builds, these are embedded into the binary. - `./src/codegen/bundle-functions.ts` -- Bundles globally-accessible functions implemented in JavaScript/TypeScript like `ReadableStream` and `WritableStream`. These are used similarly to the builtin modules, but the output more closely aligns with what WebKit/Safari does for Safari's built-in functions, so implementations can be copy-pasted from WebKit as a starting point. ## Modifying ESM modules Certain modules like `node:fs`, `node:stream`, `bun:sqlite`, and `ws` are implemented in JavaScript. These live in `src/js/{node,bun,thirdparty}` files and are pre-bundled using Bun. ## Release build To compile a release build of Bun, run: ```bash bun run build:release ``` The binaries are at `./build/release/bun` and `./build/release/bun-profile`. ### Download release build from pull requests You can run the release build from a pull request without building it locally, which is useful for manually testing changes before they are merged. Use the `bun-pr` npm package: ```sh bunx bun-pr bunx bun-pr bunx bun-pr "https://github.com/oven-sh/bun/pull/1234566" bunx bun-pr --asan # Linux x64 only ``` `bun-pr` downloads the release build from the pull request's GitHub Actions artifacts and adds it to `$PATH` as `bun-${pr-number}`, so you can run it directly: ```sh bun-1234566 --version ``` You may need the `gh` CLI installed to authenticate with GitHub. ### Viewing CI failures from the terminal Bun's CI runs on BuildKite. Install the [BuildKite CLI](https://github.com/buildkite/cli) (`brew install buildkite/buildkite/bk`) and set `BUILDKITE_API_TOKEN` to a read-scoped [API token](https://buildkite.com/user/api-access-tokens). The repo includes a `.bk.yaml` so `bk` commands default to the `bun` pipeline. ```sh bun run ci:status # progress summary for the current branch's latest build bun run ci:errors # rendered test-failure output, tagged [new] vs [also on main] bun run ci:logs # save full logs for each failed job to ./tmp/ci-/ bun run ci:watch # watch until the build finishes bun run ci:find # print the build number (compose with raw `bk`) ``` All of these accept a target: `#1234` (PR number), a PR URL, a branch name, or a build number. Without one they use the current git branch. ## AddressSanitizer [AddressSanitizer](https://en.wikipedia.org/wiki/AddressSanitizer) helps find memory issues, and is enabled by default in debug builds of Bun on Linux and macOS. This covers the Rust code, the C++ bindings, and all dependencies. It makes the build take about 2x longer; if that's stopping you from being productive you can disable it with `bun run build:debug:noasan` (or pass `--asan=off` to `scripts/build.ts`), but generally we recommend batching your changes up between builds. To build a release build with AddressSanitizer, run: ```bash bun run build:asan ``` CI runs the test suite with at least one target built with AddressSanitizer. ## Building WebKit locally + Debug mode of JSC WebKit is not cloned by default (to save time and disk space). To clone and build WebKit locally, run: ```bash # Clone WebKit into ./vendor/WebKit git clone https://github.com/oven-sh/WebKit vendor/WebKit # Check out the version pinned in WEBKIT_VERSION in scripts/build/deps/webkit.ts # (a commit sha or an autobuild-* release tag; this handles both) bun sync-webkit-source # Build bun with the local JSC build — this automatically configures and builds JSC bun run build:local ``` `bun run build:local` handles everything: configuring JSC, building JSC, and building Bun. On subsequent runs, JSC rebuilds incrementally if any WebKit sources changed. `ninja -Cbuild/debug-local` also works after the first build, and builds both Bun and JSC. The build output goes to `./build/debug-local` (instead of `./build/debug`), so you'll need to update a couple of places: - The first line in `src/js/builtins.d.ts` - The `CompilationDatabase` line in `.clangd` config should be `CompilationDatabase: build/debug-local` - In `.vscode/launch.json`, many configurations use `./build/debug/`, change them as you see fit The WebKit folder, including build artifacts, is 8GB+ in size. If you are using a JSC debug build with VSCode, run the `C/C++: Select a Configuration` command so IntelliSense finds the debug headers. If you make changes to Bun's [WebKit fork](https://github.com/oven-sh/WebKit), you also have to change `WEBKIT_VERSION` in `scripts/build/deps/webkit.ts` to point to your commit hash or release tag. ## Troubleshooting ### 'span' file not found on Ubuntu ⚠️ These instructions are specific to Ubuntu. The same issues are unlikely on other Linux distributions. Clang uses `libstdc++`, the C++ standard library implementation provided by the GNU Compiler Collection (GCC), by default. Clang can link against `libc++` instead, but that requires explicitly passing the `-stdlib` flag. Bun relies on C++20 features like `std::span`, which are not available in GCC versions lower than 11. As a result, running `bun run build` may fail with the following error: ```txt fatal error: 'span' file not found #include ^~~~~~ ``` The issue may also surface when first running `bun run build`, with Clang unable to compile a simple program: ```txt The C++ compiler "/usr/bin/clang++-21" is not able to compile a simple test program. ``` To fix the error, update GCC to version 11. It may be available in your distribution's official repositories; otherwise, add a third-party repository that provides GCC 11 packages: ```bash sudo apt update sudo apt install gcc-11 g++-11 # If the above command fails with `Unable to locate package gcc-11` we need # to add the APT repository sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test # Now run `apt install` again sudo apt install gcc-11 g++-11 ``` Then set GCC 11 as the default compiler: ```bash sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-11 100 sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-11 100 ``` ### libarchive If you see an error on macOS when compiling `libarchive`, run: ```bash brew install pkg-config ``` ### macOS `library not found for -lSystem` If you see this error when compiling, run: ```bash xcode-select --install ``` ### Cannot find `libatomic.a` Bun defaults to linking `libatomic` statically, as not all systems have it. If you are building on a distro that does not have a static libatomic available, enable dynamic linking with: ```bash bun run build --static-libatomic=off ``` The built version of Bun may not work on other systems if compiled this way. ## Using bun-debug - Disable logging: `BUN_DEBUG_QUIET_LOGS=1 bun-debug ...` (to disable all debug logging) - Enable logging for a specific scope: `BUN_DEBUG_EventLoop=1 bun-debug ...` (to enable `scoped_log!(EventLoop, ...)` output) - Bun transpiles every file it runs. To see the actual executed source in a debug build, find it in `/tmp/bun-debug-src/...path/to/file`. For example, the transpiled version of `/home/bun/index.ts` is in `/tmp/bun-debug-src/home/bun/index.ts` ## Contributing to the docs The docs are the MDX files in `docs/` in the Bun repository. [bun.com/docs](https://bun.com/docs) is built from them. ### Voice A docs page describes what Bun does and what you can do with it. Write it so that a developer who is new to Bun can read the page once and act on it. These guidelines are adapted from the [Next.js docs contribution guide](https://nextjs.org/docs/community/contribution-guide#voice): - Write short sentences that each make one point. If a sentence needs several commas or a parenthetical, split it up or turn it into a list. - Use plain words: "use" rather than "utilize", "to" rather than "in order to". Cut filler such as "Note that" and "Please". - Use the active voice and name the actor: "Bun reads `bunfig.toml`", rather than "`bunfig.toml` is read". A sentence built around "is" and "by" is usually passive. - Describe current behavior in the present tense: "Bun installs the package", rather than "Bun will install the package". - Name the subject when "this" or "it" could refer to more than one thing: "`--isolate` is how Jest behaves by default", rather than "This is how Jest behaves by default". - Address the reader as "you", and make Bun (or the specific tool) the other actor: "Bun caches the tarball", rather than "we cache the tarball" or "let's cache the tarball". - Leave out "easy", "simple", "just", and "quick". They add nothing when a task is easy and discourage readers when it is not. State the concrete property instead: "one command", "no configuration". - Say what to do rather than what to avoid: "use `port: 0` so the operating system picks a free port", rather than "don't hardcode ports". State limitations plainly; a limitation is a fact, not a warning to the reader. - Use gender-neutral language: "developers", "users", "they". - Make link text name its destination: "see [`bun install`](/pm/cli/install)", rather than "see here". - Run every code example before you publish it, and check option names and defaults against the implementation on `main`.