Bun v1.0.16 fixes 49 bugs (addressing 38 👍 reactions). Rewritten IO for Bun.file, Bun.write auto-creates the parent directory if it doesn't exist, expect.extend inside of preload works, napi_create_object gets 2.5x faster, bugfix for module resolution impacting Astro v4 and p-limit, console.log bugfixes, and more.
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.12- Addsbun -efor evaluating scripts,bun --env-filefor loading environment variables,server.url,import.meta.env,expect.unreachable(), improved CLI help output, and morev1.0.13- Fixes 6 bugs (addressing 317 👍 reactions). 'http2' module & gRPC.js work now. Vite 5 & Rollup 4 work. Implements process.report.getReport(), improves support for ES5 'with' statements, fixes a regression in bun install, fixes a crash when printing exceptions, fixes a Bun.spawn bug, and fixes a peer dependencies bugv1.0.14-Bun.Glob, a fast API for matching files and strings using glob patterns. It also fixes a race condition when extracting dependencies duringbun install, improves TypeScript module resolution innode_modules, and makes error messages easier to read.v1.0.15- Fixes 23 bugs (addressing 117 👍 reactions),tscstarts 2x faster. StableWebSocketclient, syntax-highlighted errors, cleaner stack traces, add custom test matchers withexpect.extend()+ additional expect matchers.
To install Bun:
curl
curl -fsSL https://bun.sh/install | bashnpm
npm install -g bunbrew
brew tap oven-sh/bunbrew install bundocker
docker pull oven/bundocker run --rm --init --ulimit memlock=-1:-1 oven/bunTo upgrade Bun:
bun upgradeBun.write now creates the parent directory if it doesn't exist#
Previously, Bun.write would throw an error if the directory didn't exist. Now, it will create the directory if it doesn't exist.
// script.ts
import { write } from "bun";
await write("i/dont/exist.txt", "Hello, world!");Now, i/dont will be created if it doesn't exist.
bun script.tsPreviously, it would throw an error:
1 | import { write } from "bun";
2 | await write("i/dont/exist.txt", "heyyy");
^
ENOENT: No such file or directory
path: "i/dont/exist.txt"
syscall: "open"
errno: -2To throw an error when the parent directory doesn't exist: createPath: false#
This behavior can be disabled by passing createPath: false to Bun.write.
// script.ts
import { write } from "bun";
await write("i/dont/exist.txt", "Hello, world!", { createPath: false });Now, it will throw an error if the directory doesn't exist.
bun script.ts1 | import { write } from "bun";
2 | await write("i/dont/exist.txt", "heyyy", { createPath: false });
^
ENOENT: No such file or directory
path: "i/dont/exist.txt"
syscall: "open"
errno: -2In the next version of Bun
— Jarred Sumner (@jarredsumner) December 10, 2023
Bun.write() will automatically create parent directories if they do not exist pic.twitter.com/jNgjZ5LAtl
You run
— Bun (@bunjavascript) December 7, 2023
await Bun.write(“/dir-doesnt-exist/file.txt”, “abc”)
Do you expect the function to throw because “dir-doesn’t-exist” doesn’t exist?
Rewritten IO for Bun.file#
The internals of Bun.file on Linux have been rewritten to use epoll instead of io_uring, which improves compatibility in Docker and cloud hosts like Vercel and Google Cloud Run.
This addresses 11 issues related to EBADF when using Bun.file:
The impetus for this change was Docker disabled io_uring by default in November 2023.
Bun.write() and Bun.file().text() gets 3x faster under concurrent load#
Along the way, we improved the performance of reading and writing files concurrently using Bun.file() and Bun.write().
Reading:
❯ N=100 hyperfine "bun ./read.js" "bun-1.0.15 read.js" --warmup=20
Benchmark 1: bun ./read.js
Time (mean ± σ): 40.9 ms ± 2.9 ms [User: 27.6 ms, System: 181.7 ms]
Range (min … max): 37.1 ms … 48.3 ms 73 runs
Benchmark 2: bun-1.0.15 read.js
Time (mean ± σ): 134.5 ms ± 19.0 ms [User: 24.8 ms, System: 82.5 ms]
Range (min … max): 98.8 ms … 161.2 ms 23 runs
Summary
bun ./read.js ran
3.29 ± 0.52 times faster than bun-1.0.15 read.jsWriting:
❯ N=100 hyperfine "bun ./write.js" "bun-1.0.15 write.js" --warmup=20
Benchmark 1: bun ./write.js
Time (mean ± σ): 35.0 ms ± 8.0 ms [User: 13.0 ms, System: 364.6 ms]
Range (min … max): 25.7 ms … 71.5 ms 83 runs
Benchmark 2: bun-1.0.15 write.js
Time (mean ± σ): 134.7 ms ± 13.6 ms [User: 23.6 ms, System: 193.3 ms]
Range (min … max): 117.8 ms … 171.2 ms 23 runs
Summary
bun ./write.js ran
3.85 ± 0.96 times faster than bun-1.0.15 write.jsread.js:
const promises = new Array(parseInt(process.env.N || "20"));
for (let i = 0; i < promises.length; i++) {
promises[i] = Bun.file("out.log." + i).text();
}
await Promise.all(promises);write.js:
const toPipe = new Blob(["abc".repeat(1_000_000)]);
const promises = new Array(parseInt(process.env.N || "20"));
for (let i = 0; i < promises.length; i++) {
promises[i] = Bun.write("out.log." + i, toPipe);
}
await Promise.all(promises);Fixed: Bun.stdin.text() incomplete reads#
Previously, Bun.stdin.text() would sometimes not return all of the data from stdin.
import { stdin } from "bun";
console.log(await stdin.text());echo "Hello, world!" | bun script.tsPreviously, sometimes it would print nothing:
Now, it prints the full input:
Hello, world!napi_create_object gets 2.5x faster#
in the next version of Bun
— Jarred Sumner (@jarredsumner) December 5, 2023
napi_create_object gets 2.5x faster pic.twitter.com/lXyj6koceT
expect and expect.extend in --preload#
Previously, expect was scoped to the indivdual test file, which would break expect.extend if it was called in a shared file. This is now fixed, and you can now use a shared file containing your custom matchers such as.
custom-matchers.ts
import { expect } from "bun:test";
// Based off of from Jest's example documentation
// https://jestjs.io/docs/expect#expectextendmatchers
function toBeWithinRange(actual, floor, ceiling) {
const pass = actual >= floor && actual <= ceiling;
return {
pass,
message: () =>
"expected " +
this.utils.printReceived(actual) +
" to " +
(pass ? "not " : "") +
"be within range " +
this.utils.printExpected(`${floor} - ${ceiling}`),
};
}
expect.extend({
toBeWithinRange,
});Then configure bunfig.toml to load them during testing,
bunfig.toml
test.preload = "./custom-matchers.ts"and run bun test:
example.test.ts
import { expect, test } from "bun:test";
test("example", () => {
expect(1234).toBeWithinRange(1000, 2000);
});example.test.ts:
-TypeError: expect.extend is not a function. (In 'expect.extend({
- toBeWithinRange
-})', 'expect.extend' is undefined)
- at /custom-matchers.ts:21:1
+✓ example [1.31ms]You can also use expect() outside of test callbacks.
-error: This function can only be used in a test.
- at /custom-matchers.ts:21:1Fixed: module resolution bug with "imports" in package.json#
Packages can declare a list of internal-only imports in their package.json file, which Bun will use to resolve imports. This is useful for avoiding exposing internal files to users.
{
"name": "my-package",
"imports": {
"my-package": "./dist/index.js"
}
}These imports are prefixed with #.
import { foo } from "#my-package";This can also be used to rewrite imports from other packages. For example, if you wanted to rewrite all imports from async-hooks to #async-hooks, you could do:
{
"name": "my-package",
"imports": {
"async-hooks": "async-hooks"
}
}Then, it will load the async-hooks package:
import { foo } from "#async-hooks";Except, there was a bug that caused aliasing packages this way to not work in Bun. This has been fixed.
Notable packages this bug impacted:
- Astro v4
p-limit
Fixed: holey arrays appearing as undefined in console.log#
Previously, holey arrays would appear as undefined in console.log. This has been fixed.
console.log([1, , 3]);Now:
[ 1, empty item, 3 ]Previously:
[1, undefined, 3];Fixed: file: URLs in createRequire() that contain spaces#
Previously, createRequire() would throw an error when passed a file: URL which contained an unescaped space. This has been fixed.
import { createRequire } from "node:module";
const require = createRequire(import.meta.url);
require("file:///Users/jarred/My%20Folder/script.js");Thanks to @paperclover for fixing this.
Fixed: console.log was omiting the length property sometimes#
Sometimes, console.log would omit the length property of an object. This has been fixed.
console.log({ length: 1, a: 1 });Previously, it would print:
{
a: 1
}Now, it prints:
{
length: 1,
a: 1,
}Fixed: transpiler inconsistentcy with jsx key after spread#
The following input would behave differently in Bun versus in TypeScript:
const obj = {};
console.log(<div {...obj} key="after" />, <div key="before" {...obj} />);This has been fixed, thanks to @rhyzx.
Fixed: console.log("%d", value) bug#
A bug causing %d to not handle non-numeric values correctly has been fixed.
console.log("%d", "1");Previously, it would print:
0Now, it prints:
1Fixed: Syntax highlighter template string handling#
In the previous version of Bun, we added syntax-highlighting to the code shown in error messages, but it has a bug that caused some template strings to crash the highlighter. This has been fixed.
Fixed: jest.fn(jest.fn()) crash#
A crash when calling jest.fn(jest.fn(() => {})) has been fixed, thanks to @Hanassagi.
Thanks to 16 contributors!#
We're always excited to welcome new contributors to Bun. 6 new contributors made their first contribution in this release.
- @mkayander made their first contribution in #7453
- @asilvas made their first contribution in #7446
- @annervisser made their first contribution in #7289
- @sstephant made their first contribution in #7477
- @DaleSeo made their first contribution in #7532
- @bradymadden97 made their first contribution in #7529
- @dfaio made their first contribution in #7542
Thanks to the 16 contributors who made this release possible: