web-dev-qa-db-fra.com

Puis-je installer un package NPM à partir de javascript s'exécutant dans Node.js?

Puis-je installer un package NPM à partir d'un fichier javascript exécuté dans Node.js? Par exemple, j'aimerais avoir un script, appelons-le "script.js" qui installe en quelque sorte (... avec NPM ou non ...) un paquet généralement disponible via NPM. Dans cet exemple, j'aimerais installer "FFI". (npm install ffi) 

68
Justin

Il est en effet possible d'utiliser npm par programmation, et cela a été décrit dans les versions précédentes de la documentation. Depuis, il a été supprimé de la documentation officielle, mais existe toujours sur le contrôle de source avec la déclaration suivante:

Bien que npm puisse être utilisé par programme, son API est destinée à être utilisée par la CLI uniquement, et aucune garantie n’est donnée concernant son adéquation à une quelconque autre but. Si vous souhaitez utiliser npm pour effectuer des tâches de manière fiable, la solution la plus sûre consiste à appeler la commande npm souhaitée avec arguments appropriés.

La version sémantique de npm fait référence à la CLI elle-même plutôt qu'à la API sous-jacente. Il n'est pas garanti que l'API interne reste stable même lorsque la version de npm indique qu’aucune modification radicale n’a été apportée selon semver.

Dans la documentation d'origine, voici l'exemple de code fourni:

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

Comme npm existe dans le dossier node_modules, vous pouvez utiliser require('npm') pour le charger comme n'importe quel autre module. Pour installer un module, vous voudrez utiliser npm.commands.install().

Si vous avez besoin de chercher dans la source, c'est aussi sur GitHub . Voici un exemple de travail complet du code, qui équivaut à exécuter npm install sans aucun argument de ligne de commande:

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

Notez que le premier argument de la fonction install est un tableau. Chaque élément du tableau est un module que npm tentera d’installer.

Une utilisation plus avancée se trouve dans le fichier npm-cli.js sur le contrôle de source.

94
hexacyanide

oui. vous pouvez utiliser child_process pour exécuter une commande système

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

si vous voulez aussi une sortie, vous pouvez utiliser:

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

de cette façon, vous pouvez regarder l'installation comme vous le faites sous la main et éviter les mauvaises surprises (mémoire tampon pleine, etc.)

12
krankuba

ça peut être un peu facile

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

J'ai eu une sacrée occasion de faire en sorte que le premier exemple fonctionne dans un répertoire de projet, en publiant ici au cas où quelqu'un d'autre le trouverait. Autant que je sache, NPM fonctionne toujours bien chargé directement, mais comme il suppose CLI, nous devons nous répéter un peu les réglages:

// 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
Megamind

Je suis l'auteur d'un module qui permet de faire exactement ce que vous avez en tête ... Voir live-plugin-manager .

Vous pouvez installer et exécuter pratiquement n'importe quel package à partir de NPM, Github ou d'un dossier.

Voici un exemple:

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

Dans le code ci-dessus, j'installe le package moment au moment de l'exécution, le charge et l'exécute. À la fin, je le désinstalle.

En interne, je n'exécute pas npm cli mais je télécharge des packages et je l'exécute dans un sandbox de noeud VM.

1
Davide Icardi

pacote est le paquet que npm utilise pour récupérer les métadonnées et les archives de paquets Il possède une API publique stable.

0
James A. Rosen

Une excellente solution de @hexacyanide, mais il s'est avéré que NPM n'émet plus d'événement "log" (au moins à partir de la version 6.4.1). Au lieu de cela, ils s'appuient sur un module autonome https://github.com/npm/npmlog . Heureusement, il s’agit d’un singleton. Nous pouvons donc atteindre la même instance que NPM pour les journaux et s’abonner aux événements de journal:

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

  });

Comme vous pouvez le constater d'après le code, NPM émet également des métriques de performance sur la variable process, afin que nous puissions également l'utiliser pour surveiller les progrès.

0
Dmitry Sheiko