Skip to main content

O que é package-lock.json e como funciona um arquivo de lock para pacotes do Yarn e do npm?

Escrito por
Package Lock Files blog

14 de março de 2019

0 minutos de leitura

O que é package-lock.json?

Neste artigo, vamos falar tanto do arquivo de lock de pacotes do npm, `package-lock.json`, quanto do arquivo do Yarn, `_yarn.lock`. Os arquivos de lock de pacotes funcionam como um manifesto completo das dependências de um projeto, especificando as versões exatas a serem instaladas, assim como as dependências dessas dependências, e assim por diante — abrangendo toda a árvore de dependências.

Um arquivo de lock de pacotes é criado pela primeira vez em um projeto quando suas dependências são instaladas do zero. Durante a instalação, toda a árvore de dependências é calculada e salva no arquivo de lock, junto com metadados sobre cada dependência, como:

  • A versão do pacote que deve ser instalada

  • Um hash de integridade que ajuda a garantir que o pacote não foi adulterado

  • O endereço resolvido no registro, que indica de onde o pacote foi obtido e de onde deverá ser obtido nas próximas instalações

Editor de código exibindo uma entrada do package-lock para a dependência acorn, incluindo a versão 5.7.3, o URL do registro, o hash de integridade e a indicação de dependência de desenvolvimento.

Por que precisamos de arquivos de lock?

Os arquivos de lock servem para fixar, ou bloquear, as versões de toda a árvore de dependências no momento em que são criados. Por que é importante usar um arquivo de lock de pacotes e fixar as versões dos pacotes?

Sem um arquivo de lock de pacotes, um gerenciador de pacotes como Yarn ou npm resolve em tempo real a versão mais recente de um pacote durante a instalação das dependências, em vez de usar a versão originalmente prevista para aquele pacote.

Diagrama de versionamento semântico que mostra os componentes de versão principal, secundária e de correção.

Por exemplo, se um projeto depende de dummy-pkg: ^1.0.0, duas instalações separadas, feitas em momentos diferentes, podem obter versões diferentes de dummy-pkg. Isso pode acontecer se alguém instalar `dummy-pkg` e obtiver a versão 1.0.0, e alguns minutos depois o pacote lançar uma nova versão, 1.0.1. Com isso, uma segunda pessoa que instalar as dependências do projeto obterá a versão 1.0.1 de `dummy-pkg`, em vez da versão 1.0.0.

O uso de arquivos de lock garante que as instalações permaneçam idênticas e reproduzíveis em toda a árvore de dependências, entre diferentes pessoas — como integrantes da mesma equipe — e entre sistemas, como na execução de um build de CI.

Como funcionam os arquivos de lock?

Na maior parte do ecossistema npm, há dois arquivos de lock de pacotes:

  • O yarn.lock do Yarn

  • O package-lock.json do npm

Nada como um bom fluxograma para explicar como esses dois arquivos são usados:

Diagrama que mostra como os arquivos de lock do npm e do Yarn orientam a instalação e a publicação de pacotes, o controle de versão e a criação de builds reproduzíveis.

Esta ilustração usa o package-lock.json do npm, mas ele pode ser substituído por yarn.lock em todos os casos. A única exceção é que o processo de publicação do cliente npm não ignora automaticamente um arquivo yarn.lock, que será incluído no tarball do pacote, a menos que seja explicitamente ignorado no arquivo .npmignore. No entanto, como vemos no lado esquerdo da ilustração, mesmo que esse arquivo de lock faça parte do pacote, ele não é usado pela pessoa que consome a biblioteca.

Nem o Yarn nem o npm consideram arquivos de lock de dependências transitivas: os gerenciadores de pacotes os ignoram completamente. Apenas o projeto de nível superior, no qual uma instalação é executada, tem toda a sua árvore de dependências verificada por meio de um arquivo de lock, que o gerenciador de pacotes usa como manifesto das dependências.

Arquivos de lock shrinkwrap

Nunca diga nunca. Há um caso em que um arquivo de lock especial também é considerado para dependências transitivas. O arquivo npm-shrinkwrap.json fixa a árvore de dependências, assim como os outros arquivos de lock, mas o processo de publicação do npm também envia esse arquivo para o registro. Mais importante: quando alguém usa a biblioteca em uma aplicação comum e executa npm install, o arquivo shrinkwrap da biblioteca determina quais versões serão instaladas, em vez da resolução semver que ocorre durante a instalação.

Vale observar que Yarn e npm lidam de formas diferentes com pacotes que têm um arquivo npm-shrinkwrap.json:

  • O npm sempre coloca as dependências especificadas em npm-shrinkwrap.json na pasta node_modules/ do próprio pacote e não tenta movê-las para um nível superior da árvore.

  • O Yarn nunca considera npm-shrinkwrap.json e o ignora completamente.

Para que serve um arquivo shrinkwrap? Ele permite que quem mantém uma biblioteca fixe e organize as dependências dela para distribuir versões conhecidas. No entanto, isso exige cuidado na manutenção de toda a árvore de dependências. Além disso, se um arquivo shrinkwrap for usado, não é necessário manter outro arquivo de lock no controle de versão. Um bom exemplo de biblioteca que segue essa abordagem é o conhecido projeto hapijs.

Arquivos de lock desatualizados

Os arquivos de lock são criados quando desenvolvedores interagem com um projeto, por exemplo, ao adicionar uma dependência ou instalar as dependências de um clone recém-criado. É comum adicionar ou remover dependências durante o ciclo de desenvolvimento. Mas o que acontece se alguém alterar o package.json e esquecer de enviar o arquivo de lock correspondente para o controle de versão?

Quando o package.json de um projeto não está sincronizado com o arquivo de lock, gerenciadores de pacotes como npm e Yarn tentam reconciliar a diferença e gerar um novo manifesto. Embora isso pareça uma boa ideia, pode causar problemas se acontecer durante a CI.

Se a etapa de build não falhar quando houver divergência entre o arquivo de lock e as dependências declaradas em package.json, os artefatos compilados ou testados poderão usar qualquer versão disponível no momento do build, anulando todos os benefícios de um arquivo de lock.

Por isso, recomendamos configurar os gerenciadores de pacotes para usar o arquivo de lock ao instalar dependências, por exemplo:

$ yarn install --frozen-lock file
$ npm ci

Arquivos de lock para aplicações e bibliotecas

As opiniões sobre o uso de arquivos de lock variam conforme o projeto seja a aplicação principal ou uma biblioteca destinada a ser usada por uma aplicação ou outra biblioteca.

Há consenso de que aplicações devem usar um arquivo de lock. Já no caso de bibliotecas, nem tanto. Por quê?

O principal argumento contra o uso de arquivos de lock em bibliotecas é que eles podem fazer com que as dependências instaladas por quem consome a biblioteca sejam diferentes. Com essa diferença, quem mantém os pacotes pode não perceber que os builds estão falhando.

Por outro lado, sem um arquivo de lock, as dependências só são resolvidas no momento da instalação e provavelmente serão diferentes para quem mantém a biblioteca e para quem a consome.

Na minha opinião, projetos de bibliotecas não são diferentes e devem incluir um arquivo de lock para facilitar a colaboração da equipe e garantir builds reproduzíveis.

Mas sugiro uma melhoria: se você realmente está preocupado com a possibilidade de as dependências da sua biblioteca causarem problemas para quem a usa, pode configurar dois builds de CI diferentes para detectar essas questões:

  • Um que use o arquivo de lock do projeto, como qualquer configuração normal de build.

  • Outro que ignore o arquivo de lock do projeto e resolva as dependências de acordo com as versões semver mais recentes.

Esse fluxo permite manter builds reproduzíveis e dependências consistentes durante o desenvolvimento e, ao mesmo tempo, ajuda a detectar possíveis mudanças incompatíveis para quem consome sua biblioteca — tudo isso sem deixar a equipe de desenvolvimento insatisfeita.

Comece a resolver desafios de capture the flag

Aprenda a resolver desafios de capture the flag assistindo sob demanda ao nosso workshop virtual introdutório.

Publicado em:

Leia mais

Live Stream

Agentes de Remediação, Desmistificados: Por Que Corrigir é Melhor do que Encontrar

Veja como o Remediation Agent da Snyk usa inteligência de segurança, análise de explorabilidade e validação para transformar vulnerabilidades em pull requests prontos para merge.

feature insights context
Blog

Por dentro do comprometimento do keyv npm: malware no preinstall, procedência confiável e hooks de IDE

O keyv 6.0.0 e outras dez versões do npm distribuíram malware executado durante a instalação. Confira as versões afetadas, os hashes, as etapas de detecção e a ordem segura de correção.

feature ai ide dark
Blog

Uma primeira visão do Evo Agentic AppSec: remediação agentiva e defesa contra código malicioso

Conheça os primeiros recursos de Agentic AppSec da Snyk: um agente de remediação autônomo que corrige vulnerabilidades e uma defesa contra código malicioso que bloqueia pacotes arriscados antes que sejam lançados.