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 标志完全禁用宏。它会产生如下构建错误:
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
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
getGitCommitHash 调用替换为调用该函数的结果:
你可能会想:“为什么不使用
process.env.GIT_COMMIT_HASH?”当然也可以。但你能用环境变量做到这件事吗?在打包时执行 fetch() 请求
此示例使用fetch() 发出 HTTP 请求,使用 HTMLRewriter 解析 HTML 响应,并在打包时返回一个包含标题和 meta 标签的对象。
meta.ts
extractMetaTags 函数,并将其替换为函数调用的结果。fetch 请求会在打包时发生,Bun 会将结果嵌入 bundle 中。启用 minify 语法选项后,由于抛出错误的分支不可达,Bun 也会将其消除。