help icon indicating copy to clipboard operation
help copied to clipboard

Installing NPM packages with child_process

Open Paultje52 opened this issue 5 years ago • 10 comments

  • Node.js Version: 12.17.0
  • OS: Windows 10
  • Scope (install, code, runtime, meta, other?): Code
  • Module (and version) (if relevant): Child_Process

I made a function to ensure that NPM packages are installed. If a package isn't installed, I use child_process to install it.

const childProcess = require("child_process");
function ensureDependencies(dependencies) {
  return new Promise((res) => {
    try {
      for (let i of dependencies) {
        require(i);
      }
      res();
    } catch(e) {
      childProcess.exec(`npm i -s ${dependencies.join(" ")}`, async () => {
        res();
      });
     }
  });
}
ensureDependencies(["discord.js", "express"]).then(() => {
  const discord = require("discord.js");
  const express = require("express");
});

When the packages are already installed, it just continues. When they aren't installed, child_process installs them, without any errors. But when I try to require them, I get a module not found error. When I stop my process and start it again, the packages are installed.

Is there any way to "refresh" the node_modules or whatever? I don't want to restart the process after child_process installed the dependencies.

I also tried deleting the cache, like this, after the child_process exec is compleat.

for (let i of dependencies) {
  delete require.cache[require.resolve(i)];
}

That also doesn't work.

Paultje52 avatar Jul 16 '20 15:07 Paultje52

It's been almost two weeks. Is this a bug or am I doing something wrong?

Paultje52 avatar Jul 27 '20 08:07 Paultje52

Can you please share the error with something like this?

 childProcess.exec(`npm i -s ${dependencies.join(" ")}`, async (error) => {
        if(error) {
          console.log('error is:' , error);
           //or
           throw error;
        }
      res();
});

I believe it would help the community to understand the source of your issue.

Another question: Have you considered using a package json file instead of joining all the dependencies in the command line? You might be hitting the limit of the command line arguments.

qballer avatar Jul 28 '20 06:07 qballer

Hello. I don't get any errors, and the packages install just fine, but node can't find it until I stop and then start the process.

And yes, I thought of putting the packages in the package.json, but it's for a package that maybe require dependencies, bases on which parts you use. That's why I want to install the packages with child_process.

Paultje52 avatar Jul 28 '20 07:07 Paultje52

Ok, I've reconstructed the issue. It seems funny because I've dealt with module reloading in the past, but the module was never loaded so I believe this brings us to some kind of edge case.

const childp = require('child_process')
const {resolve} = require('path')
const package = 'rimraf'

try {
  const found = require(package)
  console.log('found', found)
} catch (e) {
  childp.execSync(`npm i ${package}`)
  console.log('installed')
}

console.log('require.cache:', require.cache[package]) // undefined
console.log(require.resolve(package, {paths: [resolve(__dirname, 'node_modules')]})) //💥  shoule be regular require goes 💥

Edit: For those looking to help here when running the script do something along the lines of rm -rf node_modules; node [script_name]

qballer avatar Jul 28 '20 08:07 qballer

You're trying to do the same as https://github.com/nodejs/node/issues/31803, which AFAIK is not currently possible due to the internal caching.

richardlau avatar Jul 28 '20 10:07 richardlau

The strange thing is that it works sometimes, but other times it doesn't work. Is there a way to clear the internal caching or something like that?

Paultje52 avatar Jul 28 '20 10:07 Paultje52

It doesn't seem to be about the cache. npm install spawned in child process appeared not to actually install any packages into node_modules, nor updating package.json

SevenOutman avatar Jul 28 '21 08:07 SevenOutman

We encountered the same situation, the following example can reproduce the problem.

const { execSync } = require('child_process');

function resolveSilent(id) {
  try {
    return require.resolve(id);
  } catch (e) {
    return undefined;
  }
}

const pkgName = 'add';

const resolveBefore = resolveSilent(pkgName);
console.log('Resolve before: ', resolveBefore);

if (!resolveBefore) {
  const stdout = execSync(`npm i -D ${pkgName}`, { encoding: 'utf8' });
  console.log('stdout: ', stdout);
  console.log('Resolve after: ', resolveSilent(pkgName));
}

jeasonstudio avatar Sep 14 '21 01:09 jeasonstudio

I tried the same but with dynamic imports and arrived to the same problem. Has anybody found a way to deal with the cache before attempting the 2nd import/require?

fernandopasik avatar Sep 14 '21 10:09 fernandopasik

It seems there has been no activity on this issue for a while, and it is being closed in 30 days. If you believe this issue should remain open, please leave a comment. If you need further assistance or have questions, you can also search for similar issues on Stack Overflow. Make sure to look at the README file for the most updated links.

github-actions[bot] avatar May 12 '24 01:05 github-actions[bot]

It seems there has been no activity on this issue for a while, and it is being closed. If you believe this issue should remain open, please leave a comment. If you need further assistance or have questions, you can also search for similar issues on Stack Overflow. Make sure to look at the README file for the most updated links.

github-actions[bot] avatar Jun 12 '24 01:06 github-actions[bot]