Bun

Bun v0.6.6


Ashcon Partovi · May 31, 2023

We're hiring C/C++ and Zig engineers to build the future of JavaScript! Join our team →

We've been releasing a lot of changes to Bun recently, here's a recap in case you missed it:

  • v0.6.0 - Introducing bun build, Bun's new JavaScript bundler.
  • v0.6.2 - Performance boosts: 20% faster JSON.parse, up to 2x faster Proxy and arguments.
  • v0.6.3 - Implemented node:vm, lots of fixes to node:http and node:tls.
  • v0.6.4 - Implemented require.cache, process.env.TZ, and 80% faster bun test.
  • v0.6.5 - Native support for CommonJS modules (previously, Bun did CJS to ESM transpilation), Vue support

Now, we're releasing Bun v0.6.6, with significant improvements to bun test, including Github Actions support, support for test.only(), test.if(), describe.skip(), and 15+ more expect() matchers; also streaming file uploads using fetch().

To install Bun:

curl
npm
brew
docker
curl
curl -fsSL https://bun.sh/install | bash
npm
npm install -g bun
brew
brew tap oven-sh/bun
brew install bun
docker
docker pull oven/bun
docker run --rm --init --ulimit memlock=-1:-1 oven/bun

To upgrade Bun:

bun upgrade

bun test in Github Actions

Bun will now detect if bun test is running in a Github Action.

First, it will collapse tests by file in the logs output to make it easier to read.

bun test in github action logs

Next, it will annotate errors with its name, message, and stack trace.

bun test in github annotations

You also don't need to install any plugins or extensions for this to work. Bun will read the GITHUB_ACTIONS environment variable and check if it's set to true.

bun test --only

You can now specify --only to only run tests that are marked with test.only() or describe.only(). This is useful when you want to debug a specific test in a file.

import { test, describe } from "bun:test";

describe.only("describe #1", () => {
  test("test #1", () => {
    // runs
  });
  test.skip("test #2", () => {
    // does not run
  });
});

test("test #3", () => {
  // does not run
});

test.only("test #4", () => {
  // runs
});

bun test --todo

In addition to test.skip(), you can also define a test using test.todo() or describe.todo(). By default, these tests are not run, unless you specify --todo. This is useful for tests that are not yet implemented or are broken or flaky in some way.

import { test } from "bun:test";

test("test #1", () => {
  // runs
});

test.todo("test #2"); // never runs

test.todo("test #3", () => {
  // only runs if --todo is passed
});

test.if() and describe.if()

You can now specify a conditional test, based on a boolean value. You can also use test.skipIf() if you want the inverse behaviour.

So instead of doing this:

import { it } from "bun:test";

const macOS = process.arch === "darwin";
const test = macOS ? test : test.skip;

You can just do this:

import { test, describe } from "bun:test";

const macOS = process.arch === "darwin";

test.if(macOS)("test #1", () => {
  // runs if macOS
});

test.skipIf(macOS)("test #2", () => {
  // skips if macOS
});

describe.if(macOS)("describe #1", () => {
  test("test #3", () => {
    // runs if macOS
  });
});

Timeouts in test()

In Jest, you can specify a third argument to test() as a timeout value in milliseconds. In Vitest, you can also specify an object, with a timeout property. In Bun, you can now do both.

import { test } from "bun:test";
import { sleep } from "bun";

test("timeout #1", async () => {
  await sleep(10);
}, 1);

test(
  "timeout #2",
  async () => {
    await sleep(10);
  },
  { timeout: 1 },
);

The default timeout is 5000, you can change the default by using --timeout.

bun test --timeout 1000 # 1 second

New matchers for expect()

We've added a lot of new matchers to expect(), with more coming soon. Most of these are supported by the jest-extended matchers.

import { test, expect } from "bun:test";

test("new matchers", () => {
  expect(null).toBeNil();
  expect(true).toBeBoolean();
  expect(3.14).toBePositive();
  expect(314).toBeInteger();
  expect(3.1).toBeWithin(3, 3.14);
  expect(() => {}).toBeFunction();
  expect(new Date()).toBeDate();
  expect("bunny").toInclude("bun");
  expect("buntime").toStartWith("bun");
});

Streaming fetch() file uploads

You can now do a streaming upload a file using fetch() in Bun

import { file } from "bun";

await fetch("https://example.com/", {
  method: "POST",
  body: file("avatar.png"),
});

You can also do this with a FormData upload.

import { file } from "bun";

const formData = new FormData();
formData.set("attachment", file("data.csv"));

await fetch("https://example.com/", {
  method: "POST",
  body: formData,
});

When a large file is sent over HTTP, Bun automatically makes it faster using the sendfile() system call.

Bug fixes

We also made various bug fixes.

Regressions in 0.6.5 from the CommonJS rewrite:

  • --watch and --hot were broken when importing CommonJS modules, this is now fixed.
  • Fixed a bug with Object.defineProperty(module, "exports", { get }), which fixes packages like supports-color.

More bugfixes:

  • A bundler transform would incorrectly rewrite module.exports to exports when used in UMD modules, this is now fixed.
  • Fixed a bug with path.parse() that did not resolve certain paths properly, thanks to @cirospaciari.
  • Fixed a crash with expect(null).toHaveProperty().
  • Fixed a crash that could occur when an error is thrown in Bun.listen() or Bun.connect() callbacks (or node:net or node:tls)
  • Fixed a bug where Bun.spawn() would attempt to chdir to an empty string
  • Fixed a mistranspilation involving optional chaining with computed properties in multiple levels of nesting thanks to @dylan-conway

More things:

  • Added the --no-macros flag and disabled macros in the node_modules folder
  • Added the macro package.json export condition