我可以从在 Node.js 中运行的 javascript 安装 NPM 包吗?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/15957529/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Can I install a NPM package from javascript running in Node.js?
提问by Justin
Can I install a NPM package from a javascript file running in Node.js? For example, I'd like to have a script, let's call it "script.js" that somehow (...using NPM or not...) install a package usually available through NPM. In this example, I'd like to install "FFI". (npm install ffi)
我可以从在 Node.js 中运行的 javascript 文件安装 NPM 包吗?例如,我想要一个脚本,让我们称其为“script.js”,以某种方式(...使用 NPM 或不...)安装通常可通过 NPM 获得的包。在这个例子中,我想安装“FFI”。(npm 安装 ffi)
回答by hexacyanide
It is indeed possible to use npmprogrammatically, and it was outlined in older revisions of the documentation. It has since been removed from the official documentation, but still exists on source control with the following statement:
确实可以以编程方式使用npm,并且在文档的较旧修订版中进行了概述。它已从官方文档中删除,但仍然存在于源代码控制中,并带有以下声明:
Although npm can be used programmatically, its API is meant for use by the CLI only, and no guarantees are made regarding its fitness for any other purpose. If you want to use npm to reliably perform some task, the safest thing to do is to invoke the desired npm command with appropriate arguments.
The semantic version of npm refers to the CLI itself, rather than the underlying API. The internal API is not guaranteed to remain stable even when npm's version indicates no breaking changes have been made according to semver.
尽管 npm 可以以编程方式使用,但其 API 仅供 CLI 使用,并且不保证其适用于任何其他目的。如果您想使用 npm 可靠地执行某些任务,最安全的做法是使用适当的参数调用所需的 npm 命令。
npm 的语义版本指的是 CLI 本身,而不是底层 API。即使 npm 的版本表明没有根据 semver 进行重大更改,内部 API 也不能保证保持稳定。
In the original documentation, the following is the code sample that was provided:
在原始文档中,以下是提供的代码示例:
var npm = require('npm')
npm.load(myConfigObject, function (er) {
if (er) return handlError(er)
npm.commands.install(['some', 'args'], function (er, data) {
if (er) return commandFailed(er)
// command succeeded, and data might have some info
})
npm.registry.log.on('log', function (message) { ... })
})
Since npmexists in the node_modulesfolder, you can use require('npm')to load it like any other module. To install a module, you will want to use npm.commands.install().
由于npm存在于node_modules文件夹中,因此您可以require('npm')像加载任何其他模块一样使用它。要安装模块,您需要使用npm.commands.install().
If you need to look in the source then it's also on GitHub. Here's a complete working example of the code, which is the equivalent of running npm installwithout any command-line arguments:
如果您需要查看源代码,那么它也在GitHub 上。这是代码的完整工作示例,相当于在npm install没有任何命令行参数的情况下运行:
var npm = require('npm');
npm.load(function(err) {
// handle errors
// install module ffi
npm.commands.install(['ffi'], function(er, data) {
// log errors or data
});
npm.on('log', function(message) {
// log installation progress
console.log(message);
});
});
Note that the first argument to the install function is an array. Each element of the array is a module that npmwill attempt to install.
请注意, install 函数的第一个参数是一个数组。数组的每个元素都是一个npm将尝试安装的模块。
More advanced use can be found in the npm-cli.jsfile on source control.
更高级的用法可以npm-cli.js在源代码管理文件中找到。
回答by TheBrain
yes. you can use child_process to execute a system command
是的。您可以使用 child_process 来执行系统命令
var exec = require('child_process').exec,
child;
child = exec('npm install ffi',
function (error, stdout, stderr) {
console.log('stdout: ' + stdout);
console.log('stderr: ' + stderr);
if (error !== null) {
console.log('exec error: ' + error);
}
});
回答by krankuba
in order to see the output as well you can use:
为了查看输出,您可以使用:
var child_process = require('child_process');
child_process.execSync('npm install ffi',{stdio:[0,1,2]});
this way you can watch the installation like you do it manualy and avoid surprises like full buffer, etc.
通过这种方式,您可以像手动操作一样观看安装,并避免出现缓冲区满等意外情况。
回答by Vyacheslav Shebanov
it can actually be a bit easy
它实际上可能有点容易
var exec = require('child_process').exec;
child = exec('npm install ffi').stderr.pipe(process.stderr);
回答by Megamind
I had a heck of a time trying to get the first example to work inside a project directory, posting here in case anyone else finds this. As far as I can tell, NPM still works fine loaded directly, but because it assumes CLI, we have to repeat ourselves a little setting it up:
我花了很多时间试图让第一个示例在项目目录中工作,并在此处发布以防其他人发现它。据我所知,直接加载 NPM 仍然可以正常工作,但是因为它假设了 CLI,所以我们必须重复自己的一些设置:
// this must come before load to set your project directory
var previous = process.cwd();
process.chdir(project);
// this is the part missing from the example above
var conf = {'bin-links': false, verbose: true, prefix: project}
// this is all mostly the same
var cli = require('npm');
cli.load(conf, (err) => {
// handle errors
if(err) {
return reject(err);
}
// install module
cli.commands.install(['ffi'], (er, data) => {
process.chdir(previous);
if(err) {
reject(err);
}
// log errors or data
resolve(data);
});
cli.on('log', (message) => {
// log installation progress
console.log(message);
});
});
回答by James A. Rosen
回答by Dmitry Sheiko
A great solution by @hexacyanide, but it turned out that NPM doesn't emit "log" event anymore (at least as of version 6.4.1). Instead they rely on a standalone module https://github.com/npm/npmlog. Fortunately it's a singleton, so we can reach the very same instance NPM uses for logs and subscribe for log events:
@hexacyanide 的一个很好的解决方案,但事实证明 NPM 不再发出“日志”事件(至少从 6.4.1 版开始)。相反,他们依赖于一个独立的模块https://github.com/npm/npmlog。幸运的是,它是一个单例,因此我们可以访问 NPM 用于日志的相同实例并订阅日志事件:
const npmlog = require( "npm/node_modules/npmlog" ),
npm = require( "npm" );
npmlog.on( "log", msg => {
console.log({ msg });
});
process.on("time", milestone => {
console.log({ milestone });
});
process.on("timeEnd", milestone => {
console.log({ milestone });
});
npm.load({
loaded: false,
progress: false,
"no-audit": true
}, ( err ) => {
npm.commands.install( installDirectory, [
"cross-env@^5.2.0",
"shelljs@^0.8.2"
], ( err, data ) => {
console.log( "done" );
});
});
As you can see from the code, NPM also emits performance metrics on the process, so we can also use it to monitor the progress.
从代码中可以看出,NPM 还会在 上发出性能指标process,因此我们也可以使用它来监控进度。
回答by tarkh
Another option, which wasn't mentioned here, is to do fork and run CLI right from ./node_modules/npm/bin/npm-cli.js
此处未提及的另一种选择是直接从 ./node_modules/npm/bin/npm-cli.js
For example you want to be able to install node modules from running script on machine, which do not have NPM installed. And you DO want to do it with CLI. In this case just install NPM in your node_modules locally while building your program (npm i npm).
例如,您希望能够通过在未安装 NPM 的机器上运行脚本来安装节点模块。你确实想用 CLI 来做。在这种情况下,只需在构建程序时在本地 node_modules 中安装 NPM ( npm i npm)。
Then use it like this:
然后像这样使用它:
// Require child_process module
const { fork } = require('child_process');
// Working directory for subprocess of installer
const cwd = './path-where-to-run-npm-command';
// CLI path FROM cwd path! Pay attention
// here - path should be FROM your cwd directory
// to your locally installed npm module
const cli = '../node_modules/npm/bin/npm-cli.js';
// NPM arguments to run with
// If your working directory already contains
// package.json file, then just install it!
const args = ['install']; // Or, i.e ['audit', 'fix']
// Run installer
const installer = fork(cli, args, {
silent: true,
cwd: cwd
});
// Monitor your installer STDOUT and STDERR
installer.stdout.on('data', (data) => {
console.log(data);
});
installer.stderr.on('data', (data) => {
console.log(data);
});
// Do something on installer exit
installer.on('exit', (code) => {
console.log(`Installer process finished with code ${code}`);
});
Then your program could be even packed to binary file, for example with PKGpackage. In this case you need to use --ignore-scriptsnpm option, because node-gyp required to run preinstall scripts
然后你的程序甚至可以打包成二进制文件,例如使用PKG包。在这种情况下,您需要使用--ignore-scriptsnpm 选项,因为运行预安装脚本需要 node-gyp
回答by Davide Icardi
I'm the author of a module that allow to do exactly what you have in mind. See live-plugin-manager.
我是一个模块的作者,该模块允许完全按照您的想法进行操作。请参阅live-plugin-manager。
You can install and run virtually any package from NPM, Github or from a folder.
您几乎可以从 NPM、Github 或文件夹中安装和运行任何包。
Here an example:
这里有一个例子:
import {PluginManager} from "live-plugin-manager";
const manager = new PluginManager();
async function run() {
await manager.install("moment");
const moment = manager.require("moment");
console.log(moment().format());
await manager.uninstall("moment");
}
run();
In the above code I install momentpackage at runtime, load and execute it. At the end I uninstall it.
在上面的代码中,我moment在运行时安装包,加载并执行它。最后我卸载它。
Internally I don't run npmcli but actually download packages and run inside a node VM sandbox.
在内部,我不运行npmcli,而是实际下载包并在节点 VM 沙箱中运行。

