eslint.config.mjs (393 lines of code) (raw):

/* eslint-disable no-magic-numbers */ /* eslint-disable max-lines */ import path from 'node:path'; import {fileURLToPath} from 'node:url'; import {defineConfig} from 'eslint/config'; import globals from 'globals'; import babelParser from '@babel/eslint-parser'; import importPlugin from 'eslint-plugin-import'; import storybook from 'eslint-plugin-storybook'; import prettierConfig from 'eslint-config-prettier/flat'; import unicorn from 'eslint-plugin-unicorn'; import {FlatCompat} from '@eslint/eslintrc'; import eslint from '@eslint/js'; import tsEslint from 'typescript-eslint'; import reactHooks from 'eslint-plugin-react-hooks'; // eslint-disable-next-line no-underscore-dangle const __filename = fileURLToPath(import.meta.url); // eslint-disable-next-line no-underscore-dangle const __dirname = path.dirname(__filename); const compat = new FlatCompat({ baseDirectory: __dirname, recommendedConfig: eslint.configs.recommended, allConfig: eslint.configs.all, }); // JetBrains configurations with higher priority const jetbrainsConfigRules = compat.extends( '@jetbrains', '@jetbrains/eslint-config/es6', '@jetbrains/eslint-config/browser', '@jetbrains/eslint-config/react', '@jetbrains/eslint-config/test', ); // Remove conflicting import plugin from JetBrains config jetbrainsConfigRules.forEach(config => { if (config.plugins?.import) { delete config.plugins.import; } }); export default defineConfig([ // Global ignores { ignores: [ 'components', 'docs', '**/coverage', '**/dist', '**/test-app', '**/docs', '**/html-report', '**/node_modules', '**/build', '**/test_gen', '**/storybook-dist', ], }, eslint.configs.recommended, importPlugin.flatConfigs.recommended, prettierConfig, ...jetbrainsConfigRules, reactHooks.configs.flat['recommended-latest'], // Base configuration for all files { linterOptions: { reportUnusedInlineConfigs: 'warn', }, languageOptions: { ecmaVersion: 'latest', sourceType: 'module', parser: babelParser, parserOptions: { requireConfigFile: false, babelOptions: { presets: ['@babel/preset-env'], }, }, globals: { ...globals.es2021, }, }, settings: { 'import/core-modules': ['./metadata-messages.json'], 'import/resolver': { exports: {}, webpack: true, }, react: { version: 'detect', runtime: 'automatic', }, }, rules: { camelcase: ['error', {allow: ['^UNSAFE_'], properties: 'never'}], complexity: ['error', 20], // override JetBrains config from 7 to default 20 'consistent-return': 'off', // TypeScript handles this curly: ['error', 'multi-line'], 'default-case-last': 'error', 'default-param-last': 'error', eqeqeq: 'error', 'import/extensions': ['error', 'always', {js: 'never', jsx: 'never', ts: 'never', tsx: 'never'}], 'import/named': 'error', 'import/no-commonjs': 'off', 'import/no-duplicates': 'error', 'import/no-extraneous-dependencies': [ 'error', { devDependencies: [ 'webpack-test.config.js', 'wallaby.config.js', 'rollup.config.js', '*webpack.config.js', '**/*.figma.js', '**/*.test.{js,ts,tsx}', '**/*.stories.{js,ts,tsx}', 'eslint.config.mjs', '.storybook/**', '**/.testplane.conf.js', 'packages/screenshots/**', 'report-metadata.js', 'scripts/**', 'test-runner-jest.config.js', 'test-helpers/**', ], peerDependencies: true, }, ], 'import/no-unresolved': 'error', 'import/prefer-default-export': 'off', 'import/order': [ 'error', { 'newlines-between': 'always', groups: [ ['external', 'builtin'], ['index', 'sibling', 'parent', 'internal'], ['unknown'], ['type'], ['object'], ], distinctGroup: true, pathGroups: [ { pattern: '*.{png,svg,jpg,gif,json}', patternOptions: {matchBase: true}, group: 'object', position: 'after', }, { pattern: '*.{css,scss}', patternOptions: {matchBase: true}, group: 'object', position: 'after', }, ], pathGroupsExcludedImportTypes: [], }, ], 'jsx-quotes': ['error', 'prefer-single'], 'max-classes-per-file': 'off', // Allow multiple classes per file 'max-depth': 'error', 'max-len': [ 'error', 120, { ignoreComments: true, ignoreTemplateLiterals: true, ignoreRegExpLiterals: true, ignorePattern: '"(?=([^"]|"){40,}")|\'(?=([^\']|\'){40,}\')', }, ], 'max-lines': ['error', {max: 400}], 'max-nested-callbacks': ['error', 5], 'no-alert': 'error', 'no-caller': 'error', 'no-confusing-arrow': 'off', // conflict with Prettier 'no-div-regex': 'error', 'no-duplicate-imports': 'error', 'no-else-return': ['error', {allowElseIf: false}], 'no-empty': ['error', {allowEmptyCatch: true}], 'no-extra-label': 'error', 'no-implicit-globals': 'error', 'no-inner-declarations': 'off', // Allow function declarations in blocks 'no-invalid-this': 'error', 'no-iterator': 'error', 'no-loss-of-precision': 'error', 'no-magic-numbers': [ 'error', { ignore: [-1, 0, 1, 2], ignoreEnums: true, ignoreTypeIndexes: true, ignoreReadonlyClassProperties: true, ignoreArrayIndexes: true, }, ], 'no-new-object': 'error', 'no-return-await': 'error', 'no-shadow': 'error', 'no-undef-init': 'error', 'no-underscore-dangle': ['error', {allowAfterThis: true}], 'no-unsafe-finally': 'error', 'no-unused-expressions': 'error', 'no-unused-vars': [ 'error', { argsIgnorePattern: '^_', varsIgnorePattern: '^_', caughtErrors: 'none', ignoreRestSiblings: true, }, ], 'no-use-before-define': ['error', 'nofunc'], 'no-useless-return': 'error', 'no-var': 'error', 'prefer-const': 'error', 'prefer-rest-params': 'error', 'prefer-spread': 'error', 'require-await': 'error', 'react/jsx-uses-react': 'off', // we support new JSX transform in React 17+ 'react/react-in-jsx-scope': 'off', 'react/jsx-tag-spacing': 'off', 'react/jsx-wrap-multilines': 'off', // conflict with Prettier quotes: ['error', 'single', {avoidEscape: true, allowTemplateLiterals: true}], 'use-isnan': 'error', }, }, // Source files configuration (browser code) { files: ['src/**/*'], languageOptions: { globals: { ...globals.browser, }, }, rules: { 'import/no-commonjs': 'error', 'import/no-unused-modules': 'off', }, }, // TypeScript files configuration { files: ['**/*.{ts,tsx}'], plugins: { '@typescript-eslint': tsEslint.plugin, }, extends: [tsEslint.configs.recommended], languageOptions: { parser: tsEslint.parser, ecmaVersion: 'latest', sourceType: 'module', parserOptions: { ecmaFeatures: { jsx: true, }, project: './tsconfig.json', }, }, settings: { 'import/resolver': { typescript: { alwaysTryTypes: true, project: './tsconfig.json', }, webpack: true, }, }, rules: { '@typescript-eslint/ban-ts-comment': 'error', '@typescript-eslint/consistent-type-assertions': 'error', '@typescript-eslint/consistent-type-imports': [ 'error', { fixStyle: 'inline-type-imports', }, ], '@typescript-eslint/consistent-type-definitions': 'error', '@typescript-eslint/explicit-module-boundary-types': 'off', '@typescript-eslint/naming-convention': [ 'error', { selector: 'class', format: ['PascalCase'], }, ], '@typescript-eslint/no-empty-function': 'off', '@typescript-eslint/no-empty-object-type': 'off', '@typescript-eslint/no-inferrable-types': 'off', '@typescript-eslint/no-misused-promises': [ 'error', { checksVoidReturn: false, }, ], '@typescript-eslint/no-namespace': 'off', '@typescript-eslint/no-non-null-assertion': 'off', '@typescript-eslint/no-this-alias': 'off', '@typescript-eslint/no-unsafe-function-type': 'error', '@typescript-eslint/no-unused-expressions': 'error', '@typescript-eslint/no-unused-vars': [ 'error', { argsIgnorePattern: '^_', varsIgnorePattern: '^_', caughtErrors: 'none', ignoreRestSiblings: true, }, ], '@typescript-eslint/no-useless-constructor': 'error', '@typescript-eslint/no-var-requires': 'off', '@typescript-eslint/no-wrapper-object-types': 'error', '@typescript-eslint/triple-slash-reference': 'error', }, }, // Component filename rules { files: ['src/**/*.{ts,tsx}'], plugins: { unicorn, }, rules: { // Enforce kebab-case for component files 'unicorn/filename-case': [ 'error', { cases: { kebabCase: true, }, ignore: [/^[a-z]+(?:-[a-z]+)*(\.(utils|test|constants|classes|interface|stories))?\.tsx?/], }, ], }, }, // Node.js configuration files and scripts { files: ['scripts/**/*', 'test-helpers/**/*', 'packages/**/*', '*.config.js', '.prettierrc.js', '.storybook/**/*'], languageOptions: { globals: { ...globals.node, ...globals.jest, }, }, rules: { 'import/extensions': 'off', 'import/no-commonjs': 'off', // Allow CommonJS in Node.js files }, }, // Test files configuration { files: ['**/*.test.{js,ts,tsx}', 'test-helpers/mocks/**'], languageOptions: { globals: { ...globals.jest, sandbox: false, }, }, rules: { 'new-cap': ['error', {capIsNewExceptionPattern: '^.*.UNSAFE_'}], '@typescript-eslint/no-unused-expressions': 'off', 'no-magic-numbers': 'off', 'max-lines': 'off', }, }, // Storybook files configuration { files: ['**/*.stories.{js,ts,tsx}'], plugins: { storybook, }, languageOptions: { globals: { ...globals.browser, ...globals.node, sandbox: false, }, }, rules: { 'react/no-multi-comp': 'off', 'react/jsx-no-literals': 'off', 'react/no-this-in-sfc': 'off', 'react/prop-types': 'off', 'no-magic-numbers': 'off', 'max-lines': 'off', 'storybook/await-interactions': 'error', 'storybook/context-in-play-function': 'error', 'storybook/default-exports': 'error', 'storybook/hierarchy-separator': 'error', 'storybook/no-redundant-story-name': 'error', 'storybook/story-exports': 'error', 'storybook/use-storybook-expect': 'error', 'storybook/use-storybook-testing-library': 'error', 'storybook/prefer-pascal-case': 'off', }, }, ]);