ESLint 9 e Flat Config: Guia Completo de Migração e Configuração
Olá HaWkers, o ESLint 9 trouxe uma mudança significativa na forma como configuramos o linter: o Flat Config. Se você ainda está usando .eslintrc.js ou .eslintrc.json, é hora de entender o novo sistema e migrar seus projetos.
Neste guia vamos cobrir desde os conceitos básicos até configurações avançadas para projetos TypeScript e React.
Por Que o Flat Config?
O sistema antigo de configuração do ESLint tinha algumas limitações:
Problemas do sistema antigo:
- Cascata confusa de configurações
- Resolução complexa de plugins
- Dificuldade em entender qual regra vem de onde
- Performance afetada pela busca de arquivos de configuração
Benefícios do Flat Config:
- Arquivo único de configuração (
eslint.config.js) - Imports explícitos de plugins e configurações
- Melhor performance
- Mais fácil de debugar e entender
- Suporte nativo a ESM
Estrutura Básica do Flat Config
O Flat Config usa um arquivo eslint.config.js (ou .mjs, .cjs) na raiz do projeto:
// eslint.config.js
import js from '@eslint/js';
export default [
js.configs.recommended,
{
rules: {
'no-unused-vars': 'warn',
'no-console': 'off',
},
},
];Anatomia de uma Configuração
// eslint.config.js
export default [
// Configuração 1: Aplica a todos os arquivos
{
rules: {
'semi': ['error', 'always'],
},
},
// Configuração 2: Aplica apenas a arquivos específicos
{
files: ['**/*.ts', '**/*.tsx'],
rules: {
'@typescript-eslint/no-explicit-any': 'warn',
},
},
// Configuração 3: Ignora arquivos
{
ignores: ['dist/**', 'node_modules/**', '*.config.js'],
},
];Migração do Sistema Antigo
Antes: .eslintrc.js
// .eslintrc.js (antigo)
module.exports = {
env: {
browser: true,
es2021: true,
node: true,
},
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:react/recommended',
'prettier',
],
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaFeatures: {
jsx: true,
},
ecmaVersion: 'latest',
sourceType: 'module',
},
plugins: ['@typescript-eslint', 'react', 'react-hooks'],
rules: {
'react/react-in-jsx-scope': 'off',
'@typescript-eslint/no-unused-vars': 'warn',
},
};Depois: eslint.config.js
// eslint.config.js (novo)
import js from '@eslint/js';
import typescript from '@typescript-eslint/eslint-plugin';
import typescriptParser from '@typescript-eslint/parser';
import react from 'eslint-plugin-react';
import reactHooks from 'eslint-plugin-react-hooks';
import prettier from 'eslint-config-prettier';
import globals from 'globals';
export default [
js.configs.recommended,
// Configuração global
{
languageOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
globals: {
...globals.browser,
...globals.node,
...globals.es2021,
},
},
},
// TypeScript
{
files: ['**/*.ts', '**/*.tsx'],
languageOptions: {
parser: typescriptParser,
parserOptions: {
ecmaFeatures: { jsx: true },
},
},
plugins: {
'@typescript-eslint': typescript,
},
rules: {
...typescript.configs.recommended.rules,
'@typescript-eslint/no-unused-vars': 'warn',
},
},
// React
{
files: ['**/*.jsx', '**/*.tsx'],
plugins: {
react,
'react-hooks': reactHooks,
},
rules: {
...react.configs.recommended.rules,
...reactHooks.configs.recommended.rules,
'react/react-in-jsx-scope': 'off',
},
settings: {
react: {
version: 'detect',
},
},
},
// Prettier (deve ser o último)
prettier,
// Ignores
{
ignores: ['dist/**', 'node_modules/**', '.next/**'],
},
];Configurações Comuns
Projeto React + TypeScript
// eslint.config.js
import js from '@eslint/js';
import typescript from 'typescript-eslint';
import react from 'eslint-plugin-react';
import reactHooks from 'eslint-plugin-react-hooks';
import jsxA11y from 'eslint-plugin-jsx-a11y';
import globals from 'globals';
export default [
js.configs.recommended,
...typescript.configs.recommended,
{
files: ['**/*.{js,jsx,ts,tsx}'],
languageOptions: {
globals: {
...globals.browser,
...globals.es2021,
},
},
plugins: {
react,
'react-hooks': reactHooks,
'jsx-a11y': jsxA11y,
},
rules: {
// React
'react/prop-types': 'off',
'react/react-in-jsx-scope': 'off',
'react/jsx-uses-react': 'off',
// Hooks
'react-hooks/rules-of-hooks': 'error',
'react-hooks/exhaustive-deps': 'warn',
// Acessibilidade
'jsx-a11y/alt-text': 'error',
'jsx-a11y/anchor-is-valid': 'warn',
// TypeScript
'@typescript-eslint/no-unused-vars': ['warn', {
argsIgnorePattern: '^_',
varsIgnorePattern: '^_',
}],
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/no-explicit-any': 'warn',
},
settings: {
react: { version: 'detect' },
},
},
{
ignores: ['dist/**', 'build/**', 'node_modules/**'],
},
];Projeto Node.js + TypeScript
// eslint.config.js
import js from '@eslint/js';
import typescript from 'typescript-eslint';
import node from 'eslint-plugin-n';
import globals from 'globals';
export default [
js.configs.recommended,
...typescript.configs.recommended,
{
files: ['**/*.{js,ts}'],
languageOptions: {
globals: {
...globals.node,
...globals.es2021,
},
},
plugins: {
n: node,
},
rules: {
// Node.js
'n/no-missing-import': 'off', // TypeScript cuida disso
'n/no-unsupported-features/es-syntax': 'off',
'n/no-process-exit': 'warn',
// TypeScript
'@typescript-eslint/no-unused-vars': ['error', {
argsIgnorePattern: '^_',
}],
'@typescript-eslint/explicit-function-return-type': ['warn', {
allowExpressions: true,
}],
// Geral
'no-console': ['warn', { allow: ['warn', 'error'] }],
'prefer-const': 'error',
},
},
{
ignores: ['dist/**', 'node_modules/**', '*.js'],
},
];Plugins e Suas Novas Configurações
@typescript-eslint
O typescript-eslint v8+ tem suporte nativo ao Flat Config:
import typescript from 'typescript-eslint';
export default [
// Usa o helper do typescript-eslint
...typescript.configs.recommended,
// Ou configuração mais estrita
...typescript.configs.strict,
// Customizações
{
files: ['**/*.ts', '**/*.tsx'],
rules: {
'@typescript-eslint/consistent-type-imports': 'error',
'@typescript-eslint/no-floating-promises': 'error',
},
},
];eslint-plugin-import
import importPlugin from 'eslint-plugin-import';
export default [
{
plugins: {
import: importPlugin,
},
rules: {
'import/order': ['error', {
groups: [
'builtin',
'external',
'internal',
'parent',
'sibling',
'index',
],
'newlines-between': 'always',
alphabetize: { order: 'asc' },
}],
'import/no-duplicates': 'error',
'import/no-unresolved': 'error',
},
settings: {
'import/resolver': {
typescript: true,
node: true,
},
},
},
];Dicas de Migração
1. Use o Migration Tool
O ESLint oferece uma ferramenta de migração:
npx @eslint/migrate-config .eslintrc.js2. Verifique Compatibilidade de Plugins
Nem todos os plugins foram atualizados para Flat Config. Verifique a documentação de cada um:
// Alguns plugins ainda usam o formato antigo
// Use o compatibility layer se necessário
import { FlatCompat } from '@eslint/eslintrc';
import path from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const compat = new FlatCompat({
baseDirectory: __dirname,
});
export default [
// Plugins modernos
js.configs.recommended,
// Plugins legados via compat
...compat.extends('plugin:legacy-plugin/recommended'),
// Suas regras
{
rules: {
// ...
},
},
];3. Remova Arquivos Antigos
Após migrar, remova os arquivos de configuração antigos:
rm .eslintrc.js .eslintrc.json .eslintrc.yaml .eslintignoreO Flat Config usa a propriedade ignores no próprio arquivo de configuração.
4. Atualize os Scripts
{
"scripts": {
"lint": "eslint .",
"lint:fix": "eslint . --fix"
}
}Debugging da Configuração
Ver Configuração Final
npx eslint --print-config src/index.tsVerificar Quais Arquivos São Lintados
npx eslint . --debug 2>&1 | grep "Linting"Testar Regras Específicas
// eslint.config.js - temporariamente
export default [
{
files: ['**/*.ts'],
rules: {
// Teste apenas uma regra
'no-console': 'error',
},
},
];Conclusão
O Flat Config do ESLint 9 representa uma evolução significativa na forma como configuramos linting em projetos JavaScript e TypeScript. Embora a migração exija algum esforço inicial, os benefícios de clareza, performance e manutenibilidade compensam.
Recomendações finais:
- Migre projetos novos diretamente para Flat Config
- Para projetos existentes, use a ferramenta de migração como ponto de partida
- Verifique a compatibilidade dos plugins antes de migrar
- Aproveite para revisar e simplificar suas regras durante a migração
Se você está interessado em mais ferramentas para melhorar a qualidade do seu código, recomendo conferir o artigo Debugging JavaScript: Técnicas Avançadas com DevTools que complementa bem as práticas de linting com técnicas de debugging.

