O node_modules fantasma que sequestrava os builds — e como um npm install na home quebrou 4 projetos
💡 Descobertas·

O node_modules fantasma que sequestrava os builds — e como um npm install na home quebrou 4 projetos

📖 6 min de leitura← Voltar para timeline

⚡ O erro que não fazia sentido

Era 20:10 de 05/08/2026. Eu estava fazendo um fix na animação de tema do LifeLog — nada demais, só ajustar umas regras CSS de View Transition. Build local, push, CI. Rotina.

O build quebrou com:

Named export 'parseCookie' not found.
The requested module 'cookie' is a CommonJS module...

Estranho. O pacote cookie no lockfile era 2.0.1 (ESM, com parseCookie). O CI do último commit tinha passado. Ninguém mexeu em dependência. Por que local quebrava?

🧠 O contexto: CI passa, local quebra

Esse é o tipo de erro mais irritante que existe. O CI (GitHub Actions) builda num ambiente limpo — tudo funciona. Local, o mesmo código, mesmas dependências, mesmo Node — quebra.

# Verificação 1: o lockfile está igual ao do CI?
git show HEAD:pnpm-lock.yaml | grep -c "cookie@2.0.1"    # 2 — igual

# Verificação 2: o pacote existe no node_modules?
ls node_modules/.pnpm/cookie@2.0.1/node_modules/cookie/dist/index.js
# Existe! E tem parseCookie exportado.

# Verificação 3: o que o Node resolve?
node --input-type=module -e "import('cookie').then(m => console.log(Object.keys(m)))"
# [ 'default', 'parse', 'serialize' ]  — ⚠️ faltando parseCookie!

O Node resolvia um cookie diferente do que estava no node_modules do projeto. Alguma coisa na árvore de diretórios estava sequestrando a resolução.

🔧 A luta: seguindo o rastro do Node Module Resolution

O Node resolve módulos subindo a árvore de diretórios: primeiro o node_modules local, depois o do pai, até a raiz. Se em algum nível acima houver um pacote com o mesmo nome, ele ganha.

# Onde o Node está resolvendo 'cookie'?
node --input-type=module -e "console.log(import.meta.resolve('cookie'))"
# file:///home/samuel/node_modules/cookie/index.js   ← O QUÊ?

/home/samuel/node_modules/. Isso não devia existir.

ls -la /home/samuel/node_modules/ | head -5
# total 253M
# drwxr-xr-x  cookies@0.7.2/

253MB de node_modules na HOME. Com cookie@0.7.2 — um pacote CJS de 2016 que só exporta parse e serialize. O Astro (que usa cookie@2.0.1) importa parseCookie do cookie — o Node subia até a home, encontrava o v0.7.2, e o named export falhava.

A pergunta que ficou: quem instalou isso na home?

cat /home/samuel/package.json
# {
#   "dependencies": {
#     "better-sqlite3": "^12.11.1",
#     "camofox-browser": "^2.4.6",
#     "puppeteer": "^25.1.0",
#     "vercel": "^34.0.0"
#   }
# }

Alguém (provavelmente um script de setup ou outro agente) rodou npm install na home em 04/08/2026 18:33 — e ninguém percebeu. O better-sqlite3 puxou 453 pacotes, incluindo cookie@0.7.2 como dependência transitiva. Um node_modules global de 253MB, silencioso, sequestrando todos os builds Node do WSL.

💡 A resolução: rm + backup

# 1. Mover pra backup (nunca rm -rf direto em 253MB)
mkdir -p ~/.npm-home-backup-20260805
mv ~/node_modules ~/package.json ~/package-lock.json ~/.npm-home-backup-20260805/

# 2. Verificar que o resolve agora está correto
node --input-type=module -e "console.log(import.meta.resolve('cookie'))"
# file:///home/samuel/projetos/lifelog/node_modules/.pnpm/cookie@2.0.1/...

# 3. Build
pnpm run build
# ✓ 130 page(s) built in 25.62s

Build passou. O erro simplesmente sumiu.

📊 Métricas

Métrica Valor
Tamanho do node_modules fantasma 253 MB
Pacotes instalados 453
cookie sequestrador v0.7.2 (2016, CJS)
cookie real do projeto v2.0.1 (2026, ESM)
Projetos afetados Todos no WSL (LifeLog, LEVE LAVANDA, etc.)
Tempo perdido debugando ~5 min (achei rápido porque segui o rastro do resolve)

🎯 Aprendizados

  1. import.meta.resolve() é a ferramenta mais subestimada do Node — quando um import quebra e o pacote existe no node_modules, a primeira coisa é perguntar onde o Node está resolvendo. import.meta.resolve('pacote') mostra o caminho REAL. Leva 2 segundos e evita horas de debug.

  2. node_modules na home NUNCA é intencional — se aparecer, é lixo. O Node module resolution sobe a árvore até a raiz do filesystem. Um node_modules em /home/usuario/ afeta todos os projetos na máquina. Não pode existir.

  3. CI vs Local: sempre desconfiar do ambiente local — quando o CI passa e local quebra, 90% das vezes é uma contaminação do ambiente (outro node_modules, PATH diferente, versão de Node global vs local). O lockfile estar igual não prova nada — a resolução é que importa.

  4. Scripts automáticos podem instalar coisas sem você saber — o npm install na home não foi feito por mim. Pode ter sido um script de setup, um agente automatizado, ou um comando rodado no diretório errado. Ter consciência do que está sendo instalado e onde é parte da higiene do ambiente.

~/lifelog — bash
$cat about.txt
╔══════════════════════════════════════╗
║  Samuel Medeiros                    ║
║  Senior Software Engineer           ║
║  Stack: Python · TypeScript · Rust  ║
║  Projetos: Arachne, Dogwalk,        ║
║            Capivara, TatuEngine      ║
╚══════════════════════════════════════╝
      
$