Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 22 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,20 @@ Node.js version management: no subshells, no profile setup, no convoluted API, j
![usage animation](https://nimit.io/images/n/n.gif)

- [`n` – Interactively Manage Your Node.js Versions](#n--interactively-manage-your-nodejs-versions)
- [Supported Platforms](#supported-platforms)
- [Installation](#installation)
- [Third Party Installers](#third-party-installers)
- [Replacing a previous node install](#replacing-a-previous-node-install)
- [Installing Node.js Versions](#installing-nodejs-versions)
- [Specifying Node.js Versions](#specifying-nodejs-versions)
- [Removing Versions](#removing-versions)
- [Using Downloaded Node.js Versions Without Reinstalling](#using-downloaded-nodejs-versions-without-reinstalling)
- [Preserving npm](#preserving-npm)
- [Miscellaneous](#miscellaneous)
- [Custom Mirror](#custom-mirror)
- [Custom Architecture](#custom-architecture)
- [Optional Environment Variables](#optional-environment-variables)
- [How It Works](#how-it-works)
- [Supported Platforms](#supported-platforms)
- [Installation](#installation)
- [Third Party Installers](#third-party-installers)
- [Replacing a previous node install](#replacing-a-previous-node-install)
- [Installing Node.js Versions](#installing-nodejs-versions)
- [Specifying Node.js Versions](#specifying-nodejs-versions)
- [Removing Versions](#removing-versions)
- [Using Downloaded Node.js Versions Without Reinstalling](#using-downloaded-nodejs-versions-without-reinstalling)
- [Preserving npm](#preserving-npm)
- [Miscellaneous](#miscellaneous)
- [Custom Mirror](#custom-mirror)
- [Custom Architecture](#custom-architecture)
- [Optional Environment Variables](#optional-environment-variables)
- [How It Works](#how-it-works)

## Supported Platforms

Expand Down Expand Up @@ -132,11 +132,12 @@ Numeric version numbers can be complete or incomplete, with an optional leading
- `8`: 8.x.y versions
- `v6.1`: 6.1.x versions

There are labels for two especially useful versions:
There are labels for three especially useful versions:

- `lts`: newest Long Term Support release
- `latest`: newest release
- `current`: newest release ignoring alpha versions

- `lts`, `lts_latest`: newest Long Term Support official release
- `latest`, `current`: newest official release

There is an `auto` label to read the target version from a file in the current directory, or any parent directory. `n` looks for in order:

- `.n-node-version`: version on single line. Custom to `n`.
Expand Down Expand Up @@ -286,10 +287,10 @@ On a Mac with Apple silicon:

You can override the default architecture by using the `-a` or `--arch` option, or set `N_ARCH` environment variable.

e.g. reinstall latest version of Node.js with x64 binaries:
e.g. reinstall Node.js with x64 binaries:

n rm current
n --arch x64 current
n rm lts
n --arch x64 lts

## Optional Environment Variables

Expand Down
67 changes: 56 additions & 11 deletions bin/n
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,27 @@ function is_exact_numeric_version() {
[[ "$1" =~ ^[v]{0,1}[0-9]+\.[0-9]+\.[0-9]+$ ]]
}

#
# Synopsis: is_alpha_version version
# Matching n.n.n-alpha
#

function is_alpha_version() {
# e.g. 26.0.0-alpha or 26.0.0-alpha.0
[[ "$1" =~ ^[v]{0,1}[0-9]+\.[0-9]+\.[0-9]+-alpha(\.|$) ]]
}

#
# Synopsis: is_exact_alpha_version version
# Matching n.n.n-alpha.n.n.n
#

function is_exact_alpha_version() {
# e.g. 26.0.0-alpha.0.0.0
[[ "$1" =~ ^[v]{0,1}[0-9]+\.[0-9]+\.[0-9]+-alpha\.[0-9]+\.[0-9]+\.[0-9]+$ ]]
}


#
# Synopsis: is_node_support_version version
# Reference: https://github.com/nodejs/package-maintenance/issues/236#issue-474783582
Expand All @@ -319,10 +340,7 @@ function is_node_support_version() {
# Synopsis: display_latest_node_support_alias version
# Map aliases onto existing n aliases, current and lts.
#
# Since we resolve to a single version these are of limited use. Now only
# mention lts_latest in README and help, since that one is used a bit in the wild.
# Caveat: lts_active may be incorrect across node releases when
# there isn't actually an active version for a brief period.
# Deprecated since little used and Node.js changing their release policy in 2026.
#

function display_latest_node_support_alias() {
Expand Down Expand Up @@ -427,8 +445,9 @@ Versions:
and other downloadable releases by <remote-folder>/<version>

4.9.1, 8, v6.1 Numeric versions
lts, lts_latest Newest Long Term Support official release
latest, current Newest official release
lts Newest Long Term Support release
latest Newest release
current Newest release ignoring alpha versions
auto Read version from file: .n-node-version, .node-version, .nvmrc, or package.json
engine Read version from package.json
boron, carbon Codenames for release streams
Expand Down Expand Up @@ -500,11 +519,24 @@ function set_active_node() {
#

display_versions_paths() {
# Example listings:
# node/26.0.0
# node/27.0.0-alpha.1
# nightly/26.0.0-nightly20260411726b22048a
# Split sort keys on '.' and do numeric sorting on major, minor, patch, and prerelease number.
# Tricks with sed substitutions to add key separators:
# k1: "node/" => "node." => "node/"
# k5: "-alpha" => ".~alpha" => "-alpha"
# k6: "26.0.0" => "26.0.0.~ZZZ" => "26.0.0" so release versions sort after semver prerelease
find "$CACHE_DIR" -maxdepth 2 -type d \
| sed 's|'"$CACHE_DIR"'/||g' \
| n_grep -E "/[0-9]+\.[0-9]+\.[0-9]+" \
| sed 's|/|.|' \
| sort -k 1,1 -k 2,2n -k 3,3n -k 4,4n -t . \
| sed 's/-/.~/' \
| sed '/\.~/! s/$/.~ZZZ/' \
| sort -k 1,1 -k 2,2n -k 3,3n -k 4,4n -k 5,5 -k 6,6n -t . \
| sed 's|\.~ZZZ$||' \
| sed 's|\.~|-|' \
| sed 's|\.|/|'
}

Expand Down Expand Up @@ -1244,8 +1276,8 @@ function get_latest_resolved_version() {
fi

simple_version=${version#node/} # Only place supporting node/ [sic]
if is_exact_numeric_version "${simple_version}"; then
# Just numbers, already resolved, no need to lookup first.
if is_exact_numeric_version "${simple_version}" || is_exact_alpha_version "${version}"; then
# Already fully resolved, no need to lookup first.
simple_version="${simple_version#v}"
g_target_node="${simple_version}"
elif [[ "$OFFLINE" == "true" ]]; then
Expand Down Expand Up @@ -1359,9 +1391,14 @@ function display_remote_versions() {
match_count=1
# Codename is last field, first one with a name is newest lts
match="${TAB_CHAR}[a-zA-Z]+\$"
elif [[ "${version}" = "latest" || "${version}" = "current" ]]; then
elif [[ "${version}" = "latest" ]]; then
match_count=1
match='.'
elif [[ "${version}" = "current" ]]; then
match_count=1
# Use simple numeric match to match "v26.0.0" and not alphas like "v27.0.0-alpha.0.0.0"
# Expecting one or the other.
match='^v[0-9]+\.[0-9]+\.[0-9]+\t'
elif is_numeric_version "${version}"; then
version="v${version#v}"
# Avoid restriction message if exact version
Expand All @@ -1370,6 +1407,14 @@ function display_remote_versions() {
match="${version//\./\.}"
# Avoid 1.2 matching 1.23
match="^${match}[^0-9]"
elif is_alpha_version "${version}"; then
version="v${version#v}"
# Avoid restriction message if exact version
is_exact_alpha_version "${version}" && match_count=1
# Quote any dots in version so they are literal for expression
match="${version//\./\.}"
# Avoid -alpha.1 matching -alpha.12
match="^${match}[^0-9]"
elif is_lts_codename "${version}"; then
# Capitalise (could alternatively make grep case insensitive)
codename="$(echo "${version:0:1}" | tr '[:lower:]' '[:upper:]')${version:1}"
Expand All @@ -1379,7 +1424,7 @@ function display_remote_versions() {
match='.'
elif is_download_version "${version}"; then
version="${version#"${g_mirror_folder_name}"/}"
if [[ "${version}" = "latest" || "${version}" = "current" ]]; then
if [[ "${version}" = "latest" ]]; then
match_count=1
match='.'
else
Expand Down