Home > Blog > Desenvolvimento Web
Desenvolvimento Web
JavaScript
Programação

npm Scripts no Node.js

Atualizado em: 9 de setembro de 2026

Terminal Linux usado em desenvolvimento e administração de aplicações Node.js

Os npm Scripts no Node.js transformam comandos de desenvolvimento, teste, build e publicação em uma interface padronizada dentro do package.json. Em vez de cada pessoa memorizar opções ou instalar CLIs globalmente, o projeto declara scripts que usam executáveis locais do node_modules/.bin.

Scripts simples tornam o fluxo reproduzível e funcionam no terminal, editor e CI. Porém, cadeias longas com shell específico, lifecycle hooks inesperados e comandos destrutivos podem dificultar portabilidade e segurança.

Neste guia, você aprenderá a criar scripts, passar argumentos, usar pre e post hooks, compreender eventos de lifecycle, acessar variáveis, escrever scripts portáveis, trabalhar com workspaces e proteger instalações.

O campo scripts

{
  "scripts": {
    "dev": "node --watch src/server.js",
    "start": "node dist/server.js",
    "build": "tsc -p tsconfig.build.json",
    "test": "node --test",
    "lint": "eslint .",
    "format": "prettier . --write"
  }
}

Execute:

npm run build
npm test
npm start

start, test, stop e restart possuem atalhos. Scripts arbitrários usam npm run nome.

Executáveis locais no PATH

A documentação oficial de npm Scripts explica que binários das dependências são adicionados ao PATH durante a execução. Por isso:

{
  "devDependencies": {
    "eslint": "10.7.0"
  },
  "scripts": {
    "lint": "eslint ."
  }
}

Não é necessário escrever ./node_modules/.bin/eslint nem instalar globalmente.

Passando argumentos

npm run test -- --test-name-pattern="pedido"

O separador -- encaminha os argumentos seguintes ao comando do script.

Compondo scripts

{
  "scripts": {
    "check": "npm run lint && npm run typecheck && npm test",
    "lint": "eslint .",
    "typecheck": "tsc --noEmit",
    "test": "node --test"
  }
}

&& executa a próxima etapa somente quando a anterior termina com código zero. Para lógica complexa ou compatibilidade entre shells, crie um script Node.js.

Scripts Node.js

{
  "scripts": {
    "release:check": "node scripts/release-check.js"
  }
}
// scripts/release-check.js
import { readFile } from 'node:fs/promises';

const pkg = JSON.parse(
  await readFile(new URL('../package.json', import.meta.url), 'utf8')
);

if (!pkg.version) {
  console.error('Versão ausente');
  process.exitCode = 1;
}

JavaScript oferece validação, tratamento de erros e portabilidade melhores que uma linha de shell enorme.

Pre e post scripts

{
  "scripts": {
    "pretest": "npm run build:test",
    "test": "node --test",
    "posttest": "node scripts/cleanup-test.js"
  }
}

npm test executa pretest, test e posttest. Use hooks com moderação, porque comportamento implícito surpreende quem lê apenas o script principal.

Lifecycle de publicação

Eventos importantes:

  • prepare;
  • prepublishOnly;
  • prepack;
  • postpack;
  • publish;
  • postpublish.

prepare

{
  "scripts": {
    "prepare": "npm run build",
    "build": "tsc -p tsconfig.build.json"
  }
}

prepare roda antes de pack e publish, além de certas instalações locais ou por Git. Isso permite compilar TypeScript antes do pacote ser usado, mas também torna instalações mais lentas e exige devDependencies quando a origem é Git.

prepublish é confuso

O evento prepublish é legado e não roda como muitos esperam durante npm publish. Prefira:

  • prepare para build;
  • prepublishOnly para validação exclusiva antes de publicar;
  • prepack para preparar o tarball.

prepublishOnly

{
  "scripts": {
    "prepublishOnly": "npm run check && npm run build"
  }
}

Não confie apenas nesse hook em publicação automatizada. O CI também precisa bloquear release sem testes.

prepack

{
  "scripts": {
    "prepack": "npm run clean && npm run build"
  }
}

npm pack e npm publish executam o evento. Teste com:

npm pack --dry-run

install e postinstall

Evite scripts de instalação sempre que possível. Eles executam durante instalação de dependências e ampliam riscos de supply chain. Casos legítimos incluem compilação nativa necessária à arquitetura, mas o npm já reconhece binding.gyp em situações padrão.

Para segurança, consulte Supply Chain no Node.js.

Ignorando scripts de dependências

npm ci --ignore-scripts

Essa opção reduz risco, mas pode impedir funcionamento de pacotes que precisam de build. Avalie dependências e aprove scripts conscientemente.

Diretório de execução

Scripts são executados na raiz do pacote, mesmo quando o comando é chamado de outro diretório. O ambiente INIT_CWD guarda o diretório original.

console.log(process.cwd());
console.log(process.env.INIT_CWD);

Variáveis npm

Variáveis úteis:

  • npm_package_name;
  • npm_package_version;
  • npm_package_json;
  • npm_lifecycle_event;
  • npm_old_version;
  • npm_new_version.
console.log({
  name: process.env.npm_package_name,
  version: process.env.npm_package_version,
  event: process.env.npm_lifecycle_event
});

Para outros campos, leia o arquivo indicado por npm_package_json.

Variáveis de ambiente

No Linux:

NODE_ENV=test node --test

Essa sintaxe não funciona igual no Windows. Use cross-env quando o projeto precisa ser multiplataforma:

npm install --save-dev cross-env
{
  "scripts": {
    "test": "cross-env NODE_ENV=test node --test"
  }
}

Consulte Variáveis de Ambiente no Node.js.

Shell e portabilidade

npm usa /bin/sh em sistemas POSIX e cmd.exe no Windows. Recursos como expansão de glob, remoção de diretórios e atribuição de variável podem divergir.

Prefira:

  • CLIs multiplataforma;
  • scripts Node.js;
  • APIs nativas como fs.rm;
  • cross-env para variáveis;
  • comandos pequenos.

Script de limpeza portável

// scripts/clean.js
import { rm } from 'node:fs/promises';

await Promise.all([
  rm(new URL('../dist', import.meta.url), {
    recursive: true,
    force: true
  }),
  rm(new URL('../coverage', import.meta.url), {
    recursive: true,
    force: true
  })
]);
{
  "scripts": {
    "clean": "node scripts/clean.js"
  }
}

Execução paralela

Para tarefas independentes, uma ferramenta como npm-run-all2 ou concurrently pode ajudar. Fixe a dependência e trate sinais corretamente.

{
  "scripts": {
    "dev": "concurrently -k \"npm:dev:api\" \"npm:dev:worker\""
  }
}

Watch mode

{
  "scripts": {
    "dev": "node --watch src/server.js",
    "test:watch": "node --test --watch"
  }
}

Veja Watch Mode no Node.js.

Workspaces

npm run test --workspace=@empresa/api
npm run test --workspaces --if-present

Consulte npm Workspaces no Node.js.

Scripts na raiz do monorepo

{
  "scripts": {
    "check": "npm run lint --workspaces --if-present && npm run test --workspaces --if-present",
    "build": "npm run build --workspaces --if-present"
  }
}

Se a ordem de dependências importa, use grafo explícito ou ferramenta de monorepo.

CI

- run: npm ci
- run: npm run check
- run: npm run build

Veja CI para Node.js com GitHub Actions.

Códigos de saída

Quando um script termina com código diferente de zero, npm considera falha. Não esconda erros críticos:

try {
  await validate();
} catch (error) {
  console.error(error);
  process.exitCode = 1;
}

Use warning e código zero somente para recurso realmente opcional.

Sinais

Ferramentas de desenvolvimento que iniciam processos precisam encaminhar SIGINT e SIGTERM. Caso contrário, portas e containers podem permanecer ativos. Teste Ctrl+C no Windows e Linux.

Nomes consistentes

Convenções úteis:

  • dev;
  • start;
  • build;
  • clean;
  • lint e lint:fix;
  • format e format:check;
  • test, test:unit, test:integration;
  • typecheck;
  • check.

Não coloque segredos em scripts

// Nunca
"deploy": "TOKEN=segredo comando"

Use secret store e variáveis protegidas. Scripts ficam no repositório e podem aparecer em logs.

Command injection

Não concatene entrada não confiável em comandos:

exec(`tool ${userInput}`);

Use spawn com argumentos separados, validação e allowlist quando precisar executar processos. Veja Child Process no Node.js.

Scripts pequenos

Quando um script ultrapassa uma linha complexa, mova a lógica para arquivo. Isso permite teste unitário, comentários e tratamento de plataforma.

Erros comuns

  • CLI global: versão diverge.
  • Shell específico: falha no Windows.
  • Pre/post escondidos: comportamento surpreende.
  • postinstall desnecessário: risco e lentidão.
  • Segredo inline: vazamento.
  • Comando gigante: difícil de testar.
  • Erro ignorado: CI passa incorretamente.
  • Script duplicado: local e CI divergem.

Exemplo recomendado

{
  "scripts": {
    "dev": "node --watch src/server.js",
    "clean": "node scripts/clean.js",
    "build": "npm run clean && tsc -p tsconfig.build.json",
    "lint": "eslint .",
    "lint:fix": "eslint . --fix",
    "format": "prettier . --write",
    "format:check": "prettier . --check",
    "typecheck": "tsc --noEmit",
    "test": "node --test",
    "check": "npm run lint && npm run format:check && npm run typecheck && npm test",
    "start": "node dist/server.js",
    "prepack": "npm run check && npm run build"
  }
}

Conclusão

Os npm Scripts no Node.js oferecem uma interface única para tarefas do projeto. Executáveis locais, nomes consistentes e scripts pequenos tornam desenvolvimento e CI reproduzíveis.

Use lifecycle hooks apenas quando a semântica corresponde, evite scripts de instalação desnecessários e mova lógica complexa para JavaScript. Com portabilidade e códigos de saída corretos, os scripts deixam de ser atalhos frágeis e se tornam parte confiável do processo de engenharia.

10 melhores cursos de programação em 2026

Descubra os melhores cursos de programação. Aprenda a escolher o curso ideal para iniciar ou avançar na carreira de desenvolvedor

POSTS RELACIONADOS

Ver todos

Seta para a direita