Bun

工具

Bun.version

一個 string,包含目前正在執行的 bun CLI 版本。

Bun.version;
// => "0.6.4"

Bun.revision

用於建立目前 bun CLI 的 Bun 的 git commit。

Bun.revision;
// => "f02561530fda1ee9396f51c8bc99b38716e38296"

Bun.env

process.env 的別名。

Bun.main

目前程式進入點的絕對路徑(使用 bun run 執行的檔案)。

script.ts
Bun.main;
// /path/to/script.ts

這對於判斷腳本是直接執行,還是由另一個腳本導入特別有用。

if (import.meta.path === Bun.main) {
  // this script is being directly executed
} else {
  // this file is being imported from another script
}

這類似於 Node.js 中的 require.main = module 技巧

Bun.sleep()

Bun.sleep(ms: number)

傳回一個 Promise,該 Promise 在指定的毫秒數後解析。

console.log("hello");
await Bun.sleep(1000);
console.log("hello one second later!");

或者,傳遞一個 Date 物件以接收一個 Promise,該 Promise 將在該時間點解析。

const oneSecondInFuture = new Date(Date.now() + 1000);

console.log("hello");
await Bun.sleep(oneSecondInFuture);
console.log("hello one second later!");

Bun.sleepSync()

Bun.sleepSync(ms: number)

Bun.sleep 的同步阻塞版本。

console.log("hello");
Bun.sleepSync(1000); // blocks thread for one second
console.log("hello one second later!");

Bun.which()

Bun.which(bin: string)

傳回可執行檔的路徑,類似於在終端機中輸入 which

const ls = Bun.which("ls");
console.log(ls); // "/usr/bin/ls"

預設情況下,Bun 會查看目前的 PATH 環境變數以判斷路徑。若要設定 PATH

const ls = Bun.which("ls", {
  PATH: "/usr/local/bin:/usr/bin:/bin",
});
console.log(ls); // "/usr/bin/ls"

傳遞 cwd 選項以從特定目錄中解析可執行檔。

const ls = Bun.which("ls", {
  cwd: "/tmp",
  PATH: "",
});

console.log(ls); // null

您可以將其視為 which npm 套件的內建替代方案。

Bun.randomUUIDv7()

Bun.randomUUIDv7() 傳回 UUID v7,它是單調遞增的,適合排序和資料庫。

import { randomUUIDv7 } from "bun";

const id = randomUUIDv7();
// => "0192ce11-26d5-7dc3-9305-1426de888c5a"

UUID v7 是一個 128 位元的值,它編碼了目前的時間戳記、一個隨機值和一個計數器。時間戳記使用最低的 48 位元進行編碼,而隨機值和計數器則使用剩餘的位元進行編碼。

timestamp 參數預設為目前的毫秒時間。當時間戳記變更時,計數器會重設為一個包裹在 4096 中的虛擬隨機整數。此計數器是原子且執行緒安全的,這表示在同一個程序中以相同時間戳記執行的多個 Worker 中使用 Bun.randomUUIDv7() 不會發生計數器值衝突。

UUID 的最後 8 個位元組是一個密碼學安全的隨機值。它使用與 crypto.randomUUID() 相同的隨機數字產生器(來自 BoringSSL,而 BoringSSL 又來自平台特定的系統隨機數字產生器,通常由底層硬體提供)。

namespace Bun {
  function randomUUIDv7(
    encoding?: "hex" | "base64" | "base64url" = "hex",
    timestamp?: number = Date.now(),
  ): string;
  /**
   * If you pass "buffer", you get a 16-byte buffer instead of a string.
   */
  function randomUUIDv7(
    encoding: "buffer",
    timestamp?: number = Date.now(),
  ): Buffer;

  // If you only pass a timestamp, you get a hex string
  function randomUUIDv7(timestamp?: number = Date.now()): string;
}

您可以選擇將編碼設定為 "buffer" 以取得 16 位元組的緩衝區而不是字串。有時可以避免字串轉換的額外開銷。

buffer.ts
const buffer = Bun.randomUUIDv7("buffer");

當您想要稍微短一點的字串時,也支援 base64base64url 編碼。

base64.ts
const base64 = Bun.randomUUIDv7("base64");
const base64url = Bun.randomUUIDv7("base64url");

Bun.peek()

Bun.peek(prom: Promise)

讀取 promise 的結果,無需 await.then,但僅當 promise 已實現或已拒絕時才行。

import { peek } from "bun";

const promise = Promise.resolve("hi");

// no await!
const result = peek(promise);
console.log(result); // "hi"

當嘗試減少效能敏感程式碼中不必要的微刻度數量時,這非常重要。這是一個進階 API,除非您知道自己在做什麼,否則可能不應使用它。

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

test("peek", () => {
  const promise = Promise.resolve(true);

  // no await necessary!
  expect(peek(promise)).toBe(true);

  // if we peek again, it returns the same value
  const again = peek(promise);
  expect(again).toBe(true);

  // if we peek a non-promise, it returns the value
  const value = peek(42);
  expect(value).toBe(42);

  // if we peek a pending promise, it returns the promise again
  const pending = new Promise(() => {});
  expect(peek(pending)).toBe(pending);

  // If we peek a rejected promise, it:
  // - returns the error
  // - does not mark the promise as handled
  const rejected = Promise.reject(
    new Error("Successfully tested promise rejection"),
  );
  expect(peek(rejected).message).toBe("Successfully tested promise rejection");
});

peek.status 函式可讓您讀取 promise 的狀態,而無需解析它。

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

test("peek.status", () => {
  const promise = Promise.resolve(true);
  expect(peek.status(promise)).toBe("fulfilled");

  const pending = new Promise(() => {});
  expect(peek.status(pending)).toBe("pending");

  const rejected = Promise.reject(new Error("oh nooo"));
  expect(peek.status(rejected)).toBe("rejected");
});

Bun.openInEditor()

在您的預設編輯器中開啟檔案。Bun 會透過 $VISUAL$EDITOR 環境變數自動偵測您的編輯器。

const currentFile = import.meta.url;
Bun.openInEditor(currentFile);

您可以透過 bunfig.toml 中的 debug.editor 設定來覆寫此設定。

bunfig.toml
[debug]
editor = "code"

或使用 editor 參數指定編輯器。您也可以指定行號和欄號。

Bun.openInEditor(import.meta.url, {
  editor: "vscode", // or "subl"
  line: 10,
  column: 5,
});

Bun.deepEquals()

遞迴檢查兩個物件是否相等。這在 bun:test 中由 expect().toEqual() 在內部使用。

const foo = { a: 1, b: 2, c: { d: 3 } };

// true
Bun.deepEquals(foo, { a: 1, b: 2, c: { d: 3 } });

// false
Bun.deepEquals(foo, { a: 1, b: 2, c: { d: 4 } });

第三個布林參數可用於啟用「嚴格」模式。這在測試執行器中由 expect().toStrictEqual() 使用。

const a = { entries: [1, 2] };
const b = { entries: [1, 2], extra: undefined };

Bun.deepEquals(a, b); // => true
Bun.deepEquals(a, b, true); // => false

在嚴格模式下,以下項目被視為不相等:

// undefined values
Bun.deepEquals({}, { a: undefined }, true); // false

// undefined in arrays
Bun.deepEquals(["asdf"], ["asdf", undefined], true); // false

// sparse arrays
Bun.deepEquals([, 1], [undefined, 1], true); // false

// object literals vs instances w/ same properties
class Foo {
  a = 1;
}
Bun.deepEquals(new Foo(), { a: 1 }, true); // false

Bun.escapeHTML()

Bun.escapeHTML(value: string | object | number | boolean): string

從輸入字串逸出以下字元:

  • " 變成 "
  • & 變成 &
  • ' 變成 '
  • < 變成 &lt;
  • > 變成 &gt;

此函式針對大型輸入進行了最佳化。在 M1X 上,它的處理速度為 480 MB/s - 20 GB/s,取決於逸出的資料量以及是否有非 ASCII 文字。非字串類型將在逸出前轉換為字串。

Bun.stringWidth() ~6,756 倍快於 string-width 替代方案

取得字串在終端機中顯示時的欄數。 支援 ANSI 逸出碼、表情符號和寬字元。

使用範例:

Bun.stringWidth("hello"); // => 5
Bun.stringWidth("\u001b[31mhello\u001b[0m"); // => 5
Bun.stringWidth("\u001b[31mhello\u001b[0m", { countAnsiEscapeCodes: true }); // => 12

這對於以下情況很有用:

  • 在終端機中對齊文字
  • 快速檢查字串是否包含 ANSI 逸出碼
  • 測量字串在終端機中的寬度

此 API 旨在與流行的 "string-width" 套件相符,以便 現有的程式碼可以輕鬆移植到 Bun,反之亦然。

在此基準測試中,對於大於約 500 個字元的輸入,Bun.stringWidthstring-width npm 套件快約 6,756 倍。非常感謝 sindresorhusstring-width 上的工作!

❯ bun string-width.mjs
cpu: 13th Gen Intel(R) Core(TM) i9-13900
runtime: bun 1.0.29 (x64-linux)

benchmark                                          time (avg)             (min … max)       p75       p99      p995
------------------------------------------------------------------------------------- -----------------------------
Bun.stringWidth     500 chars ascii              37.09 ns/iter   (36.77 ns … 41.11 ns)  37.07 ns  38.84 ns  38.99 ns

❯ node string-width.mjs

benchmark                                          time (avg)             (min … max)       p75       p99      p995
------------------------------------------------------------------------------------- -----------------------------
npm/string-width    500 chars ascii             249,710 ns/iter (239,970 ns … 293,180 ns) 250,930 ns  276,700 ns 281,450 ns

為了使 Bun.stringWidth 快速,我們已使用最佳化的 SIMD 指令在 Zig 中實作它,考量了 Latin1、UTF-16 和 UTF-8 編碼。它通過了 string-width 的測試。

檢視完整基準測試

TypeScript 定義

namespace Bun {
  export function stringWidth(
    /**
     * The string to measure
     */
    input: string,
    options?: {
      /**
       * If `true`, count ANSI escape codes as part of the string width. If `false`, ANSI escape codes are ignored when calculating the string width.
       *
       * @default false
       */
      countAnsiEscapeCodes?: boolean;
      /**
       * When it's ambiugous and `true`, count emoji as 1 characters wide. If `false`, emoji are counted as 2 character wide.
       *
       * @default true
       */
      ambiguousIsNarrow?: boolean;
    },
  ): number;
}

Bun.fileURLToPath()

file:// URL 轉換為絕對路徑。

const path = Bun.fileURLToPath(new URL("file:///foo/bar.txt"));
console.log(path); // "/foo/bar.txt"

Bun.pathToFileURL()

將絕對路徑轉換為 file:// URL。

const url = Bun.pathToFileURL("/foo/bar.txt");
console.log(url); // "file:///foo/bar.txt"

Bun.gzipSync()

使用 zlib 的 GZIP 演算法壓縮 Uint8Array

const buf = Buffer.from("hello".repeat(100)); // Buffer extends Uint8Array
const compressed = Bun.gzipSync(buf);

buf; // => Uint8Array(500)
compressed; // => Uint8Array(30)

您可以選擇傳遞參數物件作為第二個引數:

zlib 壓縮選項

Bun.gunzipSync()

使用 zlib 的 GUNZIP 演算法解壓縮 Uint8Array

const buf = Buffer.from("hello".repeat(100)); // Buffer extends Uint8Array
const compressed = Bun.gzipSync(buf);

const dec = new TextDecoder();
const uncompressed = Bun.gunzipSync(compressed);
dec.decode(uncompressed);
// => "hellohellohello..."

Bun.deflateSync()

使用 zlib 的 DEFLATE 演算法壓縮 Uint8Array

const buf = Buffer.from("hello".repeat(100));
const compressed = Bun.deflateSync(buf);

buf; // => Uint8Array(25)
compressed; // => Uint8Array(10)

第二個引數支援與 Bun.gzipSync 相同的組態選項集。

Bun.inflateSync()

使用 zlib 的 INFLATE 演算法解壓縮 Uint8Array

const buf = Buffer.from("hello".repeat(100));
const compressed = Bun.deflateSync(buf);

const dec = new TextDecoder();
const decompressed = Bun.inflateSync(compressed);
dec.decode(decompressed);
// => "hellohellohello..."

Bun.inspect()

將物件序列化為 string,與 console.log 列印的結果完全相同。

const obj = { foo: "bar" };
const str = Bun.inspect(obj);
// => '{\nfoo: "bar" \n}'

const arr = new Uint8Array([1, 2, 3]);
const str = Bun.inspect(arr);
// => "Uint8Array(3) [ 1, 2, 3 ]"

Bun.inspect.custom

這是 Bun 用於實作 Bun.inspect 的符號。您可以覆寫此符號以自訂物件的列印方式。它與 Node.js 中的 util.inspect.custom 相同。

class Foo {
  [Bun.inspect.custom]() {
    return "foo";
  }
}

const foo = new Foo();
console.log(foo); // => "foo"

Bun.inspect.table(tabularData, properties, options)

將表格資料格式化為字串。類似於 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 │
// └───┴───┴───┘

您也可以有條件地透過傳遞 { colors: true } 來啟用 ANSI 彩色輸出。

console.log(
  Bun.inspect.table(
    [
      { a: 1, b: 2, c: 3 },
      { a: 4, b: 5, c: 6 },
    ],
    {
      colors: true,
    },
  ),
);

Bun.nanoseconds()

傳回自目前的 bun 程序啟動以來經過的奈秒數,以 number 形式表示。適用於高精確度計時和效能評測。

Bun.nanoseconds();
// => 7288958

Bun.readableStreamTo*()

Bun 實作了一組便利函式,用於非同步使用 ReadableStream 的 body 並將其轉換為各種二進制格式。

const stream = (await fetch("https://bun.dev.org.tw")).body;
stream; // => ReadableStream

await Bun.readableStreamToArrayBuffer(stream);
// => ArrayBuffer

await Bun.readableStreamToBytes(stream);
// => Uint8Array

await Bun.readableStreamToBlob(stream);
// => Blob

await Bun.readableStreamToJSON(stream);
// => object

await Bun.readableStreamToText(stream);
// => string

// returns all chunks as an array
await Bun.readableStreamToArray(stream);
// => unknown[]

// returns all chunks as a FormData object (encoded as x-www-form-urlencoded)
await Bun.readableStreamToFormData(stream);

// returns all chunks as a FormData object (encoded as multipart/form-data)
await Bun.readableStreamToFormData(stream, multipartFormBoundary);

Bun.resolveSync()

使用 Bun 的內部模組解析演算法解析檔案路徑或模組識別符。第一個引數是要解析的路徑,第二個引數是「根目錄」。如果找不到符合的項目,則會擲回 Error

Bun.resolveSync("./foo.ts", "/path/to/project");
// => "/path/to/project/foo.ts"

Bun.resolveSync("zod", "/path/to/project");
// => "/path/to/project/node_modules/zod/index.ts"

若要相對於目前的工作目錄解析,請傳遞 process.cwd()"." 作為根目錄。

Bun.resolveSync("./foo.ts", process.cwd());
Bun.resolveSync("./foo.ts", "/path/to/project");

若要相對於包含目前檔案的目錄解析,請傳遞 import.meta.dir

Bun.resolveSync("./foo.ts", import.meta.dir);

serialize & deserialize in bun:jsc

若要將 JavaScript 值儲存到 ArrayBuffer 和取回,請使用 "bun:jsc" 模組中的 serializedeserialize

import { serialize, deserialize } from "bun:jsc";

const buf = serialize({ foo: "bar" });
const obj = deserialize(buf);
console.log(obj); // => { foo: "bar" }

在內部,structuredClonepostMessage 以相同的方式序列化和反序列化。這將底層的 HTML Structured Clone Algorithm 作為 ArrayBuffer 公開給 JavaScript。

estimateShallowMemoryUsageOf in bun:jsc

estimateShallowMemoryUsageOf 函式傳回物件記憶體使用量的最佳估計值(以位元組為單位),不包括屬性或它引用的其他物件的記憶體使用量。若要取得精確的每個物件記憶體使用量,請使用 Bun.generateHeapSnapshot

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

const req = new Request("https://bun.dev.org.tw");
estimateShallowMemoryUsageOf(req);
// => 167

const array = Array(1024).fill({ a: 1 });
// Arrays are usually not stored contiguously in memory, so this will not return a useful value (which isn't a bug).
estimateShallowMemoryUsageOf(array);
// => 16