Bun 是一個速度極快的 JavaScript 執行時環境、打包器、轉譯器和套件管理器 — 功能All-in-One。如果您錯過了,以下是 Bun 最近的一些變更。
此版本修正了 40 個錯誤(解決了 194 個 👍 表情符號回應)。在 Bun 中導入 & 嵌入 sqlite 資料庫、資源管理('using' TC39 stage3)支援、為 Node.js 建置時的打包器改進、peer dependency 解析的錯誤修正、semver 錯誤修正、Linux 上 TCP 速度提升 4%、Node.js 相容性改進等等。
先前的版本
v1.0.22
修正了 29 個錯誤(解決了 118 個 👍 表情符號回應)、修正了 Vercel 上的bun install
問題、新增了performance.mark()
API、為額外的管道新增了child_process
支援、加快了Buffer.concat
速度、新增了toBeEmptyObject
和toContainKeys
匹配器、修正了使用 emoji 的console.table
寬度,以及worker_threads
中對argv
和execArgv
選項的支援,並支援fetch
中的 Brotli。v1.0.21
- 修正了 33 個錯誤(解決了 80 個 👍 表情符號回應)。console.table()
支援。Bun.write
、Bun.file 和 bun:sqlite 使用更少的記憶體。使用 FormData 上傳大型檔案使用更少的記憶體。bun:sqlite 錯誤訊息變得更詳細。修正了 node:fs 錯誤中的記憶體洩漏。Node.js 相容性改進,以及許多崩潰問題已修正。v1.0.20
- 降低了fs.readlink
、fs.readFile
、fs.writeFile
、fs.stat
和HTMLRewriter
中的記憶體使用量。修正了 setTimeout 在 Linux 上導致 CPU 使用率過高的回歸問題。HTMLRewriter.transform
現在支援字串和ArrayBuffer
。fs.writeFile()
和fs.readFile()
現在支援hex
&base64
編碼。Bun.spawn
顯示了程序使用的 CPU 和記憶體量。
安裝 Bun
curl -fsSL https://bun.dev.org.tw/install | bash
npm install -g bun
brew tap oven-sh/bun
brew install bun
docker pull oven/bun
docker run --rm --init --ulimit memlock=-1:-1 oven/bun
升級 Bun
bun upgrade
在 Bun 中導入 sqlite 資料庫
您現在可以在 Bun 中導入 sqlite 資料庫。這使得在您的專案中開始使用 sqlite 更加簡單。
import db from './my.db' with {type: "sqlite"};
const {id} = db
.query("SELECT id FROM users LIMIT 1")
.get();
console.log(id); // 1
這也適用於 bun build --compile
,這非常適合將小型資料庫部署到生產環境。這表示您可以將整個應用程式建置成單一可執行檔,然後將該可執行檔與您的資料庫檔案一起部署,並在開發與生產環境中使用不同的資料庫檔案。
bun build --compile ./my-app.ts
# => ./my-app
# On your remote machine:
./my-app
在內部,這大致等同於
import { Database } from "bun:sqlite";
const db = new Database("./my.db");
將 sqlite 資料庫嵌入到單一檔案可執行檔中
如果您的應用程式受益於嵌入到可執行檔本身,也支援此功能。要嵌入 sqlite 資料庫,請在導入屬性中傳遞 embed: "true"
。
import db from './my.db'
with {
type: "sqlite",
// Embed the database into the executable
embed: "true"
};
const {id} = db
.query("SELECT id FROM users LIMIT 1")
.get();
console.log(id); // 1
現在您可以使用 bun build --compile
將您的應用程式建置成單一可執行檔,資料庫將會嵌入到可執行檔本身中。
bun build --compile ./my-app.ts
mv my.db /tmp/my.db # This isn't used in my-app anymore
./my-app # Since the database is embedded, it will work without the database file
您也可以將嵌入式 sqlite 資料庫與 bun build --target=bun
一起使用。在這種情況下,它會將資料庫檔案複製到輸出目錄,並從那裡導入。
bun build --target=bun ./my-app.ts --outdir=out
./out/my-app # Since the database is copied into the output directory, it will work without the database file
將 SQLite 升級至 v3.45.0
SQLite 3.45.0 新增了 JSONB 支援,這使得儲存和讀取 JSON 資料更快。我們已升級 Bun(在 Linux 上)以使用此版本的 SQLite。
使用 bun build --compile
嵌入 .node 檔案
您現在可以使用 bun build --compile
嵌入 NAPI (n-api) addons .node
檔案。這對於捆綁原生 Node.js 模組非常有用,例如 @anpi-rs/canvas
。
import { promises } from "fs";
import { join } from "path";
import { createCanvas } from "@napi-rs/canvas";
const canvas = createCanvas(300, 320);
const ctx = canvas.getContext("2d");
ctx.lineWidth = 10;
ctx.strokeStyle = "#03a9f4";
ctx.fillStyle = "#03a9f4";
// Wall
ctx.strokeRect(75, 140, 150, 110);
// Door
ctx.fillRect(130, 190, 40, 60);
// Roof
ctx.beginPath();
ctx.moveTo(50, 140);
ctx.lineTo(150, 60);
ctx.lineTo(250, 140);
ctx.closePath();
ctx.stroke();
const pngData = await canvas.encode("png"); // JPEG, AVIF and WebP are also supported
await promises.writeFile(join(__dirname, "simple.png"), pngData);
bun build --compile canvas.ts
然後您可以執行可執行檔
rm -rf node_modules # Not needed anymore
./canvas # => simple.png
針對 bun build --target=node
的錯誤修正
我們已修正 bun build --target=node
中的許多錯誤。
現在支援要求 Node.js 內建模組,例如 fs
和 path
。
var { promises } = require("fs");
var { join } = require("path");
promises.readFile(join(__dirname, "data.txt"));
先前,此程式碼在執行時會失敗
bun build --target=node ./my-app.ts --outfile=app.mjs
node ./app.mjs
TypeError: (intermediate value).require is not a function
at __require (file:///app.mjs:2:22)
at file:///app.mjs:7:20
at ModuleJob.run (node:internal/modules/esm/module_job:218:25)
at async ModuleLoader.import (node:internal/modules/esm/loader:329:24)
at async loadESM (node:internal/process/esm_loader:28:7)
at async handleMainPromise (node:internal/modules/run_main:113:12)
現在它可以運作了
bun build --target=node ./my-app.ts --outfile=app.mjs
node ./app.mjs
還修正了許多其他錯誤,包括
- 遺失了
__require
函式的無效程式碼消除
現在支援資源管理
我們已實作 資源管理 的打包器支援,該提案目前處於 TC39 stage 3 階段,並在 TypeScript 中可用。這是一個新功能,可讓您管理檔案控制代碼、資料庫連線和網路 socket 等資源。它類似於 C# 中的 using
關鍵字。
// in an async function:
async function * g() {
await using handle = acquireFileHandle(); // async-block-scoped critical resource
} // async cleanup
// in a block in an async context:
{
await using obj = g(); // block-scoped declaration
const r = await obj.next();
} // calls finally blocks in `g` and awaits result
您可以在 Bun 中以及使用 bun build
在執行時使用此功能。我們已實作一個 polyfill,它將在 Bun、Node.js 和網頁瀏覽器中運作(基於 esbuild 的 polyfill)。
我們也在 JavaScriptCore 中實作了部分支援,包括 Symbol.dispose
、Symbol.asyncDispose
和 SuppressedError
。我們尚未在 JavaScriptCore 中實作剖析器或 AST 支援,因為目前我們可以為此使用轉譯器。
感謝 @paperclover 實作此功能。
bun remove missing-pkg
不再發生錯誤
先前,如果未安裝套件,bun remove missing-pkg
會發生錯誤。現在它不會發生錯誤。這使行為與 npm
一致。
感謝 @kaioduarte 的貢獻。
Linux 上 TCP socket 速度提升 4%
在 Linux 上,透過 TCP socket 發送大量資料現在速度提升了 4%。我們減少了 event loop 滴答計時所涉及的系統呼叫次數。
之前(為了簡潔起見,移除了一些數字)
timerfd_settime(ufd: 8, utmr: 0x456) = 0
epoll_ctl(epfd: 7, op: ADD, fd: 8, event: 0x567) = -1 EEXIST (File exists)
epoll_wait(epfd: 7, events: 0x567, maxevents: 1024, timeout: 4294967295) = 1
recvfrom(fd: 14, ubuf: 0x123, size: 524288) = 524288
timerfd_settime(ufd: 8, utmr: 0x456) = 0
epoll_ctl(epfd: 7, op: ADD, fd: 8, event: 0x567) = -1 EEXIST (File exists)
epoll_wait(epfd: 7, events: 0x567, maxevents: 1024, timeout: 4294967295) = 1
recvfrom(fd: 14, ubuf: 0x123, size: 524288) = 524288
timerfd_settime(ufd: 8, utmr: 0x456) = 0
epoll_ctl(epfd: 7, op: ADD, fd: 8, event: 0x567) = -1 EEXIST (File exists)
epoll_wait(epfd: 7, events: 0x567, maxevents: 1024, timeout: 4294967295) = 1
recvfrom(fd: 14, ubuf: 0x123, size: 524288) = 524288
timerfd_settime(ufd: 8, utmr: 0x456) = 0
epoll_ctl(epfd: 7, op: ADD, fd: 8, event: 0x567) = -1 EEXIST (File exists)
epoll_wait(epfd: 7, events: 0x567, maxevents: 1024, timeout: 4294967295) = 2
recvfrom(fd: 14, ubuf: 0x123, size: 524288) = 524288
sendto(fd: 15, buff: 0x7f9093666dc0, len: 1016697408, flags: NOSIGNAL) = 1571592
timerfd_settime(ufd: 8, utmr: 0x456) = 0
epoll_ctl(epfd: 7, op: ADD, fd: 8, event: 0x567) = -1 EEXIST (File exists)
epoll_wait(epfd: 7, events: 0x567, maxevents: 1024, timeout: 4294967295) = 1
recvfrom(fd: 14, ubuf: 0x123, size: 524288) = 524288
timerfd_settime(ufd: 8, utmr: 0x456) = 0
epoll_ctl(epfd: 7, op: ADD, fd: 8, event: 0x567) = -1 EEXIST (File exists)
epoll_wait(epfd: 7, events: 0x567, maxevents: 1024, timeout: 4294967295) = 1
recvfrom(fd: 14, ubuf: 0x123, size: 524288) = 524288
timerfd_settime(ufd: 8, utmr: 0x456) = 0
epoll_ctl(epfd: 7, op: ADD, fd: 8, event: 0x567) = -1 EEXIST (File exists)
epoll_wait(epfd: 7, events: 0x567, maxevents: 1024, timeout: 4294967295) = 1
recvfrom(fd: 14, ubuf: 0x123, size: 524288) = 524288
timerfd_settime(ufd: 8, utmr: 0x456) = 0
epoll_ctl(epfd: 7, op: ADD, fd: 8, event: 0x567) = -1 EEXIST (File exists)
epoll_wait(epfd: 7, events: 0x567, maxevents: 1024, timeout: 4294967295) = 2
之後(為了簡潔起見,移除了一些數字)
epoll_wait(epfd: 7, events: 0x559c3cae78a0, maxevents: 1024, timeout: 4294967295) = 1
recvfrom(fd: 14, ubuf: 0x123, size: 524288, flags: DONTWAIT) = 524288
recvfrom(fd: 14, ubuf: 0x123, size: 524288, flags: DONTWAIT) = 524288
recvfrom(fd: 14, ubuf: 0x123, size: 524288, flags: DONTWAIT) = 524288
recvfrom(fd: 14, ubuf: 0x123, size: 524288, flags: DONTWAIT) = 524288
recvfrom(fd: 14, ubuf: 0x123, size: 524288, flags: DONTWAIT) = 524288
recvfrom(fd: 14, ubuf: 0x123, size: 524288, flags: DONTWAIT) = 524288
recvfrom(fd: 14, ubuf: 0x123, size: 524288, flags: DONTWAIT) = 524288
recvfrom(fd: 14, ubuf: 0x123, size: 524288, flags: DONTWAIT) = 524288
recvfrom(fd: 14, ubuf: 0x123, size: 524288, flags: DONTWAIT) = 127574
epoll_wait(epfd: 7, events: 0x559c3cae78a0, maxevents: 1024, timeout: 4294967295) = 1
sendto(fd: 15, buff: 0x456, len: 72939772, flags: DONTWAIT|NOSIGNAL) = 4321878
epoll_wait(epfd: 7, events: 0x559c3cae78a0, maxevents: 1024, timeout: 4294967295) = 1
recvfrom(fd: 14, ubuf: 0x123, size: 524288, flags: DONTWAIT) = 524288
recvfrom(fd: 14, ubuf: 0x123, size: 524288, flags: DONTWAIT) = 524288
recvfrom(fd: 14, ubuf: 0x123, size: 524288, flags: DONTWAIT) = 524288
recvfrom(fd: 14, ubuf: 0x123, size: 524288, flags: DONTWAIT) = 524288
recvfrom(fd: 14, ubuf: 0x123, size: 524288, flags: DONTWAIT) = 524288
recvfrom(fd: 14, ubuf: 0x123, size: 524288, flags: DONTWAIT) = 524288
recvfrom(fd: 14, ubuf: 0x123, size: 524288, flags: DONTWAIT) = 524288
recvfrom(fd: 14, ubuf: 0x123, size: 524288, flags: DONTWAIT) = 524288
recvfrom(fd: 14, ubuf: 0x123, size: 524288, flags: DONTWAIT) = 127574
epoll_wait(epfd: 7, events: 0x559c3cae78a0, maxevents: 1024, timeout: 4294967295) = 1
sendto(fd: 15, buff: 0x456, len: 68617894, flags: DONTWAIT|NOSIGNAL) = 4321878
epoll_wait(epfd: 7, events: 0x559c3cae78a0, maxevents: 1024, timeout: 4294967295) = 1
recvfrom(fd: 14, ubuf: 0x123, size: 524288, flags: DONTWAIT) = 524288
recvfrom(fd: 14, ubuf: 0x123, size: 524288, flags: DONTWAIT) = 524288
將 HTTP 伺服器標頭限制從 50 提高到 100
我們已將 uWebSockets fork 升級到最新版本,隨之而來的是預設 HTTP 伺服器標頭限制的變更。它從 50 提高到 100。
import.meta.dirname
和 import.meta.filename
支援
現在支援 import.meta.dirname
和 import.meta.filename
。它們是 import.meta.dir
和 import.meta.path
的別名。這對於與 Node.js 相容性非常有用,Node.js 最近發布了對這些功能的支援。
// /path/to/file.js
console.log(import.meta.dirname); // /path/to
console.log(import.meta.filename); // /path/to/file.js
fs/promises
中的 FileHandle
支援
先前,Bun 的 fs/promises
不支援 FileHandle
物件。現在它支援了。
import { promises } from "node:fs";
const filehandle = await promises.open("file.txt", "r");
const stat = await filehandle.stat();
await filehandle.close();
已實作 events.on
events.on
傳回一個 AsyncIterator
,可讓您在事件發生時循環遍歷事件。
有些套件依賴此 API,現在 Bun 中支援了它。
import { on, EventEmitter } from "node:events";
const ee = new EventEmitter();
// Emit later on
process.nextTick(() => {
ee.emit("foo", "bar");
ee.emit("foo", 42);
});
for await (const event of on(ee, "foo")) {
// The execution of this inner block is synchronous and it
// processes one event at a time (even with await). Do not use
// if concurrent execution is required.
console.log(event); // prints ['bar'] [42]
}
// Unreachable here
感謝 @nektro 實作此功能。
process.binding('tty_wrap')
支援
現在支援 process.binding('tty_wrap')
。某些套件(例如 readline-sync
)依賴此 API,即使 bun 的 tty
實作與 Node.js 不同,我們也實作了此 Node.js 內部綁定 API,以支援依賴它的套件。
process.binding("tty_wrap").TTY;
已實作 fs.fdatasync
現在已實作 fs.fdatasync
,感謝 @nektro。
已修正:來自 zlibBufferSync 錯誤的「Not a string or buffer」
我們的 zlibBufferSync polyfill 未正確檢查非 Buffers,例如 Uint8Array。此問題已修正,感謝 @Electroid。
已修正:Bun.spawn() 與 IPC 中的檔案描述器洩漏
當使用 IPC 時,Bun.spawn() 會忽略關閉第二個 socket 的檔案描述器,導致檔案描述器洩漏。此問題已修正。
已修正:以與 node 一致的方式處理 node:url
中遺失的 .host
以下程式碼片段在 Bun 中的行為與 Node.js 中的行為不同
const url = require("url");
console.log(url.parse("http://"));
此問題已修正,感謝 @kaioduarte。
已提升 Node.js 版本
process.versions.node
中報告的 Node.js 版本已提升至 v21.6.0,這是發布時的最新版本。
已修正:peer dependency 升級解析
假設您安裝了 drizzle-cli
和 drizzle-orm
。drizzle-cli
對 drizzle-orm@^1.0.0
有 peer dependency。您將 drizzle-orm
升級到 1.0.1
,您期望 drizzle-cli
現在使用 drizzle-orm@1.0.1
。先前,這不會發生 -- 但現在會發生了,感謝 @eriklangille。
已修正:semver 比較錯誤
與 node-semver
相比,以下測試在 Bun.semver
中會表現出不同的行為
import { test, expect } from "bun:test";
import { semver } from "bun";
test("semver with multiple tags work properly", () => {
expect(semver.satisfies("3.4.5", ">=3.3.0-beta.1 <3.4.0-beta.3")).toBeFalse();
});
此錯誤影響了 bun install
和 Bun.semver
。
此問題已修正,感謝 @Electroid。
已修正:WebSocket
在連線失敗時拋出錯誤
以下程式碼在 Bun 中會拋出錯誤,但在網頁瀏覽器中則不會
const ws = new WebSocket("wss://apsodkapsodkpo.com:3000");
// => Uncaught Error:
相反地,它應該發出錯誤事件。此問題已修正,感謝 @LukasKastern。
已修正:無效的 stdio 選項 "null"
以下程式碼在 Bun 與 Node.js 中的行為會有所不同
import cp from "child_process";
cp.spawnSync("ls", ["-lagh"], { stdio: [null, "inherit", null] });
此問題已修正,感謝 @Electroid。
已修正:Bun.serve() 有時會重複印出例外狀況
已修正以下程式碼會重複印出例外狀況的錯誤
Bun.serve({
port: 3000,
fetch: async () => {
await Bun.sleep(1);
throw new Error("A");
return new Response("A");
},
error() {
return new Response("Handled");
},
});
此問題已修正,感謝 @LukasKastern。
已修正:ServerWebSocket idleTimeout
已修正一個 event loop 錯誤,該錯誤可能導致 ServerWebSocket
timeout 花費的時間比預期的要長得多。
感謝 @LukasKastern 修正此問題。
已修正:macOS 大型檔案上傳有時會失敗
已修正一個影響 macOS 的錯誤,其中一次接收到的最終大型檔案上傳區塊(> 512 KB)有時會遺失最終區塊的一部分。此錯誤是由於 KQueue 事件的處理不正確所致。與 Linux (epoll) 不同,kqueue 可能同時發送 socket hangup 和 readable 事件,當資料大於讀取緩衝區時,我們未正確處理這種情況。
感謝 12 位促成此版本發布的貢獻者!
- @aayushbtw
- @cirospaciari
- @dylan-conway
- @Electroid
- @eriklangille
- @gvilums
- @hugo-syn
- @Jarred-Sumner
- @kaioduarte
- @LukasKastern
- @nektro
- @paperclover
完整變更日誌:https://github.com/oven-sh/bun/compare/bun-v1.0.22...bun-v1.0.23