自动切换 Node.js 版本号 📍 Pin Node.js Version Per-Project

154 阅读2分钟

nodejs_cover_photo_smaller_size.png

🤒 问题

当遇到多个项目每个项目依赖的 Node.js 版本不相同时,我们需要手动用 nvm use 切换版本号,太麻烦还容易忘记。只要重复做的事情就要问问能否自动化

我们想要的效果是:cd 到某个项目,自动切换 Node.js 版本。

nvm 自动切换 macOS 支持但不支持 Windows,且定义在 package.json 中的版本号也无法切换。

索性自己写一个!

🏗️ 实现

一般版本号会存在两个地方:.nvmrc 或 package.json 的 engines 字段。

.nvmrc:

22

package.json:

{
  "name": "foo",
  ...
  "engines": {
    "node": ">=22.0.0"
  },
  ...
}

思路:我们可以重写 cd 命令,在 Linux 下可以使用 alias。

📁 覆写 cd 命令

.zshrc:

cd() {
    builtin cd "$@" && change_node_version_per_project
}
  • builtin cd 表示使用内置命令
  • "$@"cd 所有参数传给内置 cd 命令,相当于 js 的 ...args,如此我们并没有破坏 cd 的功能,而是对其进行了增强。

🎯 实现 change_node_version_per_project

change_node_version_per_project() {
    local nvmrc_path="./.nvmrc"

    if [[ -f "$nvmrc_path" ]]; then
        local node_version=$(grep -oP '\d+(\.\d+)?(\.\d+)?' .nvmrc | head -n1)
        
        echo '---------------------------------------'
        echo "node_version in .nvmrc is $node_version"
        echo '---------------------------------------'

        nvm use "$node_version" || use_from_pkg_json
    else
        # echo "No .nvmrc file found in the current directory."
        use_from_pkg_json
    fi
}

Best Practices:局部变量使用 local 是好习惯避免定义全局变量

  1. 优先从当前目录的 .nvmrc 获取版本号,通过正则表达式匹配,并且只取第一个 (head -n1)。
  2. nvm use "$node_version" || use_from_pkg_json:用 || 的用意是如果版本号切换报错,则兜底去 package.json 获取版本号。
  3. 如果不存在 .nvmrc 则去 package.json 获取版本号 use_from_pkg_json

📦 实现 use_from_pkg_json

use_from_pkg_json() {
    if [[ -f "./package.json" ]]; then
      local node_version=$(grep -A 2 engines package.json | grep -oP '[0-9]+' | head -n1);

      echo '---------------------------------------------'
      echo "engines.node in package.json is $node_version";
      echo '---------------------------------------------'

      # local pkg_json_path="package.json"
      # local node_version=$(jq -r '.engines.node' "$pkg_json_path" | cut -d' ' -f1)

      if [ -z "$node_version" ]; then
          echo "No node version specified in package.json."
      else
          nvm use "$node_version"
      fi
  fi
}
  1. grep -A 2 engines package.json:从 package.json 中匹配 engines:使用 grep -A 2A 表示 After)即从匹配处往下多获取两行

匹配示例如下:

  "engines": {
    "node": ">=22.0.0"
  },
  1. if [ -z "$node_version" ]: 如果匹配结果为空则打印未找到,否则使用 nvm use xxx 切换版本号。

至此我们的功能完整实现了。

💐 效果

如果某个项目没有配置 nvmrc 但是指定了 engines:cd dir_with_package_json_and_engines

---------------------------------------------
engines.node in package.json is 16
---------------------------------------------
Now using node v16.20.2 (64-bit)

如果有 nvmrc:cd dir_with_nvmrc

---------------------------------------
node_version in .nvmrc is 22
---------------------------------------
Now using node v22.7.0 (64-bit)