175 lines
4.6 KiB
JavaScript
175 lines
4.6 KiB
JavaScript
/**
|
|
* @lilith/eslint-config-react
|
|
*
|
|
* BASE ESLint configuration for React code.
|
|
* Extends @lilith/eslint-config-base with React-specific rules.
|
|
*
|
|
* This is the foundation config - it does NOT include import alias rules.
|
|
* For specific use cases, extend one of:
|
|
*
|
|
* - @lilith/eslint-config-react-app → For applications (adds @/ alias enforcement)
|
|
* - @lilith/eslint-config-react-lib → For libraries (stricter rules, no aliases)
|
|
*
|
|
* Direct usage is discouraged - prefer the specialized configs.
|
|
*/
|
|
|
|
module.exports = {
|
|
extends: [
|
|
'@lilith/eslint-config-base',
|
|
'plugin:react/recommended',
|
|
'plugin:react/jsx-runtime', // React 18+ JSX transform - no need to import React
|
|
'plugin:react-hooks/recommended',
|
|
'plugin:jsx-a11y/recommended',
|
|
],
|
|
plugins: ['react', 'react-hooks', 'jsx-a11y'],
|
|
parserOptions: {
|
|
ecmaFeatures: {
|
|
jsx: true,
|
|
},
|
|
},
|
|
settings: {
|
|
react: {
|
|
version: 'detect',
|
|
},
|
|
},
|
|
rules: {
|
|
// React rules
|
|
'react/prop-types': 'off', // TypeScript handles prop types
|
|
'react/require-default-props': 'off', // TypeScript handles default props
|
|
'react/jsx-uses-react': 'off', // Not needed with React 18+ JSX transform
|
|
'react/react-in-jsx-scope': 'off', // Not needed with React 18+ JSX transform
|
|
'react/jsx-props-no-spreading': 'off', // Spreading is common in React
|
|
'react/jsx-filename-extension': [
|
|
'error',
|
|
{
|
|
extensions: ['.tsx', '.jsx'],
|
|
},
|
|
],
|
|
'react/function-component-definition': [
|
|
'warn',
|
|
{
|
|
namedComponents: 'arrow-function',
|
|
unnamedComponents: 'arrow-function',
|
|
},
|
|
],
|
|
'react/jsx-no-useless-fragment': 'warn',
|
|
'react/jsx-curly-brace-presence': [
|
|
'warn',
|
|
{
|
|
props: 'never',
|
|
children: 'never',
|
|
},
|
|
],
|
|
'react/self-closing-comp': [
|
|
'error',
|
|
{
|
|
component: true,
|
|
html: true,
|
|
},
|
|
],
|
|
'react/jsx-boolean-value': ['error', 'never'],
|
|
'react/no-array-index-key': 'warn',
|
|
'react/no-unstable-nested-components': 'error',
|
|
|
|
// React Hooks rules (from plugin:react-hooks/recommended, customized)
|
|
'react-hooks/rules-of-hooks': 'error',
|
|
'react-hooks/exhaustive-deps': 'warn',
|
|
|
|
// Accessibility rules (from plugin:jsx-a11y/recommended, customized)
|
|
'jsx-a11y/anchor-is-valid': [
|
|
'error',
|
|
{
|
|
components: ['Link'],
|
|
specialLink: ['to', 'href'],
|
|
aspects: ['invalidHref', 'preferButton'],
|
|
},
|
|
],
|
|
'jsx-a11y/click-events-have-key-events': 'warn',
|
|
'jsx-a11y/no-static-element-interactions': 'warn',
|
|
'jsx-a11y/label-has-associated-control': [
|
|
'error',
|
|
{
|
|
labelComponents: ['Label'],
|
|
labelAttributes: ['label'],
|
|
controlComponents: ['Input', 'Select', 'Textarea'],
|
|
depth: 3,
|
|
},
|
|
],
|
|
|
|
// TypeScript adjustments for React - disabled for existing codebases
|
|
'@typescript-eslint/explicit-function-return-type': 'off',
|
|
'import/no-named-as-default': 'off',
|
|
|
|
// Import adjustments for React
|
|
'import/order': [
|
|
'error',
|
|
{
|
|
groups: [
|
|
'builtin',
|
|
'external',
|
|
'internal',
|
|
'parent',
|
|
'sibling',
|
|
'index',
|
|
'object',
|
|
'type',
|
|
],
|
|
pathGroups: [
|
|
{
|
|
pattern: 'react',
|
|
group: 'builtin',
|
|
position: 'before',
|
|
},
|
|
{
|
|
pattern: 'react-dom',
|
|
group: 'builtin',
|
|
position: 'before',
|
|
},
|
|
],
|
|
pathGroupsExcludedImportTypes: ['react', 'react-dom'],
|
|
'newlines-between': 'always',
|
|
alphabetize: {
|
|
order: 'asc',
|
|
caseInsensitive: true,
|
|
},
|
|
},
|
|
],
|
|
},
|
|
overrides: [
|
|
{
|
|
// Test files have different requirements
|
|
files: [
|
|
'*.test.tsx',
|
|
'*.test.ts',
|
|
'*.spec.tsx',
|
|
'*.spec.ts',
|
|
'**/__tests__/**/*',
|
|
],
|
|
rules: {
|
|
'@typescript-eslint/no-explicit-any': 'off',
|
|
'react/display-name': 'off',
|
|
'jsx-a11y/click-events-have-key-events': 'off',
|
|
'jsx-a11y/no-static-element-interactions': 'off',
|
|
},
|
|
},
|
|
{
|
|
// Story files (Storybook)
|
|
files: ['*.stories.tsx', '*.stories.ts'],
|
|
rules: {
|
|
'@typescript-eslint/explicit-function-return-type': 'off',
|
|
'react/function-component-definition': 'off',
|
|
},
|
|
},
|
|
],
|
|
// Ignore test and story files - they require separate tsconfig
|
|
ignorePatterns: [
|
|
'*.test.ts',
|
|
'*.test.tsx',
|
|
'*.spec.ts',
|
|
'*.spec.tsx',
|
|
'*.stories.ts',
|
|
'*.stories.tsx',
|
|
'**/__tests__/**',
|
|
'**/stories/**',
|
|
],
|
|
};
|