Javascript

ESLint e Prettier: código limpo e padronizado Já leu

10 min de leitura

ESLint e Prettier: código limpo e padronizado
Imagine entrar em um projeto onde cada arquivo tem um estilo diferente — aspas simples em um, duplas em outro, ponto e vírgula em alguns, ausente em outros, indentação com 2 espaços aqui e 4 ali. Agora imagine o mesmo pr

Imagine entrar em um projeto onde cada arquivo tem um estilo diferente — aspas simples em um, duplas em outro, ponto e vírgula em alguns, ausente em outros, indentação com 2 espaços aqui e 4 ali. Agora imagine o mesmo projeto sendo mantido por cinco desenvolvedores sem nenhuma padronização.

É caos. E esse caos tem custo real: revisões de código que discutem estilo em vez de lógica, diffs enormes por mudanças de formatação, e bugs introduzidos por erros que uma ferramenta detectaria em segundos.

ESLint e Prettier resolvem isso — um encontra problemas no código, o outro garante que todos escrevam da mesma forma.


ESLint vs Prettier — papéis diferentes

┌─────────────────────────────────────────────────────────┐
│  ESLINT — Linter                                        │
│                                                         │
│  "Seu código tem problemas"                             │
│  ✗ Variável declarada mas nunca usada                   │
│  ✗ == em vez de ===                                     │
│  ✗ console.log esquecido                                │
│  ✗ Função sem return explícito                          │
│  ✗ Import não utilizado                                 │
│  Foco: qualidade e correção                             │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│  PRETTIER — Formatter                                   │
│                                                         │
│  "Seu código vai ficar assim"                           │
│  → Aspas simples ou duplas (você escolhe)               │
│  → Ponto e vírgula ou não                               │
│  → 80 ou 120 colunas por linha                          │
│  → 2 ou 4 espaços de indentação                         │
│  → Vírgula no último item de objeto                     │
│  Foco: consistência visual — sem debates                │
└─────────────────────────────────────────────────────────┘

Usados juntos: o Prettier formata, o ESLint encontra problemas lógicos.


Instalando ESLint

npm install -D eslint

# Inicializar configuração interativamente
npx eslint --init

Ou criar manualmente:

npm install -D eslint @eslint/js
// eslint.config.js (formato moderno — ESLint 9+)
const js = require("@eslint/js");

module.exports = [
  js.configs.recommended,
  {
    languageOptions: {
      ecmaVersion: 2024,
      sourceType: "commonjs",
      globals: {
        require: "readonly",
        module: "readonly",
        exports: "readonly",
        __dirname: "readonly",
        __filename: "readonly",
        process: "readonly",
        console: "readonly",
      },
    },
    rules: {
      // ── Erros graves ─────────────────────────────
      "no-unused-vars": ["error", { argsIgnorePattern: "^_" }],
      "no-undef": "error",
      "no-unreachable": "error",
      "no-duplicate-case": "error",

      // ── Boas práticas ────────────────────────────
      "eqeqeq": ["error", "always"],        // sempre === nunca ==
      "no-console": "warn",                  // avisa sobre console.log
      "no-var": "error",                     // proíbe var
      "prefer-const": "error",              // prefere const
      "prefer-arrow-callback": "warn",      // arrow functions em callbacks
      "no-return-await": "error",           // return await desnecessário
      "require-await": "error",             // async sem await
      "no-throw-literal": "error",          // throw new Error(), não throw "string"

      // ── Estilo (o Prettier cuida do resto) ───────
      "no-multiple-empty-lines": ["warn", { max: 2 }],
      "no-trailing-spaces": "warn",
    },
    ignores: ["node_modules/", "coverage/", "dist/", "build/"],
  },
];

Rodando o ESLint

# Verificar todos os arquivos da pasta src
npx eslint src/

# Verificar um arquivo específico
npx eslint src/index.js

# Corrigir automaticamente o que for possível
npx eslint src/ --fix

# Saída típica:
# src/controllers/authController.js
#   12:5  error    'token' is assigned a value but never used  no-unused-vars
#   34:9  warning  Unexpected console statement                no-console
#   67:3  error    Expected '===' and instead saw '=='         eqeqeq
#
# ✖ 3 problems (2 errors, 1 warning)
#   0 errors and 1 warning potentially fixable with the `--fix` option.

Regras do ESLint — entendendo os níveis

{
  rules: {
    // "off" ou 0    → regra desabilitada
    // "warn" ou 1   → aviso (não falha o build)
    // "error" ou 2  → erro (falha o build)

    "no-console": "off",                // desabilitada
    "no-unused-vars": "warn",           // apenas aviso
    "eqeqeq": "error",                 // falha o build

    // Regras com opções
    "no-unused-vars": ["error", {
      vars: "all",                     // todas as variáveis
      args: "after-used",              // args usados após o último usado
      argsIgnorePattern: "^_",         // ignora args começando com _
      ignoreRestSiblings: true,
    }],

    "prefer-const": ["error", {
      destructuring: "any",            // const se qualquer var pode ser const
    }],
  }
}

Ignorando arquivos e linhas

# .eslintignore (alternativa ao campo ignores no config)
node_modules/
dist/
coverage/
*.min.js
public/
// Ignorar linha específica
const x = 1; // eslint-disable-line no-unused-vars

// Ignorar próxima linha
// eslint-disable-next-line no-console
console.log("debug");

// Ignorar bloco inteiro
/* eslint-disable no-console */
console.log("início do bloco");
console.log("meio do bloco");
/* eslint-enable no-console */

// ⚠️ Use com moderação — desabilitar regras
// esconde problemas reais

Instalando e configurando o Prettier

npm install -D prettier
// .prettierrc
{
  "semi": true,
  "singleQuote": true,
  "quoteProps": "as-needed",
  "trailingComma": "es5",
  "tabWidth": 2,
  "useTabs": false,
  "printWidth": 100,
  "bracketSpacing": true,
  "arrowParens": "always",
  "endOfLine": "lf"
}
# .prettierignore
node_modules/
dist/
coverage/
package-lock.json
*.min.js

O que o Prettier faz na prática

// ── Antes do Prettier ────────────────────────────
const usuario={nome:'Ana',   email:"ana@email.com",  idade:28}

function saudar(nome,     saudacao='Olá'){
    return `${saudacao}, ${nome}!`
}

const numeros = [1,2,3,4,5]
const dobro = numeros.map(n=>n*2)

// ── Depois do Prettier ───────────────────────────
const usuario = { nome: 'Ana', email: 'ana@email.com', idade: 28 };

function saudar(nome, saudacao = 'Olá') {
  return `${saudacao}, ${nome}!`;
}

const numeros = [1, 2, 3, 4, 5];
const dobro = numeros.map((n) => n * 2);

O Prettier não pergunta — ele decide. Essa é a vantagem: fim dos debates de estilo.


Rodando o Prettier

# Verificar arquivos (não modifica)
npx prettier --check src/

# Formatar todos os arquivos
npx prettier --write src/

# Formatar arquivo específico
npx prettier --write src/index.js

# Saída do --check:
# Checking formatting...
# [warn] src/controllers/authController.js
# [warn] src/utils/validacoes.js
# [warn] Code style issues found in 2 files.
#        Run Prettier to fix.

Integrando ESLint + Prettier — sem conflitos

O ESLint tem regras de formatação que conflitam com o Prettier. A solução é desabilitar essas regras do ESLint e deixar apenas o Prettier cuidar da formatação:

npm install -D eslint-config-prettier
// eslint.config.js — adicione prettier ao final
const js = require("@eslint/js");
const prettier = require("eslint-config-prettier");

module.exports = [
  js.configs.recommended,
  {
    // ... suas regras
  },
  prettier, // deve ser o ÚLTIMO — desabilita regras conflitantes
];

Agora ESLint cuida da qualidade, Prettier cuida do estilo — sem sobreposição.


Scripts no package.json

{
  "scripts": {
    "lint": "eslint src/",
    "lint:fix": "eslint src/ --fix",
    "format": "prettier --write src/",
    "format:check": "prettier --check src/",
    "quality": "npm run format:check && npm run lint"
  }
}
npm run lint          # verifica problemas
npm run lint:fix      # corrige o que é possível automaticamente
npm run format        # formata todos os arquivos
npm run quality       # executa ambas as verificações

Configuração do VS Code

Para que o editor formate automaticamente ao salvar:

// .vscode/settings.json
{
  "editor.formatOnSave": true,
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": "explicit"
  },
  "[javascript]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[json]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  }
}

Extensões necessárias:

  • esbenp.prettier-vscode — Prettier
  • dbaeumer.vscode-eslint — ESLint

Husky + lint-staged — validação no commit

Garante que ninguém commite código com erros — o hook roda automaticamente antes de cada git commit:

npm install -D husky lint-staged

# Inicializa o Husky
npx husky init
# .husky/pre-commit
npx lint-staged
// package.json
{
  "lint-staged": {
    "src/**/*.js": [
      "prettier --write",
      "eslint --fix",
      "eslint"
    ],
    "*.{json,md}": [
      "prettier --write"
    ]
  }
}

Agora, ao tentar fazer commit:

git add .
git commit -m "feat: adiciona rota de usuários"

# ✔ Prettier formatou 3 arquivos
# ✔ ESLint corrigiu 1 problema
# ✖ ESLint encontrou 1 erro não corrigível:
#   src/controllers/authController.js:42
#   'usuario' is assigned a value but never used
#
# ✖ Commit bloqueado. Corrija os erros antes de commitar.

O commit só passa se o código estiver limpo.


EditorConfig — padronização entre editores

Mesmo antes do Prettier, o EditorConfig garante que diferentes editores (VS Code, WebStorm, Vim) usem a mesma indentação:

# .editorconfig
root = true

[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

[*.md]
trim_trailing_whitespace = false

[*.{json,yml,yaml}]
indent_size = 2

Configuração completa para projeto Node.js

Aqui está uma configuração profissional e pronta para usar:

# Instalar tudo de uma vez
npm install -D 
  eslint 
  @eslint/js 
  prettier 
  eslint-config-prettier 
  husky 
  lint-staged
// eslint.config.js
const js = require("@eslint/js");
const prettier = require("eslint-config-prettier");

module.exports = [
  js.configs.recommended,
  {
    languageOptions: {
      ecmaVersion: 2024,
      sourceType: "commonjs",
      globals: {
        require: "readonly",
        module: "readonly",
        exports: "readonly",
        __dirname: "readonly",
        __filename: "readonly",
        process: "readonly",
        console: "readonly",
        Buffer: "readonly",
        setTimeout: "readonly",
        clearTimeout: "readonly",
        setInterval: "readonly",
        clearInterval: "readonly",
      },
    },
    rules: {
      "no-unused-vars": ["error", { argsIgnorePattern: "^_" }],
      "no-undef": "error",
      "eqeqeq": ["error", "always"],
      "no-var": "error",
      "prefer-const": "error",
      "no-console": ["warn", { allow: ["warn", "error", "info"] }],
      "no-return-await": "error",
      "require-await": "error",
      "no-throw-literal": "error",
      "no-duplicate-imports": "error",
      "no-shadow": "warn",
      "consistent-return": "warn",
    },
    ignores: ["node_modules/", "coverage/", "dist/"],
  },
  prettier,
];
// .prettierrc
{
  "semi": true,
  "singleQuote": true,
  "trailingComma": "es5",
  "tabWidth": 2,
  "printWidth": 100,
  "arrowParens": "always",
  "endOfLine": "lf"
}
// package.json
{
  "scripts": {
    "lint": "eslint src/",
    "lint:fix": "eslint src/ --fix",
    "format": "prettier --write .",
    "format:check": "prettier --check .",
    "quality": "npm run format:check && npm run lint",
    "prepare": "husky"
  },
  "lint-staged": {
    "src/**/*.js": ["prettier --write", "eslint --fix", "eslint"],
    "*.{json,md,yml}": ["prettier --write"]
  }
}

ESLint para projetos com Express e Node

Regras adicionais recomendadas para back-end:

npm install -D eslint-plugin-node
// eslint.config.js — adicione ao array
const nodePlugin = require("eslint-plugin-node");

module.exports = [
  // ... configurações anteriores
  {
    plugins: { node: nodePlugin },
    rules: {
      "node/no-missing-require": "error",        // require de módulo inexistente
      "node/no-extraneous-require": "error",     // require não listado no package.json
      "node/no-unpublished-require": "warn",     // require de devDependency em produção
      "node/handle-callback-err": "error",       // callback com erro não tratado
      "node/no-process-exit": "warn",            // process.exit() direto
    },
  },
];

Visualizando o impacto no projeto real

Antes de configurar ESLint e Prettier em um projeto real, você frequentemente encontra:

// Código típico sem padronização
var db = require('./config/db')
const Usuario = require('./models/usuario')

async function getUsuarios(req,res){
  var usuarios = await Usuario.find()
  if(usuarios == null){
    res.status(404).send({erro: "não encontrado"})
  }
  else{
  res.json(usuarios)
  }
}

module.exports={getUsuarios}

Depois do ESLint + Prettier:

// Código após linting e formatação
const db = require('./config/db');
const Usuario = require('./models/usuario');

async function getUsuarios(req, res) {
  const usuarios = await Usuario.find();
  if (usuarios === null) {
    return res.status(404).json({ erro: 'não encontrado' });
  }
  res.json(usuarios);
}

module.exports = { getUsuarios };
// ESLint ainda aponta: 'db' is assigned a value but never used

Tarefa para você

Configure ESLint e Prettier no projeto da API REST que construímos no Módulo 4:

# 1. Instale as dependências de desenvolvimento
# 2. Crie eslint.config.js com as regras recomendadas
# 3. Crie .prettierrc com suas preferências
# 4. Crie .editorconfig
# 5. Configure os scripts no package.json

# 6. Rode npm run lint no projeto e corrija TODOS os erros
#    — não use eslint-disable para esconder, resolva de verdade

# 7. Rode npm run format e veja o que mudou

# 8. Configure o Husky para bloquear commits com erros

# 9. Crie uma regra customizada simples:
#    Proíba o uso de "any" como nome de variável
#    (evita conflito com TypeScript no futuro)
#    Dica: "id-denylist": ["error", "any", "Number", "String"]

# 10. Teste o hook: tente commitar código com um console.log
#     e veja o commit ser bloqueado

Conclusão

Neste artigo você aprendeu:

  • A diferença fundamental entre ESLint (qualidade) e Prettier (formatação)
  • Como instalar e configurar o ESLint com regras essenciais
  • Os três níveis de severidade de regra — off, warn, error
  • Como instalar e configurar o Prettier com as opções mais comuns
  • Como integrar os dois sem conflitos usando eslint-config-prettier
  • Como automatizar com scripts no package.json
  • Como configurar o VS Code para formatar ao salvar
  • Como usar Husky e lint-staged para validar código antes do commit
  • O EditorConfig para padronização entre diferentes editores
  • Uma configuração completa e pronta para projetos Node.js

No próximo artigo vamos aprender sobre Git e fluxo de trabalho profissional — como usar controle de versão de forma estratégica em projetos em equipe.


 

📚 Fontes e Referências

  • ESLint — Documentação: https://eslint.org/docs/latest
  • ESLint — Rules reference: https://eslint.org/docs/latest/rules
  • Prettier — Documentação: https://prettier.io/docs/en
  • Prettier — Options: https://prettier.io/docs/en/options.html
  • eslint-config-prettier: https://github.com/prettier/eslint-config-prettier
  • Husky: https://typicode.github.io/husky
  • lint-staged: https://github.com/okonet/lint-staged
  • EditorConfig: https://editorconfig.org
  • Clean Code — Robert C. Martin (Prentice Hall)
  • roadmap.sh — Code Quality: https://roadmap.sh/best-practices/code-review
Comentários

Mais em Javascript

Trabalhando com JSON
Trabalhando com JSON

JSON está em absolutamente tudo. É o formato de dados mais usado na web — em...

Temporizadores: setTimeout e setInterval
Temporizadores: setTimeout e setInterval

Até agora todo o nosso código executou imediatamente — um...

MongoDB e Mongoose: banco de dados com Node
MongoDB e Mongoose: banco de dados com Node

Até agora nossa API guardava dados em um array na memória — tudo sumia quando...