O que é pnpm?
pnpm é um gerenciador de pacotes que armazena cada versão de cada pacote apenas uma vez em um content-addressable store global e cria hard-links para cada projeto que precise dele. O resultado é um uso de disco drasticamente menor e instalações muito mais rápidas, especialmente em múltiplos projetos ou em um monorepo.
Ele também é estrito. Em vez de achatar todas as dependências em um único node_modules, o pnpm utiliza symlinks que espelham o grafo de dependências real. Um pacote só consegue importar aquilo que ele realmente declara; portanto, a dependência acidental de uma dependência transitiva falha imediatamente, em vez de funcionar localmente e quebrar em produção.
O store e o node_modules
O store reside em um diretório global e armazena o conteúdo de cada versão de pacote que você já instalou. Quando um projeto precisa de um pacote, o pnpm cria um hard-link dele a partir do store, em vez de copiá-lo.
O layout node_modules então utiliza symlinks:
node_modules/.pnpmarmazena os pacotes reais.node_modules/<name>linka para a versão que seu projeto declarou.- O
node_modulesde cada pacote linka apenas para as suas dependências declaradas.
É por isso que o pnpm detecta dependências fantasmas: se o seu código importa um pacote que você esqueceu de adicionar ao package.json, ele falha, pois o pacote não está linkado no nível superior. O layout flat do npm frequentemente deixa esse erro passar despercebido.
Comandos
Os comandos são muito semelhantes aos do npm, o que torna a migração fácil.
pnpm install # install from the lockfile
pnpm add zod # add a dependency
pnpm add -D vitest # add a dev dependency
pnpm remove zod # remove a dependency
pnpm run build # run a script
pnpm dlx create-vite # run a package without installing
pnpm install utiliza o store, por isso instalações repetidas são rápidas. pnpm-lock.yaml desempenha o mesmo papel que package-lock.json e deve ser commitado.
Workspaces
Workspaces são o recurso de maior destaque do pnpm. Você declara a localização dos pacotes em pnpm-workspace.yaml.
# pnpm-workspace.yaml
packages:
- "apps/*"
- "packages/*"
Então, um workspace pode depender de um pacote irmão utilizando o workspace protocol em vez de um caminho de arquivo relativo.
{
"name": "@repo/web",
"dependencies": {
"@repo/ui": "workspace:*"
}
}
O pnpm vincula o pacote local, e workspace:* é substituído pela versão real no momento da publicação. Isso mantém as dependências internas explícitas e evita caminhos file:../.. frágeis.
Catálogos e consistência de versões
Em um monorepo grande, é comum que diferentes pacotes dependam de versões distintas da mesma biblioteca. Os Catalogs centralizam essa decisão.
# pnpm-workspace.yaml
packages:
- "apps/*"
- "packages/*"
catalog:
react: ^19.0.0
typescript: ^5.6.0
{
"dependencies": {
"react": "catalog:"
}
}
Todo pacote que utiliza catalog: resolve para a versão definida uma única vez na raiz, o que torna as atualizações uma edição simples e evita a divergência de versões.
Filtrando e executando tarefas
O pnpm pode focar em um subconjunto de um workspace, o que é essencial em um monorepo.
# run tests only in packages that changed since main
pnpm --filter "...[origin/main]" test
# run a script in one package
pnpm --filter @repo/web dev
# run a script in every package
pnpm -r build
Os filtros suportam nomes de pacotes, globs de diretórios e relacionamentos de dependência, permitindo que você execute um comando apenas onde ele for relevante. Para cache e orquestração entre pacotes, combine o pnpm com o Turborepo, que foi projetado exatamente para essa configuração.
CI e reprodutibilidade
Use um lockfile congelado (frozen lockfile) em CI para que a instalação falhe caso o lockfile e os manifestos estejam divergentes.
pnpm install --frozen-lockfile
pnpm run build
Este é o equivalente do pnpm para npm ci e é o que garante um build reprodutível. Como as instalações são rápidas e o store pode ser cacheado em CI, o pnpm também tende a reduzir o tempo do pipeline.
pnpm comparado ao npm
- Disco e velocidade: o pnpm compartilha pacotes através de um store e cria links para eles; o npm copia uma árvore achatada (flattened tree) por projeto.
- Rigor: o pnpm expõe apenas as dependências declaradas; o layout flat do npm permite importações fantasmas (phantom imports).
- Workspaces: ambos oferecem suporte, mas o protocolo de workspace e a filtragem do pnpm são mais ergonômicos para monorepos grandes.
- Compatibilidade: ambos leem
package.jsone suportam o mesmo registry, então a migração geralmente consiste em deletarnode_modulese o lockfile antigo.
Escolha o npm pela simplicidade e ubiquidade, e o pnpm quando espaço em disco, velocidade ou a ergonomia de monorepos forem prioridades. Veja o guia do npm para o workflow padrão.
Melhores práticas
- Faça commit de
pnpm-lock.yaml. - Use
pnpm install --frozen-lockfileno CI. - Use
workspace:*para dependências internas. - Centralize versões compartilhadas com catálogos.
- Aproveite a rigidez: adicione as dependências ausentes em vez de desativá-la.
- Use
--filterpara executar tarefas apenas onde elas são necessárias. - Combine o pnpm com um task runner para cache em repositórios grandes.
Erros comuns
- Adicionar dependências no nível errado do workspace com
-w. - Usar caminhos
file:em vez do protocolo de workspace. - Ignorar erros de “not declared in package.json” em vez de corrigir o manifesto.
- Esquecer o
--frozen-lockfileno CI e ter instalações não reprodutíveis. - Permitir que cada pacote fixe sua própria versão de uma biblioteca compartilhada.
- Misturar gerenciadores de pacotes em um único repositório e criar lockfiles conflitantes.
Próximos passos
O pnpm é a escolha eficiente para projetos JavaScript modernos, especialmente monorepos. Compare-o com o npm, adicione o Turborepo para cache de tarefas e entenda o runtime do Node.js por trás de tudo. Depois, tente aplicá-lo em um projeto existente removendo node_modules e deixando o pnpm reconstruir a árvore a partir do store.