Por que o “npm install” reescreve o package-lock.json?

614

Recentemente, atualizei para npm @ 5 . Agora tenho um arquivo package-lock.json com tudo, desde package.json . Eu esperava que, ao executar, npm installas versões de dependência fossem extraídas do arquivo de bloqueio para determinar o que deveria ser instalado no meu diretório node_modules . O estranho é que ele acaba modificando e reescrevendo meu arquivo package-lock.json .

Por exemplo, o arquivo de bloqueio tinha o texto datilografado especificado para a versão 2.1.6 . Depois do npm installcomando, a versão foi alterada para 2.4.1 . Isso parece derrotar todo o objetivo de um arquivo de bloqueio.

o que estou perdendo? Como faço para que o npm respeite meu arquivo de bloqueio?

Viper Bailey
fonte
4
Isso não responde à sua pergunta, então espero que um comentário seja aceitável, mas dê uma olhada no Yarn. A troca levou menos de uma hora para nós.
KayakinKoder
4
O mesmo problema, mas utilizando fios github.com/yarnpkg/yarn/issues/570 (muito instrutivo)
Yves M.
2
Estou tendo o mesmo problema. Meu package-lock.jsoné regenerado quando eu corro npm install. Isso cheira a um erro npm. Você usa seu próprio registro?
HaNdTriX
@YvesM. --no-saveimpede a alteração do arquivo de bloqueio, mas não afeta a atualização de dependência de primeiro nível que o OP menciona.
Ross Allen

Respostas:

423

Atualização 3: Como outras respostas também apontam, o npm cicomando foi introduzido na npm 5.7.0 como forma adicional de obter compilações rápidas e reproduzíveis no contexto do IC. Consulte a documentação e o blog do npm para obter mais informações.


Atualização 2: O problema para atualizar e esclarecer a documentação é o problema no GitHub # 18103 .


Atualização 1: O comportamento descrito abaixo foi corrigido na npm 5.4.2: o comportamento pretendido atualmente é descrito na edição # 17979 do GitHub .


Resposta original: o comportamento de package-lock.jsonfoi alterado na npm 5.1.0, conforme discutido na edição # 16866 . O comportamento que você observa é aparentemente planejado pelo npm a partir da versão 5.1.0.

Isso significa que package.jsonpode substituir package-lock.jsonsempre que uma versão mais recente for encontrada para uma dependência no package.json. Se você deseja fixar suas dependências efetivamente, agora deve especificar as versões sem um prefixo; por exemplo, você precisa escrevê-las como em 1.2.0vez de ~1.2.0ou ^1.2.0. Em seguida, a combinação de package.jsone package-lock.jsonproduzirá construções reproduzíveis. Para ficar claro: package-lock.jsonsozinho já não bloqueia as dependências no nível raiz!

Se essa decisão de projeto foi boa ou não, é discutível, há uma discussão em andamento resultante dessa confusão no GitHub na edição # 17979 . (Aos meus olhos, é uma decisão questionável; pelo menos o nome locknão se aplica mais.)

Mais uma observação: há também uma restrição para registros que não suportam pacotes imutáveis, como quando você extrai pacotes diretamente do GitHub em vez de npmjs.org. Consulte esta documentação dos bloqueios de pacotes para obter mais explicações.

jotaen
fonte
43
Para que serve o hack npm update? : o Eu tive o mesmo pressentimento que os npm installdeps atualizados, mas não quero acreditar ... mas parece que é verdade. De qualquer forma, ainda há uma opção a ser usada npm shrinkwrappara bloquear deps, mas definitivamente o nome do pacote-lock está incorreto uma vez que não congela, nem dependências de bloqueio ..
Jurosh
266
Que bagunça! O maior gerenciador de pacotes do mundo ainda não possui documentação sobre como deve funcionar. Todo mundo está adivinhando o que deve fazer e isso se transforma em uma guerra de opiniões. A discussão é boa, mas deve ocorrer antes do lançamento na natureza. Em algum momento, alguém precisa fazer a chamada final e, em seguida, pode ser implementado, documentado e liberado. O PHP foi projetado pelo comitê e ad-hoc'd juntos e veja como ficou. Eu odiaria ver o mesmo acontecer com uma ferramenta tão crítica e amplamente usada.
Landon Poch
85
Então, qual é o sentido de usar o bloqueio de pacote? Eu pensei que iria criar o mesmo ambiente em diferentes áreas de trabalho, mas Acontece que ele é não fazer nada
laltin
17
"Então a combinação de package.json e package-lock.json produzirá construções reproduzíveis." Qual o papel do "package-lock.json" aqui? Só o "package.json" já não gera compilações reproduzíveis se nenhum prefixo de versão for usado?
Jānis Elmeris 27/02
12
@ JānisElmeris Acho package.json não pode bloquear dependências profundas ...
Juan Mendes
165

Eu descobri que haverá uma nova versão do npm 5.7.1 com o novo comando npm ci, que será instalado package-lock.jsonapenas

O novo comando npm ci é instalado somente a partir do seu arquivo de bloqueio. Se seu package.json e seu arquivo de bloqueio estiverem fora de sincronia, ele reportará um erro.

Ele funciona jogando fora o node_modules e recriando-o do zero.

Além de garantir que você obterá apenas o que está no seu arquivo de bloqueio, também é muito mais rápido (2x-10x!) Que a instalação do npm quando você não inicia com um node_modules.

Como você pode imaginar, esperamos que seja um grande benefício para ambientes de integração contínua. Também esperamos que as pessoas que fazem implantações de produção com tags git tenham grandes ganhos.

Ivan Shcherbakov
fonte
133
Esse deve ser o comportamento padrão se existir um arquivo de bloqueio.
nullability
13
Então eles mudaram como o npm i funciona, apenas para trazê-lo de volta como npm ci meses depois?
Scott Flack
1
Ainda estou confuso. A documentação diz "Verifique se você possui um bloqueio de pacote e uma instalação atualizada: npm install" antes de executar o comando npm cinesse projeto. Não npm installsubstitui o arquivo package-lock.json?
adiga 22/06/19
1
AFAIK: @adiga - a partir da versão 5.4, npm apenas altera o arquivo de bloqueio, se necessário, para atender às especificações em packages.json . Portanto, se os pacotes costumavam dizer thatpackage: 1, e lock diz ..: 1.0.4, o dev pode editar para dizer thatpackage: 2- e isso forçará a alteração do arquivo de bloqueio, porque 1.0.4não é compatível com o novo intervalo especificado. Se não mudar packages.json, permanecerá bloqueado na versão exata, até excluir o arquivo de bloqueio. [Se não permanecer bloqueado e não alterar o packages.json, envie um relatório de bug.] #
ToolmakerSteve #
1
@ George A partir das informações que li (para versões recentes do npm), e meus testes limitados: sim para ambos.
Venryx 19/11/19
95

Use o recém-introduzido

npm ci

O npm ci promete o maior benefício para grandes equipes. Oferecer aos desenvolvedores a capacidade de "assinar" em um bloqueio de pacote promove uma colaboração mais eficiente entre grandes equipes, e a capacidade de instalar exatamente o que está em um arquivo de bloqueio tem o potencial de economizar dezenas, senão centenas de horas de desenvolvedor por mês, liberando equipes para gastar mais tempo construindo e enviando coisas incríveis.

Apresentando construções npm cimais rápidas e confiáveis

Gal Margalit
fonte
3
isso parece estar correto para mim? mais alguém pode confirmar?
phouse512
6
@ phouse512 Isso está correto. Nós apenas usamos npm cie npm installatualizamos ou instalamos novos pacotes.
Jacob Sievers
1
Comentários recentes, etc. Esta é a resposta que eu vou usar. Pena que eles não conseguiram consertar a horrível confusão, mas se o novo evangelho for "npm ci", tudo bem. Eu posso me adaptar.
Svend
Pena que sempre exclui um node_modulesdiretório existente e reconstrói localmente, mesmo que seja um link simbólico vazio, mas importante. :(
Joe Atzberger 13/06/19
2
@ToolmakerSteve Não prenda a respiração! Eu acho que excluir o conteúdo de um diretório seria magnitudes mais lentas do que apenas excluir o diretório. Você precisaria enumerar o conteúdo e emitir uma série de comandos de exclusão, em vez de apenas o comando de exclusão no sistema operacional. Com os problemas de desempenho anteriormente nivelados em npm e a melhoria usando npm ci, espero que eles relutem muito em introduzir qualquer coisa que possa reduzir o desempenho para um caso de uso bastante incomum. Você pode querer consultar o pnpm.js.org, apesar de usar links físicos para reduzir o uso do disco.
Calor #
64

Resposta curta:

  • npm install honra o package-lock.json apenas se atender aos requisitos do package.json.
  • Se não atender a esses requisitos, os pacotes serão atualizados e o bloqueio do pacote será substituído.
  • Se você preferir falhar na compilação, do que reescrever o bloqueio de pacotes quando isso acontecer, use npm ci.

Aqui está um cenário que pode explicar as coisas (verificado com o NPM 6.3.0)

Você declara uma dependência no package.json como:

"depA": "^1.0.0"

Então você faz, o npm installque irá gerar um package-lock.json com:

"depA": "1.0.0"

Poucos dias depois, uma versão menor mais nova de "depA" é lançada, diga "1.1.0", e o seguinte é verdadeiro:

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)

Em seguida, você atualiza manualmente o seu package.json para:

"depA": "^1.1.0"

Em seguida, execute novamente:

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
fonte
4
Esse é realmente o comportamento pretendido de um arquivo "bloqueado". Aparentemente, não era o caso de versões mais antigas do NPM.
Blockost 15/01/19
1
Então, como o npm rastreia a última atualização do package.json? O que acontece quando você move o package.json e o package-lock.json para outro computador? Como o npm no novo computador sabe se o package.lock é o original ou se foi atualizado para decidir se precisa atualizar o package-lock.json ou não?
Lahiru Chandima
3
@LahiruChandima Realmente não rastreia atualizações. npm installusará as versões bloqueadas a package-lock.jsonmenos que não satisfaça o package.jsoncaso em que instala o package.json e reconstrói o package-lock.json de acordo. Se você alterou o seu package.jsonde tal maneira que o bloqueio de pacote existente ainda satisfaz a atualização, package.jsonele continuará a usá-lo #package-lock
Ahmad Abdelghany
1
Se você já possui um módulo no node_modules que atende aos requisitos do package.json, npm installnão fará nada, independentemente do package-lock.json. Temos que atualizar explicitamente os pacotes, mesmo quando houver atualizações disponíveis que correspondam ao semver especificado em package.json. Pelo menos essa tem sido minha experiência há anos.
Carlin.scott 12/09/19
1
@ToolmakerSteve Eu também era cético em relação ao comportamento relatado por @ carlin.scott, mas eu apenas testei e, de fato, ele está correto. Se a versão dentro node_modulessatisfizer o intervalo package.jsone não houver package-lock.jsonarquivo, o npm não atualizará o módulo durante a execução npm install. Eu acho que está bom, já que você pode usar npm update(ou npm-checkmais recente) para atualizar dependências, e esse comportamento é mais rápido no caso de alguém apenas adicionar uma entrada package.jsone não querer que pacotes não relacionados se atualizem para a mais recente que satisfaça a sem-ver alcance.
Venryx
19

Use o npm cicomando em vez denpm install .

"ci" significa "integração contínua".

Ele instalará as dependências do projeto com base no arquivo package-lock.json, em vez das dependências do arquivo package.json.

Produzirá versões idênticas às de seus companheiros de equipe e também será muito mais rápido.

Você pode ler mais sobre isso nesta postagem do blog: https://blog.npmjs.org/post/171556855892/introducing-npm-ci-for-faster-more-reliable

Daniel Tonon
fonte
2
cirefere-se à "integração contínua", conforme mencionado na documentação e na postagem do blog que anuncia o comando: blog.npmjs.org/post/171556855892/…
Joe Atzberger
Obrigado Joe. Atualizei minha resposta com o nome correto e vinculado à postagem do blog. 😊 (para aqueles que estão lendo isso, já que eu disse que ele representa "instalação limpa")
Daniel Tonon
"E também é muito mais rápido" - ele excluirá a node_modulespasta e a recriará do zero. É realmente muito mais rápido? A pasta também é npm installexcluída node_modules?
Izogfif 27/11/19
Eu acho que a velocidade vem do npm não precisando calcular quais pacotes baixar. Pense nisso como npm installtem que resolver todas as dependências do pacote quando executado. npm cié apenas uma lista de compras de "obtenha esses módulos exatos".
Daniel Tonon
8

No futuro, você poderá usar um --from-lock-filesinalizador (ou similar) para instalar somente a partir do package-lock.jsonsem modificá-lo.

Isso será útil para ambientes de CI, etc. em que construções reproduzíveis são importantes.

Consulte https://github.com/npm/npm/issues/18286 para rastrear o recurso.

Timothy Higinbottom
fonte
Eu duvido. Como se as dependências são diferentes para diferentes sistemas operacionais, como você pode forçar a instalação de algo que não funcionaria?
Yevgeniy Afanasyev
4
@YevgeniyAfanasyev Em vez dessa bandeira, ela foi implementada e npm citambém trata da sua pergunta.
Spex 30/0318
8

Parece que esse problema foi corrigido na npm v5.4.2

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

(Role para baixo até o último comentário no tópico)

Atualizar

Realmente corrigido em 5.6.0. Houve um erro de plataforma cruzada na 5.4.2 que estava causando o problema ainda ocorria.

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

Atualização 2

Veja minha resposta aqui: https://stackoverflow.com/a/53680257/1611058

npm ci é o comando que você deve usar ao instalar projetos existentes agora.

Daniel Tonon
fonte
5
Estou usando a 5.4.2 e ainda está resultando na modificação do meu package-lock.json when npm i. Por exemplo, o módulo fseventsé removido quando eu estiver npm iem uma máquina que não oferece suporte fseventse, em seguida, o módulo é adicionado npm inovamente quando outra vez em uma máquina que suporta.
Hdwdmrbl
Você deve levantar um novo problema no repositório npm GitHub, explicando isso. Se não funcionar como eles dizem que deve funcionar, eles o veem como um bug de alta prioridade que precisa ser corrigido com urgência.
Daniel17
@hrdwdmrbl Estou vendo a mesma fseventsqueda na minha package-lock.jsoncom [email protected]colaborando com contribuidores Mac OS X. Se você não abriu um problema, eu irei.
AL o X
@hrdwdmrbl Descobri isso (e o longo tópico de problemas associados) depois que deixei meu comentário e esqueci de voltar ao SO para atualizar meu comentário. Obrigado por me recuperar. Tudo está bem.
AL o X
4

Você provavelmente tem algo como:

"typescript":"~2.1.6"

no package.jsonqual npm é atualizado para a versão secundária mais recente, no seu caso2.4.1

Edit: Pergunta do OP

Mas isso não explica por que "npm install" alteraria o arquivo de bloqueio. O arquivo de bloqueio não serve para criar uma compilação reproduzível? Nesse caso, independentemente do valor semver, ele ainda deve usar a mesma versão 2.1.6.

Responda:

Isso tem como objetivo bloquear sua árvore de dependência completa. Digamos que typescript v2.4.1requer widget ~v1.0.0. Quando você instala, ele pega widget v1.0.0. Posteriormente, seu colega desenvolvedor (ou compilação de IC) instala e obtém o npm, typescript v2.4.1mas widgetfoi atualizado para widget v1.0.1. Agora o seu módulo do nó está fora de sincronia. Isto é o que package-lock.jsonimpede.

Ou mais geralmente:

Como exemplo, considere

pacote A:

{"nome": "A", "versão": "0.1.0", "dependências": {"B": "<0.1.0"}}

pacote B:

{"nome": "B", "versão": "0.0.1", "dependências": {"C": "<0.1.0"}}

e pacote C:

{"nome": "C", "versão": "0.0.1"}

Se estas são as únicas versões de A, B e C disponíveis no registro, uma instalação normal do npm A instalará:

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

No entanto, se [email protected] for publicado, uma nova instalação do NPM A será instalada:

[email protected] - [email protected] - [email protected] assumindo que a nova versão não modificou as dependências de B. Obviamente, a nova versão do B pode incluir uma nova versão do C e qualquer número de novas dependências. Se tais alterações forem indesejáveis, o autor de A poderá especificar uma dependência em [email protected]. No entanto, se o autor de A e o autor de B não são a mesma pessoa, não há como o autor de A dizer que ele ou ela não deseja obter versões recém-publicadas de C quando B não mudou.


OP Pergunta 2: Então, deixe-me ver se entendi corretamente. O que você está dizendo é que o arquivo de bloqueio especifica as versões das dependências secundárias, mas ainda depende da correspondência difusa do package.json para determinar as dependências de nível superior. Isso é preciso?

Resposta: Não. Package-lock bloqueia toda a árvore de pacotes, incluindo os pacotes raiz descritos em package.json. Se typescriptestiver bloqueado no 2.4.1seu package-lock.json, ele deve permanecer assim até que seja alterado. E digamos que amanhã typescriptlibere a versão 2.4.2. Se eu fizer o checkout do seu ramo e executar npm install, o npm respeitará o arquivo de bloqueio e a instalação 2.4.1.

Mais sobre package-lock.json :

O package-lock.json é gerado automaticamente para qualquer operação em que o npm modifique a árvore node_modules ou o package.json. Ele descreve a árvore exata que foi gerada, de forma que as instalações subseqüentes possam gerar árvores idênticas, independentemente das atualizações intermediárias de dependência.

Este arquivo deve ser confirmado nos repositórios de origem e serve a vários propósitos:

Descreva uma única representação de uma árvore de dependência, de modo que colegas de equipe, implantações e integração contínua garantam a instalação exatamente das mesmas dependências.

Forneça um recurso para os usuários "viajarem no tempo" para estados anteriores de node_modules sem precisar confirmar o próprio diretório.

Para facilitar uma maior visibilidade das alterações nas árvores por meio de diferenças de controle de fonte legíveis.

E otimize o processo de instalação, permitindo que o npm pule resoluções repetidas de metadados para pacotes instalados anteriormente.

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

Matt
fonte
29
Mas isso não explica por que "npm install" alteraria o arquivo de bloqueio. O arquivo de bloqueio não serve para criar uma compilação reproduzível? Nesse caso, independentemente do valor semver, ele ainda deve usar a mesma versão 2.1.6.
Viper Bailey
3
E é isso que estou dizendo. Meu arquivo de bloqueio do pacote diz [email protected], mas quando executo a instalação do npm, a entrada é substituída por [email protected].
Viper Bailey
5
Eu experimentei esse mesmo problema. Em nosso CI / CD, o arquivo é package-lock.jsonbaixado e, em seguida, executamos npm install, mas o package-lock.jsonarquivo é modificado e precisamos fazer uma redefinição antes que possamos realizar as próximas alterações.
BayssMekanique
15
Eu não entendo. Como esse arquivo é "bloqueado" se as instalações subsequentes ainda podem fazer atualizações ?!
Ross Allen
5
Eu acho que eles começaram com a idéia de ter esse arquivo como "info" e "lock" e decidiram que seria apenas um arquivo "info". Um nome melhor seria "package-info.json". Eu adoraria ter um "npm instalar -lock", que vai instalar a partir de "pacote-lock.json" e ignorar "package.json"
Jeremy Chone
2

Provavelmente você deve usar algo como isto

npm ci

Ao invés de usar npm install se você não quiser alterar a versão do seu pacote.

De acordo com a documentação oficial, ambos npm installe npm ciinstalar as dependências que são necessários para o projeto.

A principal diferença é npm installque instala os pacotes tomando packge.jsoncomo referência. Onde, no caso de npm ci, ele instala os pacotes tomando package-lock.jsoncomo referência, certificando-se sempre que o pacote exato é instalado.

Sengottaian Karthik
fonte
1

Existe um problema em aberto na página do github: https://github.com/npm/npm/issues/18712

Esse problema é mais grave quando os desenvolvedores estão usando diferentes sistemas operacionais.

hrdwdmrbl
fonte
As regravações no pacote-lock são destinados, a questão não é consequência desta
Z. Khullah
0

EDIT: o nome "lock" é complicado, seu NPM está tentando atualizar o Yarn. Não é um arquivo bloqueado. package.jsoné um arquivo fixo pelo usuário que, uma vez "instalado", gerará a árvore de pastas node_modules e a árvore será gravada package-lock.json. Então, veja bem, é o contrário - as versões de dependência serão retiradas package.jsoncomo sempre e package-lock.jsondevem ser chamadaspackage-tree.json

(espero que isso tenha esclarecido minha resposta depois de tantos votos negativos)


Uma resposta simplista: package.jsontenha suas dependências como de costume, enquanto package-lock.jsoné "uma árvore node_modules exata e mais importante, reproduzível" (extraída do próprio npm docs ).

Quanto ao nome complicado, seu NPM está tentando alcançar Yarn.

Z. Khullah
fonte
1
Porque se você executar o npm install, o bloqueio do pacote será atualizado.
Jean-Baptiste