@aleph/eslint-config
Version:
Aleph's ESLint configuration for JavaScript and TypeScript projects - ESLint 9 flat config
951 lines (935 loc) • 32 kB
JavaScript
/**
* Aleph ESLint Configuration - ESLint 9 Flat Config
*
* This configuration is maintained as part of our documentation system.
* For detailed explanation of rules and rationale, see:
* https://docs.aleph.inc/docs/resources/4devs/standards/code-style-standards
*
* Based on Airbnb style guide but with minimal external dependencies.
* Compatible with ESLint 9+ flat config format.
*/
const js = require('@eslint/js');
const stylistic = require('@stylistic/eslint-plugin');
const importPlugin = require('eslint-plugin-import');
const reactPlugin = require('eslint-plugin-react');
const reactHooksPlugin = require('eslint-plugin-react-hooks');
const jsxA11yPlugin = require('eslint-plugin-jsx-a11y');
const globals = require('globals');
module.exports = [
// Base JavaScript configuration
js.configs.recommended,
// Global settings
{
languageOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
globals: {
...globals.browser,
...globals.node,
...globals.es2022,
},
parserOptions: {
ecmaFeatures: {
jsx: true,
},
},
},
plugins: {
'@stylistic': stylistic,
'import': importPlugin,
'react': reactPlugin,
'react-hooks': reactHooksPlugin,
'jsx-a11y': jsxA11yPlugin,
},
settings: {
'react': {
version: 'detect',
},
'import/resolver': {
node: {
extensions: ['.js', '.jsx', '.ts', '.tsx'],
},
},
},
rules: {
// === Core JavaScript Rules (Airbnb-based) ===
// Possible Errors
'no-await-in-loop': 'error',
'no-console': 'warn',
'no-constant-condition': 'error',
'no-constructor-return': 'error',
'no-duplicate-case': 'error',
'no-duplicate-imports': 'error',
'no-empty-character-class': 'error',
'no-ex-assign': 'error',
'no-extra-boolean-cast': 'error',
'no-inner-declarations': 'error',
'no-invalid-regexp': 'error',
'no-irregular-whitespace': 'error',
'no-loss-of-precision': 'error',
'no-misleading-character-class': 'error',
'no-new-symbol': 'error',
'no-obj-calls': 'error',
'no-promise-executor-return': 'error',
'no-prototype-builtins': 'error',
'no-self-assign': 'error',
'no-self-compare': 'error',
'no-setter-return': 'error',
'no-sparse-arrays': 'error',
'no-template-curly-in-string': 'error',
'no-this-before-super': 'error',
'no-undef': 'error',
'no-unexpected-multiline': 'error',
'no-unmodified-loop-condition': 'error',
'no-unreachable': 'error',
'no-unreachable-loop': 'error',
'no-unsafe-finally': 'error',
'no-unsafe-negation': 'error',
'no-unsafe-optional-chaining': 'error',
'no-unused-private-class-members': 'error',
'no-unused-vars': ['error', {
argsIgnorePattern: '^_',
varsIgnorePattern: '^_',
ignoreRestSiblings: true,
}],
'no-use-before-define': ['error', {
functions: false,
classes: true,
variables: true,
}],
'no-useless-backreference': 'error',
'require-atomic-updates': 'off',
'use-isnan': 'error',
'valid-typeof': 'error',
// Best Practices
'accessor-pairs': 'off',
'array-callback-return': ['error', { allowImplicit: true }],
'block-scoped-var': 'error',
'class-methods-use-this': ['error', {
exceptMethods: [],
}],
'complexity': ['off', 20],
'consistent-return': 'error',
'curly': ['error', 'multi-line'],
'default-case': ['error', { commentPattern: '^no default$' }],
'default-case-last': 'error',
'default-param-last': 'error',
'dot-notation': ['error', { allowKeywords: true }],
'eqeqeq': ['error', 'always', { null: 'ignore' }],
'grouped-accessor-pairs': 'error',
'guard-for-in': 'error',
'max-classes-per-file': ['error', 1],
'no-alert': 'warn',
'no-caller': 'error',
'no-case-declarations': 'error',
'no-constructor-return': 'error',
'no-div-regex': 'off',
'no-else-return': ['error', { allowElseIf: false }],
'no-empty-function': ['error', {
allow: [
'arrowFunctions',
'functions',
'methods',
]
}],
'no-empty-pattern': 'error',
'no-eq-null': 'off',
'no-eval': 'error',
'no-extend-native': 'error',
'no-extra-bind': 'error',
'no-extra-label': 'error',
'no-fallthrough': 'error',
'no-floating-decimal': 'error',
'no-global-assign': ['error', { exceptions: [] }],
'no-implicit-coercion': ['off', {
boolean: false,
number: true,
string: true,
allow: [],
}],
'no-implicit-globals': 'off',
'no-implied-eval': 'error',
'no-invalid-this': 'off',
'no-iterator': 'error',
'no-labels': ['error', { allowLoop: false, allowSwitch: false }],
'no-lone-blocks': 'error',
'no-loop-func': 'error',
'no-magic-numbers': ['off', {
ignore: [],
ignoreArrayIndexes: true,
enforceConst: true,
detectObjects: false,
}],
'no-multi-spaces': ['error', {
ignoreEOLComments: false,
}],
'no-multi-str': 'error',
'no-new': 'error',
'no-new-func': 'error',
'no-new-wrappers': 'error',
'no-nonoctal-decimal-escape': 'error',
'no-octal': 'error',
'no-octal-escape': 'error',
'no-param-reassign': ['error', {
props: true,
ignorePropertyModificationsFor: [
'acc', // for reduce accumulators
'accumulator', // for reduce accumulators
'e', // for e.returnvalue
'ctx', // for Koa routing
'context', // for Koa routing
'req', // for Express requests
'request', // for Express requests
'res', // for Express responses
'response', // for Express responses
'$scope', // for Angular 1 scopes
'staticContext', // for ReactRouter context
]
}],
'no-proto': 'error',
'no-redeclare': 'error',
'no-restricted-properties': ['error', {
object: 'arguments',
property: 'callee',
message: 'arguments.callee is deprecated',
}, {
object: 'global',
property: 'isFinite',
message: 'Please use Number.isFinite instead',
}, {
object: 'self',
property: 'isFinite',
message: 'Please use Number.isFinite instead',
}, {
object: 'window',
property: 'isFinite',
message: 'Please use Number.isFinite instead',
}, {
object: 'global',
property: 'isNaN',
message: 'Please use Number.isNaN instead',
}, {
object: 'self',
property: 'isNaN',
message: 'Please use Number.isNaN instead',
}, {
object: 'window',
property: 'isNaN',
message: 'Please use Number.isNaN instead',
}, {
property: '__defineGetter__',
message: 'Please use Object.defineProperty instead.',
}, {
property: '__defineSetter__',
message: 'Please use Object.defineProperty instead.',
}, {
object: 'Math',
property: 'pow',
message: 'Use the exponentiation operator (**) instead.',
}],
'no-return-assign': ['error', 'always'],
'no-return-await': 'error',
'no-script-url': 'error',
'no-self-compare': 'error',
'no-sequences': 'error',
'no-throw-literal': 'error',
'no-unmodified-loop-condition': 'off',
'no-unused-expressions': ['error', {
allowShortCircuit: false,
allowTernary: false,
allowTaggedTemplates: false,
}],
'no-unused-labels': 'error',
'no-useless-call': 'off',
'no-useless-catch': 'error',
'no-useless-concat': 'error',
'no-useless-escape': 'error',
'no-useless-return': 'error',
'no-void': 'error',
'no-warning-comments': ['off', { terms: ['todo', 'fixme', 'xxx'], location: 'start' }],
'no-with': 'error',
'prefer-promise-reject-errors': ['error', { allowEmptyReject: true }],
'prefer-regex-literals': ['error', {
disallowRedundantWrapping: true,
}],
'radix': 'error',
'require-await': 'off',
'require-unicode-regexp': 'off',
'vars-on-top': 'error',
'wrap-iife': ['error', 'outside', { functionPrototypeMethods: false }],
'yoda': 'error',
// Variables
'init-declarations': 'off',
'no-delete-var': 'error',
'no-label-var': 'error',
'no-restricted-globals': [
'error',
{
name: 'isFinite',
message: 'Use Number.isFinite instead',
},
{
name: 'isNaN',
message: 'Use Number.isNaN instead',
},
'addEventListener',
'blur',
'close',
'closed',
'confirm',
'defaultStatus',
'defaultstatus',
'event',
'external',
'find',
'focus',
'frameElement',
'frames',
'history',
'innerHeight',
'innerWidth',
'length',
'location',
'locationbar',
'menubar',
'moveBy',
'moveTo',
'name',
'onblur',
'onerror',
'onfocus',
'onload',
'onresize',
'onunload',
'open',
'opener',
'opera',
'outerHeight',
'outerWidth',
'pageXOffset',
'pageYOffset',
'parent',
'print',
'removeEventListener',
'resizeBy',
'resizeTo',
'screen',
'screenLeft',
'screenTop',
'screenX',
'screenY',
'scroll',
'scrollbars',
'scrollBy',
'scrollTo',
'scrollX',
'scrollY',
'self',
'status',
'statusbar',
'stop',
'toolbar',
'top',
],
'no-shadow': 'error',
'no-shadow-restricted-names': 'error',
'no-undef-init': 'error',
'no-undefined': 'off',
'no-unused-vars': ['error', { vars: 'all', args: 'after-used', ignoreRestSiblings: true }],
// ES6
'arrow-body-style': ['error', 'as-needed', {
requireReturnForObjectLiteral: false,
}],
'arrow-parens': ['error', 'always'],
'arrow-spacing': ['error', { before: true, after: true }],
'constructor-super': 'error',
'generator-star-spacing': ['error', { before: false, after: true }],
'no-class-assign': 'error',
'no-confusing-arrow': ['error', {
allowParens: true,
}],
'no-const-assign': 'error',
'no-dupe-class-members': 'error',
'no-duplicate-imports': 'off', // replaced by import/no-duplicates
'no-new-symbol': 'error',
'no-restricted-exports': ['error', {
restrictedNamedExports: [
'default', // use `export default` to provide a default export
'then', // this will cause tons of confusion when your module is dynamically `import()`ed, and will break in most node ESM versions
]
}],
'no-restricted-imports': ['off', {
paths: [],
patterns: []
}],
'no-this-before-super': 'error',
'no-useless-computed-key': 'error',
'no-useless-constructor': 'error',
'no-useless-rename': ['error', {
ignoreDestructuring: false,
ignoreImport: false,
ignoreExport: false,
}],
'no-var': 'error',
'object-shorthand': ['error', 'always', {
ignoreConstructors: false,
avoidQuotes: true,
}],
'prefer-arrow-callback': ['error', {
allowNamedFunctions: false,
allowUnboundThis: true,
}],
'prefer-const': ['error', {
destructuring: 'any',
ignoreReadBeforeAssign: true,
}],
'prefer-destructuring': ['error', {
VariableDeclarator: {
array: false,
object: true,
},
AssignmentExpression: {
array: true,
object: false,
},
}, {
enforceForRenamedProperties: false,
}],
'prefer-numeric-literals': 'error',
'prefer-rest-params': 'error',
'prefer-spread': 'error',
'prefer-template': 'error',
'require-yield': 'error',
'rest-spread-spacing': ['error', 'never'],
'sort-imports': ['off', {
ignoreCase: false,
ignoreDeclarationSort: false,
ignoreMemberSort: false,
memberSyntaxSortOrder: ['none', 'all', 'multiple', 'single'],
}],
'symbol-description': 'error',
'template-curly-spacing': 'error',
'yield-star-spacing': ['error', 'after'],
// === Stylistic Rules ===
'@stylistic/array-bracket-newline': ['off', 'consistent'],
'@stylistic/array-bracket-spacing': ['error', 'never'],
'@stylistic/array-element-newline': ['off', { multiline: true, minItems: 3 }],
'@stylistic/block-spacing': ['error', 'always'],
'@stylistic/brace-style': ['error', '1tbs', { allowSingleLine: true }],
'@stylistic/comma-dangle': ['error', {
arrays: 'always-multiline',
objects: 'always-multiline',
imports: 'always-multiline',
exports: 'always-multiline',
functions: 'always-multiline',
}],
'@stylistic/comma-spacing': ['error', { before: false, after: true }],
'@stylistic/comma-style': ['error', 'last', {
exceptions: {
ArrayExpression: false,
ArrayPattern: false,
ArrowFunctionExpression: false,
CallExpression: false,
FunctionDeclaration: false,
FunctionExpression: false,
ImportDeclaration: false,
ObjectExpression: false,
ObjectPattern: false,
VariableDeclaration: false,
NewExpression: false,
}
}],
'@stylistic/computed-property-spacing': ['error', 'never'],
'@stylistic/eol-last': ['error', 'always'],
'@stylistic/func-call-spacing': ['error', 'never'],
'@stylistic/function-call-argument-newline': ['off', 'consistent'],
'@stylistic/function-paren-newline': ['error', 'consistent'],
'@stylistic/implicit-arrow-linebreak': ['error', 'beside'],
'@stylistic/indent': ['error', 2, {
SwitchCase: 1,
VariableDeclarator: 1,
outerIIFEBody: 1,
FunctionDeclaration: {
parameters: 1,
body: 1
},
FunctionExpression: {
parameters: 1,
body: 1
},
CallExpression: {
arguments: 1
},
ArrayExpression: 1,
ObjectExpression: 1,
ImportDeclaration: 1,
flatTernaryExpressions: false,
ignoredNodes: ['JSXElement', 'JSXElement > *', 'JSXAttribute', 'JSXIdentifier', 'JSXNamespacedName', 'JSXMemberExpression', 'JSXSpreadAttribute', 'JSXExpressionContainer', 'JSXOpeningElement', 'JSXClosingElement', 'JSXFragment', 'JSXOpeningFragment', 'JSXClosingFragment', 'JSXText', 'JSXEmptyExpression', 'JSXSpreadChild'],
ignoreComments: false
}],
'@stylistic/jsx-quotes': ['off', 'prefer-double'],
'@stylistic/key-spacing': ['error', { beforeColon: false, afterColon: true }],
'@stylistic/keyword-spacing': ['error', {
before: true,
after: true,
overrides: {
return: { after: true },
throw: { after: true },
case: { after: true }
}
}],
'@stylistic/line-comment-position': ['off', {
position: 'above',
ignorePattern: '',
applyDefaultPatterns: true,
}],
'@stylistic/linebreak-style': ['error', 'unix'],
'@stylistic/lines-between-class-members': ['error', 'always', { exceptAfterSingleLine: false }],
'@stylistic/lines-around-comment': ['off'],
'@stylistic/max-len': ['error', 100, 2, {
ignoreUrls: true,
ignoreComments: false,
ignoreRegExpLiterals: true,
ignoreStrings: true,
ignoreTemplateLiterals: true,
}],
'@stylistic/max-statements-per-line': ['off', { max: 1 }],
'@stylistic/multiline-comment-style': ['off', 'starred-block'],
'@stylistic/multiline-ternary': ['off', 'never'],
'@stylistic/new-parens': 'error',
'@stylistic/newline-per-chained-call': ['error', { ignoreChainWithDepth: 4 }],
'@stylistic/no-confusing-arrow': ['error', {
allowParens: true,
}],
'@stylistic/no-extra-parens': ['off', 'all', {
conditionalAssign: true,
nestedBinaryExpressions: false,
returnAssign: false,
ignoreJSX: 'all',
enforceForArrowConditionals: false,
}],
'@stylistic/no-extra-semi': 'error',
'@stylistic/no-floating-decimal': 'error',
'@stylistic/no-mixed-operators': ['error', {
groups: [
['%', '**'],
['%', '+'],
['%', '-'],
['%', '*'],
['%', '/'],
['/', '*'],
['&', '|', '<<', '>>', '>>>'],
['==', '!=', '===', '!=='],
['&&', '||'],
],
allowSamePrecedence: false
}],
'@stylistic/no-mixed-spaces-and-tabs': 'error',
'@stylistic/no-multi-spaces': ['error', {
ignoreEOLComments: false,
}],
'@stylistic/no-multiple-empty-lines': ['error', { max: 1, maxBOF: 0, maxEOF: 0 }],
'@stylistic/no-tabs': 'error',
'@stylistic/no-trailing-spaces': ['error', {
skipBlankLines: false,
ignoreComments: false,
}],
'@stylistic/no-whitespace-before-property': 'error',
'@stylistic/nonblock-statement-body-position': ['error', 'beside', { overrides: {} }],
'@stylistic/object-curly-newline': ['error', {
ObjectExpression: { minProperties: 4, multiline: true, consistent: true },
ObjectPattern: { minProperties: 4, multiline: true, consistent: true },
ImportDeclaration: { minProperties: 4, multiline: true, consistent: true },
ExportDeclaration: { minProperties: 4, multiline: true, consistent: true },
}],
'@stylistic/object-curly-spacing': ['error', 'always'],
'@stylistic/object-property-newline': ['error', {
allowAllPropertiesOnSameLine: true,
}],
'@stylistic/one-var-declaration-per-line': ['error', 'always'],
'@stylistic/operator-linebreak': ['error', 'before', { overrides: { '=': 'none' } }],
'@stylistic/padded-blocks': ['error', { blocks: 'never', classes: 'never', switches: 'never' }, { allowSingleLineBlocks: true }],
'@stylistic/padding-line-between-statements': ['off'],
'@stylistic/quote-props': ['error', 'as-needed', { keywords: false, unnecessary: true, numbers: false }],
'@stylistic/quotes': ['error', 'single', { avoidEscape: true }],
'@stylistic/rest-spread-spacing': ['error', 'never'],
'@stylistic/semi': ['error', 'always'],
'@stylistic/semi-spacing': ['error', { before: false, after: true }],
'@stylistic/semi-style': ['error', 'last'],
'@stylistic/space-before-blocks': 'error',
'@stylistic/space-before-function-paren': ['error', {
anonymous: 'always',
named: 'never',
asyncArrow: 'always'
}],
'@stylistic/space-in-parens': ['error', 'never'],
'@stylistic/space-infix-ops': 'error',
'@stylistic/space-unary-ops': ['error', {
words: true,
nonwords: false,
overrides: {
},
}],
'@stylistic/spaced-comment': ['error', 'always', {
line: {
exceptions: ['-', '+'],
markers: ['=', '!', '/'],
},
block: {
exceptions: ['-', '+'],
markers: ['=', '!', ':', '::'],
balanced: true,
}
}],
'@stylistic/switch-colon-spacing': ['error', { after: true, before: false }],
'@stylistic/template-curly-spacing': 'error',
'@stylistic/template-tag-spacing': ['error', 'never'],
'@stylistic/wrap-iife': ['error', 'outside', { functionPrototypeMethods: false }],
'@stylistic/wrap-regex': 'off',
'@stylistic/yield-star-spacing': ['error', 'after'],
// === Import Rules ===
'import/export': 'error',
'import/no-duplicates': 'error',
'import/no-named-as-default': 'error',
'import/no-named-as-default-member': 'error',
'import/no-unresolved': ['error', { commonjs: true, caseSensitive: true }],
'import/named': 'error',
'import/namespace': 'off',
'import/default': 'off',
'import/no-restricted-paths': 'off',
'import/no-absolute-path': 'error',
'import/no-dynamic-require': 'error',
'import/no-internal-modules': ['off', {
allow: [],
}],
'import/no-webpack-loader-syntax': 'error',
'import/no-self-import': 'error',
'import/no-cycle': ['error', { maxDepth: '∞' }],
'import/no-useless-path-segments': ['error', { commonjs: true }],
'import/no-relative-parent-imports': 'off',
'import/no-unused-modules': ['off', {
ignoreExports: [],
missingExports: true,
unusedExports: true,
}],
'import/first': 'error',
'import/exports-last': 'off',
'import/no-duplicates': 'error',
'import/no-namespace': 'off',
'import/extensions': ['error', 'ignorePackages', {
js: 'never',
mjs: 'never',
jsx: 'never',
ts: 'never',
tsx: 'never',
}],
'import/order': ['error', {
groups: [['builtin', 'external', 'internal']],
'newlines-between': 'always',
}],
'import/newline-after-import': 'error',
'import/prefer-default-export': 'error',
'import/max-dependencies': ['off', { max: 10 }],
'import/no-unassigned-import': 'off',
'import/no-named-default': 'error',
'import/no-default-export': 'off',
'import/no-named-export': 'off',
'import/no-anonymous-default-export': ['off', {
allowArray: false,
allowArrowFunction: false,
allowAnonymousClass: false,
allowAnonymousFunction: false,
allowCallExpression: true,
allowNew: false,
allowLiteral: false,
allowObject: false,
}],
'import/group-exports': 'off',
'import/dynamic-import-chunkname': ['off', {
importFunctions: [],
webpackChunknameFormat: '[0-9a-zA-Z-_/.]+',
}],
},
},
// React-specific configuration
{
files: ['**/*.{js,jsx,ts,tsx}'],
rules: {
// === React Rules ===
'react/boolean-prop-naming': ['off', {
propTypeNames: ['bool', 'mutuallyExclusiveTrueProps'],
rule: '^(is|has)[A-Z]([A-Za-z0-9]?)+',
message: '',
}],
'react/button-has-type': ['error', {
button: true,
submit: true,
reset: false,
}],
'react/default-props-match-prop-types': ['error', {
allowRequiredDefaults: false,
}],
'react/destructuring-assignment': ['error', 'always'],
'react/display-name': ['off', { ignoreTranspilerName: false }],
'react/forbid-component-props': ['off', { forbid: [] }],
'react/forbid-dom-props': ['off', { forbid: [] }],
'react/forbid-elements': ['off', { forbid: [] }],
'react/forbid-foreign-prop-types': ['warn', { allowInPropTypes: true }],
'react/forbid-prop-types': ['error', {
forbid: ['any', 'array', 'object'],
checkContextTypes: true,
checkChildContextTypes: true,
}],
'react/function-component-definition': ['error', {
namedComponents: 'function-declaration',
unnamedComponents: 'function-expression',
}],
'react/no-access-state-in-setstate': 'error',
'react/no-array-index-key': 'error',
'react/no-children-prop': 'error',
'react/no-danger': 'warn',
'react/no-danger-with-children': 'error',
'react/no-deprecated': ['error'],
'react/no-did-mount-set-state': 'off',
'react/no-did-update-set-state': 'error',
'react/no-direct-mutation-state': 'off',
'react/no-find-dom-node': 'error',
'react/no-is-mounted': 'error',
'react/no-multi-comp': 'off',
'react/no-redundant-should-component-update': 'error',
'react/no-render-return-value': 'error',
'react/no-set-state': 'off',
'react/no-typos': 'error',
'react/no-string-refs': 'error',
'react/no-this-in-sfc': 'error',
'react/no-unescaped-entities': 'error',
'react/no-unknown-property': 'error',
'react/no-unsafe': 'off',
'react/no-unused-prop-types': ['error', {
customValidators: [],
skipShapeProps: true,
}],
'react/no-unused-state': 'error',
'react/no-will-update-set-state': 'error',
'react/prefer-es6-class': ['error', 'always'],
'react/prefer-read-only-props': 'off',
'react/prefer-stateless-function': ['error', { ignorePureComponents: true }],
'react/prop-types': ['error', {
ignore: [],
customValidators: [],
skipUndeclared: false,
}],
'react/react-in-jsx-scope': 'error',
'react/require-default-props': ['error', {
forbidDefaultForRequired: true,
}],
'react/require-optimization': ['off', { allowDecorators: [] }],
'react/require-render-return': 'error',
'react/self-closing-comp': 'error',
'react/sort-comp': ['error', {
order: [
'static-variables',
'static-methods',
'instance-variables',
'lifecycle',
'/^handle.+$/',
'/^on.+$/',
'getters',
'setters',
'/^(get|set)(?!(InitialState$|DefaultProps$|ChildContext$)).+$/',
'instance-methods',
'everything-else',
'rendering',
],
groups: {
lifecycle: [
'displayName',
'propTypes',
'contextTypes',
'childContextTypes',
'mixins',
'statics',
'defaultProps',
'constructor',
'getDefaultProps',
'getInitialState',
'state',
'getChildContext',
'getDerivedStateFromProps',
'componentWillMount',
'UNSAFE_componentWillMount',
'componentDidMount',
'componentWillReceiveProps',
'UNSAFE_componentWillReceiveProps',
'shouldComponentUpdate',
'componentWillUpdate',
'UNSAFE_componentWillUpdate',
'getSnapshotBeforeUpdate',
'componentDidUpdate',
'componentDidCatch',
'componentWillUnmount',
],
rendering: [
'/^render.+$/',
'render',
],
},
}],
'react/sort-prop-types': ['off', {
ignoreCase: true,
callbacksLast: false,
requiredFirst: false,
sortShapeProp: true,
}],
'react/state-in-constructor': ['error', 'always'],
'react/static-property-placement': ['error', 'property assignment'],
'react/style-prop-object': 'error',
'react/void-dom-elements-no-children': 'error',
// === React Hooks Rules ===
'react-hooks/rules-of-hooks': 'error',
'react-hooks/exhaustive-deps': 'warn',
// === JSX A11y Rules ===
'jsx-a11y/accessible-emoji': 'off',
'jsx-a11y/alt-text': 'error',
'jsx-a11y/anchor-has-content': 'error',
'jsx-a11y/anchor-is-valid': ['error', {
components: ['Link'],
specialLink: ['hrefLeft', 'hrefRight'],
aspects: ['noHref', 'invalidHref', 'preferButton'],
}],
'jsx-a11y/aria-activedescendant-has-tabindex': 'error',
'jsx-a11y/aria-props': 'error',
'jsx-a11y/aria-proptypes': 'error',
'jsx-a11y/aria-role': ['error', { ignoreNonDOM: false }],
'jsx-a11y/aria-unsupported-elements': 'error',
'jsx-a11y/click-events-have-key-events': 'error',
'jsx-a11y/control-has-associated-label': ['off', {
labelAttributes: ['label'],
controlComponents: [],
ignoreElements: [
'audio',
'canvas',
'embed',
'input',
'textarea',
'tr',
'video',
],
ignoreRoles: [
'grid',
'listbox',
'menu',
'menubar',
'radiogroup',
'row',
'tablist',
'toolbar',
'tree',
'treegrid',
],
depth: 5,
}],
'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', {
labelComponents: [],
labelAttributes: [],
controlComponents: [],
assert: 'both',
depth: 25,
}],
'jsx-a11y/lang': 'error',
'jsx-a11y/media-has-caption': ['off', {
audio: [],
video: [],
track: [],
}],
'jsx-a11y/mouse-events-have-key-events': 'error',
'jsx-a11y/no-access-key': 'error',
'jsx-a11y/no-autofocus': ['error', { ignoreNonDOM: true }],
'jsx-a11y/no-distracting-elements': ['error', {
elements: ['marquee', 'blink'],
}],
'jsx-a11y/no-interactive-element-to-noninteractive-role': ['error', {
tr: ['none', 'presentation'],
}],
'jsx-a11y/no-noninteractive-element-interactions': ['error', {
handlers: [
'onClick',
'onMouseDown',
'onMouseUp',
'onKeyPress',
'onKeyDown',
'onKeyUp',
],
}],
'jsx-a11y/no-noninteractive-element-to-interactive-role': ['error', {
ul: ['listbox', 'menu', 'menubar', 'radiogroup', 'tablist', 'tree', 'treegrid'],
ol: ['listbox', 'menu', 'menubar', 'radiogroup', 'tablist', 'tree', 'treegrid'],
li: ['menuitem', 'option', 'row', 'tab', 'treeitem'],
table: ['grid'],
td: ['gridcell'],
}],
'jsx-a11y/no-noninteractive-tabindex': ['error', {
tags: [],
roles: ['tabpanel'],
}],
'jsx-a11y/no-onchange': 'off',
'jsx-a11y/no-redundant-roles': 'error',
'jsx-a11y/no-static-element-interactions': ['error', {
handlers: [
'onClick',
'onMouseDown',
'onMouseUp',
'onKeyPress',
'onKeyDown',
'onKeyUp',
],
allowExpressionValues: true,
}],
'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',
},
},
// TypeScript-specific overrides
{
files: ['**/*.{ts,tsx}'],
languageOptions: {
parser: require('@typescript-eslint/parser'),
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
ecmaFeatures: {
jsx: true,
},
},
},
plugins: {
'@typescript-eslint': require('@typescript-eslint/eslint-plugin'),
},
rules: {
// TypeScript-specific rule overrides
'no-unused-vars': 'off',
'@typescript-eslint/no-unused-vars': ['error', {
argsIgnorePattern: '^_',
varsIgnorePattern: '^_',
ignoreRestSiblings: true,
}],
'no-use-before-define': 'off',
'@typescript-eslint/no-use-before-define': ['error', {
functions: false,
classes: true,
variables: true,
}],
'no-shadow': 'off',
'@typescript-eslint/no-shadow': 'error',
'no-redeclare': 'off',
'@typescript-eslint/no-redeclare': 'error',
},
},
];