Node can't find certain modules after synchronous install

后端 未结 3 1978
陌清茗
陌清茗 2021-01-07 11:23

I\'ve got a script that synchronously installs non-built-in modules at startup that looks like this

const cp = require(\'child_process\')

function requireOr         


        
3条回答
  •  青春惊慌失措
    2021-01-07 12:30

    It seems that the require operation after an npm install needs a certain delay. Also the problem is worse in windows, it will always fail if the module needs to be npm installed. It's like at a specific event snapshot is already known what modules can be required and what cannot. Probably that's why require.cache was mentioned in the comments. Nevertheless I suggest you to check the 2 following solutions.

    1) Use a delay

    const cp = require("child_process");
    
    const requireOrInstall = async module => {
      try {
        require.resolve(module);
      } catch (e) {
        console.log(`Could not resolve "${module}"\nInstalling`);
        cp.execSync(`npm install ${module}`);
        // Use one of the two awaits below
        // The first one waits 1000 milliseconds
        // The other waits until the next event cycle
        // Both work
        await new Promise(resolve => setTimeout(() => resolve(), 1000));
        await new Promise(resolve => setImmediate(() => resolve()));
        console.log(`"${module}" has been installed`);
      }
      console.log(`Requiring "${module}"`);
      try {
        return require(module);
      } catch (e) {
        console.log(require.cache);
        console.log(e);
      }
    }
    
    const main = async() => {
      const http = require("http");
      const path = require("path");
      const fs = require("fs");
      const ffp = await requireOrInstall("find-free-port");
      const express = await requireOrInstall("express");
      const socket = await requireOrInstall("socket.io");
    }
    
    main();
    

    await always needs a promise to work with, but it's not needed to explicitly create one as await will wrap whatever it is waiting for in a promise if it isn't handed one.

    2) Use a cluster

    const cp = require("child_process");
    
    function requireOrInstall(module) {
      try {
        require.resolve(module);
      } catch (e) {
        console.log(`Could not resolve "${module}"\nInstalling`);
        cp.execSync(`npm install ${module}`);
        console.log(`"${module}" has been installed`);
      }
      console.log(`Requiring "${module}"`);
      try {
        return require(module);
      } catch (e) {
        console.log(require.cache);
        console.log(e);
        process.exit(1007);
      }
    }
    
    const cluster = require("cluster");
    
    if (cluster.isMaster) {
      cluster.fork();
      cluster.on("exit", (worker, code, signal) => {
        if (code === 1007) {
          cluster.fork();
        }
      });
    } else if (cluster.isWorker) {
      // The real work here for the worker
    
      const http = require("http");
      const path = require("path");
      const fs = require("fs");
      const ffp = requireOrInstall("find-free-port");
      const express = requireOrInstall("express");
      const socket = requireOrInstall("socket.io");
    
      process.exit(0);
    }
    

    The idea here is to re-run the process in case of a missing module. This way we fully reproduce a manual npm install so as you guess it works! Also it seems more synchronous rather the first option, but a bit more complex.

提交回复
热议问题