前言
大家好,我是 sonion。
最近在搭新项目时,发现用 only-allow 限制包管理器强制用 pnpm 的方案失效了,排查半天才发现 only-allow 不支持 pnpm v11。
于是,我开始寻找新的方案。一个可选的方案是: packageManager + node corepack,但使用率不高,还要求成员都打开 corepack,有点麻烦。并且packageManager 字段要求的是精确版本,不太灵活。
翻了下 only-allow 支持计划,才发现 only-allow 停止维护了,推荐使用 devEngines。devEngines何许人也?🤔了解后才发现 devEngines 才是固定 包管理器 和 运行时 的终极解。
为什么要固定包管理器和运行时?
原因很简单:绝大多数的项目运行问题都是 运行时版本不匹配:
- Node.js 版本不一致,导致语法报错或 API 缺失
- 不同包管理器安装的依赖有差异,导致运行时行为不同
- 新人加入团队,用错了包管理器,装完依赖后项目直接崩掉
所以,固定包管理器和运行时环境是前端工程化的必要环节。
为什么放弃 only-allow?
先看看我们以前的方案:
json
{
"scripts": {
"preinstall": "npx only-allow pnpm"
}
}
看起来没问题?但有几个致命缺点:
1. 只能限制包管理器,无法限制运行时
only-allow 只能检测你用了哪个包管理器,不能限制版本,还要实时安装 only-allow。
3. 停止维护,不支持 pnpm v11
only-allow 已经归档,对于 pnpm v11 的新特性完全没有适配。在 pnpm v11 下完全失效。
devEngines 是什么?
devEngines 是 OpenJS Foundation 正在标准化的一个新字段,专门用于约束开发环境。它不仅可以限制包管理器,还能限制运行时版本。
最重要的是,它是标准方案,不依赖第三方库,npm、pnpm 都已支持。yarn 等待跟进。
devEngines 的用法
配置 packageManager
json
{
"devEngines": {
"packageManager": {
"name": "pnpm",
"version": ">=11.4.0",
"onFail": "error"
}
}
}
配置 runtime
json
{
"devEngines": {
"runtime": {
"name": "node",
"version": ">=24.15.0",
"onFail": "error"
}
}
}
完整配置
json
{
"devEngines": {
"packageManager": {
"name": "pnpm",
"version": ">=11.4.0",
"onFail": "error"
},
"runtime": {
"name": "node",
"version": ">=24.15.0",
"onFail": "error"
}
}
}
onFail 选项详解
onFail 是 devEngines 的核心能力,它定义了当环境不满足要求时的行为:
| onFail | 行为 |
|---|---|
error |
直接报错,阻止操作 |
warn |
显示警告,但允许继续 |
ignore |
完全忽略 |
download |
自动下载正确的版本(pnpm 支持) |
devEngines vs engines:到底有什么区别?
这是很多人混淆的点:
| 字段 | 作用阶段 | 触发时机 | 适用场景 |
|---|---|---|---|
engines |
运行时约束 | 被作为第三方库按安装时约束/npm install 安装第三方库时校验 |
告诉「运行我这个包需要什么环境」 |
devEngines |
开发时约束 | pnpm install / npm install 安装当前项目时校验 |
告诉「开发我这个项目需要什么环境」 |
- engines :运行时限制,发布包的消费者 。当你的项目作为 npm 库发布时,
engines的值会作为安装限制。在开发阶段也可以勉强用,如果希望开发环境使用高版本,运行时兼容更低版本时,就不好用了。 - devEngines :开发时限制,用于开发者自己。它约束的是开发环境,发布时不会生效
最佳实践是两者都用:
json
{
"engines": {
"node": ">=22.0.0"
},
"devEngines": {
"runtime": {
"name": "node",
"version": ">=24.15.0",
"onFail": "error"
},
"packageManager": {
"name": "pnpm",
"version": ">=11.4.0",
"onFail": "error"
}
}
}
这样做的好处是:
engines限制使用你的库的项目需要满足什么条件devEngines限制开发这个项目的环境需要满足什么条件
兼容性情况
目前各包管理器的支持情况:
| 包管理器 | devEngines 支持 | 版本要求 |
|---|---|---|
| npm | ✅ | v10.9+ |
| pnpm | ✅ | v10.14+(v11.4 强校验) |
| yarn | ❌ | 无计划 |
推荐版本组合:
- Node.js 22.10+
- npm v10.9+
- pnpm v11.8+
注意事项
1. 配置了包管理器,项目中所有命令都需要使用对应的包管理器提供的否则会被识别到和限制不一致
如:npm view、npm pkg、npm i 等都要使用 pnpm view、pnpm pkg、pnpm i ...
2.prepare 等钩子中不能用 npx
如果你在 prepare 钩子中执行脚本,不要用 npx,改用 pnpm exec、pnpm dlx:
json
{
"scripts": {
"prepare": "pnpm exec some-command"
}
}
否则 devEngines 识别到的是 npm 而不是 pnpm。
2. 避免同时使用 packageManager 和 devEngines.packageManager
pnpm v11 不允许同时使用两个字段:
go
WARN Cannot use both "packageManager" and "devEngines.packageManager" in package.json. "packageManager" will be ignored
建议直接删除 packageManager 字段,只保留 devEngines。
迁移步骤
1. 删除 only-allow
bash
pnpm remove only-allow
2. 删除 package.json 中的 preinstall 脚本
json
{
"scripts": {
"preinstall": "npx only-allow pnpm" // 删除这行
}
}
3. 删除 packageManager 字段
json
{
"packageManager": "pnpm@11.5.1" // 删除这行
}
4. 添加 devEngines 配置
json
{
"devEngines": {
"packageManager": {
"name": "pnpm",
"version": ">=11.4.0",
"onFail": "error"
},
"runtime": {
"name": "node",
"version": ">=24.15.0",
"onFail": "error"
}
}
}
5. CI 配置更新
如果你使用 GitHub Actions,确保 pnpm/action-setup 使用正确的版本:
yaml
- uses: pnpm/action-setup@v4
with:
version: latest # 不要写死版本号或读取 devEngines.packageManager
6. 还可以配合 fnm 等自动切换 node 版本。
自动切换 node 版本的工具有很多,可按习惯自行选择。
不过,我习惯用 fnm,简单、兼容性好,官方推荐、从nvm迁移几乎无痛。要说明的是 fnm 虽计划支持 devEngines,但现阶段还需要用脚本读 devEngines.runtime 生成一个 .node-version 文件。用 AI 简单写了个同步脚本
总结
devEngines 是目前固定开发环境的最佳方案:
- 标准方案:OpenJS Foundation 标准化,不是某个包管理器的私有实现
- 功能强大:不仅能限制包管理器,还能限制运行时版本
- 支持语义化版本 :比
packageManager的精确版本要求更灵活 - 有失败策略 :
onFail可以配置为 error、warn、download 等
如果你的项目还在用 only-allow,建议尽快迁移到 devEngines。毕竟都 2026 年了,是时候用点新东西了。
参考链接: