2026 年了,别再用 only-allow 固定包管理器了!devEngines 才是正解

前言

大家好,我是 sonion。

最近在搭新项目时,发现用 only-allow 限制包管理器强制用 pnpm 的方案失效了,排查半天才发现 only-allow 不支持 pnpm v11。

于是,我开始寻找新的方案。一个可选的方案是: packageManager + node corepack,但使用率不高,还要求成员都打开 corepack,有点麻烦。并且packageManager 字段要求的是精确版本,不太灵活。

翻了下 only-allow 支持计划,才发现 only-allow 停止维护了,推荐使用 devEnginesdevEngines何许人也?🤔了解后才发现 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 选项详解

onFaildevEngines 的核心能力,它定义了当环境不满足要求时的行为:

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 execpnpm 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 是目前固定开发环境的最佳方案:

  1. 标准方案:OpenJS Foundation 标准化,不是某个包管理器的私有实现
  2. 功能强大:不仅能限制包管理器,还能限制运行时版本
  3. 支持语义化版本 :比 packageManager 的精确版本要求更灵活
  4. 有失败策略onFail 可以配置为 error、warn、download 等

如果你的项目还在用 only-allow,建议尽快迁移到 devEngines。毕竟都 2026 年了,是时候用点新东西了。


参考链接

相关推荐
先吃饱再说19 小时前
编写一个颜色选择器之后,我理解了 React 工程化的三层架构
react.js·前端框架·前端工程化
先吃饱再说19 小时前
从“传事件”到“只传值”:React + TypeScript 组件 Props 设计的两次进化
react.js·前端框架·前端工程化
Dr_哈哈3 天前
从一个 Vue 项目到一套工程体系:Bun、Monorepo、Turbo 与 Docker 到底在解决什么?
前端工程化
布兰妮甜3 天前
暗黑模式一键切换完整方案(CSS 变量 + 本地存储)
javascript·css·web开发·用户体验·前端工程化
布兰妮甜3 天前
原子化 CSS 深度解析:Tailwind 原理、自定义配置、大型项目利弊
css·性能优化·tailwind·前端工程化·设计系统
labixiong4 天前
Rspack 2.0 正式发布 — 前端构建工具格局再变天
webpack·前端工程化·turbopack
Flynt4 天前
我用Biome替换ESLint+Prettier在项目上踩了不少坑
rust·eslint·前端工程化
Revolution615 天前
页面更新后为什么出现 Loading chunk failed:旧页面如何请求了已删除的构建产物
前端·面试·前端工程化
Revolution615 天前
首屏变慢后,应该先查资源下载、脚本执行还是接口请求
前端·性能优化·前端工程化