random.ts
cli.tsx
宏使用导入属性语法进行标记。这是一项处于第 3 阶段的 TC39 提案,用于向导入语句附加额外的元数据。
bun build 打包该文件。打包后的文件会输出到 stdout。
terminal
random 函数的源代码不会出现在打包文件中的任何位置。相反,它会在打包过程中运行,并且调用(random())会被替换为其结果。由于源代码从未包含在打包文件中,因此宏可以安全地执行读取数据库等需要特权的操作。
何时使用宏
对于那些原本需要编写一次性构建脚本来完成的小任务,在打包时执行代码可能更易于维护。它与其余代码共存,与其余构建过程一同运行,会自动并行执行,而且如果它失败,构建也会失败。 不过,如果你发现自己在打包时运行大量代码,考虑改为运行服务器。导入属性
宏是带有以下注解之一的导入语句:with { type: 'macro' }— 导入属性,一项处于第 3 阶段的 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 会被编码为 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 请求会在打包时执行,结果会被嵌入到打包产物中。由于抛出错误的分支不可达,因此也会被移除。