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

插件生命周期

命名空间

onLoad 和 onResolve 接受一个可选的 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() 的第一个参数是一个包含 filter 和 namespace 属性的对象。filter 是一个对导入字符串运行的正则表达式。它们共同决定了你的自定义解析逻辑适用于哪些模块。 onResolve() 的第二个参数是一个回调,为 Bun 找到的每个匹配第一个参数中 filter 和 namespace 的模块导入运行。 回调接收匹配模块的 路径,并可以返回一个 新路径。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 模块实现必须是线程安全的。