import eslint from '@eslint/js' import tseslint from 'typescript-eslint' import importX from 'eslint-plugin-import-x' import react from 'eslint-plugin-react' import reactHooks from 'eslint-plugin-react-hooks' import jsxA11y from 'eslint-plugin-jsx-a11y' import eslintConfigPrettier from 'eslint-config-prettier' import eslintPluginPrettier from 'eslint-plugin-prettier' import globals from 'globals' import boundaries from 'eslint-plugin-boundaries' import reactRefresh from 'eslint-plugin-react-refresh' const fsdPathGroups = [ { pattern: 'app/**', group: 'internal', position: 'before' }, { pattern: 'pages/**', group: 'internal', position: 'before' }, { pattern: 'widgets/**', group: 'internal', position: 'before' }, { pattern: 'features/**', group: 'internal', position: 'before' }, { pattern: 'entities/**', group: 'internal', position: 'before' }, { pattern: 'shared/**', group: 'internal', position: 'before' }, // alias вида "@/shared/..." { pattern: '@/**', group: 'internal', position: 'before' }, ] /** Правила + FSD-границы. */ export default tseslint.config( { ignores: ['dist/**', 'node_modules/**'], }, { name: 'react-plugin-settings', settings: { react: { version: '19' } }, }, eslint.configs.recommended, ...tseslint.configs.recommended, importX.flatConfigs.recommended, importX.flatConfigs.typescript, react.configs.flat.recommended, react.configs.flat['jsx-runtime'], reactHooks.configs.flat.recommended, reactRefresh.configs.vite, jsxA11y.flatConfigs.recommended, { files: ['**/*.{ts,tsx}'], languageOptions: { globals: { ...globals.browser, ...globals.es2021 }, }, settings: { 'import/internal-regex': '^(@/)?(app|pages|widgets|features|entities|shared)(/|$)', 'import/resolver': { typescript: { project: './tsconfig.json' }, node: true, }, }, rules: { 'no-console': ['error', { allow: ['warn', 'error', 'info'] }], quotes: ['error', 'single', { avoidEscape: true, allowTemplateLiterals: false }], 'max-len': [ 'warn', { code: 120, ignoreStrings: true, ignoreTrailingComments: true, ignoreTemplateLiterals: true, ignoreComments: true, }, ], 'import-x/extensions': [ 'warn', { js: 'never', jsx: 'never', ts: 'never', tsx: 'never', json: 'always', svg: 'always', }, ], 'import-x/prefer-default-export': 'off', 'import-x/no-extraneous-dependencies': 'off', 'import-x/no-cycle': 'warn', 'import-x/order': [ 'warn', { groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index', 'object', 'type'], pathGroups: [ { pattern: 'react', group: 'external', position: 'before' }, { pattern: 'react-dom', group: 'external', position: 'before' }, { pattern: '@mui/**', group: 'external', position: 'before' }, ...fsdPathGroups, ], pathGroupsExcludedImportTypes: ['react'], 'newlines-between': 'never', alphabetize: { order: 'asc', caseInsensitive: true }, }, ], 'react/prop-types': 'off', 'react/jsx-curly-brace-presence': ['error', { props: 'never', children: 'ignore' }], 'react/display-name': 'off', 'react/no-unescaped-entities': 'warn', '@typescript-eslint/no-explicit-any': 'warn', '@typescript-eslint/no-unused-vars': ['error', { args: 'none' }], 'no-unused-vars': 'off', '@typescript-eslint/no-shadow': 'off', 'no-shadow': 'off', '@typescript-eslint/no-use-before-define': 'error', 'no-use-before-define': 'off', 'consistent-return': 'off', '@typescript-eslint/no-empty-function': 'warn', '@typescript-eslint/no-unnecessary-type-constraint': 'warn', 'class-methods-use-this': 'warn', }, }, { files: ['**/*.{ts,tsx}'], plugins: { prettier: eslintPluginPrettier }, rules: { 'prettier/prettier': ['warn', { endOfLine: 'lf' }] }, }, eslintConfigPrettier, { files: ['**/*.{ts,tsx}'], plugins: { boundaries }, languageOptions: { parserOptions: { ecmaVersion: 'latest', sourceType: 'module', ecmaFeatures: { jsx: true }, }, }, settings: { 'import/resolver': { typescript: { project: './tsconfig.json' }, }, 'boundaries/include': ['src/**/*'], 'boundaries/elements': [ { type: 'app', pattern: 'src/app/**' }, { type: 'pages', pattern: 'src/pages/**' }, { type: 'widgets', pattern: 'src/widgets/**' }, { type: 'features', pattern: 'src/features/**' }, { type: 'entities', pattern: 'src/entities/**' }, { type: 'shared', pattern: 'src/shared/**' }, ], }, rules: { 'boundaries/no-unknown': 'off', 'boundaries/no-unknown-files': 'off', 'boundaries/dependencies': [ 'error', { default: 'disallow', checkUnknownLocals: true, rules: [ { from: { type: 'shared' }, allow: { to: { type: 'shared' } } }, { from: { type: 'entities' }, allow: { to: { type: ['entities', 'shared'] } }, }, { from: { type: 'features' }, allow: { to: { type: ['features', 'entities', 'shared'] } }, }, { from: { type: 'widgets' }, allow: { to: { type: ['widgets', 'features', 'entities', 'shared'] }, }, }, { from: { type: 'pages' }, allow: { to: { type: ['pages', 'widgets', 'features', 'entities', 'shared'], }, }, }, { from: { type: 'app' }, allow: { to: { type: ['app', 'pages', 'widgets', 'features', 'entities', 'shared'], }, }, }, ], }, ], }, }, { files: ['src/app/providers/theme-controller.tsx'], rules: { 'react-refresh/only-export-components': 'off' }, }, { files: ['src/pages/**/ui/**/*.tsx'], rules: { 'react-hooks/incompatible-library': 'off' }, }, { files: ['eslint.config.js'], rules: { 'import-x/no-unresolved': 'off', 'import-x/no-named-as-default': 'off', 'import-x/no-named-as-default-member': 'off', }, }, )