FNSP Web Page

O package-lock existe por uma razão, e não é a que parece

A explicação habitual é que o package-lock.json serve para fixar versões. Está certa e é a menos importante — as versões já estão no package.json, e quem quiser fixá-las pode simplesmente tirar o ^.

O que ele faz de facto são três coisas, e nenhuma delas se consegue de outra maneira.

1. Fixa a árvore inteira, não as suas dependências

O package.json deste site tem cerca de quarenta dependências. O package-lock.json tem milhares de entradas.

A diferença são as dependências das dependências — aquelas que ninguém escolheu e que ninguém lê. São a maioria esmagadora do que corre em produção, e é sobre elas que o package.json não diz absolutamente nada.

Sem o ficheiro de bloqueio, cada instalação resolve tudo outra vez. Duas máquinas, no mesmo dia, com o mesmo package.json, podem acabar com árvores diferentes — porque uma delas correu depois de um autor qualquer, três níveis abaixo, ter publicado uma correção.

2. Verifica que os bytes são os mesmos

Esta é a parte que quase ninguém conhece, e é a mais valiosa. Cada entrada traz um resumo criptográfico do pacote:

"node_modules/semver": {
  "version": "7.6.0",
  "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz",
  "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AqRPM4QIhMKbd+i7ldYP1MdZ4S9nwE0jsNqe..."
}

Na instalação, o npm descarrega o ficheiro, calcula o resumo e compara. Se não bater certo, falha.

É uma defesa concreta contra o cenário que mais preocupa quem pensa nisto: alguém conseguir substituir o conteúdo de uma versão já publicada, ou um intermediário na rede trocar o que é servido. A versão continua a chamar-se 7.6.0, mas os bytes já não são os mesmos — e a instalação recusa-se a continuar.

3. Guarda a forma, não só os números

O npm achata a árvore: sobe pacotes para o nível de cima sempre que pode, para não repetir. Duas resoluções com exatamente as mesmas versões podem produzir arrumações diferentes — e onde cada pacote fica muda quem resolve o quê.

O ficheiro de bloqueio também guarda isso.

4. E de graça, velocidade

Resolver restrições de versões é trabalho a sério. Com a árvore já escrita, o npm deixa de ter de a calcular e passa a descarregar o que está na lista. É metade da razão pela qual o npm ci é bastante mais rápido — a outra metade está no artigo sobre o npm ci.

5. Conflitos: nunca à mão

Dois ramos mexeram em dependências e o resultado são centenas de linhas em conflito. Resolver aquilo à mão dá sempre um ficheiro inválido.

git checkout --theirs package-lock.json
npm install
git add package-lock.json

Escolhe-se um dos lados, deixa-se o npm regenerar a partir do package.json já resolvido, e comita-se o resultado. É o único caminho fiável.

6. Uma exceção que se costuma ignorar

Numa biblioteca publicada, o ficheiro de bloqueio não serve a quem a instala: quem depende dela resolve as versões com o ficheiro dele. Continua a valer a pena comitá-lo, mas por outra razão — para que a integração contínua da biblioteca seja reproduzível.

É uma confusão comum: publicar uma biblioteca com versões fixas no ficheiro de bloqueio não impõe nada a ninguém. Quem impõe é o package.json, e é por isso que os intervalos lá dentro merecem atenção — o que está no artigo sobre o ^.

O resumo

O ficheiro de bloqueio não existe para fixar o que se escolheu; existe para fixar o que não se escolheu — os milhares de pacotes que entraram por arrasto — e para garantir que o que chega é byte a byte o que chegou da primeira vez. Por isso se comita, e por isso não se edita à mão.

Comentários

Ainda ninguém comentou este artigo.

Voltar ao blog

Gostávamos de saber quantas pessoas visitam o site, com o Google Analytics. Sem a sua autorização não corre nada. Política de Privacidade.