101

我可以从 Node.js 中运行的 javascript 文件安装 NPM 包吗?例如,我想要一个脚本,我们称它为“script.js”,它以某种方式(......使用 NPM 或不......)安装通常通过 NPM 提供的包。在这个例子中,我想安装“FFI”。(npm 安装 ffi)

4

10 回答 10

122

确实可以以编程方式使用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在源代码管理文件中找到。

于 2013-04-11T19:48:14.127 回答
41

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

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

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

child_process.execSync() 方法通常与 child_process.exec() 相同,只是该方法在子进程完全关闭之前不会返回。

于 2017-05-14T10:14:53.047 回答
29

是的。您可以使用 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);
     }
 });
于 2013-04-11T19:48:43.143 回答
11

它实际上可能有点容易

var exec = require('child_process').exec;
child = exec('npm install ffi').stderr.pipe(process.stderr);
于 2013-10-16T14:31:52.713 回答
7

我花了很长时间试图让第一个示例在项目目录中工作,在这里发布以防其他人发现它。据我所知,直接加载 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);
    });
});
于 2017-07-15T14:49:58.553 回答
4

pacote是 npm 用来获取包元数据和 tarball 的包。它有一个稳定的公共 API。

于 2018-10-08T15:29:34.617 回答
3

此处未提及的另一种选择是直接从./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

于 2019-09-12T20:37:50.450 回答
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 沙箱中运行。

于 2018-01-28T10:17:58.787 回答
1

@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" );    
    });

  });

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

于 2018-11-08T08:39:14.870 回答
0

增加一点tarkh 的好答案。如果您不打算显式安装 npm CLI 并且已经通过 package.json 文件安装 npm。那么里面的文件./node_modules/npm/bin/npm-cli.js就是可以用来运行命令的CLI。

为了使其全局可用,请运行以下命令并将二进制文件添加到路径中。

ln -sf /usr/app/node_modules/npm/bin/npm-cli.js /usr/bin/npm

此外,如果它对某人有帮助。这是您如何模仿npm install命令以安装 package.json 中的所有包的方法。

const npm = require('npm');
const Bluebird = require('bluebird');

    async installDependencies() {
            await Bluebird.promisify(npm.load)({
                loglevel: 'silent',
                progress: false,
            });
            await Bluebird.promisify(npm.install)(
                "/path/to/directory/where/package.json/is",
            );
        }
于 2021-12-23T15:17:57.353 回答