Instale dependencias global y localmente usando package.json

189

Usando npm podemos instalar los módulos globalmente usando la -gopción. ¿Cómo podemos hacer esto en el archivo package.json?

Supongamos que estas son mis dependencias en el archivo package.json

"dependencies": {
    "mongoose": "1.4.0",
    "node.io" : "0.3.3",
    "jquery"  : "1.5.1",
    "jsdom"   : "0.2.0",
    "cron"    : "0.1.2"
  }

Cuando ejecuto npm install, solo quiero node.ioinstalarme globalmente, el resto deben instalarse localmente. ¿Hay una opción para esto?

Madhusudhan
fuente
11
No puedes Sin embargo, puede establecer "preferGlobal": truedentro de package.json para un módulo.
Raynos
sí, sé sobre <code> preferGlobal </code>, pero eso instalaría todas las dependencias a nivel mundial ... de todos modos ¡Gracias! Supongo que no hay una característica como esa ...
Madhusudhan
3
No creo que lo haga. Instala el módulo actual globalmente. Si una dependencia individual lo ha establecido en verdadero, también puede instalarse globalmente. Realmente deberías preguntarle a @isaacs en # node.js
Raynos
3
Las instalaciones globales pueden producir dependencia infierno. Digamos que el paquete A necesita la versión 0.3.3 y el paquete B versión 0.3.4 y ambos no funcionan con la otra versión. Entonces necesitaría dos máquinas para acomodar los dos paquetes.
finalmente
66
ninguno de estos comentarios me ayuda con este problema ... sería bueno si el código me muestra más que solo "preferGlobal":true... Realmente no sé dónde poner esto en package.json. npmjs.org/doc/json.html La documentación de NPM dice que preferGlobal es para su propio paquete y que su configuración lo hará instalar su propio paquete como global. Sin embargo, parece más una guía.
PPPaul

Respuestas:

216

Nueva Nota: Probablemente no quieras o necesites hacer esto. Lo que probablemente quiera hacer es poner esos tipos de dependencias de comandos para compilación / prueba, etc. en la devDependenciessección de su package.json. Cada vez que usa algo de scriptspackage.json, sus comandos devDependencies (en node_modules / .bin) actúan como si estuvieran en su ruta.

Por ejemplo:

npm i --save-dev mocha # Install test runner locally
npm i --save-dev babel # Install current babel locally

Luego en package.json:

// devDependencies has mocha and babel now

"scripts": {
  "test": "mocha",
  "build": "babel -d lib src",
  "prepublish": "babel -d lib src"
}

Luego, en el símbolo del sistema, puede ejecutar:

npm run build # finds babel
npm test # finds mocha

npm publish # will run babel first

Pero si realmente desea realizar una instalación global, puede agregar una preinstalación en la sección de secuencias de comandos de package.json:

"scripts": {
  "preinstall": "npm i -g themodule"
}

Así que en realidad mi instalación npm ejecuta la instalación npm nuevamente ... lo cual es extraño pero parece funcionar.

Nota: es posible que tenga problemas si está utilizando la configuración más común para npmdonde se requiere la instalación del paquete Node global sudo. Una opción es cambiar su npmconfiguración para que esto no sea necesario:

npm config set prefix ~/npm, agregue $ HOME / npm / bin a $ PATH agregando export PATH=$HOME/npm/bin:$PATHa su ~/.bashrc.

Jason Livesay
fuente
3
No pude hacer que esto funcione npm i -g underscore-cli. da una advertencia sobre wd estar equivocado. wd significa directorio de trabajo, supongo. cuando hago esto manualmente en la línea de comando, entonces las cosas van bien, sin embargo, preferiría que el usuario pudiera lidiar con la instalación de mi código con un simplenpm install
PPPaul
3
PPPaul: tuve el mismo problema cuando intenté este truco nuevamente recientemente. Tal vez mi configuración es diferente ahora o solo funciona con ciertos módulos. De lo contrario, supongo que algo cambió con npm?
Jason Livesay
9
Además de eso, puede verificar previamente si el paquete ya está instalado: npm list module -g || npm install module -gya que npm devolverá los valores de salida adecuados.
m90
3
@CMCDragonkai: Esta realmente debería ser una pregunta separada. Pero, coloca sus comandos en un script y especifica el script como el comando a ejecutar (como "preinstall" : "scripts/preinstall.sh").
Todos somos Mónica
1
@CMCDragonkai los atrapa &&, por ejemplonpm install -g bower && npm install -g grunt-cli
Matsemann el
12

Debido a las desventajas descritas a continuación, recomendaría seguir la respuesta aceptada:

Use y npm install --save-dev [package_name]luego ejecute scripts con:

$ npm run lint
$ npm run build
$ npm test

Mi respuesta original pero no recomendada sigue.


En lugar de utilizar una instalación global, puede agregar el paquete a su devDependencies( --save-dev) y luego ejecutar el binario desde cualquier lugar dentro de su proyecto:

"$(npm bin)/<executable_name>" <arguments>...

En tu caso:

"$(npm bin)"/node.io --help

Este ingeniero proporcionó un npm-execalias como acceso directo. Este ingeniero usa un shellscript llamado env.sh. Pero prefiero usar $(npm bin)directamente, para evitar cualquier archivo adicional o configuración.

Aunque hace que cada llamada sea un poco más grande, debería funcionar , evitando:

  • conflictos de dependencia potenciales con paquetes globales (@nalply)
  • la necesidad de sudo
  • la necesidad de configurar un prefijo npm (aunque recomiendo usar uno de todos modos)

Desventajas

  • $(npm bin) No funcionará en Windows.
  • Las herramientas más profundas en su árbol de desarrollo no aparecerán en la npm bincarpeta. (Instale npm-run o npm-which para encontrarlos).

Parece que una mejor solución es colocar tareas comunes (como construir y minificar) en la sección "scripts" de su package.json, como lo demuestra Jason anteriormente.

joeytwiddle
fuente
Añadir un alias en su .bashrcañadir fácilmente el bin/directorio a la PATHvariable de entorno: alias nodebin='export PATH=$(npm bin)/:$PATH'. Ejecute nodebiny luego simplemente puede escribir sus comandos como de costumbre.
gitaarik
No sé por qué no funcionaría para los equipos. Por supuesto, debe configurarlo, y si no le gusta usar el alias, es su elección. Pero no está de más usarlo en un equipo.
gitaarik
9

Esto es un poco viejo, pero me encontré con el requisito, así que aquí está la solución que se me ocurrió.

El problema:

Nuestro equipo de desarrollo mantiene muchos productos de aplicaciones web .NET que estamos migrando a AngularJS / Bootstrap. VS2010 no se presta fácilmente a procesos de compilación personalizados y mis desarrolladores están trabajando rutinariamente en múltiples versiones de nuestros productos. Nuestro VCS es Subversion (lo sé, lo sé. Estoy tratando de mudarme a Git pero mi molesto personal de marketing es muy exigente) y una única solución VS incluirá varios proyectos separados. Necesitaba que mi personal tuviera un método común para inicializar su entorno de desarrollo sin tener que instalar los mismos paquetes Node (trago, bower, etc.) varias veces en la misma máquina.

TL; DR:

  1. Necesita "npm install" para instalar el entorno de desarrollo global Node / Bower, así como todos los paquetes necesarios localmente para un producto .NET.

  2. Los paquetes globales deben instalarse solo si aún no están instalados.

  3. Los enlaces locales a paquetes globales deben crearse automáticamente.

La solución:

Ya tenemos un marco de desarrollo común compartido por todos los desarrolladores y todos los productos, así que creé un script NodeJS para instalar los paquetes globales cuando sea necesario y crear los enlaces locales. El script reside en ".... \ SharedFiles" en relación con la carpeta base del producto:

/*******************************************************************************
* $Id: npm-setup.js 12785 2016-01-29 16:34:49Z sthames $
* ==============================================================================
* Parameters: 'links' - Create links in local environment, optional.
* 
* <p>NodeJS script to install common development environment packages in global
* environment. <c>packages</c> object contains list of packages to install.</p>
* 
* <p>Including 'links' creates links in local environment to global packages.</p>
* 
* <p><b>npm ls -g --json</b> command is run to provide the current list of 
* global packages for comparison to required packages. Packages are installed 
* only if not installed. If the package is installed but is not the required 
* package version, the existing package is removed and the required package is 
* installed.</p>.
*
* <p>When provided as a "preinstall" script in a "package.json" file, the "npm
* install" command calls this to verify global dependencies are installed.</p>
*******************************************************************************/
var exec = require('child_process').exec;
var fs   = require('fs');
var path = require('path');

/*---------------------------------------------------------------*/
/* List of packages to install and 'from' value to pass to 'npm  */
/* install'. Value must match the 'from' field in 'npm ls -json' */
/* so this script will recognize a package is already installed. */
/*---------------------------------------------------------------*/
var packages = 
  {
  "bower"                      :                      "[email protected]", 
  "event-stream"               :               "[email protected]",
  "gulp"                       :                       "[email protected]",
  "gulp-angular-templatecache" : "[email protected]",
  "gulp-clean"                 :                 "[email protected]", 
  "gulp-concat"                :                "[email protected]",
  "gulp-debug"                 :                 "[email protected]",
  "gulp-filter"                :                "[email protected]",
  "gulp-grep-contents"         :         "[email protected]",
  "gulp-if"                    :                    "[email protected]", 
  "gulp-inject"                :                "[email protected]", 
  "gulp-minify-css"            :            "[email protected]",
  "gulp-minify-html"           :           "[email protected]",
  "gulp-minify-inline"         :         "[email protected]",
  "gulp-ng-annotate"           :           "[email protected]",
  "gulp-processhtml"           :           "[email protected]",
  "gulp-rev"                   :                   "[email protected]",
  "gulp-rev-replace"           :           "[email protected]",
  "gulp-uglify"                :                "[email protected]",
  "gulp-useref"                :                "[email protected]",
  "gulp-util"                  :                  "[email protected]",
  "lazypipe"                   :                   "[email protected]",
  "q"                          :                          "[email protected]",
  "through2"                   :                   "[email protected]",

  /*---------------------------------------------------------------*/
  /* fork of 0.2.14 allows passing parameters to main-bower-files. */
  /*---------------------------------------------------------------*/
  "bower-main"                 : "git+https://github.com/Pyo25/bower-main.git" 
  }

/*******************************************************************************
* run */
/**
* Executes <c>cmd</c> in the shell and calls <c>cb</c> on success. Error aborts.
* 
* Note: Error code -4082 is EBUSY error which is sometimes thrown by npm for 
* reasons unknown. Possibly this is due to antivirus program scanning the file 
* but it sometimes happens in cases where an antivirus program does not explain 
* it. The error generally will not happen a second time so this method will call 
* itself to try the command again if the EBUSY error occurs.
* 
* @param  cmd  Command to execute.
* @param  cb   Method to call on success. Text returned from stdout is input.
*******************************************************************************/
var run = function(cmd, cb)
  {
  /*---------------------------------------------*/
  /* Increase the maxBuffer to 10MB for commands */
  /* with a lot of output. This is not necessary */
  /* with spawn but it has other issues.         */
  /*---------------------------------------------*/
  exec(cmd, { maxBuffer: 1000*1024 }, function(err, stdout)
    {
    if      (!err)                   cb(stdout);
    else if (err.code | 0 == -4082) run(cmd, cb);
    else throw err;
    });
  };

/*******************************************************************************
* runCommand */
/**
* Logs the command and calls <c>run</c>.
*******************************************************************************/
var runCommand = function(cmd, cb)
  {
  console.log(cmd);
  run(cmd, cb);
  }

/*******************************************************************************
* Main line
*******************************************************************************/
var doLinks  = (process.argv[2] || "").toLowerCase() == 'links';
var names    = Object.keys(packages);
var name;
var installed;
var links;

/*------------------------------------------*/
/* Get the list of installed packages for   */
/* version comparison and install packages. */
/*------------------------------------------*/
console.log('Configuring global Node environment...')
run('npm ls -g --json', function(stdout)
  {
  installed = JSON.parse(stdout).dependencies || {};
  doWhile();
  });

/*--------------------------------------------*/
/* Start of asynchronous package installation */
/* loop. Do until all packages installed.     */
/*--------------------------------------------*/
var doWhile = function()
  {
  if (name = names.shift())
    doWhile0();
  }

var doWhile0 = function()
  {
  /*----------------------------------------------*/
  /* Installed package specification comes from   */
  /* 'from' field of installed packages. Required */
  /* specification comes from the packages list.  */
  /*----------------------------------------------*/
  var current  = (installed[name] || {}).from;
  var required =   packages[name];

  /*---------------------------------------*/
  /* Install the package if not installed. */
  /*---------------------------------------*/
  if (!current)
    runCommand('npm install -g '+required, doWhile1);

  /*------------------------------------*/
  /* If the installed version does not  */
  /* match, uninstall and then install. */
  /*------------------------------------*/
  else if (current != required)
    {
    delete installed[name];
    runCommand('npm remove -g '+name, function() 
      {
      runCommand('npm remove '+name, doWhile0);
      });
    }

  /*------------------------------------*/
  /* Skip package if already installed. */
  /*------------------------------------*/
  else
    doWhile1();
  };

var doWhile1 = function()
  {
  /*-------------------------------------------------------*/
  /* Create link to global package from local environment. */
  /*-------------------------------------------------------*/
  if (doLinks && !fs.existsSync(path.join('node_modules', name)))
    runCommand('npm link '+name, doWhile);
  else
    doWhile();
  };

Ahora, si quiero actualizar una herramienta global para nuestros desarrolladores, actualizo el objeto "paquetes" y verifico el nuevo script. Mis desarrolladores lo comprueban y lo ejecutan con "node npm-setup.js" o con "npm install" desde cualquiera de los productos en desarrollo para actualizar el entorno global. Todo toma 5 minutos.

Además, para configurar el entorno para un nuevo desarrollador, primero deben instalar NodeJS y GIT para Windows, reiniciar su computadora, revisar la carpeta "Archivos compartidos" y cualquier producto en desarrollo, y comenzar a trabajar.

El "package.json" para el producto .NET llama a este script antes de la instalación:

{ 
"name"                    : "Books",
"description"             : "Node (npm) configuration for Books Database Web Application Tools",
"version"                 : "2.1.1",
"private"                 : true,
"scripts":
  {
  "preinstall"            : "node ../../SharedFiles/npm-setup.js links",
  "postinstall"           : "bower install"
  },
"dependencies": {}
}

Notas

  • Tenga en cuenta que la referencia del script requiere barras diagonales incluso en un entorno Windows.

  • "npm ls" dará "npm ERR! extraño": mensajes para todos los paquetes vinculados localmente porque no están listados en las "dependencias" "package.json".

Editar 29/01/16

El npm-setup.jsscript actualizado anterior se ha modificado de la siguiente manera:

  • La "versión" var packagesdel paquete ahora es el valor del "paquete" pasado npm installen la línea de comando. Esto se cambió para permitir la instalación de paquetes desde otro lugar que no sea el repositorio registrado.

  • Si el paquete ya está instalado pero no es el solicitado, se elimina el paquete existente y se instala el correcto.

  • Por razones desconocidas, npm arrojará periódicamente un error EBUSY (-4082) al realizar una instalación o enlace. Este error queda atrapado y el comando se vuelve a ejecutar. El error rara vez ocurre por segunda vez y parece que siempre desaparece.

sthames42
fuente
¡Esto es un salvavidas @ sthames42! He estado trolleando durante horas tratando de descubrir exactamente cómo hacer esto. Claro, completo, generalmente impresionante. #points Preguntas: (a) ¿Por qué Bower está en el postinstall cuando ya está en la lista de paquetes? (b) ¿Cómo NO vincular localmente los paquetes globales? ¿Simplemente no incluye "enlaces" en el comando?
MaxRocket
@MaxRocket: Me alegro de poder ayudar. He actualizado la respuesta para incluir mi último, que funciona mucho mejor. Respuestas: (a) el comando 'bower install' se ejecuta después de que se realiza 'npm install' para instalar los componentes de Bower listados en el archivo bower.json que no se muestra aquí. Quería que mi gente pudiera escribir 'npm install' y tener su entorno completamente configurado sin tener que escribir otro comando. (b) Sí.
sthames42
La versión actual de este script ahora se mantiene aquí .
sthames42
6

Puede usar un archivo separado, como npm_globals.txt, en lugar de package.json. Este archivo contendría cada módulo en una nueva línea como esta,

mongoose@1.4.0
node.io@0.3.3
jquery@1.5.1
jsdom@0.2.0
cron@0.1.2

Luego, en la línea de comando,

< npm_globals.txt xargs npm install -g

Compruebe que se instalaron correctamente con

npm list -g --depth=0

En cuanto a si debe hacer esto o no, creo que todo depende del caso de uso. Para la mayoría de los proyectos, esto no es necesario; y hacer que su proyecto package.jsonencapsule estas herramientas y dependencias juntas es muy preferido.

Pero hoy en día encuentro que siempre estoy instalando create-react-appy otras CLI a nivel mundial cuando me subo a una nueva máquina. Es bueno tener una manera fácil de instalar una herramienta global y sus dependencias cuando el control de versiones no importa mucho.

Y hoy en día, estoy usando npx, un corredor paquete de NPM , en lugar de instalar los paquetes a nivel mundial.

Atav32
fuente
3

Todos los módulos de package.json están instalados en ./node_modules/

No pude encontrar esto explícitamente indicado, pero esta es la referencia de package.json para NPM .

nibblebot
fuente
1

Cree su propio script para instalar dependencias globales. No toma mucho. package.json es bastante ampliable.

const {execSync} = require('child_process');

JSON.parse(fs.readFileSync('package.json'))
     .globalDependencies.foreach(
         globaldep => execSync('npm i -g ' + globaldep)
     );

Usando lo anterior, ¡incluso puede hacerlo en línea, a continuación!

Mira la preinstalación a continuación:

{
  "name": "Project Name",
  "version": "0.1.0",
  "description": "Project Description",
  "main": "app.js",
  "scripts": {
    "preinstall": "node -e \"const {execSync} = require('child_process'); JSON.parse(fs.readFileSync('package.json')).globalDependencies.foreach(globaldep => execSync('npm i -g ' + globaldep));\"",
    "build": "your transpile/compile script",
    "start": "node app.js",
    "test": "./node_modules/.bin/mocha --reporter spec",
    "patch-release": "npm version patch && npm publish && git add . && git commit -m \"auto-commit\" && git push --follow-tags"
  },
  "dependencies": [
  },
  "globalDependencies": [
    "[email protected]",
    "ionic",
    "potato"
  ],
  "author": "author",
  "license": "MIT",
  "devDependencies": {
    "chai": "^4.2.0",
    "mocha": "^5.2.0"
  },
  "bin": {
    "app": "app.js"
  }
}

Los autores del nodo pueden no admitir package.json es un archivo de proyecto. Pero es.

TamusJRoyce
fuente