我們正在舊金山招募系統工程師,一同打造 JavaScript 的未來!
此版本修正了 41 個錯誤(解決了 595 個 👍)。它包含了 node:http2 伺服器和 gRPC 伺服器支援、bun install 中的 ca 和 cafile 支援、Bun.inspect.table、bun build --drop、Promise.try、Buffer.copyBytesFrom、可迭代的 SQLite 查詢、迭代器輔助方法,以及多項 Node.js 相容性改進和錯誤修正。
安裝 Bun
curl -fsSL https://bun.dev.org.tw/install | bash
npm install -g bun
powershell -c "irm bun.sh/install.ps1|iex"
scoop install 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
支援 node:http2
伺服器和 gRPC
在此 Bun 版本中,我們新增了對我們最受歡迎的功能請求的支援:HTTP2 伺服器和 gRPC 支援。Bun 自 v1.0.13 以來一直支援 node:http2 用戶端,但直到現在我們才支援伺服器。
在 Bun 中,node:http2
的執行速度比 Node v23 快 2.4 倍。
在下一個 Bun 版本中
— Bun (@bunjavascript) October 17, 2024
已實作 node:http2 伺服器支援。對於相同的程式碼
Bun v1.1.31:128,879 req/s(快 2.4 倍)
Node v23.0.0:52,785 req/s pic.twitter.com/SIM0I0Td4T
您可以使用 node:http2
API 來建立 HTTP2 伺服器。
import { createSecureServer } from "node:http2";
import { readFileSync } from "node:fs";
const server = createSecureServer({
key: readFileSync("privkey.pem"),
cert: readFileSync("cert.pem"),
});
server.on("error", (error) => console.error(error));
server.on("stream", (stream, headers) => {
stream.respond({
":status": 200,
"content-type": "text/html; charset=utf-8",
});
stream.end("<h1>Hello from Bun!</h1>");
});
server.listen(3000);
有了 HTTP2 支援,您也可以將 gRPC 與 @grpc/grpc-js
等套件一起使用。
const grpc = require("@grpc/grpc-js");
const protoLoader = require("@grpc/proto-loader");
const packageDefinition = protoLoader.loadSync("benchmark.proto", {});
const proto = grpc.loadPackageDefinition(packageDefinition).benchmark;
const fs = require("fs");
function ping(call, callback) {
callback(null, { message: "Hello, World" });
}
function main() {
const server = new grpc.Server();
server.addService(proto.BenchmarkService.service, { ping: ping });
const tls =
!!process.env.TLS &&
(process.env.TLS === "1" || process.env.TLS === "true");
const port = process.env.PORT || 50051;
const host = process.env.HOST || "localhost";
let credentials;
if (tls) {
const ca = fs.readFileSync("./cert.pem");
const key = fs.readFileSync("./key.pem");
const cert = fs.readFileSync("./cert.pem");
credentials = grpc.ServerCredentials.createSsl(ca, [
{ private_key: key, cert_chain: cert },
]);
} else {
credentials = grpc.ServerCredentials.createInsecure();
}
server.bindAsync(`${host}:${port}`, credentials, () => {
console.log(
`Server running at ${tls ? "https" : "http"}://${host}:${port}`,
);
});
}
main();
bun install
中的 CA 支援
您現在可以為 bun install
設定 CA 憑證。當您需要從公司的私有註冊表安裝套件,或者想要使用自簽憑證時,這非常有用。
[install]
# The CA certificate as a string
ca = "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----"
# A path to a CA certificate file. The file can contain multiple certificates.
cafile = "path/to/cafile"
如果您不想變更您的 bunfig.toml
檔案,您也可以使用 --ca
和 --cafile
旗標。
bun install --cafile=/path/to/cafile
bun install --ca="..."
如果您正在使用現有的 .npmrc
檔案,您也可以在那裡設定 CA 憑證。
cafile=/path/to/cafile
ca="..."
bun build --drop
您可以使用 --drop
從您的 JavaScript 捆綁包中移除函式呼叫。如果您想要從您的生產捆綁包中移除偵錯程式碼,這非常有用。例如,如果您傳遞 --drop=console
,則所有對 console.log
的呼叫都將從您的程式碼中移除。
await Bun.build({
entrypoints: ["./index.tsx"],
outdir: "./out",
drop: ["console", "anyIdentifier.or.propertyAccess"],
})
bun build ./index.tsx --outdir ./out --drop=console --drop=anyIdentifier.or.propertyAccess
bun --drop
--drop
也可在執行時使用。
for (let i = 0; i < 3; i++) {
console.log("hello called!");
}
在使用 --drop
之前,您會看到 hello called!
3 次。
bun ./index.ts
hello called!
hello called!
hello called!
使用 --drop=console
,console.log
呼叫會被捨棄,而您不再看到 hello called!
。
bun --drop=console ./index.ts
# no output
Bun.inspect.table()
您現在可以使用 Bun.inspect.table
將表格資料格式化為字串。它類似於 console.table
,但它會傳回字串,而不是列印到主控台。
console.log(
Bun.inspect.table([
{ a: 1, b: 2, c: 3 },
{ a: 4, b: 5, c: 6 },
{ a: 7, b: 8, c: 9 },
]),
);
// ┌───┬───┬───┬───┐
// │ │ a │ b │ c │
// ├───┼───┼───┼───┤
// │ 0 │ 1 │ 2 │ 3 │
// │ 1 │ 4 │ 5 │ 6 │
// │ 2 │ 7 │ 8 │ 9 │
// └───┴───┴───┴───┘
您也可以傳遞屬性名稱陣列,以僅顯示屬性的子集。
console.log(
Bun.inspect.table(
[
{ a: 1, b: 2, c: 3 },
{ a: 4, b: 5, c: 6 },
],
["a", "c"],
),
);
// ┌───┬───┬───┐
// │ │ a │ c │
// ├───┼───┼───┤
// │ 0 │ 1 │ 3 │
// │ 1 │ 4 │ 6 │
// └───┴───┴───┘
如果您想要啟用 ANSI 色彩,您可以在選項物件上設定 colors
屬性。
console.log(
Bun.inspect.table(
[
{ a: 1, b: 2, c: 3 },
{ a: 4, b: 5, c: 6 },
],
{
colors: true,
},
),
);
可迭代的 SQLite 查詢
Bun 具有內建的 SQLite API,可讓您輕鬆查詢 SQLite 資料庫。現在您可以傳回一個迭代器,它會產生從資料庫傳回的列,而不是傳回列陣列。
import { Database } from "bun:sqlite";
const sst = new Database(":memory:");
sst.run("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT)");
class User {
id: number;
name: string;
email: string;
get nameAndEmail() {
return `${this.name} <${this.email}>`;
}
}
const rows = sst
.query("SELECT * FROM users")
.as(User)
.iterate() // <--- here
// call the .nameAndEmail property on each row
.map((row) => row.nameAndEmail);
for (const row of rows) {
console.log(row);
}
// prints nothing because sst has no users
您也可以在查詢上呼叫 .iterate()
方法,以取得一個迭代器,它會產生從資料庫傳回的列。
for (const row of db.query("SELECT * FROM users").iterate()) {
console.log(row); // { id: 1, name: "Bun" }
}
感謝 @Skywalker13 實作此功能!
迭代器輔助方法
在此 Bun 版本中,有新的 API 可以更輕鬆地使用 JavaScript 迭代器和生成器。這些 API 是在 TC39 提案中引入的,現在已在 Safari 和 Chrome 中提供。感謝 WebKit 團隊實作這些 API,特別是 @sosukesuzuki 和 @shvaikalesh!
iterator.map(fn)
傳回一個迭代器,它產生 fn
函式應用於原始迭代器每個值的結果,類似於 Array.prototype.map
。
function* range(start: number, end: number): Generator<number> {
for (let i = start; i < end; i++) {
yield i;
}
}
const result = range(3, 5).map((x) => x * 2);
result.next(); // { value: 6, done: false }
iterator.flatMap(fn)
傳回一個迭代器,它產生原始迭代器的值,但會展平 fn
函式的結果,類似於 Array.prototype.flatMap
。
function* randomThoughts(): Generator<string> {
yield "Bun is written in Zig";
yield "Bun runs JavaScript and TypeScript";
}
const result = randomThoughts().flatMap((x) => x.split(" "));
result.next(); // { value: "Bun", done: false }
result.next(); // { value: "is", done: false }
// ...
result.next(); // { value: "TypeScript", done: false }
iterator.filter(fn)
傳回一個迭代器,它僅產生通過 fn
述詞的值,類似於 Array.prototype.filter
。
function* range(start: number, end: number): Generator<number> {
for (let i = start; i < end; i++) {
yield i;
}
}
const result = range(3, 5).filter((x) => x % 2 === 0);
result.next(); // { value: 4, done: false }
iterator.take(n)
傳回一個迭代器,它產生原始迭代器的前 n
個值。
function* odds(): Generator<number> {
let i = 1;
while (true) {
yield i;
i += 2;
}
}
const result = odds().take(1);
result.next(); // { value: 1, done: false }
result.next(); // { done: true }
iterator.drop(n)
傳回一個迭代器,它產生原始迭代器的所有值,但排除前 n
個值。
function* evens(): Generator<number> {
let i = 0;
while (true) {
yield i;
i += 2;
}
}
const result = evens().drop(2);
result.next(); // { value: 4, done: false }
result.next(); // { value: 6, done: false }
iterator.reduce(fn, initialValue)
使用函式縮減迭代器的值,類似於 Array.prototype.reduce
。
function* powersOfTwo(): Generator<number> {
let i = 1;
while (true) {
yield i;
i *= 2;
}
}
const result = powersOfTwo()
.take(5)
.reduce((acc, x) => acc + x, 0);
console.log(result); // 15
iterator.toArray()
傳回一個陣列,其中包含原始迭代器的所有值。請確保迭代器是有限的,否則會導致無限迴圈。
function* range(start: number, end: number): Generator<number> {
for (let i = start; i < end; i++) {
yield i;
}
}
const result = range(1, 5).toArray();
console.log(result); // [1, 2, 3, 4]
iterator.forEach(fn)
對原始迭代器的每個值呼叫 fn
函式,類似於 Array.prototype.forEach
。
function* randomThoughts(): Generator<string> {
yield "Bun is written in Zig";
yield "Bun runs JavaScript and TypeScript";
}
const result = randomThoughts().forEach((x) => console.log(x));
// Bun is written in Zig
// Bun runs JavaScript and TypeScript
iterator.find(fn)
傳回原始迭代器中第一個通過 fn
述詞的值,類似於 Array.prototype.find
。如果不存在此類值,則傳回 undefined
。
function* range(start: number, end: number): Generator<number> {
for (let i = start; i < end; i++) {
yield i;
}
}
const result = range(0, 99).find((x) => x % 100 === 0);
console.log(result); // undefined
Promise.try
Promise.try
方法類似於 Promise.resolve
,但它也適用於同步函式。
Promise.try(() => {
return 1 + 1;
}).then((result) => {
console.log(result); // 2
});
這是一個 stage4 TC39 提案(即,它已從提案升級到標準化 ECMAScript)。感謝 @rkirsling 實作此功能!
Error.captureStackTrace 速度提升 9 倍
在下一個 Bun 版本中
— Jarred Sumner (@jarredsumner) October 14, 2024
Error.captureStackTrace(err) 速度提升 9 倍 pic.twitter.com/Ej7XW8KPNk
expect(fn).toThrow()
在未擲回錯誤時列印傳回值
之前
現在
感謝 @nektro 實作此功能!
在首次閒置請求逾時時發出警告
在下一個 Bun 版本中
— Jarred Sumner (@jarredsumner) October 11, 2024
在 Bun.serve() 中請求自動逾時時,若未明確傳遞 idleTimeout 且未使用 AbortSignal,則新增警告 pic.twitter.com/krMmUNi14x
新錯誤:JSX 關閉標籤不符
先前,即使以下程式碼是無效的 JSX,也不會擲回錯誤
const Oopsie = () => {
return (
<h1>
That should be an h1! Not a div.
</div>
);
};
我們應該一直在此處擲回錯誤,但我們沒有。現在我們做到了
❯ bun build Oopsie.tsx
5 | </div>
^
error: Expected closing JSX tag to match opening tag "<h1>"
at Oopsie.tsx:5:7
3 | <h1>
^
note: Opening tag here:
at Oopsie.tsx:3:6
感謝 @DonIsaac 修正此問題!
Node.js 相容性
新增:Buffer.copyBytesFrom(view[, offset[, length]])
您現在可以使用 Buffer.copyBytesFrom
方法,透過從型別陣列或 DataView 複製位元組來建立新的 Buffer
。
const u16 = new Uint16Array([0, 0xffff]);
const buf = Buffer.copyBytesFrom(u16, 1, 1);
u16[1] = 0;
console.log(buf.length); // 2
console.log(buf[0]); // 255
console.log(buf[1]); // 255
感謝 @nektro 實作此功能!
已修正:fs.open()
我們修正了 fs.open()
中的各種問題,使其更符合 Node.js 的行為。感謝 @nektro 修正此問題!
已修正:Node-API 錯誤
我們一直在努力修正 Bun 中 Node-API 實作的錯誤。感謝 @190n 修正這些錯誤!
napi_create_empty_array
在陣列過大時不會崩潰napi_create_empty_array
使用空插槽,而不是undefined
值napi_get_value_int32
使用與 JavaScript 中相同的位元運算轉換napi_get_value_int64
現在符合 Node.js 的行為napi_throw_error
和napi_create_error
現在已測試,且有時不會崩潰
已修正:設定 UV_THREADPOOL_SIZE
有些 Node.js 套件會設定 UV_THREADPOOL_SIZE
環境變數,以控制 libuv 使用的執行緒數。雖然 Bun 僅在 Windows 上且僅針對某些 API 使用 libuv,但現在它會尊重此環境變數。
已修正:Buffer.from
JSON 序列化
當您在 Buffer 上呼叫 JSON.stringify
時,它會將 {type: "Buffer", value: [...buffer contents]}
新增至 JSON。這已經可以運作,但 Buffer.from 不會接受此 JSON。現在可以了。
const buf = Buffer.from([1, 2, 3]);
JSON.stringify(buf); // "{"type":"Buffer","data":[1,2,3]}"
Buffer.from(JSON.parse('{"type":"Buffer","data":[1,2,3]}'))[0] === 1; // works
先前,這會擲回錯誤
TypeError: The first argument must be of type string or an instance of Buffer, ArrayBuffer, or Array or an Array-like Object.
at [file]:3:20
感謝 @nektro 修正此問題!
已修正:node:child_process.SpawnResult
.stderr
在 .stdio
不是 "pipe"
時應為 null
import child_process from "node:child_process";
const res = child_process.spawn("ls", {
stdio: "inherit",
});
console.log(res.stderr); // now 'null'
這解鎖了 tinyexec
套件的使用。
感謝 @nektro 修正此問題!
錯誤修正
已修正:具有數字鍵的 console.table
格式
我們修正了一個錯誤,當鍵為數字時,console.table
會以不正確的間距格式化。感謝 @pfgithub 修正此問題!
console.table({test: {"10": 123, "100": 154}});
// Before
┌──────┬─────┬─────┐
│ │ 10 │ 100 │
├──────┼─────┼─────┤
│ test │ │ │
└──────┴─────┴─────┘
// After
┌─────────┬─────┬─────┐
│ (index) │ 10 │ 100 │
├─────────┼─────┼─────┤
│ test │ 123 │ 154 │
└─────────┴─────┴─────┘
已修正:使用 Bun.color
時崩潰
在最近的版本中,我們推出了 Bun.color
,這是一個 API,可讓您剖析和轉換不同格式的色彩,包括 css、ansi、hex、rgb、hsl 等。
我們修正了一個錯誤,如果您沒有傳遞格式字串,Bun.color
會崩潰。
已修正:instanceof
的回歸
透過 WebKit 升級在 Bun v1.1.30 中引入的 instanceof
回歸已修正。錯誤可能是由 JavaScript 程式碼將 instanceof
的輸出值儲存在與輸入相同的變數中所導致。錯誤訊息看起來會像
error: [BUG] Unreachable
at ... (input.js:2247:47)
此錯誤最常在使用啟用來源地圖的 sass
時看到。
感謝 @hyjorc1 修正!
已修正:使用 using
和生成器的轉譯器錯誤
我們修正了一個錯誤,其中 using
語句之前的變數宣告會導致轉譯器將生成器提升到 using
宣告的範圍之外。這會導致 ReferenceError
,但現在已感謝 @snoglobe 修正。
已修正:bun install
的檔案權限
有些 npm 套件發佈時具有不正確的檔案權限,我們在 Bun 中新增了一個檢查,以確保從 npm 套件解壓縮的檔案至少是可讀的。這與 npm install
的行為相符。
已修正:bun publish
的中繼資料發佈遺失
修正了一個錯誤,其中 bun publish
在傳送發佈請求時,遺失了重要的套件中繼資料,包括來自 bin
或 directories.bin
的相依性和二進位檔。這會導致發佈的套件由於 node_modules/.bin
中缺少過渡性相依性和二進位檔而無法正確安裝。
已修正:fs.mkdir
的空字串引數
已修正將空字串傳遞給 fs.mkdir
所造成的整數溢位。這只會在 recursive
選項設定為 true
時發生。
const fs = require("fs");
fs.mkdirSync("", { recursive: true }); // throws ENOENT
已修正:fetch keepalive
選項
先前已忽略 fetch
的 keepalive
選項。現在它預設會停用傳送 "Connection: keep-alive" 標頭。
const res = await fetch("https://example.com", { keepalive: false });
console.log(res.headers.get("connection")); // "close"