你知道项目需要什么 node 版本吗?哪个包管理工具的什么版本?

96 阅读2分钟

近期接手了一个新项目,clone下来发现

  1. readme 无迹可循,node 版本等信息,只能口口相传,强依赖于上一个开发者
  2. 项目中有 npm, yan, pnpm 相关的配置,却无法知道明确应该使用那个包管理工具

于是开始致力于寻找解决之法

1. 指定 node 版本

1.1. 约束

问题:如果开发者任意使用了某个版本的 node,显然是不符合预期的,所以我们需要添加约束,来尽可能早的暴露错误

目的:开发者可以从项目配置获取到 node 版本信息,以及使用了不符合的 node 版本时warn 或 error

步骤一:最基础约束:package.json → engines

配置方式:

{
  "engines": {
    "node": ">=18 <21"
  }
}

实际效果:

  • npm / pnpm / yarn 会检查

不一样的包管理工具,或同一包管理工具的不同版本,对应行为的说法多种多样,最好自己试一下,下面贴我试下来的结果:

  • npm 10.9.2
  • pnpm 9.5.0
  • yarn 1.22.22

npm: warn

pnpm: warn

yarn: error

步骤二:最基础约束基础上添加engine-strict=true

engine-strict=true

npm: error

pnpm: error

yarn: error (原本也是)

步骤三:脚本约束(最终防线 可选)

const [major] = process.versions.node.split('.').map(Number)

if (major < 18 || major >= 21) {
  console.error(
    `❌ Node.js version ${process.versions.node} is not supported.\n` +
    `Required: >=18 <21`
  )
  process.exit(1)
}
{
  "scripts":{
    "preinstall": "node scripts/check-node.js"
  }
}

1.2. node 版本切换辅助

目的:进入项目实现 node 版本自动切换,或简化手动版本切换

方案一:nvm + .nvmrc (手动)

1️⃣ 在项目根目录新建 .nvmrc

22.16.0

2️⃣ 进入项目执行 nvm use

开发者需要提前安装

  • nvm(macOS / Linux)
  • Windows 需要 nvm-windows

方案二:Volta (自动)

1️⃣ 在项目中 pin node

volta pin node@22.16.0

# 包管理工具一起固定
# volta pin pnpm@9.12.2     # 或 yarn@1.22.19 / npm@10.8.3

生成如下内容,会添加在 package.josn文件中

{
  "volta": {
    "node": "18.19.0"
  }
}

2️⃣ 自动切换 node

不需要手动切换,进入项目后,volta 会在第一次用到 node 时自动切换为目标 node 版本(如果没有目标版本,会自动下载),进入项目 node -v可验证

开发者需要提前安装

  • Volta

注意⚠️:现在 volta 不支持 uninstall node

原因:Volta 把 Node 当成“基础设施”,官方不支持、也不推荐卸载 node

偏方:自己找到文件夹的位置~/.volta/tools/image/node/删掉

2. 指定包管理工具

包管理工具 packageManager (PM)

当看见项目中关于npm,pnpm,yarn包管理工具的配置都存在时,我一脸蒙,不知道应该用哪一个包管理工具,此时明确指定包管理工具才是预期,那么如何指定呢?

2.1. 约束

目的:开发者可以从项目配置获取到可以使用哪个包管理工具,以及使用了不符合的 node 版本时 error

步骤一:package.json -> packageManager(声明 软提醒)

比如指定 pnpm

{
  "packageManager": "pnpm@10.18.3"
}

步骤二: only-allow(强制)

npx only-allow pnpm,npx only-allow npm,npx only-allow yarn

{
  "preinstall": "npx only-allow pnpm"
}

如果有其他脚本,建议把这个放在前面 npx only-allow pnpm && node scripts/check-node.js,此时再用pnpm外的包管理工具可就不行了:

步骤三:脚本约束(最终防线 可选)

const userAgent = process.env.npm_config_user_agent || '';
if (!userAgent.includes('pnpm')) {
  console.error('❌ 请使用 pnpm 安装依赖');
  console.error('💡 运行: corepack enable && pnpm install');
  process.exit(1);
}
{
  "scripts":{
    "preinstall": "npx only-allow pnpm && node scripts/check-node.js"
  }
}

暂时用"preinstall": "node scripts/check-node.js"查看报错:

2.2. 包管理工具切换辅助 ❌

我们没法辅助开发者切换npm/ pnpm /yarn,因为他们本来就不是项目级工具,而是系统级工具,开发者想用哪个用哪个(他尽管用,我们在约束环节已经拦截)

3. 指定包管理工具版本

3.1. 约束

3.1.1. npm 专有约束

1️⃣ package.json#npm

{
  "engines": {
    "node": ">=18 <21",
    "npm": "11.7.0"
  },
}

2️⃣ .npmrc

engine-strict=true

3.1.2. pnpm 专有约束

1️⃣ package.json (声明)

{
  "engines": {
    "node": ">=18 <21",
    "pnpm": "9.1.1"
  },
}

3.1.3. 共享约束:脚本约束

三个PM都可以用的约束,以 pnpm@10.28.2 为例

import * as semver from 'semver';
import { execSync } from 'child_process';

const REQUIRED = '10.28.2';

const current = execSync('pnpm -v').toString().trim();

if (!semver.eq(current, REQUIRED)) {
  console.error(`
❌ pnpm 版本不符合要求

当前版本: ${current}
要求版本: ${REQUIRED}
`);
  process.exit(1);
}

3.2. 包管理工具版本辅助切换

3.2.1. pnpm 自身的版本管理

 {
    "packageManager": "pnpm@10.28.0",
 }

pnpm 触发时,检查 packageManager字段,如果发现不一致会尝试下载并切换到packageManager指定的版本

3.2.2. yarn 依赖 packageManager+corepack

1️⃣ corepack enable

Node.js 版本 ≥ 16.9 <25 自带corepack,没有则先安装corepack

2️⃣ 提供PM信息

{
  "packageManager": "yarn@1.22.20",
}

3️⃣ 自动切换

使用PM时,corepack会读packageManager如果发现版本不一致,触发自动下载

关于corepack可以解决用什么PM(packageManager 包管理工具)?什么PM 版本?

我持怀疑态度,理由如下

corepack 出现的初衷本来就是为了统一PM的版本,而不是统一用户哪一个包版本工具,那都有人说它可以,那尝试一下硬着头皮用。

发现用它来指定PM需要:

  1. 开发者本地存在corepack或Node.js 版本 ≥ 16.9 <25(自带,也不完全自带,如果是 volta下载的,就不会带),且需要corepack enable
  2. 无论 packageManager 配置了什么,都不限制 npm install
  3. 只能限制 corepack下载的包版本工具,但哪个前端开发笔记本不安装几个包管理工具?

发现用它来指PM版本也存在问题:

Corepack 不管理 npm, 配置了npm@10.25.0但是任何版本的npm都会直接执行,不会下载指定版本

结论:❌ 多少有些不可靠,现在能想到的应用场景就只有辅助 yarn 版本切换了

绕死我了!🙂‍↔️🙂‍↔️🙂‍↔️