I just found out about Automatic Version Switching for Node.js https://github.com/wbyoung/avn, you can use that.
npm install -g avn avn-nvm avn-n
avn setup
You can also follow this thread https://github.com/creationix/nvm/issues/110
Pregunta
How to configure my shell so that nvm use
run automatically every time there's a .nvmrc file on the directory and use the latest version or a global config when there's no .nvmrc file?
Solución 4
I just found out about Automatic Version Switching for Node.js https://github.com/wbyoung/avn, you can use that.
npm install -g avn avn-nvm avn-n
avn setup
You can also follow this thread https://github.com/creationix/nvm/issues/110
Otros consejos
If you use zsh (z shell):
Calling 'nvm use' automatically in a directory with a .nvmrc file
Put this into your $HOME/.zshrc to call nvm use automatically whenever you enter a directory that contains an .nvmrc file with a string telling nvm which node to use:
# place this after nvm initialization!
autoload -U add-zsh-hook
load-nvmrc() {
local node_version="$(nvm version)"
local nvmrc_path="$(nvm_find_nvmrc)"
if [ -n "$nvmrc_path" ]; then
local nvmrc_node_version=$(nvm version "$(cat "${nvmrc_path}")")
if [ "$nvmrc_node_version" = "N/A" ]; then
nvm install
elif [ "$nvmrc_node_version" != "$node_version" ]; then
nvm use
fi
elif [ "$node_version" != "$(nvm version default)" ]; then
echo "Reverting to nvm default version"
nvm use default
fi
}
add-zsh-hook chpwd load-nvmrc
load-nvmrc
More info: https://github.com/creationix/nvm#zsh
If you use bash you can add this to your ~/.bashrc
file:
_nvmrc_hook() {
if [[ $PWD == $PREV_PWD ]]; then
return
fi
PREV_PWD=$PWD
[[ -f ".nvmrc" ]] && nvm use
}
if ! [[ "${PROMPT_COMMAND:-}" =~ _nvmrc_hook ]]; then
PROMPT_COMMAND="_nvmrc_hook${PROMPT_COMMAND:+;$PROMPT_COMMAND}"
fi
Excellent answer from @devius.
I just extended it so it can revert to the default version when leaving a directory with .nvmrc
to another without it.
~/.bashrc
:
#
# Run 'nvm use' automatically every time there's
# a .nvmrc file in the directory. Also, revert to default
# version when entering a directory without .nvmrc
#
enter_directory() {
if [[ $PWD == $PREV_PWD ]]; then
return
fi
PREV_PWD=$PWD
if [[ -f ".nvmrc" ]]; then
nvm use
NVM_DIRTY=true
elif [[ $NVM_DIRTY = true ]]; then
nvm use default
NVM_DIRTY=false
fi
}
export PROMPT_COMMAND="$PROMPT_COMMAND; enter_directory"
Following @doug-barbieri's suggestion, the script below will not change node back to the default version if there's no .nvmrc
file in the current directory but there is one in the parent sub-directory.
~/.bashrc
:
enter_directory() {
if [[ $PWD == $PREV_PWD ]]; then
return
fi
if [[ "$PWD" =~ "$PREV_PWD" && ! -f ".nvmrc" ]]; then
return
fi
PREV_PWD=$PWD
if [[ -f ".nvmrc" ]]; then
nvm use
NVM_DIRTY=true
elif [[ $NVM_DIRTY = true ]]; then
nvm use default
NVM_DIRTY=false
fi
}
The trick is here:
if [[ "$PWD" =~ "$PREV_PWD" && ! -f ".nvmrc" ]]; then
return
fi
It checks if the PWD contains PREV_PWD. For example, if /home/user1/a/b
contains /home/user1/a
.
This can be extended to work with Starship (even on Git Bash for Windows not WSL) as well using the starship_precmd_user_func
set_win_title() {
BASEPWD=$(basename "$PWD")
echo -ne "\033]0; 📁 $BASEPWD \a" < /dev/null
if [[ $PWD == $PREV_PWD ]]; then
return
fi
if [[ "$PWD" =~ "$PREV_PWD" && ! -f ".nvmrc" ]]; then
return
fi
PREV_PWD=$PWD
if [[ -f ".nvmrc" ]]; then
nvm use
NVM_DIRTY=true
elif [[ $NVM_DIRTY = true ]]; then
nvm use default
NVM_DIRTY=false
fi
}
starship_precmd_user_func="set_win_title"
eval "$(starship init bash)"
This answer is taken from the official nvm documentation.
Put the following at the end of your $HOME/.bashrc
:
find-up () {
path=$(pwd)
while [[ "$path" != "" && ! -e "$path/$1" ]]; do
path=${path%/*}
done
echo "$path"
}
cdnvm(){
cd "$@";
nvm_path=$(find-up .nvmrc | tr -d '[:space:]')
# If there are no .nvmrc file, use the default nvm version
if [[ ! $nvm_path = *[^[:space:]]* ]]; then
declare default_version;
default_version=$(nvm version default);
# If there is no default version, set it to `node`
# This will use the latest version on your machine
if [[ $default_version == "N/A" ]]; then
nvm alias default node;
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) != "$default_version" ]]; then
nvm use default;
fi
elif [[ -s $nvm_path/.nvmrc && -r $nvm_path/.nvmrc ]]; then
declare nvm_version
nvm_version=$(<"$nvm_path"/.nvmrc)
# Add the `v` suffix if it does not exists in the .nvmrc file
if [[ $nvm_version != v* ]]; then
nvm_version="v""$nvm_version"
fi
# If it is not already installed, install it
if [[ $(nvm ls "$nvm_version" | tr -d '[:space:]') == "N/A" ]]; then
nvm install "$nvm_version";
fi
if [[ $(nvm current) != "$nvm_version" ]]; then
nvm use "$nvm_version";
fi
fi
}
alias cd='cdnvm'
This is an improvement over:
avn
)This alias would search 'up' from your current directory in order to detect a .nvmrc
file. If it finds it, it will switch to that version; if not, it will use the default version.
I tried many solutions for this and nothing worked the way I wanted, so I wrote my own:
ZSH function to auto-switch to correct Node version
As far as I know, this is the only one that meets all the following criteria:
.nvmrc
(just like nvm use
);.nvmrc
format;.nvmrc
,default
if there is no .nvmrc
anywhere up the tree;When you swim with the fish
shell, here is how to run nvm use
whenever there is a .nvmrc
in the directory:
# TODO: save this as `$HOME/.config/fish/conf.d/use_nvmrc.fish`
# HOW IT WORKS
# `nvm use` whenever .nvmrc is present in $PWD when using fish shell
# when traveling deeper, use the parent .nvmrc unless otherwise set
# also go back to default nvm when leaving the nvmrc-specified zone
function set_nvm --on-event fish_prompt
# runs whenever the fish_prompt event occurs
# if the current directory hasn't changed, do nothing
string match -q $PWD $PREV_PWD; and return 1
# if the current directory is within the previous one where we found an nvmrc
# and there is no subsequent .nvmrc here, do nothing, we are in the same repo
string match -eq $PREV_PWD $PWD; and not test -e '.nvmrc'; and return 1
# if we clear those checks, keep track of where we are
set -g PREV_PWD $PWD
if test -e '.nvmrc'
# if we find .nvmrc, run nvm use
nvm use
# and remember that we used that node
set NVM_DIRTY true
else if not string match $NVM_DIRTY true
# if we have set nvm and have stepped out of that repo
# go back to default node, if not already on it
not string match -eq (nvm current) (nvm alias default); and nvm use default
# and clear the flag
set NVM_DIRTY
end
end
Yet another solution using direnv. Direnv comes with OS X and many distros so no installation is needed.
Add these two lines to your .zshenv or .bash_profile depending on which shell you use:
export NVM_DIR="$HOME/.nvm" # You probably have this line already
export NODE_VERSIONS="${NVM_DIR}/versions/node"
export NODE_VERSION_PREFIX="v"
Add an .envrc file to the project root with the contents
set -e
use node
Finally cd to your directory. (Don't forget to source .zshenv)
direnv will ask you to allow load config.
Type direnv allow
and voila!
Note that direnv doesn't support fancy constructs like lts/*
in .nvrmc. On the positive side, direnv supports a bunch of runtimes like node, php, go, pyhton, ruby etc. allowing us to use a single tool to solve path issues.
Bash version (to put in $HOME/.bashrc
), with these features:
cd
alias (it allows to change directories in other ways, for example starting your terminal directly in another directory).nvmrc
in upper directories (using nvm_find_nvmrc
)nvm use
if not neededcall_nvm_use_if_needed() {
NEW_NVMRC="$(nvm_find_nvmrc)"
if [[ "$NEW_NVMRC" != "$CURRENT_NVMRC" ]]; then
if [[ -z "$NEW_NVMRC" ]]; then
nvm use default
else
nvm use
fi
CURRENT_NVMRC="$NEW_NVMRC"
fi
}
PROMPT_COMMAND="call_nvm_use_if_needed; ${PROMPT_COMMAND}"
For those on Apple Silicon (M1) Macs, you may have noticed NVM tries and fails to compile Node versions <16 from source on nvm install
. Here's an update to @Rotareti's and @Reynke's answer that uses Rosetta to install the x86_64 version for Node < 16 while installing the native ARM version for Node >= 16, as Node 16 is the earliest version to support Apple Silicon.
Once installed, you can nvm use
from either native or rosetta terminals to use the right version, so only the nvm install
portion of the original function changes.
Replace /opt/homebrew/opt/nvm/nvm.sh
with the path to your nvm installation.
~/.zshrc
# place this after nvm initialization!
autoload -U add-zsh-hook
load-nvmrc() {
local node_version="$(nvm version)"
local nvmrc_path="$(nvm_find_nvmrc)"
if [ -n "$nvmrc_path" ]; then
local nvmrc_node_version=$(nvm version "$(cat "${nvmrc_path}")")
if [ "$nvmrc_node_version" = "N/A" ]; then
# check if we're in a native (ARM) terminal
if [[ $(uname -p) == "arm" ]]; then
local nvmrc_remote_version=$(nvm version-remote "$(cat "${nvmrc_path}")")
if printf '%s\n%s\n' v16.0.0 "${nvmrc_remote_version}" | sort -VC; then
# arm and node >= v16; install native node
nvm install
else
# arm and node < v16; install x64 node using rosetta
arch -x86_64 zsh -c '. "/opt/homebrew/opt/nvm/nvm.sh"; nvm install'
nvm use
fi
else
# not arm
nvm install
fi
elif [ "$nvmrc_node_version" != "$node_version" ]; then
nvm use
fi
elif [ "$node_version" != "$(nvm version default)" ]; then
echo "Reverting to nvm default version"
nvm use default
fi
}
add-zsh-hook chpwd load-nvmrc
load-nvmrc
cd
performanceautoload -U add-zsh-hook
use_nvmrc_version_automatically() {
if [[ -f .nvmrc ]]; then
echo ".nvmrc FOUND now INSTALLING and USING $(cat .nvmrc)"
nvm install $(cat .nvmrc) && nvm use $(cat .nvmrc)
fi
}
add-zsh-hook chpwd use_nvmrc_version_automatically
use_nvmrc_version_automatically
There is now offical docs on how to do this really well, see deeper-shell-integration at https://github.com/nvm-sh/nvm#deeper-shell-integration
If you use zsh (z shell):
I load nvm in a different way that's faster, but it means nvm_find_nvmrc
is not available so @Rotareti solution wasn't working for me.
I found any easy way to fix: simply call nvm use
without params since it already handles the logic of looking for an .nvmrc
file itself and uses the default version if not found.
# ~/.zshrc
# DEFAULT NVM CONFIG
#export NVM_DIR="$HOME/.nvm"
#[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
#[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # This loads nvm bash_completion
# Add the following lines before all other OH MY ZSH config
# FASTER WAY TO CONFIGURE NVM ON STARTUP - OTHERWISE IT'S REALLY SLOW
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/bash_completion" ] && . "$NVM_DIR/bash_completion"
export PATH="$NVM_DIR/versions/node/v$(<$NVM_DIR/alias/default)/bin:$PATH"
alias nvm="unalias nvm; [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"; nvm $@"
# USE NVM VERSION IF .NVMRC FOUND, OTHERWISE USE DEFAULT
nvm use &>/dev/null
Altogether, I find this to be a really fast solution, that saves me the pain of typing nvm use
.
I'd like to avoid nvm use
when there's no .nvmrc
file available, but loading time is quite low on my computer as it stands and I rarely need a terminal without Node - so this works for me for now.
Update: Added instructions on where to place suggested script
For zsh users, you should try zsh-nvm:
Zsh plugin for installing, updating, and loading nvm
Suppose you are using antigen, you can turn on auto use like this:
export NVM_AUTO_USE=true
antigen bundle lukechilds/zsh-nvm
zsh-nvm also supports lazy load nvm, which dramatically reduced the startup time of zsh
% time (source "$NVM_DIR/nvm.sh")
( source "$NVM_DIR/nvm.sh"; ) 0.58s user 0.37s system 109% cpu 0.874 total
% time (_zsh_nvm_lazy_load)
( _zsh_nvm_lazy_load; ) 0.01s user 0.01s system 168% cpu 0.012 total
Extending on @Adriano P answer, I'd propose this version that is less general (only works if .nvmrc
is set on a git repository root), but works in cases when we navigate to elsewhere in project than its root:
_enter_dir() {
local git_root
git_root=$(git rev-parse --show-toplevel 2>/dev/null)
if [[ "$git_root" == "$PREV_PWD" ]]; then
return
elif [[ -n "$git_root" && -f "$git_root/.nvmrc" ]]; then
nvm use
NVM_DIRTY=1
elif [[ "$NVM_DIRTY" == 1 ]]; then
nvm use default
NVM_DIRTY=0
fi
PREV_PWD="$git_root"
}
export PROMPT_COMMAND=_enter_dir
#export PROMPT_COMMAND="$PROMPT_COMMAND;_enter_dir" # use this if PROMPT_COMMAND already defined
I use this zsh configuration framework called Oh My Zsh. It's a very active repository with regular updates. Try it and I'm sure you will love it. Oh, and it has the automatic .nvmrc feature built-in so it's as simple as installing the package thru npm!
For someone still facing the above issue the README for nvm
has this section which would be helpful
https://github.com/creationix/nvm#deeper-shell-integration
Personally I prefer editing the .bashrc
(https://github.com/creationix/nvm#automatically-call-nvm-use) over other solutions.
I've actually used Rotareti's solution for a while, before wanting to use the lts version if there is no .nvmrc
(which was actually what the author asked originally).
So I worked on it a little and all it required was a few replaced/added lines:
# Load right version of NVM
autoload -U add-zsh-hook
load-nvmrc() {
local node_version="$(nvm version)"
local nvmrc_path="$(nvm_find_nvmrc)"
if [ -n "$nvmrc_path" ]; then
local nvmrc_node_version=$(nvm version "$(cat "${nvmrc_path}")")
if [ "$nvmrc_node_version" = "N/A" ]; then
nvm install
elif [ "$nvmrc_node_version" != "$node_version" ]; then
nvm use
fi
elif [ "$node_version" != "$(nvm version-remote --lts)" ]; then
if [[ "$(nvm ls)" != *"$(nvm version-remote --lts)"* ]]; then
echo "New lts version available. Installing..."
nvm install --lts
fi
echo "Reverting to node lts version"
nvm use --lts
fi
}
add-zsh-hook chpwd load-nvmrc
load-nvmrc
For users of Windows, zsh and coreybutler's nvm-windows, this slightly modified script adapted from the answer above could come in handy at the end of your .zshrc:
autoload -U add-zsh-hook
load-nvmrc() {
if [ -f ".nvmrc" ]; then
local required_version=$(cat .nvmrc | cut -c2-)
local current_version=$(node -v)
echo "Required Node version: $required_version"
local is_available_already=$(nvm ls | grep -c "$required_version")
if [[ $required_version != $current_version && $is_available_already -lt 1 ]]; then
echo "Required version $required_version not installed, installing..."
nvm install $required_version
fi
nvm use $required_version
fi
}
add-zsh-hook chpwd load-nvmrc
load-nvmrc