Bun

Bun v0.6.3


Ashcon Partovi · May 22, 2023

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

Last week, we launched our new JavaScript bundler in Bun v0.6.0.

Today, we're releasing support for node:vm, improvements to node:tls and node:http — which fixes support for socket.io and mongodb, improvements to bun test — which introduces test.todo(), test timeouts, and better preloading, and many fixes to Bun's bundler.

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

Introducing node:vm

Bun now has support for the built-in node:vm module. It can be used to execute code, similar to eval(), except with more control over which globalThis JavaScript context the code is executed.

image

import { runInContext } from "node:vm";

let context = {
  foo: "bar",
  baz: 123,
};

runInContext(`foo = "fizz"; delete baz;`, context);

console.log(context.foo); // "fizz"
console.log(context.baz); // undefined

You can also use runInNewContext to run code in a separate JavaScript context.

import { runInNewContext } from "node:vm";

runInNewContext(`globalThis.fetch = undefined;`);

console.log(globalThis.fetch); // [Function fetch]

Thanks to @silversquirl for implementing this feature!

Fixes for node:tls and node:http

We've fixed a lot of code with TLS and HTTP, such as TLS handshaking, so you can now use socket.io and mongodb authentication in Bun.

Thanks to @cirospaciari for working on this!

Improvments to bun test

test.todo()

You can now use test.todo() to mark tests that you want to implement later.

image

expect().toBeCloseTo()

You can also use expect().toBeCloseTo() to compare two floating-point numbers.

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

test("toBeCloseTo()", () => {
  expect(3 + 0.14).toBeCloseTo(3.14);
  expect(3.14).toBeCloseTo(Math.PI, 2);
});

Thanks to @blackmann for working on both of these features!

Test timeouts

You can now set a timeout for your tests with a third argument to test().

import { test } from "bun:test";

test("i took too long", async () => {
  await Bun.sleep(100);
}, 50); // the last argument is a timeout in milliseconds

image

Hook support with --preload

You can now use beforeAll, beforeEach, afterEach, and afterAll with the --preload flag.

This lets you run code before and after all files instead of just the current file. This is useful for libraries that need to be initialized before running tests.

import { test, beforeAll, beforeEach, afterEach, afterAll } from "bun:test";

beforeEach(() => {
  console.log("This runs before each test in every file");
});

Fixes to pretty-printing

We also fixed bug that would cause code like this to print values as undefined in the test runner.

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

test("wat", () => {
  const left = {};
  const right = {};
  for (let i = 0; i < 2; i++) {
    left[i] = i + 1;
    right[i] = i + 2;
  }
  expect(left).toBe(right);
});
BeforeAfter
image
image

Fixes to bun build

  • A crash that was highlighted in Using Bun.js as a bundler when --minify with multiple entry points was enabled has been fixed. It was a race condition when merging adjacent top-level variable declarations.

  • A bug that caused assets to copy to an incorrect output path has been fixed.

  • A minifier bug involving template literals being incorrectly merged has been fixed.

  • A race condition that could occur when generating sourcemaps has been fixed.

  • A bug that allowed generated variable names to start with numbers has been fixed.

  • A crash that could occur when creating the BuildArtifact objects after saving to disk has been fixed.

  • A memory leak in the logs has been fixed.

  • Improved an error message when using a Node.js built-in when building for browsers to suggest g the --target option to node or bun.

fetch() memory leak

Two memory leaks found in fetch() have been fixed.

  1. The callback was creating two strongly-held references to the Promise<Response> object, which prevented it from being garbage collected.
  2. The input validation logic would potentially create a strongly held Promise<Response> and immediately discarded it, without releasing the strong reference. This prevented the Promise<Response> from being garbage collected.

This was embarrassing and we've improved our test coverage for memory leak tests to make sure this doesn't happen again. Here was graph of the memory leak, courtesy of @dimka3553:

Improvements to console.log()

Mismatched quotes

Previously, console.log() would print mismatched quotes for non-ascii property names. This is now fixed.

{
  '🐛 Bug with mismatched quotes": 'fixed'
}

Set depth limit

Previously, console.log() would print an infinite amount of nested objects, excluding circular references. This was sometimes undesirable and caused crashes when printing sufficiently large objects. This has been fixed with a depth limit of 8.

Remove unnecessary quote identifiers

Previously, Bun would unnecessarily quote object keys in console.log and this was a little noisy. After a Twitter poll voted 90% in favor of removing the quotes, we did it.

Changes to WebSocket

Breaking change to publishToSelf behaviour

Bun supports publish/subscribe in its server-side WebSocket APIs. You can use Server.publish() or ServerWebSocket.publish() to send a message to each connected client.

However, we made a design mistake with the ServerWebSocket.publish() API, as it also sent a message to itself — when the typical behaviour is to exclude it. We patched this mistake by introducing an optional publishToSelf option, but the default behaviour was still confusing, as highlighted by various issues.

So we're relunctantly fixing this behaviour, with a breaking change, to match developers' intuition. We rarely make these types of decisions, but when we do, we make sure there is overwhelming evidence and feedback that maintaing the status-quo is more harmful than any potential breakage.

const server = Bun.serve({
  websocket: {
    open(ws) {
      ws.subscribe("topic");
      // <= Bun v0.6.2
      ws.publish("topic", "send to all clients, including `ws`");
      // >= Bun v0.6.3
      ws.publish("topic", "send to all clients, excluding `ws`");
    },
  },
  fetch(request, server) {
    if (server.upgrade(request)) {
      return;
    }
    return new Response();
  },
});

Buffer support in WebSocket messages

Many npm packages expect Buffer objects as WebSocket messages. Instead of asking you to re-create the Buffer object yourself when passing through to a library, Bun now lets you directly receive a "nodebuffer" value for binary WebSocket messages.

const server = Bun.serve({
  websocket: {
    open(ws) {
      ws.binaryType = "nodebuffer";
      ws.send(Buffer.from("hello world"));
    },
    message(ws, message) {
      console.log(Buffer.isBuffer(message)); // true
    },
  },
  fetch(request, server) {
    if (server.upgrade(request)) {
      return;
    }
    return new Response("hello world");
  },
});

const client = new WebSocket("ws://localhost:3000");
client.binaryType = "nodebuffer";
client.onmessage = ({ data }) => {
  console.log(Buffer.isBuffer(data)); // true
};

More bug fixes

  • fs.writeFile({ flag: "a" }) now appends to files instead of overwriting it
  • N-API finalizers are now called with the correct data pointer and finalizer hint
  • A crash that could occur when creating many Error objects inside a bound function has been fixed, thanks to @Constellation
  • Setting megamorphic property values gets faster in this release, thanks to @Constellation

Changelog

2544742Fixed memory leak with BuildError and ResolveError by @Jarred-Sumner
4f7198fFixed incorrect behaviour with fs.writeFile({ flag: "a" }) by @Jarred-Sumner
aacbef3Improved error message when node built-in could not be resolved by @Jarred-Sumner
#2939Fixed incorrect encoding of utf8 property names by @Jarred-Sumner
#2937Fixed bug with incorrect String.raw output by @dylan-conway
#2870Implemented expect().toBeCloseTo() by @blackmann
#2947Fixed crash when bundling with multiple entrypoints by @Jarred-Sumner
#2949Fixed "Numeric separators are not allowed at the end of numeric literals" by @Jarred-Sumner
5bec025Fixed issue where the cwd is not writable during bundling by @Jarred-Sumner
b76974aFixed bug where IncomingMessage.socket was not writable by @Jarred-Sumner
#2785Implemented node:vm by @silversquirl
#2962Improved node-fetch polyfill to include more exports by @Jarred-Sumner