Tôi có thể cài đặt gói NPM từ javascript chạy trong Node.js không?


91

Tôi có thể cài đặt gói NPM từ tệp javascript chạy trong Node.js không? Ví dụ: tôi muốn có một tập lệnh, hãy gọi nó là "script.js" bằng cách nào đó (... sử dụng NPM hoặc không ...) cài đặt một gói thường có sẵn thông qua NPM. Trong ví dụ này, tôi muốn cài đặt "FFI". (npm cài đặt ffi)

Câu trả lời:


109

Thực sự có thể sử dụng npm theo lập trình, và nó đã được nêu trong các bản sửa đổi cũ hơn của tài liệu. Nó đã bị xóa khỏi tài liệu chính thức, nhưng vẫn tồn tại trên kiểm soát nguồn với tuyên bố sau:

Mặc dù npm có thể được sử dụng theo chương trình, nhưng API của nó chỉ dành cho CLI và không có đảm bảo nào được đưa ra về tính phù hợp của nó cho bất kỳ mục đích nào khác. Nếu bạn muốn sử dụng npm để thực hiện một số tác vụ một cách đáng tin cậy, điều an toàn nhất cần làm là gọi lệnh npm mong muốn với các đối số thích hợp.

Phiên bản ngữ nghĩa của npm đề cập đến chính CLI, thay vì API cơ bản. API nội bộ không được đảm bảo sẽ duy trì ổn định ngay cả khi phiên bản của npm chỉ ra rằng không có thay đổi vi phạm nào được thực hiện theo semver .

Trong tài liệu gốc, sau đây là mẫu mã đã được cung cấp:

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 tồn tại trong node_modulesthư mục, bạn có thể sử dụng require('npm')để tải nó giống như bất kỳ mô-đun nào khác. Để cài đặt một mô-đun, bạn sẽ muốn sử dụng npm.commands.install().

Nếu bạn cần tìm nguồn thì nó cũng có trên GitHub . Đây là một ví dụ hoạt động hoàn chỉnh của mã, tương đương với việc chạy npm installmà không có bất kỳ đối số dòng lệnh nào:

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

Lưu ý rằng đối số đầu tiên của hàm cài đặt là một mảng. Mỗi phần tử của mảng là một mô-đun mà npm sẽ cố gắng cài đặt.

Sử dụng nâng cao hơn có thể được tìm thấy trong npm-cli.jstệp kiểm soát nguồn.


5
trong trường hợp điều này giúp ích cho bất kỳ ai - hãy đảm bảo rằng bạn làm npm install npm --savetrước. Ví dụ làm việc :) lớn
mikermcneil

6
Ngoài ra, hãy cẩn thận - npmcó rất nhiều phụ thuộc, vì vậy việc thêm nó vào mô-đun của bạn rất có thể sẽ dẫn đến việc mất nhiều thời gian hơn để tải xuống. Kiểm tra một trong những child_processcâu trả lời để tận dụng npm toàn cầu đã được cài đặt trên máy của người dùng của bạn.
mikermcneil

1
Không vượt qua npm.configđể npm.load! Ngay cả @isaacs cũng không biết sau đó sẽ xảy ra những chuyện kỳ ​​quái gì nữa! Xem github.com/npm/npm/issues/4861#issuecomment-40533836 Thay vào đó, bạn có thể bỏ qua đối số đầu tiên.
Georgii Ivankin

2
Làm cách nào để đặt đường dẫn đích? (khi nó khác với process.cwd())
Gajus

1
Đối với những người muốn nhập NPM bất chấp các cảnh báo, global-npm tốt hơn (nhỏ hơn, không phụ thuộc) so vớinpm install npm --save
Xunnamius 12/09/18

26

Đúng. bạn có thể sử dụng child_process để thực thi một lệnh hệ thống

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
Có, bạn có thể, tuy nhiên một số phụ thuộc SẼ không cài đặt được (nói theo kinh nghiệm, bởi vì đã có lần tôi thực sự viết một máy chủ CI cho node.js)
Matej

5
Trên cửa sổ điều này không hoạt động! Bạn phải gọi npm.cmdthay thế.
DUzun

26

Bạn có thể sử dụng child_process . exec hoặc execSync để đẻ trứng một vỏ sau đó thực hiện lệnh mong muốn trong vỏ đó, đệm bất kỳ sản lượng được tạo ra:

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

Nếu một hàm gọi lại được cung cấp, nó sẽ được gọi với các đối số (error, stdout, stderr). Bằng cách này, bạn có thể chạy cài đặt giống như thao tác thủ công và xem toàn bộ kết quả đầu ra.

Phương thức child_process.execSync () nói chung giống với child_process.exec () với ngoại lệ là phương thức sẽ không trả về cho đến khi quá trình con đóng hoàn toàn.


2
đây là tùy chọn duy nhất trong số tất cả các câu trả lời, ví dụ cho phép bạn chạy cài đặt npm và nhận toàn bộ kết quả đầu ra như thể bạn đang thực hiện lệnh theo cách thủ công! cảm ơn bạn!
Jörn Berkefeld

1
Làm gì stdio: [0,1,2]?
Zach Smith

nếu một hàm callback được cung cấp cho child_process.exec, nó được gọi với các đối số tương đương với [process.stdin, process.stdout, process.stderr] hoặc [0,1,2] theo api doc
krankuba

11

nó thực sự có thể dễ dàng một chút

var exec = require('child_process').exec;
child = exec('npm install ffi').stderr.pipe(process.stderr);

2
Điều này cũng có lợi thế là stderr (và stdout) được in ra khi chúng xảy ra, không phải ở cuối quá trình thực thi!
mvermand

1
Điều này không in ra ở mức độ giống như câu trả lời từ @krankuba bên dưới, theo như tôi có thể nói.
Zach Smith

6

Tôi đã rất mất thời gian cố gắng lấy ví dụ đầu tiên để hoạt động bên trong thư mục dự án, đăng ở đây trong trường hợp bất kỳ ai khác tìm thấy điều này. Theo như tôi có thể nói, NPM vẫn hoạt động tốt khi tải trực tiếp, nhưng vì nó giả định CLI, chúng tôi phải tự thiết lập lại một chút:

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

3

pacote là gói mà npm sử dụng để tìm nạp siêu dữ liệu gói và tarball. Nó có một API công khai, ổn định.


2

Tôi là tác giả của một mô-đun cho phép thực hiện chính xác những gì bạn có trong đầu. Xem live-plugin-manager .

Bạn có thể cài đặt và chạy hầu như bất kỳ gói nào từ NPM, Github hoặc từ một thư mục.

Đây là một ví dụ:

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

Trong đoạn mã trên, tôi cài đặt momentgói trong thời gian chạy, tải và thực thi nó. Cuối cùng, tôi gỡ cài đặt nó.

Bên trong, tôi không chạy npmcli nhưng thực sự tải xuống các gói và chạy bên trong hộp cát của máy ảo nút.


1

Một giải pháp tuyệt vời của @hexacyanide, nhưng hóa ra NPM không phát ra sự kiện "log" nữa (ít nhất là kể từ phiên bản 6.4.1). Thay vào đó, họ dựa vào một mô-đun độc lập https://github.com/npm/npmlog . May mắn thay, đó là một singleton, vì vậy chúng ta có thể tiếp cận cùng một trường hợp NPM sử dụng cho nhật ký và đăng ký các sự kiện nhật ký:

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

  });

Như bạn có thể thấy từ mã, NPM cũng phát ra các số liệu hiệu suất trên process, vì vậy chúng tôi cũng có thể sử dụng nó để theo dõi tiến trình.


1

Một tùy chọn khác, không được đề cập ở đây, là thực hiện fork và chạy CLI ngay từ ./node_modules/npm/bin/npm-cli.js

Ví dụ: bạn muốn có thể cài đặt các mô-đun nút từ tập lệnh đang chạy trên máy không được cài đặt NPM. Và bạn CÓ muốn làm điều đó với CLI. Trong trường hợp này, chỉ cần cài đặt NPM trong node_modules cục bộ trong khi xây dựng chương trình của bạn ( npm i npm).

Sau đó, sử dụng nó như thế này:

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

Sau đó, chương trình của bạn thậm chí có thể được đóng gói thành tệp nhị phân, ví dụ với gói PKG . Trong trường hợp này, bạn cần sử dụng --ignore-scriptstùy chọn npm, vì node-gyp bắt buộc phải chạy các tập lệnh cài đặt trước

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.