Skip to main content
Bun 有一个通用的插件 API,可以同时扩展_运行时_和_打包器_。 插件拦截导入并执行自定义加载逻辑,例如读取文件或转译代码。用于增加对其他文件类型的支持,例如 .scss.yaml。在 Bun 的打包器中,插件可以实现框架级功能,如 CSS 提取、宏和客户端-服务器代码共存。

生命周期钩子

插件注册在打包生命周期的各个点运行的回调:
  • onStart():打包器开始打包后运行一次
  • onResolve():在模块被解析之前运行
  • onLoad():在模块被加载之前运行
  • onBeforeParse():在文件被解析之前,在解析器线程中运行零拷贝原生插件

参考

类型的大致概览(完整类型定义见 Bun 的 bun.d.ts):
插件类型

使用

插件的定义是一个包含 name 属性和 setup 函数的 JavaScript 对象。
myPlugin.ts
在调用 Bun.build 时,将插件传入 plugins 数组。
index.ts

插件生命周期

命名空间

onLoadonResolve 接受一个可选的 namespace 字符串。每个模块都有一个命名空间,它在转译代码中作为导入的前缀;例如,使用 filter: /\.yaml$/namespace: "yaml:" 的加载器会将 ./myfile.yaml 的导入转换为 yaml:./myfile.yaml 默认命名空间是 "file",你通常不需要指定它:import myModule from "./my-module.ts" 等同于 import myModule from "file:./my-module.ts" 其他常见的命名空间有:
  • "bun":用于 Bun 特定模块("bun:test""bun:sqlite"
  • "node":用于 Node.js 模块("node:fs""node:path"

onStart

注册一个回调,当打包器开始新打包时运行。
index.ts
回调可以返回 Promise。在打包过程初始化后,打包器会等待所有 onStart() 回调完成,然后再继续。 例如:
index.ts
这里,Bun 等待两个 onStart() 回调完成:第一个睡眠 10 秒,第二个将打包时间写入文件。 onStart() 回调(与每个其他生命周期回调一样)不能修改 build.config 对象。要修改 build.config,请在 setup() 函数中直接进行。

onResolve

要打包项目,Bun 会遍历项目中所有模块的依赖树。对于每个导入的模块,Bun 需要找到并读取该模块。“查找”部分被称为”解析”模块。 onResolve() 生命周期回调自定义模块的解析方式。 onResolve() 的第一个参数是一个包含 filternamespace 属性的对象。filter 是一个对导入字符串运行的正则表达式。它们共同决定了你的自定义解析逻辑适用于哪些模块。 onResolve() 的第二个参数是一个回调,为 Bun 找到的每个匹配第一个参数中 filternamespace 的模块导入运行。 回调接收匹配模块的 路径,并可以返回一个 新路径。Bun 会读取 新路径 的内容并将其解析为模块。 例如,将所有 images/ 的导入重定向到 ./public/images/
index.ts

onLoad

在 Bun 的打包器解析完模块后,它需要读取和解析模块的内容。 使用 onLoad() 生命周期回调在 Bun 读取和解析模块之前修改模块的_内容_。 onResolve() 类似,onLoad() 的第一个参数过滤此 onLoad() 调用适用的模块。 onLoad() 的第二个参数是一个回调,在 Bun 将每个匹配模块的内容加载到内存之前运行。 回调接收匹配模块的 路径、它的 命名空间、该文件的默认 加载器 以及一个 defer 函数。 回调可以为该模块返回新的 contents 字符串以及新的 loader 例如:
index.ts
此插件将所有 import env from "env" 形式的导入转换为导出当前环境变量的 JavaScript 模块。

.defer()

onLoad 回调接收一个 defer 函数,该函数返回一个在所有 其他 模块加载完成后解析的 Promise。当一个模块的内容依赖于其他模块时,请 await 它。
示例:跟踪和报告未使用的导出
index.ts
.defer() 函数每个 onLoad 回调只能调用一次。

原生插件

Bun 的打包器之所以快,部分原因在于它是用原生代码编写的,并且使用多个线程并行加载和解析模块。用 JavaScript 编写的插件无法利用这一点,因为 JavaScript 本身是单线程的。 原生插件被编写为 NAPI 模块,可以在多个线程上运行,因此它们比 JavaScript 插件运行得快得多。它们还可以跳过不必要的工作,例如将字符串传递给 JavaScript 所需的 UTF-8 到 UTF-16 转换。 以下生命周期钩子可用于原生插件:
  • onBeforeParse():在任何线程上,在 Bun 的打包器解析文件之前调用。
原生插件是 NAPI 模块,它将生命周期钩子暴露为 C ABI 函数。要创建一个,导出一个匹配你要实现的生命周期钩子签名的 C ABI 函数。

在 Rust 中创建原生插件

terminal
然后安装这个 crate:
terminal
lib.rs 中,使用 bun_native_plugin::bun 过程宏定义一个实现你的原生插件的函数。 这是一个实现 onBeforeParse 钩子的示例:
lib.rs
Bun.build() 中使用:

onBeforeParse

此生命周期回调在 Bun 的打包器解析文件之前立即运行。 它接收文件的内容,并可以返回新的源代码。 该回调可以从任何线程调用,因此 NAPI 模块实现必须是线程安全的。