我可以通过Node.js中运行的javascript安装NPM软件包吗?


91

我可以从Node.js中运行的javascript文件安装NPM软件包吗?例如,我想要一个脚本,我们称它为“ script.js”,该脚本以某种方式(...是否使用NPM ...)安装了通常可通过NPM获得的软件包。在此示例中,我想安装“ FFI”。(npm安装ffi)

Answers:


109

确实可以以编程方式使用npm,并且在文档的较早版本中对此进行了概述。此后,它已从官方文档中删除,但仍在源代码控制中使用以下语句:

尽管npm可以通过编程方式使用,但其API仅供CLI使用,并且不能保证其适用于任何其他目的。如果要使用npm可靠地执行某些任务,最安全的做法是使用适当的参数调用所需的npm命令。

npm的语义版本是指CLI本身,而不是基础API。即使npm的版本指示根据semver没有进行任何重大更改,也不能保证内部API保持稳定

在原始文档中,以下是所提供的代码示例:

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) { ... })
})

由于npm存在于node_modules文件夹中,因此您可以require('npm')像其他任何模块一样使用它来加载它。要安装模块,您将需要使用npm.commands.install()

如果您需要查看源代码,那么它也位于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);
  });
});

请注意,install函数的第一个参数是数组。数组的每个元素都是npm会尝试安装的模块。

npm-cli.js在源代码管理的文件中可以找到更高级的用法。


5
万一这对任何人都有帮助-请确保您先做npm install npm --save。示例效果很好:)
mikermcneil 2013年

6
另外,请注意,它npm具有很多依赖性,因此将其添加到模块中很可能导致其花费大量时间来下载。找出child_process答案之一,以利用用户计算机上已经安装的全局npm。
mikermcneil

1
千万不要错过npm.confignpm.load!甚至@isaacs都不知道那会发生什么奇怪的事情!参见github.com/npm/npm/issues/4861#issuecomment-40533836相反,您可以跳过第一个参数。
Georgii Ivankin

2
如何设置目标路径?(如果有所不同process.cwd()
Gajus,2016年

1
对于那些尽管有警告仍希望导入NPM的用户,global-npmnpm install npm --save
Xunnamius

26

是。您可以使用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);
     }
 });

2
是的,您可以,但是某些依赖项将无法安装(从经验谈起,因为从前我实际上为node.js 编写了CI服务器)
Matej 2013年

5
在Windows上,这不起作用!您必须打电话给npm.cmd
DUzun '16

26

您可以使用child_processexecexecSync生成外壳程序,然后在该外壳程序中执行所需的命令,缓冲任何生成的输出:

var child_process = require('child_process');
child_process.execSync('npm install ffi',{stdio:[0,1,2]});

如果提供了回调函数,则使用参数(错误,stdout,stderr)调用它。这样,您可以像手动进行安装一样运行安装并查看完整输出。

child_process.execSync()方法通常与child_process.exec()相同,不同之处在于该方法直到子进程完全关闭后才会返回。


2
这是所有答案中的唯一选项,例如,它使您可以运行npm install并获得完整的输出,就像您手动执行命令一样!谢谢!
约恩·伯克费尔德

1
怎么stdio: [0,1,2]办?
扎克·史密斯

如果为child_process.exec提供了回调函数,则根据api doc
krankuba,


6

我花了很长时间尝试使第一个示例在项目目录中运行,以防其他人发现此问题。据我所知,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);
    });
});


2

我是一个模块的作者,该模块可以完全按照您的想法进行操作。见live-plugin-manager

您几乎可以从NPM,Github或文件夹中安装和运行任何软件包。

这里是一个例子:

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();

在上面的代码中,我moment在运行时安装软件包,然后加载并执行它。最后,我将其卸载。

在内部,我不运行npmcli,而是实际下载软件包并在节点VM沙箱中运行。


1

@hexacyanide是一个很好的解决方案,但事实证明NPM不再发出“ log”事件(至少从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" );    
    });

  });

从代码中可以看到,NPM还在上发出性能指标process,因此我们也可以使用它来监视进度。


1

此处未提及的另一个选项是执行fork并直接从命令行运行CLI ./node_modules/npm/bin/npm-cli.js

例如,您希望能够通过未安装NPM的计算机上的运行脚本来安装节点模块。而且您确实想使用CLI做到这一点。在这种情况下,只需在构建程序时在本地的node_modules中安装NPM(npm i npm)。

然后像这样使用它:

// 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}`);
});

然后,您的程序甚至可以打包为二进制文件,例如使用PKG软件包。在这种情况下,您需要使用--ignore-scriptsnpm选项,因为node-gyp需要运行预安装脚本

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.