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 年了,是时候用点新东西了。


参考链接

相关推荐
冬奇Lab5 小时前
开源项目第195期:OpenTelemetry Demo(Astronomy Shop)— 官方出品的分布式系统可观测性实战教材
人工智能·开源·前端工程化
一位正在转型AI全栈的前端工程师6 天前
从 0 到 1 搭建生产级 RAG 系统:切片、召回与重排序调优实录
python·前端工程化
嘟嘟07178 天前
Next.js 博客左侧笔记列表是怎么从 Redis 渲染出来的?从组件、children 到动态路由一次讲清
redis·react.js·前端工程化
烬羽9 天前
ESLint 10 把 .eslintrc 删了:flat config 到底怎么配,我帮你踩完了
代码规范·eslint·前端工程化
烬羽9 天前
彻底搞懂 'use client':Next.js 组件根本不是"只在浏览器渲染"
全栈·next.js·前端工程化
烬羽9 天前
彻底搞懂 App Router 约定式路由:文件放对位置,路由就自动生成了
react.js·next.js·前端工程化
码云之上15 天前
Harness Engineering:让 Agent 在受控边界内运行
人工智能·前端框架·前端工程化
AINative软件工程15 天前
LLM 应用的依赖注入工程实践:解耦 Client、Prompt 和 Tool Registry,让 AI 系统真正可测试可替换
后端·llm·前端工程化
码云之上16 天前
Loop Engineering:把模型调用变成可控的多步任务
人工智能·前端框架·前端工程化
码云之上17 天前
Context Engineering:让 Agent 在当前步骤看到正确的事实
前端·人工智能·前端工程化