Workspaces & Monorepos
Workspaces do npm vinculam pacotes internos em um único repositório para que o código compartilhado seja enviado sem a necessidade de publicar em um registro a cada alteração.
Receita
Cartão de receita de referência rápida - pronto para copiar e colar.
{
"name": "@acme/platform",
"private": true,
"workspaces": ["apps/*", "packages/*"],
"scripts": {
"build": "npm run build -w @acme/shared && npm run build -w @acme/api"
}
}// apps/api/package.json
{
"name": "@acme/api",
"dependencies": {
"@acme/shared": "workspace:*"
}
}Quando usar isso:
- Múltiplos serviços implantáveis compartilham tipos ou utilitários TypeScript.
- Você deseja um único
package-lock.jsone um úniconpm cipara toda a fatia da organização. - Pacotes internos são alterados no mesmo PR que seus consumidores.
Exemplo de Trabalho
platform/
package.json # raiz dos workspaces
package-lock.json
apps/
api/
package.json # @acme/api
src/server.ts
packages/
shared/
package.json # @acme/shared
src/index.ts
// packages/shared/package.json
{
"name": "@acme/shared",
"version": "0.0.0",
"type": "module",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"scripts": {
"build": "tsc -p tsconfig.json"
}
}// apps/api/src/server.ts
import { createLogger } from "@acme/shared";
const log = createLogger("api");
log.info("listening");npm install # vincula os workspaces
npm run build -w @acme/shared
npm run dev -w @acme/apiO que isso demonstra:
workspace:*informa ao npm para criar um link simbólico (symlink) para o pacote local@acme/shared.-wdireciona para um único workspace sem precisar usarcd.- O pacote compartilhado é compilado antes que a API importe a saída compilada.
Mergulho Profundo
Como Funciona
- Os globs
workspacesna raiz descobrem os arquivospackage.jsonemapps/*epackages/*. npm installeleva (hoists) dependências compartilhadas e cria links simbólicos (symlinks) dos pacotes internos para o diretórionode_modulesdo consumidor.- O
workspace:*é resolvido para a versão local no momento da instalação; a publicação o substitui pela versão semântica concreta. - Um único
package-lock.jsonna raiz captura todo o grafo de dependências.
Protocolo workspace:
| Especificador | Significado |
|---|---|
workspace:* | Qualquer versão local (mais comum) |
workspace:^ | Compatível com a versão principal local |
workspace:1.2.3 | Fixado na versão local exata |
Notas do TypeScript
// packages/shared/tsconfig.json
{
"compilerOptions": {
"composite": true,
"declaration": true,
"outDir": "dist",
"rootDir": "src"
}
}- Habilite
compositepara referências de projeto entre pacotes. - Consumidores devem importar pelos nomes dos pacotes (
@acme/shared), não por caminhos relativos dentro depackages/shared/src.
Armadilhas
- Importar caminhos de origem entre pacotes - Ignora os limites do pacote e quebra a publicação. Correção: exportar através do
main/exportsdopackage.json. - Esquecer de compilar bibliotecas compartilhadas - A API é executada com um
dist/desatualizado. Correção: ordene os scripts debuildou use dependências de pipeline do Turborepo. - Dependências fantasmas - O hoisting permite importar pacotes não declarados. Correção: considere usar pnpm ou o ESLint
import-x/no-extraneous-dependencies. - Pacotes internos com versão 0.0.0 - Funciona para monorepos privados; bibliotecas publicáveis precisam de semver real antes de
npm publish. Correção: executenpm versionno pacote antes do lançamento externo. - Dependências circulares de workspace -
@acme/adepende de@acme/be vice-versa. Correção: extraia o núcleo compartilhado para@acme/core.
Alternativas
| Alternativa | Usar Quando | Não Usar Quando |
|---|---|---|
npm pack + file: | Teste rápido sem workspaces | Manutenção de monorepo a longo prazo |
| Verdaccio Privado / npm org | Equipes precisam de lançamentos internos versionados | Cada alteração é atômica em um único PR |
| Turborepo / Nx por cima | Builds com cache entre 5+ pacotes | Repositório com dois pacotes (exagero) |
FAQs
O que `workspace:*` significa no momento da publicação?
O npm substitui workspace:* pela versão concreta do package.json do pacote do workspace quando você executa npm publish a partir desse pacote.
Como executo um script em um workspace?
npm run test -w @acme/api
npm run build --workspaces --if-present-w direciona para um único pacote; --workspaces executa em todos.
Workspaces podem misturar aplicativos e bibliotecas?
Sim. Layout comum: apps/* para implantáveis, packages/* para bibliotecas compartilhadas. Mantenha os implantáveis claramente separados em Docker/CI.
Preciso de lockfiles separados por pacote?
Não. Um lockfile raiz é o padrão dos workspaces do npm e é preferível para CI reproduzível.
Como adiciono uma dependência a um workspace?
npm install zod -w @acme/apiO lockfile raiz é atualizado; a dependência é colocada no package.json desse workspace.
Pacotes internos devem ser privados?
Marque "private": true em pacotes que nunca serão publicados externamente. Remova apenas ao publicar no npm com proveniência.
Como o TypeScript encontra os tipos do workspace?
Arquivos .d.ts compilados em dist/ mais o campo types no package.json. Compile pacotes compartilhados antes de fazer a verificação de tipos dos dependentes.
Posso usar Express e Fastify em workspaces diferentes?
Sim. Cada workspace de aplicativo é responsável por sua dependência de framework; o código compartilhado permanece agnóstico ao framework.
E se dois workspaces precisarem de versões diferentes da mesma biblioteca?
O npm pode instalar várias versões aninhadas no lockfile. Prefira alinhar as versões para reduzir o tamanho do bundle e a superfície de auditoria.
Quando devo publicar pacotes internos no npm?
Quando outro repositório os consome ou quando você precisa de limites semver entre equipes. Até lá, workspace:* é suficiente.
Relacionados
- Noções Básicas de Gerenciadores de Pacotes - pnpm vs npm para monorepos
- Turborepo para Node - builds de workspace com cache
- Monorepo Multi-Serviço - limites de serviço
Versões da Stack: Esta página foi escrita para Node.js 24.18.0 (LTS Ativo), npm 10+, TypeScript 5.6+, Express 5, Fastify 5 e NestJS 11.