Bun

指南运行时

使用 Bun 定义和替换静态全局变量和常量

--define 标志允许你声明静态可分析的常量和全局变量。它会将 JavaScript 或 TypeScript 文件中标识符或属性的所有用法替换为一个常量值。此功能在运行时和 `bun build` 中都支持。这有点类似于 C/C++ 中的 `#define`,但用于 JavaScript。

bun --define process.env.NODE_ENV="'production'" src/index.ts # Runtime
bun build --define process.env.NODE_ENV="'production'" src/index.ts # Build

这些静态已知值被 Bun 用于死代码消除和其他优化。

if (process.env.NODE_ENV === "production") {
  console.log("Production mode");
} else {
  console.log("Development mode");
}

在代码到达 JavaScript 引擎之前,Bun 会将 `process.env.NODE_ENV` 替换为 `"production"`。

if ("production" === "production") {
  console.log("Production mode");
} else {
  console.log("Development mode");
}

但这还不是全部。Bun 的优化转译器足够智能,可以执行一些基本的常量折叠。

由于 `"production" === "production"` 始终为 `true`,Bun 会将整个表达式替换为 `true` 值。

if (true) {
  console.log("Production mode");
} else {
  console.log("Development mode");
}

最后,Bun 检测到 `else` 分支是不可达的,并将其删除。

console.log("Production mode");

支持哪些类型的值?

值可以是字符串、标识符、属性或 JSON。

替换全局标识符

要将 `window` 的所有用法替换为 `undefined`,可以使用以下命令。

bun --define window="undefined" src/index.ts

这在服务器端渲染 (SSR) 时很有用,或者当你想确保代码不依赖于 `window` 对象时。

if (typeof window !== "undefined") {
  console.log("Client-side code");
} else {
  console.log("Server-side code");
}

你也可以将值设置为另一个标识符。例如,要将 `global` 的所有用法设置为 `globalThis`,可以使用以下命令。

bun --define global="globalThis" src/index.ts

`global` 是 Node.js 中的一个全局对象,但在 Web 浏览器中不是。因此,你可以使用此功能来修复一些代码假定 `global` 可用的情况。

用 JSON 替换值

--define 也可用于用 JSON 对象和数组替换值。

要将 `AWS` 的所有用法替换为 JSON 对象 `{"ACCESS_KEY":"abc","SECRET_KEY":"def"}`,可以使用以下命令。

# JSON
bun --define AWS='{"ACCESS_KEY":"abc","SECRET_KEY":"def"}' src/index.ts

这些将被转换为等效的 JavaScript 代码。

console.log(AWS.ACCESS_KEY); // => "abc"

console.log("abc");

用其他属性替换值

你也可以将属性传递给 `--define` 标志。

例如,要将 `console.write` 的所有用法替换为 `console.log`,可以使用以下命令(需要 Bun v1.1.5 或更高版本)

bun --define console.write=console.log src/index.ts

这会将以下输入转换为

console.write("Hello, world!");

转换为以下输出

console.log("Hello, world!");

这与设置变量有什么不同?

你也可以在代码中将 `process.env.NODE_ENV` 设置为 `"production"`,但这无助于死代码消除。在 JavaScript 中,属性访问可能具有副作用。Getter 和 Setter 可以是函数,甚至可以动态定义(由于原型链和 Proxy)。即使你将 `process.env.NODE_ENV` 设置为 `"production"`,在下一行,静态分析工具也不能安全地假定 `process.env.NODE_ENV` 是 `"production"`。

这与查找和替换或字符串替换有什么不同?

--define 标志在 AST(抽象语法树)级别运行,而不是在文本级别。它发生在转译过程中,这意味着它可以用于死代码消除等优化。

字符串替换工具往往存在转义问题,并会替换代码中不相关的部分。