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

生命周期钩子

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

参考

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

使用方法

一个插件被定义为一个包含 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 匹配的每个模块导入,该回调都会运行。 回调会接收匹配模块的 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,当所有 其他 模块都加载完成后,该 Promise 才会解析。当模块的内容依赖其他模块时,请等待该 Promise。
示例:跟踪并报告未使用的导出
index.ts
.defer() 函数在每个 onLoad 回调中只能调用一次。

原生插件

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 的打包器解析文件之前立即运行。 它会接收文件内容,并可以返回新的源代码。 此回调可能会从任意线程调用,因此 NAPI 模块的实现必须是线程安全的。