Operations 10 min read

Automatic Node.js Version Switching Plugin for VS Code

This article explains the limitations of global Node version managers like n and nvm, presents a demo Node script for per‑project version switching, and details a VS Code extension that automatically detects a project's required Node version and switches it in the integrated terminal.

Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Rare Earth Juejin Tech Community
Automatic Node.js Version Switching Plugin for VS Code

Background

I was learning Next.js, which requires Node ≥ v18.17.0, while an older company project is limited to Node ≤ v16.20.0, so I used v16.20.0 at work and v20.0.0 after hours. Forgetting to switch versions with n caused frequent errors, prompting me to create a plugin that automatically switches Node versions per project.

n and nvm implementation principles

n implementation

n stores all Node versions under /usr/local/n/versions , each in a subdirectory named after the version.

Switching copies the selected version's /usr/local/n/versions/{version}/bin/node binary to /usr/local/bin , making the change global.

Therefore, n switches versions globally and cannot be configured per directory.

nvm implementation

When installing a specific Node version, nvm downloads the pre‑compiled binary and extracts it to ~/.nvm/versions/node/{version} , keeping each version isolated.

Running nvm use <version> prepends the selected version's bin directory to the PATH environment variable, so subsequent node or npm commands use that version.

To load nvm in new shell sessions, users add a source ~/.nvm/nvm.sh line to their ~/.bashrc or ~/.zshrc files.

Thus, nvm allows independent Node versions per shell session.

Node script

Implementation idea

Read the nodeVersion field from the project's package.json ; if present, invoke n to switch to that version before starting the project.

Demo code

const { spawn } = require('child_process');
const { readFileSync } = require('fs');
const path = require('path');
const { cwd } = require('process');

let packageJson = readFileSync(path.join(cwd(), '/package.json')).toString();
if (packageJson) {
  packageJson = JSON.parse(packageJson);
}
if (packageJson.nodeVersion) {
  // Execute n command
  spawn('n', [packageJson.nodeVersion]);
}

The demo reads nodeVersion from package.json and runs n to switch versions.

VS Code plugin

Implementation idea

Because projects are usually started from VS Code's integrated terminal, the plugin detects the opened terminal, reads the project's nodeVersion , and sends the appropriate nvm use command to the terminal without affecting other projects.

Implementation steps

Create VS Code extension project

npm install -g yo
yo code

Core code (extension.js)

// extension.js
const { existsSync, readFileSync } = require('fs');
const { join } = require('path');
const vscode = require('vscode');
const { exec } = require('child_process');

function commandExistsInShell(shell, command, callback) {
  let execCommand = `type ${command}`;
  if (shell.includes('zsh')) {
    execCommand = `source ~/.zshrc && type ${command}`;
  }
  exec(execCommand, { shell }, (error, stdout, stderr) => {
    if (error || stderr) return callback(false);
    callback(true);
  });
}

function activate() {
  vscode.window.onDidOpenTerminal((terminal) => {
    if (!vscode.workspace.workspaceFolders) return;
    const workspacePath = vscode.workspace.workspaceFolders[0].uri.fsPath;
    const pkgPath = join(workspacePath, '/package.json');
    if (!existsSync(pkgPath)) return;
    const pkgStr = readFileSync(pkgPath, 'utf8').toString();
    if (!pkgStr) return;
    try {
      const pkg = JSON.parse(pkgStr);
      if (!pkg.nodeVersion) return;
      const { shellPath } = terminal.creationOptions;
      commandExistsInShell(shellPath, 'nvm', (exists) => {
        if (exists) {
          let command = `source ~/.nvm/nvm.sh && nvm ls ${pkg.nodeVersion}`;
          if (shellPath.includes('fish')) command = `nvm ls ${pkg.nodeVersion}`;
          exec(command, { shell: shellPath }, (error, stdout, stderr) => {
            if (error || stderr) {
              vscode.window.showErrorMessage(`${pkg.nodeVersion} version not found, install?`, 'Yes', 'No')
                .then((value) => {
                  if (value === 'Yes') terminal.sendText(`nvm install ${pkg.nodeVersion}`, true);
                });
              return;
            }
            if (stdout) {
              terminal.sendText('nvm use ' + pkg.nodeVersion, true);
            }
          });
        } else {
          vscode.window.showErrorMessage('nvm command not found, install it first.', 'View guide', 'Cancel')
            .then((value) => {
              if (value === 'View guide') vscode.env.openExternal(vscode.Uri.parse('https://github.com/nvm-sh/nvm'));
            });
        }
      });
    } catch (e) {}
  });
}

function deactivate() {}
module.exports = { activate, deactivate };

Demo effect

Conclusion

The extension has been published to the VS Code Marketplace (search for auto-switch-node-version or visit the provided link). Users must install nvm first; installation instructions are linked. Source code is available at GitHub .

AutomationextensionVSCodenodejsNVMversion management
Rare Earth Juejin Tech Community
Written by

Rare Earth Juejin Tech Community

Juejin, a tech community that helps developers grow.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.