Este artículo debería servir como una guía esencial todo en uno para el compañero favorito de Node.js: npm.

Node.js ha estado tomando el mundo por asalto desde 2009. Cientos de miles de sistemas han sido construidos usando Node.js, lo que ha llevado a la comunidad de desarrolladores a afirmar que "JavaScript se está comiendo el software".

Uno de los principales factores del éxito de Node es npm - su popular gestor de paquetes, que permite a los desarrolladores de JavaScript compartir paquetes útiles como lodash y moment de forma rápida y fácil.

En el momento en que estoy escribiendo este post, ¡npm ha facilitado la publicación de más de 1.3 millones de paquetes con una tasa de descarga semanal de más de 16 mil millones! Estos números son fantásticos para cualquier herramienta de software. Así que ahora hablemos de lo que es npm exactamente.

¿Qué es NPM?

NPM – o "Node Package Manager" – es el administrador de paquetes predeterminado para el tiempo de ejecución de JavaScript Node.js.

También se conoce como "Ninja Pumpkin Mutants", "Nonprofit Pizza Makers", y una serie de otros nombres al azar que puedes explorar y probablemente puedas contribuir en npm-expansions.

NPM consiste de dos partes principales:

  • una herramienta CLI (interfaz de línea de comandos) para la publicación y descarga de paquetes, y
  • un repositorio en línea que alberga paquetes de JavaScript

Para una explicación más visual, podemos pensar en el repositorio npmjs.com como un centro de cumplimiento que recibe paquetes de bienes de los vendedores (autores de paquetes npm) y los distribuye a los compradores (usuarios de paquetes npm).

Para facilitar este proceso, el centro de cumplimiento npmjs.com emplea un ejército de trabajadores wombats (npm CLI) que serán asignados como asistentes personales a cada cliente individual de npmjs.com. Así que las dependencias se entregan a los desarrolladores de JavaScript de esta manera:

y el proceso de publicar un paquete para tus compañeros de JS sería algo así:

Veamos cómo este ejército de wombats ayuda a los desarrolladores que quieren usar paquetes de JavaScript en sus proyectos. También veremos cómo ayudan a los magos del código abierto a sacar sus geniales librerías al mundo.

package.json

Cada proyecto en JavaScript – ya sea Node.js o una aplicación de navegador – puede ser enfocado como un paquete npm con su propia información de paquete y su archivo package.json para describir el proyecto.

Podemos pensar en package.json como etiquetas estampadas en esas buenas cajas de npm que nuestro ejército de Wombats entrega.

package.json se generará cuando se ejecute npm init para inicializar un proyecto JavaScript/Node.js, con estos metadatos básicos proporcionados por los desarrolladores:

  • name: el nombre de tu librería/proyecto JavaScript
  • version: la versión de tu proyecto. A menudo, para el desarrollo de aplicaciones, este campo es a menudo descuidado ya que no hay necesidad aparente de versionar librerías de código abierto. Pero aún así, puede ser útil como fuente de la versión del despliegue
  • description: la descripción del proyecto
  • license: la licencia del proyecto

npm scripts

package.json también soporta la propiedad scripts que puede definirse para ejecutar herramientas de línea de comandos que se instalan en el contexto local del proyecto. Por ejemplo, la porción de scripts de un proyecto npm puede tener un aspecto similar a este:

{
  "scripts": {
    "build": "tsc",
    "format": "prettier --write **/*.ts",
    "format-check": "prettier --check **/*.ts",
    "lint": "eslint src/**/*.ts",
    "pack": "ncc build",
    "test": "jest",
    "all": "npm run build && npm run format && npm run lint && npm run pack && npm test"
  }
}

con eslint, prettier, ncc, jest no necesariamente instalados como ejecutables globales sino como locales de tu proyecto dentro de node_modules/.bin/.

La reciente introducción de npx nos permite ejecutar estos comandos node_modules del proyecto al igual que un programa instalado globalmente usando el prefijo npx ... (es decir, `npx prettier --write **/*.ts).

dependencies vs devDependencies

Estos dos vienen en forma de objetos clave-valor (key-value) con los nombre de las librerías npm como clave y sus versiones en formato semántico como valor. Este es un ejemplo de la plantilla de Github's TypeScript Action:

{
  "dependencies": {
    "@actions/core": "^1.2.3",
    "@actions/github": "^2.1.1"
  },
  "devDependencies": {
    "@types/jest": "^25.1.4",
    "@types/node": "^13.9.0",
    "@typescript-eslint/parser": "^2.22.0",
    "@zeit/ncc": "^0.21.1",
    "eslint": "^6.8.0",
    "eslint-plugin-github": "^3.4.1",
    "eslint-plugin-jest": "^23.8.2",
    "jest": "^25.1.0",
    "jest-circus": "^25.1.0",
    "js-yaml": "^3.13.1",
    "prettier": "^1.19.1",
    "ts-jest": "^25.2.1",
    "typescript": "^3.8.3"
  }
}

Estas dependencias se instalan mediante el comando npm install con las banderas --save y --save-dev. Están pensadas para ser usadas en entornos de producción y desarrollo/prueba respectivamente. Profundizaremos en la instalación de estos paquetes en la siguiente sección.

Mientras tanto, es importante entender los posibles signos que vienen antes de las versiones semánticas (suponiendo que hayas leído el modelo major.minor.patch (mayor.menor.parche) de semver):

  • ^: último lanzamiento menor. Por ejemplo, una especificación ^1.0.4 podría instalar la versión 1.3.0 si es la última versión menor de la serie mayor 1.
  • ~: última versión del parche. De la misma manera que ^ para las versiones menores, la especificación ~1.0.4 podría instalar la versión 1.0.7 si es la última versión menor de la serie menor 1.0.

Todas estas versiones exactas del paquete se documentarán un archivo generado package-lock.json.

package-lock.json

Este archivo describe las versiones exactas de las dependencias utilizadas en un proyecto de JavaScript npm. Si package.json es una etiqueta descriptiva genérica, package-lock.kson es una tabla de ingredientes.

Y al igual que no solemos leer la tabla de ingredientes de un producto (a menos que estés demasiado aburrido o necesites saberlo), package-lock.json no está pensado para ser leído línea por línea por los desarrolladores (a menos que estemos desesperados por resolver problemas de "funciona en mi máquina").

package-lock.json es usualmente generado por el comando npm install, y también es leído por nuestra herramienta NPM CLI para asegurar la reproducción de los entornos de construcción para el proyecto con npm ci.

Cómo ordenar efectivamente a NPM Wombats como un "comprador"

Como se deduce de los 1.3 millones de paquetes publicados frente a los 16,000 millones de descargas mencionadas anteriormente, la mayoría de los usuarios de npm utilizan npm en esta dirección. Así que es bueno saber cómo manejar esta poderosa herramienta.

npm install

Este es el comando más utilizado en el desarrollo de aplicaciones JavaScript/Node.js hoy en día.

Por defecto, npm install <nombre del paquete> instalará la última versión con el signo ^. Un npm install dentro del contexto de un proyecto npm descargará los paquetes en la carpeta node_modules del proyecto según las especificaciones de package.json, actualizando la versión del paquete (y a su vez regenerando package-lock.json) donde pueda basándose en la coincidencia de las versiones ^ y ~.

Puedes especificar una bandera global -g si deseas instalar un paquete en el contexto global que puedes utilizar en cualquier lugar de tu máquina (esto es común para los paquetes de herramientas de línea de comandos como live-server).

npm ha hecho la instalación de los paquetes de JavaScript tan fácil que este comando se utiliza a menudo de forma incorrecta. Esto resulta en que npm es el blanco de muchas bromas de programadores como estas:

¡Aquí es donde la bandera --production viene al rescate! En la sección anterior, discutimos las dependencies y devDependencies destinadas a ser usadas en entornos de producción y desarrollo/prueba respectivamente. Esta bandera de --production es la forma en que se hacen las diferencias en node_modules.

Al adjuntar esta bandera al comando npm install, sólo instalaremos paquetes de dependencies, reduciendo así drásticamente el tamaño de nuestros node_modules a lo que sea absolutamente necesario para que nuestras aplicaciones estén en funcionamiento.

Al igual que cuando los niños y niñas exploradoras no trajeron exprimidores de limón a nuestra cabina de limonada, ¡No deberíamos llevar las devDependencies a producción!

npm ci

Así que si npm install --production es óptimo para un entorno de producción, ¿Debe haber un comando que sea óptimo para mi desarrollo local, probando la configuración?

La respuesta es npm ci.

Al igual que si package-lock.json no existe ya en el proyecto que se genera cada vez que se llama npm install, npm ci consume este archivo para descargar la versión exacta de cada paquete individual del que depende el proyecto.

Así es como podemos asegurarnos de que el contexto de nuestro proyecto se mantiene exactamente igual en las diferentes máquina, ya sean nuestras laptops utilizados para el desarrollo o CI (Integración Continua) como GitHub Actions.

npm audit

Con el enorme número de paquetes que se han publicado y que pueden ser fácilmente instalados, los paquetes npm son susceptibles a los malos autores con intenciones maliciosas.

Al darse cuenta de que había un problema en el ecosistema, la organización npm.js tuvo la idea de npm audit. Mantienen una lista de lagunas de seguridad para que los desarrolladores puedan auditar sus dependencias con el uso del comando npm audit.

npm audit da a los desarrolladores información sobre las vulnerabilidades y si hay versiones con correcciones a las que actualizar. Por ejemplo,

Si las correcciones están disponibles en las próximas actualizaciones de versiones que no sean de última hora, se puede utilizar npm audit para actualizar automáticamente las versiones de las dependencias afectadas.

Cómo ordenar efectivamente a NPM Wombats como un "vendedor"

Hemos repasado cómo manejar la herramienta NPM CLI como consumidor, pero ¿Qué tal si la usamos efectivamente como autor (y potencialmente nos convertimos en un mago del código abierto de JavaScript)?

npm publish

Enviar un paquete a nuestro centro de cumplimiento de npmjs.com es súper fácil ya que sólo tenemos que ejecutar npm publish. La parte difícil, que no es específica de los autores de los paquetes de npm, es determinar la versión del paquete.

La regla empírica según semver.org:

  1. Versión MAYOR cuando haces cambios incompatibles en la API,
  2. Versión MENOR cuando se agrega la funcionalidad de una manera compatible con el pasado, y
  3. Versión PARCHE cuando haces correcciones de errores compatibles con el pasado.

Es aún más importante seguir la regla anterior cuando publiques tus paquetes para asegurarte de que no estás rompiendo el código de nadie ya que la versión por defecto que coincide en npm es ^ (también conocida como la siguiente versión menor).

❤️ npm ❤️ JavaScript ❤️ Node.js ❤️

¡Eso es todo lo que necesitamos saber para empezar a manejar npm efectivamente y ordenar nuestro encantador ejército de wombats!

Traducido del artículo de Stanley Nguyen - What is npm? A Node Package Manager Tutorial for Beginners