如果在工作目录或更高层级找不到 node_modules 目录,Bun 将放弃 Node.js 风格的模块解析,转而采用 Bun 模块解析算法。
在 Bun 风格的模块解析下,所有导入的包在执行期间都会即时自动安装到全局模块缓存中(与 bun install 使用的缓存相同)。
import { foo } from "foo"; // install `latest` version
foo();
首次运行此脚本时,Bun 将自动安装 "foo" 并缓存它。下次运行脚本时,它将使用缓存的版本。
版本解析
为了确定要安装哪个版本,Bun 遵循以下算法:
- 检查项目根目录中是否存在
bun.lock文件。如果存在,则使用 lockfile 中指定的版本。 - 否则,向上扫描目录树,查找包含
"foo"作为依赖项的package.json。如果找到,则使用指定的 semver 版本或版本范围。 - 否则,使用
latest。
缓存行为
一旦确定了版本或版本范围,Bun 将执行以下操作:
- 检查模块缓存中是否存在兼容版本。如果存在,则使用它。
- 解析
latest时,Bun 将检查package@latest是否在过去 *24 小时*内已下载并缓存。如果是,则使用它。 - 否则,从
npm注册表下载并安装相应的版本。
安装
包安装并缓存到 <cache>/<pkg>@<version> 中,因此同一包的多个版本可以同时缓存。此外,在 <cache>/<pkg>/<version> 下创建一个符号链接,以加快查找缓存中存在的所有包版本的速度。
版本说明符
通过直接在 import 语句中指定版本或版本范围,可以短路整个解析算法。
import { z } from "zod@3.0.0"; // specific version
import { z } from "zod@next"; // npm tag
import { z } from "zod@^3.20.0"; // semver range
优点
这种自动安装方法有几个优点:
- 空间效率 — 依赖项的每个版本只存在于磁盘上的一个位置。与冗余的每个项目安装相比,这节省了大量的空间和时间。
- 可移植性 — 要共享简单的脚本和 Gist,您的源文件是*自包含的*。无需将包含您的代码和配置文件的目录压缩在一起。通过在
import语句中指定版本,甚至不需要package.json。 - 便利性 — 在运行文件或脚本之前,无需运行
npm install或bun install。只需bun run即可。 - 向后兼容性 — 因为如果存在
package.json,Bun 仍然尊重其中指定的版本,所以您可以通过一个命令切换到 Bun 风格的解析:rm -rf node_modules。
限制
- 无 Intellisense。IDE 中的 TypeScript 自动补全依赖于
node_modules中类型声明文件的存在。我们正在调查各种解决方案。 - 不支持 patch-package
常见问题
这与 pnpm 的做法有何不同?
这与 Yarn Plug'N'Play 的做法有何不同?
这与 Deno 的做法有何不同?