Skip to main content
宏是打包时运行的 JavaScript 函数。Bun 会将它们的返回值直接内联到你的 bundle 中。 作为一个简单示例,考虑下面这个返回随机数的函数。
random.ts
这是一个普通文件中的普通函数,但你可以将它用作宏:
cli.tsx
宏使用导入属性语法标记,这是一项 Stage 4 TC39 提案,用于为导入语句附加额外元数据。
使用 bun build 打包文件。Bun 会将打包后的文件打印到 stdout。
terminal
random 函数的源代码不会出现在 bundle 中。相反,该函数会在打包期间运行,Bun 会将调用(random())替换为其结果。由于源代码不会包含在 bundle 中,宏可以安全地执行读取数据库等特权操作。

何时使用宏

对于一些原本需要编写一次性构建脚本才能完成的小任务,在打包时执行代码会更易于维护。它与其余代码放在一起,也会随其余构建流程一起运行。Bun 会自动并行执行宏;如果宏执行失败,构建也会失败。 不过,如果你发现自己在打包时运行大量代码,考虑改为运行服务器。

导入属性

宏是带有以下注解之一的导入语句:
  • with { type: 'macro' } — 导入属性,是一项 Stage 4 ECMAScript 提案
  • assert { type: 'macro' } — 导入断言,是导入属性的早期形式,现已被弃用(但一些浏览器和运行时已经支持它)

安全注意事项

必须使用 { type: "macro" } 显式导入宏,宏才会在打包时运行。这些导入只有在调用时才会生效,不同于可能产生副作用的普通 JavaScript 导入。 你可以使用 --no-macros 标志完全禁用宏。它会产生如下构建错误:
为减少恶意包可能造成的攻击面,Bun 不允许 node_modules/**/* 中的代码调用宏。如果某个包尝试调用宏,你会看到类似这样的错误:
你的应用代码仍然可以从 node_modules 引入宏并调用它们。
cli.tsx

导出条件 “macro”

当将包含宏的库发布到 npm 或其他包注册表时,使用 "macro" 导出条件,为宏环境提供专用的包版本。
package.json
这个配置允许用户使用相同的导入标识符在运行时或打包时消费你的包:
index.ts
第一个导入解析为 ./node_modules/my-package/index.js;Bun 的打包器会将第二个导入解析为 ./node_modules/my-package/index.macro.js。

执行

当 Bun 的转译器看到宏导入时,它会使用 Bun 的 JavaScript 运行时调用该函数,并将返回值转换为 AST 节点。 宏会在转译器的访问阶段同步运行,此时转译器已经将文件解析为 AST。它们按照调用在文件中出现的顺序运行;转译器只有在遇到对某个宏模块导出的函数的调用时,才会加载或运行该宏模块。转译器会等待每个宏完成后再继续,并会等待宏返回的任何 Promise。 Bun 的打包器是多线程的,因此宏会在多个生成的 JavaScript“工作线程”中并行执行。

死代码消除

打包器会在运行并内联宏之后执行死代码消除。给定以下宏:
returnFalse.ts
……在启用 minify 语法选项的情况下,打包以下文件会生成一个空的 bundle。
index.ts

可序列化性

Bun 的转译器必须能够序列化宏的结果,以便将其内联到 AST 中。所有与 JSON 兼容的数据结构都受支持:
macro.ts
宏可以是异步的,也可以返回 Promise 实例。Bun 的转译器会等待 Promise,并将结果内联。
macro.ts
转译器实现了特殊逻辑,用于序列化 Response 和 Blob 等常见数据格式。
  • Response:Bun 会读取 Content-Type 并据此进行序列化。例如,它会将类型为 application/json 的 Response 解析为对象,并将 text/plain 内联为字符串。对于类型无法识别或未定义的 Response,Bun 会使用 base64 编码。
  • Blob:与 Response 一样,序列化方式取决于 type 属性。
fetch 的结果是 Promise<Response>,因此宏可以直接返回它。
macro.ts
函数和大多数类的实例(之前列出的实例除外)都无法序列化。
macro.ts

参数

宏可以接受输入,但仅限于有限情况。参数值必须是静态已知的。例如,以下用法不被允许:
index.ts
但是,如果 foo 的值在打包时已知(例如,它是一个常量或另一个宏的结果),那么就允许这样调用:
index.ts
其输出为:

示例

嵌入最新 git 提交哈希

getGitCommitHash.ts
打包时,Bun 会将 getGitCommitHash 调用替换为调用该函数的结果:
你可能会想:“为什么不使用 process.env.GIT_COMMIT_HASH?”当然也可以。但你能用环境变量做到这件事吗?

在打包时执行 fetch() 请求

此示例使用 fetch() 发出 HTTP 请求,使用 HTMLRewriter 解析 HTML 响应,并在打包时返回一个包含标题和 meta 标签的对象。
meta.ts
Bun 会在打包时擦除 extractMetaTags 函数,并将其替换为函数调用的结果。fetch 请求会在打包时发生,Bun 会将结果嵌入 bundle 中。启用 minify 语法选项后,由于抛出错误的分支不可达,Bun 也会将其消除。