Bun

Bun v1.0.18


Jarred Sumner ยท December 14, 2023

Bun v1.0.18 fixes 27 bugs (addressing 28 ๐Ÿ‘ reactions). A hang impacting create-vite & create-next & stdin has been fixed. Lifecycle scripts reporting "node" or "node-gyp" not found has been fixed. expect().rejects works like Jest now, and more bugfixes.

Bun is an incredibly fast JavaScript runtime, bundler, transpiler, and package manager โ€” all in one. In case you missed it, here are some of the recent changes to 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 - 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 - 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.
  • v1.0.16 - 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
  • v1.0.17 - Fixes 15 bugs (addressing 152 ๐Ÿ‘ reactions). bun install postinstall scripts run for top 500 packages, bunx supabase starts 30x faster than npx supabase, bunx esbuild starts 50x faster than npx esbuild and bugfixes to bun install

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

Fixed: Hang when reading from process.stdin

A bug where Bun.stdin.stream() or process.stdin would sometimes hang after the first read has been fixed, thanks to @paperdave.

This bug impacted many packages, including create-vite and create-next-app. While it prompts for user input, the terminal would freeze at the second prompt.

Where bun would get stuck

This was caused by two bugs related to our implementation of process.stdin, which is built on top of Bun.stdin.stream():

  1. We previously called cancel on the ReadableStream input, closing the stream and preventing the ability to recieve new data. The fix is to use releaseLock instead.
  2. A bug where after calling releaseLock, Bun would think the stream is still "in use", which would keep the process alive forever.

This has been fixed, thanks to @paperdave.

Fixed: Lifecycle scripts running in the wrong working directory

A bug where lifecycle scripts would sometimes run in the wrong working directory has been fixed. This bug was fixed by @dylan-conway.

Fixed: Lifecycle scripts reporting command not found "node" or "node-gyp"

Yesterday in Bun v1.0.17, we enabled lifecycle scripts for the top 500 packages on npm. While we did plenty of testing ourselves, we missed a few cases that were reported by users.

For example, what happens if node-gyp was not installed?

bun install
> node-gyp rebuild
Command not found: node-gyp

That's no good. We've fixed this. Now, Bun will symlink node-gyp into running bunx node-gyp if node-gyp is not installed (which will lazily auto-install node-gyp).

Or what if a lifecycle script you're using depends on node, but you're running in Docker and the container does not have node installed?

bun install
> node install.js
Command not found: node

That's not good either. Now Bun will symlink node into running bun if node is not installed.

These have been fixed, thanks to @dylan-conway.

Symlinking node to bun works better

Bun will automatically symlink node to bun when it is not available to ensure scripts that expect node to exist still work. However, Bun's CLI is very different from Node's, so when running a command like node build or node install, it is expected to find a file like build.js, not invoke a bundler or package manager.

Previously:

ln -s $(which bun) node && chmod +x node
./node build
bun build v1.0.18 (a648ed9e)
error: Missing entrypoints. What would you like to bundle?

Usage:
  $ bun build <entrypoint> [...<entrypoints>] [...flags]

To see full documentation:
  $ bun build --help

Now:

./node build
Hello! (Bun 1.0.18)
build.js
const isBun = typeof Bun !== 'undefined';
const name = isBun ? "Bun" : "Node";
const version = isBun ? Bun.version : process.version;
console.log(`Hello! (${name} ${version})`)

Thanks to @paperdave.

Reflinks are copy-on-write copies of files. They make it faster to copy files. macOS has supported reflinks since APFS was released in 2017. Linux has supported reflinks with brtfs and more recently more filesystems have added support for reflinks.

Bun already used copy_file_range on Linux to make copying files faster, but some hypervisors (like gVisor) disable this system call. Now Bun will try to use ioctl_ficlone, which is a lower-level system call that internally does something very similar to copy_file_range. ioctl_ficlone is overall less well-supported than copy_file_range, but it's a useful fallback when copy_file_range is unavailable.

When copy_file_range is disabled but ioctl_ficlone is available, you will notice a speed improvement to:

  • bun install
  • fs.copyFile, fs.copyFileSync
  • fs.cp, fs.cpSync

To force the usage of ioctl_ficlone, you can pass constants.COPYFILE_FICLONE_FORCE as the flags argument to fs.copyFile or fs.cp:

import { copyFileSync, constants } from "fs";

copyFileSync("src/index.js", "dest/index.js", constants.COPYFILE_FICLONE_FORCE);

This will throw an error if ioctl_ficlone is not available.

Fixed: Wildcard tsconfig path not including suffix

A bug where wildcard tsconfig.json paths would fail to resolve due to the suffix not being added has been fixed

error: Cannot find module "@faasjs/bar" from ".../test/js/bun/resolve/resolve-test.js"

Thanks to @james-elicx for fixing this.

Fixed: expect().rejects.toThrow works more like Jest

In Jest, expect().toThrow requires you pass a function to call. With expect().rejects.toThrow, they let you simply pass in the rejecting promise. Bun did not support this case.

The following test now passes in bun:test:

rejects-toThrow.test.js
test("rejects to octopus", async () => {
  const rejection = Promise.reject(new Error("octopus"));
  await expect(rejection).rejects.toThrow("octopus");
});

Thanks to @paperdave for fixing this.

Thanks to 10 contributors!