问题描述
我有一个脚本可以在启动时同步安装非内置模块,看起来像这样
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的列表的第一个或前两个模块时,脚本开始抛出
无法找到模块
错误$ 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 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.
指出,当需要两次模块时会发生这个确切的错误。我已将脚本更改为使用 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()
,可以像。
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.
这篇关于同步安装后,节点找不到某些模块的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!