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

生命周期钩子

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

参考

类型概览(完整的类型定义请参阅 Bun 的 bun.d.ts):
Plugin Types

使用方法

插件是一个包含 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() 生命周期回调用于自定义 Bun 解析模块的方式。 onResolve() 的第一个参数是一个包含 filter 和 namespace 属性的对象。filter 是一个针对导入字符串运行的正则表达式。两者共同决定自定义解析逻辑适用于哪些模块。 onResolve() 的第二个参数是一个回调函数,对于 Bun 找到的、与第一个参数中定义的 filter 和 namespace 匹配的每个模块导入,该回调都会运行。 回调会接收匹配模块的 path,并可以为其返回一个 新路径。Bun 会读取 新路径 的内容,并将其解析为模块。 例如,把所有以 images/ 开头的导入重定向到 ./public/images/:
index.ts

onLoad

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

.defer()

onLoad 回调会接收一个 defer 函数,该函数会返回一个 Promise,当 Bun 加载完所有_其他_模块后,该 Promise 就会兑现。当模块内容依赖其他模块时,请等待此函数返回的 Promise。
示例:跟踪并报告未使用的导出
index.ts
每个 onLoad 回调只能调用一次 .defer() 函数。

原生插件

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

用 Rust 创建原生插件

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

onBeforeParse

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