Bun

实用工具

Bun.version

一个包含当前运行的 bun CLI 版本的 string

Bun.version;
// => "0.6.4"

Bun.revision

用于创建当前 bun CLI 的 Bun 的 Git 提交。

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

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

或者,传入一个 Date 对象,以在那个时间点解析一个 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()string-width 快约 6,756 倍的替代方案

获取字符串在终端中显示时的列数。 支持 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; // => Buffer(500)
compressed; // => Uint8Array(12)

第二个参数支持与 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.zstdCompress() / Bun.zstdCompressSync()

使用 Zstandard 算法压缩 Uint8Array

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

// Synchronous
const compressedSync = Bun.zstdCompressSync(buf);
// Asynchronous
const compressedAsync = await Bun.zstdCompress(buf);

// With compression level (1-22, default: 3)
const compressedLevel = Bun.zstdCompressSync(buf, { level: 6 });

Bun.zstdDecompress() / Bun.zstdDecompressSync()

使用 Zstandard 算法解压缩 Uint8Array

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

// Synchronous
const decompressedSync = Bun.zstdDecompressSync(compressed);
// Asynchronous
const decompressedAsync = await Bun.zstdDecompress(compressed);

const dec = new TextDecoder();
dec.decode(decompressedSync);
// => "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 的主体并将其转换为各种二进制格式。

const stream = (await fetch("https://bun.net.cn")).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);

bun:jsc 中的 serializedeserialize

要将 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 结构化克隆算法作为 ArrayBuffer 暴露给 JavaScript。

Bun.stripANSI()strip-ansi 快约 6-57 倍的替代方案

Bun.stripANSI(text: string): string

从字符串中去除 ANSI 转义码。这对于从终端输出中删除颜色和格式很有用。

const coloredText = "\u001b[31mHello\u001b[0m \u001b[32mWorld\u001b[0m";
const plainText = Bun.stripANSI(coloredText);
console.log(plainText); // => "Hello World"

// Works with various ANSI codes
const formatted = "\u001b[1m\u001b[4mBold and underlined\u001b[0m";
console.log(Bun.stripANSI(formatted)); // => "Bold and underlined"

Bun.stripANSI 比流行的 strip-ansi npm 包快得多

> bun bench/snippets/strip-ansi.mjs
cpu: Apple M3 Max
runtime: bun 1.2.21 (arm64-darwin)

benchmark                               avg (min … max) p75 / p99
------------------------------------------------------- ----------
Bun.stripANSI      11 chars no-ansi        8.13 ns/iter   8.27 ns
                                   (7.45 ns … 33.59 ns)  10.29 ns

Bun.stripANSI      13 chars ansi          51.68 ns/iter  52.51 ns
                                 (46.16 ns … 113.71 ns)  57.71 ns

Bun.stripANSI  16,384 chars long-no-ansi 298.39 ns/iter 305.44 ns
                                (281.50 ns … 331.65 ns) 320.70 ns

Bun.stripANSI 212,992 chars long-ansi    227.65 µs/iter 234.50 µs
                                (216.46 µs … 401.92 µs) 262.25 µs
> node bench/snippets/strip-ansi.mjs
cpu: Apple M3 Max
runtime: node 24.6.0 (arm64-darwin)

benchmark                                avg (min … max) p75 / p99
-------------------------------------------------------- ---------
npm/strip-ansi      11 chars no-ansi      466.79 ns/iter 468.67 ns
                                 (454.08 ns … 570.67 ns) 543.67 ns

npm/strip-ansi      13 chars ansi         546.77 ns/iter 550.23 ns
                                 (532.74 ns … 651.08 ns) 590.35 ns

npm/strip-ansi  16,384 chars long-no-ansi   4.85 µs/iter   4.89 µs
                                     (4.71 µs … 5.00 µs)   4.98 µs

npm/strip-ansi 212,992 chars long-ansi      1.36 ms/iter   1.38 ms
                                     (1.27 ms … 1.73 ms)   1.49 ms

bun:jsc 中的 estimateShallowMemoryUsageOf

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.net.cn");
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