Bun

Bun v1.0.15


Ashcon Partovi · 2023 年 12 月 2 日

Bun v1.0.15 修正了 23 個錯誤(解決了 117 個 👍 反應),tsc 啟動速度快 2 倍,Prettier 快 40%。穩定的 WebSocket 用戶端、語法突顯錯誤、更清晰的堆疊追蹤、使用 expect.extend() 新增自訂測試匹配器 + 其他 expect 匹配器、TensorFlow.js 支援,以及更多功能。

Bun 是一個極其快速的 JavaScript 執行環境、打包工具、轉譯器和套件管理器 — 功能All-in-One。如果您錯過了,以下是 Bun 最近的一些變更

  • v1.0.12 - 新增 bun -e 以評估腳本、bun --env-file 以載入環境變數、server.urlimport.meta.envexpect.unreachable()、改進的 CLI 說明輸出,以及更多功能
  • v1.0.13 - 修正了 6 個錯誤(解決了 317 個 👍 反應)。現在可以使用 'http2' 模組和 gRPC.js。Vite 5 和 Rollup 4 可以運作。實作 process.report.getReport(),改進對 ES5 'with' 語句的支援,修正了 bun install 中的回歸錯誤,修正了列印例外時的崩潰問題,修正了 Bun.spawn 錯誤,並修正了 peer dependencies 錯誤
  • v1.0.14 - Bun.Glob,一個使用 glob 模式匹配檔案和字串的快速 API。它還修正在 bun install 期間提取依賴項時的競爭條件,改進了 node_modules 中的 TypeScript 模組解析,並使錯誤訊息更易於閱讀。

安裝 Bun

curl
npm
brew
docker
curl
curl -fsSL https://bun.dev.org.tw/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

升級 Bun

bun upgrade

轉譯器快取使像 tsc 這樣的 CLI 速度提高 2 倍

Bun 讓您可以執行 TypeScript 和 JSX 檔案,而無需建置步驟。這是透過在載入檔案之前執行 Bun 的 JavaScript 轉譯器來實現的。在此版本中,我們為大於 50KB 的檔案引入了內容可尋址快取,以避免重複轉譯相同檔案的效能開銷。這使得像 tsc 這樣的 CLI 執行速度提高了 2 倍。

與 Bun v1.0.14 相比,tsc --help 獲得了 2 倍的加速

Benchmark 1: bun --bun ./node_modules/.bin/tsc --help
  Time (mean ± σ):      82.2 ms ±   2.6 ms    [User: 70.1 ms, System: 14.3 ms]
  Range (min … max):    78.4 ms …  87.1 ms    37 runs

Benchmark 2: bun-1.0.14 --bun ./node_modules/.bin/tsc --help
  Time (mean ± σ):     197.0 ms ±   3.6 ms    [User: 172.0 ms, System: 27.2 ms]
  Range (min … max):   192.4 ms … 204.4 ms    14 runs

Benchmark 3: node ./node_modules/.bin/tsc --help
  Time (mean ± σ):     113.8 ms ±   3.2 ms    [User: 103.6 ms, System: 16.0 ms]
  Range (min … max):   110.0 ms … 123.4 ms    23 runs

Summary
  bun --bun ./node_modules/.bin/tsc --help ran
    1.38 ± 0.06 times faster than node ./node_modules/.bin/tsc --help
    2.40 ± 0.09 times faster than bun-1.0.14 --bun ./node_modules/.bin/tsc --help

Prettier 速度提高達 40%(與 Bun v1.0.14 相比)

Benchmark 1: bun --bun ./node_modules/.bin/prettier --write ./examples/hashing.js
  Time (mean ± σ):     124.5 ms ±   3.2 ms    [User: 144.5 ms, System: 23.4 ms]
  Range (min … max):   119.9 ms … 131.8 ms    23 runs

Benchmark 2: bun-1.0.14 --bun ./node_modules/.bin/prettier --write ./examples/hashing.js
  Time (mean ± σ):     184.4 ms ±   4.1 ms    [User: 202.7 ms, System: 28.6 ms]
  Range (min … max):   175.2 ms … 192.9 ms    15 runs

Benchmark 3: node ./node_modules/.bin/prettier --write ./examples/hashing.js
  Time (mean ± σ):     162.9 ms ±   3.7 ms    [User: 161.8 ms, System: 42.5 ms]
  Range (min … max):   158.2 ms … 170.7 ms    17 runs

Summary
  bun --bun ./node_modules/.bin/prettier --write ./examples/hashing.js ran
    1.31 ± 0.04 times faster than node ./node_modules/.bin/prettier --write ./examples/hashing.js
    1.48 ± 0.05 times faster than bun-1.0.14 --bun ./node_modules/.bin/prettier --write ./examples/hashing.js

為了最大限度地減少磁碟使用量,轉譯器快取是全域的,並在所有專案之間共享。隨時刪除快取是安全的,而且由於它是內容可尋址的,因此不會包含重複條目。如果您正在使用臨時檔案系統(例如 Docker)執行 Bun,建議停用快取。

如果您想自訂這些檔案的快取路徑,可以設定 BUN_RUNTIME_TRANSPILER_CACHE_PATH 環境變數。您也可以將該值設定為 0 以完全停用快取。

穩定的 WebSocket 用戶端

此版本包括(最終)穩定的 WebSocket 用戶端。Bun 已經支援 WebSocket 一段時間了,但它有各種協定錯誤,例如過早斷線或未正確處理訊息分段。現在,它已穩定,應該可以與大多數 WebSocket 伺服器一起運作。

const ws = new WebSocket("wss://echo.websocket.org/");

ws.addEventListener("message", ({ data }) => {
  console.log("Received:", data);
});

ws.addEventListener("open", () => {
  ws.send("Hello!");
});

為了測試合規性,我們使用了 Autobahn 測試套件,這是測試 WebSocket 實作的事實標準。Bun 通過了 100% 的 Autobahn 測試,排除了那些測試壓縮的項目。(我們的 WebSocket 用戶端尚不支援壓縮,但即將推出。)

這表示所有這些 問題 以及更多問題都已修正。

感謝 @cirospaciari 修正這些錯誤並使 Autobahn 通過測試!

expect.extend() 和更多匹配器

您現在可以使用 expect.extend() 定義自訂測試匹配器。當您想要建立可在多個測試中重複使用的自訂匹配器時,這非常有用。例如,您可以建立一個自訂匹配器來檢查數字是否在範圍內

import { test, expect } from "bun:test";

expect.extend({
  toBeWithinRange(received, floor, ceiling) {
    const pass = received >= floor && received <= ceiling;
    if (pass) {
      return {
        message: () =>
          `expected ${received} not to be within range ${floor} - ${ceiling}`,
        pass: true,
      };
    } else {
      return {
        message: () =>
          `expected ${received} to be within range ${floor} - ${ceiling}`,
        pass: false,
      };
    }
  },
});

test("toBeWithinRange()", () => {
  expect(1).toBeWithinRange(1, 99); // ✅
  expect(100).toBeWithinRange(1, 99); // ❌ expected 100 to be within range 1 - 99
});

Bun 還支援更多非對稱匹配器,包括

除了上述非對稱匹配器之外,Bun 還引入了兩個適用於 Promise 的新非對稱匹配器。

  • expect.resolvesTo
  • expect.rejectsTo

例如,您可以使用 expect.resolvedTo 檢查解析為值的 Promise

import { test, expect } from "bun:test";
import { getTempurature, getForecast } from "./weather";

test("expect.resolvedTo", async () => {
  const weather = {
    tempurature: getTempurature(),
    forecast: getForecast(),
  };

  await expect(weather).toMatchObject({
    tempurature: expect.resolvedTo.closeTo(10, 5),
    forecast: expect.resolvedTo.stringMatching(/rain|snow|sleet/),
  });
});

感謝 @otgerrogla 實作這些功能,並修正 expect() 的各種錯誤。

還有更多可用的模擬匹配器,包括

您可以使用這些匹配器來檢查函數是否使用特定引數調用

import { test, mock, expect } from "bun:test";

test("toHaveBeenCalledWith()", () => {
  const fn = mock(() => {});
  sum(1, 2, 3);

  expect(fn).toHaveBeenCalledWith(1, 2, 3); // ✅
  expect(fn).toHaveBeenCalledWith(1, 2); // ❌
});

感謝 @james-elicx 實作這些匹配器並改進各種錯誤訊息。

錯誤的語法突顯

當 Bun 中發生例外時,它會將堆疊追蹤列印到控制台,並附帶多行原始碼預覽。現在,原始碼預覽會進行語法突顯,使其更易於閱讀。

Syntax errors

語法突顯器還擴展到所有其他包含原始碼的錯誤,例如在使用 bun install 時,多個工作區套件共享相同名稱時發生的錯誤。

感謝 @paperclover 致力於此!

更好的 Error.stack 追蹤

我們對 Error.stack 追蹤的格式化方式進行了一些改進。

堆疊追蹤現在包含較少的雜訊,例如與錯誤無關的內部函數。

1 | throw new Error("Oops");
          ^
error: Oops
    at /Users/jarred/Desktop/oops.js:1:7
-   at globalThis (/Users/jarred/Desktop/oops.js:3:14)
-   at overridableRequire (:1:20)
    at /Users/jarred/Desktop/index.js:3:8
-   at globalThis (/Users/jarred/Desktop/index.js:3:8)

我們還修正了一個錯誤,即 Bun 無法偵測 Error.stack 屬性是否被修改。如果您使用的函式庫修改了堆疊追蹤(例如將多個追蹤連接在一起),則可能會發生這種情況。

const err = new Error("Oops 1");
err.stack += "\n" + new Error("Oops 2").stack;
throw err;

Bun 現在重新解析 error.stack,以便正確偵測和格式化修改後的堆疊追蹤。

fs.readdir() 中的 recursive 選項比 Node.js 快 40 倍

Bun 現在支援 fs.readdir() 中的 recursive 選項,該選項用於遞迴讀取目錄。

import { readdir } from "fs/promises";
const results = await readdir(__dirname, { recursive: true });
console.log(results); // ["a.js", "b/c.js" ...

在 Bun 儲存庫的 test/ 目錄中執行 readdir() 時,在 Linux 上,它比 Node.js v21.2.0 快 40 倍。

CommonJS 模組速度提升 1%

我們對 Bun 載入 CommonJS 模組的方式進行了一些改進,這使得它們的速度提高了 1%。

這是因為我們簡化了 CommonJS 的內部包裝函數。以前,我們的包裝函數是用 JavaScript 撰寫的,並在 Bun 的轉譯器的協助下完成。現在,這是在原生程式碼中完成的,因此速度更快,且使用的記憶體更少。

感謝 @paperclover 實作此功能!

TensorFlow.js 現在可以運作了

Bun 在 napi_create_string_utf16napi_create_arraybuffer 的實作中存在一個未解決的錯誤,該錯誤阻止了 TensorFlow.js 的運作。現在已修正此問題。

import * as tf from "@tensorflow/tfjs-node";

const model = tf.sequential({
  layers: [
    tf.layers.dense({ units: 128, activation: "relu", inputShape: [1] }),
    tf.layers.dense({ units: 3 }),
    tf.layers.softmax(),
  ],
});

model.compile({
  optimizer: "adam",
  loss: "categoricalCrossentropy",
  metrics: ["categoricalAccuracy"],
});

const result = model.predict(tf.tensor([0]));
if (Array.isArray(result)) throw new Error("Expected a single tensor");
const prediction = await result.data();

console.log(prediction); // Float32Array(3) [...]

支援 crypto.signcrypto.verify

Bun 現在支援 node:crypto 模組中的 signverify,可用於使用私鑰簽署和驗證資料。

import { sign, generateKeyPairSync } from "node:crypto";

const { privateKey } = generateKeyPairSync("ed25519");
const signature = sign(undefined, Buffer.from("foo"), privateKey);

console.log(signature); // Buffer(64) [ ... ]

感謝 @cirospaciari 實作此功能!

bun installpackage-lock.json v2 遷移

Bun 現在能夠遷移 lockfileVersion2package-lock.json 檔案。如果您要從 npm 遷移到 Bun,這會很有用。

❯ cat package-lock.json | jq .lockfileVersion
2

❯ bun install
bun install v1.0.15
[3.57ms] migrated lockfile from package-lock.json
 + svelte@4.0.0 (v4.2.8 available)

 21 packages installed [265.00ms]

bun install 重複工作區錯誤修正

感謝 @dylan-conway,已修正有關重複工作區套件的錯誤。

console.log 的尾隨逗號

Bun 現在包含使用 console.log 印出的物件的尾隨逗號。這使得從控制台複製貼上輸出、進行搜尋和取代變得更容易,並與像 prettier 這樣的程式碼格式化工具的做法相符。

console.log({ a: 1, b: 2, c: "3" });
{
   a: 1,
   b: 2,
-  c: "3"
+  c: "3",
}

感謝 @hustLer2k 實作此改進,並感謝 @ArnaudBarre 的建議!

已實作:console.timeLog

存在一個阻止 console.timeLog 運作的錯誤。感謝 @lqqyt2423,此問題已得到修正。

已修正:ReadableStream 的分離使用

存在一個當 ReadableStream 分離時會拋出 undefined is not an object 錯誤的錯誤,現在已修正此問題。

const { exited, stdout } = Bun.spawn(["echo", "hello"], {
  stdout: "pipe",
});

await exited;

// Since the process has exited, the `stdout` ReadableStream is detached.
console.log(await Bun.readableStreamToText(stdout));

// Before: error: undefined is not an object
// After: "hello\n"

已修正:fs.opendir() 具有 path 屬性

以前,fs.opendir() 沒有 path 屬性,現在感謝 @samfundev,此問題已得到修正。

import { test, expect } from "bun:test";
import { opendir } from "fs/promises";

test("opendir() has `path` property", () => {
  const result = await opendir(".");
  expect(result).toHaveProperty("path", ".");
});

已修正:重複的 Content-Range 標頭

存在一個 Bun 會傳送重複 Content-Range 標頭的錯誤。

const file = Bun.file("video.mp4");
const start = 0;
const end = 1000;
const range = file.slice(start, end);

return new Response(range, {
  status: 206,
  headers: {
    "Content-Range": `bytes ${start}-${end}/${file.size}`,
  },
});

Bun 無法正確偵測到已設定 Content-Range 標頭,並會傳送重複的標頭。

content-range: bytes 0-1000/10000
- content-range: bytes 0-1000/*

感謝 @libersoft-org,此問題已得到修正。

已修正:各種轉譯器錯誤

tsconfig.json 中的連字號鍵

存在一個如果 tsconfig.json 包含包含連字號的鍵,解析將會失敗的錯誤。感謝 @DontBreakAlex,此問題已得到修正。

tsconfig.json
{
  "key-with-hyphens": "value"
}

在 JSX 中擴展元素

存在一個將子元素擴展到父元素中會導致意外錯誤的錯誤。感謝 @rhyzx,此問題已得到修正。

輸入

const a = <h1>{...[123]}</h1>;

輸出

- error: Unexpected ...
+ var a = jsx_dev_runtime.jsxDEV("h1", {
+  children: [...[123]]
+ }, undefined, true, undefined, this);

已壓縮程式碼中的堆疊溢位

「堆疊溢位」不僅僅是一個網站。程式在堆疊中執行程式碼,如果堆疊使用過多記憶體,則會溢位。

Bun 的剖析器是一個遞迴下降剖析器,這表示它使用遞迴來剖析程式碼。這使得實作更簡單,但如果輸入原始碼二元運算式巢狀太深,則可能會導致堆疊溢位。此版本修正了該問題。

例如,如果您要撰寫一個包含如下內容的檔案

const chain =
  `globalThis.a = {};` +
  "\n" +
  `globalThis.a + globalThis.a +`.repeat(1000000) +
  `globalThis.a` +
  "\n";

await Bun.write("stack-overflow.js", chain);

然後使用 bun stack-overflow.js 執行它,則會由於堆疊溢位而在 bun 的轉譯器中崩潰。

感謝 @dylan-conway,此問題現已修正。

當使用 Bun 的執行環境以 Prettier 格式化 TypeScript 檔案時,此錯誤會顯現出來。

TypeScript 建構函式中的 override 關鍵字

存在一個 TypeScript 建構函式中的 override 關鍵字會導致意外錯誤的錯誤。

class Foo {}
class Bar extends Foo {}

class Fizz {
  constructor(readonly foo: Foo) {}
}

class Buzz extends Fizz {
  constructor(override bar: Bar) {
    super(foo);
  }
}

new Buzz(new Bar());
- 10 |    constructor(override foo: FooChild) {
-                             ^
- error: Expected ")" but found "foo"
-    at example.ts:10:23

已修正:bun build --compile 中動態載入的 CommonJS 模組

require 的引數無法靜態分析時,bun build 會將其保持原樣。

const dynamic = (name) => require(name);

console.log(dynamic("some-package"));

如果您將其與 bun build --compile 一起使用並以這種方式載入 commonjs 檔案,則轉譯器將產生具有語法錯誤的程式碼。此問題已得到修正。

感謝 25 位貢獻者!

我們總是很高興歡迎新的貢獻者加入 Bun。在此版本中,有 12 位新的貢獻者做出了他們的首次貢獻。

感謝 25 位讓此版本成為可能的貢獻者