Skip to content

Instantly share code, notes, and snippets.

@vhqtvn
Last active December 8, 2025 21:02
Show Gist options
  • Select an option

  • Save vhqtvn/d9fdd8118664f7a6f9c611d148b7c18b to your computer and use it in GitHub Desktop.

Select an option

Save vhqtvn/d9fdd8118664f7a6f9c611d148b7c18b to your computer and use it in GitHub Desktop.
nvm lazy

NVM Autoload Wrapper: A One-Shot Execution Patch

There’s no reason for every shell to waste cycles loading nvm, and no reason for a human to type nvm use like it’s still 2013. The wrappers below serve as a transient bootloader layer for Node tooling: node, npm, pnpm, yarn.

The behavior is simple:

  1. Shell starts clean, no nvm overhead.

  2. First time you run any Node binary:

    • nvm is loaded
    • local .nvmrc or a special-case global .nvmrc is detected
    • the correct Node version is ensured and activated
    • the wrapper removes itself from PATH
    • execution is forwarded to the real binary
  3. For the rest of the session, everything runs without any wrapper in the way. No extra cost.

This makes nvm effectively lazy-loaded and self-erasing.


Mechanics

.profile silently injects transient PATH entries:

tmp_vh_add_to_path() {
  local dir="$1"
  if [[ -d "$dir" && ":$PATH:" != *":$dir:"* ]]; then
    export PATH="$dir:$PATH"
  fi
}

tmp_vh_add_to_path "$HOME/.local/bin"
for once_dir in "$HOME/.local/bin/once-wrapper/"*; do
  if [ -d "$once_dir" ]; then
    tmp_vh_add_to_path "$once_dir"
  fi
done
tmp_vh_add_to_path "$HOME/.npm-global/bin"

export NVM_DIR="$HOME/.nvm"
load-nvm() {
    [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" --no-use
    [ -s "$NVM_DIR/bash_completion" ] && . "$NVM_DIR/bash_completion"
    unset load-nvm
}

Inside ~/.local/bin/once-wrapper/.include.sh:

remove_from_path() {
  PATH=$(echo "$PATH" | tr ':' '\n' | grep -v "$1" | tr '\n' ':' | sed 's/:$//')
  export PATH
}

wrapper_dir=$(dirname "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")")
remove_from_path "$(basename "$(dirname "$0")")"

This script deletes its own wrapper directory from PATH on invocation. The entire scheme is single-use by design.


Wrapper logic

Stored in:

~/.local/bin/once-wrapper/node/{node,npm,pnpm,yarn}

#!/usr/bin/env bash
. $(dirname $0)/../.include.sh

export NVM_DIR="$HOME/.nvm"
. "$NVM_DIR/nvm.sh" --no-use

if command -v node >/dev/null 2>&1; then
    LOADER_JS="$(grep -F '###''loader-js:' "$0" | sed 's/###''loader-js: //' | base64 --wrap=0)"
    MAIN_JS="$(node --loader="data:text/javascript;base64,$LOADER_JS" "$@" "$0" 2>/dev/null)"

    case "$MAIN_JS" in
        file://$HOME/.npm-global/*)
            NVMRC_FILE="$HOME/.npm-global/.nvmrc"
            ;;
        *)
            NVMRC_FILE="$(nvm_find_nvmrc)"
            ;;
    esac

    if [ -n "$NVMRC_FILE" ]; then
        NVM_VERSION="$(nvm version "$(cat "$NVMRC_FILE")")"
        [ "$NVM_VERSION" = "N/A" ] && nvm install "$(cat "$NVMRC_FILE")"
        nvm use "$(cat "$NVMRC_FILE")"
    else
        nvm use default
    fi
fi

exec "$(basename $0)" "$@"

###loader-js: export function load(url){console.log(url);process.exit(0);}

The JS loader technique extracts the resource URL for determining which .nvmrc applies, without executing the script normally.


Summary

A quick summary of what this setup accomplishes:

  • nvm is no longer loaded at shell startup
  • the correct Node version is automatically selected per project
  • wrappers delete themselves from PATH after first use
  • subsequent runs are direct and fast

A small amount of shell trickery results in a clean user experience and better performance. This approach can be generalized to any language version manager that wastes time during shell initialization.

# ~/.local/bin/once-wrapper/.include.sh
remove_from_path() {
PATH=$(echo "$PATH" | tr ':' '\n' | grep -v "$1" | tr '\n' ':' | sed 's/:$//')
export PATH
}
wrapper_dir=$(dirname "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")")
remove_from_path "$(basename "$(dirname "$0")")"
tmp_vh_add_to_path() {
local dir="$1"
if [[ -d "$dir" && ":$PATH:" != *":$dir:"* ]]; then
export PATH="$dir:$PATH"
fi
}
tmp_vh_add_to_path "$HOME/.local/bin"
for once_dir in "$HOME/.local/bin/once-wrapper/"*; do
if [ -d "$once_dir" ]; then
tmp_vh_add_to_path "$once_dir"
fi
done
tmp_vh_add_to_path "$HOME/.npm-global/bin"
export NVM_DIR="$HOME/.nvm"
load-nvm() {
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" --no-use
[ -s "$NVM_DIR/bash_completion" ] && . "$NVM_DIR/bash_completion"
unset load-nvm
}
# ~/.local/bin/once-wrapper/node/{node,npm,pnpm,yarn}
#!/usr/bin/env bash
. $(dirname $0)/../.include.sh
export NVM_DIR="$HOME/.nvm"
. "$NVM_DIR/nvm.sh" --no-use
if command -v node >/dev/null 2>&1; then
LOADER_JS="$(grep -F '###''loader-js:' "$0" | sed 's/###''loader-js: //' | base64 --wrap=0)"
MAIN_JS="$(node --loader="data:text/javascript;base64,$LOADER_JS" "$@" "$0" 2>/dev/null)"
case "$MAIN_JS" in
file://$HOME/.npm-global/*)
NVMRC_FILE="$HOME/.npm-global/.nvmrc"
;;
*)
NVMRC_FILE="$(nvm_find_nvmrc)"
;;
esac
if [ -n "$NVMRC_FILE" ]; then
NVM_VERSION="$(nvm version "$(cat "$NVMRC_FILE")")"
[ "$NVM_VERSION" = "N/A" ] && nvm install "$(cat "$NVMRC_FILE")"
nvm use "$(cat "$NVMRC_FILE")"
else
nvm use default
fi
fi
exec "$(basename $0)" "$@"
###loader-js: export function load(url){console.log(url);process.exit(0);}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment