本文记录一次在 NixOS(Flakes + home-manager)上从 nodejs/npm/pnpm 全面迁移到 bun 的过程,包括审计、配置修改、踩坑修复和缓存清理,供同样想统一 JS/TS 工具链的读者参考。
背景与目标
- 本机原先并存两套环境:nodejs_26 + pnpm(nix 安装)与 bun(一体化运行时 + 包管理器)
- 目标:彻底移除 node/npm/pnpm 命令,所有 JS/TS 运行、包管理、临时执行统一走 bun
- 迁移前先审计,确认没有不可迁移的软件
一、审计:npm/pnpm 安装的软件能否迁移
- npm 全局包:空(npm ls -g 无输出,历史已清空)
- pnpm 全局包:空(pnpm ls -g 无输出)
- npx 历史缓存(~/.npm/_npx/):playwright、wrangler、eslint、prettier、tsx、shadcn、chrome-devtools-mcp 等,全部可用 bunx 替代
- 项目内 npm 安装点:modules/ai/cfg_opencode/ 插件目录(npm install @opencode-ai/plugin),纯 JS 包,bun install 完全兼容
- 结论:无阻塞项,可以彻底迁移
二、修改 NixOS 配置
1. 移除运行时与包管理器
modules/dev/lang_runtime_and_tools.nix 中注释并最终删除 nodejs_26、pnpm,只保留 bun:
| |
2. 移除环境变量与 PATH
modules/system/env_manager.nix 中删除:
- PNPM_HOME(pnpm 全局目录)
- NODE_OPTIONS(node 专属的代理参数,bun 不读取;bun 原生支持环境代理)
- PATH 中的 ~/.local/share/pnpm/bin
3. 移除 permittedInsecurePackages 遗留
modules/system/nixpkgs_system.nix 中删除 nodejs-20.20.2、nodejs-slim-20.20.2。删除前用 nix-store 验证:
| |
无任何包依赖,纯历史遗留,可安全移除。
4. 迁移项目插件目录
cfg_opencode 由 npm 迁移到 bun:
| |
生成 bun.lock,package-lock.json 归档到 archive.7z 以便回退。
5. 保留项
- ~/.npmrc 符号链接保留:bun 完全兼容 //registry.npmjs.org/:_authToken,供 bun install/bunx 认证
- nix 打包的 LSP 工具(typescript-language-server、vscode-langservers-extracted、prettierd)保留:它们内部自带 nodejs,不依赖系统 node
三、关键坑:bun 全局包的 node shebang
rebuild 后立即踩坑:pi-coding-agent(bun 全局包)运行报错:
| |
原因:
- bun install -g 安装的包,bin 入口是直接 symlink 到源文件,且不重写 shebang
- 源文件 shebang 多为 #!/usr/bin/env node,移除系统 node 后自然找不到
修复方案(~/.local/bin/node 符号链接指向 bun):
| |
要点:
- 必须用 symlink 而非 shell 包装脚本:symlink 时 argv[0]=node,bun 自动进入 node 兼容模式;shell 脚本 exec bun 会把 argv[0] 变为 bun(exec -a 仅 bash 支持,/bin/sh 为 dash),失去兼容模式
- 已知限制:bun 的 node 兼容模式不支持 node –version / node -v 等版本查询参数,node -e 与脚本执行正常
- 部分工具自带 bun 原生入口可优先使用:pi-coding-agent 有 dist/bun/cli.js(含 bun-oauth 适配),把 ~/.bun/bin/pi 指过去并以 chmod +x 即可完全走 bun
npx 兼容层:为硬编码调用兜底
除 node shebang 外,生态里还有大量工具硬编码调用 npx(VSCode 的 Prettier/ESLint 扩展、#!/usr/bin/env npx 脚本、spawn('npx', ...) 等)。给 ~/.local/bin/npx 加一个 shell 包装脚本,指向 bunx:
| |
要点:
- 不能 symlink 到 bunx:bun 靠 argv[0] 判断是否进入 x 模式,symlink 名为 npx 时 argv[0]=npx 不触发,报
error: Script not found;shim 里 exec bunx 后 argv[0]=bunx,正确触发 - 与 node 恰好相反:node 需要 argv[0]=node(所以用 symlink),npx 需要 argv[0]=bunx(所以用 shell 包装)
- 为什么不用 alias:alias 只对交互式 shell 生效,VSCode 插件/脚本 spawn npx 是直接按 PATH 找可执行文件,完全绕过 shell
- 踩坑:新版 home-manager(2026-04-24 起)的 home.file 已移除 mode 选项,text 文件设执行位用
executable = true,写mode = "755"会报 option does not exist - 验证:
npx --yes prettier --version→ 3.8.3
四、清理历史缓存
| |
五、验证结果
| |
- npm/pnpm 命令完全消失
- bun 全局包 pi-coding-agent、opencode-ai 全部可用
- LSP 工具正常
- 迁移记录归档在 os-config 仓库 static_file/archive.7z 内的 nodejs_pnpm.md
相关文章
- NixOS 入门指南(第二版):其中 5.4 节的 node.nix 示例(nodejs_22 + pnpm + bun 并存)已过时,可按本文方案统一为 bun
- 使用 nix 清理磁盘:nix 磁盘清理思路,本文第四节的缓存清理是其中的一种场景
- mkOutOfStoreSymlink 的一个神坑:本文的 ~/.npmrc 与 node 兼容层 symlink 均使用了该机制