Lazy load nvm

TL;DR (2021): I ended up just writing my own script node-swap.

TL;DR (2023): I am using Homebrew to switch between node_js versions.

The root cause of the problem is that the nvm initialisation script in zshrc takes about 500ms to run. Proposed workarounds achieve faster loadtimes but not without bugs.

Setup profiling

Either implement the profiling module Zsh provides in zshrc or use the method Armno Prommarak proposes which is less verbose but effective. Read his article on the subject. Run:

for i in $(seq 1 10); do /usr/bin/time zsh -i -c exit; done

Method 1

export NVM_DIR=~/.nvm

# Postpone nvm script load
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" --no-use

# Prepend global calls
function node npm yarn {
  unfunction node npm yarn
  nvm use default
  command "$0" "$@"
}

Pros:

  • Concise

Cons:

  • Slower than other methods
  • Globals break in vim

Method 2

export NVM_DIR=~/.nvm

# Add every binary that requires nvm, npm or node to run to an array of node globals
NODE_GLOBALS=(`find ~/.nvm/versions/node -maxdepth 3 -type l -path '*/bin/*' | xargs -n1 basename | sort | uniq`)
NODE_GLOBALS+=(node nvm)

# Load nvm script
load_nvm () {
  [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
}

# Node global call triggers script loading
for cmd in "${NODE_GLOBALS[@]}"; do
  eval "${cmd}(){ unset -f ${NODE_GLOBALS}; load_nvm; ${cmd} \$@; }"
done

Pros:

  • Fast

Cons:

  • Globals break in vim

Asynchronous shell

Consider asynchronous loading https://github.com/mafredri/zsh-async