Skip to content

Instantly share code, notes, and snippets.

@callumlocke
Last active April 8, 2025 04:21
Show Gist options
  • Save callumlocke/30990e247e52ab6ac1aa98e5f0e5bbf5 to your computer and use it in GitHub Desktop.
Save callumlocke/30990e247e52ab6ac1aa98e5f0e5bbf5 to your computer and use it in GitHub Desktop.
ZSH function to auto-switch to correct Node version
####
# ZSH function to auto-switch to correct Node version
# https://gist.github.com/callumlocke/30990e247e52ab6ac1aa98e5f0e5bbf5
#
# - Searches up your directory tree for the closest .nvmrc, just like `nvm use` does.
#
# - If you are already on the right Node version, IT DOES NOTHING, AND PRINTS NOTHING.
#
# - Works correctly if your .nvmrc file contains something relaxed/generic,
# like "4" or "v12.0" or "stable".
#
# - If an .nvmrc is found but you have no installed version that satisfies it, it
# prints a clear warning, so you can decide whether you want to run `nvm install`.
#
# - If no .nvmrc is found, it does `nvm use default`.
#
# Recommended: leave your default as something generic,
# e.g. do `nvm alias default stable`
####
auto-switch-node-version() {
NVMRC_PATH=$(nvm_find_nvmrc)
CURRENT_NODE_VERSION=$(nvm version)
if [[ ! -z "$NVMRC_PATH" ]]; then
# .nvmrc file found!
# Read the file
REQUESTED_NODE_VERSION=$(cat $NVMRC_PATH)
# Find an installed Node version that satisfies the .nvmrc
MATCHED_NODE_VERSION=$(nvm_match_version $REQUESTED_NODE_VERSION)
if [[ ! -z "$MATCHED_NODE_VERSION" && $MATCHED_NODE_VERSION != "N/A" ]]; then
# A suitable version is already installed.
# Clear any warning suppression
unset AUTOSWITCH_NODE_SUPPRESS_WARNING
# Switch to the matched version ONLY if necessary
if [[ $CURRENT_NODE_VERSION != $MATCHED_NODE_VERSION ]]; then
nvm use $REQUESTED_NODE_VERSION
fi
else
# No installed Node version satisfies the .nvmrc.
# Quit silently if we already just warned about this exact .nvmrc file, so you
# only get spammed once while navigating around within a single project.
if [[ $AUTOSWITCH_NODE_SUPPRESS_WARNING == $NVMRC_PATH ]]; then
return
fi
# Convert the .nvmrc path to a relative one (if possible) for readability
RELATIVE_NVMRC_PATH="$(realpath --relative-to=$(pwd) $NVMRC_PATH 2> /dev/null || echo $NVMRC_PATH)"
# Print a clear warning message
echo ""
echo "WARNING"
echo " Found file: $RELATIVE_NVMRC_PATH"
echo " specifying: $REQUESTED_NODE_VERSION"
echo " ...but no installed Node version satisfies this."
echo " "
echo " Current node version: $CURRENT_NODE_VERSION"
echo " "
echo " You might want to run \"nvm install\""
# Record that we already warned about this unsatisfiable .nvmrc file
export AUTOSWITCH_NODE_SUPPRESS_WARNING=$NVMRC_PATH
fi
else
# No .nvmrc file found.
# Clear any warning suppression
unset AUTOSWITCH_NODE_SUPPRESS_WARNING
# Revert to default version, unless that's already the current version.
if [[ $CURRENT_NODE_VERSION != $(nvm version default) ]]; then
nvm use default
fi
fi
}
# Run the above function in ZSH whenever you change directory
autoload -U add-zsh-hook
add-zsh-hook chpwd auto-switch-node-version
auto-switch-node-version
@reorx
Copy link

reorx commented Apr 8, 2025

Sharing my approach here:

# NVM
export NVM_DIR="$HOME/.nvm"

# https://github.com/nvm-sh/nvm#zsh
use-nvmrc() {
  local nvmrc_path
  nvmrc_path="$(nvm_find_nvmrc)"

  if [ -n "$nvmrc_path" ]; then
    local nvmrc_node_version
    nvmrc_node_version=$(nvm version "$(cat "${nvmrc_path}")")

    if [ "$nvmrc_node_version" = "N/A" ]; then
      nvm install
    elif [ "$nvmrc_node_version" != "$(nvm version)" ]; then
      nvm use
    fi
  elif [ -n "$(PWD=$OLDPWD nvm_find_nvmrc)" ] && [ "$(nvm version)" != "$(nvm version default)" ]; then
    echo "Reverting to nvm default version"
    nvm use default
  fi
}
function auto-load-and-use-nvm() {
    if ! typeset -f nvm_find_nvmrc &> /dev/null; then
        # load nvm if nvm_find_nvmrc is not defined
        [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"
    fi
    use-nvmrc
}
# add hook for change directory commands
add-zsh-hook chpwd auto-load-and-use-nvm
# if current directory has .nvmrc, call auto-load-and-use-nvm immediately
[[ -f .nvmrc ]] && auto-load-and-use-nvm

This config has two advantages:

  1. it won't load nvm to zsh environment unless .nvmrc is found
  2. it automatically switches node version when changing directory

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment