import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { fixupPluginRules } from '@eslint/compat'; import { FlatCompat } from '@eslint/eslintrc'; import js from '@eslint/js'; import typescriptEslint from '@typescript-eslint/eslint-plugin'; import tsParser from '@typescript-eslint/parser'; import etc from 'eslint-plugin-etc'; import _import from 'eslint-plugin-import'; import noOnlyTests from 'eslint-plugin-no-only-tests'; import noSkipTests from 'eslint-plugin-no-skip-tests'; import react from 'eslint-plugin-react'; import reactHooks from 'eslint-plugin-react-hooks'; import unusedImports from 'eslint-plugin-unused-imports'; import globals from 'globals'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const compat = new FlatCompat({ baseDirectory: __dirname, recommendedConfig: js.configs.recommended, allConfig: js.configs.all, }); export default [ { ignores: [ '**/.eslintrc.js', '**/node_modules', '**/coverage', '**/build', '**/.docusaurus', '**/vite.config.*.timestamp*', '**/vitest.config.*.timestamp*', ], }, ...compat.extends( 'eslint:recommended', 'plugin:@typescript-eslint/recommended', 'plugin:prettier/recommended', 'prettier', 'plugin:eslint-comments/recommended', ), { plugins: { import: fixupPluginRules(_import), react, 'react-hooks': fixupPluginRules(reactHooks), '@typescript-eslint': typescriptEslint, etc, 'no-only-tests': noOnlyTests, 'no-skip-tests': noSkipTests, 'unused-imports': unusedImports, }, languageOptions: { globals: { ...globals.node, ...globals.jest, }, parser: tsParser, ecmaVersion: 6, sourceType: 'module', parserOptions: { ecmaFeatures: { modules: true, }, }, }, settings: { 'import/resolver': { node: { extensions: ['.js', '.ts', '.tsx', '.json'], }, typescript: { alwaysTryTypes: true, }, }, react: { version: 'detect', }, }, rules: { '@typescript-eslint/interface-name-prefix': 'off', '@typescript-eslint/explicit-function-return-type': 'off', '@typescript-eslint/explicit-module-boundary-types': 'off', curly: ['error', 'all'], 'max-params': 'off', 'no-console': [ 'error', { allow: ['warn', 'error'], }, ], 'no-warning-comments': [ 'error', { terms: ['fixme'], location: 'anywhere', }, ], 'no-unused-vars': 'off', 'space-before-blocks': 'error', 'padding-line-between-statements': [ 'error', { blankLine: 'always', prev: '*', next: ['break', 'continue', 'return'], }, { blankLine: 'always', prev: ['const', 'let'], next: '*', }, { blankLine: 'any', prev: ['const', 'let'], next: ['const', 'let'], }, { blankLine: 'always', prev: 'directive', next: '*', }, { blankLine: 'any', prev: 'directive', next: 'directive', }, { blankLine: 'always', prev: 'block-like', next: '*', }, { blankLine: 'always', prev: '*', next: 'block-like', }, ], 'import/order': [ 'error', { pathGroups: [ { pattern: 'react,bem-css-modules', group: 'builtin', position: 'before', }, { pattern: '@docuservix/**', group: 'internal', }, ], pathGroupsExcludedImportTypes: ['react'], 'newlines-between': 'always', groups: ['builtin', 'external', 'internal', 'parent', ['sibling', 'index']], alphabetize: { order: 'asc', caseInsensitive: true, }, }, ], 'react/no-direct-mutation-state': 'error', 'react/no-deprecated': 'error', 'react/no-unsafe': 'error', 'react/jsx-uses-vars': 'error', 'react/jsx-uses-react': 'error', 'react/jsx-curly-brace-presence': ['error', 'never'], 'react-hooks/rules-of-hooks': 'error', 'react-hooks/exhaustive-deps': 'warn', quotes: [ 'error', 'single', { avoidEscape: true, }, ], 'quote-props': ['warn', 'as-needed'], '@typescript-eslint/no-explicit-any': [ 'warn', { ignoreRestArgs: true, }, ], '@typescript-eslint/member-ordering': [ 'error', { default: [ 'public-static-field', 'protected-static-field', 'private-static-field', 'public-instance-field', 'protected-instance-field', 'private-instance-field', 'constructor', 'public-instance-method', 'protected-instance-method', 'private-instance-method', 'public-static-method', 'protected-static-method', 'private-static-method', 'signature', ], }, ], 'etc/prefer-interface': [ 'warn', { allowLocal: true, }, ], '@typescript-eslint/ban-ts-comment': [ 'error', { 'ts-ignore': 'allow-with-description', 'ts-nocheck': 'allow-with-description', 'ts-check': false, 'ts-expect-error': false, }, ], '@typescript-eslint/no-empty-interface': 'warn', '@typescript-eslint/no-empty-function': 'warn', '@typescript-eslint/no-unused-vars': 'off', // todo изучить и включить '@typescript-eslint/no-unused-expressions': 'off', // todo изучить и включить '@typescript-eslint/no-empty-object-type': 'off', 'no-restricted-imports': [ 'error', { paths: [ { name: '@nestjs/swagger', importNames: ['PartialType'], message: "Please import 'PartialType' from '@src/server/common/nest' instead.", }, { name: 'react-bootstrap', importNames: [ 'Card', 'CardHeader', 'CardBody', 'CardFooter', 'Row', 'Col', 'Modal', ], message: "Please use project's components with same name", }, { name: 'react-bootstrap/Modal', message: "Please use project's components with same name", }, { name: '@nestjs/common', importNames: ['Logger'], message: "Please import 'Logger' from '@src/server/logger' instead.", }, { name: 'nestjs-pino', importNames: ['Logger', 'PinoLogger'], message: "Please import 'Logger' from '@src/server/logger' instead.", }, ], }, ], 'unused-imports/no-unused-imports': 'error', 'unused-imports/no-unused-vars': [ 'error', { vars: 'all', args: 'after-used', ignoreRestSiblings: true, argsIgnorePattern: '^_', varsIgnorePattern: '^_', caughtErrorsIgnorePattern: '^_', }, ], 'eslint-comments/require-description': [ 'error', { ignore: ['eslint-enable'], }, ], 'eslint-comments/disable-enable-pair': [ 'error', { allowWholeFile: true, }, ], complexity: ['warn', 10], eqeqeq: ['error'], 'func-style': ['warn', 'declaration'], }, }, { files: ['**/*.spec.{js,jsx,ts,tsx}'], rules: { 'no-only-tests/no-only-tests': 'error', 'no-skip-tests/no-skip-tests': 'warn', 'no-console': [ 'warn', { allow: ['warn', 'error'], }, ], '@typescript-eslint/no-non-null-assertion': 'off', }, }, ];