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