ekalinin/nodeenv

Wrong npm version when spawning nodeenv using npx

Maxim-Mazurok opened this issue · 1 comments

In short, npx sets npm_config_prefix env var, which affects how npm prefix works, and because of this when we spawn nodeenv from scripts started with npx, we get the correct node version, but incorrect npm version.

Please consider the following code:

const { execSync } = require("child_process");
const { join } = require("path");

console.log(
  `npm_config_prefix in ENV: "${process.env["npm_config_prefix"]}"\n`
);

console.log(`Current npm version: ${execSync(`npm -v`)}`);

const nodeenvPath = join(__dirname, "nodeenv");

const node = "18.1.0";
const npm = "7.16.0";

console.log(`Expected npm version: ${npm}\n`);

console.log(`Installing nodeenv, please wait...\n`);

execSync(
  [
    `(if exist ${nodeenvPath} rmdir ${nodeenvPath} /s /q)`,
    `python -m nodeenv ${nodeenvPath} --node=${node} --npm=${npm} --with-npm`,
  ].join(" && ")
);

console.log(
  `\n\nNew npm version: ${execSync(
    ["nodeenv\\Scripts\\activate", "npm -v"].join(" && ")
  )}`
);

And if we run it like this:

npx -y cross-env TESTING=123 node index.js

we will get the following result:

npm_config_prefix in ENV: "C:\Users\maxim\AppData\Local\nvs\default"
Current npm version: 7.15.1

Expected npm version: 7.16.0

Installing nodeenv, please wait...

 * Install prebuilt node (18.1.0) ..... done.
symbolic link created for C:\Users\maxim\transactions\nodeenv\Scripts\nodejs.exe <<===>> node.exe
 * Install npm.js (7.16.0) ...

New npm version: 7.15.1

As you can see, despite nodenv installing everything correctly, we are still using "current" npm that we ran npx with, not the expected or desired one.

If we run the same script without npx, like so:

node index.js

We will get the expected results:

npm_config_prefix in ENV: "undefined"

Current npm version: 7.15.1

Expected npm version: 7.16.0

Installing nodeenv, please wait...

 * Install prebuilt node (18.1.0) ..... done.
symbolic link created for C:\Users\maxim\transactions\nodeenv\Scripts\nodejs.exe <<===>> node.exe
 * Install npm.js (7.16.0) ...

New npm version: 7.16.0

Not sure what can or should be done here from the nodeenv perspective. Probably nothing. I just spent quite some time debugging this and thought I'd share. I will probably solve this by clearing all these npm_config_* from env before spawning script that uses nodeenv, will report results.

Workaround is to remove all npm_* keys from env and pass this new env to exec():

+  const newEnv = { ...process.env };
+  for (const key in newEnv) {
+    if (key.startsWith("npm_")) {
+      delete newEnv[key];
+      console.log(`${key} deleted from env`);
+    }
+  }

  console.log(
    `\n\nNew npm version: ${execSync(
      ["nodeenv\\Scripts\\activate", "npm -v"].join(" && "),
+     { env: newEnv }
    )}`
  );

Updated script produces correct results even when running with npx:

npx -r cross-env node index.js

results in

  npm_config_prefix in ENV: "C:\Users\maxim\AppData\Local\nvs\default"

  Current npm version: 7.15.1

  Expected npm version: 7.16.0

  Installing nodeenv, please wait...

+  npm_command deleted from env
+  npm_config_cache deleted from env
+  npm_config_globalconfig deleted from env
+  npm_config_init_module deleted from env
+  npm_config_metrics_registry deleted from env
+  npm_config_node_gyp deleted from env
+  npm_config_noproxy deleted from env
+  npm_config_prefix deleted from env
+  npm_config_r deleted from env
+  npm_config_userconfig deleted from env
+  npm_config_user_agent deleted from env
+  npm_execpath deleted from env
+  npm_lifecycle_event deleted from env
+  npm_lifecycle_script deleted from env
+  npm_node_execpath deleted from env
+  npm_package_json deleted from env
+  npm_package_name deleted from env
+  npm_package_version deleted from env


-  New npm version: 7.15.1
+  New npm version: 7.16.0