UNPKG

@mikey-pro/eslint-config-react

Version:

Mikey Pro ESLint React configuration - Ultimate React coding style guide

286 lines (274 loc) 8.14 kB
// Modern React ESLint configuration for Mikey Pro import { baseConfig } from '../eslint-config/base-config.js'; import { baseOverrides } from '../eslint-config/overrides.js'; import jsxA11y from 'eslint-plugin-jsx-a11y'; import react from 'eslint-plugin-react'; import reactHooks from 'eslint-plugin-react-hooks'; import reactPerf from 'eslint-plugin-react-perf'; import reactRefresh from 'eslint-plugin-react-refresh'; // React-specific configuration const reactConfig = { files: ['**/*.{js,jsx,ts,tsx}'], languageOptions: { ecmaVersion: 'latest', parserOptions: { ecmaFeatures: { jsx: true, }, }, sourceType: 'module', }, plugins: { 'jsx-a11y': jsxA11y, react, 'react-hooks': reactHooks, 'react-perf': reactPerf, 'react-refresh': reactRefresh, }, rules: { // React rules ...react.configs.recommended.rules, ...react.configs['jsx-runtime'].rules, ...reactHooks.configs.recommended.rules, ...jsxA11y.configs.recommended.rules, // JSX A11y rules 'jsx-a11y/alt-text': 'error', 'jsx-a11y/anchor-has-content': 'error', 'jsx-a11y/anchor-is-valid': 'error', 'jsx-a11y/aria-activedescendant-has-tabindex': 'error', 'jsx-a11y/aria-props': 'error', 'jsx-a11y/aria-proptypes': 'error', 'jsx-a11y/aria-role': 'error', 'jsx-a11y/aria-unsupported-elements': 'error', 'jsx-a11y/autocomplete-valid': 'error', 'jsx-a11y/click-events-have-key-events': 'error', 'jsx-a11y/control-has-associated-label': 'error', 'jsx-a11y/heading-has-content': 'error', 'jsx-a11y/html-has-lang': 'error', 'jsx-a11y/iframe-has-title': 'error', 'jsx-a11y/img-redundant-alt': 'error', 'jsx-a11y/interactive-supports-focus': 'error', 'jsx-a11y/label-has-associated-control': 'error', 'jsx-a11y/lang': 'error', 'jsx-a11y/media-has-caption': 'error', 'jsx-a11y/mouse-events-have-key-events': 'error', 'jsx-a11y/no-access-key': 'error', 'jsx-a11y/no-aria-hidden-on-focusable': 'error', 'jsx-a11y/no-autofocus': 'error', 'jsx-a11y/no-distracting-elements': 'error', 'jsx-a11y/no-interactive-element-to-noninteractive-role': 'error', 'jsx-a11y/no-noninteractive-element-interactions': 'error', 'jsx-a11y/no-noninteractive-element-to-interactive-role': 'error', 'jsx-a11y/no-noninteractive-tabindex': 'error', 'jsx-a11y/no-redundant-roles': 'error', 'jsx-a11y/no-static-element-interactions': 'error', 'jsx-a11y/role-has-required-aria-props': 'error', 'jsx-a11y/role-supports-aria-props': 'error', 'jsx-a11y/scope': 'error', 'jsx-a11y/tabindex-no-positive': 'error', // React Hooks rules 'react-hooks/exhaustive-deps': 'warn', 'react-hooks/rules-of-hooks': 'error', // React Performance rules 'react-perf/jsx-no-new-array-as-prop': 'warn', 'react-perf/jsx-no-new-function-as-prop': 'warn', 'react-perf/jsx-no-new-object-as-prop': 'warn', // React Refresh rules 'react-refresh/only-export-components': [ 'warn', { allowConstantExport: true, allowExportNames: ['default'], }, ], // React-specific overrides 'react/boolean-prop-naming': [ 'error', { message: 'Boolean prop names should start with "is", "has", "should", "can", "will", or "did"', propTypeNames: ['bool'], rule: '^(is|has|should|can|will|did)[A-Z]([A-Za-z0-9]?)+', }, ], 'react/button-has-type': 'error', 'react/function-component-definition': [ 'error', { namedComponents: 'arrow-function', unnamedComponents: 'arrow-function', }, ], 'react/hook-use-state': 'error', 'react/iframe-missing-sandbox': 'error', 'react/jsx-boolean-value': ['error', 'never'], 'react/jsx-curly-brace-presence': [ 'error', { children: 'never', props: 'never', }, ], 'react/jsx-fragments': ['error', 'syntax'], 'react/jsx-handler-names': [ 'error', { checkInlineFunction: true, checkLocalVariables: true, eventHandlerPrefix: 'handle', eventHandlerPropPrefix: 'on', }, ], 'react/jsx-key': [ 'error', { checkFragmentShorthand: true, checkKeyMustBeforeSpread: true, warnOnDuplicates: true, }, ], 'react/jsx-no-bind': [ 'error', { allowArrowFunctions: true, allowBind: false, allowFunctions: false, ignoreDOMComponents: true, ignoreRefs: true, }, ], 'react/jsx-no-constructed-context-values': 'error', 'react/jsx-no-leaked-render': 'error', 'react/jsx-no-script-url': 'error', 'react/jsx-no-target-blank': 'error', 'react/jsx-no-undef': 'error', 'react/jsx-no-useless-fragment': [ 'error', { allowExpressions: true, }, ], 'react/jsx-pascal-case': [ 'error', { allowAllCaps: true, allowNamespace: true, ignore: [], }, ], 'react/jsx-sort-props': [ 'error', { callbacksLast: true, ignoreCase: true, noSortAlphabetically: false, reservedFirst: true, shorthandFirst: true, }, ], 'react/no-array-index-key': 'warn', 'react/no-danger': 'warn', 'react/no-deprecated': 'error', 'react/no-direct-mutation-state': 'error', 'react/no-find-dom-node': 'error', 'react/no-invalid-html-attribute': 'error', 'react/no-is-mounted': 'error', 'react/no-namespace': 'error', 'react/no-object-type-as-default-prop': 'error', 'react/no-render-return-value': 'error', 'react/no-string-refs': 'error', 'react/no-unescaped-entities': 'error', 'react/no-unknown-property': 'error', 'react/no-unsafe': 'error', 'react/no-unstable-nested-components': 'error', 'react/no-unused-class-component-methods': 'error', 'react/no-unused-prop-types': 'error', 'react/no-unused-state': 'error', 'react/prefer-es6-class': 'error', 'react/prefer-stateless-function': 'error', 'react/prop-types': 'off', // Handled by TypeScript 'react/react-in-jsx-scope': 'off', // Not needed with new JSX transform 'react/require-default-props': 'off', // Handled by TypeScript 'react/require-render-return': 'error', 'react/self-closing-comp': 'error', 'react/sort-comp': [ 'error', { order: [ 'static-methods', 'instance-variables', 'lifecycle', '/^on.+$/', 'everything-else', 'render', ], }, ], 'react/sort-default-props': 'error', 'react/sort-prop-types': 'error', 'react/state-in-constructor': 'error', 'react/static-property-placement': 'error', 'react/style-prop-object': 'error', 'react/void-dom-elements-no-children': 'error', }, settings: { 'import/resolver': { typescript: { alwaysTryTypes: true, }, }, react: { version: 'detect', }, }, }; // Export the complete React configuration export default [ // Global ignores { ignores: [ '**/dist/**/*', '**/vendor/**/*', '**/node_modules/**/*', '**/coverage/**/*', '**/.next/**/*', '**/.nuxt/**/*', '**/.output/**/*', '**/.vite/**/*', '**/build/**/*', '**/out/**/*', '*.properties', '*.cclibs', '*.svg', '*.png', '*.jpg', '*.jpeg', '*.gif', '*.ico', '*.webp', '*.aco', '*.psd', '*.ai', '*.ase', '*.sh', '*.bat', '*.cmd', 'package-lock.json', 'yarn.lock', 'pnpm-lock.yaml', 'LICENSE', 'CNAME', '*.min.js', '*.min.css', ], }, // Base configuration baseConfig, // React-specific configuration reactConfig, // File-specific overrides ...baseOverrides, ]; // Export individual components for advanced usage export { baseConfig } from '../eslint-config/base-config.js'; export { baseOverrides } from '../eslint-config/overrides.js';