We're hiring systems engineers in San Francisco to build the future of JavaScript!
This release fixes 12 bugs (addressing 196 👍). JSDOM & happy-dom work more reliably due to node:vm compatibility improvements. bun build --compile gets the --windows-icon and --windows-hide-console options (Windows-only). bun install gets the --omit=dev|optional|peer options and in .npmrc. A hoisting edgecase in bun install is fixed
To install Bun
curl -fsSL https://bun.sh/install | bash
npm install -g bun
powershell -c "irm bun.sh/install.ps1|iex"
scoop install 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
JSDOM, happy-dom, and vm compatibility improvements
We've made compatibility improvements to our node:vm
implementation, and that makes jsdom
and happy-dom
work more reliably in Bun.
JSDOM fixes
Previously, the runScripts: "outside-only"
, runScripts: "dangerously"
, and runInContext
options in JSDOM were not supported. Now they are.
happy-dom fixes
happy-dom
no longer throws an error when you call window.requestAnimationFrame
, or when you call various other methods that depend on the this
value being set correctly.
New: Icons in single-file executables for Windows
Single-file executables now support the --windows-icon=./icon.ico
option to embed an icon in the executable on Windows.
bun build --compile --windows-icon=./icon.ico ./index.ts --outfile=app.exe
Thanks to @paperdave for implementing this!
New: Hide the console window in single-file executables for Windows
You can now hide the console window on Windows when a single-file executable is run.
bun build --compile --windows-hide-console ./index.ts --outfile=app.exe
New: throw: true in Bun.build
When the build fails, the throw: true
option in Bun.build
throws an error instead of returning errors in the logs
array.
const build = Bun.build({
entrypoints: ["./index.ts"],
outdir: "./dist",
throw: true,
});
We will make this behavior the default in Bun v1.2.
New: --omit=dev|optional|peer options in bun install
We want bun install
to "just work" in existing projects using npm. We already support package-lock.json
migration (and have for awhile), and this is another step towards that goal.
bun install
now supports:
--omit=dev
to omit dev dependencies (similar to--production
)--omit=optional
to omit optional dependencies--omit=peer
to omit peer dependencies- ...and any combination of the above
We've also added support for include
and exclude
in .npmrc
, and in bunfig.toml
.
Thanks to @dylan-conway for implementing this!
New: expect().toHaveBeenCalledOnce()
You can now use expect().toHaveBeenCalledOnce()
to check that a mock function was called exactly once.
import { test, expect, mock } from "bun:test";
test("fn is called once", () => {
const fn = mock(() => {});
expect(fn).not.toHaveBeenCalledOnce();
fn();
expect(fn).toHaveBeenCalledOnce();
fn();
expect(fn).not.toHaveBeenCalledOnce();
});
'use strict' is now CommonJS
Some CLIs REALLY want to trick bundlers and get the current module's file path, do a runtime require, or check if the current module is the main module. They try all kinds of things to make it work, such as:
"use strict";
if (eval("require.main") === eval("module.main")) {
// ...
}
One of the hard parts about how Bun makes CommonJS and ESM interop "just work" is that there's a lot of ambiguity.
Consider this code:
console.log("123");
Is that CommonJS or ESM? No way to tell.
Okay, how about this?
console.log(module.require("path"));
CommonJS, because it's using module.require
to get the path
module.
And this?
import path from "path";
console.log(path);
ESM because it's using import
.
And this?
import path from "path";
const fs = require("fs");
console.log(fs.readFileSync(path.resolve("package.json"), "utf8"));
ESM, because it's using import
, and saying it's CommonJS due to the require would break the code. We want to simplify building stuff in JavaScript, so let's just say it's ESM and not be fussy.
And finally, how about this?
"use strict";
console.log(eval("module.require('path')"));
Previously, Bun would say "ESM" because it's what we default to when there's absolutely no way to tell (including when the file extension is ambiguous, no "type" field in package.json, no export, no import, no TLA, etc etc).
Now, Bun will say "CommonJS" because it had "use strict"
at the top of the file.
ES modules are always strict mode, so an explicit "use strict"
would only be useful to include if the file was originally intended to be CommonJS. Most build tools nowadays that output CommonJS will include the "use strict"
directive at the top of the file. So we can use this as a last-chance heuristic when it's completely ambiguous whether the file is CommonJS or ESM.
Previously, an eval("require.main")
would throw a ReferenceError
because the spec-compliant behavior of eval
inside of ESM is indirect (global scope), and even if it wasn't global scope, Bun replaces require
with import.meta.require
in ESM at transpilation time so that code would never work regardless. So, in the spirit of "your code should just work", we have added "use strict"
at the top of the file to our repertoire of what we consider CommonJS.
Fixed: bun install progress bar shows total install count
In the next version of Bun
— Bun (@bunjavascript) December 20, 2024
A longstanding bug that sometimes caused this fraction to be impossible is fixed pic.twitter.com/8heI6obyft
Fixed: potential infinite loop with cached disabled optional dependencies in bun install
An infinite loop that could potentially occur caused by disabling optional dependencies and installing with a populated cache has been fixed.
Thanks to @dylan-conway for fixing this!
Fixed: unresolvable dependencies edgecase in bun install
When using platform-specific dependencies or disabling dependencies through --production
, in certain rare cases bun install
would hang due to incorrectly handling unresolvable dependencies.
This was caused by a spot where we were not sorting dependencies by their "behavior" (is it optional? peer? dev? etc). This led to inconsistent
Thanks to @dylan-conway for fixing this!
Fixed: --conditions=default
in bun build
A crash that could occur when using --conditions=default
in Bun.build
has been fixed. Note: the default
condition is used by default in Bun.build
.
Thanks to @paperdave for fixing this!
Fixed: util.styleText
supports color arrays
You can now pass an array of colors to util.styleText
to style a string with multiple colors.
const styledText = util.styleText(
["red", "bold", "underline"],
"Hello, world!",
);
This aligns the behavior with Node.js
Fixed: unnecessary inline sourcemaps in VSCode
When you have Bun's official VSCode extension installed, Bun automatically reports runtime errors in the "problems" tab of VSCode to give you real-time in-editor error reporting.
This involves a WebKit inspector connection that occurs when you ordinarily launch Bun from your VSCode terminal.
When the debugger is connected, Bun internally appends inline sourcemaps to transpiled JavaScript files so the debugger can find the original source code and map errors, messages, breakpoints files, etc. This is important for debugging, but it costs you memory and startup time to append these base64-encoded sourcemaps to every single file that loads.
These sourcemaps are only necessary for the debugger, and not for the "problems" tab feature in VSCode (which sends over pre-sourcemapped errors instead of sourcemapping in the extension). This release disables inline sourcemaps when only the "problems" tab is being used.
Thanks to @riskymh for fixing this!
Fixed: Theoretical crash in CommonJS module loading
A hypothetical crash that could occur in CommonJS module loading has been fixed.
Bun transpiles every file before it loads.
CommonJS modules have a function wrapper that looks something like this:
(function (exports, require, module, __filename, __dirname) {
// module code
});
In Bun's CommonJS module loader, we were not handling the scenario where a module does not evaluate to a function. The transpiler always outputs a function. That being said, it's theoretically possible that a module could evaluate to something other than a function, and so we fixed it anyway.
As part of this, we also moved the step where it assigns the module.require
and module.require.resolve
function to occur after the outer function wrapper has been evaluated instead of before. There should in theory not be an observable behavior difference, but if you find anything please let us know!
Fixed: import namespaces and default imports for macros
You can now import namespaces and default imports for macros.
import myMacro from "at-build-time" with {type: "macro"};
import * as allMyMacros from "at-build-time" with {type: "macro"};
myMacro();
allMyMacros.myMacro();
Thanks to @brainkim for fixing this!