From aface01a4d23e0b6a00cb5d15d9c04d3ff75776c Mon Sep 17 00:00:00 2001 From: Jake Lodwick <25925+jakelodwick@users.noreply.github.com> Date: Tue, 17 Feb 2026 13:44:31 -0700 Subject: [PATCH 1/3] [Refactor] `nvm_alias`, `nvm_resolve_alias`: use builtins [Tests] `nvm_alias`, `nvm_resolve_alias`: add edge-case tests nvm_alias() used a sed/awk pipeline to strip comments and blank lines from alias files that almost always contain a single word. A while-read loop with parameter expansion does the same filtering more directly. nvm_resolve_alias() piped nvm_alias through head and tail to extract one line, and used printf/grep for cycle detection. Parameter expansion and a case statement replace both without the extra plumbing. All replacements are POSIX (read -r, case, IFS=, parameter expansion). As a side effect, this also removes 4 external process invocations during shell init. --- nvm.sh | 28 +++++++++++++++---- .../nvm_alias handles comment-only alias file | 17 +++++++++++ .../nvm_alias handles empty alias file | 17 +++++++++++ .../nvm_alias strips trailing whitespace | 16 +++++++++++ .../nvm_resolve_alias handles deep chain | 20 +++++++++++++ .../nvm_resolve_alias with nonexistent target | 19 +++++++++++++ 6 files changed, 111 insertions(+), 6 deletions(-) create mode 100755 test/fast/Aliases/nvm_alias handles comment-only alias file create mode 100755 test/fast/Aliases/nvm_alias handles empty alias file create mode 100755 test/fast/Aliases/nvm_alias strips trailing whitespace create mode 100755 test/fast/Aliases/nvm_resolve_alias handles deep chain create mode 100755 test/fast/Aliases/nvm_resolve_alias with nonexistent target diff --git a/nvm.sh b/nvm.sh index 7268de2570..98ef38719a 100755 --- a/nvm.sh +++ b/nvm.sh @@ -1323,7 +1323,22 @@ nvm_alias() { return 2 fi - command sed 's/#.*//; s/[[:space:]]*$//' "${NVM_ALIAS_PATH}" | command awk 'NF' + local NVM_ALIAS_LINE + while IFS= read -r NVM_ALIAS_LINE || [ -n "${NVM_ALIAS_LINE}" ]; do + NVM_ALIAS_LINE="${NVM_ALIAS_LINE%%#*}" + case "${NVM_ALIAS_LINE}" in + *[!\ \ ]*) ;; + *) continue ;; + esac + while : ; do + case "${NVM_ALIAS_LINE}" in + *' ') NVM_ALIAS_LINE="${NVM_ALIAS_LINE% }" ;; + *' ') NVM_ALIAS_LINE="${NVM_ALIAS_LINE% }" ;; + *) break ;; + esac + done + nvm_echo "${NVM_ALIAS_LINE}" + done < "${NVM_ALIAS_PATH}" } nvm_ls_current() { @@ -1356,13 +1371,14 @@ nvm_resolve_alias() { local ALIAS ALIAS="${PATTERN}" local ALIAS_TEMP + local ALIAS_OUTPUT local SEEN_ALIASES - SEEN_ALIASES="${ALIAS}" - local NVM_ALIAS_INDEX - NVM_ALIAS_INDEX=1 + SEEN_ALIASES=" ${ALIAS} " while true; do - ALIAS_TEMP="$( (nvm_alias "${ALIAS}" 2>/dev/null | command head -n "${NVM_ALIAS_INDEX}" | command tail -n 1) || nvm_echo)" + ALIAS_OUTPUT="$(nvm_alias "${ALIAS}" 2>/dev/null)" || ALIAS_OUTPUT='' + ALIAS_TEMP="${ALIAS_OUTPUT%% +*}" if [ -z "${ALIAS_TEMP}" ]; then break @@ -1373,7 +1389,7 @@ nvm_resolve_alias() { break fi - SEEN_ALIASES="${SEEN_ALIASES}\\n${ALIAS_TEMP}" + SEEN_ALIASES="${SEEN_ALIASES}${ALIAS_TEMP} " ALIAS="${ALIAS_TEMP}" done diff --git a/test/fast/Aliases/nvm_alias handles comment-only alias file b/test/fast/Aliases/nvm_alias handles comment-only alias file new file mode 100755 index 0000000000..cb8fa077ff --- /dev/null +++ b/test/fast/Aliases/nvm_alias handles comment-only alias file @@ -0,0 +1,17 @@ +#!/bin/sh + +die () { echo "$@" ; exit 1; } + +export NVM_DIR="$(cd ../../.. && pwd)" + +: nvm.sh +\. "${NVM_DIR}/nvm.sh" + +# Create an alias file containing only comments +printf '# this is a comment\n# another comment\n' > ../../../alias/test-edge-comments +ACTUAL="$(nvm_alias test-edge-comments 2>/dev/null)" +EXIT_CODE="$(nvm_alias test-edge-comments 2>/dev/null; echo $?)" +[ -z "${ACTUAL}" ] || die "expected empty output for comment-only alias file, got >${ACTUAL}<" +[ "${EXIT_CODE}" = '0' ] || die "expected exit code 0, got ${EXIT_CODE}" + +rm -f ../../../alias/test-edge-comments diff --git a/test/fast/Aliases/nvm_alias handles empty alias file b/test/fast/Aliases/nvm_alias handles empty alias file new file mode 100755 index 0000000000..3f80c73ad4 --- /dev/null +++ b/test/fast/Aliases/nvm_alias handles empty alias file @@ -0,0 +1,17 @@ +#!/bin/sh + +die () { echo "$@" ; exit 1; } + +export NVM_DIR="$(cd ../../.. && pwd)" + +: nvm.sh +\. "${NVM_DIR}/nvm.sh" + +# Create an empty alias file +printf '' > ../../../alias/test-edge-empty +ACTUAL="$(nvm_alias test-edge-empty 2>/dev/null)" +EXIT_CODE="$(nvm_alias test-edge-empty 2>/dev/null; echo $?)" +[ -z "${ACTUAL}" ] || die "expected empty output for empty alias file, got >${ACTUAL}<" +[ "${EXIT_CODE}" = '0' ] || die "expected exit code 0, got ${EXIT_CODE}" + +rm -f ../../../alias/test-edge-empty diff --git a/test/fast/Aliases/nvm_alias strips trailing whitespace b/test/fast/Aliases/nvm_alias strips trailing whitespace new file mode 100755 index 0000000000..caf53bb4a9 --- /dev/null +++ b/test/fast/Aliases/nvm_alias strips trailing whitespace @@ -0,0 +1,16 @@ +#!/bin/sh + +die () { echo "$@" ; exit 1; } + +export NVM_DIR="$(cd ../../.. && pwd)" + +: nvm.sh +\. "${NVM_DIR}/nvm.sh" + +# Create an alias file with trailing spaces and tabs +printf '22.1.0 \t \n' > ../../../alias/test-edge-trailing-ws +ACTUAL="$(nvm_alias test-edge-trailing-ws)" +EXPECTED='22.1.0' +[ "${ACTUAL}" = "${EXPECTED}" ] || die "expected >${EXPECTED}<, got >${ACTUAL}<" + +rm -f ../../../alias/test-edge-trailing-ws diff --git a/test/fast/Aliases/nvm_resolve_alias handles deep chain b/test/fast/Aliases/nvm_resolve_alias handles deep chain new file mode 100755 index 0000000000..435d35ed67 --- /dev/null +++ b/test/fast/Aliases/nvm_resolve_alias handles deep chain @@ -0,0 +1,20 @@ +#!/bin/sh + +die () { echo "$@" ; exit 1; } + +export NVM_DIR="$(cd ../../.. && pwd)" + +: nvm.sh +\. "${NVM_DIR}/nvm.sh" + +# Create a 4-deep alias chain: hop1 -> hop2 -> hop3 -> hop4 -> 0.0.99 +echo 'hop2' > ../../../alias/hop1 +echo 'hop3' > ../../../alias/hop2 +echo 'hop4' > ../../../alias/hop3 +echo '0.0.99' > ../../../alias/hop4 + +ACTUAL="$(nvm_resolve_alias hop1)" +EXPECTED='v0.0.99' +[ "${ACTUAL}" = "${EXPECTED}" ] || die "expected >${EXPECTED}< for 4-deep chain, got >${ACTUAL}<" + +rm -f ../../../alias/hop1 ../../../alias/hop2 ../../../alias/hop3 ../../../alias/hop4 diff --git a/test/fast/Aliases/nvm_resolve_alias with nonexistent target b/test/fast/Aliases/nvm_resolve_alias with nonexistent target new file mode 100755 index 0000000000..48eb9596a7 --- /dev/null +++ b/test/fast/Aliases/nvm_resolve_alias with nonexistent target @@ -0,0 +1,19 @@ +#!/bin/sh + +die () { echo "$@" ; exit 1; } + +export NVM_DIR="$(cd ../../.. && pwd)" + +: nvm.sh +\. "${NVM_DIR}/nvm.sh" + +# Create an alias pointing to a version that does not exist as an alias +echo '99.99.99' > ../../../alias/test-edge-noexist + +ACTUAL="$(nvm_resolve_alias test-edge-noexist)" +EXPECTED='v99.99.99' +EXIT_CODE="$(nvm_resolve_alias test-edge-noexist >/dev/null 2>&1; echo $?)" +[ "${ACTUAL}" = "${EXPECTED}" ] || die "expected >${EXPECTED}<, got >${ACTUAL}<" +[ "${EXIT_CODE}" = '0' ] || die "expected exit code 0, got ${EXIT_CODE}" + +rm -f ../../../alias/test-edge-noexist From d62de0e086efae81e2a285385ef596e3589baa2d Mon Sep 17 00:00:00 2001 From: Jake Lodwick <25925+jakelodwick@users.noreply.github.com> Date: Wed, 18 Feb 2026 14:29:52 -0700 Subject: [PATCH 2/3] [Refactor] `nvm_alias`: use [[:space:]] instead of literal tab [Tests] `nvm_alias`: add edge-case tests for hostile file content --- nvm.sh | 5 ++--- ...vm_alias handles alias with spaces in name | 17 ++++++++++++++ ...vm_alias handles binary data after version | 20 +++++++++++++++++ .../Aliases/nvm_alias handles carriage return | 22 +++++++++++++++++++ .../Aliases/nvm_alias handles embedded NUL | 22 +++++++++++++++++++ ...m_alias handles form feed and vertical tab | 22 +++++++++++++++++++ .../nvm_alias handles no trailing newline | 16 ++++++++++++++ .../Aliases/nvm_alias handles very long line | 17 ++++++++++++++ 8 files changed, 138 insertions(+), 3 deletions(-) create mode 100755 test/fast/Aliases/nvm_alias handles alias with spaces in name create mode 100755 test/fast/Aliases/nvm_alias handles binary data after version create mode 100755 test/fast/Aliases/nvm_alias handles carriage return create mode 100755 test/fast/Aliases/nvm_alias handles embedded NUL create mode 100755 test/fast/Aliases/nvm_alias handles form feed and vertical tab create mode 100755 test/fast/Aliases/nvm_alias handles no trailing newline create mode 100755 test/fast/Aliases/nvm_alias handles very long line diff --git a/nvm.sh b/nvm.sh index 98ef38719a..981f3eb72d 100755 --- a/nvm.sh +++ b/nvm.sh @@ -1327,13 +1327,12 @@ nvm_alias() { while IFS= read -r NVM_ALIAS_LINE || [ -n "${NVM_ALIAS_LINE}" ]; do NVM_ALIAS_LINE="${NVM_ALIAS_LINE%%#*}" case "${NVM_ALIAS_LINE}" in - *[!\ \ ]*) ;; + *[![:space:]]*) ;; *) continue ;; esac while : ; do case "${NVM_ALIAS_LINE}" in - *' ') NVM_ALIAS_LINE="${NVM_ALIAS_LINE% }" ;; - *' ') NVM_ALIAS_LINE="${NVM_ALIAS_LINE% }" ;; + *[[:space:]]) NVM_ALIAS_LINE="${NVM_ALIAS_LINE%[[:space:]]}" ;; *) break ;; esac done diff --git a/test/fast/Aliases/nvm_alias handles alias with spaces in name b/test/fast/Aliases/nvm_alias handles alias with spaces in name new file mode 100755 index 0000000000..645461a833 --- /dev/null +++ b/test/fast/Aliases/nvm_alias handles alias with spaces in name @@ -0,0 +1,17 @@ +#!/bin/sh + +die () { echo "$@" ; exit 1; } + +export NVM_DIR="$(cd ../../.. && pwd)" + +: nvm.sh +\. "${NVM_DIR}/nvm.sh" + +# Create an alias file whose name contains spaces +mkdir -p ../../../alias +printf 'v22.1.0\n' > "../../../alias/test edge spaces" +ACTUAL="$(nvm_alias "test edge spaces")" +EXPECTED='v22.1.0' +[ "${ACTUAL}" = "${EXPECTED}" ] || die "expected >${EXPECTED}<, got >${ACTUAL}<" + +rm -f "../../../alias/test edge spaces" diff --git a/test/fast/Aliases/nvm_alias handles binary data after version b/test/fast/Aliases/nvm_alias handles binary data after version new file mode 100755 index 0000000000..7c9de414da --- /dev/null +++ b/test/fast/Aliases/nvm_alias handles binary data after version @@ -0,0 +1,20 @@ +#!/bin/sh + +die () { echo "$@" ; exit 1; } + +export NVM_DIR="$(cd ../../.. && pwd)" + +: nvm.sh +\. "${NVM_DIR}/nvm.sh" + +# Create an alias file where the first line has a valid version +# followed by a second line of binary-like data +printf 'v22.1.0\n' > ../../../alias/test-edge-binary +printf '\001\002\003\377\n' >> ../../../alias/test-edge-binary +ACTUAL="$(nvm_alias test-edge-binary)" +# nvm_alias emits every non-blank, non-comment line — first line should be the version +FIRST_LINE="$(nvm_echo "${ACTUAL}" | command head -n 1)" +EXPECTED='v22.1.0' +[ "${FIRST_LINE}" = "${EXPECTED}" ] || die "expected first line >${EXPECTED}<, got >${FIRST_LINE}<" + +rm -f ../../../alias/test-edge-binary diff --git a/test/fast/Aliases/nvm_alias handles carriage return b/test/fast/Aliases/nvm_alias handles carriage return new file mode 100755 index 0000000000..ba44f1c855 --- /dev/null +++ b/test/fast/Aliases/nvm_alias handles carriage return @@ -0,0 +1,22 @@ +#!/bin/sh + +die () { echo "$@" ; exit 1; } + +export NVM_DIR="$(cd ../../.. && pwd)" + +: nvm.sh +\. "${NVM_DIR}/nvm.sh" + +# Create an alias file with Windows-style line endings (CRLF) +printf 'v22.1.0\r\n' > ../../../alias/test-edge-cr +ACTUAL="$(nvm_alias test-edge-cr)" +EXPECTED='v22.1.0' +[ "${ACTUAL}" = "${EXPECTED}" ] || die "expected >${EXPECTED}<, got >${ACTUAL}<" + +# Create an alias file with bare carriage return (no newline) +printf 'v22.2.0\r' > ../../../alias/test-edge-cr-bare +ACTUAL="$(nvm_alias test-edge-cr-bare)" +EXPECTED='v22.2.0' +[ "${ACTUAL}" = "${EXPECTED}" ] || die "expected >${EXPECTED}< for bare CR, got >${ACTUAL}<" + +rm -f ../../../alias/test-edge-cr ../../../alias/test-edge-cr-bare diff --git a/test/fast/Aliases/nvm_alias handles embedded NUL b/test/fast/Aliases/nvm_alias handles embedded NUL new file mode 100755 index 0000000000..28b3dbb621 --- /dev/null +++ b/test/fast/Aliases/nvm_alias handles embedded NUL @@ -0,0 +1,22 @@ +#!/bin/sh + +die () { echo "$@" ; exit 1; } + +export NVM_DIR="$(cd ../../.. && pwd)" + +: nvm.sh +\. "${NVM_DIR}/nvm.sh" + +# Create an alias file with an embedded NUL byte after the version. +# NUL handling varies by shell: some stop at NUL, others read through it. +# The function must not crash, and must emit output starting with the version. +printf 'v22.1.0\000garbage\n' > ../../../alias/test-edge-nul +ACTUAL="$(nvm_alias test-edge-nul)" +EXIT_CODE=$? +[ "${EXIT_CODE}" = '0' ] || die "expected exit code 0, got ${EXIT_CODE}" +case "${ACTUAL}" in + v22.1.0*) ;; # OK — starts with the version regardless of NUL handling + *) die "expected output starting with >v22.1.0<, got >${ACTUAL}<" ;; +esac + +rm -f ../../../alias/test-edge-nul diff --git a/test/fast/Aliases/nvm_alias handles form feed and vertical tab b/test/fast/Aliases/nvm_alias handles form feed and vertical tab new file mode 100755 index 0000000000..c2612418a7 --- /dev/null +++ b/test/fast/Aliases/nvm_alias handles form feed and vertical tab @@ -0,0 +1,22 @@ +#!/bin/sh + +die () { echo "$@" ; exit 1; } + +export NVM_DIR="$(cd ../../.. && pwd)" + +: nvm.sh +\. "${NVM_DIR}/nvm.sh" + +# Create an alias file with trailing form feed +printf 'v22.1.0\f\n' > ../../../alias/test-edge-ff +ACTUAL="$(nvm_alias test-edge-ff)" +EXPECTED='v22.1.0' +[ "${ACTUAL}" = "${EXPECTED}" ] || die "expected >${EXPECTED}< for form feed, got >${ACTUAL}<" + +# Create an alias file with trailing vertical tab +printf 'v22.2.0\v\n' > ../../../alias/test-edge-vt +ACTUAL="$(nvm_alias test-edge-vt)" +EXPECTED='v22.2.0' +[ "${ACTUAL}" = "${EXPECTED}" ] || die "expected >${EXPECTED}< for vertical tab, got >${ACTUAL}<" + +rm -f ../../../alias/test-edge-ff ../../../alias/test-edge-vt diff --git a/test/fast/Aliases/nvm_alias handles no trailing newline b/test/fast/Aliases/nvm_alias handles no trailing newline new file mode 100755 index 0000000000..c87a22ae57 --- /dev/null +++ b/test/fast/Aliases/nvm_alias handles no trailing newline @@ -0,0 +1,16 @@ +#!/bin/sh + +die () { echo "$@" ; exit 1; } + +export NVM_DIR="$(cd ../../.. && pwd)" + +: nvm.sh +\. "${NVM_DIR}/nvm.sh" + +# Create an alias file with no trailing newline +printf 'v22.1.0' > ../../../alias/test-edge-no-newline +ACTUAL="$(nvm_alias test-edge-no-newline)" +EXPECTED='v22.1.0' +[ "${ACTUAL}" = "${EXPECTED}" ] || die "expected >${EXPECTED}<, got >${ACTUAL}<" + +rm -f ../../../alias/test-edge-no-newline diff --git a/test/fast/Aliases/nvm_alias handles very long line b/test/fast/Aliases/nvm_alias handles very long line new file mode 100755 index 0000000000..face2e510d --- /dev/null +++ b/test/fast/Aliases/nvm_alias handles very long line @@ -0,0 +1,17 @@ +#!/bin/sh + +die () { echo "$@" ; exit 1; } + +export NVM_DIR="$(cd ../../.. && pwd)" + +: nvm.sh +\. "${NVM_DIR}/nvm.sh" + +# Create an alias file with a very long line (version followed by a long comment) +LONG_COMMENT="$(printf '%0*d' 10000 0 | command tr '0' 'x')" +printf 'v22.1.0 #%s\n' "${LONG_COMMENT}" > ../../../alias/test-edge-long +ACTUAL="$(nvm_alias test-edge-long)" +EXPECTED='v22.1.0' +[ "${ACTUAL}" = "${EXPECTED}" ] || die "expected >${EXPECTED}<, got >${ACTUAL}<" + +rm -f ../../../alias/test-edge-long From b763492e8e838cdedc991c7ace55d4df4ca287df Mon Sep 17 00:00:00 2001 From: Jake Lodwick <25925+jakelodwick@users.noreply.github.com> Date: Tue, 5 May 2026 23:32:58 -0400 Subject: [PATCH 3/3] [Fix] `nvm_resolve_alias`: detect cycles via newline-anchored `case` [Refactor] `nvm_alias`: one-pass trailing whitespace strip MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The earlier commit on this branch changed SEEN_ALIASES from `\n`-delimited storage (interpreted by `printf '%b' | nvm_grep -e "^${name}$"`) to space- delimited but left the line-anchored grep in place — without newlines in the haystack the anchored pattern can never match, so cycles never break. Switch to literal-newline storage and a `case` pattern anchored on those newlines. Newline anchoring also handles alias names containing spaces, which token-based patterns false-positive on (e.g. lookup of `bar` matches substring " bar " inside " foo bar midway " when the chain visits the multi-token alias `foo bar`). Replace the per-character trailing-whitespace loop in `nvm_alias` with a one-pass parameter expansion, per review. New test file covers self-loop, multi-hop loop, cycle through a space-bearing alias name, and a non-cycle through a space-bearing alias name. Existing `test/fast/Aliases/circular/` fixtures continue to pass. --- nvm.sh | 26 +++++----- .../Aliases/nvm_resolve_alias handles cycles | 52 +++++++++++++++++++ 2 files changed, 66 insertions(+), 12 deletions(-) create mode 100755 test/fast/Aliases/nvm_resolve_alias handles cycles diff --git a/nvm.sh b/nvm.sh index 981f3eb72d..b4883dfc8d 100755 --- a/nvm.sh +++ b/nvm.sh @@ -1330,12 +1330,7 @@ nvm_alias() { *[![:space:]]*) ;; *) continue ;; esac - while : ; do - case "${NVM_ALIAS_LINE}" in - *[[:space:]]) NVM_ALIAS_LINE="${NVM_ALIAS_LINE%[[:space:]]}" ;; - *) break ;; - esac - done + NVM_ALIAS_LINE="${NVM_ALIAS_LINE%"${NVM_ALIAS_LINE##*[![:space:]]}"}" nvm_echo "${NVM_ALIAS_LINE}" done < "${NVM_ALIAS_PATH}" } @@ -1373,7 +1368,9 @@ nvm_resolve_alias() { local ALIAS_OUTPUT local SEEN_ALIASES - SEEN_ALIASES=" ${ALIAS} " + SEEN_ALIASES=" +${ALIAS} +" while true; do ALIAS_OUTPUT="$(nvm_alias "${ALIAS}" 2>/dev/null)" || ALIAS_OUTPUT='' ALIAS_TEMP="${ALIAS_OUTPUT%% @@ -1383,12 +1380,17 @@ nvm_resolve_alias() { break fi - if command printf '%b' "${SEEN_ALIASES}" | nvm_grep -q -e "^${ALIAS_TEMP}$"; then - ALIAS="∞" - break - fi + case "${SEEN_ALIASES}" in + *" +${ALIAS_TEMP} +"*) + ALIAS="∞" + break + ;; + esac - SEEN_ALIASES="${SEEN_ALIASES}${ALIAS_TEMP} " + SEEN_ALIASES="${SEEN_ALIASES}${ALIAS_TEMP} +" ALIAS="${ALIAS_TEMP}" done diff --git a/test/fast/Aliases/nvm_resolve_alias handles cycles b/test/fast/Aliases/nvm_resolve_alias handles cycles new file mode 100755 index 0000000000..3856742646 --- /dev/null +++ b/test/fast/Aliases/nvm_resolve_alias handles cycles @@ -0,0 +1,52 @@ +#!/bin/sh + +die () { echo "$@" ; exit 1; } + +export NVM_DIR="$(cd ../../.. && pwd)" + +: nvm.sh +\. "${NVM_DIR}/nvm.sh" + +# 1-hop self-reference: alias points to itself +echo 'self' > '../../../alias/self' + +ACTUAL="$(nvm_resolve_alias self)" +EXPECTED='∞' +[ "${ACTUAL}" = "${EXPECTED}" ] || die "expected >${EXPECTED}< for self-reference, got >${ACTUAL}<" + +rm -f '../../../alias/self' + +# Multi-hop loop: link1 -> link2 -> link3 -> link1 +echo 'link2' > '../../../alias/link1' +echo 'link3' > '../../../alias/link2' +echo 'link1' > '../../../alias/link3' + +ACTUAL="$(nvm_resolve_alias link1)" +EXPECTED='∞' +[ "${ACTUAL}" = "${EXPECTED}" ] || die "expected >${EXPECTED}< for 3-hop cycle, got >${ACTUAL}<" + +rm -f '../../../alias/link1' '../../../alias/link2' '../../../alias/link3' + +# Cycle through an alias name containing a space +echo 'midway' > '../../../alias/foo bar' +echo 'foo bar' > '../../../alias/midway' + +ACTUAL="$(nvm_resolve_alias 'foo bar')" +EXPECTED='∞' +[ "${ACTUAL}" = "${EXPECTED}" ] || die "expected >${EXPECTED}< for space-name cycle, got >${ACTUAL}<" + +rm -f '../../../alias/foo bar' '../../../alias/midway' + +# Non-cycle through an alias name containing a space. +# Token-based cycle detection would false-positive on this chain because +# 'bar' appears as a substring of the multi-token alias name 'foo bar'. +# Resolves: 'foo bar' -> 'midway' -> 'bar' -> 0.0.99 +echo 'midway' > '../../../alias/foo bar' +echo 'bar' > '../../../alias/midway' +echo '0.0.99' > '../../../alias/bar' + +ACTUAL="$(nvm_resolve_alias 'foo bar')" +EXPECTED='v0.0.99' +[ "${ACTUAL}" = "${EXPECTED}" ] || die "expected >${EXPECTED}< for space-name chain, got >${ACTUAL}<" + +rm -f '../../../alias/foo bar' '../../../alias/midway' '../../../alias/bar'