Growing with the Web

How to fix nvm slowing down terminal initialisation


I started using nvm a while ago but ever since then my terminal has been very slow to start up.

The root cause of the problem is that the nvm initialisation script, the one that gets added to your ~/.bashrc on install, takes about 500ms to run on my Macbook. As far as I can tell the reason for this slowness is because nvm is written completely in bash.

❯ time . "$NVM_DIR"/

real    0m0.556s
user    0m0.346s
sys     0m0.255s

I did some research to try find the solution on the many perf-related issues but nothing really seemed to work perfectly except for not running the init script at all. I then stumbled upon this solution from @crenwick which mostly works:

alias load_nvm='export NVM_DIR="$HOME/.nvm" && [ -s "$NVM_DIR/" ] && . "$NVM_DIR/"'
alias node='unalias node npm && load_nvm && node'
alias npm='unalias node npm && load_nvm && npm'

The problem here is that nvm is not available at all until node or npm is run. I made a few tweaks and came with something that seemed to work pretty good:

if [ -s "$HOME/.nvm/" ]; then
  export NVM_DIR="$HOME/.nvm"
  [ -s "$NVM_DIR/bash_completion" ] && . "$NVM_DIR/bash_completion"
  alias nvm='unalias nvm node npm && . "$NVM_DIR"/ && nvm'
  alias node='unalias nvm node npm && . "$NVM_DIR"/ && node'
  alias npm='unalias nvm node npm && . "$NVM_DIR"/ && npm'

This version will defer nvm initialisation until either nvm, node or npm is run, at which point it will also unalias all the commands so they all work off the PATH as normal and run the requested command. It also contains the niceties that come standard such as checking for the files and bash completion.

After running with this solution for a couple of days I made a few more improvements to handle a larger number of commands more easily in order to cover my common globally installed npm commands:

# Defer initialization of nvm until nvm, node or a node-dependent command is
# run. Ensure this block is only run once if .bashrc gets sourced multiple times
# by checking whether __init_nvm is a function.
if [ -s "$HOME/.nvm/" ] && [ ! "$(type -t __init_nvm)" = function ]; then
  export NVM_DIR="$HOME/.nvm"
  [ -s "$NVM_DIR/bash_completion" ] && . "$NVM_DIR/bash_completion"
  declare -a __node_commands=('nvm' 'node' 'npm' 'yarn' 'gulp' 'grunt' 'webpack')
  function __init_nvm() {
    for i in "${__node_commands[@]}"; do unalias $i; done
    . "$NVM_DIR"/
    unset __node_commands
    unset -f __init_nvm
  for i in "${__node_commands[@]}"; do alias $i='__init_nvm && '$i; done

Like this article?
Subscribe for more!