Automatically switching Node.js version upon cd with n
package.json
file of every directory upon entering them, and switch Node.js versions accordingly, relying on nvm
's default version if a specific one was not required.The script I hacked together does the job in most cases, but suffers from a few limitations:
- it only looks for
package.json
in the (new) current directory, rather than the closest one in the tree - it is pretty bad at dealing with file ranges, just parsing the first correct version number it could find in them, which turns out to be their lower end
I therefore dug a bit further and found out that
n
, nvm
's main competitor, can perfectly handle those two aspects natively! Just run n auto
anywhere and it will look for the closest version constraint in the tree and interpret it correctly, reading from package.json
but also from other files - even .nvmrc
!Running it systematically has one major downside, though:
n
actually reinstalls Node.js upon every version switch, and even if it uses its local cache for versions it has already downloaded, it turns out to be pretty slow; and unfortunately, there does not seem to be an option to cancel the switch if the current version already matches the expected one. Well, in that case, why not write something of our own to do exactly that?In order to properly handle ranges, we need a list of all available Node.js versions; fortunately, this is pretty easy to get by running
n ls-remote --all
, the result of which we will store in a file that then gets passed as a parameter to our script.const fs = require("fs");
const { exec } = require("child_process");
const semver = require("semver");
const readPkgUp = require("read-pkg-up");
// Build array of available versions from input file
const versions = fs.readFileSync(process.argv.pop(), { encoding: "utf8" }).split("\n");
// Read closest package.json file
readPkgUp().then(file => {
try {
// Determine highest version satisfying its version constraint
const target = semver.maxSatisfying(versions, file.packageJson.engines.node);
// Check current node version and switch if necessary
exec("node -v", (error, stdout) => {
if (semver.clean(stdout) !== target) {
console.log(`Switching to node v${target}`);
exec(`n -p ${target}`);
}
});
} catch (e) {} // fail silently
});
Reusing our zsh bit from last time:
autoload -U add-zsh-hook
n ls-remote --all > path/to/.node-versions # update version list
switch-node-version() {
node path/to/runn path/to/.node-versions
}
add-zsh-hook chpwd switch-node-version
switch-node-version
And there you have it! The script is available here if you are interested.