<devzera

Aprendendo e compartilhando tecnologia

Tudo que você queria saber sobre o package-lock.json mas estava com vergonha de perguntar

Esse texto é uma tradução livre do artigoEverything You Wanted To Know About package-lock.json But Were Too Afraid To Ask.

Introdução

Então você atualizou o Node Package Manager (npm) para a versão 5.x.x e tudo parece que está funcionando bem, certo? Mas pera, o que é isso? Um novo arquivo foi criado automaticamente:package-lock.json. Se você abri-lo até vai parecer umpackage.json, mas com muito mais coisa, mais verboso. Você decide ignorá-lo enquanto desenvolve seu projeto. Eventualmente você se depara com um problema de dependência. Não pode ser encontrada ou uma versão errada foi instalada. A maior parte das pessoas deleta opackage-lock.jsone reexecuta onpm install. Então por que o temos? O que deveríamos fazer? O que ele faz?

Resumo

  • Se você está usando uma versão do NPM superior a 5.x.x opackage-lock.jsonserá gerado para você;
  • Você deve usar opackage-lock.jsonpara garantir uma instalação consistente e dependências compatíveis;
  • VocêDEVEcommitar seupackage-lock.jsonem seu sistema de controle de versão;
  • A partir da versão 5.1.0 do npm o conteúdo dopackage.jsoné capaz de sobrescrever o que estiver nopackage-lock.jsonpara instalar ou modificar uma dependência, o quedevegerar menos dor de cabeça;
  • Não precisa mais deletar opackage-lock.jsone executar onpm installpara regerá-lo;
  • Use semver (versionamento semântico) se sua aplicação for uma API e siga as regras do semver.

Background

Versionamento Semântico (semver)

Antes de entender opackage-lock.jsone opackage.jsonvocê precisa entender oversionamento semântico (semver). É a sacada por trás do NPM, e o que o tornou um sucesso. Você pode ler mais sobre como o NPM faz seu uso poraqui.

De forma resumida, se você está criando uma aplicação com a qual outras irão integrar, você deve comunicar como as alterações feitas afetarão a capacidade de integração desses terceiros com sua aplicação. Isso é feito através do versionamento semântico.

Uma versão é dividida em 3 partes: X, Y e Z, onde a primeira é considerada amajor(principal), a segunda, aminor(menor) e a terceira é opatch(correção).Um exemplo como 1.2.3 representaria que 1 é a major, a 2 é a minor, e o 3 o patch.

  • Uma modificação no patch não quebrará nada. Muito pelo contrário, corrigirá algo.
  • Uma alteração na versão minor lhe trará uma nova funcionalidade, mas sem quebrar o que existe atualmente.
  • E por fim, alterar algo na versão major significa que houve uma quebra de compatibilidade. Se o usuário não fizer a alteração necessária, ele não conseguirá mais integrar com sua aplicação.

Gerenciando pacotes

O NPM existe para facilitar o gerenciamento de dependências. Seu projeto pode ter centenas de dependências, cada uma com suas próprias dependências. Para nos mantermos longe desse inferno de dependências, o NPM foi criado com um conjunto de comandos que permitem você instalar e gerenciar tudo isso e, dificilmente, precisar se preocupar com elas.

Quando você instala um pacote com o NPM (e o salva), uma nova entrada é adicionada ao arquivopackage.jsoncontendo o nome do pacote e a versão (semver) que será usada. Entretanto, existem algumas artimanhas para definir melhor as versões.

Por padrão o NPM instalará a versão mais nova do pacote, inserindo um símbolo na frente da versão^2.12.0. Isso significa que o pacote será instalada com uma versão que seja igual ou superior a 2.12.0. Leia mais sobre semver e wildcards e brinque com umaferramenta para calcular a versão do pacote.

Projetos Compartilhados

O real benefício de ter dependências definidas nopackage.jsoné que qualquer um pode ter acesso a esse arquivo e criar um pasta de dependências contendo todos os módulos para rodar sua aplicação. Mas vamos dar uma olhada em uma caso específico em que as coisas podem dar errado.

Vamos dizer que criamos um projeto utilizando oexpress. Depois de rodarnpm initinstalamos o express:npm install express --save. Até o momento em que escrevo esse artigo, a última versão do express é a 4.15.4. Então será adicionado como dependência aopackage.jsona linhaexpress: ^4.15.4, e isso informará exatamente a versão que será instalada em meu computador.

Não agora, mas talvez amanhã, o pessoal que desenvolve o express pode lançar um bug fix, então a nova versão será a 4.15.5. Então se alguém quiser contribuir com meu projeto, precisará cloná-lo e rodar o comandonpm install. Com isso ele terá instalado em seu computador a versão 4.15.5. Nós dois teremos o express instalado, mas versões diferentes. Teoricamente ambas devem ser compatíveis, mas talvez esse bug fix afete alguma funcionalidade que fazemos uso, e nossa aplicação poderá produzir resultados diferentes, já que uma possui a versão 4.15.4, e outra a 4.15.5.

Package-lock

O objetivo

A proposta dopackage-locké evitar que situações como o caso acima, onde módulos instalados a partir do mesmopackage.jsonresulte em duas diferentes instalações. Opackage-lockfoi adicionado em versões superiores ao do NPM 5.x.x, então se você está usando essa versão ou alguma maior, você o verá ser gerado, a não ser que você desabilite essa função.

O formato

Opackage-lock.jsoné uma larga lista de dependências listadas no seupackage.json, além de especificar a versão exata que deve ser instalado, a localização do módulo (URI), o hash que verifica a integridade do módulo e a lista de pacotes necessários. Vamos dar uma olhada nos registros presentes apenas para o express:

"express": {
      "version": "4.15.4",
      "resolved": "https://registry.npmjs.org/express/-/express-4.15.4.tgz",
      "integrity": "sha1-Ay4iU0ic+PzgJma+yj0R7XotrtE=",
      "requires": {
        "accepts": "1.3.3",
        "array-flatten": "1.1.1",
        "content-disposition": "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",
        "methods": "1.1.2",
        "on-finished": "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",
        "send": "0.15.4",
        "serve-static": "1.12.4",
        "setprototypeof": "1.0.3",
        "statuses": "1.3.1",
        "type-is": "1.6.15",
        "utils-merge": "1.0.0",
        "vary": "1.1.1"
      }
    },

Entradas equivalentes podem ser encontradas para cada pacote listado na seção “requires”.

Então a ideia é que ao invés do NPM ler opackage.jsonpara instalar os módulos ele use opackage-lock.json. Isso acontece justamente por conta do que disse anteriormente. Pelopackage-lockespecificar a versão, localização, hash para cada módulo, e suas dependências próprias, a instalação criada será sempre a mesma, toda vez. Não importa qual dispositivo você use, ou quando você fará a instalação, ele deverá ter o mesmo resultado, sempre, e isso é útil demais.

A controvérsia

Então, se opackage-lock.jsonsupostamente resolve um problema comum, por que os principais resultados de pesquisa (além da documentação do npm) são todos sobre desabilitá-lo ou questionar a função que ele desempenha?

Antes do NPM 5, opackage.jsonera a fonte absoluta para qualquer projeto. O que estava nele era a lei. Os usuários do NPM gostaram desse modelo e se acostumaram a mantê-lo assim. Entretanto, quando opackage-lockfoi introduzido, ele agiu de forma contrária ao que as pessoas esperavam.

Dado um pacote preexistente e umpackage-lock, uma alteração nopackage.json(o que muitos usuários consideravam a fonte da verdade) não era refletida nopackage-lock.

Exemplo: Pacote A, versão 1.0.0 nopackagee nopackage-lock. Nopackage.jsona versão A foi alterada manualmente para 1.1.0. Se um usuário considerar opackage.jsoncomo a fonte da verdade e executar onpm install, ele irá esperar que a versão 1.1.0 seja instalada. Entretanto, a versão 1.0.0 é instalada, apesar do fato de que a versão 1.1.0 está listada nopackage.json.

Exemplo:Um módulo não existe nopackage-lock.json, mas existe nopackage.json. Como um usuário que olha para opackage.jsoncomo a fonte de toda a verdade, eu esperaria que meu módulo fosse instalado. No entanto, como o módulo não está presente nopackage-lock.json, ele não está instalado e meu código falha porque não consegue encontrar o módulo.

Na maior parte do tempo, porque a pessoa não conseguiu descobrir o motivo de suas dependências não estarem sendo instaladas corretamente, ela irá preferir remover opackage-lock.jsone rodar o comandoinstallnovamente, ou irá desabilitar sua criação.

O conflito entre o esperado e a realidade gerouuma discussão muito interessante no repositório do NPM. Algumas pessoas acham que opackage.jsondeveria ser a fonte da verdade, outras acham que desde que o NPM considerou opackage-lock.jsoncomo a fonte para realizar a instalação, essa deve ser a fonte real. A resolução desse conflito está noPR #17508.Os contribuidores do projeto adicionaram uma mudança aopackage.jsonfazendo com que as regras presentes nele se sobrepujam ao que está nopackage-lock.

Agora, em ambos cenários, os pacotes que os usuários esperam ser instalados, serão instalados corretamente. Essa mudança faz parte do release lançado na versão 5.1.0 do NPM, que foi ao ar em 5 julho de 2017.

Comentários