繁体   English   中英

同步安装后,节点找不到某些模块

[英]Node can't find certain modules after synchronous install

我有一个脚本,可以在启动时同步安装非内置模块,如下所示

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的列表的前两个模块时,脚本开始抛出Cannot find module错误”。 没错,仅在脚本必须安装第一个或前两个模块时才会发生错误,而不是仅在第二个模块需要安装时才发生错误。

在此示例中, 除非卸载require至少一个点的\\ _(•_•)_ /¯, 否则我将在卸载find-free-port时抛出该错误。

我还尝试过在同步安装后直接添加一个延迟,以通过以下两行使它有更多的呼吸时间:

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

暂停在那里。 它没有解决任何问题。

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

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

有人知道是什么原因造成的吗?

编辑

既然问题已经回答,我将发布一线(139个字符!)。 它没有全局定义child_modules ,没有最后一次try-catch ,也没有在控制台中记录任何内容:

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的answer中那样使用

我认为您最好的选择是:

  • (丑陋)在全球安装软件包,而不是在本地安装
  • (最佳解决方案?)在安装时以及需要时定义新的“软件包存储库安装”

首先,您可以考虑使用npm-programmatic软件包。

然后,您可以使用以下方式定义存储库路径:

const PATH='/tmp/myNodeModuleRepository';

然后,将您的安装说明替换为以下内容:

const npm = require('npm-programmatic');
npm.install(`${module}`, {
        cwd: PATH,
        save:true
}

最终,将您的故障回复要求说明替换为:

return require(module, { paths: [ PATH ] });

如果仍然无法正常工作,则可以更新require.cache变量,例如使模块失效,您可以执行以下操作:

delete require.cache[process.cwd() + 'node_modules/bluebird/js/release/bluebird.js'];

加载之前,您可能需要手动更新它,以添加有关新模块的信息。

似乎在npm installrequire执行操作。 同样,在Windows中问题更严重,如果模块需要npm installed ,它将始终失败 就像在特定的事件快照中,已经知道需要什么模块,不需要什么模块。 也许这就是为什么require.cache中提到了require.cache的原因。 不过,我建议您检查以下2个解决方案。

1)使用延迟

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将没有等待的承诺包装在等待中。

2)使用集群

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以便您猜对了! 同样,它似乎同步,而不是第一种选择,但更为复杂。

cp.execSync是异步调用,因此请尝试检查模块是否已安装在其回调函数中。 我已经尝试过了,现在安装干净了:

 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`) try { return require(module) } catch (e) { console.log(require.cache) console.log(e) } }) } console.log(`Requiring "${module}"`) } 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') 

当node_modules尚不可用时: 在此处输入图片说明

当node_modules可用时: 在此处输入图片说明

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM