同步安装后,节点找不到某些模块 [英] Node can't find certain modules after synchronous install

查看:115
本文介绍了同步安装后,节点找不到某些模块的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个脚本可以在启动时同步安装非内置模块,看起来像这样

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

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)
  }
}

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')
// List goes on...

当我卸载模块时,当我再次启动服务器时,它们会成功安装,这就是我想要的。但是,当我卸载使用函数 requireOrInstall 无法找到模块错误$ C>。没错,错误只发生在脚本必须安装第一个或前两个模块时,而不是只安装第二个模块时。

When I uninstall modules, they get installed successfully when I start the server again, which is what I want. However, the script starts throwing Cannot find module errors when I uninstall the first or first two modules of the list that use the function requireOrInstall. That's right, the errors only occur when the script has to install either the first or the first two modules, not when only the second module needs installing.

在这个例子中,当我卸载find-free-port时会抛出错误,除非我将 require 移动至少一个点¯\_(• _•)_ /¯

In this example, the error will be thrown when I uninstall find-free-port, unless I move its require at least one spot down ¯\_(• _ •)_/¯

我也尝试在同步安装后直接添加一个延迟,以便通过以下两行给它一点喘息的时间:

I've also tried adding a delay directly after the synchronous install to give it a little more breathing time with the following two lines:

var until = new Date().getTime() + 1000
while (new Date().getTime() < until) {}

暂停在那里。它没有修复任何问题。

The pause was there. It didn't fix anything.

@ velocityzen提出了检查缓存的想法,我现在已将其添加到脚本中。它没有显示任何异常。

@velocityzen came with the idea to check the cache, which I've now added to the script. It doesn't show anything out of the ordinary.

@ vaughan对另一个问题的评论指出,当需要两次模块时会发生这个确切的错误。我已将脚本更改为使用 require.resolve(),但错误仍然存​​在。

@vaughan's comment on another question noted that this exact error occurs when requiring a module twice. I've changed the script to use require.resolve(), but the error still remains.

是否有人知道可能导致这种情况的原因吗?

Does anybody know what could be causing this?

编辑

由于问题已经存在回答,我发布了一行(139个字符!)。它没有全局定义 child_modules ,没有最后的 try-catch ,并且没有在控制台中记录任何内容:

Since the question has been answered, I'm posting the one-liner (139 characters!). It doesn't globally define child_modules, has no last try-catch and doesn't log anything in the console:

const req=async m=>{let r=require;try{r.resolve(m)}catch(e){r('child_process').execSync('npm i '+m);await setImmediate(()=>{})}return r(m)}

该函数的名称是 req(),可以像 @ alex-rokabilis'回答

The name of the function is req() and can be used like in @alex-rokabilis' answer.

推荐答案

npm install 之后似乎需要操作c $ c>需要一定的延迟。
此外,Windows中的问题更严重,如果模块需要安装 npm ,它将始终失败
就像在特定事件快照已经知道可以需要什么模块和什么不可以。可能这就是评论中提到 requires.cache 的原因。不过我建议您查看以下2个解决方案。

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.

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 总是需要承诺才能使用,但是不需要明确地创建一个 await 将包装在承诺中等待的任何东西,如果没有递给它。

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.

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);
}

这里的想法是在缺少模块的情况下重新运行该过程。这样我们就可以完全重现手册 npm install ,以便您猜测它有效!它似乎更多同步而不是第一个选项,但有点复杂。

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.

这篇关于同步安装后,节点找不到某些模块的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆