ESLint 9 y Flat Config: Guía Completa de Migración y Configuración
Hola HaWkers, ESLint 9 trajo un cambio significativo en la forma como configuramos el linter: el Flat Config. Si todavía estás usando .eslintrc.js o .eslintrc.json, es hora de entender el nuevo sistema y migrar tus proyectos.
En esta guía vamos a cubrir desde los conceptos básicos hasta configuraciones avanzadas para proyectos TypeScript y React.
Por Qué el Flat Config
El sistema antiguo de configuración de ESLint tenía algunas limitaciones:
Problemas del sistema antiguo:
- Cascada confusa de configuraciones
- Resolución compleja de plugins
- Dificultad en entender cuál regla viene de dónde
- Performance afectada por la búsqueda de archivos de configuración
Beneficios del Flat Config:
- Archivo único de configuración (
eslint.config.js) - Imports explícitos de plugins y configuraciones
- Mejor performance
- Más fácil de debuggear y entender
- Soporte nativo a ESM
Estructura Básica del Flat Config
El Flat Config usa un archivo eslint.config.js (o .mjs, .cjs) en la raíz del proyecto:
// eslint.config.js
import js from '@eslint/js';
export default [
js.configs.recommended,
{
rules: {
'no-unused-vars': 'warn',
'no-console': 'off',
},
},
];Anatomía de una Configuración
// eslint.config.js
export default [
// Configuración 1: Aplica a todos los archivos
{
rules: {
'semi': ['error', 'always'],
},
},
// Configuración 2: Aplica solo a archivos específicos
{
files: ['**/*.ts', '**/*.tsx'],
rules: {
'@typescript-eslint/no-explicit-any': 'warn',
},
},
// Configuración 3: Ignora archivos
{
ignores: ['dist/**', 'node_modules/**', '*.config.js'],
},
];
Migración del Sistema Antiguo
Antes: .eslintrc.js
// .eslintrc.js (antiguo)
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',
},
};Después: eslint.config.js
// eslint.config.js (nuevo)
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,
// Configuración 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 (debe ser el último)
prettier,
// Ignores
{
ignores: ['dist/**', 'node_modules/**', '.next/**'],
},
];
Configuraciones Comunes
Proyecto 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',
// Accesibilidad
'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/**'],
},
];Proyecto 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 de eso
'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,
}],
// General
'no-console': ['warn', { allow: ['warn', 'error'] }],
'prefer-const': 'error',
},
},
{
ignores: ['dist/**', 'node_modules/**', '*.js'],
},
];
Plugins y Sus Nuevas Configuraciones
@typescript-eslint
El typescript-eslint v8+ tiene soporte nativo al Flat Config:
import typescript from 'typescript-eslint';
export default [
// Usa el helper de typescript-eslint
...typescript.configs.recommended,
// O configuración más estricta
...typescript.configs.strict,
// Customizaciones
{
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,
},
},
},
];
Tips de Migración
1. Usa el Migration Tool
ESLint ofrece una herramienta de migración:
npx @eslint/migrate-config .eslintrc.js2. Verifica Compatibilidad de Plugins
No todos los plugins fueron actualizados para Flat Config. Verifica la documentación de cada uno:
// Algunos plugins todavía usan el formato antiguo
// Usa el compatibility layer si es necesario
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'),
// Tus reglas
{
rules: {
// ...
},
},
];3. Remueve Archivos Antiguos
Después de migrar, remueve los archivos de configuración antiguos:
rm .eslintrc.js .eslintrc.json .eslintrc.yaml .eslintignoreEl Flat Config usa la propiedad ignores en el propio archivo de configuración.
4. Actualiza los Scripts
{
"scripts": {
"lint": "eslint .",
"lint:fix": "eslint . --fix"
}
}
Debugging de la Configuración
Ver Configuración Final
npx eslint --print-config src/index.tsVerificar Cuáles Archivos Son Lintados
npx eslint . --debug 2>&1 | grep "Linting"Testar Reglas Específicas
// eslint.config.js - temporariamente
export default [
{
files: ['**/*.ts'],
rules: {
// Testa solo una regla
'no-console': 'error',
},
},
];Conclusión
El Flat Config de ESLint 9 representa una evolución significativa en la forma como configuramos linting en proyectos JavaScript y TypeScript. Aunque la migración exige algún esfuerzo inicial, los beneficios de claridad, performance y mantenibilidad compensan.
Recomendaciones finales:
- Migra proyectos nuevos directamente para Flat Config
- Para proyectos existentes, usa la herramienta de migración como punto de partida
- Verifica la compatibilidad de los plugins antes de migrar
- Aprovecha para revisar y simplificar tus reglas durante la migración
Si estás interesado en más herramientas para mejorar la calidad de tu código, recomiendo conferir el artículo Debugging JavaScript: Técnicas Avanzadas con DevTools que complementa bien las prácticas de linting con técnicas de debugging.

