¿Por qué "npm install" reescribe package-lock.json?

614

Recientemente actualicé a npm @ 5 . Ahora tengo un archivo package-lock.json con todo, desde package.json . Esperaría eso, cuando ejecuto npm installque las versiones de dependencia se extraerían del archivo de bloqueo para determinar qué debería instalarse en mi directorio node_modules . Lo extraño es que en realidad termina modificando y reescribiendo mi archivo package-lock.json .

Por ejemplo, el archivo de bloqueo tenía un mecanografiado especificado en la versión 2.1.6 . Luego, después del npm installcomando, la versión se cambió a 2.4.1 . Eso parece anular todo el propósito de un archivo de bloqueo.

¿Qué me estoy perdiendo? ¿Cómo consigo que npm respete realmente mi archivo de bloqueo?

Viper Bailey
fuente
44
Esto no responde a su pregunta, así que espero que un comentario esté bien, pero eche un vistazo a Yarn. Cambiar nos llevó menos de una hora.
KayakinKoder
44
El mismo problema pero usando hilo github.com/yarnpkg/yarn/issues/570 (muy instructivo)
Yves M.
2
Tengo el mismo problema. Mi package-lock.jsonse regenera cuando corro npm install. Esto huele a un error npm. ¿Utiliza su propio registro?
HaNdTriX
@YvesM. --no-saveevita cambiar el archivo de bloqueo, pero no afecta a la tonta actualización de dependencia de primer nivel que menciona el OP.
Ross Allen

Respuestas:

423

Actualización 3: Como también señalan otras respuestas, el npm cicomando se introdujo en npm 5.7.0 como una forma adicional de lograr compilaciones rápidas y reproducibles en el contexto de CI. Consulte la documentación y el blog de npm para obtener más información.


Actualización 2: El problema para actualizar y aclarar la documentación es el problema # 18103 de GitHub .


Actualización 1: El comportamiento que se describe a continuación se solucionó en npm 5.4.2: el comportamiento previsto actualmente se describe en el problema de GitHub # 17979 .


Respuesta original: El comportamiento de package-lock.jsonse modificó en npm 5.1.0 como se discutió en el número 16866 . Al parecer, el comportamiento que observa es intencionado por npm a partir de la versión 5.1.0.

Eso significa que package.jsonpuede anularse package-lock.jsonsiempre que se encuentre una versión más nueva para una dependencia en package.json. Si desea fijar sus dependencias de manera efectiva, ahora debe especificar las versiones sin un prefijo, por ejemplo, debe escribirlas como en 1.2.0lugar de ~1.2.0o ^1.2.0. Luego, la combinación de package.jsony package-lock.jsonproducirá compilaciones reproducibles. Para ser claros: ¡ package-lock.jsonsolo ya no bloquea las dependencias de nivel raíz!

Si esta decisión de diseño fue buena o no es discutible, hay una discusión en curso como resultado de esta confusión en GitHub en el número 17979 . (En mi opinión, es una decisión cuestionable; al menos el nombre lockya no es cierto).

Una nota adicional: también hay una restricción para los registros que no admiten paquetes inmutables, como cuando extrae paquetes directamente desde GitHub en lugar de npmjs.org. Consulte esta documentación de bloqueos de paquetes para obtener más explicaciones.

jotaen
fuente
43
¿Para qué sirve el truco npm updateentonces? : o Tengo la misma sensación de que npm installactualicé los deps, pero no quiero creerlo ... pero parece que es tristemente cierto ... De todos modos, todavía hay una opción npm shrinkwrappara bloquear los deps, pero definitivamente el nombre de package-lock es incorrecto ya que no se congela, ni bloquea las dependencias ..
Jurosh
266
¡Que desastre! El administrador de paquetes más grande del mundo todavía no tiene documentación sobre cómo debería funcionar. Todos están adivinando qué debería hacer y se convierte en una guerra de opiniones. La discusión es buena, pero debe ocurrir antes de una liberación en la naturaleza. En algún momento, alguien necesita hacer la llamada final y luego puede implementarse, documentarse y liberarse. PHP fue diseñado por el comité y ad-hoc'd juntos y mira cómo resultó. Odio ver que le ocurra lo mismo a una herramienta tan crítica y ampliamente utilizada.
Landon Poch
85
Entonces, ¿cuál es el punto de usar package-lock? Pensé que crearía el mismo entorno en diferentes espacios de trabajo, pero resulta que no está haciendo nada
2017
17
"Entonces, la combinación de package.json y package-lock.json producirá compilaciones reproducibles". ¿Qué papel tiene "package-lock.json" aquí? ¿"Package.json" solo ya no produce compilaciones reproducibles si no se utilizan prefijos de versión?
Jānis Elmeris
12
@ JānisElmeris Creo que package.json no puede bloquear dependencias profundas ...
Juan Mendes
165

Descubrí que habrá una nueva versión de npm 5.7.1 con el nuevo comando npm ci, que se instalará package-lock.jsonsolo desde

El nuevo comando npm ci se instala desde su archivo de bloqueo SOLAMENTE. Si su package.json y su archivo de bloqueo no están sincronizados, informará un error.

Funciona desechando sus node_modules y recreándolos desde cero.

Más allá de garantizarle que solo obtendrá lo que está en su archivo de bloqueo, también es mucho más rápido (¡2x-10x!) Que la instalación de npm cuando no comienza con un node_modules.

Como se puede deducir del nombre, esperamos que sea de gran ayuda para los entornos de integración continua. También esperamos que las personas que realizan implementaciones de producción a partir de etiquetas git vean grandes ganancias.

Ivan Shcherbakov
fuente
133
Este debería ser el comportamiento predeterminado si existe un archivo de bloqueo.
nulabilidad
13
Entonces, ¿cambiaron la forma en que funciona npm, solo para devolverlo como npm ci meses después?
Scott Flack
1
Todavía estoy confundido. La documentación dice "Asegúrese de tener un paquete bloqueado y una instalación actualizada: npm install" antes de ejecutar el comando npm cien ese proyecto. ¿No npm installsobrescribe el archivo package-lock.json?
adiga
1
AFAIK: @adiga: a partir de la versión 5.4, npm solo cambia el archivo de bloqueo si es necesario para cumplir con la especificación en packages.json . Entonces, si los paquetes solían decir thatpackage: 1, y lock dice ..: 1.0.4, dev puede editar para decir thatpackage: 2, y eso obligará a cambiar el archivo de bloqueo, porque 1.0.4no es compatible con el rango recientemente especificado. Si no cambia packages.json, permanecerá bloqueado en la versión exacta, hasta que elimine el archivo de bloqueo. [Si no permanece bloqueado y no cambió los paquetes.json, presente un informe de error.]
ToolmakerSteve
1
@George De la información que he leído (para versiones recientes de npm) y mis pruebas limitadas: sí a ambas.
Venryx
95

Use el recién introducido

npm ci

npm ci promete el mayor beneficio para los equipos grandes. Brindar a los desarrolladores la capacidad de "cerrar sesión" en un bloqueo de paquete promueve una colaboración más eficiente en equipos grandes, y la capacidad de instalar exactamente lo que está en un archivo de bloqueo tiene el potencial de ahorrar decenas, si no cientos de horas de desarrollador al mes, liberando equipos para pasar más tiempo construyendo y enviando cosas increíbles.

Presentamos npm ciconstrucciones más rápidas y confiables

Gal Margalit
fuente
3
esto me parece correcto? ¿Alguien más puede confirmar?
phouse512
66
@ phouse512 Esto es correcto. Prácticamente solo usamos npm ci, y solo usamos npm installsi actualizamos o instalamos nuevos paquetes.
Jacob Sievers
1
Comentarios recientes, etc. Esta es la respuesta con la que voy. Lástima que no pudieron arreglar el horrible problema, pero si el nuevo evangelio es "npm ci", entonces está bien. Me puedo adaptar.
Svend
Lástima que siempre elimine un node_modulesdirectorio existente y se reconstruya localmente, incluso si ese es un enlace simbólico vacío pero importante. :(
Joe Atzberger
2
@ToolmakerSteve ¡No contengas la respiración! Creo que eliminar el contenido de un directorio sería una magnitud más lenta que simplemente eliminar el directorio. Tendría que enumerar el contenido y luego emitir una serie de comandos de eliminación en lugar de solo el comando de eliminación para el O / S. Con los problemas de rendimiento previamente nivelados en npm y la mejora en el uso npm ci, espero que sean muy reacios a introducir cualquier cosa que pueda reducir el rendimiento para un caso de uso bastante poco común. Es posible que desee consultar pnpm.js.org, aunque eso hace uso de enlaces duros para reducir el uso del disco.
Caltor
64

Respuesta corta:

  • npm install honra package-lock.json solo si cumple los requisitos de package.json.
  • Si no cumple con esos requisitos, los paquetes se actualizan y se sobrescribe el bloqueo de paquetes.
  • Si prefiere fallar la compilación, que reescribir el bloqueo del paquete cuando eso suceda, use npm ci.

Aquí hay un escenario que podría explicar cosas (verificado con NPM 6.3.0)

Declaras una dependencia en package.json como:

"depA": "^1.0.0"

Luego lo haces, npm installlo que generará un paquete-lock.json con:

"depA": "1.0.0"

Pocos días después, se lanza una versión menor más nueva de "depA", digamos "1.1.0", luego lo siguiente es cierto:

npm ci       # respects only package-lock.json and installs 1.0.0

npm install  # also, respects the package-lock version and keeps 1.0.0 installed 
             # (i.e. when package-lock.json exists, it overrules package.json)

A continuación, actualice manualmente su package.json a:

"depA": "^1.1.0"

Luego vuelva a ejecutar:

npm ci      # will try to honor package-lock which says 1.0.0
            # but that does not satisfy package.json requirement of "^1.1.0" 
            # so it would throw an error 

npm install # installs "1.1.0" (as required by the updated package.json)
            # also rewrites package-lock.json version to "1.1.0"
            # (i.e. when package.json is modified, it overrules the package-lock.json)
Ahmad Abdelghany
fuente
44
Este es de hecho el comportamiento previsto de un archivo de "bloqueo". Aparentemente, no fue el caso con versiones anteriores de NPM.
Blockost el
1
Entonces, ¿cómo rastrea npm la última actualización de package.json? ¿Qué sucede cuando mueves tu package.json y package-lock.json a otra computadora? ¿Cómo sabe npm en la computadora nueva si package.lock es el original o si se ha actualizado, para decidir si necesita actualizar package-lock.json o no?
Lahiru Chandima
3
@LahiruChandima Realmente no realiza un seguimiento de las actualizaciones. npm installutilizará las versiones bloqueadas de a package-lock.jsonmenos que no satisfaga el package.jsonen cuyo caso instala package.json y reconstruye package-lock.json en consecuencia. Si ha cambiado su package.jsonde tal manera que el paquete de frenos existentes sigue reuniendo las actualizan package.jsonse seguirá utilizando quepackage-lock
Ahmad abdelghany
1
Si ya tiene un módulo en node_modules que cumple con los requisitos de package.json, entonces npm installno hace nada, independientemente de package-lock.json. Tenemos que actualizar explícitamente los paquetes incluso cuando hay actualizaciones disponibles que coinciden con el semver especificado en package.json. Al menos esa ha sido mi experiencia durante años.
carlin.scott
1
@ToolmakerSteve También era escéptico sobre el comportamiento que informó @ carlin.scott, pero lo probé y, de hecho, está en lo correcto. Si la versión que se encuentra dentro node_modulessatisface el rango package.jsony no hay ningún package-lock.jsonarchivo, npm no actualizará el módulo cuando se ejecute npm install. Supongo que está bien, ya que puede usar npm update(o npm-checkpara la última) para actualizar las dependencias, y este comportamiento es más rápido para el caso de alguien que simplemente agrega una entrada package.jsony no desea que los paquetes no relacionados se actualicen a la última que satisface el semverver rango.
Venryx
19

Use el npm cicomando en lugar de npm install.

"ci" significa "integración continua".

Instalará las dependencias del proyecto basadas en el archivo package-lock.json en lugar de las dependencias indulgentes del archivo package.json.

Producirá construcciones idénticas para tus compañeros de equipo y también es mucho más rápido.

Puede leer más al respecto en esta publicación de blog: https://blog.npmjs.org/post/171556855892/introducing-npm-ci-for-faster-more-reliable

Daniel Tonon
fuente
2
cise refiere a la "integración continua", como se menciona en los documentos y la publicación de blog que anuncia el comando: blog.npmjs.org/post/171556855892/…
Joe Atzberger
Gracias Joe He actualizado mi respuesta con el nombre correcto y vinculado a la publicación del blog. 😊 (para aquellos que leen esto, anteriormente dije que significa "instalación limpia")
Daniel Tonon
"Y también es mucho más rápido": eliminará la node_modulescarpeta y la volverá a crear desde cero. ¿Es realmente mucho más rápido? ¿ npm installEliminar node_modulescarpeta también?
izogfif
Creo que la velocidad proviene de que npm no necesita calcular qué paquetes descargar. Piense que npm installtiene que resolver todas las dependencias del paquete cuando se ejecuta. npm cies solo una lista de compras de "obtener estos módulos exactos".
Daniel Tonon
8

En el futuro, podrá usar un --from-lock-fileindicador (o similar) para instalar solo desde el package-lock.jsonsin modificarlo.

Esto será útil para entornos de CI, etc., donde las construcciones reproducibles son importantes.

Consulte https://github.com/npm/npm/issues/18286 para el seguimiento de la función.

Timothy Higinbottom
fuente
Lo dudo. ¿Cómo si las dependencias son diferentes para diferentes sistemas operativos, cómo puede forzar la instalación de algo que no funcionaría?
Yevgeniy Afanasyev
44
@YevgeniyAfanasyev En lugar de esa bandera, se implementó ya npm cique también maneja su pregunta.
spex
8

Parece que este problema se solucionó en npm v5.4.2

https://github.com/npm/npm/issues/17979

(Desplácese hasta el último comentario en el hilo)

Actualizar

Realmente arreglado en 5.6.0. Hubo un error multiplataforma en 5.4.2 que todavía causaba el problema.

https://github.com/npm/npm/issues/18712

Actualización 2

Vea mi respuesta aquí: https://stackoverflow.com/a/53680257/1611058

npm ci es el comando que debe usar al instalar proyectos existentes ahora.

Daniel Tonon
fuente
55
Estoy usando 5.4.2 y todavía está resultando en la modificación de mi paquete-lock.json cuando npm i. Por ejemplo, el módulo fseventsse elimina cuando estoy npm ien una máquina que no es compatible fseventsy luego el módulo se vuelve a agregar cuando se npm ivuelve a instalar en una máquina que sí.
hrdwdmrbl
Entonces deberías plantear un nuevo problema en el repositorio de npm GitHub explicando esto. Si no funciona como dicen que se supone que debe funcionar, lo ven como un error de alta prioridad que necesita solución urgente.
Daniel Tonon
@hrdwdmrbl que estoy viendo la misma fseventscaída en mi package-lock.jsoncon [email protected]la vez que colaboran con Mac OS X contribuyentes. Si no has abierto un problema, lo haré.
AL the X
@hrdwdmrbl Encontré eso (y el largo hilo de problemas asociados) después de dejar mi comentario y olvidé volver a SO para actualizar mi comentario. Gracias por apoyarme. Todo esta bien.
AL the X
4

Probablemente tengas algo como:

"typescript":"~2.1.6"

en package.jsonque npm se actualiza a la última versión menor, en su caso2.4.1

Editar: Pregunta de OP

Pero eso no explica por qué "npm install" cambiaría el archivo de bloqueo. ¿No se pretende que el archivo de bloqueo cree una compilación reproducible? Si es así, independientemente del valor de semver, aún debe usar la misma versión 2.1.6.

Responder:

Esto está destinado a bloquear su árbol de dependencia completa. Digamos que typescript v2.4.1requiere widget ~v1.0.0. Cuando npm lo instales agarra widget v1.0.0. Más tarde en su compañero de desarrollador (o acumulación CI) no instala un MNP y obtiene typescript v2.4.1, pero widgetse ha actualizado a widget v1.0.1. Ahora su módulo de nodo no está sincronizado. Esto es lo que package-lock.jsonimpide.

O más generalmente:

Como ejemplo, considere

paquete A:

{"nombre": "A", "versión": "0.1.0", "dependencias": {"B": "<0.1.0"}}

paquete B:

{"nombre": "B", "versión": "0.0.1", "dependencias": {"C": "<0.1.0"}}

y paquete C:

{"nombre": "C", "versión": "0.0.1"}

Si estas son las únicas versiones de A, B y C disponibles en el registro, se instalará una instalación normal de npm A:

[email protected] - [email protected] - [email protected]

Sin embargo, si se publica [email protected], se instalará una nueva instalación npm A:

[email protected] - [email protected] - [email protected] suponiendo que la nueva versión no modificó las dependencias de B. Por supuesto, la nueva versión de B podría incluir una nueva versión de C y cualquier cantidad de nuevas dependencias. Si dichos cambios no son deseables, el autor de A podría especificar una dependencia en [email protected]. Sin embargo, si el autor de A y el autor de B no son la misma persona, no hay forma de que el autor de A diga que él o ella no quiere obtener versiones recién publicadas de C cuando B no ha cambiado en absoluto.


OP Pregunta 2: Entonces déjame ver si entiendo correctamente. Lo que está diciendo es que el archivo de bloqueo especifica las versiones de las dependencias secundarias, pero aún se basa en la coincidencia aproximada de package.json para determinar las dependencias de nivel superior. ¿Es eso exacto?

Respuesta: No. package-lock bloquea todo el árbol de paquetes, incluidos los paquetes raíz descritos en package.json. Si typescriptestá bloqueado 2.4.1en su package-lock.json, debe permanecer así hasta que se cambie. Y digamos que mañana typescriptlanza la versión 2.4.2. Si npm installreviso su rama y ejecuto , npm respetará el archivo de bloqueo y la instalación 2.4.1.

Más sobre package-lock.json:

package-lock.json se genera automáticamente para cualquier operación en la que npm modifique el árbol node_modules o package.json. Describe el árbol exacto que se generó, de modo que las instalaciones posteriores pueden generar árboles idénticos, independientemente de las actualizaciones de dependencia intermedias.

Este archivo está destinado a ser confirmado en repositorios de origen y sirve para varios propósitos:

Describa una representación única de un árbol de dependencias de modo que se garantice que los compañeros de equipo, las implementaciones y la integración continua instalen exactamente las mismas dependencias.

Proporcione una facilidad para que los usuarios "viajen en el tiempo" a estados anteriores de node_modules sin tener que confirmar el directorio en sí.

Para facilitar una mayor visibilidad de los cambios de árbol a través de diferencias de control de fuente legible.

Y optimice el proceso de instalación permitiendo que npm omita las resoluciones de metadatos repetidos para paquetes instalados previamente.

https://docs.npmjs.com/files/package-lock.json

Mate
fuente
29
Pero eso no explica por qué "npm install" cambiaría el archivo de bloqueo. ¿No se pretende que el archivo de bloqueo cree una compilación reproducible? Si es así, independientemente del valor de semver, aún debe usar la misma versión 2.1.6.
Viper Bailey
3
Y eso es lo que digo. El archivo de bloqueo de mi paquete dice [email protected] pero cuando ejecuto npm install, la entrada se reemplaza por [email protected].
Viper Bailey
55
He experimentado este mismo problema. En nuestro CI / CD, package-lock.jsonse tira hacia abajo y luego lo ejecutamos npm install, pero el package-lock.jsonarchivo se modifica y tenemos que realizar un reinicio antes de que podamos sacar los siguientes cambios.
BayssMekanique
15
No lo entiendo ¿Cómo es esto un archivo de "bloqueo" si las instalaciones posteriores aún pueden hacer actualizaciones?
Ross Allen el
55
Creo que comenzaron con la idea de tener este archivo como "información" y "bloqueo" y luego, decidieron que sería solo un archivo de "información". Mejor nombre sería "package-info.json". Me encantaría tener un "npm install -lock" que se instalará desde "package-lock.json" e ignoraría "package.json"
Jeremy Chone
2

Probablemente deberías usar algo como esto

npm ci

En lugar de usarlo npm install si no desea cambiar la versión de su paquete.

Según la documentación oficial, ambos npm installe npm ciinstalan las dependencias que se necesitan para el proyecto.

La principal diferencia es, npm installinstala los paquetes tomando packge.jsoncomo referencia. Donde, en el caso de npm ci, instala los paquetes tomando package-lock.jsoncomo referencia, asegurándose cada vez que se instala el paquete exacto.

Sengottaian Karthik
fuente
1

Hay un problema abierto para esto en su página de github: https://github.com/npm/npm/issues/18712

Este problema es más grave cuando los desarrolladores utilizan diferentes sistemas operativos.

hrdwdmrbl
fuente
Las reescrituras en el paquete de bloqueo están destinadas, el problema no es una consecuencia de esto
Z. Khullah
0

EDITAR: el nombre "bloqueo" es complicado, su NPM intenta ponerse al día con Yarn. No es un archivo bloqueado en absoluto. package.jsones un archivo fijo por el usuario, que una vez "instalado" generará el árbol de carpetas node_modules y ese árbol se escribirá en él package-lock.json. Como puede ver, es al revés: las versiones de dependencia se extraerán package.jsoncomo siempre, y package-lock.jsondeberían llamarsepackage-tree.json

(Espero que esto haya aclarado mi respuesta, después de tantos votos negativos)


Una respuesta simplista: package.jsontenga sus dependencias como de costumbre, mientras que package-lock.jsones "un árbol node_modules exacto y más importante reproducible" (tomado de npm docs ).

En cuanto al nombre complicado, su NPM intenta ponerse al día con Yarn.

Z. Khullah
fuente
1
Porque si ejecuta npm install, se actualizará package-lock.
Jean-Baptiste