From a9933f77a6a416b3e9fa9a8d8b31377b448af4a8 Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Tue, 5 May 2026 18:10:53 -0700 Subject: [PATCH] [Fix] `nvm exec`/`nvm run`: warn when no version and no .nvmrc Currently these commands silently fall back to the active node version when neither a version argument nor an `.nvmrc` resolves, making them invisibly dependent on shell state and impossible to script predictably (see #3755). Print a stderr deprecation warning in this case (suppressed by `--silent`) and continue with the active node version, so existing callers keep working. The follow-up change will turn this into a hard error; pass `current` explicitly (e.g. `nvm exec current node ...`) to silence the warning and lock in the new behavior now. Refs #3755 --- nvm.sh | 25 ++++++++ ...ut a resolvable version warn and fall back | 63 +++++++++++++++++++ 2 files changed, 88 insertions(+) create mode 100755 test/fast/Running 'nvm exec' and 'nvm run' without a resolvable version warn and fall back diff --git a/nvm.sh b/nvm.sh index 1554b5b1..52c2b7f8 100755 --- a/nvm.sh +++ b/nvm.sh @@ -4179,6 +4179,14 @@ nvm() { if [ $has_checked_nvmrc -ne 1 ]; then { NVM_RC_VERSION="$(NVM_SILENT="${NVM_SILENT:-0}" nvm_rc_version 3>&1 1>&4)"; } 4>&1 && has_checked_nvmrc=1 fi + if [ -z "${NVM_RC_VERSION-}" ]; then + if [ "${NVM_SILENT:-0}" -ne 1 ]; then + nvm_err 'WARNING: `nvm run` was invoked without a version argument and without an .nvmrc file.' + nvm_err ' Falling back to the active node version; this will become an error in a future release.' + nvm_err ' Pass `current` explicitly (e.g. `nvm run current ...`) to silence this warning.' + fi + NVM_RC_VERSION="$(nvm_version current)" ||: + fi provided_version="${NVM_RC_VERSION}" IS_VERSION_FROM_NVMRC=1 VERSION="$(nvm_version "${NVM_RC_VERSION}")" ||: @@ -4236,19 +4244,36 @@ nvm() { local provided_version provided_version="$1" + local VERSION_SOURCE + VERSION_SOURCE='' if [ "${NVM_LTS-}" != '' ]; then provided_version="lts/${NVM_LTS:-*}" VERSION="${provided_version}" + VERSION_SOURCE='lts' elif [ -n "${provided_version}" ]; then VERSION="$(nvm_version "${provided_version}")" ||: if [ "_${VERSION}" = '_N/A' ] && ! nvm_is_valid_version "${provided_version}"; then { provided_version="$(NVM_SILENT="${NVM_SILENT:-0}" nvm_rc_version 3>&1 1>&4)"; } 4>&1 && has_checked_nvmrc=1 VERSION="$(nvm_version "${provided_version}")" ||: + if [ -n "${provided_version}" ]; then + VERSION_SOURCE='nvmrc' + fi else + VERSION_SOURCE='arg' shift fi fi + if [ -z "${VERSION_SOURCE}" ]; then + if [ "${NVM_SILENT:-0}" -ne 1 ]; then + nvm_err 'WARNING: `nvm exec` was invoked without a version argument and without an .nvmrc file.' + nvm_err ' Falling back to the active node version; this will become an error in a future release.' + nvm_err ' Pass `current` explicitly (e.g. `nvm exec current ...`) to silence this warning.' + fi + provided_version='current' + VERSION="$(nvm_version current)" ||: + fi + nvm_ensure_version_installed "${provided_version}" EXIT_CODE=$? if [ "${EXIT_CODE}" != "0" ]; then diff --git a/test/fast/Running 'nvm exec' and 'nvm run' without a resolvable version warn and fall back b/test/fast/Running 'nvm exec' and 'nvm run' without a resolvable version warn and fall back new file mode 100755 index 00000000..7e6261bf --- /dev/null +++ b/test/fast/Running 'nvm exec' and 'nvm run' without a resolvable version warn and fall back @@ -0,0 +1,63 @@ +#!/bin/sh + +set -ex + +die () { echo "$@" ; cleanup ; exit 1; } + +cleanup() { + cd "${ORIG_PWD}" 2>/dev/null || true + [ -n "${TMP_DIR-}" ] && rm -rf "${TMP_DIR}" +} + +export NVM_DIR="$(cd ../.. && pwd)" + +: nvm.sh +\. ../../nvm.sh + +\. ../common.sh + +ORIG_PWD="$(pwd)" + +# Run from a fresh, empty directory so no ambient .nvmrc above the test dir +# can satisfy the lookup and mask the warning. +TMP_DIR="$(mktemp -d)" +cd "${TMP_DIR}" || die "could not cd to temp dir" + +EXEC_WARNING='WARNING: `nvm exec` was invoked without a version argument and without an .nvmrc file.' +RUN_WARNING='WARNING: `nvm run` was invoked without a version argument and without an .nvmrc file.' + +# `nvm exec` with no version and no .nvmrc should warn on stderr (and fall back). +set +ex # needed for stderr +EXEC_STDERR="$(nvm exec &1 1>/dev/null)" +set -ex +case "${EXEC_STDERR}" in + *"${EXEC_WARNING}"*) ;; + *) die "'nvm exec' with no version did not warn; got >${EXEC_STDERR}<" ;; +esac + +# `--silent` should suppress the warning. +set +ex # needed for stderr +EXEC_SILENT_STDERR="$(nvm exec --silent &1 1>/dev/null)" +set -ex +case "${EXEC_SILENT_STDERR}" in + *WARNING*) die "'nvm exec --silent' should not warn; got >${EXEC_SILENT_STDERR}<" ;; +esac + +# `nvm run` with an unresolvable version and no .nvmrc should warn (and fall back). +set +ex # needed for stderr +RUN_STDERR="$(nvm run bogusversion &1 1>/dev/null)" +set -ex +case "${RUN_STDERR}" in + *"${RUN_WARNING}"*) ;; + *) die "'nvm run' with no resolvable version did not warn; got >${RUN_STDERR}<" ;; +esac + +# `--silent` should suppress the warning. +set +ex # needed for stderr +RUN_SILENT_STDERR="$(nvm run --silent bogusversion &1 1>/dev/null)" +set -ex +case "${RUN_SILENT_STDERR}" in + *WARNING*) die "'nvm run --silent' should not warn; got >${RUN_SILENT_STDERR}<" ;; +esac + +cleanup