Hermetically sealed versions of node and pnpm.
Instead of relying on the version of node
, npm
, pnpm
, etc. each individual developer or
machine has installed locally, packages use a system we've named HNVM, which stands for Hermetic
Node Version Manager. Having our node
binaries be hermetic means each app or package defines what
version of node
they depend on. That version is installed and used to run whenever the package
runs scripts in node
, npm
, pnpm
, and yarn
. This ensures that everyone's systems output
consistent packages, and upgrades required by one package don't affect another.
Homebrew (Compass Employees Only)
HNVM is distributed via Homebrew inside the
UrbanCompass/versions
tap:
brew tap UrbanCompass/versions
# If you get a permissions error tap'ing, try using the repo's ssh url
brew tap UrbanCompass/versions git@github.com:UrbanCompass/homebrew-versions.git
brew install hnvm
You can also use basher to install hnvm:
basher install UrbanCompass/hnvm
This will install both hnvm
and jq
.
If you can't or don't want to install using homebrew
or basher
, you can use the provided
install.sh
and uninstall.sh
scripts to add or remove the local bin path to your global $PATH:
source install.sh
node -v # Uses hnvm node script to get the node version
source uninstall.sh
node -v # No longer uses hnvm node script
Note that hnvm
depends on jq
being available in your global
$PATH
, and if you don't use brew
you'll have to make sure that jq
is installed on your own.
HNVM reads the version of node
/pnpm
/yarn
set in your package.json
file' "engines"
field. If
no version is set, it will default to the current versions set in HNVM's own package.json
. Unlike
HNVM 1.0, you don't have to find any particular bash script to run HNVM. Just use the regular
node
, npm
, etc. commands you're used to from anywhere on your computer. If you run it
next to a package.json
file, it will read the engines field. If not, it'll default to the global
version.
// main.js
console.log('Node version', process.version);
# package.json in this directory has the hnvm version set to 12.10.0
node main.js # Echo's: "Node version v12.10.0
HNVM reads its configuration from one of the following places, in order from highest to lowest priority:
- The
package.json
file in the current process working directory ($PWD/package.json
) - An
.hnvmrc
file in thePWD
($PWD/.hnvmrc
) - An
.hnvmrc
file at the root of your git repo (if running in a git repo) - An
.hnvmrc
file in your home directory (~/.hnvmrc
) - The
.hnvmrc
file that ships with the current version of HNVM
The .hnvmrc
files are all simple key/value pairs prepended with HNVM_
:
HNVM_PATH=/path/to/.hnvm
HNVM_NODE=10.0.0
HNVM_PNPM=3.0.0
HNVM_YARN=1.19.0
The full list of config options are detailed below.
Versions configured in package.json
files go in either the "engines"
field or an
"hnvm"
field, with the latter overriding the former:
{
"name": "some-package",
"version": "1.0.0",
"engines": {
"node": "10.0.0",
"pnpm": "3.0.0",
"yarn": "1.19.0"
},
"hnvm": {
"node": "11.0.0" // This overrules any versions set in "engines"
}
}
The "hnvm"
field can contain the same values as in the "engines"
field. It's useful for when
you want HNVM to be stricter than your engines. For example, your package might be compatible with a
node version range, but HNVM itself runs at a specific version:
{
"name": "some-package",
"version": "1.0.0",
},
"engines": {
"node": ">=10.0.0"
},
"hnvm": {
"node": "11.0.0"
}
}
Location on disk to download binaries to, defaulting to an .hnvm
directory in your $HOME
directory.
Version of node
/pnpm
/yarn
to use. If semver ranges are provided instead of exact versions
(e.g. the defaults are set to latest
), HNVM will perform curl requests to resolve those to an
exact version. However you'll get a warning about this since it could slow down execution time from
the async request, or it might even fail to work at all if the curl requests fail to load.
It's best to create an .hnvmrc
file in your home directory and set the versions to exact versions.
If you want to temporarily override any of these values, you can override the env vars when your run the script:
env HNVM_NODE='12.0.0' node --version # v12.0.0
To try to mitigate the slowdown above, semver range results are cached locally. The default is 60s
but you can control this by setting the HNVM_RANGE_CACHE
environment variable. To disable the
cache set the value to 0.
HNVM outputs information about the current version of node running, and any download statuses if a
new version is being downloaded so that you know exactly what's going on. If you don't want this
output, set the HNVM_QUIET
environment variable to true
to have this output redirected to
/dev/null
.
Location of the NodeJS distribution files. If you provide a custom destination, you should ensure that the repository layout mirrors that of nodejs.org:
/node-v${node_ver}-${platform}-${cpu_arch}.tar.gz
If using the --with-pnpm
flag, npm
-compatible registry to install pnpm
from. Defaults to the
public npm
registry.
If using the --with-yarn
flag, the location from where to download yarn
from. When providing a
custom destination, you should ensure the layout mirrors that of yarnpkg.com
:
/${yarn_ver}/yarn-v${yarn_ver}.tar.gz
When set to true, HNVM will stop going up the tree to look for .hnvmrc
files. This is useful in
monorepo's if you want to enforce that your specific package's HNVM version is run and it doesn't
end up falling back to a version outside of the repo.
# In package's .hnvmrc
HNVM_NODE=11.0.0
# In git root's .hnvmrc
HNVM_NOFALLBACK=true
# In HNVM default .hnvmrc
HNVM_NODE=12.0.0
If I run node --version
next to the package, I'll get v11.0.0
. If I run it outside the package
but still in the repo, I'll get an error asking to specify a node version instead of defaulting to
node v12.0.0.
Why Not Just Use NVM
Because nvm
doesn't support fish
😅. This project also focuses on individual
projects or packages declaring versions of node to use, as opposed to switching globally between
different versions. It results in a simpler setup with far less code.
NOTE: The tests hit live npm registry servers and go through the process of downloading multiple copies of node. Do not run in bandwidth-constrained environments!
Tests for HNVM are written with jest and require node 14.4.0 or higher (protip: use hnvm to test hnvm 😉). To run the tests, cd test/ && npm install && npm test