我有一个在启动时同步安装非内置模块的脚本,如下所示
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
错误。没错,只有当脚本必须安装第一个或前两个模块时才会出现错误,而不是只有第二个模块需要安装时才会出现。
在此示例中,当我卸载 find-free-port 时将抛出错误,除非我将其 require
至少向下移动一个位置¯\_(• _ •)_/¯
我还尝试在同步安装后直接添加延迟,以通过以下两行为其提供更多的呼吸时间:
var until = new Date().getTime() + 1000
while (new Date().getTime() < until) {}
暂停就在那里。它没有解决任何问题。
@velocityzen came with the idea to check the cache ,我现在已将其添加到脚本中。它没有显示出任何异常。
@vaughan's comment on another question请注意,当两次需要一个模块时会发生这个确切的错误。我已将脚本更改为使用 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 install
之后的require
操作似乎需要一定的延迟。
此外,Windows 中的问题更严重,如果模块需要 npm 安装
,它将总是失败。
就像在特定事件快照中已经知道哪些模块是必需的,哪些不是。可能这就是评论中提到 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
总是需要一个 promise 才能工作,但不需要显式创建一个,因为 await
会把它正在等待的任何东西包装在一个 promise 中,如果它不是的话。我递给了一个。
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
,正如您所猜测的那样!此外,它似乎更同步而不是第一个选项,但有点复杂。
关于javascript - Node 在同步安装后找不到某些模块,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53270058/