Bun

Bun v1.0.31


Ashcon Partovi · 2024 年 3 月 14 日

Bun 是一個速度極快的 JavaScript 執行環境、打包器、轉譯器和套件管理器 — 功能All in one。

Bun v1.0.31 修復了 54 個錯誤(解決了 113 個 👍 反應),引入了 bun --print<stdin> | bun run -bun add --trust、具有 Unix socket 的 fetch(),修正了 macOS 二進制檔案大小回歸問題,修復了舊版 Linux 上 spawn() 中 CPU 使用率過高的錯誤,新增了 util.styleText、Node.js 相容性改進、bun install 錯誤修復以及 bunx 錯誤修復。

先前的版本

  • v1.0.30 修復了 27 個錯誤(解決了 103 個 👍 反應),修正了 Bun.serve() 的 8 倍效能回歸問題,為 bun build 和 Bun 的執行環境新增了 --conditions 標誌,在 Bun 的測試執行器中新增了對 expect.assertions()expect.hasAssertions() 的支援,修復了崩潰問題並改進了 Node.js 相容性。
  • v1.0.29 修復了 8 個錯誤。Bun.stringWidth(a) 是 'string-width' 這個熱門套件的極速替代方案,速度快約 6,756 倍。bunx 更頻繁地檢查更新。在 bun:test 中新增了 expect().toBeOneOf()。修復了影響 Prisma 的記憶體洩漏問題。Shell 現在支援進階重新導向,例如 '2>&1'、'&>'。提升了 bunx、bun install、WebSocket client 和 Bun Shell 的可靠性
  • v1.0.28 修復了 6 個錯誤(解決了 26 個 👍 反應)。修復了影響 Prisma 和 Astro、node:eventsnode:readlinenode:http2 的錯誤。修復了 Bun Shell 中涉及 stdin 重新導向的錯誤,以及 bun:testtest.eachdescribe.only 的錯誤。

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

現在您可以使用 bun --print 來評估提供的程式碼,並使用 console.log 印出結果。它與 node --print 相同,只是它支援最上層 await、ESM、CommonJS、TypeScript 和 JSX。

bun --print 'fetch("https://api.github.com/repos/bigskysoftware/htmx").then(r => r.json())'

Bun 也會等待懸而未決的 promise,因此您不需要在程式碼中附加 await

bun --print 'fetch("https://api.github.com/repos/bigskysoftware/htmx").then(r => r.json())'

作為參考,以下是使用 node --print 的等效輸出

node --print 'await fetch("https://api.github.com/repos/bigskysoftware/htmx").then(r => r.json())'

我們實作此功能的原因是許多 npm 套件都有呼叫 node --printpostinstall 腳本,我們不希望在您未安裝 Node.js 的情況下,bun install 發生任何問題。

感謝 @dylan-conway 實作此功能!

新增功能:從 stdin 執行程式碼

現在您可以使用 bun run - 將 stdin 導入 Bun。這對於從檔案或腳本執行程式碼非常有用。

echo 'console.log("Hello")' | bun run -
cat file.js | bun run -
bun run - < file.js

與 Bun 中通常一樣,這適用於最上層 await、ESM、CommonJS、TypeScript 和 JSX。

感謝 @nektro 新增此功能!

新增功能:bun add --trust <套件>

我們改進了 Bun 處理 package.json 中 trustedDependencies 的方式。預設情況下,Bun 不會為不受信任的套件執行 postinstall 腳本。這是一項安全功能,可防止惡意程式碼在您的機器上執行。

當您首次新增套件時,如果該套件有 postinstall 腳本未執行,Bun 會告知您。

bun add v1.0.31
Saved lockfile

installed @biomejs/biome@1.6.1 with binaries:
- biome

1 package installed [55.00ms]

Blocked 1 postinstall. Run `bun pm untrusted` for details.

新增功能:bun pm untrusted

如果您想查看哪些腳本被封鎖,可以執行 bun pm untrusted

bun pm untrusted v1.0.31

./node_modules/@biomejs/biome @1.6.1
» [postinstall]: node scripts/postinstall.js

These dependencies had their lifecycle scripts blocked during install.

If you trust them and wish to run their scripts, use `bun pm trust`.

新增功能:bun pm trust

如果您信任該套件,可以執行 bun pm trust [套件]。如果您想信任每個套件,也可以執行 bun pm trust --all

bun pm trust v1.0.31

./node_modules/@biomejs/biome @1.6.1
[postinstall]: node scripts/postinstall.js

1 script ran across 1 package [71.00ms]

最受歡迎的套件預設已受信任。您可以執行 bun pm default-trusted 來查看受信任套件的列表。

如果您已經知道要信任某個依賴項,可以使用 bun add --trust [套件] 來新增它。這會將該套件及其遞移依賴項新增到您的 trustedDependencies 列表中,因此您無需為該套件執行 bun pm trust

{
  "dependencies": {
    "@biomejs/biome": "1.6.1"
  },
   "trustedDependencies": [
     "@biomejs/biome"
   ]
}

感謝 @dylan-conway 改進此功能!

新增功能:fetch() 中的 Unix socket

Bun 現在支援透過 Unix socket 發送 HTTP 請求。

const response = await fetch("https://127.0.0.1/info", {
  // a file path to a Unix socket
  unix: "/var/run/docker.sock",
});

const { ID } = await response.json();
console.log("Docker ID:", ID);

這表示您可以將 fetch() 請求發送到使用 HTTP 通訊但使用 Unix socket 通訊的服務,例如 Docker daemon。

此功能適用於 fetchnode:http

通常,unix socket 的檔案路徑限制約為 108 個字元,但在 Linux 上,我們新增了一個變通方法來支援更長的路徑。

抽象網域 socket 在 fetch()

我們也新增了對稱為「抽象網域 socket」的晦澀 Linux 功能的支援。

與 Unix socket 不同,抽象 socket 不存在於檔案系統上,並且在兩端都關閉時會自動清除。它們在一個程序可能不與父程序共享檔案系統權限的環境中很有用。

import { $, serve } from "bun";

const unix = "\0much-abstract-very-domain";
const server = serve({
  unix,

  fetch(req) {
    return new Response("hello from abstract socket!");
  },
});

// abstract domain sockets don't exist in the filesystem
// and they don't run on a port
await $`rm -rf ${unix.slice(1)}`;

// but we can still make requests to them
await $`curl --abstract-unix-socket ${unix.slice(1)} http://anything/hello`;
await fetch(`http://a:1234/b`, { unix });

server.stop();

抽象 socket 適用於 fetch()Bun.serve()node:http 中的 socketPath

已修復:舊版 Linux 核心上的 Bun.spawn() 100% CPU 使用率錯誤

正確產生程序並監控它們何時退出非常複雜。

對於 Linux,Bun 有兩種實作方式來監控產生的程序何時退出

  1. 使用 pidfd_open(2),它已完全新增到 v5.10(2020 年 12 月)的 Linux 核心中。pidfd 是監控程序何時退出的最有效方法,因為它可以與 epoll 或 io_uring 一起使用,就像其他檔案描述符一樣。
  2. 使用 SIGCHLD 的回退實作,所有 Linux 核心都支援此實作

在虛擬碼中,回退實作過去看起來像這樣

// in a dedicated thread:
while (true) {
  for (const pid of pids) {
    if (wait4(pid, WNOHANG, ...) === pid) {
      pids.delete(pid);
      tellBunThatProcessExited(pid);
    }
  }

  sleepUntilNewPidsOrAnyProcessExits(eventfd, signalfd);
}

有一個錯誤,當應該休眠時,sleepUntilNewPidsOrAnyProcessExits 的等效項會立即解析,導致 Bun 在此執行緒上消耗 100% CPU。

此錯誤有兩個原因

  • 我們未在底層 eventfd 上呼叫 read(),導致它始終準備好讀取(因此永遠不會休眠)
  • SIGCHLD 的訊號處理常式未正確安裝,導致執行緒永遠不會收到訊號

第一個錯誤阻止了第二個錯誤被注意到。由於它從未休眠,因此永遠不需要喚醒來處理訊號。

為了防止將來發生回歸,我們新增了一個測試,用於檢查產生 sleep infinity 並在 1 秒後終止子程序時的 CPU 使用率。如果 CPU 時間超過 1/2 秒,則測試失敗。

已修復:舊版 Linux 核心上 Bun.spawn() 中遺失 resourceUsage 統計資訊

Bun 支援追蹤產生的程序消耗多少記憶體、CPU 時間和更多統計資訊。在舊版 Linux 核心上的回退實作中,我們無法追蹤這些統計資訊。現在我們可以了。

const proc = Bun.spawn({
  cmd: ["sleep", "1"],
});

await proc.exited;

console.log(proc.resourceUsage);

// Before: all 0s

已修復:macOS 上 bun 可執行檔案大小回歸問題

在 Bun v1.0.28(上個月)中,我們新增了 Bun.stringWidth,它使用了 ICU 國際化程式庫中的更多函式。Bun 建置程序中的一個錯誤導致將整個 ICU 程式庫靜態連結到 macOS 上的 Bun 可執行檔案中。這使 Bun 可執行檔案的大小增加了 32.7 MB。

macOS 上的大小Bun 版本
47.8 MBBun v1.0.31
80.4 MBBun v1.0.30
80.4 MBBun v1.0.29
47.7 MBBun v1.0.28

此回歸問題已修復,我們將新增監控以防止再次發生。

Node.js 相容性改進

新增功能:util.styleText()

Node.js 最近新增了 util.styleText() API,可讓您使用 ANSI 逸出碼設定文字樣式。我們已將其新增到 Bun 中。

import { styleText } from "node:util";

console.log(styleText("red", "This is a failure!"));
console.log(styleText("yellow", "This is a warning!"));
console.log(styleText("green", "This is a success!"));

上面的程式碼將印出以下輸出

This is a failure!
This is a warning!
This is a success!

感謝 @paperclover 致力於此功能!

新增功能:node:http 中的 socketPath 選項

我們新增了對 node:httpsocketPath 選項的支援。這可讓您使用 Unix socket 或抽象網域 socket 發送 HTTP 請求。

import { request } from "node:http";

request(
  {
    socketPath: "/var/run/docker.sock",
    path: "/info",
  },
  (res) => {
    let data = "";
    res.on("data", (chunk) => (data += chunk));
    res.on("end", () => console.log(JSON.parse(data)));
  },
);

新增功能:domainToASCIIdomainToUnicode

我們新增了對 url.domainToASCIIurl.domainToUnicode API 的支援。這些是舊版 Node.js API,可用於在網域名稱的 ASCII 和 Unicode 表示法之間進行轉換。

import { domainToASCII, domainToUnicode } from "node:url";

console.log(domainToASCII("www.🍕.com")); // => www.xn--xj8h.com
console.log(domainToUnicode("www.xn--xj8h.com")); // => www.🍕.com

感謝 @nektro 新增這些 API!

已修復:child_process 可能會提早逾時

我們修復了一個錯誤,其中在定義逾時時,產生的 child_process 會提早逾時。這不會影響未定義逾時的程式碼。

import { spawn } from "node:child_process";

const start = performance.now();
await new Promise((resolve, reject) => {
  const child = spawn("sleep", ["1000"], { timeout: 1000 });
  child.on("error", reject);
  child.on("exit", resolve);
});
const duration = performance.now() - start;

console.log(duration); // ~10ms instead of ~1000ms

感謝 @Electroid 修復此錯誤!

已修復:node:http 中未觸發 socket 事件

我們修復了一個錯誤,其中當用戶端連線到伺服器時,node:http 中不會觸發 socket 事件。

import { request } from "http";

const request = request("https://127.0.0.1:8080");
await new Promise((resolve, reject) => {
  request.on("error", reject);
  request.on("socket", function onSocket(socket) {
    request.destroy();
    console.log(socket);
    resolve();
  });
});

已修復:使用無效引數呼叫 Bun.serve() 時崩潰

我們修復了一個錯誤,其中在某些情況下,如果 Bun.serve() 選項無效,Bun.serve() 會崩潰。它應該改為擲回錯誤。

感謝 @paperclover 修復此錯誤!

已修復:在測試外部使用 expect() 時崩潰

我們修復了一個錯誤,其中如果在使用非同步解析器的 expect() 在測試外部呼叫,則會崩潰。

import { expect } from "bun:test";

expect(Bun.sleep(1000)).resolves.toBeTruthy();

感謝 @paperclover 修復此錯誤!

已修復:fetch() 收到無效的 Location 標頭時可能崩潰

如果 fetch() 收到的 Location 標頭指向無效的 URL,Bun 可能會崩潰。現在已修復此問題。

感謝 @nektro 修復此錯誤!

已實作:URLSearchParams.deleteURLSearchParams.has 的第二個引數

URLSearchParams.prototype.deleteURLSearchParams.prototype.has 方法支援第二個 "value" 引數。這可讓您刪除或檢查 URLSearchParams 中的特定值。

const params = new URLSearchParams("a=1&a=2&b=3");
params.delete("a", 1);
params.delete("b", undefined);
params + ""; // => actual='' expected='a=2'

此功能是 Web Platform 最近新增的功能,我們已將其新增到 Bun 中。

已修復:當 NODE_ENV=test 時,.test.env 未載入

我們發現一個錯誤,當 NODE_ENV=test 且存在 .production.env 檔案時,.test.env 檔案不會載入。感謝 @nektro,此問題已修復。

已修復:未緩衝錯誤到 stderr

我們發現一個效能錯誤,其中 Bun 在將堆疊追蹤印到 stderr 時不會緩衝寫入。現在已修復此問題。

bun install 錯誤修復

已修復:bunx 的各種錯誤

我們也修復了 bunx 的各種錯誤

  • bunx --bun 有時會忽略 --bun 標誌,具體取決於標誌順序。
  • bun x --bun 會將 --bun 標誌轉發到腳本,而不是 Bun。
  • bun --bun create 會崩潰且無法正常運作。
  • bunx <github> 有時會掛起。

感謝 @paperclover 修復這些錯誤!

已修復:node-gyp 有時不會執行

已修復一個錯誤,其中當 package.json 同時具有 postinstall 腳本和 bindings.gyp 檔案時,bun install 不會執行生命週期腳本。此錯誤影響了 node-pty 和其他套件。

感謝 @dylan-conway 修復此錯誤!

已修復:bun pm cache rm 現在會移除 bunx 快取

先前,bun pm cache rm 不會移除 bunxnode_modules 快取。此問題已修復。

內部:I/O 架構變更

在此版本中,我們重新實作了 Bun 如何

  • 產生程序
  • 讀取檔案(串流)
  • 寫入檔案(串流)

這些新的實作在防止讀取/寫入封鎖主執行緒方面做得更好,而無需付出將所有 I/O 移至執行緒池的成本。峰值記憶體使用量應該更低,尤其是在使用 node:streamnode:fs 時。

如果您在使用 Bun.file(path).stream()、fs.createReadStreamfs.createWriteStreamBun.spawn() 時發現任何錯誤,請回報。

Windows 支援即將推出

我們即將在 Bun v1.1 中推出 Windows 支援。一旦 Windows 版 Bun 通過 95% 的 Bun 測試套件,我們將宣布發布日期。

感謝 18 位貢獻者!

完整變更記錄