首页 / Bun 入门教程 / 依赖管理进阶

Bun 入门教程

依赖管理进阶

本教程共 34 篇 · 第 14 篇 · 更新于 2026-08-06

Bun依赖管理全局缓存生命周期脚本trustedDependenciesoverridesresolutionsbun patch最小发布年龄

本节目标:

  • 理解 Bun 的全局缓存~/.bun/install/cache)如何复用已下载的包、用 hardlink / clonefile 加速并省磁盘。
  • 掌握生命周期脚本的安全模型:默认不执行 postinstall,以及如何用 trustedDependencies 精确放行。
  • 会用 overrides / resolutions 强制”依赖的依赖”使用指定版本。
  • 学会用 bun patch 给第三方包打补丁并纳入版本库,以及了解 isolated 安装与最小发布年龄等工程/安全细节。

14.1 全局缓存

Bun 把从 registry 下载过的每一个包版本都存进一个全局缓存,默认位于 ~/.bun/install/cache(也可用环境变量 BUN_INSTALL_CACHE_DIR 覆盖)。缓存目录里按 ${name}@${version} 命名子目录,因此同一个包的多个版本可以同时并存。

它的价值体现在两方面:

  1. 避免重复下载:安装时如果缓存里已经有 package.json 范围内匹配的版本,Bun 直接复用,不再联网。
  2. 快速拷贝到 node_modules:下载进缓存后,Bun 仍要把文件”放”进项目的 node_modules。这一步它用平台最快的系统调用——Linux / Windows 上用 hardlink(硬链接),macOS 上用 clonefile(写时复制)。硬链接意味着包内容在磁盘上只有一份,多个项目共享,极大节省空间。
Note

若 semver 版本带预发布后缀(如 1.0.0-beta.0)或构建后缀(如 1.0.0+20220101),Bun 会用该值的哈希替换这部分字符串,以降低超长文件路径导致出错的概率。

缓存行为可在 bunfig.toml 里微调:

[install.cache]
dir = "~/.bun/install/cache"  # 缓存目录
disable = false               # 为 true 时不从全局缓存加载(仍可能写 node_modules/.cache)
disableManifest = false        # 为 true 时总是向 registry 请求最新元数据

需要彻底清理缓存时:

bun pm cache rm
# 或等价地
rm -rf ~/.bun/install/cache

14.2 生命周期脚本与 trustedDependencies

npm 上的包可以在 package.json 里定义生命周期脚本,例如:

  • preinstall:安装前运行
  • postinstall:安装后运行(常用于为原生插件下载/构建平台二进制,比如 sharpesbuildnode-sass
  • preuninstall / prepublishOnly

这类脚本本质是”任意 shell 命令”。Bun 的设计取向是默认安全:它不会自动执行已安装依赖包的生命周期脚本(这一点和 npm/yarn/pnpm 的默认行为不同)。理由很直接——执行第三方任意代码是供应链攻击的高风险面。

Tip

自己项目{pre|post}install{pre|post}prepare 脚本依然会照常运行,被限制的只是”依赖带来的”脚本。

如果你确实需要某个依赖跑它的 postinstall,把它加入 trustedDependencies 白名单:

{
  "name": "my-app",
  "version": "1.0.0",
  "trustedDependencies": ["node-sass"]
}

加入后重新安装,Bun 就会为该包运行生命周期脚本。Bun 还内置了一份常用可信包列表(针对 npm 来源的包默认放行,例如 sharpesbuild 等),所以多数常见原生包开箱即用。

这里有个容易踩坑的细节:trustedDependencies 一旦显式定义,就会”替换”而非”追加”默认列表。三种情形如下:

package.json 状态允许运行脚本的包
完全不写 trustedDependencies仅 Bun 内置列表(仅限 npm 来源)
写了具体列表 ["pkg-a", ...]只有列表里的包,默认列表被忽略
写成空数组 []任何包都不行(比 --ignore-scripts 更彻底)
Warning

内置可信列表只适用于从 npm 安装的包。对 file:link:git:github: 来源的依赖,即便名字恰好命中内置列表,也必须显式加进 trustedDependencies——这是为了防止恶意包通过本地路径或 git 仓库”冒充”可信包名。

全局禁用的方式也有两种:命令行 --ignore-scripts,或在 bunfig.toml / .npmrc 里设置 install.ignoreScripts=true / ignore-scripts=true

14.3 用 overrides / resolutions 锁定”依赖的依赖”

假设你只声明了一个直接依赖 foo,而 foo 又依赖 barbar 就是你的元依赖(metadependency)。若 bar@4.5.6 出现了安全漏洞,你想强制把它钉到更旧的 4.4.x,可以用 npm 的 overrides 或 Yarn 的 resolutions(Bun 两者都支持):

{
  "name": "my-app",
  "dependencies": {
    "foo": "^2.0.0"
  },
  "overrides": {
    "bar": "~4.4.0"
  }
}
{
  "name": "my-app",
  "resolutions": {
    "bar": "~4.4.0"
  }
}

效果一致:无论 bar 是直接依赖还是某包的依赖,Bun 在解析版本时都会服从这里指定的范围。这在进行安全应急处理、或统一某深层依赖的版本时非常有用。

Note

Bun 目前只支持顶层的 overrides / resolutions,不支持嵌套写法。

14.4 用 bun patch 给依赖打补丁

偶尔你需要对 node_modules 里某个包做一点小改动(修 bug、加特性)。直接改 node_modules 不行——下次安装就丢了。bun patch 提供了可持久化、对 git 友好的方案。

它的思路是:生成 .patch 文件,提交进你的仓库;之后每次 bun install 都会自动把这些补丁应用到对应依赖上,同时不破坏全局缓存的完整性。补丁文件还能跨项目、跨机器复用。

完整三步:

# 第 1 步:准备要打补丁的包(会生成一份"去链接"的副本,避免误改全局缓存)
bun patch react
# 也可指定版本或路径:bun patch react@17.0.2  /  bun patch node_modules/react

# 第 2 步:直接编辑 node_modules/react 里的文件,本地验证效果

# 第 3 步:提交改动,生成 patches/ 下的补丁并更新 package.json 与锁文件
bun patch --commit react
# 指定补丁目录:bun patch --commit react --patches-dir=mypatches

提交后,package.json 里会通过 "patchedDependencies" 记录被打补丁的包,补丁文件落在 patches/bun patch-commit 也提供,以兼容 pnpm 习惯。

Warning

不要跳过 bun patch <pkg> 这一步。它确保 node_modules/ 中的包是一份”无 symlink / hardlink”的干净副本。若直接编辑,可能改到的是全局缓存里的包,污染其他项目。

14.5 安装策略:isolated 与 hoisted

第 11 章提到过,bun install --linker 有两种 node_modules 组织方式:

  • hoisted(平铺):传统 npm/Yarn 风格,依赖被扁平化到共享的 node_modules,可能存在”幽灵依赖”(没声明却能 import)。
  • isolated(隔离):类似 pnpm,在 node_modules/.bun/ 建一个中央包仓库,顶层 node_modules 用符号链接指向它,包只能访问自己声明过的依赖。

默认策略:isolated 用于新建工作区/monorepo(防幽灵依赖),hoisted 用于新建单包项目(贴近 npm)以及 v1.3.2 之前创建的旧项目(保兼容)。底层由锁文件里的 configVersion 决定。

Tip

文件拷贝的”后端”也可用 --backend 强制指定:hardlink(Linux/Windows 默认)、clonefile(macOS 默认)、clonefile_each_dircopyfile(兜底)、symlink(仅用于 file: 依赖)。正常情况下让 Bun 自动选最快的即可。

14.6 最小发布年龄(供应链防护)

为防范”刚发布就被塞入恶意代码”的供应链攻击,Bun 支持给 npm 包设置最小发布年龄:安装时会过滤掉”发布时间距今不足阈值”的版本。

# 只安装至少 3 天前发布的版本(259200 秒)
bun add @types/bun --minimum-release-age 259200

bunfig.toml 里配置更持久:

[install]
minimumReleaseAge = 259200               # 秒
minimumReleaseAgeExcludes = ["@types/node", "typescript"]  # 对这些包豁免

启用后:只影响新解析的包;已存在于 bun.lock 的包保持不变;所有直接/间接依赖都会被过滤;若某版本被年龄门槛挡住,Bun 还会做稳定性检查——如果门槛外紧接着连续发布了多个版本(疑似快速打补丁的异常模式),它会把过滤范围扩大、挑选更成熟的旧版本。

14.7 小结

  • 全局缓存 ~/.bun/install/cache 复用已下载包,配合 hardlink / clonefile 既快又省磁盘。
  • 生命周期脚本默认不执行(默认安全),需要时用 trustedDependencies 精确放行;注意显式列表会替换默认列表。
  • overrides / resolutions 用于强制”依赖的依赖”版本,适合安全应急与版本统一。
  • bun patch 以 git 友好的 .patch 文件持久化修改第三方包;安装策略提供 isolated/hoisted 选择;最小发布年龄是额外的供应链防护。

下一章我们把 Bun 和 npm/yarn/pnpm 做一个整体对比,并汇总常见命令速查与 CI 加速技巧。