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 -e
for evaluating scripts,bun --env-file
for 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),tsc
starts 2x faster. StableWebSocket
client, syntax-highlighted errors, cleaner stack traces, add custom test matchers withexpect.extend()
+ additional expect matchers.
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.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.ts
Previously, 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: -2
To 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.ts
1 | 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: -2
In 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.js
Writing:
❯ 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.js
read.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.ts
Previously, 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.
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,
test.preload = "./custom-matchers.ts"
and run bun test
:
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:1
Fixed: 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 @paperdave 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:
0
Now, it prints:
1
Fixed: 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: