Bun 1.1

原文信息: 查看原文查看原文

Bun 1.1

- Jarred, Dave, Ashcon, Dylan, Meghan, Georgijs, Ciro

Bun是一个快速、全能的工具包,用于运行、构建、测试和调试JavaScript和TypeScript,从单个脚本到完整的堆栈应用。如果你对Bun还不熟悉,可以在Bun 1.0博客文章中了解更多信息。

Bun 1.1 是一个巨大的更新。

自Bun 1.0以来已经有1,700多次提交,我们一直在努力使Bun更加稳定并与Node.js更兼容。我们修复了一千多个bug,添加了大量新功能和API,并且现在,Bun支持Windows了!

Windows支持

现在你可以在Windows 10及更高版本上运行Bun了!这对我们来说是一个重大的里程碑,我们很高兴将Bun带给全新的开发人员群体。

Bun在Windows上通过了98%我们自己对macOS和Linux上Bun的测试套件。这意味着一切,从运行时、测试运行器、包管理器、捆绑器——在Windows上都能正常工作。

要在Windows上开始使用Bun,请在终端中运行以下命令:

powershell -c "irm bun.sh/install.ps1 | iex"

在Windows上的 bun install

Bun有一个内置的、与npm兼容的包管理器来安装包。在安装Vite React应用程序时,bun install 在Windows上比yarn快18倍,比npm快30倍。

使用--ignore-scripts在Windows上安装vite react应用程序的依赖所花费的时间。

在Windows上的 bun run

你也可以使用bun run来运行脚本,这是npm run的一个更快的替代方案。为了让bun run在Windows上运行得更快,我们设计了一个新的文件格式:.bunx

.bunx 文件是一个跨文件系统的符号链接,能够使用Bun或Node.js启动脚本或可执行文件。我们决定创建它有几个原因:

  • 符号链接不能保证在Windows上工作。
  • 文件顶部的Shebang (#!/usr/bin/env bun) 在Windows上不被识别。
  • 我们不想创建每个可执行文件的三个排列组合:.cmd.sh.ps1
  • 我们想避免令人困惑的 "Terminate batch job? (Y/n)" 提示。

最终的结果是,bun runnpm run 快11倍,bunx 也比 npx 快11倍。

在Windows上运行 bunx cowsay vs npx cowsay 所花费的时间。

即使你只将Bun用作包管理器而不是运行时,.bunx 也可以与Node.js一起正常工作。这也解决了在向正在运行的脚本发送 ctrl-c 时,Windows开发人员习惯遇到的令人讨厌的 "Terminate batch job?" 提示。

在Windows上的 bun --watch

Bun内置了对 --watch 模式的支持。这使你在修改和影响代码之间有了快速的迭代循环。在Windows上,我们确保了在按下 Ctrl-S 和进程重新加载之间所花费的时间。

在左边,修改测试文件。在右边,bun test --watch 在Windows上的运行。

Windows上的Node.js API

我们也花费了时间优化Node.js API,以使用Windows上可用的最快的系统调用。例如,Bun上的 fs.readdir() 在Windows上比Node.js快 58%

在Windows上列出目录中的文件所花费的时间,1000次。

虽然我们还没有对每个API进行优化,但是如果你在Windows上注意到某些东西比Node.js慢或者更慢,请提交一个问题,我们将会找出如何让它更快。

Bun是一个JavaScript运行时

与Bun 1.0相比,Windows支持只是其中一个例子,Bun 1.0以来我们做了许多新功能、API和改进。

大型项目的启动速度提高了2倍

Bun内置了对JavaScript、TypeScript和JSX的支持,由Bun自己编写高度优化的本地代码支持。

自Bun 1.0以来,我们实现了一个内容可寻址缓存,用于避免重复转译相同的文件所带来的性能开销。

这使得命令行工具,如tsc,的运行速度提高了2倍

在Bun和Node.js中运行 tsc --help 所花费的时间。

Bun Shell

Bun现在是一个跨平台的Shell——像bash一样,但也在Windows上。

JavaScript是世界上最流行的脚本语言。那么,为什么运行Shell脚本会这么复杂呢?

import { spawnSync } from "child_process";
// 这比它本来应该做的多了很多工作
const { status, stdout, stderr } = spawnSync("ls", ["-l", "*.js"], {
  encoding: "utf8",
});

不同的平台也有不同的Shell,每个Shell都有略微不同的语法规则、行为,甚至命令。例如,如果你想要在Windows上使用cmd来运行一个Shell脚本:

  • rm -rf 不起作用。
  • FOO=bar <command> 不起作用。
  • which 不存在。(它被称为 where

Bun Shell 是一个词法分析器、解析器和解释器,实现了一个类似于bash的编程语言,以及一系列核心工具,如 lsrmcat

这个Shell也可以从JavaScript和TypeScript中运行,使用 Bun.$ API。

import { $ } from "bun";
// 管道到stdout:
await $`ls *.js`;
// 管道到字符串:
const text = await $`ls *.js`.text();

这种语法使得在Shell和JavaScript之间传递参数、缓冲区和管道变得很容易。

const response = await fetch("https://example.com/");
// 将响应作为stdin,
// 将stdout返回给JavaScript:
const stdout = await $`gzip -c < ${response}`.arrayBuffer();

变量也会被转义以防止命令注入。

const filename = "foo.js; rm -rf /";
// ls: 无法访问 'foo.js; rm -rf /':
// 没有那个文件或目录
await $`ls ${filename}`;

你可以通过运行 bun run 来使用Bun Shell 运行Shell脚本。

bun run my-script.sh

在使用 bun run 运行 package.json 脚本时,默认情况下,Bun Shell 在Windows上是启用的。要了解更多信息,请查阅文档发布博客文章

Bun.Glob

Bun现在有一个内置的Glob API,用于使用glob模式匹配文件和字符串。它类似于流行的Node.js库,如 fast-globmicromatch,不同之处在于它匹配字符串的速度快3倍

使用 glob.match() 来匹配字符串与glob模式。

import { Glob } from "bun";
const glob = new Glob("**/*.ts");
const match = glob.match("src/index.ts"); // true

使用 glob.scan() 来列出匹配glob模式的文件,使用 AsyncIterator

const glob = new Glob("**/*.ts");
for await (const path of glob.scan("src")) {
  console.log(path); // "src/index.ts", "src/utils.ts", ...
}

Bun.Semver

Bun现在有一个新的Semver API,用于解析和排序Semver字符串。它类似于流行的 node-semver 包,不同之处在于它快了20倍

使用 semver.satisfies() 来检查一个版本是否满足一个范围。

import { semver } from "bun";
semver.satisfies("1.0.0", "^1.0.0"); // true
semver.satisfies("1.0.0", "^2.0.0"); // false

使用 semver.order() 来比较两个版本,或者对版本数组进行排序。

const versions = ["1.1.0", "0.0.1", "1.0.0"];
versions.sort(semver.order); // ["0.0.1", "1.0.0", "1.1.0"]

Bun.stringWidth()

Bun还支持一个新的字符串宽度API,用于测量终端中字符串的可见宽度。当你想要知道一个字符串在终端中占据多少列时,这就很有用了。

它类似于流行的 string-width 包,不同之处在于它快了6000倍

import { stringWidth } from "bun";
stringWidth("hello"); // 5
stringWidth("👋"); // 2
stringWidth("你好"); // 4
stringWidth("👩‍👩‍👧‍👦"); // 2
stringWidth("\u001b[31mhello\u001b[39m"); // 5

它支持ANSI转义码、全角字符、图形符号和表情符号。它还支持Latin1、UTF-16和UTF-8编码,对每种编码都进行了优化实现。

server.url

当你使用 Bun.serve() 创建一个HTTP服务器时,你现在可以使用 server.url 属性来获取服务器的URL。这对于在测试中获取服务器的格式化URL很有用。

import { serve } from "bun";
const server = serve({
  port: 0, // 随机端口
  fetch(request) {
    return

 new Response();
  },
});
console.log(`${server.url}`); // "http://localhost:1234/"

server.requestIP()

你还可以使用 server.requestIP() 方法获取HTTP请求的IP地址。这不会读取诸如 X-Forwarded-ForX-Real-IP 的头部信息。它只是简单地返回套接字的IP地址,这可能对应于代理的IP地址。

import { serve } from "bun";
const server = serve({
  port: 0,
  fetch(request) {
    console.log(server.requestIP(request)); // "127.0.0.1"
    return new Response();
  },
});

subprocess.resourceUsage()

当你使用 Bun.spawn() 生成一个子进程时,你现在可以使用 resourceUsage() 方法访问进程的CPU和内存使用情况。这对于监控进程的性能很有用。

import { spawnSync } from "bun";
const { resourceUsage } = spawnSync([
  "bun",
  "-e",
  "console.log('Hello world!')",
]);
console.log(resourceUsage);
// {
//   cpuTime: { user: 5578n, system: 4488n, total: 10066n },
//   maxRSS: 22020096,
//   ...
// }

import.meta.env

Bun现在支持使用 import.meta.env 的环境变量。它是 process.envBun.env 的别名,存在是为了与JavaScript生态系统中的其他工具兼容,比如Vite。

import.meta.env.NODE_ENV; // "development"

Node.js兼容性

Bun旨在成为Node.js的一种插拔式替代方案。

Node.js兼容性仍然是Bun的首要任务。我们对Bun对Node.js API的支持进行了许多改进和修复。以下是其中一些亮点:

HTTP/2客户端

Bun现在支持 node:http2 客户端API,允许您进行出站HTTP/2请求。这也意味着您可以使用诸如 @grpc/grpc-js 之类的包来通过HTTP/2发送gRPC请求。

import { connect } from "node:http2";
const client = connect("https://example.com/");
const request = client.request({ ":path": "/" });
request.on("response", (headers, flags) => {
  for (const name in headers) {
    console.log(`${name}: ${headers[name]}`);
    // "cache-control: max-age=604800", ...
  }
});
request.on("end", () => {
  client.close();
});
request.end();

我们仍然在努力为HTTP/2服务器添加支持,您可以在这个问题中跟踪我们的进度。

Date.parse() 兼容Node.js

Bun使用JavaScriptCore作为其JavaScript引擎,而不是Node.js使用的V8Date 解析很复杂,在不同的引擎中的行为差异很大。

例如,在Bun 1.0中,以下的 Date 在Node.js中是有效的,但在Bun中不是:

const date = "2020-09-21 15:19:06 +00:00";
Date.parse(date); // Bun: Invalid Date
Date.parse(date); // Node.js: 1600701546000

为了修复这些不一致性,我们将 Date 解析器从V8移植到Bun。这意味着 Date.parsenew Date() 在Bun中的行为与在Node.js中的行为相同。

递归的 fs.readdir()

在Bun 1.0中,我们没有支持 fs.readdir() 中的 recursive 选项。这是一个疏忽,导致许多包中出现了一些微妙的错误。

我们不仅为 recursive 选项添加了支持,而且使它比Node.js快22倍。

在一个大型目录中使用递归的 fs.readdir() 列出文件所花费的时间。

Bun和Node.js之间的IPC支持

您现在可以使用 ipc 选项在Bun和Node.js进程之间发送IPC消息。这也修复了一个问题,该问题将导致在使用Next.js 14.1时Bun挂起。

if (typeof Bun !== "undefined") {
  const prefix = `[bun ${process.versions.bun} 🐇]`;
  const node = Bun.spawn({
    cmd: ["node", __filename],
    ipc({ message }) {
      console.log(message);
      node.send({ message: `${prefix} 👋 hey node` });
      node.kill();
    },
    stdio: ["inherit", "inherit", "inherit"],
    serialization: "json",
  });
  node.send({ message: `${prefix} 👋 hey node` });
} else {
  const prefix = `[node ${process.version}]`;
  process.on("message", ({ message }) => {
    console.log(message);
    process.send({ message: `${prefix} 👋 hey bun` });
  });
}

未记录的Node.js API

Node.js 有 大量 未记录的API,你在阅读其文档时找不到这些API。

npm有数百万个包,不可避免地,其中一些包会依赖于模糊或未记录的API。我们不会让这些包出现问题或被遗忘,我们实际上会将这些API添加到Bun中,这样你就不需要重写你的代码。

例如,ServerResponse 有一个未记录的 _headers 属性,允许

您访问响应的标头。这对于一些中间件很重要。

const { createServer } = require("http");
createServer((req, res) => {
  res.write("Hello world");
  res.end();
  console.log(res._headers); // { 'content-type': 'text/plain', 'content-length': '11', ... }
});

我们还添加了对未记录的Node.js API的类型定义。因此,如果你的编辑器支持类型检查,你将得到对未记录API的自动完成功能和类型检查。

const http = require("http");
http.ServerResponse.prototype._headers; // { [Symbol(outHeadersKey)]: ... }

如此之多

我们还添加或修复了大量其他Node.js API,包括:

模块API
顶级添加了 import.meta.filenameimport.meta.dirnamemodule.parent
process添加了 getReport()binding("tty_wrap")
node:util添加了 domainToASCII()domainToUnicode()styleText()。修改了 inspect() 以使其与Node.js更一致
node:crypto添加了 KeyObjectcreatePublicKey()createPrivateKey()generateKeyPair()generateKey()sign()verify()
node:fs添加了 openAsBlob()opendir()fdatasync()。修复了在各种API中未返回 FileHandle 的问题
node:console添加了 Console
node:dns添加了 lookupService()
node:http通过 request() 支持Unix域套接字
node:events添加了 on()
node:path修复了许多与Windows路径相关的错误
node:vm添加了 createScript()
node:os添加了 availableParallelism()

Web APIs

Bun 也支持 Web 标准 API,包括 fetch()Response。这使得编写同时在浏览器和 Bun 中运行的代码变得更加容易。

自从 Bun 1.0 以来,我们对 Web API 进行了许多改进和修复。

WebSocket 稳定化

以前,由于协议错误,如早期断开连接和分段问题,WebSocket 被标记为实验性。

在 Bun 1.1 中,WebSocket 现在稳定,并通过了行业标准的 Autobahn 符合测试套件。这修复了 WebSocket 客户端的许多 bug,并使其更适合生产环境使用。

const ws = new WebSocket("wss://echo.websocket.org/");
ws.addEventListener("message", ({ data }) => {
  console.log("Received:", data);
});
ws.addEventListener("open", () => {
  ws.send("Hello!");
});

performance.mark()

Bun 现在支持 user-timings API,其中包括 performance.mark()performance.measure() 等 API。这对于衡量应用程序的性能非常有用。

performance.mark("start");
while (true) {
  // ...
}
performance.mark("end");
performance.measure("task", "start", "end");

使用 Brotil 压缩的 fetch()

现在您可以使用 fetch() 发送带有 br 编码的请求。这对于向支持 Brotli 压缩的服务器发出请求非常有用。

const response = await fetch("https://example.com/", {
  headers: {
    "Accept-Encoding": "br",
  },
});

URL.canParse()

Bun 现在支持最近添加的 URL.canParse() API。这使得可以在不抛出错误的情况下检查字符串是否为有效的 URL。

URL.canParse("https://example.com:8080/"); // true
URL.canParse("apoksd!"); // false

通过 Unix 套接字发送 fetch()

Bun 现在支持通过 Unix 套接字发送 fetch() 请求。

const response = await fetch("http://localhost/info", {
  unix: "/var/run/docker.sock",
});
const { ID } = await response.json();
console.log("Docker ID:", ID); // <uuid>

尽管这不是浏览器支持的 API,但对于需要通过 Unix 套接字与服务通信的服务器端应用程序(如 Docker 守护程序)非常有用。

Response 体作为 AsyncIterator

现在可以将 AsyncIterator 传递给 Response 构造函数。这对于从不支持 ReadableStream 的源中流式传输数据非常有用。

const response = new Response({
  async *[Symbol.asyncIterator]() {
    yield "Hello, ";
    yield Buffer.from("world!");
  },
});
await response.text(); // "Hello, world!"

这是 Node.js 支持的 fetch() API 的非标准扩展,并且为了兼容性原因已添加到 Bun 中。

其他更改

Bun 是一个与 npm 兼容的软件包管理器

即使您不将 Bun 作为运行时使用,您仍然可以使用 bun install 作为软件包管理器。Bun 是一个与 npm 兼容的软件包管理器,其安装速度比 npm 快多达 29 倍。

自从 Bun 1.0 以来,我们显着提高了 bun install 的稳定性和性能。我们修复了数百个 bug,添加了新功能,并改进了整体开发人员体验。

生命周期脚本

如果您在 Bun 1.0 中使用 bun install 遇到 bug,很可能是与生命周期脚本相关的。生命周期脚本 是在安装软件包期间运行的脚本,例如 postinstall

在 Bun 1.1 中,我们修复了许多这些 bug,并彻底改进了生命周期脚本的工作方式。

Windows 上的生命周期脚本

在 Windows 上,生命周期脚本使用 Bun Shell。这意味着您无需像 rimrafcross-envnode-which 这样的辅助库。

trustedDependencies

默认情况下,Bun 不会为未受信任的软件包运行生命周期脚本。这是一个安全功能,可防止恶意脚本在您的计算机上运行。Bun 只会运行在您的 package.json 中定义的 trustedDependencies 列表中的脚本。

当您第一次添加一个软件包时,Bun 会告诉您是否存在一个未运行的生命周期脚本。

b

un add v1.1.0  
 Saved lockfile  

 installed @biomejs/biome@1.6.1 with binaries:  
  - biome  

 1 package installed [55.00ms]  

 Blocked 1 postinstall. Run `bun pm untrusted` for details.  

bun pm untrusted

如果您想查看哪些脚本被阻止了,可以运行 bun pm untrusted

bun pm untrusted v1.1.0  

./node_modules/@biomejs/biome @1.6.1  
 » [postinstall]: node scripts/postinstall.js  

These dependencies had their lifecycle scripts blocked during install.  

If you trust them and wish to run their scripts, use `bun pm trust`.  

bun pm trust

如果您信任该软件包,可以运行 bun pm trust [package]。如果您想信任每个软件包,也可以运行 bun pm trust --all

bun pm trust v1.1.0  

./node_modules/@biomejs/biome @1.6.1  
 ✓ [postinstall]: node scripts/postinstall.js  

 1 script ran across 1 package [71.00ms]  

bun add --trust

如果您已经知道您想信任一个依赖项,可以使用 bun add --trust [package] 添加它。这将把软件包及其传递依赖项添加到您的 trustedDependencies 列表中,因此您无需为该软件包运行 bun pm trust

{
  "dependencies": {
    "@biomejs/biome": "1.6.1"
  },
   "trustedDependencies": [
     "@biomejs/biome"
   ]
}

Bun 包含一个流行软件包的默认允许列表,这些软件包包含已知安全的生命周期脚本。您可以通过运行 bun pm default-trusted 查看完整列表。

此外,为了减少生命周期脚本的性能影响,我们使其并行运行。这意味着生命周期脚本将并发运行,从而减少安装软件包所需的时间。

bun pm migrate

Bun 使用二进制锁文件 bun.lockb,以便在 bun install 中更快地安装软件包。

现在,您可以运行 bun pm migratepackage-lock.json 文件转换为 bun.lockb 文件。如果您想要从 npm 迁移到 Bun,这将非常有用。

bun pm migrate

[5.67ms] migrated lockfile from package-lock.json 21 packages installed [54.00ms]

如果您使用 bun install,则无需运行此命令,因为如果检测到 package-lock.json 文件,它将自动迁移锁定文件。

Bun 是一个 JavaScript 打包工具

Bun 是一个 JavaScript 和 TypeScript 打包工具、转译器和压缩器,可用于为浏览器、Node.js 和其他平台打包代码。

bun build --target=node

Bun 可以使用 --target=node 标志将代码打包成在 Node.js 上运行的代码。

var { promises } = require("node:fs");
var { join } = require("node:path");
promises.readFile(join(__dirname, "data.txt"));

在 Bun 1.0 中,有几个 bug 会导致这无法正确运行,例如无法要求内置模块如 node:fsnode:path。以下是在 Bun 1.0 中的情况:

bun-1.0 build --target=node app.ts --outfile=dist.mjs
node dist.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 1.1 中,这些 bug 现已修复。

bun build --target=node app.ts --outfile=dist.mjs
node dist.mjs

bun build --compile

Bun 可以使用 --compile 标志将 TypeScript 和 JavaScript 文件编译为单个文件可执行文件。

bun build --compile app.ts
./app
Hello, world!

在 Bun 1.1 中,您还可以嵌入 NAPI (n-api) 插件 .node 文件。这对于打包本机 Node.js 模块(如 @anpi-rs/canvas)非常有用。

import { promises } from "fs";
import { createCanvas } from "@napi-rs/canvas";
const canvas = createCanvas(300, 320);
const data = await canvas.encode("png");
await promises.writeFile("empty.png", data);

然后,您可以将您的应用程序编译并运行为单个文件可执行文件。

bun build --compile canvas.ts
./canvas # => simple.png

Bun 有一个强大的宏系统,可以让您在编译时转换代码。宏可用于生成代码、优化代码,甚至在编译时运行代码。

在 Bun 1.1 中,您现在可以在打包时导入内置模块。

在打包时将文件读入字符串

import { readFileSync } from "node:fs"
  with { type: "macro" };
export const contents = readFileSync("hello.txt", "utf8");

在打包时生成进程

import { spawnSync } from "node:child_process"
  with { type: "macro" };
const result = spawnSync("echo", ["Hello, world!"], {encoding: "utf-8"}).stdout;
console.log(result); // "Hello, world!"

Bun 是一个测试运行器

Bun具有内置的测试模块,使得在JavaScript、TypeScript和JSX中编写和运行测试变得轻松。它支持与Jest相同的API,其中包括 expect() 风格的API。

匹配器

匹配器是您可以使用来测试代码的断言。自Bun 1.0以来,我们已经添加了数十个新的 expect() 匹配器,包括:

import { expect } from "bun:test";
expect.hasAssertions();
expect.assertions(9);
expect({}).toBeObject();
expect([{ foo: "bar" }]).toContainEqual({ foo: "bar" });
expect(" foo ").toEqualIgnoringWhitespace("foo");
expect("foo").toBeOneOf(["foo", "bar"]);
expect({ foo: Math.PI }).toEqual({ foo: expect.closeTo(3.14) });
expect({ a: { b: 1 } }).toEqual({ a: expect.objectContaining({ b: 1 }) });
expect({ a: Promise.resolve("bar") }).toEqual({ a: expect.resolvesTo("bar") });
expect({ b: Promise.reject("bar") }).toEqual({ b: expect.rejectsTo("bar") });
expect.unreachable();

使用 expect.extend() 自定义匹配器

如果Bun不支持某个匹配器,您可以使用 expect.extend() 创建自己的匹配器。当您想要定义一个可重用于多个测试的自定义匹配器时,这非常有用。

import { test, expect } from "bun:test";
expect.extend({
  toBeWithinRange(received, floor, ceiling) {
    const pass = received >= floor && received <= ceiling;
    if (pass) {
      return {
        message: () =>
          `Expected ${received} not to be within range ${floor} - ${ceiling}`,
        pass: true,
      };
    } else {
      return {
        message: () =>
          `Expected ${received} to be within range ${floor} - ${ceiling}`,
        pass: false,
      };
    }
  },
});
test("toBeWithinRange()", () => {
  expect(1).toBeWithinRange(1, 99); // ✅
  expect(100).toBeWithinRange(1, 99); // ❌ Expected 100 to be within range 1 - 99
});

模块模拟

Bun现在支持模块模拟。

  • 与Jest不同,Bun能够模拟ESM和CommonJS模块。
  • 如果一个模块已经被导入,Bun能够在就地更新模块,这意味着模拟在运行时起作用。其他测试运行器不能做到这一点,因为它们在构建时设置模拟。
  • 您可以覆盖任何内容:本地文件、npm包和内置模块。

file.jspackage.jsbuilt-in.jsfile.js

import { mock, test, expect } from "bun:test";
import { fn } from "./mock";
test("模拟本地文件", async () => {
  mock.module("./mock", () => {
    return {
      fn: () => 42,
    };
  });
  // fn 已经被导入,因此它将在就地更新
  expect(fn()).toBe(42);
  // 也适用于cjs
  expect(require("./mock").fn()).toBe(42);
});

package.js

import { mock, test, expect } from "bun:test";
import stringWidth from "string-width";
test("模拟npm包", async () => {
  mock.module("string-width", () => {
    return {
      default: Bun.stringWidth,
    };
  });
  const string = "hello";
  expect(stringWidth()).toBe(5);
  expect(require("string-width")()).toBe(5);
});

built-in.js

import { mock, test, expect } from "bun:test";
import { readFileSync } from "node:fs";
test("模拟内置模块", async () => {
  mock.module("node:fs", () => {
    return {
      readFileSync: () => "模拟!",
    };
  });
  expect(readFileSync("./foo.txt")).toBe("模拟!");
  expect(require("fs").readFileSync("./bar.txt")).toBe("模拟!");
});

Bun 内置支持SQLite

自1.0以来,Bun就已经内置了对SQLite的支持。它有一个受 better-sqlite3 启发的API,但是使用本机代码编写以提高速度。

import { Database } from "bun:sqlite";
const db = new Database(":memory:");
const query = db.query("select 'Bun' as runtime;");
query.get(); // { runtime: "Bun" }

此后,对 bun:sqlite 进行了大量新功能和改进。

多语句查询

我们添加了对多语句查询的支持,允许在单个 run()exec() 调用中运行多个SQL语句,这些语句以 ; 分隔。

import { Database } from "bun:sqlite";
const db = new Database(":memory:");
db.run(`
  CREATE TABLE users (
    id INTEGER PRIMARY KEY,
    name TEXT
  );
  INSERT INTO users (name) VALUES ("Alice");
  INSERT INTO users (name) VALUES ("Bob");
`);

详细错误

当从 bun:sqlite 抛出错误时,您现在将看到更详细的错误,包括表和列名。在Bun 1.0中,错误消息很简短,不包含额外的细节。

- error: 约束失败
+ SQLiteError: UNIQUE 约束失败: foo.bar
+   errno: 2067
+   code: "SQLITE_CONSTRAINT_UNIQUE"
      at run (bun:sqlite:185:11)
      at /index.js:7:1

导入数据库

在Bun 1.1中,您现在可以使用 import 语法导入SQLite数据库。这使用了新的 import attributes 功能来指定导入的类型。

import db from "./

users.db"
  with { type: "sqlite" };
const { n } = db
  .query("SELECT COUNT(id) AS n FROM users")
  .get();
console.log(`发现 ${n} 个用户!`); // "发现 42 个用户!"

嵌入数据库

您还可以将您的应用程序和SQLite数据库编译成一个单文件可执行文件。为此,需要在导入上指定 embed 属性,然后使用 bun build --compile 来构建您的应用程序。

import db from "./users.db"
  with { type: "sqlite", embed: "true" };
bun build --compile ./app.ts
./app
发现 42 个用户!

Bun 使JavaScript变得更简单

我们花了很多时间思考Bun中的开发者体验。我们希望使JavaScript和TypeScript代码的编写、运行和调试变得简单。

我们对命令、输出和错误消息进行了大量改进,以使Bun更易于使用。

语法高亮的错误

当Bun中抛出错误时,它会将堆栈跟踪打印到控制台,并显示多行源代码预览。现在,该源代码预览已经获得了语法高亮,这使得它更容易阅读。

带有和不带有语法高亮的错误预览。

简化的堆栈跟踪

来自 Error.stack 的堆栈跟踪现在包含较少的噪音,例如与错误无关的内部函数。这使得更容易看到错误发生的位置。

1 | throw new Error("糟糕");
          ^
错误: 糟糕
    at /oops.js:1:7
    at globalThis (/oops.js:3:14)
    at overridableRequire (:1:20)
    at /index.js:3:8
    at globalThis (/index.js:3:8)

bun --eval

您可以运行 bun --eval,或者简写为 bun -e,来评估一个脚本而不创建文件。就像Bun的其余部分一样,它支持顶级等待、ESM、CommonJS、TypeScript和JSX。

您可以将脚本作为字符串传递。

bun -e 'console.log(Bun.version)'
1.1.0

或者您可以使用 bun - 将脚本通过 stdin 管道传递。

echo 'console.log(await fetch("https://example.com/"))' | bun -
Response (1.26 KB) {
  status: 200, ...
}

bun --print

您也可以使用 bun --print,它与 bun -e 相同,但是使用 console.log() 打印最后一条语句。

bun --print 'await Bun.file("package.json").json()'
{
  name: "bun",
  dependencies: { ... },
}

您也可以省略 await,因为Bun会检测到悬挂的承诺。

bun --print 'fetch("https://example.com/").then(r => r.text())'
<!DOCTYPE html>
...

bun --env-file

Bun默认检测并加载 .env 文件,但现在您可以使用 bun --env-file 来加载自定义的 .env 文件。这对于测试不同的环境很有用。

bun --env-file=custom.env src/index.ts
bun --env-file=.env.a --env-file=.env.b run build

您可以在运行JavaScript文件或运行package.json脚本时使用 --env-file

行为变更

Bun 1.1 包含了一些行为上的微小调整,您应该注意,但我们认为这些调整极不可能破坏您的代码。

更长的网络超时时间

在Bun 1.0中,fetch()bun install 的默认网络超时时间为30秒。

Bun 1.0.4 开始,将默认网络超时时间增加到了 5分钟。这使得默认与Google Chrome保持一致,并应有助于处理高延迟连接。

您还可以使用 fetch() 禁用超时:

const response = await fetch("https://example.com/", {
  timeout: false,
});

Bun.write() 创建父目录

以前,如果父目录不存在,Bun.write() 将会抛出错误。

import { write } from "bun";
await write("does/not/exist/hello.txt", "Hello!");
// ENOENT: 没有该文件或目录

Bun 1.0.16 开始,如果父目录不存在,Bun将会创建它。

尽管这与像 fs.writeFileSync() 这样的API的行为不匹配,但开发人员要求我们进行此更改,以使API更直观,从而带来更好的开发人员体验。

如果不进行此更改,开发人员将不得不编写以下样板代码:

import { write } from "bun";
import { mkdir } from "node:fs/promises";
try {
  await write("does/not/exist/hello.txt", "Hello!");
} catch (error

) {
  if (error.code === "ENOENT") {
    await mkdir("does/not/exist", { recursive: true });
    await write("does/not/exist/hello.txt", "Hello!");
  } else {
    throw error;
  }
}

如果要恢复旧行为,可以指定 createPath 属性。

import { write } from "bun";
await write("does/not/exist/hello.txt", "Hello, world!", { createPath: false });
// ENOENT: 没有该文件或目录

条件导出不包括 worker

包可以使用 条件导出 来指定不同环境的不同入口文件。例如,一个包可能会为浏览器定义一个 browser 导出,为Node.js定义一个 node 导出。

{
  "exports": {
    "node": "./node.js",
    "browser": "./browser.js",
    "worker": "./worker.js"
  }
}

在Bun 1.0中,Bun会根据以下顺序选择第一个导出:bunworkernode

在Bun 1.1中,Bun将不再选择 worker 导出,因为它与 Web Workers 相关联,这通常假定类似于浏览器的环境。

此更改仅在将Bun用作运行时时适用,并修复了在更适用的 node 导出之前选择 worker 导出的各种错误。

NODE_ENV 默认为 undefined

在Node.js中,process.env.NODE_ENV 默认为 undefined

在Bun的早期开发中,我们将默认值设置为 development,这事实证明是一个错误。这是因为开发人员经常忘记将 NODE_ENV 设置为 production,这可能导致在生产构建中包含开发功能。

在Bun 1.1中,我们将默认 NODE_ENV 更改为 undefined 以匹配Node.js。

bun --print 'process.env.NODE_ENV'
undefined
NODE_ENV=development bun --print 'process.env.NODE_ENV'
development

bun install [package]@latest

以前,如果使用 latest 标签安装包,它会将字面字符串 latest 写入您的 package.json。这不是预期的行为,也不符合其他包管理器的行为。

在Bun 1.1中,latest 标签在写入到 package.json 之前会被解析。

bun install lodash@latest

package.json

{
  "dependencies": {
     "lodash": "latest"
     "lodash": "^4.17.21"
  }
}

Bun.$ 拒绝带有非零退出码的命令

Bun shell 是在 Bun 1.0.24 中引入的。当子进程退出时,承诺会解析,即使退出码为非零。

import { $ } from "bun";
await $`cd /does/not/exist`;
// 不会抛出错误

这通常不是期望的行为,并且会导致错误未被注意到,其中一个命令失败但是承诺解析了。

在Bun 1.1中,当子进程以非零退出码退出时,Bun shell 现在会拒绝并抛出错误。

import { $ } from "bun";
await $`cd /does/not/exist`;
// ShellError: cd /does/not/exist: 没有该文件或目录

如果您想恢复到以前的行为,可以调用 throws() 函数。

import { $ } from "bun";
const { exitCode, stderr } = await $`cd /does/not/exist`.throws(false);
console.log(exitCode); // 1
console.log(stderr); // "cd: /does/not/exist: 没有该文件或目录"

import.meta.resolve()

在Bun 1.0中,import.meta.resolve() 将异步解析为绝对文件路径。

这与Node.js的原始实现相匹配。但是,出于Web API兼容性的原因,Node.js将API更改为同步。因此,Bun也做出了相同的更改。

import.meta.resolve("./foo.js"); // 之前: Promise { "/path/to/foo.js" }
import.meta.resolve("./foo.js"); // 之后: "file:///path/to/foo.js"

修复了一千个错误

自Bun 1.0发布以来,我们已经修复了一千多个错误。

如果您在使用Bun 1.0时遇到错误,我们鼓励您尝试使用Bun 1.1。如果还有一些问题我们没有修复,请随时创建一个新问题或提升一个现有问题。

您可以使用以下命令升级到Bun 1.1:

bun upgrade

显著的修复

在修复了成千上万个错误中,以下是您可能遇到过的一些最常见问题的几个,现在已经修复了。

  • 运行bun install后出现模块未找到
  • WebSocket错误或提前断开连接。
  • Bun.file有时会导致EBADF: 错误的文件描述符错误。
  • 如果存在特定的预/后标签,bun install会解析出不正确的版本。
  • bun install --yarn有时会生成无效的YAML。
  • 在Docker容器中出现无法启动服务器。端口被占用吗?的错误。
  • 在Vercel和Google Cloud上出现"pidfd_open(2)"系统调用不受支持的错误。
  • Bun.serve()对带有_标头的HTTP请求无响应
  • 听取0.0.0.0会同时绑定到IPv6。
  • process.nextTick()setImmediate()的执行顺序与Node.js不同。

性能改进

我们不断对Bun进行更改,以使其更快、更高效。关注@bunjavascript获取“下一个Bun版本”的最新摘要。

以下是自Bun 1.0以来所做的一些性能改进的摘录:

入门

这就是Bun 1.1的全部内容,对于Bun来说,这仍然只是一个开始。

我们使Bun变得更快、更可靠,修复了一千多个错误,添加了大量新功能和API,并且现在Bun支持Windows。要开始使用,请在终端中运行以下任何命令。

安装Bun

curl -fsSL https://bun.sh/install | bash

升级Bun

我们正在招聘工程师、设计师以及过去或现在贡献过JavaScript引擎(如V8、WebKit、Hermes和SpiderMonkey)的人员加入我们的团队,在旧金山现场打造JavaScript的未来。

您可以查看我们的招聘页面或发送电子邮件至jobs@bun.sh

分享于 2024-04-07

访问量 28

预览图片