ljosberinn/eslint-config-galex

View on GitHub
src/plugins/eslint-core.ts

Summary

Maintainability
A
0 mins
Test Coverage
A
100%
import restrictedGlobals from 'confusing-browser-globals';
import { type Linter } from 'eslint';

import { files as typeScriptFilesPattern } from '../overrides/typescript';
import {
  type Dependencies,
  type OverrideESLintConfig,
  type RulesCreator,
  type RulesetCreator,
  type WithOverrideType,
} from '../types';
import { prettierRules } from '../utils/prettier';

export const createEslintCoreRules: RulesetCreator = ({
  rules: customRules,
  ...dependencies
}) => {
  return {
    ...createPossibleErrorRules(dependencies),
    ...createBestPractices(dependencies),
    ...createStrictModeRules(dependencies),
    ...createVariableRules(dependencies),
    ...createStylisticIssuesRules(dependencies),
    ...createES6Rules(dependencies),
    ...prettierRules,
    ...safePrettierOverrides,
    ...customRules,
  };
};

export const createPossibleTypeScriptErrorRules: RulesCreator = ({
  typescript: { hasTypeScript },
}) => {
  if (!hasTypeScript) {
    return null;
  }

  return {
    /**
     * @see https://eslint.org/docs/rules/getter-return
     * @see ts(2378)
     */
    'getter-return': 'off',

    /**
     * prevents duplicate function arg names
     *
     * @see https://eslint.org/docs/rules/
     * @see ts(2300)
     */

    'no-dupe-args': 'off',

    /**
     * prevents duplicate keys in object
     *
     * @see https://eslint.org/docs/rules/
     * @see ts(1117)
     */
    'no-dupe-keys': 'off',

    /**
     * prevents overwriting functions declared via `function` syntax
     *
     * @see https://eslint.org/docs/rules/no-func-assign
     * @see ts(2539)
     */
    'no-func-assign': 'off',

    /**
     * disallows trying to overwrite imports
     *
     * @see https://eslint.org/docs/rules/no-import-assign
     * @see ts(2539)
     * @see ts(2540)
     */
    'no-import-assign': 'off',

    /**
     * disallows the use of number literals that immediately lose precision at
     * runtime due to 64-bit floating-point rounding
     *
     * @see https://eslint.org/docs/rules/no-loss-of-precision
     * @see @typescript-eslint/no-loss-of-precision
     */
    'no-loss-of-precision': 'off',

    /**
     * prevents calling certain native objects as function or class
     *
     * @see https://eslint.org/docs/rules/no-obj-calls
     * @see ts(2349)
     */
    'no-obj-calls': 'off',

    /**
     * disallow return on setters as its nonsensical
     *
     * @see https://eslint.org/docs/rules/
     * @see ts(2408)
     */
    'no-setter-return': 'off',

    /**
     * prevents unreachable code
     *
     * @see https://eslint.org/docs/rules/no-unreachable
     * @see ts(7027)
     */
    'no-unreachable': 'off',

    /**
     * prevents cases of unsafe negation
     *
     * @see https://eslint.org/docs/rules/no-unsafe-negation
     * @see ts(2365)
     * @see ts(2360)
     * @see ts(2358)
     */
    'no-unsafe-negation': 'off',

    /**
     * prevents typos when comparing to types
     *
     * @see https://eslint.org/docs/rules/valid-typeof
     *
     * @see ts(2367)
     */
    'valid-typeof': 'off',
  };
};

/**
 * @see https://eslint.org/docs/rules/#possible-errors
 */
export const createPossibleErrorRules: RulesCreator = () => ({
  /**
   * @see https://eslint.org/docs/rules/for-direction
   */
  'for-direction': 'error',

  /**
   * @see https://eslint.org/docs/rules/getter-return
   * @see ts(2378)
   */
  'getter-return': 'warn',

  /**
   * prevents usage of async within `new Promise`
   *
   * @see https://eslint.org/docs/rules/no-async-promise-executor
   */
  'no-async-promise-executor': 'error',

  /**
   * prevents using async in for loop; `use Promise.all` instead
   *
   * @see https://eslint.org/docs/rules/no-await-in-loop
   */
  'no-await-in-loop': 'error',

  /**
   * prevents comparing against negative zero
   *
   * @see https://eslint.org/docs/rules/no-compare-neg-zero
   */
  'no-compare-neg-zero': 'error',

  /**
   * prevents assigning in condition
   *
   * @see https://eslint.org/docs/rules/no-cond-assign
   */
  'no-cond-assign': ['warn', 'except-parens'],

  /**
   * prevents forgotten debug statements. either uncomment the line
   * or remove the statement
   *
   * @see https://eslint.org/docs/rules/no-console
   */
  'no-console': 'warn',

  /**
   * prevents inline constant conditions
   *
   * @see https://eslint.org/docs/rules/no-constant-condition
   */
  'no-constant-condition': 'error',

  /**
   * prevents mistakenly using control characters in regex
   *
   * disable when really required
   *
   * @see https://eslint.org/docs/rules/no-control-regex
   */
  'no-control-regex': 'warn',

  /**
   * prevents forgotten debug statements
   *
   * @see https://eslint.org/docs/rules/no-debugger
   */
  'no-debugger': 'warn',

  /**
   * prevents duplicate function arg names
   *
   * @see https://eslint.org/docs/rules/
   * @see ts(2300)
   */
  'no-dupe-args': 'error',

  /**
   * prevents identical branches
   *
   * @see https://eslint.org/docs/rules/no-dupe-else-if
   */
  'no-dupe-else-if': 'warn',

  /**
   * prevents duplicate keys in object
   *
   * @see https://eslint.org/docs/rules/
   * @see ts(1117)
   */
  'no-dupe-keys': 'warn',

  /**
   * prevens duplicate cases in switches
   *
   * @see https://eslint.org/docs/rules/no-duplicate-case
   */
  'no-duplicate-case': 'warn',

  /**
   * prevents empty statements
   *
   * @see https://eslint.org/docs/rules/no-empty
   */
  'no-empty': 'warn',

  /**
   * disallows empty character classes in regex
   *
   * @see https://eslint.org/docs/rules/no-empty-character-class
   */
  'no-empty-character-class': 'warn',

  /**
   * disallows reassigning something in a catch clause
   *
   * @see https://eslint.org/docs/rules/no-ex-assign
   */
  'no-ex-assign': 'warn',

  /**
   * prevents unecessary boolean casting in conditions
   *
   * @see https://eslint.org/docs/rules/no-extra-boolean-cast
   */
  'no-extra-boolean-cast': 'warn',

  /**
   * off because handled by prettier
   *
   * @see https://eslint.org/docs/rules/no-extra-parens
   */
  'no-extra-parens': 'off',

  /**
   * off because handled by prettier
   *
   * @see https://eslint.org/docs/rules/no-extra-semi
   */
  'no-extra-semi': 'off',

  /**
   * prevents overwriting functions declared via `function` syntax
   *
   * @see https://eslint.org/docs/rules/no-func-assign
   * @see ts(2539)
   */
  'no-func-assign': 'warn',

  /**
   * disallows trying to overwrite imports
   *
   * @see https://eslint.org/docs/rules/no-import-assign
   * @see ts(2539)
   * @see ts(2540)
   */
  'no-import-assign': 'error',

  /**
   * disallows variable or `function` declaration in nested blocks
   *
   * @see https://eslint.org/docs/rules/no-inner-declarations
   */
  'no-inner-declarations': 'warn',

  /**
   * disallows invalid regexp
   *
   * @see https://eslint.org/docs/rules/no-invalid-regexp
   */
  'no-invalid-regexp': 'error',

  /**
   * prevents the use of non-standard whitespace characters
   *
   * @see https://eslint.org/docs/rules/no-irregular-whitespace
   */
  'no-irregular-whitespace': 'warn',

  /**
   * disallows the use of number literals that immediately lose precision at
   * runtime due to 64-bit floating-point rounding
   *
   * @see https://eslint.org/docs/rules/no-loss-of-precision
   * @see @typescript-eslint/no-loss-of-precision
   */
  'no-loss-of-precision': 'error',

  /**
   * prevents the use of multiple code point characters in regexp
   *
   * @see https://eslint.org/docs/rules/no-misleading-character-class
   */
  'no-misleading-character-class': 'warn',

  /**
   * prevents calling certain native objects as function or class
   *
   * @see https://eslint.org/docs/rules/no-obj-calls
   * @see ts(2349)
   */
  'no-obj-calls': 'error',

  /**
   * prevents returning from within a promise executor
   *
   * @see https://eslint.org/docs/rules/no-promise-executor-return
   */
  'no-promise-executor-return': 'error',

  /**
   * use Object.prototype.method instead of foo.method
   *
   * @see https://eslint.org/docs/rules/no-prototype-builtins
   */
  'no-prototype-builtins': 'error',

  /**
   * disallows arbitrary number of spaces in regex. use ` {3}` instead
   *
   * @see https://eslint.org/docs/rules/no-regex-spaces
   */
  'no-regex-spaces': 'warn',

  /**
   * disallow return on setters as its nonsensical
   *
   * @see https://eslint.org/docs/rules/
   * @see ts(2408)
   */
  'no-setter-return': 'error',

  /**
   * prevents accidental holes in arrays
   *
   * @see https://eslint.org/docs/rules/no-sparse-arrays
   */
  'no-sparse-arrays': 'warn',

  /**
   * detects strings that probably should be template literals
   *
   * @see https://eslint.org/docs/rules/no-template-curly-in-string
   */
  'no-template-curly-in-string': 'warn',

  /**
   * warns on unexpected multiline statements (mostly semicolon-free related)
   *
   * @see https://eslint.org/docs/rules/no-unexpected-multiline
   */
  'no-unexpected-multiline': 'warn',

  /**
   * prevents unreachable code
   *
   * @see https://eslint.org/docs/rules/no-unreachable
   * @see ts(7027)
   */
  'no-unreachable': 'warn',

  /**
   * off because taken care of by sonarjs/no-one-iteration-loop
   *
   * @see https://eslint.org/docs/rules/no-unreachable-loop
   * @see sonarjs/no-one-iteration-loop
   */
  'no-unreachable-loop': 'off',

  /**
   * prevents usage of unsafe finally in try/catch
   *
   * @see https://eslint.org/docs/rules/no-unsafe-finally
   */
  'no-unsafe-finally': 'error',

  /**
   * prevents cases of unsafe negation
   *
   * @see https://eslint.org/docs/rules/no-unsafe-negation
   * @see ts(2365)
   * @see ts(2360)
   * @see ts(2358)
   */
  'no-unsafe-negation': 'warn',

  /**
   * prevents self-repeating regexp
   *
   * @see https://eslint.org/docs/rules/no-useless-backreference
   */
  'no-useless-backreference': 'warn',

  /**
   * prevents subtle race conditions in async code
   *
   * @see https://eslint.org/docs/rules/require-atomic-updates
   */
  'require-atomic-updates': 'warn',

  /**
   * off because handled by prettier
   *
   * @see https://eslint.org/docs/rules/semi
   * @see @typescript-eslint/semi
   */
  semi: 'off',

  /**
   * off because handled by prettier
   *
   * @see https://eslint.org/docs/rules/space-before-function-paren
   * @see @typescript-eslint/space-before-function-paren
   */
  'space-before-function-paren': 'off',

  /**
   * prevents typos when comparing to types
   *
   * @see https://eslint.org/docs/rules/valid-typeof
   *
   * @see ts(2367)
   */
  'valid-typeof': 'warn',
});

const curly: Linter.RuleEntry = ['warn', 'all'];

export const createBestPracticesTypescriptRules: RulesCreator = ({
  typescript: { hasTypeScript },
}) => {
  if (!hasTypeScript) {
    return null;
  }

  return {
    /**
     * enforces consistent return in array methods
     *
     * @see https://eslint.org/docs/rules/array-callback-return
     */
    'array-callback-return': 'off',

    /**
     * avoids unexpected side effects of switches without default
     *
     * @see https://eslint.org/docs/rules/default-case
     */
    'default-case': 'off',

    /**
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/default-param-last.md
     * @see @typescript-eslint/default-param-last
     */
    'default-param-last': 'off',

    /**
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/dot-notation.md
     * @see @typescript-eslint/dot-notation
     */
    'dot-notation': 'off',

    /**
     * disallows returning in constructors
     *
     * @see https://eslint.org/docs/rules/no-constructor-return
     */
    'no-constructor-return': 'off',

    /**
     * @see https://eslint.org/docs/rules/no-empty-function
     * @see @typescript-eslint/no-empty-function
     */
    'no-empty-function': 'off',
    /**
     * disallows this outside of classes/class-like objects
     *
     * @see https://eslint.org/docs/rules/no-invalid-this
     */
    'no-invalid-this': 'off',
    /**
     * disallow function creation in loops
     *
     * @see https://eslint.org/docs/rules/no-loop-func
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-loop-func.md
     */
    'no-loop-func': 'off',
    /**
     * disallows reassigning a variable
     *
     * @see https://eslint.org/docs/rules/no-redeclare
     * @see ts(2451)
     * @see https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/no-redeclare.md
     */
    'no-redeclare': 'off',

    /**
     * @see https://eslint.org/docs/rules/no-return-await
     * @see @typescript-eslint/no-return-await
     */
    'no-return-await': 'off',

    /**
     * disallows throwing anything but errors
     *
     * off because handled by @typescript-eslint/no-throw-literal
     *
     * @see https://eslint.org/docs/rules/no-throw-literal
     * @see @typescript-eslint/no-throw-literal
     */
    'no-throw-literal': 'off',

    /**
     * disallows unsafe optional chaining
     *
     * @see https://github.com/eslint/eslint/blob/master/docs/rules/no-unsafe-optional-chaining.md
     */
    'no-unsafe-optional-chaining': 'off',

    /**
     * @see https://eslint.org/docs/rules/no-unused-expression
     * @see @typescript-eslint/no-unused-expression
     */
    'no-unused-expressions': 'off',

    /**
     * @see https://github.com/eslint/eslint/blob/main/docs/rules/no-unused-private-class-members.md
     */
    'no-unused-private-class-members': 'off',
    /**
     * prevents using `async` without `await`
     *
     * @see https://eslint.org/docs/rules/require-await
     * @see @typescript-eslint/require-await
     */
    'require-await': 'off',
  };
};

/**
 * @see https://eslint.org/docs/rules/#best-practices
 */
export const createBestPractices: RulesCreator = ({
  typescript: { hasTypeScript },
}) => ({
  /**
   * off because opinionated
   *
   * @see https://eslint.org/docs/rules/accessor-pairs
   */
  'accessor-pairs': 'off',

  /**
   * enforces consistent return in array methods
   *
   * @see https://eslint.org/docs/rules/array-callback-return
   */
  'array-callback-return': [
    'error',
    {
      checkForEach: true,
    },
  ],

  /**
   * off because prefer-const
   *
   * @see https://eslint.org/docs/rules/block-scoped-var
   * @see prefer-const
   */
  'block-scoped-var': 'off',

  /**
   * off because rarely applicable
   *
   * @see https://eslint.org/docs/rules/class-methods-use-this
   */
  'class-methods-use-this': 'off',

  /**
   * off because handled by sonarjs/cognitive-complexity
   *
   * @see https://eslint.org/docs/rules/complexity
   * @see sonarjs/cognitive-complexity
   */
  complexity: 'off',

  /**
   * enforces consistent return
   *
   * off due to false positives
   *
   * @see https://eslint.org/docs/rules/consistent-return
   */
  'consistent-return': 'off',

  /**
   * prefer curly braces for clarity
   *
   * @see https://eslint.org/docs/rules/curly
   */
  curly,

  /**
   * avoids unexpected side effects of switches without default
   *
   * @see https://eslint.org/docs/rules/default-case
   */
  'default-case': 'error',

  /**
   * prevents uncommon ways of writing switch
   *
   * @see https://eslint.org/docs/rules/default-case-last
   */
  'default-case-last': 'error',

  /**
   * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/default-param-last.md
   * @see @typescript-eslint/default-param-last
   */
  'default-param-last': 'error',

  /**
   * off because prettier territory
   *
   * @see https://eslint.org/docs/rules/dot-location
   */
  'dot-location': 'off',

  /**
   * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/dot-notation.md
   * @see @typescript-eslint/dot-notation
   */
  'dot-notation': 'warn',

  /**
   * prevents unsafe comparison
   *
   * @see https://eslint.org/docs/rules/eqeqeq
   */
  eqeqeq: 'warn',

  /**
   * off because opinionated
   *
   * @see https://eslint.org/docs/rules/grouped-accessor-pairs
   */
  'grouped-accessor-pairs': 'off',

  /**
   * warns about unexpected behaviour
   *
   * @see https://eslint.org/docs/rules/guard-for-in
   */
  'guard-for-in': 'warn',

  /**
   * off because abitrary
   *
   * @see https://eslint.org/docs/rules/max-classes-per-file
   */
  'max-classes-per-file': 'off',

  /**
   * prevents potentially accidentally blocking code
   *
   * @see https://eslint.org/docs/rules/no-alert
   */
  'no-alert': 'error',

  /**
   * forbidden in strict mode anyways
   *
   * @see https://eslint.org/docs/rules/no-caller
   */
  'no-caller': 'error',

  /**
   * disallows declarations in switch cases due to potentially unexpected
   * behaviour
   *
   * @see https://eslint.org/docs/rules/no-case-declarations
   */
  'no-case-declarations': 'warn',

  /**
   * disallows returning in constructors
   *
   * @see https://eslint.org/docs/rules/no-constructor-return
   */
  'no-constructor-return': 'error',

  /**
   * requires regex literals to escape division operators
   *
   * @see https://eslint.org/docs/rules/no-div-regex
   */
  'no-div-regex': 'warn',

  /**
   * prefer early return
   *
   * @see https://eslint.org/docs/rules/no-else-return
   */
  'no-else-return': 'warn',

  /**
   * @see https://eslint.org/docs/rules/no-empty-function
   * @see @typescript-eslint/no-empty-function
   */
  'no-empty-function': 'error',

  /**
   * disallows empty destructuring
   *
   * @see https://eslint.org/docs/rules/no-empty-pattern
   */
  'no-empty-pattern': 'warn',

  /**
   * disallows empty statics
   *
   * @see https://eslint.org/docs/latest/rules/no-empty-static-block
   */
  'no-empty-static-block': 'warn',

  /**
   * disallow unsafe null comparison
   *
   * @see https://eslint.org/docs/rules/no-eq-null
   */
  'no-eq-null': 'error',

  /**
   * disallows eval
   *
   * @see https://eslint.org/docs/rules/no-eval
   */
  'no-eval': 'error',

  /**
   * prevents extending native prototypes
   *
   * @see https://eslint.org/docs/rules/no-extend-native
   */
  'no-extend-native': 'error',

  /**
   * prevents unecessary function binding
   *
   * @see https://eslint.org/docs/rules/no-extra-bind
   */
  'no-extra-bind': 'warn',

  /**
   * disallows unecessary labels
   *
   * off because labels are forbidden
   *
   * @see https://eslint.org/docs/rules/no-extra-label
   * @see no-label
   */
  'no-extra-label': 'off',

  /**
   * disallows case fallthrough
   *
   * @see https://eslint.org/docs/rules/no-fallthrough
   */
  'no-fallthrough': 'warn',

  /**
   * disallows floating decimals
   *
   * @see https://eslint.org/docs/rules/no-floating-decimal
   */
  'no-floating-decimal': 'warn',

  /**
   * disallows overwriting globals
   *
   * @see https://eslint.org/docs/rules/no-global-assign
   */
  'no-global-assign': 'warn',

  /**
   * disallows coercion for anything but booleans
   *
   * @see https://eslint.org/docs/rules/no-implicit-coercion
   */
  'no-implicit-coercion': [
    'warn',
    {
      boolean: false,
      number: true,
      string: true,
    },
  ],

  /**
   * disallows implicit globals for clarity
   *
   * @see https://eslint.org/docs/rules/no-implicit-globals
   */
  'no-implicit-globals': 'error',

  /**
   * disallows implied eval
   *
   * @see https://eslint.org/docs/rules/no-implied-eval
   */
  'no-implied-eval': 'error',

  /**
   * disallows this outside of classes/class-like objects
   *
   * @see https://eslint.org/docs/rules/no-invalid-this
   */
  'no-invalid-this': 'error',

  /**
   * disallows __iterator__ property
   *
   * off because legacy
   *
   * @see https://eslint.org/docs/rules/no-iterator
   */
  'no-iterator': 'off',

  /**
   * disallows labels
   *
   * @see https://eslint.org/docs/rules/no-labels
   */
  'no-labels': 'error',

  /**
   * disallows standalone code blocks
   *
   * off because valid in es6
   *
   * @see https://eslint.org/docs/rules/no-lone-blocks
   */
  'no-lone-blocks': 'off',

  /**
   * disallow function creation in loops
   *
   * @see https://eslint.org/docs/rules/no-loop-func
   * @see https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-loop-func.md
   */
  'no-loop-func': 'error',

  /**
   * always off because it expects literally every number to be assigned to
   * a variable before
   *
   * @see https://eslint.org/docs/rules/no-magic-numbers
   * @see @typescript-eslint/no-magic-numbers
   */
  'no-magic-numbers': 'off',

  /**
   * off because prettier takes care of that
   *
   * @see https://eslint.org/docs/rules/no-multi-spaces
   */
  'no-multi-spaces': 'off',

  /**
   * disallow multiline strings; use template literals instead.
   *
   * off because of misleading error message.
   *
   * @see https://eslint.org/docs/rules/no-multi-str
   */
  'no-multi-str': 'off',

  /**
   * disallow new for side effects
   *
   * @see https://eslint.org/docs/rules/no-new
   */
  'no-new': 'error',

  /**
   * disallows implicit eval
   *
   * @see https://eslint.org/docs/rules/no-new-func
   */
  'no-new-func': 'error',

  /**
   * @see https://eslint.org/docs/latest/rules/no-new-native-nonconstructor
   */
  'no-new-native-nonconstructor': hasTypeScript ? 'off' : 'warn',

  /**
   * disallows primitive wrapper instances such as `new String('foo')`
   *
   * @see https://eslint.org/docs/rules/no-new-wrappers
   */
  'no-new-wrappers': 'error',

  /**
   * disallows legacy escape sequences
   *
   * @see https://eslint.org/docs/rules/no-nonoctal-decimal-escape
   */
  'no-nonoctal-decimal-escape': 'error',

  /**
   * disallows octals
   *
   * @see https://eslint.org/docs/rules/no-octal
   */
  'no-octal': 'warn',

  /**
   * disallow octal escapse; deprecated
   *
   * @see https://eslint.org/docs/rules/no-octal-escape
   */
  'no-octal-escape': 'error',

  /**
   * disallows param mutation. copy locally instead.
   *
   * @see https://eslint.org/docs/rules/no-param-reassign
   */
  'no-param-reassign': 'error',

  /**
   * disallows usage of `__proto__`
   *
   * @see https://eslint.org/docs/rules/no-proto
   */
  'no-proto': 'error',

  /**
   * disallows reassigning a variable
   *
   * @see https://eslint.org/docs/rules/no-redeclare
   * @see ts(2451)
   * @see https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/no-redeclare.md
   */
  'no-redeclare': 'error',

  /**
   * off because indivudal
   *
   * @see https://eslint.org/docs/rules/no-restricted-properties
   */
  'no-restricted-properties': 'off',

  /**
   * disallows assignments in return
   *
   * @see https://eslint.org/docs/rules/no-return-assign
   */
  'no-return-assign': 'error',

  /**
   * @see https://eslint.org/docs/rules/no-return-await
   * @see @typescript-eslint/no-return-await
   */
  'no-return-await': 'error',

  /**
   * disallow inline javascript in urls
   *
   * @see https://eslint.org/docs/rules/no-script-url
   */
  'no-script-url': 'error',

  /**
   * disallow self assignments
   *
   * @see https://eslint.org/docs/rules/no-self-assign
   */
  'no-self-assign': 'error',

  /**
   * disallow comparing a variable against itself
   *
   * @see https://eslint.org/docs/rules/no-self-compare
   */
  'no-self-compare': 'error',

  /**
   * disallows use of comma as operator
   *
   * @see https://eslint.org/docs/rules/no-sequences
   */
  'no-sequences': 'error',

  /**
   * disallows throwing anything but errors
   *
   * off because handled by @typescript-eslint/no-throw-literal
   *
   * @see https://eslint.org/docs/rules/no-throw-literal
   * @see @typescript-eslint/no-throw-literal
   */
  'no-throw-literal': 'error',

  /**
   * disallow infinite loops
   *
   * @see https://eslint.org/docs/rules/no-unmodified-loop-condition
   */
  'no-unmodified-loop-condition': 'error',

  /**
   * disallows unsafe optional chaining
   *
   * @see https://github.com/eslint/eslint/blob/master/docs/rules/no-unsafe-optional-chaining.md
   */
  'no-unsafe-optional-chaining': 'error',

  /**
   * @see https://eslint.org/docs/rules/no-unused-expression
   * @see @typescript-eslint/no-unused-expression
   */
  'no-unused-expressions': [
    'error',
    {
      allowShortCircuit: true,
      allowTaggedTemplates: true,
      allowTernary: true,
    },
  ],

  /**
   * removes unused labels
   *
   * off because labels are forbidden
   *
   * @see https://eslint.org/docs/rules/no-unused-labels
   * @see no-label
   */
  'no-unused-labels': 'off',

  /**
   * @see https://github.com/eslint/eslint/blob/main/docs/rules/no-unused-private-class-members.md
   */
  'no-unused-private-class-members': 'warn',

  /**
   * call functions directly
   *
   * @see https://eslint.org/docs/rules/no-useless-call
   */
  'no-useless-call': 'error',

  /**
   * disallows useless catch
   *
   * off because already handled by @sonarjs/no-useless-catch
   *
   * @see https://eslint.org/docs/rules/no-useless-catch
   * @see sonarjs/no-useless-catch
   */
  'no-useless-catch': 'off',

  /**
   * disallows string concat, just write it as string
   *
   * @see https://eslint.org/docs/rules/no-useless-concat
   */
  'no-useless-concat': 'error',

  /**
   * disallows needlessly escaping
   *
   * @see https://eslint.org/docs/rules/no-useless-escape
   */
  'no-useless-escape': 'warn',

  /**
   * disallows useless return
   *
   * @see https://eslint.org/docs/rules/no-useless-return
   */
  'no-useless-return': 'warn',

  /**
   * prevents use of void operator
   *
   * @see https://eslint.org/docs/rules/no-void
   */
  'no-void': 'off',

  /**
   * off because opinionated
   *
   * @see https://eslint.org/docs/rules/no-warning-comments
   */
  'no-warning-comments': 'off',

  /**
   * disallows `with`, forbidden in strict mode anyways
   *
   * @see https://eslint.org/docs/rules/no-with
   */
  'no-with': 'error',

  /**
   * prefer named capture groups in regexp
   *
   * off because it leads to a lot of additional code. case per case basis.
   *
   * @see https://eslint.org/docs/rules/prefer-named-capture-group
   */
  'prefer-named-capture-group': 'off',

  /**
   * prefer rejecting with errors
   *
   * @see https://eslint.org/docs/rules/prefer-promise-reject-errors
   */
  'prefer-promise-reject-errors': 'error',

  /**
   * disallow regexp constructor with strings as argument
   *
   * use it inline
   *
   * @see https://eslint.org/docs/rules/prefer-regex-literals
   */
  'prefer-regex-literals': 'error',

  /**
   * off because irrelevant in an es5+ world
   *
   * @see https://eslint.org/docs/rules/radix
   */
  radix: 'off',

  /**
   * prevents using `async` without `await`
   *
   * @see https://eslint.org/docs/rules/require-await
   * @see @typescript-eslint/require-await
   */
  'require-await': 'error',

  /**
   * enforces u flag on regexp. mostly here for the 2nd reason: find errors early
   *
   * @see https://eslint.org/docs/rules/require-unicode-regexp
   */
  'require-unicode-regexp': 'error',

  /**
   * off because nonsensical
   *
   * @see https://eslint.org/docs/rules/vars-on-top
   */
  'vars-on-top': 'off',

  /**
   * expects iifes to be wrapped
   *
   * @see https://eslint.org/docs/rules/wrap-iife
   */
  'wrap-iife': 'warn',

  /**
   * disallows reversing a comparison
   *
   * @see https://eslint.org/docs/rules/yoda
   */
  yoda: 'warn',
});

export const createStrictModeRules: RulesCreator = () => ({
  /**
   * enables/disables strict mode
   *
   * without effect since index declares parserOptions.sourceType to module
   *
   * @see https://eslint.org/docs/rules/strict
   */
  strict: 'off',
});

export const createVariableTypeScriptRules: RulesCreator = ({
  typescript: { hasTypeScript },
}) => {
  if (!hasTypeScript) {
    return null;
  }

  return {
    /**
     * disallow using undefined variables
     *
     * @see https://eslint.org/docs/rules/no-undef
     * @see ts(2304)
     */
    'no-undef': 'off',
    /**
     * @see https://eslint.org/docs/rules/no-unused-vars
     * @see @typescript-eslint/no-unused-vars
     */
    'no-unused-vars': 'off',

    /**
     * @see https://eslint.org/docs/rules/no-use-before-define
     * @see @typescript-eslint/no-use-before-define
     */
    'no-use-before-define': 'off',
  };
};

/**
 * @see https://eslint.org/docs/rules/#variables
 */
export const createVariableRules: RulesCreator = () => ({
  /**
   * off because required to escape scope
   *
   * @see https://eslint.org/docs/rules/init-declarations
   */
  'init-declarations': 'off',

  /**
   * off because opinionated
   *
   * @see https://eslint.org/docs/rules/no-delete-var
   */
  'no-delete-var': 'off',

  /**
   * disallows labels that are variable names
   *
   * off because labels are disallowed
   *
   * @see https://eslint.org/docs/rules/no-label-var
   * @see no-label
   */
  'no-label-var': 'off',

  /**
   * disallows usage of specific globals
   *
   * @see https://eslint.org/docs/rules/no-restricted-globals
   */
  'no-restricted-globals': ['error', ...restrictedGlobals],

  /**
   * off because opinionated
   *
   * @see https://eslint.org/docs/rules/no-shadow
   */
  'no-shadow': 'off',

  /**
   * disallows shadowing restricted names
   *
   * @see https://eslint.org/docs/rules/no-shadow-restricted-names
   */
  'no-shadow-restricted-names': 'error',

  /**
   * disallow using undefined variables
   *
   * @see https://eslint.org/docs/rules/no-undef
   * @see ts(2304)
   */
  'no-undef': 'error',

  /**
   * disallows declaring new variables with `undefined` as explicit value
   *
   * @see https://eslint.org/docs/rules/no-undef-init
   */
  'no-undef-init': 'warn',

  /**
   * off because already taken care of by no-shadow-restricted-names
   *
   * @see https://eslint.org/docs/rules/no-undefined
   * @see no-shadow-restricted-names
   */
  'no-undefined': 'off',

  /**
   * @see https://eslint.org/docs/rules/no-unused-vars
   * @see @typescript-eslint/no-unused-vars
   */
  'no-unused-vars': [
    'warn',
    {
      args: 'none',
      ignoreRestSiblings: true,
    },
  ],

  /**
   * @see https://eslint.org/docs/rules/no-use-before-define
   * @see @typescript-eslint/no-use-before-define
   */
  'no-use-before-define': [
    'warn',
    {
      classes: false,
      functions: false,
      variables: false,
    },
  ],
});

export const createStylisticIssuesTypeScriptRules: RulesCreator = ({
  typescript: { hasTypeScript, config },
  react: { isCreateReactApp, isNext },
}) => {
  if (!hasTypeScript) {
    return null;
  }

  return {
    /**
     * ensures proper spacing between class members
     *
     * @see https://eslint.org/docs/rules/lines-between-class-members
     * @see @typescript-eslint/lines-between-class-members
     */
    'lines-between-class-members': 'off',

    /**
     * expects instance creations to begin with a capital letter
     *
     * @see https://eslint.org/docs/rules/new-cap
     */
    'new-cap': config?.compilerOptions?.experimentalDecorators ? 'off' : 'warn',

    /**
     * @see https://eslint.org/docs/rules/no-array-constructor
     * @see @typescript-eslint/no-array-constructor
     * @see unicorn/no-new-array
     */
    'no-array-constructor': 'off',

    /**
     * @see https://eslint.org/docs/rules/spaced-comment
     */
    'spaced-comment': [
      'warn',
      'always',
      /**
       * next and create-react-app have `...d.ts` files using old
       * `reference types="..."`
       * syntax which gets reported otherwise
       *
       * @see https://github.com/ljosberinn/eslint-config-galex/issues/315
       */
      isCreateReactApp || isNext ? { markers: ['/'] } : {},
    ],
  };
};

/**
 * @see https://eslint.org/docs/rules/#stylistic-issues
 */
export const createStylisticIssuesRules: RulesCreator = () => ({
  /**
   * off because handled by prettier
   *
   * @see https://eslint.org/docs/rules/array-bracket-newline
   */
  'array-bracket-newline': 'off',

  /**
   * off because handled by prettier
   *
   * @see https://eslint.org/docs/rules/array-bracket-spacing
   */
  'array-bracket-spacing': 'off',

  /**
   * off because handled by prettier
   *
   * @see https://eslint.org/docs/rules/array-element-newline
   */
  'array-element-newline': 'off',

  /**
   * off because handled by prettier
   *
   * @see https://eslint.org/docs/rules/block-spacing
   */
  'block-spacing': 'off',

  /**
   * off because handled by prettier
   *
   * @see https://eslint.org/docs/rules/brace-style
   */
  'brace-style': 'off',

  /**
   * off because opinionated
   *
   * @see https://eslint.org/docs/rules/camelcase
   */
  camelcase: 'off',

  /**
   * off because opinionated
   *
   * @see https://eslint.org/docs/rules/capitalized-comments
   */
  'capitalized-comments': 'off',

  /**
   * off because handled by prettier
   *
   * @see https://eslint.org/docs/rules/comma-dangle
   */
  'comma-dangle': 'off',

  /**
   * off because handled by prettier
   *
   * @see https://eslint.org/docs/rules/comma-spacing
   */
  'comma-spacing': 'off',

  /**
   * off because handled by prettier
   *
   * @see https://eslint.org/docs/rules/comma-style
   */
  'comma-style': 'off',

  /**
   * off because handled by prettier
   *
   * @see https://eslint.org/docs/rules/computed-property-spacing
   */
  'computed-property-spacing': 'off',

  /**
   * off because opinionated, partially outdated and individual
   *
   * @see https://eslint.org/docs/rules/consistent-this
   */
  'consistent-this': 'off',

  /**
   * off because handled by prettier
   *
   * @see https://eslint.org/docs/rules/eol-last
   */
  'eol-last': 'off',

  /**
   * disallows `fn ()`, prefers `fn()`
   *
   * off because prettier takes care of that
   *
   * @see https://eslint.org/docs/rules/func-call-spacing
   * @see @typescript-eslint/func-call-spacing.md
   */
  'func-call-spacing': 'off',

  /**
   * off because arbitrary
   *
   * @see https://eslint.org/docs/rules/func-name-matching
   */
  'func-name-matching': 'off',

  /**
   * off because opinionated
   *
   * @see https://eslint.org/docs/rules/func-names
   */
  'func-names': ['warn', 'as-needed'],

  /**
   * off because opinionated
   *
   * @see https://eslint.org/docs/rules/func-style
   */
  'func-style': 'off',

  /**
   * off because handled by prettier
   *
   * @see https://eslint.org/docs/rules/function-call-argument-newline
   */
  'function-call-argument-newline': 'off',

  /**
   * off because handled by prettier
   *
   * @see https://eslint.org/docs/rules/function-paren-newline
   */
  'function-paren-newline': 'off',

  /**
   * off because opinonated
   *
   * @see https://eslint.org/docs/rules/id-denylist
   */
  'id-denylist': 'off',

  /**
   * off because too specific
   *
   * @see https://eslint.org/docs/rules/id-length
   */
  'id-length': 'off',

  /**
   * off because too specific
   *
   * @see https://eslint.org/docs/rules/id-match
   */
  'id-match': 'off',

  /**
   * off because handled by prettier
   *
   * @see https://eslint.org/docs/rules/implicit-arrow-linebreak
   */
  'implicit-arrow-linebreak': 'off',

  /**
   * off because handled by prettier
   *
   * @see https://eslint.org/docs/rules/indent
   */
  indent: 'off',

  /**
   * off because handled by prettier
   *
   * @see https://eslint.org/docs/rules/jsx-quotes
   */
  'jsx-quotes': 'off',

  /**
   * off because handled by prettier
   *
   * @see https://eslint.org/docs/rules/key-spacing
   */
  'key-spacing': 'off',

  /**
   * off because prettier takes care of that
   *
   * @see https://eslint.org/docs/rules/keyword-spacing
   * @see @typescript-eslint/keyword-spacing
   */
  'keyword-spacing': 'off',

  /**
   * off because opinionated
   *
   * @see https://eslint.org/docs/rules/line-comment-position
   */
  'line-comment-position': 'off',

  /**
   * off because handled by prettier
   *
   * @see https://eslint.org/docs/rules/linebreak-style
   */
  'linebreak-style': 'off',

  /**
   * off because opinionated
   *
   * @see https://eslint.org/docs/rules/lines-around-comment
   */
  'lines-around-comment': 'off',

  /**
   * ensures proper spacing between class members
   *
   * @see https://eslint.org/docs/rules/lines-between-class-members
   * @see @typescript-eslint/lines-between-class-members
   */
  'lines-between-class-members': 'warn',

  /**
   * off because opinionated and mostly unevaluated at this time, sorry!
   * needs to be guarded behind ES2021 check at least
   *
   * @see https://eslint.org/docs/latest/rules/logical-assignment-operators#rule-details
   */
  'logical-assignment-operators': 'off',

  /**
   * off because opinionated
   *
   * @see https://eslint.org/docs/rules/max-depth
   */
  'max-depth': 'off',

  /**
   * off because taken care of by prettier
   *
   * @see https://eslint.org/docs/rules/max-len
   */
  'max-len': 'off',

  /**
   * off because opinionated
   *
   * @see https://eslint.org/docs/rules/max-lines
   */
  'max-lines': 'off',

  /**
   * off because opinionated
   *
   * @see https://eslint.org/docs/rules/max-lines-per-function
   */
  'max-lines-per-function': 'off',

  /**
   * off because opinionated
   *
   * @see https://eslint.org/docs/rules/max-nested-callbacks
   */
  'max-nested-callbacks': 'off',

  /**
   * off because technically nice, although individual
   *
   * @see https://eslint.org/docs/rules/max-params
   */
  'max-params': 'off',

  /**
   * off because opinionated
   *
   * @see https://eslint.org/docs/rules/max-statements
   */
  'max-statements': 'off',

  /**
   * off because handled by prettier to some degree
   *
   * @see https://eslint.org/docs/rules/max-statements-per-line
   */
  'max-statements-per-line': 'off',

  /**
   * off because opinionated
   *
   * @see https://eslint.org/docs/rules/multiline-comment-style
   */
  'multiline-comment-style': 'off',

  /**
   * off because handled by prettier
   *
   * @see https://eslint.org/docs/rules/multiline-ternary
   */
  'multiline-ternary': 'off',

  /**
   * expects instance creations to begin with a capital letter
   *
   * @see https://eslint.org/docs/rules/new-cap
   */
  'new-cap': 'warn',

  /**
   * off because handled by prettier
   *
   * @see https://eslint.org/docs/rules/new-parens
   */
  'new-parens': 'off',

  /**
   * off because handled by prettier
   *
   * @see https://eslint.org/docs/rules/newline-per-chained-call
   */
  'newline-per-chained-call': 'off',

  /**
   * @see https://eslint.org/docs/rules/no-array-constructor
   * @see @typescript-eslint/no-array-constructor
   * @see unicorn/no-new-array
   */
  'no-array-constructor': 'error',

  /**
   * prevents accidental uses of bitwise operators
   *
   * @see https://eslint.org/docs/rules/no-bitwise
   */
  'no-bitwise': 'warn',

  /**
   * off because opinionated although rarely used anyways
   *
   * @see https://eslint.org/docs/rules/no-continue
   */
  'no-continue': 'off',

  /**
   * off because opinionated
   *
   * @see https://eslint.org/docs/rules/no-inline-comments
   */
  'no-inline-comments': 'off',

  /**
   * prefer `else if` over `else { if (condition )}`
   *
   * @see https://eslint.org/docs/rules/no-lonely-if
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/master/docs/rules/no-lonely-if.md
   */
  'no-lonely-if': 'off',

  /**
   * @see https://eslint.org/docs/rules/no-mixed-operators
   */
  'no-mixed-operators': [
    'warn',
    {
      allowSamePrecedence: false,
      groups: [
        ['&', '|', '^', '~', '<<', '>>', '>>>'],
        ['==', '!=', '===', '!==', '>', '>=', '<', '<='],
        ['&&', '||'],
        ['in', 'instanceof'],
      ],
    },
  ],

  /**
   * off because handled by prettier
   *
   * @see https://eslint.org/docs/rules/no-mixed-spaces-and-tabs
   */
  'no-mixed-spaces-and-tabs': 'off',

  /**
   * prefer declaring one variable at a time, also bug prone
   *
   * @see https://eslint.org/docs/rules/no-multi-assign
   */
  'no-multi-assign': 'error',

  /**
   * off because handled by prettier
   *
   * @see https://eslint.org/docs/rules/no-multiple-empty-lines
   */
  'no-multiple-empty-lines': 'off',

  /**
   * prefer `if(true) {} else` flows
   *
   * @see https://eslint.org/docs/rules/no-negated-condition
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/no-negated-condition.md
   */
  'no-negated-condition': 'off',

  /**
   * off because opinionated
   *
   * @see https://eslint.org/docs/rules/no-nested-ternary
   */
  'no-nested-ternary': 'off',

  /**
   * prevents calling `new Object()`
   *
   * @see https://eslint.org/docs/rules/no-new-object
   */
  'no-new-object': 'error',

  /**
   * off because opinionated
   *
   * @see https://eslint.org/docs/rules/no-plusplus
   */
  'no-plusplus': 'off',

  /**
   * off because opinionated
   *
   * @see https://eslint.org/docs/rules/no-restricted-syntax
   */
  'no-restricted-syntax': 'off',

  /**
   * off because opinionated
   *
   * @see https://eslint.org/docs/rules/no-tabs
   */
  'no-tabs': 'off',

  /**
   * off because pointless
   *
   * @see https://eslint.org/docs/rules/no-ternary
   */
  'no-ternary': 'off',

  /**
   * off because handled by prettier
   *
   * @see https://eslint.org/docs/rules/no-trailing-spaces
   */
  'no-trailing-spaces': 'off',

  /**
   * off because opinionated and sometimes still needed for copies
   *
   * @see https://eslint.org/docs/rules/no-underscore-dangle
   */
  'no-underscore-dangle': 'off',

  /**
   * prefer foo === bar over foo === bar ? true : false
   *
   * @see https://eslint.org/docs/rules/no-unneeded-ternary
   */
  'no-unneeded-ternary': 'warn',

  /**
   * off because handled by prettier
   *
   * @see https://eslint.org/docs/rules/no-whitespace-before-property
   */
  'no-whitespace-before-property': 'off',

  /**
   * off because handled by prettier
   *
   * @see https://eslint.org/docs/rules/nonblock-statement-body-position
   */
  'nonblock-statement-body-position': 'off',

  /**
   * off because handled by prettier
   *
   * @see https://eslint.org/docs/rules/object-curly-newline
   */
  'object-curly-newline': 'off',

  /**
   * off because handled by prettier
   *
   * @see https://eslint.org/docs/rules/object-curly-spacing
   */
  'object-curly-spacing': 'off',

  /**
   * off because handled by prettier
   *
   * @see https://eslint.org/docs/rules/object-property-newline
   */
  'object-property-newline': 'off',

  /**
   * always declare variables separately
   *
   * @see https://eslint.org/docs/rules/one-var
   */
  'one-var': ['warn', 'never'],

  /**
   * off because handled by prettier
   *
   * @see https://eslint.org/docs/rules/one-var-declaration-per-line
   */
  'one-var-declaration-per-line': 'off',

  /**
   * prefer `x += y` over `x = x + y`
   *
   * @see https://eslint.org/docs/rules/operator-assignment
   */
  'operator-assignment': ['warn', 'always'],

  /**
   * off because handled by prettier
   *
   * @see https://eslint.org/docs/rules/operator-linebreak
   */
  'operator-linebreak': 'off',

  /**
   * off because handled by prettier
   *
   * @see https://eslint.org/docs/rules/padded-blocks
   */
  'padded-blocks': 'off',

  /**
   * off because too opinionated
   *
   * @see https://eslint.org/docs/rules/padding-line-between-statements
   */
  'padding-line-between-statements': 'off',

  /**
   * prefer ** over Math.pow
   *
   * @see https://eslint.org/docs/rules/prefer-exponentiation-operator
   */
  'prefer-exponentiation-operator': 'warn',

  /**
   * prefer Object.hasOwn over Object.prototype.hasOwnproperty.call()
   *
   * @see https://eslint.org/docs/rules/prefer-object-has-own
   */
  'prefer-object-has-own': 'warn',

  /**
   * prefer spreading over Object.assign
   *
   * @see https://eslint.org/docs/rules/prefer-object-spread
   */
  'prefer-object-spread': 'warn',

  /**
   * off because handled by prettier
   *
   * @see https://eslint.org/docs/rules/quote-props
   */
  'quote-props': 'off',

  /**
   * off because handled by prettier
   *
   * @see https://eslint.org/docs/rules/quotes
   */
  quotes: 'off',

  /**
   * off because handled by prettier
   *
   * @see https://eslint.org/docs/rules/semi
   */
  semi: 'off',

  /**
   * off because handled by prettier
   *
   * @see https://eslint.org/docs/rules/semi-spacing
   */
  'semi-spacing': 'off',

  /**
   * off because handled by prettier
   *
   * @see https://eslint.org/docs/rules/semi-style
   */
  'semi-style': 'off',

  /**
   * off because taken care of by sort-keys-fix
   *
   * @see https://eslint.org/docs/rules/sort-keys
   * @see sort-keys-fix/sort-keys-fix
   */
  'sort-keys': 'off',

  /**
   * off because opinionated and you usually don't assign multiple variables
   * anymore nowadays anyways
   *
   * @see https://eslint.org/docs/rules/sort-vars
   */
  'sort-vars': 'off',

  /**
   * off because handled by prettier
   *
   * @see https://eslint.org/docs/rules/space-before-blocks
   * @see https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/space-before-blocks.md
   */
  'space-before-blocks': 'off',

  /**
   * off because handled by prettier
   *
   * @see https://eslint.org/docs/rules/space-before-function-paren
   */
  'space-before-function-paren': 'off',

  /**
   * off because handled by prettier
   *
   * @see https://eslint.org/docs/rules/space-in-parens
   */
  'space-in-parens': 'off',

  /**
   * off because handled by prettier
   *
   * @see https://eslint.org/docs/rules/space-infix-ops
   */
  'space-infix-ops': 'off',

  /**
   * off because handled by prettier
   *
   * @see https://eslint.org/docs/rules/space-unary-ops
   */
  'space-unary-ops': 'off',

  /**
   * @see https://eslint.org/docs/rules/spaced-comment
   */
  'spaced-comment': ['warn', 'always', {}],

  /**
   * off because handled by prettier
   *
   * @see https://eslint.org/docs/rules/switch-colon-spacing
   */
  'switch-colon-spacing': 'off',

  /**
   * off because handled by prettier
   *
   * @see https://eslint.org/docs/rules/template-tag-spacing
   */
  'template-tag-spacing': 'off',

  /**
   * off because handled by prettier
   *
   * @see https://eslint.org/docs/rules/unicode-bom
   */
  'unicode-bom': 'off',
  /**
   * prefer using isNaN over == NaN
   *
   * off because taken care of by unicorn/prefer-number-properties
   *
   * @see https://eslint.org/docs/rules/use-isnan
   * @see unicorn/prefer-number-properties
   */
  'use-isnan': 'off',

  /**
   * off because handled by prettier
   *
   * @see https://eslint.org/docs/rules/wrap-regex
   */
  'wrap-regex': 'off',
});

export const createES6TypeScriptRules: RulesCreator = ({
  typescript: { hasTypeScript },
}) => {
  if (!hasTypeScript) {
    return null;
  }

  return {
    /**
     * @see https://eslint.org/docs/rules/
     * @see ts(2335)
     * @see ts(2377)
     */
    'constructor-super': 'off',

    /**
     * disallows trying to overwrite a constant
     *
     * @see https://eslint.org/docs/rules/no-const-assign
     * @see ts(2588)
     */
    'no-const-assign': 'off',
    /**
     * @see https://eslint.org/docs/rules/no-dupe-class-members
     * @see @typescript-eslint/no-dupe-class-members
     * @see ts(2393)
     * @see ts(2300)
     */
    'no-dupe-class-members': 'off',

    /**
     * prevents using `this` before calling `super`
     *
     * @see https://eslint.org/docs/rules/no-this-before-super
     * @see ts(2376)
     */
    'no-this-before-super': 'off',
    /**
     * prevents empty/useless constructors
     *
     * @see https://eslint.org/docs/rules/no-useless-constructor
     * @see @typescript-eslint/no-useless-constructor
     */
    'no-useless-constructor': 'off',
    /**
     * does this really need a comment?
     *
     * ts provides better types with const
     *
     * @see https://eslint.org/docs/rules/prefer-const
     */
    'prefer-const': 'error',
  };
};

/**
 * @see https://eslint.org/docs/rules/#ecmascript-6
 */
export const createES6Rules: RulesCreator = () => ({
  /**
   * off because handled by prettier
   *
   * @see https://eslint.org/docs/rules/arrow-body-style
   */
  'arrow-body-style': 'off',

  /**
   * off because handled by prettier
   *
   * @see https://eslint.org/docs/rules/arrow-parens
   */
  'arrow-parens': 'off',

  /**
   * off because handled by prettier
   *
   * @see https://eslint.org/docs/rules/arrow-spacing
   */
  'arrow-spacing': 'off',

  /**
   * @see https://eslint.org/docs/rules/
   * @see ts(2335)
   * @see ts(2377)
   */
  'constructor-super': 'error',

  /**
   * off because handled by prettier
   *
   * @see https://eslint.org/docs/rules/generator-star-spacing
   */
  'generator-star-spacing': 'off',

  /**
   * disallows reassigning a class
   *
   * @see https://eslint.org/docs/rules/no-class-assign
   */
  'no-class-assign': 'error',

  /**
   * off because handled by prettier
   *
   * @see https://eslint.org/docs/rules/no-confusing-arrow
   */
  'no-confusing-arrow': 'off',

  /**
   * disallows trying to overwrite a constant
   *
   * @see https://eslint.org/docs/rules/no-const-assign
   * @see ts(2588)
   */
  'no-const-assign': 'error',

  /**
   * @see https://eslint.org/docs/rules/no-constant-binary-expression
   */
  'no-constant-binary-expression': 'warn',

  /**
   * @see https://eslint.org/docs/rules/no-dupe-class-members
   * @see @typescript-eslint/no-dupe-class-members
   * @see ts(2393)
   * @see ts(2300)
   */
  'no-dupe-class-members': 'error',

  /**
   * off because handled by import/no-duplicates
   *
   * @see https://eslint.org/docs/rules/no-duplicate-imports
   * @see import/no-duplicates
   */
  'no-duplicate-imports': 'off',

  /**
   * disallow calling `new Symbol()`
   *
   * off because handled by unicorn/new-for-builtins
   *
   * @see https://eslint.org/docs/rules/no-new-symbol
   * @see unicorn/new-for-builtins
   * @see ts(2588)
   */
  'no-new-symbol': 'off',

  /**
   * off because codebase specific
   *
   * @see https://eslint.org/docs/rules/no-restricted-exports
   */
  'no-restricted-exports': 'off',

  /**
   * off because codebase specific
   *
   * @see https://eslint.org/docs/rules/no-restricted-imports
   */
  'no-restricted-imports': 'off',

  /**
   * prevents using `this` before calling `super`
   *
   * @see https://eslint.org/docs/rules/no-this-before-super
   * @see ts(2376)
   */
  'no-this-before-super': 'error',

  /**
   * disallows unnecessary usage of computed property keys
   *
   * @see https://eslint.org/docs/rules/no-useless-computed-key
   */
  'no-useless-computed-key': 'warn',

  /**
   * prevents empty/useless constructors
   *
   * @see https://eslint.org/docs/rules/no-useless-constructor
   * @see @typescript-eslint/no-useless-constructor
   */
  'no-useless-constructor': 'warn',

  /**
   * prevents useless renames in imports/exports/destructuring assignments
   *
   * @see https://eslint.org/docs/rules/no-useless-rename
   */
  'no-useless-rename': 'warn',

  /**
   * prevents usage of `var`
   *
   * @see https://eslint.org/docs/rules/no-var
   * @see ts(2365)
   * @see ts(2360)
   * @see ts(2358)
   */
  'no-var': 'error',

  /**
   * prefer object shorthand
   *
   * @see https://eslint.org/docs/rules/object-shorthand
   */
  'object-shorthand': 'warn',

  // prefer-arrow-callback is in `safePrettierOverrides`

  /**
   * does this really need a comment?
   *
   * ts provides better types with const
   *
   * @see https://eslint.org/docs/rules/prefer-const
   */
  'prefer-const': 'warn',

  /**
   * prefer destructuring :shrug:
   *
   * @see https://eslint.org/docs/rules/prefer-destructuring
   */
  'prefer-destructuring': [
    'warn',
    {
      object: true,
      array: false,
    },
  ],

  /**
   * disallow parseInting certain values
   *
   * @see https://eslint.org/docs/rules/prefer-numeric-literals
   */
  'prefer-numeric-literals': 'warn',

  /**
   * prefer ...rest for variadic functions
   *
   * ts provides better types with rest args over arguments
   *
   * @see https://eslint.org/docs/rules/prefer-rest-params
   */
  'prefer-rest-params': 'error',

  /**
   * prefer spreading where possible
   *
   * ts transpiles spread to apply, so no need for manual apply
   *
   * @see https://eslint.org/docs/rules/prefer-spread
   */
  'prefer-spread': 'error',

  /**
   * prefer template literals over string concat
   *
   * @see https://eslint.org/docs/rules/prefer-template
   */
  'prefer-template': 'warn',

  /**
   * generators must have yield keyword
   *
   * @see https://eslint.org/docs/rules/require-yield
   */
  'require-yield': 'error',

  /**
   * off because handled by prettier
   *
   * @see https://eslint.org/docs/rules/rest-spread-spacing
   */
  'rest-spread-spacing': 'off',

  /**
   * off because handled by import/order
   *
   * @see https://eslint.org/docs/rules/sort-imports
   * @see import/order
   * @see eslint-plugin-simple-import-sort
   */
  'sort-imports': 'off',

  /**
   * requires description on Symbol creation
   *
   * @see https://eslint.org/docs/rules/symbol-description
   */
  'symbol-description': 'error',

  /**
   * off because handled by prettier
   *
   * @see https://eslint.org/docs/rules/template-curly-spacing
   */
  'template-curly-spacing': 'off',

  /**
   * off because handled by prettier
   *
   * @see https://eslint.org/docs/rules/yield-star-spacing
   */
  'yield-star-spacing': 'off',
});

export const safePrettierOverrides: Linter.RulesRecord = {
  /**
   * @see https://eslint.org/docs/rules/curly
   */
  curly,

  /**
   * off because handled by prettier
   *
   * @see https://eslint.org/docs/rules/prefer-arrow-callback
   */
  'prefer-arrow-callback': 'warn',
};

export const eslintDefaultRulesTypeScriptOverride = (
  dependencies: Dependencies,
  enableJavaScriptSpecificRulesInTypeScriptProject = false
): WithOverrideType<OverrideESLintConfig> | null => {
  if (!dependencies.typescript.hasTypeScript) {
    return null;
  }

  const files = [
    ...typeScriptFilesPattern,
    ...(enableJavaScriptSpecificRulesInTypeScriptProject ||
    Boolean(dependencies.typescript.config?.compilerOptions?.checkJs)
      ? []
      : ['**/*.js?(x)']),
  ];

  return {
    files,
    rules: {
      ...createStylisticIssuesTypeScriptRules(dependencies),
      ...createBestPracticesTypescriptRules(dependencies),
      ...createPossibleTypeScriptErrorRules(dependencies),
      ...createVariableTypeScriptRules(dependencies),
      ...createES6TypeScriptRules(dependencies),
    },
  };
};