Criar e publicar um pacote npm com Claude Code
Crie um pacote npm com Claude Code usando tsup, exports, tipos, npm pack, README gerado e publicação via CI.
Pedir ao Claude Code para “criar um pacote npm” é simples. Publicar um pacote que outra pessoa consiga instalar com confiança exige mais cuidado. Você precisa de package.json consistente, exports corretos, tipos gerados, README alinhado com a API, checagem com npm pack e uma esteira de CI que publique somente quando os critérios forem cumpridos.
Neste guia vamos criar um pequeno pacote TypeScript de utilitários de string. O Claude Code será usado como parceiro de implementação e revisão, não como autoridade para publicar sem supervisão. Para decisões reais, consulte as fontes oficiais: npm package.json, scoped public packages, npm pack, trusted publishing e a documentação do Claude Code.
Defina o contrato do pacote
O primeiro prompt não deve ser genérico. Informe nome do pacote, público, runtime, formatos de módulo, comandos de verificação e política de publicação. type: "module", main, exports e types precisam apontar para a mesma história. Se um deles for gerado isoladamente, o pacote pode compilar e ainda assim falhar no projeto do usuário.
| Área | Decisão do exemplo | O que o Claude Code deve verificar |
|---|---|---|
| Nome | @acme/string-kit | se pacote scoped público exige --access public |
| Usuários | Node.js e TypeScript | se ESM import e CJS require funcionam |
| Build | tsup gera ESM, CJS e tipos | se dist contém os arquivos em exports |
| Conteúdo | apenas dist, README.md, LICENSE | se npm pack --dry-run mostra arquivos inesperados |
| Publicação | GitHub Actions + npm Trusted Publishing | se evita tokens npm de longa duração |
O diagrama abaixo ajuda a manter implementação, testes, pacote e publicação no mesmo fluxo.
flowchart LR
A["Brief do pacote"] --> B["package.json"]
B --> C["src/index.ts"]
C --> D["Vitest"]
D --> E["tsup build"]
E --> F["npm pack dry-run"]
F --> G["CI publish"]
Se o pacote for uma CLI, veja também desenvolvimento de CLI com Claude Code. Para melhorar prompts e verificações, leia dicas de produtividade com Claude Code.
Crie o projeto mínimo
Comece em uma pasta limpa. Em um repositório grande, erros de pacote podem se misturar com workspace, dependências antigas e scripts já existentes.
mkdir string-kit
cd string-kit
npm init -y
npm install -D typescript tsup vitest @types/node
mkdir src scripts
O package.json é o contrato público. main atende CJS, module ajuda alguns bundlers, types atende TypeScript e exports controla os pontos de entrada modernos. files fica restrito para evitar publicar testes, rascunhos, sourcemaps ou configurações locais.
{
"name": "@acme/string-kit",
"version": "0.1.0",
"description": "Small TypeScript string utilities used as an npm package example.",
"type": "module",
"main": "./dist/index.cjs",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js",
"require": "./dist/index.cjs"
},
"./package.json": "./package.json"
},
"files": ["dist", "README.md", "LICENSE"],
"sideEffects": false,
"scripts": {
"build": "tsup",
"test": "vitest run",
"docs": "node scripts/write-readme.mjs",
"test:pack": "npm pack --dry-run",
"prepublishOnly": "npm run test && npm run build && npm run test:pack"
},
"keywords": ["string", "typescript", "utilities"],
"license": "MIT",
"devDependencies": {
"@types/node": "^22.15.0",
"tsup": "^8.5.0",
"typescript": "^5.8.0",
"vitest": "^3.2.0"
}
}
TypeScript verifica os tipos e o tsup gera os arquivos finais. Essa divisão deixa claro para o Claude Code que dist precisa corresponder aos caminhos expostos.
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "Bundler",
"strict": true,
"declaration": true,
"declarationMap": true,
"skipLibCheck": true,
"noEmit": true
},
"include": ["src", "tsup.config.ts"]
}
Implementação real e testes
Evite exemplo que parece código mas não roda. Este pacote exporta quatro funções pequenas: slugify para slugs ASCII, truncate para limitar texto, interpolate para templates simples e byteLength para contar bytes UTF-8.
export function slugify(input: string): string {
return input
.normalize("NFKD")
.replace(/[\u0300-\u036f]/g, "")
.toLowerCase()
.trim()
.replace(/[^a-z0-9]+/g, "-")
.replace(/^-+|-+$/g, "");
}
export function truncate(input: string, maxLength: number, suffix = "..."): string {
if (!Number.isInteger(maxLength) || maxLength < 1) {
throw new RangeError("maxLength must be a positive integer");
}
if (suffix.length >= maxLength) {
throw new RangeError("suffix must be shorter than maxLength");
}
if (input.length <= maxLength) return input;
return `${input.slice(0, maxLength - suffix.length)}${suffix}`;
}
export function interpolate(template: string, values: Record<string, string | number>): string {
return template.replace(/\{\{\s*([\w.-]+)\s*\}\}/g, (match, key: string) => {
return Object.hasOwn(values, key) ? String(values[key]) : match;
});
}
export function byteLength(input: string): number {
return new TextEncoder().encode(input).length;
}
Os testes cobrem casos normais, limites, exceções e Unicode. Ao pedir testes ao Claude Code, seja explícito: acentos, placeholders desconhecidos, comprimentos inválidos e bytes UTF-8.
import { describe, expect, it } from "vitest";
import { byteLength, interpolate, slugify, truncate } from "./index";
describe("slugify", () => {
it("turns a title into an npm-friendly slug", () => {
expect(slugify("Hello npm Package!")).toBe("hello-npm-package");
});
it("removes accents before replacing separators", () => {
expect(slugify("Crème brûlée utils")).toBe("creme-brulee-utils");
});
});
describe("truncate", () => {
it("keeps short text unchanged", () => {
expect(truncate("short", 10)).toBe("short");
});
it("adds a suffix inside the requested length", () => {
expect(truncate("Claude Code package", 12)).toBe("Claude Co...");
});
it("rejects invalid lengths", () => {
expect(() => truncate("abc", 2)).toThrow(RangeError);
});
});
describe("interpolate", () => {
it("replaces known placeholders and keeps unknown ones", () => {
expect(interpolate("Hi {{ name }}, ship {{pkg}} {{missing}}", {
name: "Masa",
pkg: "@acme/string-kit",
})).toBe("Hi Masa, ship @acme/string-kit {{missing}}");
});
});
describe("byteLength", () => {
it("counts UTF-8 bytes", () => {
expect(byteLength("npm")).toBe(3);
expect(byteLength("日本語")).toBe(9);
});
});
tsup e README gerado
A configuração do tsup é pequena. outExtension emite ESM como .js e CJS como .cjs, alinhado com package.json. O sourcemap fica desligado para não publicar mapeamentos internos sem uma decisão consciente.
import { defineConfig } from "tsup";
export default defineConfig({
entry: ["src/index.ts"],
format: ["esm", "cjs"],
dts: true,
clean: true,
sourcemap: false,
minify: false,
target: "es2022",
outDir: "dist",
outExtension({ format }) {
return { js: format === "esm" ? ".js" : ".cjs" };
},
});
README desatualizado é uma fonte comum de suporte. Um gerador simples mantém instalação e uso próximos da API real.
import { writeFile } from "node:fs/promises";
const fence = String.fromCharCode(96).repeat(3);
const readme = `# @acme/string-kit
Small TypeScript string utilities packaged with tsup.
## Install
${fence}bash
npm install @acme/string-kit
${fence}
## Usage
${fence}ts
import { slugify, truncate } from "@acme/string-kit";
console.log(slugify("Hello npm Package!"));
console.log(truncate("Claude Code package", 12));
${fence}
`;
await writeFile(new URL("../README.md", import.meta.url), readme);
Verifique com npm pack
npm publish não deve ser a primeira verificação real. npm pack --dry-run mostra os arquivos que serão enviados. Revise README, LICENSE, tipos, ESM/CJS e a ausência de testes ou rascunhos.
npm run docs
npm test
npm run build
npm pack --dry-run
node -e "import('./dist/index.js').then((m)=>console.log(m.slugify('Hello ESM')))"
node -e "const m=require('./dist/index.cjs'); console.log(m.slugify('Hello CJS'))"
Para uma prova mais próxima do usuário, instale o .tgz em outra pasta.
npm pack
mkdir ../string-kit-smoke
cd ../string-kit-smoke
npm init -y
npm install ../string-kit/acme-string-kit-0.1.0.tgz
node -e "import('@acme/string-kit').then((m)=>console.log(m.truncate('Claude Code package', 12)))"
Três usos práticos aparecem muito. Um time de produto compartilha regras de texto entre web, admin e documentação. Um fluxo de conteúdo usa truncate e interpolate em descrições, cards e notas de versão. Um design system ou CLI publica utilitários pequenos para que apps atualizem por SemVer.
Publique via GitHub Actions
Publicar por CI torna o caminho revisável. npm Trusted Publishing permite publicação por OIDC e reduz tokens npm longos. Configure o trusted publisher no npm e publique apenas em evento de release.
name: package
on:
push:
branches: [main]
pull_request:
release:
types: [published]
permissions:
contents: read
id-token: write
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
registry-url: https://registry.npmjs.org
cache: npm
- run: npm ci
- run: npm run docs
- run: npm test
- run: npm run build
- run: npm pack --dry-run
publish:
if: github.event_name == 'release'
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
registry-url: https://registry.npmjs.org
cache: npm
- run: npm ci
- run: npm run docs
- run: npm test
- run: npm run build
- run: npm publish --access public
Para changelog e SemVer mais organizados, combine com Claude Code e Changesets.
Armadilhas comuns
A primeira armadilha é usar só main e esquecer exports. A segunda é publicar arquivos demais por não limitar files. A terceira é manter README antigo depois de mudar nomes de funções ou package scope. A quarta é prometer Unicode completo quando truncate usa comprimento de string JavaScript, não graphemes visuais. A quinta é deixar Claude Code decidir nome, scope, 2FA, Trusted Publishing e aprovação final de release.
Prompt para Claude Code
Create a TypeScript npm package.
Goal:
- Package name: @acme/string-kit
- Support both ESM import and CJS require
- Use tsup to emit dist/index.js, dist/index.cjs, and dist/index.d.ts
- Include README generation, Vitest tests, and npm pack verification
Constraints:
- Only touch package.json, tsconfig.json, tsup.config.ts, src, scripts, and .github/workflows
- Do not use pseudocode; the project must run after npm install
- Do not publish source maps or unnecessary test files in the package tarball
Acceptance criteria:
- npm test passes
- npm run build passes
- npm pack --dry-run output is summarized
- ESM import and CJS require smoke tests are shown
- List the human release checks before npm publish
CTA: transforme publicação em modelo
Publicar em npm não termina na primeira versão. README, CI, dependências, versões e permissões mudam. Comece pela cola gratuita de Claude Code para guardar prompts seguros e comandos de verificação. Para modelos reutilizáveis, veja os produtos ClaudeCodeLab. Para times que precisam organizar CLAUDE.md, CI, permissões e revisão, use treinamento e consultoria Claude Code.
Testei este fluxo em uma pasta temporária no Windows: npm install, npm test, npm run build, npm pack --dry-run e smoke tests ESM/CJS com node -e passaram. Na rotina de Masa, pedir ao Claude Code que explique a saída de npm pack antes do release encontra README desatualizado, tipos ausentes e arquivos inesperados no tarball.
Resumo
Claude Code acelera a criação de pacotes npm, mas o contrato de publicação precisa ser explícito. Una package.json, exports, tipos, tsup, testes, README, npm pack e CI. Antes de publicar, verifique dist, o tarball e os pontos de entrada que o usuário realmente usará.
PDF grátis: cheatsheet do Claude Code
Informe seu e-mail e baixe uma página com comandos, hábitos de revisão e workflows seguros.
Cuidamos dos seus dados e não enviamos spam.
Sobre o autor
Masa
Engenheiro focado em workflows práticos com Claude Code.
Artigos relacionados
Escada de segurança de permissões no Claude Code
Amplie de read-only para edições limitadas, comandos de prova e deploy checks sem perder controle.
Claude Code Small PR Proof Pack: pequenas mudanças fáceis de revisar
Um pacote de prova para PRs do Claude Code: diff, checks, URL pública, CTA e rollback.
Gate de revisão antes do commit com Claude Code
Revisão antes do commit com Claude Code: diff, build, URL pública, Gumroad, consultoria, testes e arquivos fora do escopo.