Bun

Bun v1.0.16


Jarred Sumner · 2023 年 12 月 10 日

Bun v1.0.16 修復了 49 個錯誤(解決了 38 個 👍 表情符號)。針對 Bun.file 重寫了 IO,Bun.write 現在會自動建立父目錄(如果不存在),expect.extend 在 preload 內運作正常,napi_create_object 速度提升 2.5 倍,修復了模組解析影響 Astro v4 和 p-limit 的錯誤,console.log 錯誤修復,以及更多。

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 模組解析,並使錯誤訊息更易於閱讀。
  • v1.0.15 - 修復了 23 個錯誤(解決了 117 個 👍 表情符號),tsc 啟動速度提升 2 倍。穩定的 WebSocket 客戶端、語法突顯的錯誤、更清晰的堆疊追蹤,使用 expect.extend() + 其他 expect 比對器新增自訂測試比對器。

要安裝 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

Bun.write 現在會建立父目錄(如果不存在)

先前,如果目錄不存在,Bun.write 會拋出錯誤。現在,如果目錄不存在,它會建立目錄。

// script.ts
import { write } from "bun";
await write("i/dont/exist.txt", "Hello, world!");

現在,如果 i/dont 不存在,將會建立它。

$ bun script.ts

先前,它會拋出錯誤

1 | import { write } from "bun";
2 | await write("i/dont/exist.txt", "heyyy");
          ^
ENOENT: No such file or directory
   path: "i/dont/exist.txt"
 syscall: "open"
   errno: -2

若要在父目錄不存在時拋出錯誤:createPath: false

可以透過將 createPath: false 傳遞給 Bun.write 來停用此行為。

// script.ts
import { write } from "bun";
await write("i/dont/exist.txt", "Hello, world!", { createPath: false });

現在,如果目錄不存在,它將拋出錯誤。

$ bun script.ts
1 | import { write } from "bun";
2 | await write("i/dont/exist.txt", "heyyy", { createPath: false });
          ^
ENOENT: No such file or directory
   path: "i/dont/exist.txt"
 syscall: "open"
   errno: -2

針對 Bun.file 重寫 IO

Linux 上 Bun.file 的內部結構已重新編寫為使用 epoll 而不是 io_uring,這提高了在 Docker 和雲端主機(如 Vercel 和 Google Cloud Run)中的相容性。

這解決了使用 Bun.file 時與 EBADF 相關的 11 個問題

此變更的動機是 Docker 在 2023 年 11 月預設停用了 io_uring。

Bun.write() 和 Bun.file().text() 在並行負載下速度提升 3 倍

在此過程中,我們使用 Bun.file() 和 Bun.write() 提高了並行讀取和寫入檔案的效能。

讀取

❯ N=100 hyperfine "bun ./read.js" "bun-1.0.15 read.js" --warmup=20
Benchmark 1: bun ./read.js
  Time (mean ± σ):      40.9 ms ±   2.9 ms    [User: 27.6 ms, System: 181.7 ms]
  Range (min … max):    37.1 ms …  48.3 ms    73 runs

Benchmark 2: bun-1.0.15 read.js
  Time (mean ± σ):     134.5 ms ±  19.0 ms    [User: 24.8 ms, System: 82.5 ms]
  Range (min … max):    98.8 ms … 161.2 ms    23 runs

Summary
  bun ./read.js ran
    3.29 ± 0.52 times faster than bun-1.0.15 read.js

寫入

❯ N=100 hyperfine "bun ./write.js" "bun-1.0.15 write.js" --warmup=20
Benchmark 1: bun ./write.js
  Time (mean ± σ):      35.0 ms ±   8.0 ms    [User: 13.0 ms, System: 364.6 ms]
  Range (min … max):    25.7 ms …  71.5 ms    83 runs

Benchmark 2: bun-1.0.15 write.js
  Time (mean ± σ):     134.7 ms ±  13.6 ms    [User: 23.6 ms, System: 193.3 ms]
  Range (min … max):   117.8 ms … 171.2 ms    23 runs

Summary
  bun ./write.js ran
    3.85 ± 0.96 times faster than bun-1.0.15 write.js

read.js

const promises = new Array(parseInt(process.env.N || "20"));
for (let i = 0; i < promises.length; i++) {
  promises[i] = Bun.file("out.log." + i).text();
}

await Promise.all(promises);

write.js

const toPipe = new Blob(["abc".repeat(1_000_000)]);

const promises = new Array(parseInt(process.env.N || "20"));
for (let i = 0; i < promises.length; i++) {
  promises[i] = Bun.write("out.log." + i, toPipe);
}

await Promise.all(promises);

已修復:Bun.stdin.text() 讀取不完整

先前,Bun.stdin.text() 有時不會傳回來自 stdin 的所有資料。

import { stdin } from "bun";
console.log(await stdin.text());
$ echo "Hello, world!" | bun script.ts

先前,有時它會不列印任何內容


現在,它會列印完整輸入

Hello, world!

napi_create_object 速度提升 2.5 倍

expectexpect.extend--preload

先前,expect 的作用域限定於個別測試檔案,如果在共用檔案中呼叫 expect.extend,則會中斷。現在已修復此問題,您現在可以使用包含自訂比對器的共用檔案,例如。

custom-matchers.ts
import { expect } from "bun:test";

// Based off of from Jest's example documentation
// https://jest.dev.org.tw/docs/expect#expectextendmatchers
function toBeWithinRange(actual, floor, ceiling) {
  const pass = actual >= floor && actual <= ceiling;

  return {
    pass,
    message: () =>
      "expected " +
      this.utils.printReceived(actual) +
      " to " +
      (pass ? "not " : "") +
      "be within range " +
      this.utils.printExpected(`${floor} - ${ceiling}`),
  };
}

expect.extend({
  toBeWithinRange,
});

然後設定 bunfig.toml 以在測試期間載入它們,

bunfig.toml
test.preload = "./custom-matchers.ts"

並執行 bun test

example.test.ts
import { expect, test } from "bun:test";

test("example", () => {
  expect(1234).toBeWithinRange(1000, 2000);
});
example.test.ts:
-TypeError: expect.extend is not a function. (In 'expect.extend({
-  toBeWithinRange
-})', 'expect.extend' is undefined)
-      at /custom-matchers.ts:21:1
+✓ example [1.31ms]

您也可以在 test 回呼之外使用 expect()

-error: This function can only be used in a test.
-    at /custom-matchers.ts:21:1

已修復:package.json 中帶有 "imports" 的模組解析錯誤

套件可以在其 package.json 檔案中宣告僅限內部使用的匯入清單,Bun 將使用它來解析匯入。這對於避免向使用者公開內部檔案很有用。

{
  "name": "my-package",
  "imports": {
    "my-package": "./dist/index.js"
  }
}

這些匯入以 # 為前綴。

import { foo } from "#my-package";

這也可以用於重寫來自其他套件的匯入。例如,如果您想將所有來自 async-hooks 的匯入重寫為 #async-hooks,您可以執行

{
  "name": "my-package",
  "imports": {
    "async-hooks": "async-hooks"
  }
}

然後,它將載入 async-hooks 套件

import { foo } from "#async-hooks";

但,有一個錯誤導致以這種方式別名套件在 Bun 中無法運作。這已修復。

此錯誤影響的著名套件

  • Astro v4
  • p-limit

已修復:稀疏陣列在 console.log 中顯示為 undefined

先前,稀疏陣列在 console.log 中會顯示為 undefined。這已修復。

console.log([1, , 3]);

現在

[ 1, empty item, 3 ]

先前

[1, undefined, 3];

已修復:createRequire() 中包含空格的 file: URL

先前,當傳遞包含未逸出空格的 file: URL 時,createRequire() 會拋出錯誤。這已修復。

import { createRequire } from "node:module";
const require = createRequire(import.meta.url);
require("file:///Users/jarred/My%20Folder/script.js");

感謝 @paperclover 修復此問題。

已修復:console.log 有時會省略 length 屬性

有時,console.log 會省略物件的 length 屬性。這已修復。

console.log({ length: 1, a: 1 });

先前,它會列印

{
  a: 1
}

現在,它會列印

{
  length: 1,
  a: 1,
}

已修復:轉譯器在 spread 後面 JSX key 的不一致性

以下輸入在 Bun 中的行為會與 TypeScript 中不同

const obj = {};
console.log(<div {...obj} key="after" />, <div key="before" {...obj} />);

這已修復,感謝 @rhyzx

已修復:console.log("%d", value) 錯誤

已修復導致 %d 無法正確處理非數值的錯誤。

console.log("%d", "1");

先前,它會列印

0

現在,它會列印

1

已修復:語法突顯器範本字串處理

在 Bun 的先前版本中,我們為錯誤訊息中顯示的程式碼新增了語法突顯功能,但它有一個錯誤,導致某些範本字串使突顯器崩潰。這已修復。

已修復:jest.fn(jest.fn()) 崩潰

已修復呼叫 jest.fn(jest.fn(() => {})) 時發生的崩潰,感謝 @Hanassagi

感謝 16 位貢獻者!

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

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