我有一个脚本,它可以在启动时同步安装非内置模块,就像这样
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...
当我卸载模块时,它们将在我再次启动服务器时成功安装。但是,当我卸载使用功能Cannot find module
的列表的前两个模块时,脚本开始引发requireOrInstall
错误。没错,仅当脚本必须安装前两个模块时才发生错误,而仅第二个模块需要安装时才发生错误。
在此示例中,当我卸载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中那样使用。
答案 0 :(得分:1)
我认为您最好的选择是:
首先,您可以考虑使用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'];
在加载之前,您可能需要手动对其进行更新,以添加有关新模块的信息。
答案 1 :(得分:1)
似乎require
之后的npm install
操作需要一定的延迟。
同样,在Windows中问题更严重,如果模块需要为npm installed
,它将总是失败。
就像在特定的事件快照中,已经知道需要什么模块,不需要什么模块。也许这就是在评论中提到require.cache
的原因。不过,我建议您检查以下两种解决方案。
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
会将承诺中没有等待的内容包装起来。 >
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
,以便您猜对了!同样,似乎 more 是同步的,而不是第一种选择,但是有点复杂。
答案 2 :(得分:0)
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')