Можно ли установить пакет NPM из файла javascript, работающего в Node.js? Например, я хотел бы иметь script, называть его "script.js", который каким-то образом (... используя NPM или нет...) устанавливает пакет, обычно доступный через NPM. В этом примере я хотел бы установить "FFI". (npm install ffi)
Могу ли я установить пакет NPM из javascript, работающего в Node.js?
Ответ 1
Действительно, можно использовать npm программно, и это было указано в более старых версиях документации. С тех пор он был удален из официальной документации, но все еще существует в отношении источника управления со следующим утверждением:
Хотя npm можно использовать программно, его API предназначен для использования только CLI, и никаких гарантий относительно его пригодности для каких-либо другой цели. Если вы хотите использовать npm для выполнения определенной задачи, самая безопасная вещь - вызвать желаемую команду npm с соответствующие аргументы.
Семантическая версия npm относится к самому CLI, а не к базовый API. Внутренний API не гарантированно остается стабильным даже когда версия npm указывает, что никаких изменений не было сделано согласно semver.
В исходной документации приведен пример кода, который был предоставлен:
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);
});
});
Обратите внимание, что первым аргументом функции установки является массив. Каждый элемент массива - это модуль, который будет пытаться установить npm.
Более расширенное использование можно найти в файле npm-cli.js
в исходном элементе управления.
Ответ 2
да. вы можете использовать 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);
}
});
Ответ 3
если вы хотите получить вывод, вы также можете использовать:
var child_process = require('child_process');
child_process.execSync("npm install ffi",{stdio:[0,1,2]});
таким образом вы можете наблюдать за установкой, как вы делаете это под рукой, и избегать неприятных неожиданностей (буфер заполнен и т.д.)
Ответ 4
он может быть немного легким
var exec = require('child_process').exec;
child = exec('npm install ffi').stderr.pipe(process.stderr);
Ответ 5
У меня было чертовски время, пытаясь заставить первый пример работать внутри каталога проекта, размещая здесь, если кто-нибудь еще найдет это. Насколько я могу судить, 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);
});
});
Ответ 6
Я являюсь автором модуля, позволяющего делать именно то, что вы имеете в виду. Смотрите 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
во время выполнения, загружаю и выполняю его. В конце я его деинсталлирую.
Внутренне я не запускаю npm
cli, но на самом деле загружаю пакеты и запускаю внутри песочницы VM узла.
Ответ 7
pacote - это пакет, который npm использует для получения метаданных и тарболлов. Имеет стабильный публичный API.
Ответ 8
Отличное решение от @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, [
"[email protected]^5.2.0",
"[email protected]^0.8.2"
], ( err, data ) => {
console.log( "done" );
});
});
Как видно из кода, NPM также генерирует показатели производительности на process
, поэтому мы также можем использовать его для мониторинга прогресса.
Ответ 9
Другой вариант, который здесь не упоминался, это сделать fork и запустить CLI прямо из ./node_modules/npm/bin/npm-cli.js
.Например, вы хотите иметь возможность устанавливать модули узлов из запущенного сценария на компьютере, на котором не установлен NPM. И вы действительно хотите сделать это с CLI. В этом случае просто установите NPM в свои node_modules локально при сборке вашей программы (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.