I start up a lot of terminal windows on my Mac and for some reason the commands
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" --no-use
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"
that I had in my .bashrc would sometimes take a noticeable and annoying amount of time to run. (Maybe as long as 2 seconds.) My solution was to setup environment variables like NVM_DIR as usual but to fully defer all other nvm setup until first use by adding these definitions to my .bash_profile script:
function _install_nvm() {
unset -f nvm
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This sets up nvm
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # load nvm bash_completion
nvm "$@"
}
function nvm() {
_install_nvm "$@"
}
I am not familiar enough with other shells to say how portable that is (I am using GNU bash), but I suggest you at least add it to the documentation (if not the install script) so that people can have an easy way to install deferred setup of nvm that is truly deferred.
By default, this is because it runs npm config get prefix, which is quite slow. With --no-use, it's as fast as it can go, as far as I'm aware - and I've not heard of any speed complaints before with that.
As for your approach, that has a number of caveats - including that node, npm, and any global modules you have installed in a default version won't be available until you run nvm.
In other words, it would not be a good idea to add it to the documentation, because it's error-prone and comes with caveats.
@ljharb I do not understand the relevance of the caveats you listed. I am not suggesting my solution to replace full initialization of nvm, only to replace/enhance the --no-use configuration. When using
. "$NVM_DIR/nvm.sh" --no-use
as currently recommended, node and npm remain unavailable until you execute nvm use (at least that's how it works for me), so your caveats apply equally to the current --no-use situation and the behavior with the deferred initialization.
With the script I suggested, nvm gets completely initialized the first time someone executes any nvm command. If you want, I could provide additional functions that initialize nvm on the first call to npm or node, but in my environment that is not helpful, because either way those commands do nothing until I nvm use some version of node.
That's a very fair point - but I'm still not sure why with --no-use it'd be slow - not one person has reported that previously.
I don't think the solution is to make nvm be lazy-loaded only when in "no-use" mode - i think a better solution is to figure out why sourcing nvm is slow on your machine, and fix that :-) I'll reopen for that.
(Note that nvm has many commands that do not require any version to be used or installed in order to be useful)
@ljharb Thanks for re-opening this.
It appears --no-use is sometimes slow because nvm_supports_source_options is not reliable, at least not under GNU bash version 3.2.57(1) on my Mac, and when nvm_supports_source_options is false, the --no-use option is ignored.
$ (nvm_supports_source_options && echo true) || echo false
true
$ (nvm_supports_source_options && echo true) || echo false
false
$ i=0; while nvm_supports_source_options; do echo -n .; ((i++)); done; printf "\n%s\n" $i
..
2
$ i=0; while nvm_supports_source_options; do echo -n .; ((i++)); done; printf "\n%s\n" $i
0
$ i=0; while nvm_supports_source_options; do echo -n .; ((i++)); done; printf "\n%s\n" $i
.......
7
The current implementation of nvm_supports_source_options writes to a pipe that then tries to source that pipe as a shell script via /dev/stdin. I guess there is some race condition resulting in the output of the pipe being discarded rather than processed.
FWIW, I updated my .bash_profile script to this:
function _install_nvm() {
unset -f nvm npm node
# Set up "nvm" could use "--no-use" to defer setup, but we are here to use it
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This sets up nvm
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # nvm bash_completion
"$@"
}
function nvm() {
_install_nvm nvm "$@"
}
function npm() {
_install_nvm npm "$@"
}
function node() {
_install_nvm node "$@"
}
That suits me better, because there is no loading of anything until I need it, but when I need node, it is (almost) immediately available.
Interesting, thanks - that's helpful. I'll see about fixing nvm_supports_source_options so that it's reliable - I believe that would resolve your problem.
Looks like this is a bug in Gnu bash 3.2.x, which is what Apple ships, apparently because of licensing issues. (bash 3.2.57(1) is what shipped with El Capitan in 2015 and it is still what is shipping with Catalina in 2020, although starting with Catalina the default shell was switched to zsh.) It appears the receiving side of the pipe does not wait for the sending side of the pipe to start producing output before deciding that is has reached EOF. The best workaround I can give you so far is to use a here-is document instead of echo to produce the script. I'm not a portability expert and do not know what it might break, so I do not want to offer this as a PR, but feel free to take it.
nvm_supports_source_options() {
[ "_$( . /dev/stdin yes 2> /dev/null <<'EOF'
[ $# -gt 0 ] && echo $1
EOF
)" = "_yes" ]
}
That seems totally workable! I鈥檒l test it out and report back.
With this solution:
function _install_nvm() {
unset -f nvm npm node
# Set up "nvm" could use "--no-use" to defer setup, but we are here to use it
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This sets up nvm
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # nvm bash_completion
"$@"
}
function nvm() {
_install_nvm nvm "$@"
}
function npm() {
_install_nvm npm "$@"
}
function node() {
_install_nvm node "$@"
}
I still losing 0.01 - 0.03s of shell speed. but thanks anyway.
Spent a hour of debugging my .zshrc file, thanks god I found why
@milushov what did you find out?
I use this for zsh:
if [ -s "$HOME/.nvm/nvm.sh" ]; then
export NVM_DIR="$HOME/.nvm"
nvm_cmds=(nvm node npm yarn)
for cmd in $nvm_cmds ; do
alias $cmd="unalias $nvm_cmds && unset nvm_cmds && . $NVM_DIR/nvm.sh && $cmd"
done
fi
Most helpful comment
@ljharb Thanks for re-opening this.
It appears
--no-useis sometimes slow becausenvm_supports_source_optionsis not reliable, at least not underGNU bash version 3.2.57(1)on my Mac, and whennvm_supports_source_optionsisfalse, the--no-useoption is ignored.The current implementation of
nvm_supports_source_optionswrites to a pipe that then tries to source that pipe as a shell script via/dev/stdin. I guess there is some race condition resulting in the output of the pipe being discarded rather than processed.FWIW, I updated my
.bash_profilescript to this:That suits me better, because there is no loading of anything until I need it, but when I need
node, it is (almost) immediately available.