Todo lo que quería saber sobre package-lock.json pero tenía demasiado miedo de preguntar

También puedes leer esta historia en mi blog personal de Gatsby.

Introducción

Así que actualizó Node Package Manager (npm) a v5.x.x, y todo parece ir bien. Pero espera, ¿qué es esto? Se creó un nuevo archivo automáticamente. Package-lock.json. Si lo abre, se parece a las dependencias en package.json, pero más detallado. Decides ignorarlo y seguir tu camino desarrollando tu proyecto. Eventualmente, tienes problemas con una dependencia. No se puede encontrar o la versión incorrecta parece estar instalada. La mayoría de las personas terminan eliminando el paquete-lock.json y ejecutando `npm install`. Entonces, ¿por qué incluso tenerlo? ¿Qué se supone que haga? ¿Qué es lo que hace?

Resumen

  • Si usa npm ^ 5.x.x, de forma predeterminada se generará un paquete-lock.json para usted
  • Debe usar el bloqueo de paquete para garantizar una instalación coherente y dependencias compatibles
  • DEBE comprometer su paquete de bloqueo al control de origen
  • A partir de npm ^ 5.1.x, package.json ahora puede superar a package-lock.json, por lo que debería experimentar mucho menos dolor de cabeza
  • No más eliminar ese bloqueo de paquete solo para ejecutar `npm install` y regenerarlo
  • Use semver si su aplicación ofrece una API y cumpla con las reglas de semver.

Fondo

Versiones Semánticas

Antes de que pueda comprender el bloqueo de paquete e incluso package.json, debe comprender el control de versiones semántico (semver). Es el genio detrás de npm, y lo que lo ha hecho más exitoso. Puede leer más sobre cómo npm lo usa aquí. En pocas palabras, si está creando una aplicación con la que otras aplicaciones interactúan, debe comunicar cómo los cambios que realice afectarán la capacidad del tercero para interactuar con su aplicación. Esto se hace a través de versiones semánticas. Una versión se compone de tres partes: X, Y, Z, donde son versiones mayor, menor y parche respectivamente. Un ejemplo sería 1.2.3, o versión principal 1, versión secundaria 2, parche 3. Un cambio en el parche representa una corrección de errores que no rompe nada. Un cambio en la versión menor representa una nueva funcionalidad que no rompe nada. Un cambio en la versión principal representa un gran cambio que rompe la compatibilidad. Si los usuarios no se adaptan a un cambio importante de versión, las cosas no funcionarán.

Administrar paquetes

npm existe para facilitar la administración de paquetes. Sus proyectos pueden tener cientos de dependencias, cada una con cientos de otras. Para mantener su mente alejada del infierno de las dependencias, npm fue creado para que con algunos comandos simples, usted pudiera instalar y administrar estas dependencias y casi nunca tenga que pensar en ellas.

Cuando instala un paquete con npm (y lo guarda), se agrega una entrada a su package.json que contiene el nombre del paquete y el semver que se debe usar. Sin embargo, npm admite algunos comodines en esta definición de semver. Por defecto, npm instala la última versión, y antepone un cursor, p. "^ 1.2.12". Esto significa que, como mínimo, debe usarse la versión 1.2.12, pero cualquier versión superior a esa está bien, siempre que tenga la misma versión principal, 1. Dado que los números menores y los parches solo representan correcciones de errores y adiciones sin interrupciones, debería estar seguro de usar cualquier versión superior de la misma versión principal. Lea más sobre los comodines de semver y juegue con la genial calculadora de semver de npm aquí.

Proyectos compartidos

El beneficio real de tener dependencias definidas de esta manera en package.json, es que cualquiera que tenga acceso al paquete.json podría crear una carpeta de dependencia que contenga los módulos necesarios para ejecutar su aplicación. Pero echemos un vistazo a una forma específica en que las cosas pueden salir mal.

Digamos que creamos un nuevo proyecto que usará express. Después de ejecutar `npm init`, instalamos express:` npm install express - save`. Al momento de escribir, la última versión express es 4.15.4. Entonces "express": "^ 4.15.4" se agrega como una dependencia dentro de mi package.json y esa versión exacta está instalada en mi máquina. Ahora tal vez mañana, los encargados del mantenimiento de Express lanzan una corrección de errores, por lo que la última versión se convierte en 4.15.5. Entonces, si alguien quisiera contribuir a mi proyecto, lo clonarían y ejecutarían 'npm install'. Dado que 4.15.5 es una versión superior con la misma versión principal, está instalada para ellos. Ambos tenemos express, pero tenemos dos versiones diferentes. Teóricamente, aún deberían ser compatibles, pero tal vez esa corrección de errores afectó la funcionalidad que estamos usando, y nuestra aplicación produciría resultados diferentes cuando se ejecuta con la versión express 4.15.4 en comparación con 4.15.5.

Paquete de bloqueo

La meta

El propósito del bloqueo de paquetes es evitar la situación descrita anteriormente, donde instalar módulos desde el mismo paquete.json da como resultado dos instalaciones diferentes. Package-lock.json se agregó en npm versión 5.x.x, por lo que si está utilizando la versión principal 5 o superior, la verá generada a menos que la desactive.

El formato

Package-lock es una lista grande de cada dependencia incluida en su package.json, la versión específica que debe instalarse, la ubicación del módulo (URI), un hash que verifica la integridad del módulo, la lista de paquetes que requiere y una lista de dependencias. Echemos un vistazo a cuál es la entrada para express:

"express": {
      "versión": "4.15.4",
      "resuelto": "https://registry.npmjs.org/express/-/express-4.15.4.tgz",
      "integridad": "sha1-Ay4iU0ic + PzgJma + yj0R7XotrtE =",
      "requiere": {
        "acepta": "1.3.3",
        "array-flatten": "1.1.1",
        "disposición de contenido": "0.5.2",
        "content-type": "1.0.2",
        "cookie": "0.3.1",
        "cookie-signature": "1.0.6",
        "debug": "2.6.8",
        "depd": "1.1.1",
        "encodeurl": "1.0.1",
        "escape-html": "1.0.3",
        "etag": "1.8.0",
        "finalhandler": "1.0.4",
        "fresh": "0.5.0",
        "merge-descriptors": "1.0.1",
        "métodos": "1.1.2",
        "al terminar": "2.3.0",
        "parseurl": "1.3.1",
        "path-to-regexp": "0.1.7",
        "proxy-addr": "1.1.5",
        "qs": "6.5.0",
        "range-parser": "1.2.0",
        "enviar": "0.15.4",
        "serve-static": "1.12.4",
        "setprototypeof": "1.0.3",
        "status": "1.3.1",
        "type-is": "1.6.15",
        "utils-merge": "1.0.0",
        "variar": "1.1.1"
      }
    },

Se pueden encontrar entradas equivalentes para cada paquete enumerado en la sección "requiere".

La idea es que, en lugar de usar package.json para resolver e instalar módulos, npm usará package-lock.json. Debido a que el bloqueo de paquete especifica una versión, ubicación y hash de integridad para cada módulo y cada una de sus dependencias, la instalación que crea será la misma, cada vez. No importa en qué dispositivo se encuentre, o cuando lo instale en el futuro, debería brindarle el mismo resultado cada vez, lo cual es muy útil.

La controversia

Entonces, si se supone que el bloqueo de paquetes resuelve un problema común, ¿por qué los principales resultados de búsqueda (aparte de la documentación de npm) se tratan de deshabilitarlo o cuestionar el papel que desempeña?

Antes de npm 5.x.x, package.json era la fuente de verdad para un proyecto. Lo que vivía en package.json era la ley. A los usuarios de npm les gustó este modelo y se acostumbraron mucho a mantener su archivo de paquete. Sin embargo, cuando se introdujo el bloqueo de paquetes por primera vez, actuó en contra de la cantidad de personas que esperaban. Dado un paquete y un bloqueo de paquete preexistentes, un cambio en package.json (lo que muchos usuarios consideraron la fuente de la verdad) no se reflejó en el bloqueo de paquete.

Ejemplo: el paquete A, la versión 1.0.0 está en el paquete y el bloqueo del paquete. En package.json, A se edita manualmente a la versión 1.1.0. Si un usuario que considera que package.json es la fuente de la verdad ejecuta `npm install`, esperaría que se instale la versión 1.1.0. Sin embargo, la versión 1.0.0 está instalada, a pesar de que v1.1.0 aparece en la lista es package.json.

Ejemplo: un módulo no existe en el paquete de bloqueo, pero existe en el paquete.json. Como usuario que busca package.json como la fuente de la verdad, esperaría que mi módulo esté instalado. Sin embargo, dado que el módulo no está presente en el bloqueo de paquete, no está instalado y mi código falla porque no puede encontrar el módulo.

La mayoría de las veces, debido a que no podían entender por qué sus dependencias no se instalaban correctamente, los usuarios eliminaban el bloqueo de paquete y lo reinstalaban, o desactivaban el bloqueo de paquete por completo.

Este conflicto entre esperar y el comportamiento real provocó un tema muy interesante en el repositorio npm. Algunas personas pensaron que package.json debería ser la fuente de la verdad, algunas personas pensaron que dado que package-lock es lo que npm usa para crear la instalación, esa debería considerarse la fuente de la verdad. La resolución de esta controversia se encuentra en npm PR # 17508. Los mantenedores de Npm agregaron un cambio que hace que package.json anule el bloqueo de paquete si package.json ha sido actualizado. Ahora, en los dos escenarios anteriores, los paquetes que un usuario esperaría instalar están instalados correctamente. Este cambio se lanzó como parte de npm v5.1.0, que entró en vigencia el 5 de julio de 2017.