Skip to content

Instantly share code, notes, and snippets.

@RichardTMiles
Last active July 4, 2025 03:08
Show Gist options
  • Save RichardTMiles/cf29cb157402e376f1d782b111583d38 to your computer and use it in GitHub Desktop.
Save RichardTMiles/cf29cb157402e376f1d782b111583d38 to your computer and use it in GitHub Desktop.
NVM wrapper to make execution pass through the systems PATH bypassing the need for shell profile edits
#!/usr/bin/env bash
# ---------------------------------------------------------------------------
# nvx — self‑installing, self‑initialising, shell‑agnostic wrapper around nvm
# ---------------------------------------------------------------------------
# • Installs **nvm** (≥ $MIN_VERSION) the first time it runs.
# • Loads nvm with NVM_AUTO_MODE=install so `nvm use` auto‑installs versions.
# • Always activates an .nvmrc auto‑switch hook—no NVX_AUTO flag needed.
# • On first run it appends a single line to the user’s shell rc file:
# source /path/to/nvx
# so future shells initialise nvx automatically.
# • Safe to *execute* or *source*; exits when run as a command, returns when sourced.
# ---------------------------------------------------------------------------
set -euo pipefail
# ---------------------------------------------------------------------------
# Detect if nvx is being *sourced* (so we use `return` instead of `exit`)
# ---------------------------------------------------------------------------
_nvxsourced=0
if [[ "${BASH_SOURCE[0]:-}" != "" && "${BASH_SOURCE[0]}" != "$0" ]]; then
_nvxsourced=1
fi
nvx_done() {
local code="${1:-0}"
if [[ $_nvxsourced -eq 1 ]]; then
return "$code"
else
exit "$code"
fi
}
# ---------------------------------------------------------------------------
# Ensure $HOME (some sudo/CI contexts lack it)
# ---------------------------------------------------------------------------
if [[ -z "${HOME:-}" ]]; then
export HOME="$(getent passwd "$(id -u)" | cut -d: -f6 || echo /usr/local/lib)"
fi
# ---- configuration ---------------------------------------------------------
NVM_REPO="https://github.com/nvm-sh/nvm.git"
MIN_VERSION="${MIN_VERSION:-v0.40.3}"
# Prefer user‑specified $NVM_DIR → $HOME/.nvm → /usr/local/.nvm
NVM_DIR="${NVM_DIR:-${HOME:+$HOME/.nvm}}"; : "${NVM_DIR:=/usr/local/.nvm}"
# ---------------------------------------------------------------------------
# ---------------------------------------------------------------------------
# Helper: add nvx to the user’s shell startup file once ---------------------
# ---------------------------------------------------------------------------
auto_init_rc() {
local script_path="$(command -v nvx 2>/dev/null || printf '%s' "$0")"
local rc
case "${SHELL##*/}" in
bash) rc="$HOME/.bashrc" ;;
zsh) rc="$HOME/.zshrc" ;;
fish) rc="$HOME/.config/fish/config.fish" ;;
*) rc="$HOME/.profile" ;;
esac
# Append only if we haven’t already added it
if ! grep -qs "source .*nvx" "$rc" 2>/dev/null; then
printf '\n# Load nvx (nvm wrapper)\nsource %s\n' "$script_path" >> "$rc"
printf '☑️ nvx initialised in %s\n' "$rc" >&2
fi
}
auto_init_rc # harmless if already present
# ---------------------------------------------------------------------------
# Install nvm if missing -----------------------------------------------------
# ---------------------------------------------------------------------------
install_nvm() {
echo "🔧 Installing nvm into $NVM_DIR ..." >&2
git clone --depth 1 "$NVM_REPO" "$NVM_DIR"
(
cd "$NVM_DIR"
git fetch --tags --quiet
git -c advice.detachedHead=false checkout "$MIN_VERSION"
)
}
[[ -s "$NVM_DIR/nvm.sh" ]] || install_nvm
# ---------------------------------------------------------------------------
# Load nvm with auto‑install mode -------------------------------------------
# ---------------------------------------------------------------------------
export NVM_AUTO_MODE="install"
set +e # tolerate initial nvm_auto failures while sourcing
# shellcheck source=/dev/null
. "$NVM_DIR/nvm.sh"
set -e
# ---------------------------------------------------------------------------
# Auto .nvmrc hook (always active) ------------------------------------------
# ---------------------------------------------------------------------------
if [[ -z ${__NVX_HOOK_LOADED:-} ]]; then
__NVX_HOOK_LOADED=1
__nvx_load_nvmrc() {
local nvmrc_path="$(nvm_find_nvmrc)"
if [[ -n $nvmrc_path ]]; then
nvm use --silent --install "$(<"$nvmrc_path")"
else
[[ "$(nvm current)" == "$(nvm version default)" ]] || nvm use --silent default
fi
}
case "${SHELL##*/}" in
zsh)
autoload -U add-zsh-hook && add-zsh-hook chpwd __nvx_load_nvmrc ;;
fish)
cat <<'FISH' | fish -q
function __nvx_load_nvmrc --on-variable PWD
set nvmrc_path (nvm_find_nvmrc)
if test -n "$nvmrc_path"
nvm use --silent --install (cat "$nvmrc_path")
else if test (nvm current) != (nvm version default)
nvm use --silent default
end
end
__nvx_load_nvmrc
FISH
;;
*) PROMPT_COMMAND="__nvx_load_nvmrc${PROMPT_COMMAND:+ ; $PROMPT_COMMAND}" ;;
esac
fi
# Ensure it runs at least once for the starting directory
__nvx_load_nvmrc 2>/dev/null || true
# ---------------------------------------------------------------------------
# Sub‑commands --------------------------------------------------------------
# ---------------------------------------------------------------------------
if [[ $# -eq 0 ]]; then
nvm --help
nvx_done 0
fi
if [[ $1 == print-env ]]; then
shift
nvm use --silent --install "$@" >/dev/null
nvm_print_npm_config
nvx_done 0
fi
if [[ $1 == exec ]]; then
shift
nvm exec "$@"
nvx_done "$?"
fi
# ---------------------------------------------------------------------------
# Forward everything else straight to nvm ------------------------------------
# ---------------------------------------------------------------------------
nvm "$@"
nvx_done "$?"
@RichardTMiles
Copy link
Author

RichardTMiles commented Jul 2, 2025

The comand below is a drop-in replacement to put this code in /bin/nvx. You can make this /bin/nvm or just stick with switching you nvm commands to the wrapper nvx. I recommend the latter. Using commands like nvm use then becomes nvx use.

curl -fsSL https://gist.githubusercontent.com/RichardTMiles/cf29cb157402e376f1d782b111583d38/raw/nvx | install -Dm755 /dev/stdin /bin/nvx

If you have permission issues you might try running the following export NVM_DIR = $HOME then re-run nvx.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment