Você finalmente terminou aquela biblioteca utilitária ou componente que resolve um problemão. Agora, quer publicar no NPM. Mas aí vem a dúvida: como garantir que ela funcione para todo mundo?
Hoje em dia, o ecossistema JavaScript está dividido entre o antigo CommonJS (CJS) e o moderno ES Modules (ESM). Se você configurar errado, seu usuário vai dar de cara com o erro: Err: Module Not Found ou require is not defined.
Neste guia, vamos dissecar o package.json perfeito.
1. O Ponto de Entrada: main vs module
Antigamente, só precisávamos do campo main. Hoje, a história é outra:
*main: * Aponta para o arquivo no formato CommonJS. É o que permite que alguém use const suaLib = require('sua-lib'). Geralmente gerado em .js puro.
module: Aponta para o arquivo no formato ES Modules. É o que permite o uso de import { funcionalidade } from 'sua-lib'. Geralmente gerado em .mjs.
Por que ter os dois? Porque você quer que sua biblioteca seja “Universal”. Se o seu usuário estiver em um projeto Legado, o main salva ele. Se estiver em um projeto moderno (como Vite ou Next.js), o module garante performance e suporte a Tree Shaking (remover código que não está sendo usado).
2. A Magia do campo exports
Este é o campo mais moderno e poderoso. Ele funciona como um “roteador” de módulos.
JSON
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs",
"require": "./dist/index.js"
}
}
A ordem importa: Sempre coloque types primeiro para que o TypeScript reconheça as definições antes de tentar ler o código.
Encapsulamento: O campo exports impede que os usuários acessem arquivos internos da sua biblioteca que não deveriam ser mexidos. Se não estiver no exports, o usuário não consegue importar.
3. TypeScript e o campo types
Mesmo que você não tenha escrito sua lib em TypeScript, o arquivo .d.ts é obrigatório para uma boa experiência do desenvolvedor (DX). Sem ele, o usuário não tem o “autocompletar” no VS Code. Ele fica “no escuro” sem saber quais funções sua biblioteca oferece ou quais parâmetros elas recebem.
4. O que realmente vai para o NPM? (files)
Por padrão, quando você roda npm publish, o NPM envia quase tudo da sua pasta. Isso é ruim por dois motivos:
O pacote fica pesado (incluindo testes, arquivos de configuração e código fonte original).
Expõe sua lógica de desenvolvimento desnecessariamente.
Use o campo files para criar uma “lista branca”:
JSON
"files": [
"dist"
]
Isso diz ao NPM: “Só leve o que estiver na pasta dist”.
5. Como automatizar tudo isso?
Não tente escrever os arquivos da pasta dist na mão. A dica de ouro é usar o tsup. Ele é um “bundler” que não precisa de configuração.
Passo a passo rápido:
Instale: npm install tsup -D
No package.json, adicione o script: "build": "tsup src/index.ts --format cjs,esm --dts"
Ao rodar npm run build, o tsup vai:
Ler seu arquivo fonte.
Gerar o .js (CommonJS).
Gerar o .mjs (ES Module).
Gerar o .d.ts (Tipagens).
Conclusão
Configurar o package.json corretamente é a diferença entre uma biblioteca profissional e uma que gera issues no GitHub em 5 minutos. Com essa estrutura, você garante que qualquer desenvolvedor, independente da tecnologia que use, consiga integrar sua solução sem atrito.
Top comments (0)