---
title: Bun v1.0.16
description: "Fixes 49 bugs (addressing 38 👍 reactions). Concurrent IO for Bun.file & Bun.write gets 3x faster and now supports Google Cloud Run & Vercel, 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"
date: "2023-12-10T13:28:21.915Z"
author: jarred
draft: false
---

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 &mdash; all in one. In case you missed it, here are some of the recent changes to Bun:

- [`v1.0.12`](/blog/bun-v1.0.12) - Adds `bun -e` for evaluating scripts, `bun --env-file` for loading environment variables, `server.url`, `import.meta.env`, `expect.unreachable()`, improved CLI help output, and more
- [`v1.0.13`](/blog/bun-v1.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 bug
- [`v1.0.14`](/blog/bun-v1.0.14) - `Bun.Glob`, a fast API for matching files and strings using glob patterns. It also fixes a race condition when extracting dependencies during `bun install`, improves TypeScript module resolution in `node_modules`, and makes error messages easier to read.
- [`v1.0.15`](/blog/bun-v1.0.15) - Fixes 23 bugs (addressing 117 👍 reactions), `tsc` starts 2x faster. Stable `WebSocket` client, syntax-highlighted errors, cleaner stack traces, add custom test matchers with `expect.extend()` + additional expect matchers.

To install Bun:

{% codetabs %}

```sh#curl
$ curl -fsSL https://bun.sh/install | bash
```

```sh#npm
$ npm install -g bun
```

```sh#brew
$ brew tap oven-sh/bun
$ brew install bun
```

```sh#docker
$ docker pull oven/bun
$ docker run --rm --init --ulimit memlock=-1:-1 oven/bun
```

{% /codetabs %}

To upgrade Bun:

```sh
$ 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.

```ts
// 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.

```shell
$ bun script.ts
```

Previously, it would throw an error:

```js
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`.

```ts
// 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.

```shell
$ bun script.ts
```

```js
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
```

{% raw %}

<blockquote class="twitter-tweet"><p lang="en" dir="ltr">In the next version of Bun<br><br>Bun.write() will automatically create parent directories if they do not exist <a href="https://t.co/jNgjZ5LAtl">pic.twitter.com/jNgjZ5LAtl</a></p>&mdash; Jarred Sumner (@jarredsumner) <a href="https://twitter.com/jarredsumner/status/1733680612819804494?ref_src=twsrc%5Etfw">December 10, 2023</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

<blockquote class="twitter-tweet"><p lang="en" dir="ltr">You run<br><br>await Bun.write(“/dir-doesnt-exist/file.txt”, “abc”) <br><br>Do you expect the function to throw because “dir-doesn’t-exist” doesn’t exist?</p>&mdash; Bun (@bunjavascript) <a href="https://twitter.com/bunjavascript/status/1732710459583897674?ref_src=twsrc%5Etfw">December 7, 2023</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

{% /raw %}

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

![](https://github.com/oven-sh/bun/assets/24465214/29076f9f-adff-42d0-9948-dc35b08b7036)

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:

```js
❯ 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:

```ts
❯ 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:

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

```ts
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.

```ts
import { stdin } from "bun";
console.log(await stdin.text());
```

```shell
$ echo "Hello, world!" | bun script.ts
```

Previously, sometimes it would print nothing:

```sh

```

Now, it prints the full input:

```sh
Hello, world!
```

## `napi_create_object` gets 2.5x faster

{% raw %}

<blockquote class="twitter-tweet"><p lang="en" dir="ltr">in the next version of Bun<br><br>napi_create_object gets 2.5x faster <a href="https://t.co/lXyj6koceT">pic.twitter.com/lXyj6koceT</a></p>&mdash; Jarred Sumner (@jarredsumner) <a href="https://twitter.com/jarredsumner/status/1731836157606940851?ref_src=twsrc%5Etfw">December 5, 2023</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
{% /raw %}

## `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.

```ts#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,

```toml#bunfig.toml
test.preload = "./custom-matchers.ts"
```

and run `bun test`:

```ts#example.test.ts
import { expect, test } from "bun:test";

test("example", () => {
  expect(1234).toBeWithinRange(1000, 2000);
});
```

```diff
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.

```diff
-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.

```json
{
  "name": "my-package",
  "imports": {
    "my-package": "./dist/index.js"
  }
}
```

These imports are prefixed with `#`.

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

```json
{
  "name": "my-package",
  "imports": {
    "async-hooks": "async-hooks"
  }
}
```

Then, it will load the `async-hooks` package:

```js
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.

```js
console.log([1, , 3]);
```

Now:

```js
[ 1, empty item, 3 ]
```

Previously:

```js
[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.

```js
import { createRequire } from "node:module";
const require = createRequire(import.meta.url);
require("file:///Users/jarred/My%20Folder/script.js");
```

Thanks to [@paperclover](https://github.com/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.

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

```jsx
const obj = {};
console.log(<div {...obj} key="after" />, <div key="before" {...obj} />);
```

This has been fixed, thanks to [@rhyzx](https://github.com/rhyzx).

## Fixed: `console.log("%d", value)` bug

A bug causing `%d` to not handle non-numeric values correctly has been fixed.

```js
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](https://github.com/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](https://github.com/@mkayander) made their first contribution in [#7453](https://github.com/oven-sh/bun/pull/7453)
- [@asilvas](https://github.com/@asilvas) made their first contribution in [#7446](https://github.com/oven-sh/bun/pull/7446)
- [@annervisser](https://github.com/@annervisser) made their first contribution in [#7289](https://github.com/oven-sh/bun/pull/7289)
- [@sstephant](https://github.com/@sstephant) made their first contribution in [#7477](https://github.com/oven-sh/bun/pull/7477)
- [@DaleSeo](https://github.com/@DaleSeo) made their first contribution in [#7532](https://github.com/oven-sh/bun/pull/7532)
- [@bradymadden97](https://github.com/@bradymadden97) made their first contribution in [#7529](https://github.com/oven-sh/bun/pull/7529)
- [@dfaio](https://github.com/@dfaio) made their first contribution in [#7542](https://github.com/oven-sh/bun/pull/7542)

Thanks to the 16 contributors who made this release possible:

- [@annervisser](https://github.com/annervisser)
- [@asilvas](https://github.com/asilvas)
- [@bradymadden97](https://github.com/bradymadden97)
- [@DaleSeo](https://github.com/DaleSeo)
- [@dfaio](https://github.com/dfaio)
- [@dylan-conway](https://github.com/dylan)
- [@Hanaasagi](https://github.com/Hanaasagi)
- [@ImBIOS](https://github.com/ImBIOS)
- [@Jarred-Sumner](https://github.com/Jarred)
- [@jerome-benoit](https://github.com/jerome)
- [@mkayander](https://github.com/mkayander)
- [@otgerrogla](https://github.com/otgerrogla)
- [@paperclover](https://github.com/paperclover)
- [@rhyzx](https://github.com/rhyzx)
- [@RiskyMH](https://github.com/RiskyMH)
- [@sirenkovladd](https://github.com/sirenkovladd)
- [@sstephant](https://github.com/sstephant)
