Bun

Bun v1.1.9


Ashcon Partovi · 2024年5月22日

Bun v1.1.9 版本在此!此版本修正了 67 個錯誤(解決了 150 個 👍),並包含了針對 bun install 中工作區、bun build 中來源地圖、IPv6 和 VPN 連線問題、載入 UNC 路徑、連接點、符號連結以及 Windows 上 pnpm 的修正。fetch() 速度更快了。導入了新的 dns.prefetch() API。atob() 速度提升了 8 倍。buffer.toString('base64url') 速度提升了 5 倍。導入了 expect().toBeReturned() 比對器。我們也改進了 Node.js 相容性並新增了 Bun Shell 修正。還有,以及更多錯誤修正。

我們正在舊金山招募系統工程師,一同打造 JavaScript 的未來!

之前的版本

  • v1.1.8 修正了 54 個錯誤(解決了 184 個 👍)。支援 process.on("uncaughtException")process.on("unhandledRejection")JSON.parse 速度更快、node:zlib 中的 Brotli 支援、Bun API 中的 [Symbol.dispose]、修正了 Windows 上許多當機問題,以及許多其他錯誤修正。
  • v1.1.7 修正了 28 個錯誤(解決了 11 個 👍)。bun install 中使用 Glob 工作區名稱。expect.extends() equals 中非對稱比對器支援。bunx --version。修正了 JSX 轉譯、來源地圖、獨立可執行檔的跨平台編譯、bun shell、RegExp、Windows 上的 Worker,以及 Node.js 相容性改進的錯誤。
  • v1.1.0 Bundows。Windows 支援登場!

安裝 Bun

curl
npm
powershell
scoop
brew
docker
curl
curl -fsSL https://bun.dev.org.tw/install | bash
npm
npm install -g bun
powershell
powershell -c "irm bun.sh/install.ps1|iex"
scoop
scoop install 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 install 中工作區的修正。

如果您在使用工作區時遇到任何其他問題,請開啟 issue,我們會進行調查!

已修正:Workspace dependency "..." not found

我們修正了在工作區根目錄執行 bun add 時,會無法新增相依性的錯誤。

已修正:Script not found "..."

我們修正了在工作區的 script 中執行 bun run 時,無法正確解析其他 script 的錯誤。

package.json
{
  "name": "monorepo",
  "workspaces": ["foo"]
}
foo/package.json
{
  "name": "foo",
  "scripts": {
    "prepare": "bun run start",
    "start": "echo 'Starting...'"
  }
}

先前,bun add 會失敗並顯示

bun add bar
bun add
  ⚙️ bar [1/1] error: Script not found "start"

error: prepare script from "start" exited with 1

現在,bun add 將如預期般運作

bun add bar
bun add
  ⚙️ bar [1/1] success

已修正:回報 Removed 套件數量不正確

鎖定檔中的一個錯誤導致 Bun 在執行 bun install/add/remove 時,回報的移除套件數量不正確,有時甚至不一致。

已修正:將不存在的 --cwd 提供給 bun install 時會當機

已修正錯誤,bun install --cwd=i-dont-exist 原本會當機並顯示無用的錯誤訊息。

非常感謝 @dylan-conway 修正這些問題!

已修正:使用不同版本的 bun install 時會當機

我們修正了當使用較新版本的 Bun 執行 bun install,然後使用較舊版本的 Bun 執行 bun add 時,可能會發生的當機問題。

# Run `bun install` using a newer version of Bun
bun upgrade --canary
bun install

# Run `bun add` with an older version of Bun
bun upgrade --stable
bun add lodash

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

改進 IPv4、IPv6 和 VPN 連線能力

已修正長期存在的問題,Bun 原本會因為伺服器僅在 IPv4 或 IPv6 上廣播而無法連線。

當您的電腦連線到雙堆疊網路時,DNS 查詢可能會同時傳回 IPv4 和 IPv6 位址。當使用 VPN 或連線到 localhost 時,經常會發生這種情況。

先前,Bun 無法處理這種情況,並且會連線 socket 失敗。

對於以下輸入程式碼

using server = Bun.serve({
  hostname: "127.0.0.1",
  port: 0,
  fetch() { return new Response("👀😊👀"); }
});

const response = await fetch("https://127.0.0.1:" + server.port);
console.log(await response.text());

Bun 現在輸出

❯ bun happy-eyeballs.js # New
👀😊👀

先前,Bun 會輸出

❯ bun-1.1.8 happy-eyeballs.js # Old
ConnectionRefused: Unable to connect. Is the computer able to access the url?
 path: "https://127.0.0.1:64836/"

Bun v1.1.8 (macOS arm64)

現在,Bun 實作了 IETF 標準 "Happy Eyeballs" 演算法的子集。如果 Bun 收到單一主機名稱的多個 IP 位址,它會嘗試同時連線到每個位址,然後選取第一個成功連線。

感謝 @gvilums 實作此功能!

fetch() 取得更快速的 DNS 解析

Bun 現在在 fetch() 請求中支援非同步 DNS 解析。

這表示在您的作業系統未使用 DNS 快取,或者您要將請求傳送到許多不同的子網域的情況下,fetch() 的速度會更快。Bun 會將這些 DNS 記錄快取在記憶體中,因此每次發出 fetch() 請求時,都不會重新解析它們。

感謝 @gvilums 實作此功能!

非同步 DNS 和快取

Bun 現在會自動將最多 255 個 DNS 記錄快取在記憶體中,最長 30 秒。這減少了在短時間內對相同網域發出多個請求時的延遲。

這適用於

  • fetch() 請求
  • node:http 請求
  • node:https 請求
  • Bun.connect 請求
  • node:net 請求
  • node:tls 請求

此快取適用於進行中的 DNS 請求和已完成的 DNS 請求。這表示如果您同時向 example.com 發出 50 個 fetch() 請求,它只會解析 DNS 記錄一次。如果您等待 31 秒並發出另一個請求,它會再次向作業系統詢問 DNS 記錄。

這對於 Docker 容器和作業系統可能沒有有效 DNS 快取的環境非常有用。

新增:dns.prefetch() API

您也可以使用新的 dns.prefetch() API 在需要 DNS 記錄之前預先擷取它們。如果您想在啟動時預熱 DNS 快取,這會很有用。

import { dns } from "bun";

// ...on startup
dns.prefetch("example.com");

// ...later on
await fetch("https://example.com/");

這將預先擷取 example.com 的 DNS 記錄,並使其可用於 fetch() 請求。

新增:dns.getCacheStats() API

若要觀察 DNS 快取,您可以使用新的 dns.getCacheStats() API。

import { dns } from "bun";

console.log(dns.getCacheStats());
//
// {
//   cacheHitsCompleted: 0,
//   cacheHitsInflight: 0,
//   cacheMisses: 0,
//   size: 0,
//   errors: 0,
//   totalCount: 0,
// }
//

await fetch("https://example.com/");

console.log(dns.getCacheStats());
// {
//   cacheHitsCompleted: 0,
//   cacheHitsInflight: 0,
//   cacheMisses: 1,
//   size: 1,
//   errors: 0,
//   totalCount: 1,
// }

atob() 速度提升 8 倍

感謝 @lemiresimdutf 程式庫,Bun 中的 atob() 現在速度快了 8 倍。

新增:為 Web streams 新增 bytes() API

Bun 現在支援 Web streams 上的 bytes() 屬性,它會傳回 stream 資料的 Uint8Array。這適用於 RequestResponseBlobBun.file() 物件。

const response = await fetch("https://example.com/");
const bytes = await response.bytes();
console.log(bytes); // Uint8Array(1256) [ 60, 33, ... ]

bytes() 是最近新增至 fetch() 標準的功能。

感謝 @nektro 實作此功能!

新增:bun --no-clear-screen --watch

您現在可以使用 bun --no-clear-screen 在 Bun 以 --watch--hot 模式執行時,停用清除螢幕。如果您想在檔案重新載入後保留終端機歷史記錄,這會很有用。

您也可以使用 BUN_CONFIG_NO_CLEAR_TERMINAL_ON_RELOAD 環境變數來設定此選項。

BUN_CONFIG_NO_CLEAR_TERMINAL_ON_RELOAD=1 bun --watch src/index.ts

新增:expect().toHaveReturned() 比對器

Bun 現在支援 Jest 實作的 expect().toHaveReturned()expect().toHaveReturnedTimes(n) 比對器。

import { expect } from "bun:test";

test("expect().toHaveReturned()", () => {
  const fn = jest.fn(() => "foo");
  fn();
  expect(fn).toHaveReturned();
  fn();
  expect(fn).toHaveReturnedTimes(2);
});

感謝 @nektro 新增此功能。

來源地圖改進

Bun 現在在 bun build --target=bun --sourcemap 之後載入來源地圖

當您使用 bun build --target=bun --sourcemap 進行生產建置時,Bun 現在會在執行階段載入來源地圖,並使用它們將堆疊追蹤對應回原始原始碼。

❯ bun out/sourca.js
1 | function mySource(hello: string) {
2 |   throw new Error("woopsie!!");
            ^
error: woopsie!!
      at mySource (/Users/jarred/Desktop/sourca.ts:2:9)
      at /Users/jarred/Desktop/sourca.ts:5:1

Bun v1.1.9 (macOS arm64)

先前

❯ bun-1.1.8 out/sourca.js
1 | // @bun
2 | // sourca.ts
3 | var mySource = function(hello) {
4 |   throw new Error("woopsie!!");
            ^
error: woopsie!!
      at mySource (/Users/jarred/Desktop/out/sourca.js:4:9)
      at /Users/jarred/Desktop/out/sourca.js:6:1

Bun v1.1.8 (macOS arm64)

感謝 @paperclover

已修正:來源地圖偏移 24 位元組錯誤

在執行階段,Bun 會為來源地圖的中繼資料保留內部來源地圖緩衝區的前 24 個位元組。

此行為原本僅在執行階段發生,但卻錯誤地在 bun build 中也啟用了。這導致在使用 bun build --target=bun --sourcemap 時,來源地圖會偏移 24 個位元組。

感謝 @paperclover,此問題現已修正。

Bundows 改進

在模組解析器程式碼中,Bun 認為連接點和符號連結是規則檔案,而不是目錄。這在使用 Windows 上的 monorepos 或搭配 Bun 使用 pnpm 套件管理員時,造成了一些問題。

以下程式碼現在可以在 Windows 上如預期般運作

import { version } from "lodash";
console.log(version); // 4.17.21

先前

error: Unexpected reading 'C:\\abcd\\node_modules\\lodash'

感謝 @paperclover

已修正:在 Windows 中載入 UNC 路徑

當 UNC 路徑用於 Bun 的各個部分(例如 require("\\\\wsl.local\\path"))時,會因為關於在 Windows 中儲存 posix 路徑的判斷提示失敗而失敗。此判斷提示失敗是 Bun 的內部錯誤,現已修正。

此錯誤影響了

  • bun install
  • bun run <file.ts>
  • bun <file.ts>

以及更多。

感謝 @paperclover

Node.js 相容性改進

已修正:msw 現在似乎可以運作了

我們修正了 node:http 中的一個錯誤,其中 statusCode 屬性是唯讀的,這可能會在嘗試設定狀態碼時造成錯誤。這是由套件(例如 msw)完成的,現在這些套件可以在 Bun 中運作。

import axios from "axios";
import { http, HttpResponse } from "msw";
import { setupServer } from "msw/node";

const server = setupServer(
  ...[
    http.get("https://example.com/", () => {
      return HttpResponse.json({ msw: "works!" });
    }),
  ],
);
server.listen();
const response = await axios.get("https://example.com/");
console.log(response.data.msw); // "works!"

感謝 @nektro,此問題現已修正。

新增:為 node:crypto 新增 shake128shake256 演算法

Bun 現在在 node:crypto 中支援 shake128shake256 演算法。

import { createHash } from "node:crypto";

const hash = createHash("shake256");
hash.update("hello");
console.log(hash.digest());

感謝 @nektro,此問題現已修正。

buffer.toString("base64url") 速度提升 5 倍

再次感謝 simdutf 程式庫,Bun 中的 toString("base64url") 現在速度快了 5 倍。

已修正:http.Agent 在沒有 new 的情況下無法運作

我們修正了一個錯誤,您原本無法在不使用 new 關鍵字的情況下建構 http.Agent。某些較舊的 Node.js API(例如 http.Agent)在 JavaScript 引入類別之前就已存在,因此它們不需要 new 關鍵字。

import { Agent } from "node:http";

const agent = Agent();

感謝 @nektro,此問題現已修正。

已修正:newListener 事件以正確的順序發出

我們修正了一個問題,newListener 事件原本是以錯誤的順序發出,這可能會造成某些程式庫發生問題。先前,newListener 事件是在監聽器新增之後發出,但現在它是在監聽器新增之前發出。

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

test("newListener event is emitted in correct order", () => {
  const stream = new Stream();
  const cb = mock((event) => {
    expect(stream.listenerCount(event)).toBe(0);
  });
  stream.on("newListener", cb);
  stream.on("foo", () => {});
  expect(cb).toHaveBeenCalled();
});

已修正:改進 net.connect() 中的引數解析

我們修正了各種錯誤,其中 net.connect() 中的引數解析無法比照 Node.js 的行為。例如,當提供 porthost,或僅提供 options 物件時,porthost 引數會被忽略。

import { connect } from "node:net";

const socket = connect({
  port: 443,
  host: "localhost",
  rejectUnauthorized: false,
});

socket.on("secureConnect", () => {
  console.log("Connected!");
  socket.end();
});

已修正:Promisified child_process.execFile() 傳回錯誤的值

我們修正了一個錯誤,當使用 util.promisify() 函數時,execFile() 函數未傳回正確的值。

import { execFile } from "node:child_process";
import util from "node:util";

const execFileAsync = util.promisify(execFile);
const result = await execFileAsync("ls");

console.log(result);
// Before: 'src\n'
// After: { stdout: 'src\n', stderr: '' }

已修正:搭配長路徑使用 which 時會當機

我們修正了一個錯誤,當給定長路徑時,Bun.which() 會當機。現在它不會當機,而是會擲回錯誤:bin path is too long

import { which } from "bun";

which("a".repeat(100000));
// Before: <crash>
// After: <error: bin path is too long>

感謝 @zackradisic 修正此問題。

已修正:將 debuglog 別名為 debug

我們修正了一個錯誤,util.debuglog() 原本未別名為 util.debug()。感謝 @gaurishhs 修正此問題。

import { debuglog } from "node:util";

debuglog("foo");

已修正:fs.Dirent 現在具有 pathparentPath 屬性

我們修正了一個錯誤,path 屬性和新的 parentPath 屬性原本未在 fs.Dirent 物件上設定。當搭配 fs.readdir() 使用 fs.Dirent 物件時,可能會造成問題。

import { readdirSync } from "node:fs";

readdirSync("src").forEach((dirent) => {
  console.log(dirent.path);
  // Before: undefined
  // After: 'src'
});

感謝 @nektro,此問題現已修正。

已修正:must be a number or a Date

我們修正了一個錯誤,某些將引數解譯為倍精度的 API,會因為不正確的整數強制型轉而擲回錯誤。

import { utimesSync } from "node:fs";

const atime = Math.floor(Date.now() / 1000);
const mtime = Math.floor(Date.now() / 1000);
utimesSync("hello.txt", atime, mtime);

// Before: Error: Argument must be a number or a Date
// After: <no error>

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

Bun Shell 改進

逸出更多字元

我們修正了一些錯誤,特殊字元(例如 |&;)在使用於 shell 中時需要逸出,但原本並未逸出。

bun shell 'echo "Hello, world!" | grep "world"'
# Hello, world!
bun shell 'echo "Hello, world!" | grep "world"'
# Hello, world!

感謝 @zackradisic 修正這些問題。

Bun shell 中的波浪號展開

Bun shell 現在支援波浪號展開,這表示您可以使用 ~(又稱波浪號)來參照您的主目錄。例如,~/Documents 將展開為 $HOME/Documents 的路徑。

所有平台(包括 Windows)都支援此功能。

echo ~
# /Users/bun

echo ~/Documents
# /Users/bun/Documents

感謝 @zackradisic 實作此功能。

已修正:cd 會傳回錯誤的結束代碼

我們修正了一個錯誤,在 Bun shell 中執行的 cd 會傳回錯誤的結束代碼。當變更目錄時發生錯誤時,就會發生這種情況。

cd /not/a/directory
echo $?
0

已修正:在 argv0 中貼上非常大的輸入時會當機

已修正將過大的輸入貼到 Bun shell 的第一個引數中會當機的錯誤。

bun exec ${"a".repeat(100_000_000)}

此錯誤是由 which 中缺少邊界檢查所造成。作業系統針對檔案路徑的最大長度為 512、1024 或 4096 個位元組,當超過該限制時,Bun 就會當機。

感謝 @zackradisic,此問題現已修正。

感謝 16 位貢獻者!