Categories
Coding

Auto nvm use

I use nvm to manage my version of node which makes it easy to use the right version of node for specific projects. What has annoyed me with nvm is opening up a directory in terminal and running npm only to get:

-bash: npm: command not found
Code language: Shell Session (shell)

I’d have to remember to run nvm install after navigating to a project directory (where an .nvmrc is located). My annoyance finally spurred me to action today and I looked up if there was some way to do it automatically. Naturally, someone asked this exact question on Stackoverflow: run nvm use automatically every time there’s a .nvmrc file on the directory. The fifth answer points over to the nvm official docs on how to automatically call nvm use. The problem with that official answer is that it requires that you use its aliased cd command to navigate to a directory; it doesn’t work if you use something like autojump. So I modified it to incorporate the PROMPT_COMMAND injection approach from the higher-ranked answers. I’ve also improved performance by persisting the default version and local version in environment variables which avoid unnecessary subshells for nvm. Here’s what I’ve got in my .bashrc now:

autonvm() { nvm_path=$(nvm_find_up .nvmrc | tr -d '\n') # If there are no .nvmrc file, use the default nvm version if [[ ! $nvm_path = *[^[:space:]]* ]]; then if [[ -z $nvm_default_version ]]; then nvm_default_version=$(nvm version default); fi # If there is no default version, set it to `node` # This will use the latest version on your machine if [[ $nvm_default_version == "N/A" ]]; then nvm alias default node; nvm_default_version=$(nvm version default); fi # If the current version is not the default version, set it to use the default version if [[ $(nvm current) != $nvm_default_version ]]; then nvm use default; fi elif [[ -s $nvm_path/.nvmrc && -r $nvm_path/.nvmrc ]]; then nvm_version=$(<"$nvm_path"/.nvmrc) # `nvm ls` will check all locally-available versions # If there are multiple matching versions, take the latest one # Remove the `->` and `*` characters and spaces # `nvm_local_version` will be `N/A` if no local versions are found if [[ -z $nvm_local_version ]]; then nvm_local_version=$(nvm ls --no-colors "$nvm_version" | tail -1 | tr -d '\->*' | tr -d '[:space:]') fi # If it is not already installed, install it # `nvm install` will implicitly use the newly-installed version if [[ "$nvm_local_version" == "N/A" ]]; then nvm install "$nvm_version"; elif [[ $(nvm current) != "$nvm_local_version" ]]; then nvm use "$nvm_version"; fi fi } export PROMPT_COMMAND="$PROMPT_COMMAND; autonvm"
Code language: Bash (bash)

Now when I open a new tab in iTerm and I’m in my home directory, I see first:

Now using node v16.14.2 (npm v8.5.0)
Code language: Shell Session (shell)

And if I then cd into a directory with an .nvmrc containing 14 (or a subdirectory of such a directory), I see:

Found '/Users/westonruter/foo/.nvmrc' with version <14> Now using node v14.19.3 (npm v6.14.17)
Code language: Shell Session (shell)

Much better!


Prior to seeing that official answer, I had improved upon the third answer as follows:

  • Prevent clobbering any existing COMMAND_PROMPT (e.g. from autojump).
  • Run when first starting a terminal session (when $PREV_PWD is empty).

Here’s what I previously added to my .bash_profile:

run_nvm_use_when_directory_changed() { if [[ $PWD == $NVM_PREV_PWD ]]; then return fi # Do nothing if a previously-seen parent directory has an .nvmrc. if [[ $NVM_DIRTY = true ]] && [[ ! -z $NVM_PREV_PWD ]] && [[ $PWD =~ $NVM_PREV_PWD ]]; then return fi if [[ -f ".nvmrc" ]]; then nvm use NVM_DIRTY=true elif [[ $NVM_DIRTY = true ]] || [[ -z $NVM_PREV_PWD ]]; then nvm use stable NVM_DIRTY=false fi NVM_PREV_PWD=$PWD } export PROMPT_COMMAND="$PROMPT_COMMAND; run_nvm_use_when_directory_changed"
Code language: Bash (bash)

The problem with this version is that it doesn’t recognize the .nvmrc from an ancestor directory if you started a session in a subdirectory. The official answer from nvm addresses that problem by use of nvm_find_up to recursively search for the file in ancestor directories.

Leave a Reply

Your email address will not be published.