8.5 KiB
This is the Bun repository - an all-in-one JavaScript runtime & toolkit designed for speed, with a bundler, test runner, and Node.js-compatible package manager. It's written primarily in Zig with C++ for JavaScriptCore integration, powered by WebKit's JavaScriptCore engine.
Building and Running Bun
Build Commands
- Build debug version:
bun bdorbun run build:debug- Creates a debug build at
./build/debug/bun-debug - Compilation takes ~2.5 minutes
- Creates a debug build at
- Run tests with your debug build:
bun bd test <test-file>- CRITICAL: Never use
bun testdirectly - it won't include your changes
- CRITICAL: Never use
- Run any command with debug build:
bun bd <command>
Other Build Variants
bun run build:release- Release build
Address sanitizer is enabled by default in debug builds of Bun.
Testing
Running Tests
- Single test file:
bun bd test test/js/bun/http/serve.test.ts - Fuzzy match test file:
bun bd test http/serve.test.ts - With filter:
bun bd test test/js/bun/http/serve.test.ts -t "should handle"
Test Organization
test/js/bun/- Bun-specific API tests (http, crypto, ffi, shell, etc.)test/js/node/- Node.js compatibility teststest/js/web/- Web API tests (fetch, WebSocket, streams, etc.)test/cli/- CLI command tests (install, run, test, etc.)test/regression/issue/- Regression tests (create one per bug fix)test/bundler/- Bundler and transpiler teststest/integration/- End-to-end integration teststest/napi/- N-API compatibility teststest/v8/- V8 C++ API compatibility tests
Writing Tests
Tests use Bun's Jest-compatible test runner with proper test fixtures:
import { test, expect } from "bun:test";
import { bunEnv, bunExe, tempDirWithFiles } from "harness";
test("my feature", async () => {
// Create temp directory with test files
const dir = tempDirWithFiles("test-prefix", {
"index.js": `console.log("hello");`,
});
// Spawn Bun process
await using proc = Bun.spawn({
cmd: [bunExe(), "index.js"],
env: bunEnv,
cwd: dir,
});
const [stdout, stderr, exitCode] = await Promise.all([
new Response(proc.stdout).text(),
new Response(proc.stderr).text(),
proc.exited,
]);
expect(exitCode).toBe(0);
expect(stdout).toBe("hello\n");
});
Code Architecture
Language Structure
- Zig code (
src/*.zig): Core runtime, JavaScript bindings, package manager - C++ code (
src/bun.js/bindings/*.cpp): JavaScriptCore bindings, Web APIs - TypeScript (
src/js/): Built-in JavaScript modules with special syntax (see JavaScript Modules section) - Generated code: Many files are auto-generated from
.classes.tsand other sources
Core Source Organization
Runtime Core (src/)
bun.zig- Main entry pointcli.zig- CLI command orchestrationjs_parser.zig,js_lexer.zig,js_printer.zig- JavaScript parsing/printingtranspiler.zig- Wrapper around js_parser with sourcemap supportresolver/- Module resolution systemallocators/- Custom memory allocators for performance
JavaScript Runtime (src/bun.js/)
bindings/- C++ JavaScriptCore bindings- Generated classes from
.classes.tsfiles - Manual bindings for complex APIs
- Generated classes from
api/- Bun-specific APIsserver.zig- HTTP server implementationFFI.zig- Foreign Function Interfacecrypto.zig- Cryptographic operationssqlite.zig- Native SQLite integrationglob.zig- File pattern matching
node/- Node.js compatibility layer- Module implementations (fs, path, crypto, etc.)
- Process and Buffer APIs
webcore/- Web API implementationsfetch.zig- Fetch APIstreams.zig- Web StreamsBlob.zig,Response.zig,Request.zig
event_loop/- Event loop and task management
Build Tools & Package Manager
src/bundler/- JavaScript bundler- Advanced tree-shaking
- CSS processing
- HTML handling
src/install/- Package managerlockfile/- Lockfile handlingnpm.zig- npm registry clientlifecycle_script_runner.zig- Package scripts
Other Key Components
src/shell/- Cross-platform shell implementationsrc/css/- CSS parser and processorsrc/http/- HTTP client implementationwebsocket_client/- WebSocket client (including deflate support)
src/sql/- SQL database integrationssrc/bake/- Server-side rendering framework
JavaScript Class Implementation (C++)
When implementing JavaScript classes in C++:
-
Create three classes if there's a public constructor:
class Foo : public JSC::JSDestructibleObject(if has C++ fields)class FooPrototype : public JSC::JSNonFinalObjectclass FooConstructor : public JSC::InternalFunction
-
Define properties using HashTableValue arrays
-
Add iso subspaces for classes with C++ fields
-
Cache structures in ZigGlobalObject
Development Workflow
Code Formatting
bun run prettier- Format JS/TS filesbun run zig-format- Format Zig filesbun run clang-format- Format C++ files
Watching for Changes
bun run watch- Incremental Zig compilation with error checkingbun run watch-windows- Windows-specific watch mode
Code Generation
Code generation happens automatically as part of the build process. The main scripts are:
src/codegen/generate-classes.ts- Generates Zig & C++ bindings from*.classes.tsfilessrc/codegen/generate-jssink.ts- Generates stream-related classessrc/codegen/bundle-modules.ts- Bundles built-in modules likenode:fssrc/codegen/bundle-functions.ts- Bundles global functions likeReadableStream
In development, bundled modules can be reloaded without rebuilding Zig by running bun run build.
JavaScript Modules (src/js/)
Built-in JavaScript modules use special syntax and are organized as:
node/- Node.js compatibility modules (node:fs,node:path, etc.)bun/- Bun-specific modules (bun:ffi,bun:sqlite, etc.)thirdparty/- NPM modules we replace (likews)internal/- Internal modules not exposed to usersbuiltins/- Core JavaScript builtins (streams, console, etc.)
Special Syntax in Built-in Modules
-
$prefix - Access to private properties and JSC intrinsics:const arr = $Array.from(...); // Private global map.$set(...); // Private method const arr2 = $newArrayWithSize(5); // JSC intrinsic -
require()- Must use string literals, resolved at compile time:const fs = require("fs"); // Directly loads by numeric ID -
Debug helpers:
$debug()- Like console.log but stripped in release builds$assert()- Assertions stripped in release buildsif($debug) {}- Check if debug env var is set
-
Platform detection:
process.platformandprocess.archare inlined and dead-code eliminated -
Export syntax: Use
export defaultwhich gets converted to a return statement:export default { readFile, writeFile, };
Note: These are NOT ES modules. The preprocessor converts $ to @ (JSC's actual syntax) and handles the special functions.
Important Development Notes
- Never use
bun testorbun <file>directly - always usebun bd testorbun bd <command>.bun bdcompiles & runs the debug build. - Use
await usingfor proper resource cleanup with Bun APIs (Bun.spawn, Bun.serve, Bun.connect, etc.) - Follow existing code style - check neighboring files for patterns
- Create regression tests in
test/regression/issue/when fixing bugs - Use absolute paths - Always use absolute paths in file operations
- Avoid shell commands - Don't use
findorgrepin tests; use Bun's Glob and built-in tools - Memory management - In Zig code, be careful with allocators and use defer for cleanup
- Cross-platform - Test on macOS, Linux, and Windows when making platform-specific changes
- Debug builds - Use
BUN_DEBUG_QUIET_LOGS=1to disable debug logging, orBUN_DEBUG_<scope>=1to enable specific scopes - Transpiled source - Find transpiled files in
/tmp/bun-debug-src/for debugging
Key APIs and Features
Bun-Specific APIs
- Bun.serve() - High-performance HTTP server
- Bun.spawn() - Process spawning with better performance than Node.js
- Bun.file() - Fast file I/O operations
- Bun.write() - Unified API for writing to files, stdout, etc.
- Bun.$ (Shell) - Cross-platform shell scripting
- Bun.SQLite - Native SQLite integration
- Bun.FFI - Call native libraries from JavaScript
- Bun.Glob - Fast file pattern matching