根据项目路径自动切换 node 版本

2024-06-13

最近被 node 版本切换搞烦了。我的 node 默认版本是 18,平时开发 umi4 项目是没问题的。当开发 umi3 的项目时,需要把 node 切换到 16 版本。当开发小程序时,又要把 node 切换到 21 版本...

于是想到了利用脚本自动切换 node 版本。网上搜了一下,解决方案大多是在项目根目录加一个.nvmrc文件或者在package.json中记录 node 版本,再通过脚本获取并切换。

这种方式能解决问题,不过我不希望在每个项目都加一个.nvmrc。我理想的方式是:当我在某个项目中切换 node 版本时,用脚本记录下来。下次我进入这个项目的根目录时,脚本自动帮我切换到上次记录的版本。这种方式的好处是不用修改项目中的文件。实现原理有点类型zsh-z插件,通过记录cd过的目录来实现快速跳转。

我完全不会 shell 语法,所以脚本代码是让 AI 写的,不过代码并不能运行成功。经过修改后代码总算可以运行,代码如下:

#!/usr/bin/env zsh

# Set the data file path
data_file="$HOME/.nvm_auto_use/dirs"

# Create the data file if it doesn't exist
[[ -f $data_file ]] || { mkdir -p "${data_file:h}" && touch $data_file; }

# User-defined directory paths, can be set via environment variables, for example:
# export NVM_AUTO_USE_DIRS=( "/path/to/projects/dir1" "/path/to/projects/dir2" )
nvm_auto_use_dirs=( "${NVM_AUTO_USE_DIRS[@]:-$HOME}" )

# Associative array to store directory and nvm use command mappings
declare -A nvm_use_dirs

# Function to load data from the data file
function load_data() {
  if [[ -f "$data_file" ]]; then
    # Read data from the file
    while read -r line; do
      key=$(echo "$line" | cut -d'=' -f1)
      value=$(echo "$line" | cut -d'=' -f2-)
      nvm_use_dirs[$key]="$value"
    done < "$data_file"
  fi
}

# Function to save data to the data file
function save_data() {
  # Clear the file content
  echo "" > "$data_file"  # Overwrite the file content using echo "" >
  # Write the associative array to the file
  for key in "${(k)nvm_use_dirs[@]}"; do
    echo "$key=${nvm_use_dirs[$key]}" >> "$data_file"
  done
}

# Function to check if the current directory is in the specified directory array
function is_in_nvm_auto_use_dirs() {
  local current_dir="$PWD"
  for dir in "${nvm_auto_use_dirs[@]}"; do
    if [[ "$current_dir" == "$dir"/* ]]; then
      return 0  # Current directory is within the specified directories
    fi
  done
  return 1  # Current directory is not within the specified directories
}

# Function to handle the nvm use command
function nvm_use_handler() {
  # Check if the current directory is in the array and the command is 'nvm use'
  local cmd="$1"
  if [[ "$cmd" =~ ^nvm\ use ]] && is_in_nvm_auto_use_dirs; then
    local dir="$PWD"
    nvm_use_dirs[$dir]="$cmd"
    save_data  # Save the data to the file
  fi
}

# Function to automatically execute the nvm use command when entering a directory
function chpwd_handler() {
  # Check if the current directory is in the specified directory array
  if is_in_nvm_auto_use_dirs; then
    local dir="$PWD"
    if [[ -n "${nvm_use_dirs[$dir]}" ]]; then
      eval "${nvm_use_dirs[$dir]}"
    fi
  fi
}

# Load saved data
load_data

# Register the nvm use command handler
autoload -Uz add-zsh-hook
add-zsh-hook preexec nvm_use_handler

# Register the chpwd hook function
add-zsh-hook chpwd chpwd_handler

~/.zshrcsource 一下就能运行。

代码仓库在这里

配合 zsh-z 插件实现一个命令快速启动项目

可以在 ~/.zshrc 中加入两个函数

# Open a frontend project immediately
function zo(){
  z "$1"
  code .
}

# Start a frontend project immediately
function zs(){
  z "$1"
  code .
  yarn start
}

zs函数可以让我们快速用 vscode 打开该项目并且启动该项目,配合上面的 node 版本切换,我 90% 的前端项目都可以通过这个函数快速打开。有些项目不是用 yarn 管理的,这里可以优化一下,通过 lock 文件判断是使用哪个包管理工具,或者使用 ni。不过写死 yarn 对我来说已经够了。

有时候希望打开项目前,先切换分支或者同步最新代码,此时可以使用 zo 函数,只用 vscode 打开项目而不启动它,手动切换好分支后再启动项目。