npm.io
3.0.0 • Published 14h ago

@thasmorato/eslint-config

Licence
ISC
Version
3.0.0
Deps
12
Size
18 kB
Vulns
0
Weekly
0
Stars
1

ESLint config

Configs compartilhados de ESLint, em flat config, para Node, React e Next.js.

O que vem junto

  • Base neostandard (sucessor do eslint-config-standard);
  • TypeScript via typescript-eslint;
  • React, React Hooks e JSX a11y;
  • Prettier, acoplado — a formatação vem junto, sem .prettierrc no projeto;
  • Ordenação de imports (simple-import-sort) e higiene de import (import-x);
  • Guarda-corpos de complexidade e clean code — ver a seção abaixo.

Requisitos

  • ESLint ^9 — este pacote não roda em ESLint 10 ainda, ver ADR-0001;
  • Node ^18.18 || ^20.9 || >=21.1.

Setup

npm i -D eslint @thasmorato/eslint-config

O ESLint procura por eslint.config.js na raiz. Não use mais .eslintrc.json.

Node.js
// eslint.config.js
import node from '@thasmorato/eslint-config/node'

export default node
React (sem Next.js)
// eslint.config.js
import react from '@thasmorato/eslint-config/react'

export default react
React (com Next.js)

O preset next é um fragmento: ele precisa ser composto com next/core-web-vitals. Sozinho ele falha, e isso é de propósito.

// eslint.config.js
import coreWebVitals from 'eslint-config-next/core-web-vitals'
import next from '@thasmorato/eslint-config/next'

export default [...next, ...coreWebVitals]

O eslint-config-next (v15+) já exporta flat config — não precisa de FlatCompat.

Ele registra os plugins react, react-hooks e jsx-a11y, e o flat config lança Cannot redefine plugin se dois blocos registram o mesmo plugin. Por isso o preset next não registra nenhum dos três: ele só ajusta as regras deles e deixa o Next ser o dono. Ver ADR-0005.

Ajustando no projeto

Flat config é um array; para sobrescrever, acrescente um bloco no fim:

import node from '@thasmorato/eslint-config/node'

export default [
  ...node,
  {
    rules: {
      'max-lines-per-function': ['warn', { max: 80 }],
    },
  },
]

Guarda-corpos de clean code

A severidade segue uma linha só: defeito é error, cheiro é warn. Um cheiro aparece no editor sem quebrar um build que já estava verde.

Intenção Regra Sev Valor
Funções pequenas max-lines-per-function warn 50
Arquivo não vira God object max-lines warn 300
Uma coisa só max-statements warn 20
Poucos argumentos max-params warn 4
Complexidade ciclomática complexity warn 10
Complexidade cognitiva sonarjs/cognitive-complexity warn 15
Aninhamento max-depth warn 3
Callback hell max-nested-callbacks warn 3
Níveis de abstração sonarjs/no-nested-functions warn 3
I/O serializado em loop no-await-in-loop warn
Sem efeito colateral escondido no-param-reassign error props
Nunca engolir erro no-empty, sonarjs/no-ignored-exceptions error
DRY sonarjs/no-identical-functions warn

Em arquivos de teste (*.spec.*, *.test.*, tests/, __tests__/) os limites de tamanho e de duplicação ficam desligados: um describe é uma função longa por natureza e fixture repete de propósito.

Sobre "validação de O notation"

Não existe. Complexidade assintótica é estaticamente indecidível — nenhum linter a calcula, e o eslint-plugin-complexity que aparece nas buscas está sem manutenção desde 2022.

O que este config entrega são os proxies tratáveis: max-depth e max-nested-callbacks pegam o aninhamento que costuma indicar custo polinomial, no-await-in-loop pega I/O serializado, e sonarjs/cognitive-complexity pega o que é caro de segurar na cabeça. É honesto chamar isso de guarda-corpo de complexidade, não de análise de Big-O.

Migrando da v1

A v2 é breaking em todos os eixos:

v1 v2
.eslintrc.json com extends eslint.config.js com import
eslint@^8 eslint@^9
eslint-config-standard neostandard
regras import/* regras import-x/*
CommonJS ESM ("type": "module")

Se você tem // eslint-disable-next-line import/no-duplicates espalhado, o prefixo virou import-x/.

Decisões

Os porquês estão em docs/adr/:

  • ADR-0001 — por que ESLint 9 e não 10
  • ADR-0002 — por que neostandard
  • ADR-0003 — por que import-x sem as regras de resolução
  • ADR-0004 — por que o Prettier segue acoplado
  • ADR-0005 — por que o preset next não roda sozinho

Desenvolvimento

npm test           # node:test — linta fixtures via API do ESLint e afirma os ruleId
npm run lint       # o pacote se linta com o preset que publica (dogfood)
npm run test:consumer  # empacota, instala num projeto temporário e linta de lá

Release

Publicação automática no push pra main, a partir do tipo do commit (Conventional Commits):

Commit Bump Use quando
fix: refactor: perf: patch corrige o config sem fazer regra nova disparar
feat: minor regra nova entrando como warn
feat!: / BREAKING CHANGE: major regra nova como error, warnerror, preset muda de forma
chore: docs: ci: test: style: sem release

A convenção do meio é o que mantém o ^ seguro: regra nova nasce warn; promover pra error é major. Num config compartilhado, uma regra nova que dá error quebra o build de quem não mudou uma linha de código — por isso ela não pode entrar numa minor.

O bump vem de scripts/bump.js, que é função pura e tem teste (tests/bump.spec.js). O workflow roda testes, lint e o smoke test de consumidor antes de publicar, e só empurra a tag depois do publish dar certo — assim uma falha nunca deixa tag apontando pra versão que não foi ao ar.

Setup, uma vez
  1. Trusted publishing no npm — em npmjs.com, na página do pacote → Settings → Trusted Publishers, adicione: repositório ThaSMorato/eslint-config, workflow release.yml. Não é preciso criar nem guardar NPM_TOKEN.

  2. Tag inicial — o repo não tem tags. Sem uma, o primeiro run olha o histórico inteiro, encontra feat: antigos e publicaria 2.1.0. Marque o ponto de partida:

    git tag v2.0.0 && git push origin v2.0.0

    A partir daí a automação conta só o que vier depois.

Keywords