Contributing

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.

Using Nix (Alternative)#

The repository includes a Nix flake as an alternative to installing dependencies manually:

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:

brew install automake ccache cmake coreutils gnu-sed go icu4c libiconv libtool ninja pkg-config rustup-init ruby

Bun is written in Rust and requires a specific nightly toolchain (pinned in rust-toolchain.toml). Install Rust with rustup rather than your distro's rust/cargo packages — the build scripts use rustup to automatically install and update the pinned nightly:

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.

curl -fsSL https://bun.com/install | bash

Optional: Install ccache#

ccache caches compilation artifacts, which speeds up rebuilds:

# 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:

brew install llvm@21

If none of these work, install it manually.

Make sure Clang/LLVM 21 is in your path:

which clang-21

If not, add it manually:

# 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"

⚠️ On Ubuntu <= 20.04, you may need to install the C++ standard library separately. See the troubleshooting section.

Building Bun#

After cloning the repository, run the following command to build. This can take a while: it downloads and builds dependencies.

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:

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:

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.

bun bd <args>
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 <crate> (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_<scope>=1 enables debug logging for the corresponding declare_scope!(<scope>, ...) / scoped_log!(<scope>, ...) 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=<path-to-file>.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:

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:

bunx bun-pr <pr-number>
bunx bun-pr <branch-name>
bunx bun-pr "https://github.com/oven-sh/bun/pull/1234566"
bunx bun-pr --asan <pr-number> # 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:

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 (brew install buildkite/buildkite/bk) and set BUILDKITE_API_TOKEN to a read-scoped API token. The repo includes a .bk.yaml so bk commands default to the bun pipeline.

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-<build>/
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 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:

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:

# 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, 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:

fatal error: 'span' file not found
#include <span>
         ^~~~~~

The issue may also surface when first running bun run build, with Clang unable to compile a simple program:

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:

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:

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:

brew install pkg-config

macOS library not found for -lSystem#

If you see this error when compiling, run:

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:

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 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:

  • 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", rather than "see here".
  • Run every code example before you publish it, and check option names and defaults against the implementation on main.