random.ts
cli.tsx
宏使用导入属性(import attribute)语法标记,这是 TC39 Stage 3 提案,用于向 import 语句附加额外元数据。
bun build 打包该文件。打包后的文件会打印到标准输出。
terminal
random 函数的源代码不会出现在打包产物中。相反,它在打包期间运行,调用(random())被替换为其结果。由于源代码永远不会包含在打包产物中,因此宏可以安全地执行特权操作,比如读取数据库。
何时使用宏
对于那些你本来会编写一次性构建脚本的小事情,打包时代码执行更容易维护。它与你其余代码生活在一起,与构建一起运行,自动并行化,并且如果失败了,构建也会失败。 但如果你发现自己在打包时运行了大量代码,不妨考虑运行一个服务器。导入属性
宏是用以下任一方式注解的 import 语句:with { type: 'macro' }——导入属性,一个 Stage 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 节点。 宏在转译器的访问(visiting)阶段同步运行,在插件之前、在转译器生成 AST 之前。它们按导入的顺序运行。转译器会等待每个宏完成后再继续,并 await 宏返回的任何 Promise。 Bun 的打包器是多线程的,因此宏在多个生成的 JavaScript “worker” 中并行执行。死代码消除
打包器在运行并内联宏之后执行死代码消除。给定以下宏:returnFalse.ts
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 请求在打包时发生,结果嵌入到打包产物中。抛出错误的分支也会被消除,因为它是不可达的。