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
- Introducingbun build
, Bun's new JavaScript bundler.v0.6.2
- Performance boosts: 20% fasterJSON.parse
, up to 2x fasterProxy
andarguments
.v0.6.3
- Implementednode:vm
, lots of fixes tonode:http
andnode:tls
.v0.6.4
- Implementedrequire.cache
,process.env.TZ
, and 80% fasterbun test
.v0.6.5
- Native support for CommonJS modules (previously, Bun did CJS to ESM transpilation),v0.6.6
-bun test
improvements, including Github Actions support,test.only()
,test.if()
,describe.skip()
, and 15+ moreexpect()
matchers; also streaming file uploads usingfetch()
.v0.6.7
- Node.js compatibility improvements to unblock Discord.js, Prisma, and Puppeteerv0.6.8
- IntroducedBun.password
, mocking inbun test
, andtoMatchObject()
v0.6.9
- Less memory usage and support for non-ascii filenames
To install Bun:
curl -fsSL https://bun.sh/install | bash
npm install -g bun
brew tap oven-sh/bun
brew install bun
docker pull oven/bun
docker run --rm --init --ulimit memlock=-1:-1 oven/bun
To upgrade Bun:
bun upgrade
Improved CommonJS support
Loading npm packages in Bun is more reliable now. This is because we rewrote CommonJS module loading (again). We've fixed many crashes related to loading CommonJS modules and improved our CommonJS module loader to be more compatible with Node.js.
webpack
now works in Bun
In the next version of Bun
— Jarred Sumner (@jarredsumner) June 26, 2023
webpack seems to work, thanks to @dylanconway111 and @cirospaciari pic.twitter.com/V9HEl3YRXy
require.main
is now supported
require.main
is a popular way for Node.js CLIs to check if the currently executing script started the process:
if (require.main === module.id) {
// do something
}
Bun now supports require.main
which unblocks several Node.js CLIs.
We've also added process.mainModule
which is an alias of require.main
.
console.log(require.main === process.mainModule); // true
__esModule
annotation is now supported
Bun now has runtime support for the __esModule
annotation used by bundlers & TypeScript to indicate what default
export should point to when importing a CommonJS module as an ES Module.
__esModule
is an annotation that preserves the export default <value>
for ESM that was converted into CommonJS and subseqntly re-imported back into ESM.
Should import a from 'b'
reference module.exports
or module.exports.default
?
This question is one of the things that has made the CommonJS -> ES Module transition difficult for the JavaScript ecosystem.
__esModule
is a way to preserve the default
import which is generated by Webpack, Babel, esbuild and used in many packages on npm. Bun now has runtime support for this.
That means the following input ES Module:
export default 42;
Would typically be converted by build tools into CommonJS published to npm as something like:
exports.default = 42;
exports.__esModule = true;
The __esModule
annotation preserves the default
value when imported back into ESM:
import foo from "./foo.js";
console.log(foo); // 42
Note that, in Node.js, the default
export is not preserved:
import foo from "./foo.js";
// This is already the "default" export
// so why should you have to do .default to get the original value?
console.log(foo.default); // 42
For continued compatibility with Node.js, this behavior is disabled when there is no __esModule
annotation OR when the enclosing package.json sets "type"
to "module"
(meaning, this is used in situations where Node.js would normally not support ESM anyway).
WebSocket client encoding fix
We fixed an encoding bug in Bun's client implementation of WebSocket
, which could cause a text frame with latin1 encoding to be truncated.
Thanks to these changes, support for puppeteer
has been improved in Bun.
import puppeteer from "puppeteer";
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto("https://example.com/");
await page.click("a");
await browser.close();
Improvements to bun:test
Bun now supports more matchers when using bun test
:
import { test, expect } from "bun:test";
test("new matchers", () => {
expect(fetch("http://example.com")).resolves.toBeInstanceOf(Response);
expect(fetch("invalid")).rejects.toBeInstanceOf(Error);
expect([]).toBeArray();
expect([1, 2, 3]).toBeArrayOfSize(3);
expect("").toBeTypeOf("string");
});
There is also improved support for mocking with spyOn()
.
import { test, expect, spyOn } from "bun:test";
test("spyOn()", () => {
const service = {
hasPermission: () => false,
};
let hasPermission = false;
const spy = spyOn(service, "hasPermission").mockImplementation(
() => hasPermission,
);
hasPermission = true;
expect(service.hasPermission()).toBe(true);
expect(spy).toHaveBeenCalled();
});
With all these changes, it should be faster and easier than ever to migrate your existing project to use bun test
. Here's what happens a popular npm package, such as zod
, migrates to bun test
.
Benchmarking Bun / Vitest / Jest against Zod's test suite
— Colin McDonnell (@colinhacks) June 26, 2023
Bun — 230ms
Vitest — 1.9s (8x slower)
Jest + SWC — 3s (13x slower)
Jest w/ ts-jest — 6.7s (29x slower)
Jest w/ Babel — 7.4s (32x slower) pic.twitter.com/RFqZ06hH5K
Bugfixes to .env
support
Bun has builtin support for loading .env
files into process.env
, saving you from needing to run require("dotenv").config()
on startup, allowing you to use .env
files in your tests, and in package.json "scripts"
.
There were several bugs with this functionality that @alexlamsl has fixed:
- Windows-style newlines could cause it to crash https://github.com/oven-sh/bun/issues/411
- .env with
$
inside the value of an environment variable were considered a nested variable when they shouldn't have been https://github.com/oven-sh/bun/issues/2823 - A crash on inputs of certain lengths https://github.com/oven-sh/bun/issues/3042
Node.js compatibility improvements
Watch files in Bun
fs.watch()
is now implemented!
import { watch } from "node:fs";
// file path
watch(".", (eventType, filename) => {
console.log(`event type = ${eventType}`);
if (filename) {
console.log(`filename = ${filename}`);
}
});
Bun.argv
now matchesprocess.argv
.
import { deepEquals } from "bun";
console.log(deepEquals(Bun.argv, process.argv)); // true
- More of V8's stack trace API now works.
function getStack() {
const cause = new Error();
const prevPrepareStackTrace = Error.prepareStackTrace;
Error.prepareStackTrace = (_, stack) => {
console.log(stack[0].getFunctionName()); // "getStack"
console.log(typeof stack[0].getLineNumber()); // "number"
console.log(typeof stack[0].getColumnNumber()); // "number"
};
Error.captureStackTrace(cause);
Error.prepareStackTrace = prevPrepareStackTrace;
}
import { openSync, writevSync } from "node:fs";
const fd = openSync("path/to/file");
const buffers = [new Uint8Array(3), new Uint8Array(3), new Uint8Array(3)];
console.log(writevSync(fd, buffers)); // 9
Module._nodeModulePaths
is now supported. This function returns an array of paths that Bun will search for modules in.
import { _nodeModulePaths } from "node:module";
console.log(_nodeModulePaths(".")); // ["/path/to/node_modules", "/path/node_modules", "/node_modules"]
crypto.randomInt()
is now supported, thanks to @lenovouserutil.deprecate
now supports"code"
argumentfs.createWriteStream
now supports passing flags correctly, thanks to @Hanaasagi
Improvements to bun install
We've also made a few additions to bun install
.
--exact <package>
bun install --exact
gives you the same behavior as npm install --save-exact
or yarn add --exact
. It pins the version of the package you're installing to the exact version you specify.
bun install --exact elysia@0.5.20
--frozen-lockfile
In this mode, Bun will throw an error if the bun.lock
file is out of sync with package.json
. This is useful for CI environments, where you want to ensure that bun.lock
is up to date.
bun install --frozen-lockfile
trustedDependencies
You can now configure an allow-list of dependencies to run lifecycle scripts, such as postinstall
. By default, these scripts are not run to make Bun more secure by default.
{
"trustedDependencies": [
"puppeteer"
]
}
We are planning to add a default list in the near future so that installing packages that rely on postinstall works as expected by default.
More bug fixes
- A crash that could occur when writing files with
Bun.write
has been fixed - The
node:tls
module was missing some ES exports - The
bun:sqlite
package was missing adefault
export for theDatabase
class mkdirSync(path, {recursive: true})
would sometimes return an empty string when it shouldn't have- File names in exception logs were sometimes garbled and that has been fixed
- Fixed a bug where module resolution for file: URLs with space characters in them would be % encoded instead of decoded
- Fixed a crash when readdir returns a large directory and some elements are UTF-16
- Fixed a potential crash when booelan arguments in node:fs functions are passed as non-boolean arguments
More new stuff
- The
lastInTextNode
property in HTMLRewriter'sTextChunk
is now supported which tells you if the current chunk of text is the last one. Thanks to @bru02
Changelog
#3310 | Changed Bun.argv to be same as process.argv by @Jarred-Sumner |
7f535a2 | Fixed issue when assigning module.require by @Jarred-Sumner |
#3314 | Improved validation of Bun.serve options by @cirospaciari |
#3320 | Fixed bugs with CommonJS imports by @Jarred-Sumner |
#3337 | Fixed bug where const decleration could be tree-shaked before usage by @dylan-conway |
8ad9e57 | Added missing alias for ucs2 encoding by @Jarred-Sumner |
b951c1f | Improved memory usage across the board by @Jarred-Sumner |
#3362 | Improved detection of ESM and CommonJS when file has no exports by @Jarred-Sumner |
#3316 | Implemented toBeArray , toBeArrayOfSize , and toBeTypeOf for bun:test by @TiranexDev |
#3359 | Implemented more of V8's stack trace APIs by @kvakil |
#3363 | Fixed overflow of fs.utimesSync() by @Jarred-Sumner |
83d7ec7 | Fixed bug with .copy() after .digest() with node:crypto by @Jarred-Sumner |
#3367 | Implemented asymetric matcher support for toEqual , toStrictEqual , and toHaveProperty by @dylan-conway |
#3360 | Fixed bugs with JSX in classic mode by @dylan-conway |
#3368 | Improved error message with circular dependencies by @Jarred-Sumner |
#3369 | Fixed crash with .env files by @Jarred-Sumner |
#3304 | Implemented more support for mock() in bun:test by @paperdave |
#3378 | Fixed usage of bun:test in CommonJS files by @Jarred-Sumner |
#3347 | Fixed various bugs with .env file parsing by @alexlamsl |
#3318 | Implemented resolves and rejects for expect() by @Electroid |
#3377 | Fixed bug with node-fetch import by @paperdave |
#3376 | Implemented spyOn() in bun:test by @paperdave |
#3249 | Implemented fs.watch() by @cirospaciari |
#3379 | Rewrote support for CommonJS by @Jarred-Sumner |
#3394 | Fixed bug with latin1 strings in WebSocket by @Jarred-Sumner |
#3400 | Fixed potential crash with bun install --production by @Jarred-Sumner |
5bd94b8 | Implemented process.mainModule by @Jarred-Sumner |
#3402 | Fixed bug with constructor of WriteStream by @Hanaasagi |
#3405 | Implemented support for embedding files in compiled executables by @Jarred-Sumner |
#3365 | Implemented bun install --frozen-lockfile by @tiagotex |
#3356 | Update lol-html by @bru02 |
#3401 | Fixed crash with lol-html by @Jarred-Sumner |
#3411 | Implemented _nodeModulePaths and require.main.paths by @dylan-conway |
#3288 | Implemented trustedDependencies in package.json by @alexlamsl |
#3419 | Implemented writev and readv by @Jarred-Sumner |
#3393 | Implemented __esModule annotations by @Jarred-Sumner |