paint-brush
Por que você não precisa de PNPM e YARNby@bormando

Por que você não precisa de PNPM e YARN

Dmitrii Bormotov9m2024/07/23
Read on Terminal Reader

npm é um gerenciador de pacotes padrão para o ecossistema **Node.js**. Ele vem com o pacote de instalação, então está basicamente pronto para uso quando você instala o **Node.js** em sua máquina (ou em qualquer provedor de **CI** se você configurar o **Node.js** lá). É estável e sua velocidade é comparável a outros gerenciadores de pacotes.
featured image - Por que você não precisa de PNPM e YARN
Dmitrii Bormotov HackerNoon profile picture
0-item

Olá pessoal!


Tenho certeza que você já viu projetos Node.js usando diferentes gerenciadores de pacotes, ou seja:



Eu mesmo vi isso e trabalhei com todos os itens acima, mas sempre tive uma pergunta em mente - o que leva as pessoas/equipes a usarem yarn ou pnpm em vez de npm ? Quais são as vantagens? Há algum contra?


Bem… vamos descobrir!

Regras de comparação de desempenho

Decidi comparar npm , yarn e pnpm em termos de “velocidade”…


Você verá três medidas abaixo:


  1. Gere um arquivo de bloqueio sem cache.


  2. Instale dependências de arquivos de bloqueio existentes sem nenhum cache.


  3. Instale dependências de arquivos de bloqueio existentes com cache global.


Existem dois tipos de cache:


  1. Global.

    Geralmente armazenado no diretório inicial do usuário (fe, ~/.yarn/berry/cache ).


  2. Local.

    Armazenado no diretório do projeto (fe, <project-dir>/.yarn ).


Embora os números 2 e 3 sejam os casos de uso mais comuns em minha experiência, também peguei o número 1 por precaução (embora seja um caso muito raro).


Usei um projeto de amostra do create-react-app como exemplo de benchmarks.

npm

É um gerenciador de pacotes padrão para o ecossistema Node.js – o que mais dizer? Ele vem com o pacote de instalação, portanto está basicamente pronto para uso quando você instala o Node.js em sua máquina (ou em qualquer provedor de CI se você configurar o Node.js lá).


Na minha opinião, isso é um grande “profissional” - você não precisa instalá-lo separadamente!


Nada excepcional aí - simplesmente… funciona! E não vi nenhum bug importante ao longo dos anos - parece bastante estável e dá conta do recado.


Os recursos do npm que usei até agora:


  1. Gerenciar dependências (instalar, remover, atualizar)
  2. Publicar pacotes (privados, públicos)
  3. Pacotes de link local
  4. Gerenciar espaços de trabalho.

Gerenciar dependências

O npm armazena dependências na pasta node_modules da raiz do seu projeto. Bem direto.


ℹ️ package-lock.json armazena informações sobre registros para os pacotes listados - é MUITO útil se você tiver pacotes de um único escopo, ou seja, @example-company em registros diferentes (por exemplo - pacotes npm e GitHub ):


entrada package-lock.json


Agora, vamos ver como ele funciona em termos de velocidade de instalação…

Gere package-lock.json sem nenhum cache

Gere package-lock.json e instale dependências sem nenhum cache


Levou 1 minuto para o npm gerar um package-lock.json e instalar dependências sem nenhum cache.


Comando usado:

 npm i

Instale dependências de package-lock.json sem nenhum cache

Instale dependências de package-lock.json sem nenhum cache


Levou 18 segundos para o npm instalar dependências de package-lock.json sem nenhum cache.


Comando usado:

 npm ci

Instale dependências de package-lock.json com cache global

Instale dependências de package-lock.json com cache global


Levou 8 segundos para o npm instalar dependências de package-lock.json com cache global.


Comando usado:

 npm ci

Gerenciar espaços de trabalho

Consegui criar um espaço de trabalho e gerenciar dependências para todo o espaço de trabalho de uma só vez e para projetos específicos separadamente.


Em outras palavras - ele faz o trabalho sem bugs/problemas e a documentação oficial é bastante direta.


Recursos do espaço de trabalho que usei até agora:


  1. Instale dependências para todos os projetos no espaço de trabalho.
  2. Instale dependências para um único projeto específico.
  3. Execute um único script para todos os projetos de uma só vez, de forma recursiva.

fio

Honestamente, não experimentei muito alguns dos recursos do fio . Quer dizer, usei muito em termos de “instalação de dependências” enquanto trabalhava em alguns projetos, e é isso.


O yarn não vem com um instalador Node.js , então você terá que instalá-lo separadamente. Isso significa que haveria uma etapa adicional em seus pipelines de CI - você teria que configurar o fio antes de instalar as dependências do projeto.

Gerenciar dependências

O fio tem duas abordagens para instalar dependências:


  1. Zero instalações ” (padrão) - cria a pasta .yarn e lista os pacotes nos arquivos yarn.lock e .pnp.cjs .


  2. Um regular - semelhante ao npm , armazena dependências em node_modules e as lista no arquivo yarn.lock .


ℹ️ Os arquivos yarn lock armazenam informações sobre registros de todos os pacotes listados SOMENTE se você usar a abordagem de instalação antiga (regular).


⚠️ Tenha em mente que “ Zero Installs ” parece armazenar pacotes no cache local e fornecer links para seus arquivos de bloqueio:


Links de pacotes

Pode ser importante para você se você tiver um pipeline Dockerfile ou CI onde você instala dependências em um ambiente limpo e depois deseja movê-lo para outro (você terá que copiar a pasta .yarn e o cache local).


Como a abordagem padrão para o Yarn agora é “ Zero Instalações ” e tem melhor desempenho do que a abordagem antiga, registraremos benchmarks apenas com essa abordagem.

Gere arquivos de bloqueio sem qualquer cache

Gere arquivos de bloqueio com fio e instale dependências


Levou 16,5 segundos para o yarn gerar um arquivo yarn.lock e instalar dependências sem cache.


Comando usado:

 yarn install

Instale dependências de arquivos de bloqueio existentes sem qualquer cache

Instale dependências com abordagem "Zero Install" e sem nenhum cache


Levou 11 segundos para o Yarn instalar dependências com a abordagem “Zero Install” e sem nenhum cache.


Comando usado:

 yarn install --frozen-lockfile

Instale dependências de arquivos de bloqueio existentes com cache global


Instale dependências com abordagem "Zero Install" e cache global


Levou 8 segundos para o Yarn instalar dependências com a abordagem “Zero Install” e cache global.


Comando usado:

 yarn install --frozen-lockfile

Gerenciar espaços de trabalho

Consegui criar um espaço de trabalho e gerenciar dependências para todos os projetos de uma vez e para projetos específicos separadamente.


Recursos do espaço de trabalho que usei até agora:


  1. Instale dependências para todos os projetos no espaço de trabalho.
  2. Instale dependências para um único projeto específico.
  3. Execute um único script para todos os projetos de uma só vez, de forma recursiva.


A documentação está boa, mas os nomes e sinalizadores dos comandos são um tanto confusos.


Por exemplo, devo executar isto para executar o script test na raiz ( . ) e no projeto b2b aninhado:


 yarn workspaces foreach -A --include '{.,b2b}' run test


Em comparação com npm :


 npm run test --workspace=b2b --include-workspace-root

pnpm

O pnpm está atualmente em alta - muitas empresas e projetos de código aberto o utilizam .


Assim como o yarn - pnpm não vem com um instalador Node.js , então você terá que instalá-lo separadamente. Isso significa que haverá uma etapa adicional em seus pipelines de CI – você terá que configurar o pnpm antes de instalar as dependências do seu projeto.

Gerenciar dependências

pnpm é considerado um gerenciador de pacotes rápido e eficiente em espaço em disco


Na verdade, concordo com a afirmação “eficiente de espaço em disco” em termos de gerenciamento local de dependências.


Por padrão, o pnpm elimina a duplicação de dependências compartilhadas. pnpm cria links simbólicos para os pacotes que são usados em múltiplas dependências. ou seja, se os pacotes a e b usarem o pacote c como uma dependência - o pnpm armazenará o pacote c como uma única cópia e criará links simbólicos para os pacotes a e b . Dessa forma, o gerenciador de pacotes não cria cópias impressas e economiza memória no seu SSD/HDD.


ℹ️ pnpm-lock.yaml não armazena informações sobre registros dos pacotes listados.


⚠️ Tenha em mente que o pnpm às vezes armazena dependências no cache global, em vez de mantê-lo como um projeto.

Gere pnpm-lock.yaml sem nenhum cache

Gerar pnpm-lock.yaml


Levou 31 segundos para que o pnpm gere um pnpm-lock.yaml e instale dependências sem nenhum cache.


Comando usado:

 pnpm install

Instale dependências de pnpm-lock.yaml sem cache global

Instale dependências de pnpm-lock.yaml sem cache global


Levou 16 segundos para que o pnpm instale dependências de pnpm-lock.yaml sem cache.


Comando usado:

 pnpm i --frozen-lockfile


Instalar dependências de arquivo de bloqueio existente com cache global

Instale dependências de pnpm-lock-yaml com cache


Levou 5 segundos para que o pnpm instale dependências de pnpm-lock.yaml com cache global.


Comando usado:

 pnpm i --frozen-lockfile

Gerenciar espaços de trabalho

Agora, é aí que as coisas se tornam realmente interessantes…


O pnpm tem muitas opções de configuração, mas algumas funcionalidades básicas simplesmente não funcionam!


Vamos revisar alguns bugs que enfrentei:

instalação pnpm --filtro

É importante poder instalar dependências apenas para projetos específicos – é bastante útil para monorepos quando você cria pipelines relacionados a projetos específicos dentro do espaço de trabalho.


ou seja, imagine que você tem em seu espaço de trabalho:


  • um aplicativo da web,
  • servidor back-end,
  • projeto de teste (testes ponta a ponta).


Todos esses são projetos NPM separados, mas fazem parte do mesmo repositório ☝️


Agora, você deseja que um pipeline execute apenas testes de ponta a ponta. Então, você precisa apenas de dependências de teste ponta a ponta, certo?


Bem, você não conseguirá fazer isso - o pnpm está forçando você a instalar dependências para todo o espaço de trabalho!


pnpm install --filter <project-name> deveria instalar dependências apenas para projetos selecionados, mas não funciona.


Há um bug com um ano de idade e foi recentemente fechado com uma correção que não funcionava.

instalação recursiva = falso

O pnpm por padrão instala dependências para todo o espaço de trabalho (todos os projetos) quando você executa pnpm install


Você pode alternar esse comportamento se definir recursive-install=false em .npmrc na raiz do seu espaço de trabalho.


MAS introduz outro bug que já tem quase 2 anos .

arquivo de bloqueio de espaço de trabalho compartilhado = falso

Por padrão, pnpm armazena a lista de dependências em um único arquivo de bloqueio (igual a npm e yarn ).


Você também pode alternar esse comportamento se definir shared-workspace-lockfile=false em .npmrc na raiz do seu espaço de trabalho.


Isso nos permitiria manter o recurso de espaço de trabalho e usar o sinalizador --ignore-workspace para instalar dependências para um projeto específico.


De qualquer forma, esta configuração apresenta mais alguns problemas:


  1. eslint e tsc --noEmit geram um erro “JavaScript Heap Out of Memory” em meus pipelines de ações do GitHub .


  2. Algumas das dependências são armazenadas no cache global e vinculadas simbolicamente em node_modules/.pnpm .

Resultados de comparação de desempenho

#

npm

fio

pnpm

Gere um arquivo de bloqueio

60 segundos

16,5 segundos

31 segundos

Instale dependências sem nenhum cache

18 segundos

11 segundos

8 segundos

Instale dependências com cache global

8 segundos

8 segundos

5 segundos


De acordo com o benchmark acima, npm é o gerenciador de pacotes mais lento ☝️


De qualquer forma, vamos interpretar esses resultados…

Gerar um arquivo de bloqueio

É um caso raro. Normalmente, um arquivo de bloqueio é criado na inicialização do projeto e então se expande quando você instala/atualiza pacotes.


Com isso em mente - não parece ser algo muito importante em que se confiar ao escolher um gerenciador de pacotes.

Instalar dependências

Na maioria dos casos, seus projetos mantêm uma lista específica de dependências e você raramente adiciona/remove algo.


Provavelmente, você atualizará versões de seus pacotes de tempos em tempos - essas alterações são pequenas e você reutilizará o restante dos pacotes do cache.


Em outras palavras, o caso de uso comum é -- buscar novos pacotes do registro de pacotes e pegar o restante do cache.


pnpm (5-8 seg) é quase duas vezes mais rápido que npm (8-18 seg) com fio (8-11 seg) no meio.

Conclusão

Fatos

  • O pnpm é de fato um gerenciador de pacotes “rápido e eficiente em disco” - isso fica bem claro na análise atual!


  • O recurso de espaço de trabalho pnpm está com bugs e alguns dos bugs não foram resolvidos há anos.


  • tanto o pnpm quanto o fio exigem uma configuração adicional em pipelines de CI, enquanto o npm não.


  • tanto o pnpm quanto o yarn não armazenam informações de registro de pacotes em seus arquivos de bloqueio, enquanto o npm o faz.

Pensamentos do autor

Acho que o pnpm faz o melhor trabalho se o seu requisito para o gerenciador de pacotes for tão simples quanto “instalar apenas dependências”.


Embora o pnpm não venha com um instalador Node.js pronto para uso, é fácil configurá-lo em pipelines de CI com corepack ou action existente .


Eu prefiro npm , porque:


  • é estável (especialmente em espaços de trabalho),


  • vem com Node.js e não requer configuração adicional no pipeline de CI,


  • armazena registros de pacotes em package-lock.json para que você possa instalar dependências com um único escopo de registros diferentes.


Essas vantagens superam os segundos de velocidade e espaço em disco que eu economizaria com yarn ou pnpm .


Quais são os seus critérios para escolher um gerenciador de pacotes? Não seja tímido e deixe-me saber sua opinião na seção de comentários abaixo! 👇😊