ESLint 9 et Flat Config : Guide Complet de Migration et Configuration
Salut HaWkers, ESLint 9 a apporté un changement significatif dans la façon dont nous configurons le linter : le Flat Config. Si vous utilisez encore .eslintrc.js ou .eslintrc.json, il est temps de comprendre le nouveau système et de migrer vos projets.
Dans ce guide, nous allons couvrir depuis les concepts de base jusqu'aux configurations avancées pour les projets TypeScript et React.
Pourquoi le Flat Config ?
L'ancien système de configuration d'ESLint avait quelques limitations :
Problèmes de l'ancien système :
- Cascade confuse de configurations
- Résolution complexe des plugins
- Difficulté à comprendre quelle règle vient d'où
- Performance affectée par la recherche de fichiers de configuration
Avantages du Flat Config :
- Fichier unique de configuration (
eslint.config.js) - Imports explicites des plugins et configurations
- Meilleure performance
- Plus facile à debugger et comprendre
- Support natif d'ESM
Structure Basique du Flat Config
Le Flat Config utilise un fichier eslint.config.js (ou .mjs, .cjs) à la racine du projet :
// eslint.config.js
import js from '@eslint/js';
export default [
js.configs.recommended,
{
rules: {
'no-unused-vars': 'warn',
'no-console': 'off',
},
},
];Anatomie d'une Configuration
// eslint.config.js
export default [
// Configuration 1 : S'applique à tous les fichiers
{
rules: {
'semi': ['error', 'always'],
},
},
// Configuration 2 : S'applique uniquement à des fichiers spécifiques
{
files: ['**/*.ts', '**/*.tsx'],
rules: {
'@typescript-eslint/no-explicit-any': 'warn',
},
},
// Configuration 3 : Ignore des fichiers
{
ignores: ['dist/**', 'node_modules/**', '*.config.js'],
},
];
Migration de l'Ancien Système
Avant : .eslintrc.js
// .eslintrc.js (ancien)
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',
},
};Après : eslint.config.js
// eslint.config.js (nouveau)
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,
// Configuration globale
{
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 (doit être le dernier)
prettier,
// Ignores
{
ignores: ['dist/**', 'node_modules/**', '.next/**'],
},
];
Configurations Courantes
Projet 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',
// Accessibilité
'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/**'],
},
];Projet 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 s'en occupe
'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,
}],
// Général
'no-console': ['warn', { allow: ['warn', 'error'] }],
'prefer-const': 'error',
},
},
{
ignores: ['dist/**', 'node_modules/**', '*.js'],
},
];
Plugins et Leurs Nouvelles Configurations
@typescript-eslint
typescript-eslint v8+ a un support natif du Flat Config :
import typescript from 'typescript-eslint';
export default [
// Utilise le helper de typescript-eslint
...typescript.configs.recommended,
// Ou configuration plus stricte
...typescript.configs.strict,
// Personnalisations
{
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,
},
},
},
];
Conseils de Migration
1. Utilisez l'Outil de Migration
ESLint offre un outil de migration :
npx @eslint/migrate-config .eslintrc.js2. Vérifiez la Compatibilité des Plugins
Tous les plugins n'ont pas été mis à jour pour le Flat Config. Vérifiez la documentation de chacun :
// Certains plugins utilisent encore l'ancien format
// Utilisez la couche de compatibilité si nécessaire
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 modernes
js.configs.recommended,
// Plugins legacy via compat
...compat.extends('plugin:legacy-plugin/recommended'),
// Vos règles
{
rules: {
// ...
},
},
];3. Supprimez les Anciens Fichiers
Après la migration, supprimez les anciens fichiers de configuration :
rm .eslintrc.js .eslintrc.json .eslintrc.yaml .eslintignoreLe Flat Config utilise la propriété ignores dans le fichier de configuration lui-même.
4. Mettez à Jour les Scripts
{
"scripts": {
"lint": "eslint .",
"lint:fix": "eslint . --fix"
}
}
Debugging de la Configuration
Voir la Configuration Finale
npx eslint --print-config src/index.tsVérifier Quels Fichiers Sont Lintés
npx eslint . --debug 2>&1 | grep "Linting"Tester des Règles Spécifiques
// eslint.config.js - temporairement
export default [
{
files: ['**/*.ts'],
rules: {
// Testez seulement une règle
'no-console': 'error',
},
},
];Conclusion
Le Flat Config d'ESLint 9 représente une évolution significative dans la façon dont nous configurons le linting dans les projets JavaScript et TypeScript. Bien que la migration nécessite un effort initial, les bénéfices de clarté, performance et maintenabilité en valent la peine.
Recommandations finales :
- Migrez les nouveaux projets directement vers Flat Config
- Pour les projets existants, utilisez l'outil de migration comme point de départ
- Vérifiez la compatibilité des plugins avant de migrer
- Profitez-en pour revoir et simplifier vos règles pendant la migration
Si vous êtes intéressé par plus d'outils pour améliorer la qualité de votre code, je recommande de consulter l'article Debugging JavaScript : Techniques Avancées avec DevTools qui complète bien les pratiques de linting avec des techniques de debugging.

