Bun v1.0.12 fixes 24 bugs, makes bun's CLI help menus easier to read, adds support for bun -e
, bun --env-file
, server.url
, import.meta.env
, expect.unreachable()
, improves support for module mocks, allows importing builtin modules at bundle-time, and improves Node.js compatibility.
Bun is an incredibly fast JavaScript runtime, bundler, transpiler, and package manager — all in one. In case you missed it, here are some of the recent changes to Bun:
v1.0.8
- Fixes 138 bugs (addressing 257 👍 reactions), makesrequire()
use 30% less memory, adds module mocking tobun test
, fixes morebun install
bugsv1.0.9
- Fixes a glibc symbol version error, illegal instruction error, a Bun.spawn bug, an edgecase with peer dependency installs, and a JSX transpiler bugfixv1.0.10
- Fixes 14 bugs (addressing 102 👍 reactions), node:http gets 14% faster, stability improvements to Bun for Linux ARM64, bun install bugfix, and node:http bugfixesv1.0.11
- Fixes 5 bugs, addsBun.semver
, fixes a bug in bun install and fixes bugs impactingastro
and@google-cloud/storage
To install Bun:
curl -fsSL https://bun.sh/install | bash
npm install -g bun
brew tap oven-sh/bun
brew install bun
docker pull oven/bun
docker run --rm --init --ulimit memlock=-1:-1 oven/bun
To upgrade Bun:
bun upgrade
bun -e
evaluates a script
Bun now supports the -e
flag, which is short for --eval
, and allows you to run a script without creating a file. This is useful for quick scripts where you don't want to create a file and is similar to node -e
.
bun -e "console.log('Hello, JavaScript!')"
Hello, JavaScript!
bun -e
leverages Bun's builtin support for TypeScript & JSX:
$ bun -e "console.log(<div>Hello, TSX</div> as React.ReactNode);"
<div>Hello, TSX</div>
Top-level await also works:
$ bun -e "console.write(await fetch('https://api.github.com/repos/oven-sh/bun').then(r => r.text()))"
{
"id": 357728969,
"node_id": "MDEwOlJlcG9zaXRvcnkzNTc3Mjg5Njk=",
"name": "bun",
"full_name": "oven-sh/bun",
"private": false,
"owner": {
"login": "oven-sh",
"id": 108928776,
...
bun --env-file <path>
overrides the .env file to load
Bun's automatic .env
loader first landed nearly two years ago in v0.0.x, and today we're releasing support for --env-file
to override which specific .env
file to load. This is useful for testing different environments.
If you have custom environment variables that you want to load into your process, you can use --env-file
to specify a file containing environment variables to load into the process. You can define multiple --env-file
arguments.
bun --env-file=.env.1 src/index.ts
bun --env-file=.env.abc --env-file=.env.def run build
You can use --env-file
when running scripts in bun's runtime, or when running package.json scripts.
Thanks to @otgerrogla for implementing this feature.
server.url
We added support for server.url
, which returns a URL object that defines the location of the HTTP server. This is especially useful in tests when you want to get the formatted URL of the server.
const server = Bun.serve({
port: 0, // random port
fetch(request) {
return new Response();
},
});
console.log("Listening...", `${server.url}`);
// Listening... http://localhost:1234/
import.meta.env
for accessing environment variables
You can now access environment variables using import.meta.env
, a pattern used by frameworks such as Vite.
console.log(import.meta.env.NODE_ENV); // "development"
import.meta.env
is an alias of process.env
and Bun.env
. It exists for compatibility with other parts of the JavaScript ecosystem.
Thanks to @otgerrogla for implementing this feature.
expect.unreachable()
bun test
gets support for expect.unreachable()
, which throws an error if the code path is reached. This is useful for ensuring that a function is never called.
import { expect, test } from "bun:test";
import { foo } from "my-lib";
test("enum handles every case", () => {
switch (foo()) {
case "a":
break;
case "b":
break;
default: {
// Now:
expect.unreachable();
// Previously:
// throw new Error("Unreachable");
}
}
});
Improved CLI help output
We've redone the help menus for Bun's CLI to make them easier to read and understand.
$ bun --help
Bun is a fast JavaScript runtime, package manager, bundler, and test runner. (1.0.12 (904134cc))
Usage: bun <command> [...flags] [...args]
Commands:
run ./my-script.ts Execute a file with Bun
lint Run a package.json script
test Run unit tests with Bun
x prettier Execute a package binary (CLI), installing if needed (bunx)
repl Start a REPL session with Bun
install Install dependencies for a package.json (bun i)
add astro Add a dependency to package.json (bun a)
remove left-pad Remove a dependency from package.json (bun rm)
update backbone Update outdated dependencies
link [<package>] Register or link a local npm package
unlink Unregister a local npm package
pm <subcommand> Additional package management utilities
build ./a.ts ./b.jsx Bundle TypeScript & JavaScript into a single file
init Start an empty Bun project from a blank template
create @evan/duckdb Create a new project from a template (bun c)
upgrade Upgrade to latest version of Bun.
<command> --help Print help text for command.
Flags:
--watch Automatically restart the process on file change
--hot Enable auto reload in the Bun runtime, test runner, or bundler
--smol Use less memory, but run garbage collection more often
-r, --preload Import a module before other modules are loaded
--inspect Activate Bun's debugger
--inspect-wait Activate Bun's debugger, wait for a connection before executing
--inspect-brk Activate Bun's debugger, set breakpoint on first line of code and wait
--if-present Exit without an error if the entrypoint does not exist
--no-install Disable auto install in the Bun runtime
--install Configure auto-install behavior. One of "auto" (default, auto-installs when no node_modules), "fallback" (missing packages only), "force" (always).
-i Auto-install dependencies during execution. Equivalent to --install=fallback.
-e, --eval Evaluate argument as a script
--prefer-offline Skip staleness checks for packages in the Bun runtime and resolve from disk
--prefer-latest Use the latest matching versions of packages in the Bun runtime, always checking npm
-p, --port Set the default port for Bun.serve
-b, --bun Force a script or package to use Bun's runtime instead of Node.js (via symlinking node)
--silent Don't print the script command
-v, --version Print version and exit
--revision Print version with revision and exit
--env-file Load environment variables from the specified file(s)
--cwd Absolute path to resolve files & entry points from. This just changes the process' cwd.
-c, --config Specify path to Bun config file. Default $cwd/bunfig.toml
-h, --help Display this menu and exit
(more flags in bun install --help, bun test --help, and bun build --help)
Learn more about Bun: https://bun.sh/docs
Join our Discord community: https://bun.sh/discord
$ bun install --help
Usage: bun install [flags] [...<pkg>]
Alias: bun i
Install the dependencies listed in package.json
Flags:
-c, --config Specify path to config file (bunfig.toml)
-y, --yarn Write a yarn.lock file (yarn v1)
-p, --production Don't install devDependencies
--no-save Don't update package.json or save a lockfile
--save Save to package.json (true by default)
--dry-run Don't install anything
--frozen-lockfile Disallow changes to lockfile
-f, --force Always request the latest versions from the registry & reinstall all dependencies
--cache-dir Store & load cached data from a specific directory path
--no-cache Ignore manifest cache entirely
--silent Don't log anything
--verbose Excessively verbose logging
--no-progress Disable the progress bar
--no-summary Don't print a summary
--no-verify Skip verifying integrity of newly downloaded packages
--ignore-scripts Skip lifecycle scripts in the project's package.json (dependency scripts are never run)
-g, --global Install globally
--cwd Set a specific cwd
--backend Platform-specific optimizations for installing dependencies. Possible values: "clonefile" (default), "hardlink", "symlink", "copyfile"
--link-native-bins Link "bin" from a matching platform-specific "optionalDependencies" instead. Default: esbuild, turbo
--help Print this help menu
-d, --dev Add dependency to "devDependencies"
--optional Add dependency to "optionalDependencies"
-E, --exact Add the exact version instead of the ^range
Examples:
Install the dependencies for the current project
bun install
Skip devDependencies
bun install --production
Full documentation is available at https://bun.sh/docs/cli/install
$ bun build --help
Usage:
Transpile and bundle one or more files.
bun build [...flags] [...entrypoints]
Flags:
--compile Generate a standalone Bun executable containing your bundled code
--watch Automatically restart the process on file change
--target The intended execution environment for the bundle. "browser", "bun" or "node"
--outdir Default to "dist" if multiple files
--outfile Write to a file
--sourcemap Build with sourcemaps - 'inline', 'external', or 'none'
--format Specifies the module format to build to. Only "esm" is supported.
--root Root directory used for multiple entry points
--splitting Enable code splitting
--public-path A prefix to be appended to any import paths in bundled code
-e, --external Exclude module from transpilation (can use * wildcards). ex: -e react
--entry-naming Customize entry point filenames. Defaults to "[dir]/[name].[ext]"
--chunk-naming Customize chunk filenames. Defaults to "[name]-[hash].[ext]"
--asset-naming Customize asset filenames. Defaults to "[name]-[hash].[ext]"
--server-components Enable React Server Components (experimental)
--no-bundle Transpile file only, do not bundle
--minify Enable all minification flags
--minify-syntax Minify syntax and inline data
--minify-whitespace Minify whitespace
--minify-identifiers Minify identifiers
Examples:
Frontend web apps:
bun build ./src/index.ts --outfile=bundle.js
bun build ./index.jsx ./lib/worker.ts --minify --splitting --outdir=out
Bundle code to be run in Bun (reduces server startup time)
bun build ./server.ts --target=bun --outfile=server.js
Creating a standalone executable (see https://bun.sh/docs/bundler/executables)
bun build ./cli.ts --compile --outfile=my-app
A full list of flags is available at https://bun.sh/docs/bundler
$ bun test --help
Usage: bun test [...flags] [<pattern>...]
Run all matching test files and print the results to stdout
Flags:
--timeout Set the per-test timeout in milliseconds, default is 5000.
--update-snapshots Update snapshot files
--rerun-each Re-run each test file <NUMBER> times, helps catch certain bugs
--only Only run tests that are marked with "test.only()"
--todo Include tests that are marked with "test.todo()"
--coverage Generate a coverage profile
--bail Exit the test suite after <NUMBER> failures. If you do not specify a number, it defaults to 1.
-t, --test-name-pattern Run only tests with a name that matches the given regex.
Examples:
Run all test files
bun test
Run all test files with "foo" or "bar" in the file name
bun test foo bar
Run all test files, only including tests whose names includes "baz"
bun test --test-name-pattern baz
Full documentation is available at https://bun.sh/docs/cli/test
$ bun run --help
Usage: bun run [flags] <file or script>
Flags:
--silent Don't print the script command
-b, --bun Force a script or package to use Bun's runtime instead of Node.js (via symlinking node)
--watch Automatically restart the process on file change
--hot Enable auto reload in the Bun runtime, test runner, or bundler
--smol Use less memory, but run garbage collection more often
-r, --preload Import a module before other modules are loaded
--inspect Activate Bun's debugger
--inspect-wait Activate Bun's debugger, wait for a connection before executing
--inspect-brk Activate Bun's debugger, set breakpoint on first line of code and wait
--if-present Exit without an error if the entrypoint does not exist
--no-install Disable auto install in the Bun runtime
--install Configure auto-install behavior. One of "auto" (default, auto-installs when no node_modules), "fallback" (missing packages only), "force" (always).
-i Auto-install dependencies during execution. Equivalent to --install=fallback.
-e, --eval Evaluate argument as a script
--prefer-offline Skip staleness checks for packages in the Bun runtime and resolve from disk
--prefer-latest Use the latest matching versions of packages in the Bun runtime, always checking npm
-p, --port Set the default port for Bun.serve
--main-fields Main fields to lookup in package.json. Defaults to --target dependent
--extension-order Defaults to: .tsx,.ts,.jsx,.js,.json
--tsconfig-override Specify custom tsconfig.json. Default <d>$cwd<r>/tsconfig.json
-d, --define Substitute K:V while parsing, e.g. --define process.env.NODE_ENV:"development". Values are parsed as JSON.
-l, --loader Parse files with .ext:loader, e.g. --loader .js:jsx. Valid loaders: js, jsx, ts, tsx, json, toml, text, file, wasm, napi
--no-macros Disable macros from being executed in the bundler, transpiler and runtime
--jsx-factory Changes the function called when compiling JSX elements using the classic JSX runtime
--jsx-fragment Changes the function called when compiling JSX fragments
--jsx-import-source Declares the module specifier to be used for importing the jsx and jsxs factory functions. Default: "react"
--jsx-runtime "automatic" (default) or "classic"
--env-file Load environment variables from the specified file(s)
--cwd Absolute path to resolve files & entry points from. This just changes the process' cwd.
-c, --config Specify path to Bun config file. Default <d>$cwd<r>/bunfig.toml
-h, --help Display this menu and exit
Examples:
Run a JavaScript or TypeScript file
bun run ./index.js
bun run ./index.tsx
Run a package.json script
bun run dev
bun run lint
Full documentation is available at https://bun.sh/docs/cli/run
Thanks to @colinhacks and @paperdave.
Macros now support builtin modules
You can now import builtin modules at bundle-time using macros in Bun.
Read a file into a string at bundle-time
// file.ts
import { readFileSync } from "node:fs" with {type: "macro"};
export const contents = readFileSync("hello.txt", "utf8");
Build:
bun build --target=browser ./file.ts
Spawn a process at bundle-time
// spawn.ts
import { spawnSync } from "node:child_process" with {type: "macro"};
const result = spawnSync("echo", ["Hello, world!"], {encoding: "utf-8"}).stdout;
console.log(result); // "Hello, world!"
Build:
bun build --target=browser ./spawn.ts
mock.module(...)
now supports default exports
We fixed a bug preventing mock.module
from overriding default exports.
import { mock } from "bun:test";
mock.module("node:fs", () => ({
default: {
readFileSync() {
return "Hello, world!";
},
},
}));
import fs from "node:fs";
// Previously, this would have tried to read from the file system:
console.log(fs.readFileSync("hello.txt")); // "Hello, world!"
mock.module(...)
now supports re-exports
We fixed a bug preventing mock.module
from overriding re-exports.
// # c.test.ts
import { mock } from "bun:test";
mock.module("./a.ts", () => ({
exportedFromB: 123,
}));
import { exportedFromB } from "./a.ts";
// # a.ts
import { exportedFromB } from "./b.ts";
export { exportedFromB };
// # b.ts
export const exportedFromB = 456;
// Previously, this would have returned 456:
console.log(exportedFromB); // 123
For ESM, this updates live-bindings. For CommonJS modules, the changes will apply the next time the module is require
'd.
Fixed: transpiler preserves custom directives
Previously, if you had an unknown directive at the top of the file, Bun's transpiler would consider it dead code and remove it. This has been fixed.
"use client"; // <-- this line is preserved now
export const MyComponent = () => <div>Hello, world!</div>;
Fixed: Dirent.name
setter must be a string
Previously, if you tried to create an instance of the node:fs
module's Dirent
class via Object.create(Dirent)
, accessing the name
property would throw an error since it was not created via new
.
import { Dirent } from "node:fs";
const dirent = Object.create(Dirent);
dirent.name = "hello.txt";
console.log(dirent.name); // "hello.txt"
Some libraries depend on this behaviour, and it has been fixed.
Fixed: Assertion failures and crashes
We recently made changes to our version of JavaScriptCore to ensure that assertions are enabled in debug builds. This allowed us to fix a wide range of bugs that were previously causing crashes or assertion failures. This will also help make Bun more stable going forward.
Some of the issues fixed include:
- Potential crash with
process.mainModule
- Crash when constructing a
Worker
- Crash when calling
Server.close()
- Crash with
Bun.serve()
that could theoretically occur when an in-flight request is aborted - Assertion failure in
Headers.prototype.getAll
that theoretically could cause a crash - Assertion failure in
module.require
&&module.reoslve
that theoretically could cause a crash - Assertion failure in
ServerWebSocket.close
caused by incorrectly checking the types of arguments passed to the function - Assertion failure when rejecting a Promise that could cause a missing stacktrace
- Assertion failure in the
onerror
callback on the global object that could lead to a crash
Fixed: potential crash in require()
after exception is thrown
A crash that sometimes happened when a CommonJS module that was loaded via require()
and threw an exception during initialization while nested inside many other modules that were also incompletely initialized has been fixed.
One way this crash manifested was a module requiring sharp
when native addons were not installed and that module was nested inside many other modules that were also incompletely initialized.
What caused this?
The source code was garbage collected before the exception was thrown, and it was assumed that the source code could not be garbage collected at that point. We added the missing null check to fix this issue.
Fixed: ENOENT
when using bun install
+ package-lock.json
We fixed an edge-case in bun install
that would have caused a barrage of ENOENT
errors. This would have occured when no bun.lockb
existed, but a package-lock.json
existed.
Fixed: bun install
with a package that contains ~
We fixed a bug where bun install
would fail if a package contained a ~
in the version. This has been fixed, thanks to @Hanaasagi.
Fixed: Bun does not forward arguments to lifecycle scripts
We fixed a bug where Bun would pass arguments to lifecycle scripts. For example, if you ran bun run build --production
, Bun would pass --production
to the prebuild
script, which could cause unexpected failures.
{
"scripts": {
"prebuild": "echo \"Building...\"",
"build": "bun scripts/build.ts"
}
}
Before:
$ echo "Building..." --production
Building... --production
After:
$ echo "Building..."
Building...
Thanks to @nektro for fixing this issue.
Fixed: Hang in bun:sqlite
's .get() method
Previously, the following code would potentially hang after being called about 5,000 times in quick succession:
import { Database } from "bun:sqlite";
const db = new Database(":memory:");
const query = db.prepare("SELECT 1");
for (let i = 0; i < 10000; i++) {
query.get();
}
This has been fixed.
What caused this?
The prototype used for SQLite statements had an incorrect JSType
specified.
JavaScriptCore has an internal JSType
tag associated with each heap-allocated value. This tag is used in many places, including the JIT. The SQLite statement used a FunctionType
instead of an ObjectType
, which caused the DFG JIT to incorrectly optimize the .get()
method. This led to a hang after the function was executed enough times for the DFG JIT to optimize the function.
JavaScriptCore had an assertion that caught this bug, but we weren't running JavaScriptCore with assertions enabled in development. To catch these kinds of bugs in the future, we've enabled assertions in the debug build of JavaScriptCore (the engine) to be used for the debug build of Bun.
Fixed: Set-Cookie
header capitalization with headers.raw()
We fixed an issue where headers.raw()
from node-fetch
would return the Set-Cookie
header with the wrong capitalization.
Before:
import { Headers } from "node-fetch";
const headers = new Headers({
"User-Agent": "Bun",
"Set-Cookie": "foo=bar",
});
console.log(headers.raw());
// { "user-agent": "Bun", "Set-Cookie": ["foo=bar"] }
After:
import { Headers } from "node-fetch";
const headers = new Headers({
"Set-Cookie": "foo=bar",
});
console.log(headers.raw());
// { "user-agent": "Bun", "set-cookie": ["foo=bar"] }
Thanks to @cirospaciari for fixing this issue.
Fixed: AsyncLocalStorage
could not be disabled multiple times
There was a bug where AsyncLocalStorage
could not be disabled multiple times. This has been fixed.
Before:
import { AsyncLocalStorage } from "node:async_hooks";
const store = new AsyncLocalStorage();
store.enterWith(1);
store.disable();
console.log(store.getStore()); // undefined
store.enterWith(2);
store.disable();
console.log(store.getStore()); // 2
After:
import { AsyncLocalStorage } from "node:async_hooks";
const store = new AsyncLocalStorage();
store.enterWith(1);
store.disable();
console.log(store.getStore()); // undefined
store.enterWith(2);
store.disable();
console.log(store.getStore()); // undefined
Thanks to @cirospaciari for fixing this issue.
Fixed: fs.read()
returned incorrect value when promisified
We fixed a bug where fs.read()
would return an incorrect value when util.promisify(fs.read)
was used.
Before:
import fs from "node:fs";
import { promisify } from "node:util";
const read = promisify(fs.read);
const fd = await fs.promises.open("hello.txt");
const buffer = new Uint8Array(16);
const result = await read(fd, buffer, 0, 16, 0);
console.log(result); // 16
After:
import fs from "node:fs";
import { promisify } from "node:util";
const read = promisify(fs.read);
const fd = await fs.promises.open("hello.txt");
const buffer = new Uint8Array(16);
const result = await read(fd, buffer, 0, 16, 0);
console.log(result); // { bytesRead: 16, buffer }
Thanks to @nektro for fixing this issue.
Along the way, we also fixed:
fs.write()
when promisifiedfs.writev()
when promisifiedfs.readv()
when promisified
Fixed: bug when missing certain headers in HTTP client responses
For HTTP/1.1, the Content-Length
header and the Transfer-Encoding
headers are both optional. When these headers are omitted, the HTTP client is supposed to wait until the connection is closed to determine the length of the response body (and implicitly, disable keep-alive).
Previously, Bun was not handling this case and treating these HTTP responses as malformed.
Now, Bun properly handles this scenario and disables keep-alive when the Content-Length
& Transfer-Encoding
headers are omitted. It is not recommended to omit these headers (keep-alive is an important optimization for HTTP/1.1 and particularly TLS), but Bun now handles this case.
Fixed: bun install --yarn
would occasionally produce invalid YAML
There was a bug in Bun's yarn.lock file printer that would not properly escape identifiers in certain cases. This would cause bun install --yarn
to produce a yarn.lock file that Yarn v1 was unable to parse. Note that technically yarn.lock is not a YAML file, it is a bespoke Yarn v1 specific format (but saying "YAML" is easier since it is very close to YAML).
Fixed: Allow crypto.subtle
to be polyfilled
Previously, Bun would throw an error if crypto.subtle
was polyfilled. There are some packages that depend on this behaviour, and it has been fixed.
Fixed: bun init
would contain an invalid .gitignore
Ther was a bug where bun init
would contain an invalid .gitignore
file. This has been fixed thanks to @Pierre-Mike.
Fixed: crash in debugger
A crash could occur on start when using bun --inspect
to debug an application, which has been fixed.
The crash was due to assuming a debugger was started when it might not have been started yet.
Fixed: sometimes bun install would binlink the wrong version
In certain cases, bun install could have binlinked the wrong version of a package. We previously were not bin linking into my-dependency/node_modules/.bin
, preferring the root node_modules/.bin
instead. This has been fixed, thanks to @dylan-conway.
Miscellaneous
We upgraded to a later version of Zig.
Thanks to 19 contributors!
- @antongolub
- @bh1337x
- @brettgoulder
- @cdfzo
- @cirospaciari
- @colinhacks
- @dottedmag
- @dylan-conway
- @Electroid
- @Hanaasagi
- @jaas666
- @Jarred-Sumner
- @joseph082
- @lorenzodonadio
- @nektro
- @otgerrogla
- @paperdave
- @Pierre-Mike
- @rupurt
- @SeedyROM
You can also read the full changelog.