Bun

Bun v1.1.39


Jarred Sumner · 2024 年 12 月 17 日

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

此版本修正了 61 個錯誤(解決了 99 個 👍)。它引入了 bun.lock,一種新的文字型鎖定檔格式。它使快取 bun install 速度提升了 30%。它增加了對 fetch() 請求主體串流的支援。它將 Node.js 的 string_decoderpunycodequerystring 提升到 100% Node.js 相容性。它為 Bun.build() 新增了原生插件 API,以及與 napi.rs 集成的 Rust crate。它實作了 expect().toMatchInlineSnapshot()。它增加了更精確的堆積快照。它減少了 WebSocket 伺服器記憶體用量。它升級了 WebKit,帶來了 Error.isError、更快的 String.prototype.at 等功能。

安裝 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.lock 是 bun 的新文字型鎖定檔

在此版本中,我們新增了一種新的可讀鎖定檔格式:bun.lock。這使 git diff 和合併衝突更好地工作,並解鎖了 Dependabot、Renovate、Turbo prune 等工具。

要試用它,請執行

bun install --save-text-lockfile

我們撰寫了一篇關於 bun.lock 的部落格文章

在此過程中,我們還將快取 bun install 速度提升了 30%。特別是,當先前已安裝套件,且需要驗證每個已安裝套件的版本以確保它們沒有變更時(在本地執行 bun install 時的常見情境)。

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

fetch() 請求主體串流

您現在可以將 fetch() Request 主體作為串流傳送。先前,只有 Response 主體可以在用戶端中串流(Bun.serve() 始終支援兩者)。

這在用戶端上使用 HTTP/1.1 Transfer-Encoding: chunked。當內容長度事先已知時,它會改用 Content-Length

感謝 @cirospaciari 實作此功能!

Node.js 相容性

已修正:postgres 套件連線錯誤時的凍結

兩個不同的錯誤可能導致 postgres 套件凍結

  • 當傳遞給 net.connectAbortSignal 發出訊號時,Bun 在 net.Socket 上呼叫 destroy 而不是發出 abort 事件。此問題已修正,感謝 @cirospaciari
  • 在來自升級 TLS 連線的 net.Socket 上呼叫 pause 可能會導致底層 socket 暫停,而不僅僅是 node:stream。

感謝 @cirospaciari 進行修正!

100% 的 Node 的 string_decoder 測試通過

我們修正了 "string_decoder" 模組處理不完整多位元組字元的邊緣案例,使其與 Node.js 一致,現在所有 Node.js 的 string_decoder 測試都通過了。

感謝 @paperclover

100% 的 Node 的 punycode 測試通過

Bun 現在與 Node.js punycode 實作完全相容。

感謝 @snoglobe

100% 的 Node 的 querystring 測試通過

我們修正了 "querystring" 模組中的一些邊緣案例,使其與 Node.js 一致。

感謝 @pfgithub

改進的 node:path 測試覆蓋率

除了 Node 的 path 測試現在都通過了(新加入的方法 path.matchesGlob 的測試尚未實作)。

感謝 @DonIsaac

改進的 node:os 測試覆蓋率

os 模組中僅剩一個測試失敗。

已修正:crypto.createHash(alg, options) 缺少選項

感謝 @heimskr

已修正:napi_wrap

napi_wrap 函式現在的行為與 Node.js 中相同。

感謝 @190n

已修正:node:zlib 中的錯誤代碼

Bun 對於 zlib 錯誤未拋出與 Node.js 完全相同的 error.code 屬性。此問題已修正,感謝 @nektro

已修正:在物件中使用 flag 參數的 fs.readFileSync

以下程式碼會錯誤地忽略 flag 參數

import fs from "fs";
fs.readFileSync("data.txt", { encoding: "utf8", flag: "w+" });

此問題已修正,感謝 @pfgithub

Bundler 改進

onBeforeParse - Rust/C/Zig 插件 API

此版本為 Bun.build() 引入了新的零複製原生插件 API。與我們的 JavaScript 插件 API 不同,這會在 Bun 的線程池內、緊接在剖析之前執行,無需複製原始程式碼、無需經過字串轉換,且幾乎沒有額外負荷。

bun add -g @napi-rs/cli
napi new
cargo add bun-native-plugin

從那裡,您可以實作 onBeforeParse hook

lib.rs
use bun_native_plugin::{define_bun_plugin, OnBeforeParse, bun, Result, anyhow, BunLoader};
use napi_derive::napi;

/// Define the plugin and its name
define_bun_plugin!("replace-foo-with-bar");

/// Here we'll implement `onBeforeParse` with code that replaces all occurrences of
/// `foo` with `bar`.
///
/// We use the #[bun] macro to generate some of the boilerplate code.
///
/// The argument of the function (`handle: &mut OnBeforeParse`) tells
/// the macro that this function implements the `onBeforeParse` hook.
#[bun]
pub fn replace_foo_with_bar(handle: &mut OnBeforeParse) -> Result<()> {
  // Fetch the input source code.
  let input_source_code = handle.input_source_code()?;

  // Get the Loader for the file
  let loader = handle.output_loader();


  let output_source_code = input_source_code.replace("foo", "bar");

  handle.set_output_source_code(output_source_code, BunLoader::BUN_LOADER_JSX);

  Ok(())
}

Bun.build() 中使用

import myNativeAddon from "./my-native-addon";
Bun.build({
  entrypoints: ["./app.tsx"],
  plugins: [
    {
      name: "my-plugin",

      setup(build) {
        build.onBeforeParse(
          {
            namespace: "file",
            filter: "**/*.tsx",
          },
          {
            napiModule: myNativeAddon,
            symbol: "replace_foo_with_bar",
            // external: myNativeAddon.getSharedState()
          },
        );
      },
    },
  ],
});

此插件 API 旨在與 NAPI 附加元件一起使用,以便現有程式庫可以新增這些附加元件,而無需過多考慮如何初始化它們。

感謝 @zackradisic 實作此功能!

CSS 剖析器改進

修正了多個 CSS 剖析器和列印器邊緣案例,感謝 @zackradisic

Bun.build() 中注入環境變數

此版本增加了對在 Bun.build()bun build 中注入環境變數的支援

API
CLI
API
// In Bun.build()
await Bun.build({
  entrypoints: ['./app.tsx'],
  outdir: './out',

  // All environment variables starting with "PUBLIC_"
  // will be injected in the build as process.env.PUBLIC_*
  env: "PUBLIC_*",

  // eg
  // console.log(process.env.PUBLIC_FOO); => "bar"
});

await Bun.build({
  entrypoints: ['./app.tsx'],
  outdir: './out',

  // Inject all environment variables in the build when used.
  env: "inline",
});
CLI
bun build ./app.tsx --env='PUBLIC_*'
bun build ./app.tsx --env=inline

內嵌快照測試

此版本在 bun:test 中實作了 expect().toMatchInlineSnapshot()。內嵌快照直接儲存在測試檔案中,並使用 bun test -u 更新。

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

test("format user profile", () => {
  const result = formatProfile({
    name: "Jarred",
    role: "admin",
  });

  // Snapshot stored directly in test file
  expect(result).toMatchInlineSnapshot(`{
    "displayName": "Jarred",
    "permissions": ["admin"],
    "createdAt": "2024-12-13T00:00:00.000Z"
  }`);
});

test("validate input", () => {
  // Match error messages
  expect(() => validateInput({})).toThrowErrorMatchingInlineSnapshot(`
      "Invalid input:
       - Missing required field: name"
    `);
});

使用以下命令更新快照

bun test -u

我們也新增了

  • expect().toThrowErrorMatchingSnapshot()
  • expect().toThrowErrorMatchingInlineSnapshot()

感謝 @pfgithub 實作此功能!

記憶體分析器改進

我們已實作在 JavaScriptCore <> Zig 類別綁定產生器中報告來自原生程式碼的記憶體用量的支援。這使堆積快照報告更準確

  • BlobRequestResponse 現在報告的大小包括它們的主體、URL 和底層結構,而不僅僅是 JavaScript 包裝器物件大小。
  • URLSearchParamsHeaders 現在報告 C++ 類別的大小以及每個字串的大小。
  • ServerWebSocketWebSocket 現在報告底層結構和緩衝資料的大小
  • FormData 的大小現在包括每個字串的大小、底層 C++ 類別以及其中包含的任何檔案的大小。
  • Subprocess (Bun.spawn) 報告底層結構和緩衝資料的大小
  • Bun.file().writer() 報告緩衝資料的大小
  • 所有其他在 Zig 中實作的類別都會報告其內部結構的大小

這不會影響垃圾收集器或執行階段的記憶體用量,但這確實讓您更容易偵錯使用 Bun 的應用程式中佔用記憶體的內容。

新增:"bun:jsc" 中的 estimateShallowMemoryUsageOf

"bun:jsc" 中的 estimateShallowMemoryUsageOf 函式估計 JavaScript 值的記憶體用量,但不包括其子項、屬性或內部插槽的記憶體用量。

import { estimateShallowMemoryUsageOf } from "bun:jsc";

const obj = { foo: "bar" };
const usage = estimateShallowMemoryUsageOf(obj);
console.log(usage); // => 16

const buffer = Buffer.alloc(1024 * 1024);
estimateShallowMemoryUsageOf(buffer);
// => 1048624

WebSocket 伺服器記憶體減少

此版本減少了 Bun 內建 WebSocket 伺服器 Bun.serve() 的記憶體用量,特別是對於開啟和關閉許多 WebSocket 伺服器連線的長時間執行程序。

在這個在迴圈中開啟和關閉 200,000 個 WebSocket 連線的微基準測試中,峰值 RSS 下降了 15%。

微基準測試

依賴項升級

我們已更新多個依賴項

  • c-ares 至 v1.34.3
  • lshpack 至 v2.3.3
  • libdeflate 至 v1.22
  • SQLite 至 3.470.200
  • BoringSSL

WebKit 升級

此版本升級了來自上游 WebKit 的 JavaScriptCore 內部版本,其中包括迴圈展開、Intl.PluralRulesIntl.NumberFormat 等方面的改進。

Error.isError

如果 object 確實是 Error 實例,則 Error.isError(object) 方法會傳回 true

Error.isError(new Error()); // => true
Error.isError({}); // => false
Error.isError(new Error("foo")); // => true
Error.isError(new Error("foo").message); // => false
Error.isError({ [Symbol.toStringTag]: "Error" }); // => false
Error.isError(new (class Error {})()); // => false
Error.isError({ constructor: function Error() {} }); // => false

Error.isError 是 stage3 TC39 提案.

更快的 String.prototype.at

錯誤修正

已修正:VSCode 中偵錯器的不穩定性

程序有時會在偵錯器可以附加之前退出。如果您仍然遇到此問題,請告知我們!

此問題已修正,感謝 @RiskyMH

已修正:POSIX 訊號處理中的崩潰

在註冊訊號處理常式(例如 process.on('SIGINT')process.on('SIGTERM'))並重複接收訊號後可能發生的崩潰或死鎖已修正。

已修正:Bun Shell 和 Bun.spawn 中罕見的檔案描述符洩漏

Bun Shell 和 Bun.spawn 中理論上的檔案描述符洩漏已修正。這可能會在舊版 Linux 中於其他線程上產生程序時發生。

事實證明,我們也並非總是正確地將檔案描述符標記為非阻塞,此問題也已修正。

已修正:使用 "Connection: close" 的 fetch 重定向處理

fetch() 使用 "Connection": "close" 重新導向時,Bun 可能會報告連線錯誤,而不是按預期處理重新導向。感謝 @cirospaciari 進行修正!

已修正:在工作區中使用無效 package.json 執行 bun install 時崩潰

在具有無效 package.json 檔案的工作區上執行 bun install 時可能發生的崩潰已修正。

已修正:Bun.build() 插件中罕見的崩潰

Bun.build() 插件中可能發生的罕見崩潰已修正。如果插件中使用 "file" 加載器,則可能會發生這種情況。

已修正:在 TypeScript 檔案中包含大量註解時 Bun.build() 崩潰

在某些情況下,當 TypeScript 檔案中存在大量註解時可能發生的崩潰已修正,感謝 @dylan-conway

已修正:全域 .npmrc 未使用驗證

驗證配置僅針對每個 .npmrc 檔案應用,然後被捨棄。因此,在全域 .npmrc 中定義的驗證不會應用於專案 .npmrc 中的登錄檔。

此問題已修正,感謝 @robertshuford

感謝 20 位貢獻者!