Awesome
chnode
Changes shell's current Node.js version by updating $PATH
.
chnode
is a lightweight Node.js version switcher that selects a
Node.js version for a shell session. The mechanism to do this is by
updating the PATH
environment variable. Confining version switching to
a shell session allows you to run different Node.js versions in many
shell sessions simultaneously.
The lightweight design and small feature set makes chnode
very fast to
load, having minimal negative effect on your shell's init script.
chnode
expects that Node.js versions are already installed on your
system. It cannot download them for you. Instead, download Node.js
binaries manually or use another tool, such as node-build, for the
job.
To read more about the design rationale and a comparison to nvm and nodenv, look here.
chnode
is inspired by chruby, which is awesome.
Features
- Selects Node.js version for a shell session by updating the
PATH
environment variable. Version switching is independent per shell session. - Optional automatic Node.js version switching based on the contents of
the
.node-version
file in your project directory, or from another file, specified in theCHNODE_AUTO_VERSION_FILENAME
shell variable. - Small feature set by design, making the tool very fast to load.
- Each Node.js version has its own set of global npm packages.
- Allows accessing man pages for the selected Node.js version and its global npm packages.
- After switching Node.js version, calls
hash -r
to clear the hash table for program locations. - Best candidate ("fuzzy") matching of Node.js versions at switching.
- The path to the selected Node.js version is available in the
CHNODE_ROOT
environment variable. This makes it easy to display the selected version in your shell prompt. - Locates installed Node.js versions in the
~/.nodes
directory, or from a custom directory read fromCHNODE_NODES_DIR
shell variable. - Add additional Node.js versions by appending Node.js installation
paths to the
CHNODE_NODES
array shell variable. - Works with Bash's
set -euo pipefail
shell options ("strict mode"). - Good test coverage.
Requirements
GNU Bash version >= 3.2 or Zsh version >= 5.3
Install
Manual
The manual method just downloads the latest revision of chnode.sh script:
curl -L 'https://raw.githubusercontent.com/tkareine/chnode/master/chnode.sh' > chnode.sh
It's up to you to download the script again to update.
Homebrew
Homebrew tap is available for macOS users:
brew tap tkareine/chnode
brew install tkareine/chnode/chnode
This is the easy way to install just the script and related documentation, and to keep the script up-to-date.
Git clone
Alternatively, clone the git repository:
git clone --depth 1 git@github.com:tkareine/chnode.git
The downside of cloning is that you'll download all the non-essential project files.
Post-installation
Execute the source
command to load chnode functions:
source chnode.sh
You may append the command above into your shell's init script
(~/.bashrc
for Bash, ~/.zshrc
for Zsh) to load chnode for your
interactive shell usage.
Automatic Node.js version switching requires additional setup, read below for more.
Configure the prefix path for npm
Consider configuring the prefix path for
npm
in the per-user npmrc file. It defines the
installation location for global npm packages, ensuring sharing the
package installations for the Node.js versions you've installed.
An example ~/.npmrc
:
prefix=/usr/local
Sourcing .bashrc
on macOS
(Applies to the Bash shell only.)
macOS does not execute ~/.bashrc
automatically when opening a
terminal. You might want to add the following line to ~/.bash_profile
to fix it:
[[ -r ~/.bashrc ]] && source ~/.bashrc
Available Node.js versions
When shell loads chnode with the source
command, the script
auto-detects Node.js versions installed in the ~/.nodes
directory.
You may override the ~/.nodes
directory by setting the
CHNODE_NODES_DIR
shell variable to point to another directory. Do this
before executing the source
command. For example:
CHNODE_NODES_DIR=/opt/nodes
source chnode.sh
The value of the CHNODE_NODES_DIR
shell variable should point to a
directory where you have Node.js installations. For example, if
CHNODE_NODES_DIR=~/.nodes
(the default):
ls -l ~/.nodes
Output (truncated):
… node-16 -> /usr/local/opt/node@16
… node-18.10.0
The first directory entry, node-16
, is a symbolic link that ultimately
points to the actual Node.js installation path. The second directory
entry, node-18.10.0
, is a regular directory containing another Node.js
installation.
Sourcing chnode.sh
populates the CHNODE_NODES
shell array variable
with paths to all the entries in the CHNODE_NODES_DIR
directory. These
paths are the Node.js versions you can select with the chnode NODE_VERSION
command.
After installing new Node.js versions or removing them, affecting the
contents of the CHNODE_NODES_DIR
directory, run chnode --reload
to
populate CHNODE_NODES
again.
For Node.js versions installed elsewhere, add their paths to
CHNODE_NODES
after running the source chnode.sh
or chnode --reload
commands. For example:
source chnode.sh
CHNODE_NODES+=(/opt/node-10.11.0 /usr/local/opt/node@16)
When selecting a Node.js version with the chnode NODE_VERSION
command,
chnode attempts to match the NODE_VERSION
user input to a path in the
CHNODE_NODES
shell array variable. Matching is done against the
basename of the path (the last path component). Upon finding a match,
chnode checks that the path is a valid Node.js installation: the path
must contain an executable at the bin/node
relative path. Continuing
the example above, when selecting Node.js v18.10.0 with the chnode 18
command, chnode checks that ~/.nodes/node-18.10.0/bin/node
is an
executable file. If the check fails, chnode prints an error message and
returns 1 as the exit code.
Installing Node.js versions
Use any tool you like to install Node.js binaries.
One good option is node-build. Installing to ~/.nodes
:
node-build 10.11.0 ~/.nodes/node-10.11.0
Alternatively, download binaries from the Node.js download
page and extract them to ~/.nodes
:
mkdir -p ~/.nodes/node-10.12.0 \
&& tar xzvf ~/Downloads/node-v10.12.0-darwin-x64.tar.gz --strip-components 1 -C ~/.nodes/node-10.12.0
You can also use Homebrew to install a Node.js version:
brew install node@16
ln -s /usr/local/opt/node@16 ~/.nodes/node-16
The previous approach relies on Homebrew providing you the symbolic link
at /usr/local/opt/node@16
, which points to the actual installation
path. Homebrew will update that link whenever you upgrade the node@16
formula with Homebrew.
Default Node.js version (without auto switching)
Choose the default Node.js version in your shell's init script, here a 10.x series:
source chnode.sh
chnode node-10
Usage
List available Node.js versions:
$ chnode
node-10.11.0
node-8.11.4
Select a Node.js version, here using fuzzy matching to switch to 10.x series:
$ chnode node-10
$ chnode
* node-10.11.0
node-8.11.4
$ echo "$PATH"
/Users/tkareine/.nodes/node-10.11.0/bin:/usr/local/bin:/usr/bin:…
$ echo "$CHNODE_ROOT"
/Users/tkareine/.nodes/node-10.11.0
chnode
stores the path of the selected version in the CHNODE_ROOT
environment variable.
If no version matches, chnode
prints error and preserves the previous
selection. Continuing the example above:
$ chnode nosuch
chnode: unknown Node.js: nosuch
$ echo "$CHNODE_ROOT"
/Users/tkareine/.nodes/node-10.11.0
While in the shell, install another Node.js version and reload
chnode (chnode --reload
):
$ node-build 8.9.4 ~/.nodes/node-8.9.4
$ chnode
* node-10.11.0
node-8.11.4
$ chnode --reload # or -R
$ chnode
* node-10.11.0
node-8.11.4
node-8.9.4
Reset the version (chnode --reset
), clearing the path that was set
in the PATH
environment variable:
$ chnode --reset # or -r
$ chnode
node-10.11.0
node-8.11.4
node-8.9.4
$ echo "$PATH"
/usr/local/bin:/usr/bin:…
Use --reset
to use your system's Node.js installation (usually
/usr/local/bin/node
or /usr/bin/node
).
Show usage:
$ chnode --help # or -h
Show version:
$ chnode --version # or -V
Automatic version switching
Automatic Node.js version switching is included in the auto.sh script
as an optional add-on on top of chnode.sh
. The feature detects a
.node-version
file in the current working directory (or in a parent
directory, up to the system root directory), and switches the current
Node.js version to the version specified in the file. You'll need to
have the specified version installed for switching to happen, otherwise
you'll get an error.
To use the feature, edit your shell's init script:
-
Source
chnode.sh
andauto.sh
(in this order). -
Optionally set the
CHNODE_AUTO_VERSION_FILENAME
shell variable to name the file used in detecting automatic version switching. The default value of the variable is.node-version
. If this is ok, you don't need to set the variable explicitly. For example, to use the.nvmrc
files of nvm, setCHNODE_AUTO_VERSION_FILENAME=.nvmrc
. -
Configure the
chnode_auto
function to be called in PROMPT_COMMAND (for Bash) or in the precmd_functions hook (for Zsh).
For example:
source chnode.sh
source auto.sh
# Uncomment to set the filename for the version file for something else
# than `.node-version`.
#CHNODE_AUTO_VERSION_FILENAME=.nvmrc
PROMPT_COMMAND=chnode_auto # if using Bash
precmd_functions+=(chnode_auto) # if using Zsh
Note that you might already have commands to be evaluated in
PROMPT_COMMAND
in Bash. In that case, you can choose to:
-
Wrap all the commands in a single function, calling
chnode_auto
from inside the function, and set the value ofPROMPT_COMMAND
to be the name of the function:my_prompt_function() { chnode_auto # do something else, like set PS1 } PROMPT_COMMAND=my_prompt_function
-
Include
chnode_auto
to be called inPROMPT_COMMAND
, separating other commands with a semicolon:PROMPT_COMMAND="chnode_auto; $PROMPT_COMMAND"
-
Use Bash-Preexec or a similar tool to simulate
precmd_functions
of Zsh in Bash. For example, with Bash-Preexec:source bash-preexec.sh precmd_functions+=(chnode_auto)
We don't recommend to call chnode_auto
via shell's DEBUG trap, because
it makes the shell to call the function too often. For example, Bash
executes the DEBUG trap for each command in a command group. In
addition, the trap might already be utilized by other shell extensions.
To demonstrate the problem with command groups:
# WARNING: Don't install chnode_auto like this, because the function gets called too often
trap '[[ $BASH_COMMAND != "${PROMPT_COMMAND:-}" ]] && echo CALLED && chnode_auto' DEBUG
# Execute a command group
{ echo lol; echo bal; }
CALLED
lol
CALLED
bal
To set a default node version for a project, create a .node-version
file in the root directory of the project:
echo node-8.1.0 > node-project/.node-version
The first line of the .node-version
file should contain a version
string that the chnode_auto
function uses to select a Node.js
version. The function invokes chnode $version
, where $version
is the
first line from the file. This means that fuzzy matching is
supported. If no version matches, an error is reported.
You can set the default node version by adding a .node-version
file to
the root of your home directory. The version you specify in the file
will be used unless any of your Node.js projects, located somewhere
under your home directory, has their own .node-version
file.
Supported .node-version
file format
Specifications for the chnode_auto
function parsing the version string
from the .node-version
file are:
-
The file must be a regular file or a symbolic link, and the current user must have read access to it.
-
The version string must be in the first line. The line may have leading and trailing whitespace, which get trimmed out. Trailing newline character is not required. Both Unix (
\n
) and Windows (\r\n
) style line endings are supported. -
If the version string starts with the
v
character followed by a digit, then thev
character gets trimmed out. -
The lines following the first are ignored.
-
If the first line cannot be parsed (no version string is found), then the file is ignored. No error is reported.
Display current Node.js in shell prompt
You can pick up the selected Node.js version from the CHNODE_ROOT
environment variable. An example script to customize shell prompt is in
set-prompt.sh. Usage:
$ source chnode.sh
$ source contrib/set-prompt.sh
tkareine@sky ~/Projects/chnode (node:10.11.0)
$
License
MIT. See LICENSE.txt.
shUnit2, located as a git submodule in the test/shunit2
directory:
Copyright 2008-2021 Kate Ward. Released under the Apache 2.0 license.