From 1246c2dba5b1edaf75d3555d83ca057a80456fdf Mon Sep 17 00:00:00 2001 From: Ludy87 Date: Sun, 28 Sep 2025 17:56:25 +0200 Subject: [PATCH] Revamp ESLint config for React and TypeScript --- frontend/eslint.config.mjs | 184 +++++++++++++++++++++++++++++++++---- frontend/package.json | 3 + 2 files changed, 170 insertions(+), 17 deletions(-) diff --git a/frontend/eslint.config.mjs b/frontend/eslint.config.mjs index c0f9fd678..d135972d4 100644 --- a/frontend/eslint.config.mjs +++ b/frontend/eslint.config.mjs @@ -1,21 +1,93 @@ // @ts-check import eslint from '@eslint/js'; +import globals from 'globals'; import { defineConfig } from 'eslint/config'; +import reactHooksPlugin from 'eslint-plugin-react-hooks'; +import reactPlugin from 'eslint-plugin-react'; import tseslint from 'typescript-eslint'; +import { fileURLToPath } from 'node:url'; -export default defineConfig( +const ignorePatterns = ["__tests/**", "**/dist/**", "**/build/**", "**/node_modules/**", "**/public/**"]; +const jsGlobs = ['{src,frontend/src}/**/*.{js,jsx}']; +const srcGlobs = ['{src,frontend/src}/**/*.{ts,tsx}']; +const nodeGlobs = [ + 'scripts/**/*.{js,ts,mjs}', + 'vite.config.ts', + 'vitest.config.ts', + 'vitest.minimal.config.ts', + 'playwright.config.ts', + 'tailwind.config.js', + 'postcss.config.js', + 'eslint.config.mjs', +]; + +const __dirname = fileURLToPath(new URL('./', import.meta.url)); + +export default defineConfig({ ignores: ignorePatterns }, + // Shared settings for all files + { + ignores: ignorePatterns, + rules: { + semi: "error", + "prefer-const": "error", + }, + }, + + // Core rules for all source files eslint.configs.recommended, - tseslint.configs.recommended, + + // Specific rules for different types of files { ignores: [ - "dist", // Contains 3rd party code - "public", // Contains 3rd party code + ...ignorePatterns, + ...jsGlobs, + ...nodeGlobs ], - }, - { + files: srcGlobs, + extends: [ + reactPlugin.configs.flat.recommended, + reactPlugin.configs.flat['jsx-runtime'], + ...tseslint.configs.recommendedTypeChecked, + ], + languageOptions: { + parser: tseslint.parser, + parserOptions: { + projectService: true, + tsconfigRootDir: __dirname, + }, + globals: { + ...globals.browser, + ...globals.node, + }, + }, + plugins: { + '@typescript-eslint': tseslint.plugin, + react: reactPlugin, + 'react-hooks': reactHooksPlugin, + }, + settings: { + react: { + version: 'detect', + }, + }, rules: { - "no-undef": "off", // Temporarily disabled until codebase conformant + // Enabled rules + 'react-hooks/exhaustive-deps': 'warn', + 'react-hooks/rules-of-hooks': 'warn', + 'react/display-name': 'warn', + 'react/no-children-prop': 'warn', + 'react/prop-types': 'warn', + 'react/no-unescaped-entities': 'warn', + + '@typescript-eslint/array-type': ['warn', { default: 'array', readonly: 'array' }], + '@typescript-eslint/require-await': 'warn', + '@typescript-eslint/no-unnecessary-type-assertion': 'warn', + '@typescript-eslint/prefer-regexp-exec': 'warn', + '@typescript-eslint/prefer-includes': 'warn', + '@typescript-eslint/consistent-indexed-object-style': 'warn', + '@typescript-eslint/class-literal-property-style': 'warn', + '@typescript-eslint/consistent-type-definitions': 'warn', "@typescript-eslint/no-empty-object-type": [ "error", { @@ -23,20 +95,98 @@ export default defineConfig( allowInterfaces: 'with-single-extends', }, ], - "@typescript-eslint/no-explicit-any": "off", // Temporarily disabled until codebase conformant - "@typescript-eslint/no-require-imports": "off", // Temporarily disabled until codebase conformant + "@typescript-eslint/no-explicit-any": "warn", // Temporarily disabled until codebase conformant + "@typescript-eslint/no-require-imports": "warn", // Temporarily disabled until codebase conformant "@typescript-eslint/no-unused-vars": [ - "error", + "warn", { - "args": "all", // All function args must be used (or explicitly ignored) - "argsIgnorePattern": "^_", // Allow unused variables beginning with an underscore - "caughtErrors": "all", // Caught errors must be used (or explicitly ignored) - "caughtErrorsIgnorePattern": "^_", // Allow unused variables beginning with an underscore - "destructuredArrayIgnorePattern": "^_", // Allow unused variables beginning with an underscore - "varsIgnorePattern": "^_", // Allow unused variables beginning with an underscore - "ignoreRestSiblings": true, // Allow unused variables when removing attributes from objects (otherwise this requires explicit renaming like `({ x: _x, ...y }) => y`, which is clunky) + args: 'all', // All function args must be used (or explicitly ignored) + argsIgnorePattern: '^_', // Allow unused variables beginning with an underscore + caughtErrors: 'all', // Caught errors must be used (or explicitly ignored) + caughtErrorsIgnorePattern: '^_', // Allow unused variables beginning with an underscore + destructuredArrayIgnorePattern: '^_', // Allow unused variables beginning with an underscore + varsIgnorePattern: '^_', // Allow unused variables beginning with an underscore + ignoreRestSiblings: true, // Allow unused variables when removing attributes from objects (otherwise this requires explicit renaming like `({ x: _x, ...y }) => y`, which is clunky) }, ], + "no-redeclare": "warn", // Disallow variable redeclaration + + // Disabled rules (too restrictive or not useful for this codebase) + 'react/react-in-jsx-scope': 'off', // Not needed with React 17+ + "no-unused-vars": "off", // Use the TypeScript version instead + "no-undef": "off", // Use the TypeScript version instead + '@typescript-eslint/no-empty-function': 'off', // Ignore empty functions (they're often useful) + '@typescript-eslint/prefer-nullish-coalescing': 'off', // Ignore preference of ?? over || (both have their uses) + '@typescript-eslint/unbound-method': 'off', // Ignore unbound methods (they're often useful) + '@typescript-eslint/restrict-template-expressions': 'off', // Ignore restrictions on template expressions (they're often useful) + '@typescript-eslint/dot-notation': 'off', // Ignore dot notation (it's often more readable to use bracket notation) + '@typescript-eslint/non-nullable-type-assertion-style': 'off', // Ignore preference of using !. over other methods (both have their uses) + '@typescript-eslint/consistent-generic-constructors': 'off', // Ignore preference of using new Array() over Array (both have their uses) + '@typescript-eslint/no-redundant-type-constituents': 'off', // Ignore redundant type constituents (they're often useful for clarity) + + // Should be checked + "@typescript-eslint/no-inferrable-types": "off", + '@typescript-eslint/await-thenable': 'off', + '@typescript-eslint/no-unsafe-assignment': 'off', + '@typescript-eslint/no-unsafe-return': 'off', + '@typescript-eslint/no-unsafe-call': 'off', + '@typescript-eslint/no-unsafe-member-access': 'off', + '@typescript-eslint/no-unsafe-arguments': 'off', + '@typescript-eslint/no-unsafe-argument': 'off', + '@typescript-eslint/only-throw-error': 'off', + '@typescript-eslint/no-floating-promises': 'off', + '@typescript-eslint/prefer-promise-reject-errors': 'off', + '@typescript-eslint/prefer-optional-chain': 'off', + '@typescript-eslint/no-base-to-string': 'off', + '@typescript-eslint/no-misused-promises': 'off', }, + }, + { + ignores: [ + ...ignorePatterns, + ...jsGlobs + ], + files: nodeGlobs, + extends: [ + tseslint.configs.disableTypeChecked + ], + languageOptions: { + globals: { + ...globals.node, + }, + }, + }, + { + files: jsGlobs, + ignores: nodeGlobs, + extends: [ + reactPlugin.configs.flat.recommended, + ...tseslint.configs.recommended, + ], + languageOptions: { + parser: tseslint.parser, + parserOptions: { + ecmaFeatures: { + jsx: true, + }, + projectService: true, + tsconfigRootDir: __dirname, + }, + globals: { + ...globals.browser, + ...globals.node, + }, + }, + plugins: { + react: reactPlugin, + 'react-hooks': reactHooksPlugin, + }, + settings: { react: { version: 'detect' } }, + rules: { + 'no-unused-vars': 'warn', + 'no-console': 'error', + 'react/jsx-uses-react': 'error', + 'react/jsx-uses-vars': 'error', + } } ); diff --git a/frontend/package.json b/frontend/package.json index 0ec14e357..3ab8d4cfe 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -112,7 +112,10 @@ "@vitejs/plugin-react-swc": "^4.1.0", "@vitest/coverage-v8": "^3.2.4", "eslint": "^9.36.0", + "eslint-plugin-react": "^7.37.5", "eslint-plugin-react-hooks": "^5.2.0", + "eslint-plugin-simple-import-sort": "^12.1.1", + "globals": "^16.4.0", "jsdom": "^27.0.0", "license-checker": "^25.0.1", "madge": "^8.0.0",