Bun

Bun v1.1.30


Jarred Sumner · October 8, 2024

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

This release fixes 57 bugs (addressing 150 👍). Bun's CSS bundler is here (and very experimental). bun publish is a drop-in replacement for npm publish. bun build --bytecode compiles to bytecode leading to 2x faster start time. Bun.color() formats and normalizes colors. bun pm whoami prints your npm username. bun install reads $HOME/.npmrc. bun build --format=cjs outputs CommonJS modules. bun build --target=node now supports Node.js native addons. 30x faster crypto.privateEncrypt() & crypto.publicDecrypt(). Zombie process killer in bun test. --banner and --footer options in bun build. Bun.serve().stop() returns a promise. Bun.CryptoHasher now supports HMAC. Bugfixes for node:zlib, node:module, and node:buffer. Lots more bugfixes and Node.js compatibility 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

Experimental: CSS Parsing & bundling

Over the last two months, @zackradisic built a CSS parser and bundler for Bun.

It's based on @devongovett's excellent LightningCSS parser, rewritten from Rust to Zig to vertically integrate into Bun's JavaScript & TypeScript parser, bundler, and runtime.

You can try it out today via bun build (CLI) or Bun.build (API) with the --experimental-css flag.

CSS bundling

Bun's CSS bundler concatenates multiple CSS files and any assets referenced via url, @import, @font-face, etc into a single CSS file you can send to browsers, avoiding a waterfall of network requests.

index.css
@import "foo.css";
@import "bar.css";

To try it out with bun build via CLI:

shell
bun build --experimental-css ./index.css

Those two CSS files become a single bundled CSS file:

dist.css
/** foo.css */
.foo {
  background: red;
}

/** bar.css */
.bar {
  background: blue;
}

Import .css files in JavaScript & TypeScript

Bun's CSS parser integrates with our JavaScript & TypeScript parser & bundler.

You can import .css files in JavaScript & TypeScript and an additional css entrypoint will be created that combines all the css files imported from a JavaScript/TypeScript module graph, along with any @import rules.

index.ts
import "./style.css";
import Component from "./MyComponent.tsx";

// ... rest of your app

Then, if MyComponent.tsx imports a CSS file, instead of adding extra .css files to the bundle, all the CSS imported per entrypoint is flattened into a single CSS file.

shell
bun build --experimental-css ./index.ts --outdir=dist
  index.js     0.10 KB
  index.css    0.10 KB
[5ms] bundle 4 modules

This outputs the following css file, which came from TypeScript imports:

style.css
/* style.css */
.hello {
  background: red;
}

/* MyComponent.css */
.MyComponent {
  color: green;
}

Bundle CSS with Bun.build:

We've also added support for programmatically bundling CSS with the Bun.build API. You can bundle both CSS and JavaScript in the same build with the same API.

api.ts
import { build } from "bun";

const results = await build({
  entrypoints: ["./index.css"],
  outdir: "./dist",
  experimentalCss: true,
});

console.log(results);

How fast can it parse & bundle CSS?

We will release benchmarks once it's more stable, but I think you will be satisfied with the performance.

Huge thanks to @zackradisic for building this, and for @devongovett for his excellent LightningCSS library.

bun publish: drop-in replacement for npm publish

You can use bun to publish npm packages via bun publish

bun publish

bun publish v1.1.30 (ecad797a)

packed 108B package.json
packed 3B README.md
packed 19B index.js

Total files: 3
Shasum: abd73b91b21057f0b07a58472077f5a3bbfb3e89
Integrity: sha512-aWkWikXiSXyR[...]Ubll4PQcEEoNw==
Unpacked size: 130B
Packed size: 254B
Tag: latest
Access: default
Registry: http://localhost:4873

+ lodash-2@1.0.0

bun publish is intended as a drop-in replacement for npm publish, and supports many of the same features like:

  • Reading .npmrc files for authentication
  • Packing tarballs, accounting for .gitignore and .npmignore files in multiple directories
  • OTP / Two-factor authentication
  • Handling various edgecases with package.json fields like "bin", "files", etc
  • Handling missing README files carefully

Additionally, when using workspaces, bun publish will automatically rewrite dependency versions of workspace packages to point to the published version so that you can publish packages correctly that internally use workspace: dependencies.

Thanks to @dylan-conway for implementing this!

bun pm whoami

The new bun pm whoami command will print the current user's npm username.

bun pm whoami
bun

Thanks to @dylan-conway for implementing this!

Compile to bytecode for 2x faster startup time

bun build now supports bundling JavaScript & TypeScript to bytecode for a 2x faster startup time.

bun build --bytecode --compile ./index.ts

Single-file bytecode executables

Bytecode bundles

You can also use this without --compile if you would prefer individual files without the standalone executable.

bun build --bytecode --outdir=./dist ./index.ts

This will output .jsc files alongside .js files. These .jsc contain the bytecode version of the file. Both are necessary to run in Bun, as the bytecode compilation doesn't currently compile async functions, generators, eval, and a handful of other edge cases.

The bytecode can be 8x larger than the source code, so this makes startup faster at a cost of increased disk space.

CommonJS bundle output format

bun build now supports --format=cjs to output CommonJS bundles. Preivously, only --format=esm was supported.

This makes it easier to use bun build to build libraries & applications meant for Node.js in situations where CommonJS modules are easier to work with.

shell.sh
index.js
output.js
shell.sh
bun build --format=cjs index.js
index.js
// index.js
export default "Hello, world!";
output.js
var __defProp = Object.defineProperty;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __moduleCache = /* @__PURE__ */ new WeakMap;
var __toCommonJS = (from) => {
  var entry = __moduleCache.get(from), desc;
  if (entry)
    return entry;
  entry = __defProp({}, "__esModule", { value: true });
  if (from && typeof from === "object" || typeof from === "function")
    __getOwnPropNames(from).map((key) => !__hasOwnProp.call(entry, key) && __defProp(entry, key, {
      get: () => from[key],
      enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
    }));
  __moduleCache.set(from, entry);
  return entry;
};
var __export = (target, all) => {
  for (var name in all)
    __defProp(target, name, {
      get: all[name],
      enumerable: true,
      configurable: true,
      set: (newValue) => all[name] = () => newValue
    });
};

// index.js
var exports_site = {};
__export(exports_site, {
  default: () => site_default
});
module.exports = __toCommonJS(exports_site);
var site_default = "Hello, world!";

Zombie process killer in bun test

On Linux and macOS, when a spawned process is not waited on, it's called a "zombie process".

Bun now automatically kills zombie processes from test timeouts in bun test.

When a test that spawns process(es) times out in bun test, if the process is not accessible outside of the test, then you would end up with a process that potentially never gets cleaned up if bun test were to exit soon after. This was especially noticable with tools like Puppeteer that spawn a browser instance. Lingering Chromium processes can cost you 100s of MBs of RAM and cause all sorts of issues.

Bun now internally tracks all processes spawned inside tests and if a test times out, Bun will automatically kill the process.

Bundle .node files with --target=node

You can now bundle Node.js native modules using Bun and run those Node.js native modules in Node.js.

We fixed a bug that prevented --target=node from correctly bundling .node files, which are Node.js native modules. Previously, this was only supported with --target=bun, and now it's supported with --target=node as well.

You can now add a banner and footer above and below bundled output with --banner and --footer options in bun build and Bun.build.

Input:

shell
api.js
shell
bun build --banner "/* Hello, world! */" --footer "/* Goodbye! */" index.ts
api.js
await Bun.build({
  entrypoints: ["./index.ts"],
  outdir: "./dist",
  banner: "/* Hello, world! */",
  footer: "/* Goodbye! */",
});

Output:

/**
 * Hello, world!
 */
export default "Hello, world!";
/**
 * Goodbye!
 */

Huge thanks to @versecafe for implementing this!

--registry flag in bun install

You can now specify a custom registry from the command line when installing packages with bun install. You can use this flag by itself or with packages you want to add to your project.

# Install packages from a custom local registry instead of the default npm registry.
bun install --registry=http://localhost:4873/
# With packages
bun install zod --registry=http://localhost:4873/

You can continue to set the registry in .npmrc files, bunfig.toml, or via NPM_CONFIG_REGISTRY environment variable. This extra CLI flag is useful if you want to globally install a package from a private registry without having to set the registry in your .npmrc file or use a scoped package name.

Bun.color() formats and normalizes colors

stop() in Bun.serve() returns a Promise

Bun's HTTP server API Bun.serve() now returns a Promise from server.stop() which resolves when all connections are closed.

This lets you use await server.stop() to wait for all the connections to close without abruptly closing them.

const server = Bun.serve({
  port: 3000,
  async fetch(req) {
    return new Response("Hello, world!");
  },
});

await server.stop();

HMAC in Bun.CryptoHasher

Bun.CryptoHasher can now be used to compute HMAC digests. To do so, pass the key to the constructor.

import { CryptoHasher } from "bun";
const hasher = new CryptoHasher("sha1", "secret-key");
const hash = hasher
  .update("message")
  // This creates the HMAC signature
  .digest("hex");

console.log(hash); // 48de2656eac2c9c21b04faeec4f1be9672ef53c1

bun install reads $HOME/.npmrc now

bun install now reads $HOME/.npmrc files for authentication if no other registry is specified.

Previously, only .npmrc in the current directory was read. Now, $HOME/.npmrc is also read if no .npmrc is found in the current package.json directory.

Thanks to @robertshuford for implementing this!

Bundler fixes

Fixed: top-level await bug in bun build

A bug that could cause spurious top-level await errors in bun build has been fixed, thanks to @snoglobe!

Fixed: importing JSON with unicode characters in bun build

Importing JSON with unicode characters now works correctly with bun build. Bun was previously incorrectly converting the encoding of strings from JSON, causing unicode characters to be interpreted in the wrong encoding.

// test.json
{
  "测试a": "b"
}
// test.js
import data from "./test.json";
console.log(data["测试" + "a"]); // b

Thanks to @snoglobe!

Node.js compatibility improvements

30x faster crypto.privateEncrypt() & crypto.publicDecrypt()

crypto.privateEncrypt() and crypto.publicDecrypt() in Bun are now powered by BoringSSL, which leads to a 30x performance improvement to these functions over the previous JavaScript polyfill implementation.

After:

benchmark                                  time (avg)             (min … max)       p75       p99      p999
----------------------------------------------------------------------------- -----------------------------
RSA sign RSA_PKCS1_PADDING round-trip     832 µs/iter       (816 µs … 915 µs)    834 µs    893 µs    915 µs

Before:

benchmark                                  time (avg)             (min … max)       p75       p99      p999
----------------------------------------------------------------------------- -----------------------------
RSA sign RSA_PKCS1_PADDING round-trip  26'997 µs/iter (26'657 µs … 27'551 µs) 27'119 µs 27'551 µs 27'551 µs

Thanks to @wpaulino for the fix!

require("cluster") gets 6ms faster

The time it takes to require("cluster") has been reduced by 6ms, thanks to @nektro

Previously:

bun-1.1.29 --print 'console.time("require(`cluster`)");require(`cluster`);console.timeEnd("require(`cluster`)");' # Old
[7.62ms] require(`cluster`)

Now:

bun --print 'console.time("require(`cluster`)");require(`cluster`);console.timeEnd("require(`cluster`)");' # New
[1.39ms] require(`cluster`)

Fixed: Buffer.alloc bug with 2nd argument bug

A bug when Buffer.alloc was passed an empty string as the second argument has been fixed.

Thanks to @nektro for the fix!

Fixed: Several node:zlib issues

Bun's node:zlib implementation now passes many more Node.js tests, thanks to @nektro! This addresses issues with Sharp/Jimp, Zip.js, some GPRC libraries, pubnub, and more.

Thanks to @nektro for the fix!

Fixed: Prisma not waiting for promises before exiting

A bug in our NAPI implementation could cause Prisma to not wait for promises in queries before the Bun process exits.

Thanks to @190n for the fix!

Fixed: Missing exports in node:module

A handful of missing exports in node:module have been added:

  • _preloadModules was previously undefined. Now it does nothing
  • _debug & _pathCache was previously undefined
  • enableCompileCache and getCompileCache were previously undefined
  • __resolveFilename and _resolveFilename were previously both defined, now only _resolveFilename is defined

Fixed: console.log on require.cache

Previously, console.log(require.cache) would print an empty object. Now it prints the cache.

// Old
{}

// New
{
  "/path/to/module": Module {
    id: "/path/to/module",
    exports: {},
    parent: null,
    filename: "/path/to/module.js",
    loaded: false,
    children: [],
    paths: []
  }
}

Fixed: process.cwd() returning the wrong path on Windows for the root of a drive

A regression has been fixed where process.cwd() would return an incorrect path on Windows if the current working directory was at the root of a drive. For example, if the current working directory was C:\, process.cwd() would return C: instead of C:\.

Fixed: 2 rare crashes in Bun.spawn()

Two rare crashes in Bun.spawn() have been fixed. One was a regression from Bun v1.1.25. It sometimes happened when the subprocess was garbage collected before referencing stdin in certain cases.

Prototype pollution mitigation

To make prototype pollution more difficult to exploit in Bun's APIs, Bun APIs now ignore the Object prototype when reading values from objects and functions in natively implemented Bun APIs (such as fetch, Request, Response, etc).

Previously, the following code would cause glob.scanSync to follow symlinks:

import { Glob } from "bun";
const glob = new Glob("*.ts");
Object.defineProperty(Object.prototype, "followSymlinks", {
  value: true,
  writable: true,
  configurable: true,
  enumerable: true,
});
const second = glob.scanSync({
  cwd: path.join(dir, "abc"),
  onlyFiles: true,
});

Now, since the Object.prototype is ignored, this attack is mitigated. We've done this for nearly all Bun APIs implemented in Zig.

This doesn't fully prevent prototype pollution attacks, but it does make them more difficult to exploit in Bun APIs. Thanks to @lirantal for the report!

Fixed: bun add incorrectly adding relative paths to tarballs in workspaces

A bug was fixed causing bun add to fail to add local tarball packages to workspaces. This would happen because bun was using the workspace root as the base path rather than the current workspace package the tarball would be added to.

Thanks to 17 contributors!