import { FlatCompat } from '@eslint/eslintrc'; import js from '@eslint/js'; import tsPlugin from '@typescript-eslint/eslint-plugin'; import tsParser from '@typescript-eslint/parser'; import reactPlugin from 'eslint-plugin-react'; import reactHooksPlugin from 'eslint-plugin-react-hooks'; import jsxA11yPlugin from 'eslint-plugin-jsx-a11y'; import eslintConfigPrettier from 'eslint-config-prettier'; import globals from 'globals'; 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, recommendedConfig: js.configs.recommended, allConfig: js.configs.all, }); export default [ { ignores: ['node_modules/**', 'dist/**', 'experiment/**'] }, js.configs.recommended, tsPlugin.configs['flat/eslint-recommended'], ...tsPlugin.configs['flat/recommended'], reactPlugin.configs.flat.recommended, reactHooksPlugin.configs.flat['recommended'], // Register jsx-a11y plugin (rules selectively enabled below) { plugins: { 'jsx-a11y': jsxA11yPlugin } }, // airbnb-base via FlatCompat (JS/import rules; no React plugin, no getFilename issue) ...compat.extends('airbnb-base'), eslintConfigPrettier, { languageOptions: { parser: tsParser, globals: { ...globals.browser, ...globals.es2021, JSX: 'readonly', }, parserOptions: { ecmaFeatures: { jsx: true }, ecmaVersion: 'latest', sourceType: 'module', }, }, settings: { react: { version: '18.2.0', }, }, rules: { 'linebreak-style': 0, 'no-unused-vars': 'off', // handled by @typescript-eslint/no-unused-vars 'no-underscore-dangle': 0, 'no-shadow': 'off', // Stylistic rules — off for this codebase 'no-console': 'off', 'no-continue': 'off', 'no-nested-ternary': 'off', 'no-plusplus': 'off', 'no-param-reassign': 'off', 'no-restricted-syntax': 'off', 'no-restricted-globals': 'off', 'no-constant-condition': 'off', 'prefer-destructuring': 'off', 'no-useless-assignment': 'off', 'preserve-caught-error': 'off', 'consistent-return': 'off', 'no-use-before-define': 'off', 'import/prefer-default-export': 'off', 'import/extensions': 'off', 'import/no-unresolved': 'off', 'import/no-extraneous-dependencies': [ 'error', { devDependencies: true, }, ], 'react/no-unstable-nested-components': ['error', { allowAsProps: true }], 'react/jsx-filename-extension': [ 'error', { extensions: ['.tsx', '.jsx'], }, ], 'react/display-name': 'off', 'react/require-default-props': 'off', 'react/jsx-props-no-spreading': 'off', 'react-hooks/rules-of-hooks': 'error', 'react-hooks/exhaustive-deps': 'error', // React Compiler rules added in react-hooks v7 — disabled until React Compiler is adopted 'react-hooks/react-compiler': 'off', 'react-hooks/incompatible-library': 'off', 'react-hooks/refs': 'off', 'react-hooks/set-state-in-effect': 'off', 'react-hooks/set-state-in-render': 'off', 'react-hooks/immutability': 'off', 'react-hooks/purity': 'off', 'react-hooks/use-memo': 'off', '@typescript-eslint/no-unused-vars': [ 'error', { argsIgnorePattern: '^_', varsIgnorePattern: '^_', caughtErrorsIgnorePattern: '^_' }, ], '@typescript-eslint/no-shadow': 'error', '@typescript-eslint/no-explicit-any': 'warn', // jsx-a11y — media captions not required for this app 'jsx-a11y/media-has-caption': 'off', 'jsx-a11y/no-noninteractive-element-interactions': 'off', 'jsx-a11y/alt-text': 'off', }, }, { files: ['**/*.ts', '**/*.tsx'], rules: { 'no-undef': 'off', }, }, ];