Bun

Bun v1.1.37


Dylan Conway · November 26, 2024

We're hiring systems engineers in San Francisco to build the future of JavaScript!

This release fixes 62 bugs (addressing 109 👍). It introduces realtime error reporting & test runner in VSCode, self-referencing import & require, package.json config environment variables, improved CSS parser errors, fixes for a Prisma memory leak, a bun install regression, expect.toMatchSnapshot quote escaping, node:net regression impacting http2-wrapper, and several other bugfixes and improvements.

To install Bun

curl
npm
powershell
scoop
brew
docker
curl
curl -fsSL https://bun.sh/install | bash
npm
npm install -g bun
powershell
powershell -c "irm bun.sh/install.ps1|iex"
scoop
scoop install 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

Realtime debuggerless error reporting in VSCode

Bun for VSCode now reports runtime errors with their stack trace in the "Problems" tab when you run bun in the VSCode terminal. This works without the debugger attached, with no configuration, and no need to pause execution. There's nothing extra to configure, it just works.

To get started:

  1. Install Bun for VSCode
  2. Open a terminal in VSCode
  3. Run bun test --watch, bun run foo.ts, or any other script
  4. Watch errors reported in the "Problems" tab

How is this different from tools like TypeScript, ESLint, and Biome?

Runtime errors have no false positives, but they only appear when you run the code.

Runtime errors only appear when the error prevents further execution, such as an uncaught exception, a rejected promise, or a test failure that halted execution. You'll usually only get 1 error in total, unlike static analysis tools that report many errors without running any code.

Can I still use TypeScript, ESLint, or Biome?

Yes! This works perfectly alongside static analysis tools like TypeScript, ESLint, or Biome. It doesn't compete or replace them.

This reports errors that static analysis tools can't catch:

  • Filesystem errors like ENOENT
  • Syntax errors from parsing invalid JSON
  • Exceptions like TypeError and RangeError

On the other hand, this will only report one error at a time. It doesn't come from reading your code, it comes from running your code.

Big thanks to @alii for implementing this.

Self-referencing import & require

Bun v1.1.37 adds support for self-referencing imports within a module. This allows modules to import themselves by name through the exports field in their package.json.

Given the package.json and foo.js for module foo:

./package.json
./foo.js
./package.json
{
    "name": "foo",
    "exports": {
        ".": "./foo.js"
    }
}
./foo.js
import * as foo from 'foo';
export const bar = foo;

The following code in index.js will print true:

./index.js
import * as foo from 'foo';
console.log(foo.bar === foo); // true

Implemented thanks to @SunsetTechuila!

Memory leak impacting Prisma fixed

napi_threadsafe_function is a structure that can be used by other threads to enqueue requests to call a function on the main JavaScript thread. This is necessary since JavaScript is single-threaded, and you can't access the JavaScript engine from any other thread. Previous versions of Bun never freed the queue used to keep track of pending calls to the threadsafe function, which manifested as an 8-byte memory leak for each Prisma query since every query creates a threadsafe function with a queue size of 1. We expect this change may fix memory leaks in other Node-API packages too, but Prisma was the most widely-reported leak.

Crash in shadcn & sv on Windows fixed

A crash on Windows, commonly seen in cli packages like shadcn, sv, prompts, solidui-cli, and create-astro, has been fixed. The bug existed in node:readline setRawMode in C++ where a type was incorrectly casted to another.

Fixed thanks to @heimskr!

npm_package_config_ environment variables using bun run

We've added support for populating npm_package_config_ environment variables from the config field in package.json.

Given the package.json:

package.json
{
    "name": "env",
    "config": {
        "foo": "bar",
    },
    "scripts": {
        "echo": "echo $npm_package_config_foo"
    }
}

Running bun run echo will print bar.

We've also added npm_lifecycle_event for script names, npm_lifecycle_script for script values, and npm_command variables for bun run, bun publish, and bun pm pack.

bun run:      npm_command=run-script
bun publish:  npm_command=publish
bun pm pack:  npm_command=pack

Thanks to @nektro!

Improved CSS parser error messages

We've changed the error messages in our CSS parser to be more descriptive and helpful. For example, declaring a rule before @import produces:

sh
red.css
bun build --experimental-css red.css

error: @import rules must come before any other rules except @charset and @layer
    at /path/to/red.css:5:8
red.css
div {
    color: red;
}

@import url('blue.css');

This and many other errors have been improved in this release. A snippet of the changes:

src/css/error.zig
return switch (this) {
-    .at_rule_invalid => |name| writer.print("at_rule_invalid: {s}", .{name}),
-    .unexpected_token => |token| writer.print("unexpected_token: {}", .{token}),
-    .selector_error => |err| writer.print("selector_error: {}", .{err}),
-    else => writer.print("{s}", .{@tagName(this)}),
+    .at_rule_body_invalid => writer.writeAll("Invalid at-rule body"),
+    .at_rule_prelude_invalid => writer.writeAll("Invalid at-rule prelude"),
+    .at_rule_invalid => |name| writer.print("Unknown at-rule @{s}", .{name}),
+    .end_of_input => writer.writeAll("Unexpected end of input"),
+    .invalid_declaration => writer.writeAll("Invalid declaration"),
+    .invalid_media_query => writer.writeAll("Invalid media query"),
+    .invalid_nesting => writer.writeAll("Invalid CSS nesting"),
+    .deprecated_nest_rule => writer.writeAll("The @nest rule is deprecated, use standard CSS nesting instead"),
+    .invalid_page_selector => writer.writeAll("Invalid @page selector"),
+    .invalid_value => writer.writeAll("Invalid value"),
+    .qualified_rule_invalid => writer.writeAll("Invalid qualified rule"),
+    .selector_error => |err| writer.print("Invalid selector. {s}", .{err}),
+    .unexpected_import_rule => writer.writeAll("@import rules must come before any other rules except @charset and @layer"),
+    .unexpected_namespace_rule => writer.writeAll("@namespace rules must come before any other rules except @charset, @import, and @layer"),
+    .unexpected_token => |token| writer.print("Unexpected token. {}", .{token}),
+    .maximum_nesting_depth => writer.writeAll("Maximum CSS nesting depth exceeded"),
};

Thanks to @zackradisic!

Improved Bun.write error messages

Bun.write now prints the received value when passed an unexpected argument. For example, passing a BigInt as the first argument will print:

bun --print "Bun.write(1n, 'hello stdout')"

TypeError: The "destination" argument must be of type path, file descriptor, or Blob. Received 1n
 code: "ERR_INVALID_ARG_TYPE"

      at /home/[eval]:1:5

Bun v1.1.37 (macOS arm64)

Thanks to @nektro!

performance.markResourceTiming stub

We've stubbed performance.markResourceTiming to unblock packages that rely on it. While the function will only return undefined, it will no longer throw runtime errors in packages like @sveltejs/adapter-cloudflare.

Bun.file throws OOM errors if the file is too large on Windows

Given a file larger than 4GB, Bun.file will now throw an OOM error on Windows. Previously, it would crash with an assertion failure. Fixed thanks to @cirospaciari!

index.js
const hugeFile = Bun.file('huge-file.txt'); // throws ENOMEM
console.log(hugeFile.size);

Fixed regression in bun install

Part of the work towards reducing the binary size of Bun in v1.1.35 was moving Bun's package manager state out of the BSS section of the binary, and heap allocating it when needed. This change caused crashes when parsing aliased dependencies (e.g. npm:zod@1.11.17) due to accessing undefined memory.

This has been fixed in this release, thanks to @dylan-conway!

Fixed quote and template escaping in expect.toMatchSnapshot

expect.toMatchSnapshot writes the stringified test name plus a counter and the received value to disk, both within a template literal. The release fixing escaping issues where backticks and ${} were not escaped correcly, causing snapshot to fail to match upon re-running the test.

index.test.ts
import { expect, test } from "bun:test";

test("snapshot `foo`", () => {
    const foo: string = "hello ${}!";
    expect(bar).toMatchSnapshot();
});

Fixed thanks to @pfgithub!

Fixed bun --hot with single quote in the entrypoint

Previously bun --hot would report a syntax error when the entrypoint contained a single quote.

bun --hot "src/single'quote.ts"

Now, Bun will run the entrypoint as expected, thanks to @pfgithub!

Fixed WebSocket.ping called without arguments

A bug was fixed where calling WebSocket.ping without arguments would fail to send a message without a payload.

import { WebSocketServer } from "ws";

const server = new WebSocketServer({ noServer: true });

server.on("connection", (ws) => {
  ws.ping(); // fixed
});

Fixed thanks to @cirospaciari!

Regression in printing \uFFFF

A regression in our JavaScript printer caused \uFFFF to print incorrectly. This has been fixed and now works as expected.

bun --print "'\uFFFF'.charCodeAt(1)"
# Previously:  57343
# Bun v1.1.37: NaN

Thanks to @pfgithub!

Non-zero for non-existent absolute file path in bun test

Given an aboslute file path to a file that does not exist, bun test will now exit with a non-zero exit code along with a module resolution error. Previously, the error would be reported but the exit code would be 0.

bun test /path/to/non-existent-file.test.ts

error: ModuleNotFound resolving "/path/to/non-existent-file.test.ts" (entry point)

node:net regression fixed

A regression impacting the http2-wrapper package has been fixed. The cause was a change in the node:net builtin module, where TLSSocket._handle._parentWrap was not set to JSStreamSocket correctly.

Fixed errors printing twice in watch or hot mode

Running a script with --watch or --hot would print unhandled exceptions to the console twice. A simple example of this would be:

$ bun --watch --print "throw new Error('oops!')"
1 | throw new Error('oops')
          ^
error: oops!
      at /home/user/[eval]:1:7
1 | throw new Error('oops')
          ^
error: oops!
      at /home/user/[eval]:1:7

This has been fixed and now only prints once, thanks to @alii!

Targeting browser with node:buffer in bun build

A bug preventing Buffer imports from node:buffer when targeting the browser with bun build has been fixed. Existing code could access Buffer through the default export, but importing Buffer directly would fail.

import { Buffer } from "node:buffer";
const buf = Buffer.from("bun!");
console.log(buf.toString());
bun build --target browser --outfile out.js
# Previously:  error: No matching export in "../../../../../bun-vfs$$/node_modules/buffer/index.js" for import "Buffer"
# Bun v1.1.37: out.js  41.66 KB

Thanks to 14 contributors!