@kununu/eslint-config
Version:
kununu's ESLint config
346 lines (308 loc) • 11.7 kB
JavaScript
// we need to have the baseRules to have the same on the typescript override
// because it has extends the eslint does not take into consideration the ones from the base
const baseRules = {
'import/no-extraneous-dependencies': ['error', {
devDependencies: [
'**/*.spec.js',
'**/*.spec.jsx',
'**/*.test.js',
'**/*.test.jsx',
'**/stories.jsx',
'*/test-*/*.js',
'*/test-*/*.jsx',
'config/**/*.js',
'jest.setup.js',
'jestsetup.js',
'mockBff/*',
'next.config.js',
],
}],
'max-len': 'off', // Sometimes longer lines are more readable (Airbnb rule change)
'no-param-reassign': ['error', {props: false}],
'no-prototype-builtins': 'off', // Objects aren't created that don't extend from Object.prototype (Airbnb rule change)
'object-curly-spacing': 'off', // Disabled in favor of @babel/object-curly-spacing in order to avoid false positives with ECMAScript modules (Airbnb rule change)
'no-use-before-define': 'off',
'sort-destructure-keys/sort-destructure-keys': [2, {'caseSensitive': false}],
// https://github.com/yannickcr/eslint-plugin-react/tree/master/docs/rules
'react/no-direct-mutation-state': 'error', // Use .setState() always (Airbnb rule change)
// https://github.com/babel/babel/tree/main/eslint/babel-eslint-plugin#rules
'@babel/object-curly-spacing': 'error', // No spaces in single-line objects to make nested objects like {a: {b: 'c'}} look more sane (Airbnb rule change)
// https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/
'import/order': ['error', { // Make import sort order an error (Airbnb rule change)
'newlines-between': 'always',
groups: [
'builtin', // import fs from 'fs';
'external', // import chalk from 'chalk';
'internal', // import foo from 'src/foo';
'parent', // import qux from '../qux';
'sibling', // import bar from './bar';
'index', // import main from './';
],
}],
'import/no-useless-path-segments': ['error', {
'noUselessIndex': true,
}],
// https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/anchor-is-valid.md#rule-details
// allow `Link` to have `to` and not the mandatory `href`
'jsx-a11y/anchor-is-valid': ['error', {
components: ['Link'],
specialLink: ['to'],
}],
// https://eslint.org/docs/rules/no-confusing-arrow
// turn off to prevent conflict with
// https://eslint.org/docs/rules/arrow-body-style
'no-confusing-arrow': 'off',
// https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/label-has-for.md
// 'label' tags need 'htmlFor' prop, but nesting is not required
'jsx-a11y/label-has-for': ['error', {
'required': 'id',
}],
// https://eslint.org/docs/rules/padding-line-between-statements
// enforce empty lines after variable declarations
'padding-line-between-statements': ['error', {
'blankLine': 'always', 'prev': ['const', 'let', 'var'], 'next': '*',
}, {
'blankLine': 'any', 'prev': ['const', 'let', 'var'], 'next': ['const', 'let', 'var'],
}],
// https://www.npmjs.com/package/eslint-plugin-react-hooks
// enforces the rules of react-hooks (call at top level and only from functional components; checks dependencies)
'react-hooks/rules-of-hooks': 'error',
'react-hooks/exhaustive-deps': 'warn',
// https://eslint.org/docs/rules/no-underscore-dangle
// no underscores at either the beginning or end of an identifier
'no-underscore-dangle': ['error', {'allow': ['__NEXT_DATA__', '__NEXT_REDUX_STORE__']}],
'no-multiple-empty-lines': ['error', {'max': 1, 'maxEOF': 1}],
// https://eslint.org/docs/rules/eol-last
// require newline at the end of files
'eol-last': ['error', 'always'],
// https://github.com/jsx-eslint/eslint-plugin-react/blob/master/docs/rules/sort-default-props.md
// enforce defaultProps declarations alphabetical sorting
'react/sort-default-props': ['error', {
'ignoreCase': true
}],
// https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-sort-props.md
// enforce props alphabetical sorting
'react/jsx-sort-props': ['error', {
'ignoreCase': true
}],
// https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/sort-prop-types.md
// enforce propTypes declarations alphabetical sorting
'react/sort-prop-types': ['error', {
'ignoreCase': true
}],
// https://eslint.org/docs/rules/sort-keys
// require object keys to be sorted
'sort-keys': ['error', 'asc', {'caseSensitive': false, 'natural': false}],
// https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/static-property-placement.md
// enforces where React component static properties should be positioned
'react/static-property-placement': ['error', 'property assignment'],
// https://eslint.org/docs/rules/indent
// enforces a consistent 2 spaces indentation style
'indent': ['error', 2, {
'SwitchCase': 1
}],
// https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/state-in-constructor.md
// enforces the state initialization style to be either in a constructor or with a class property
'react/state-in-constructor': 'off',
// https://eslint.org/docs/rules/arrow-parens
// enforces no braces where they can be omitted
'arrow-parens': ['error', 'as-needed'],
// https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/extensions.md
'import/extensions': ['error', 'ignorePackages', {
'js': 'never',
'jsx': 'never',
'ts': 'never',
'tsx': 'never',
'scss': 'ignorePackages',
'json': 'always'
}],
// https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-props-no-spreading.md
// disallow spread on html tags directly but allows it on React components
'react/jsx-props-no-spreading': ['error', {
'html': 'enforce',
'custom': 'ignore',
}],
// https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/function-component-definition.md
'react/function-component-definition': 'off',
'react/react-in-jsx-scope': 'off',
'prefer-promise-reject-errors': 'off',
'no-restricted-exports': 'off',
'testing-library/prefer-screen-queries': 'off',
'testing-library/render-result-naming-convention': 'off',
'indent': 'off',
'prettier/prettier': [
'error',
{
'arrowParens': 'avoid',
'bracketSpacing': false,
'semi': true,
'singleQuote': true,
'trailingComma': 'all',
},
],
'import/order': [
'error',
{
'groups': [
'builtin',
'external',
'internal',
'parent',
'sibling',
'index'
],
'newlines-between': 'always',
'alphabetize': {
'order': 'asc',
'caseInsensitive': true
},
'pathGroups': [
{
'pattern': 'react',
'group': 'builtin',
'position': 'before'
},
{
'pattern': '@kununu/**',
'group': 'external',
'position': 'after'
}
],
'pathGroupsExcludedImportTypes': ['react']
}
],
'lodash/import-scope': [2, 'method'],
// https://react.dev/blog/2024/04/25/react-19-upgrade-guide#removed-proptypes-and-defaultprops
'react/require-default-props': 0,
'no-restricted-syntax': ['error',
// Target JSX elements specifically
{
selector: 'ReturnStatement > LogicalExpression[operator="&&"] > JSXElement',
message: 'Prefer early returns over logical expressions in return statements. Use if (condition) return value; instead.'
},
// Target JSX fragments
{
selector: 'ReturnStatement > LogicalExpression[operator="&&"] > JSXFragment',
message: 'Prefer early returns over logical expressions in return statements. Use if (condition) return value; instead.'
},
// Target function calls (which could be component invocations)
{
selector: 'ReturnStatement > LogicalExpression[operator="&&"] > CallExpression',
message: 'Prefer early returns over logical expressions in return statements. Use if (condition) return value; instead.'
},
// Target object expressions (which could be props)
{
selector: 'ReturnStatement > LogicalExpression[operator="&&"] > ObjectExpression',
message: 'Prefer early returns over logical expressions in return statements. Use if (condition) return value; instead.'
},
// Target array expressions
{
selector: 'ReturnStatement > LogicalExpression[operator="&&"] > ArrayExpression',
message: 'Prefer early returns over logical expressions in return statements. Use if (condition) return value; instead.'
},
// Keep the sequence expression rules
{
selector: 'ReturnStatement > SequenceExpression LogicalExpression[operator="&&"]',
message: 'Prefer early returns over logical expressions in return statements. Use if (condition) return value; instead.'
},
{
selector: 'ReturnStatement > ParenthesizedExpression > SequenceExpression LogicalExpression[operator="&&"]',
message: 'Prefer early returns over logical expressions in return statements. Use if (condition) return value; instead.'
}
],
'granular-selectors/granular-selectors': ['error', {
// Array of patterns to include for selector function detection
include: ['use.*Selector.*', 'use.*Store.*']
}]
};
module.exports = {
extends: [
'airbnb', // Many strict rules for ECMAScript and React
'airbnb/hooks',
'plugin:import/errors',
'plugin:jest-dom/recommended',
'plugin:prettier/recommended',
'plugin:react-hooks/recommended',
],
parser: '@babel/eslint-parser',
plugins: [
'@babel',
'lodash',
'sort-destructure-keys',
'granular-selectors'
],
env: {
browser: true,
jest: true,
node: true,
es6: true,
},
rules: baseRules,
overrides: [{
files: [
'**/__tests__/**/*.[jt]s?(x)',
'**/?(*.)+(spec|test).[jt]s?(x)'
],
extends: [
'plugin:testing-library/react'
],
}, {
files: [
'**/*.ts',
'**/*.tsx',
],
extends: [
'airbnb', // Many strict rules for ECMAScript and React
'airbnb-typescript',
'airbnb/hooks',
'plugin:@typescript-eslint/recommended',
'plugin:@typescript-eslint/stylistic',
'plugin:import/errors',
'plugin:jest-dom/recommended',
'plugin:prettier/recommended',
'plugin:react-hooks/recommended',
],
parser: '@typescript-eslint/parser',
plugins: [
'typescript-sort-keys'
],
rules: {
...baseRules,
'@typescript-eslint/indent': 'off',
'@typescript-eslint/no-use-before-define': ['error'],
'@typescript-eslint/no-var-requires': 'off',
'react/prop-types': 'off',
'typescript-sort-keys/interface': ['error', 'asc', {'caseSensitive': false, 'natural': false, requiredFirst: true}],
'typescript-sort-keys/string-enum': ['error', 'asc', {'caseSensitive': false, 'natural': false}],
'import/no-extraneous-dependencies': ['error', {
devDependencies: [
'**/*.spec.ts',
'**/*.spec.tsx',
'**/stories.tsx',
],
}],
},
overrides: [
{
files: ['**/stories.tsx', '*.spec.ts', '*.spec.tsx'],
rules: {
'@typescript-eslint/no-explicit-any': 'off',
},
}
],
}, {
files: ['*.spec.js', '*.spec.jsx', '*.spec.ts', '*.spec.tsx'],
rules: {
'global-require': 'off',
'jsx-a11y/anchor-is-valid': 'off',
},
}, {
files: [
'**/reducers/**/*.js',
'**/reducers/**/*.ts'
],
rules: {
'default-param-last': 'off'
}
}],
};