ljosberinn/eslint-config-galex

View on GitHub
src/plugins/unicorn.ts

Summary

Maintainability
A
0 mins
Test Coverage
A
100%
import { ModuleKind } from 'typescript';

import {
  type Dependencies,
  type RulesCreator,
  type RulesetCreator,
} from '../types';
import { prettierUnicornRules } from '../utils/prettier';

export const createUnicornPlugin: RulesetCreator = ({
  rules: customRules = {},
  ...dependencies
}) => ({
  ...createUnicornRules(dependencies),
  ...prettierUnicornRules,
  ...customRules,
});

/**
 * @see https://github.com/sindresorhus/eslint-plugin-unicorn
 */
export const createUnicornRules: RulesCreator = ({
  typescript: { hasTypeScript, config },
  react: { hasReact },
  hasNodeTypes,
}) => ({
  /**
   * improves regex
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/better-regex.md
   */
  'unicorn/better-regex': 'error',

  /**
   * disallows (entirely) renaming the error in Promise.catch & try/catch
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/catch-error-name.md
   */
  'unicorn/catch-error-name': 'error',

  /**
   * enforces usage of destructured variables over re-accessing them
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/consistent-destructuring.md
   */
  'unicorn/consistent-destructuring': 'error',

  /**
   * enforces placing functions as close to the top level as possible
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/consistent-function-scoping.md
   */
  'unicorn/consistent-function-scoping': 'error',

  /**
   * enforce correct error subclassing when extending native errors
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/custom-error-definition.md
   */
  'unicorn/custom-error-definition': 'warn',

  /**
   * off because prettier territory
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/empty-brace-spaces.md
   */
  'unicorn/empty-brace-spaces': 'off',

  /**
   * enforce passing a message value when throwing an built-in error
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/error-message.md
   */
  'unicorn/error-message': 'error',

  /**
   * require escape sequences to use uppercase values
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/escape-case.md
   */
  'unicorn/escape-case': 'error',

  /**
   * off because currently no use-case
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/expiring-todo-comments.md
   */
  'unicorn/expiring-todo-comments': 'off',

  /**
   * enforces `Array.length === 0` instead of `Array.length`
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/explicit-length-check.md
   */
  'unicorn/explicit-length-check': 'error',

  /**
   * off because too opinionated
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/filename-case.md
   */
  'unicorn/filename-case': 'off',

  /**
   * off because situational
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/import-style.md
   */
  'unicorn/import-style': 'off',

  /**
   * enforces `new` for builtins
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/new-for-builtins.md
   */
  'unicorn/new-for-builtins': 'error',

  /**
   * disallows disabling eslint entirely
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/no-abusive-eslint-disable.md
   */
  'unicorn/no-abusive-eslint-disable': 'error',

  /**
   * off because TS tells you where this is possible and where not
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/no-array-callback-reference.md
   */
  'unicorn/no-array-callback-reference': 'off',

  /**
   * off because nonsense
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/no-array-for-each.md
   */
  'unicorn/no-array-for-each': 'off',

  /**
   * Disallow using the this argument in array methods
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/no-array-method-this-argument.md
   */
  'unicorn/no-array-method-this-argument': 'warn',

  /**
   * combines multiple array.push calls into one where possible
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/no-array-push-push.md
   */
  'unicorn/no-array-push-push': 'warn',

  /**
   * off because bad take
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/no-array-reduce.md
   */
  'unicorn/no-array-reduce': 'off',

  /**
   * disallows directly accessing awaited promises
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/no-await-expression-member.md
   */
  'unicorn/no-await-expression-member': 'error',

  /**
   * disallow leading/trailing space between console.log parameters
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/no-console-spaces.md
   */
  'unicorn/no-console-spaces': 'error',

  /**
   * disallow direct manipulation of document.cookie
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/no-document-cookie.md
   */
  'unicorn/no-document-cookie': 'warn',

  /**
   * disallows empty files. off because fairly redundant.
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/no-empty-file.md
   */
  'unicorn/no-empty-file': 'off',

  /**
   * no `for` loop when you can `for-of` instead
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/no-for-loop.md
   */
  'unicorn/no-for-loop': hasTypeScript ? 'off' : 'warn',

  /**
   * use unicode escapes instead of hexadecimal escales
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/no-hex-escape.md
   */
  'unicorn/no-hex-escape': 'error',

  /**
   * use `Array.isArray` instead of `instanceof Array`
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/no-array-instanceof.md
   */
  'unicorn/no-instanceof-array': 'error',

  /**
   * prevents calling `removeEventListener` with the result of an expression
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/no-invalid-remove-event-listener.md
   */
  'unicorn/no-invalid-remove-event-listener': 'error',

  /**
   * ensures not using keywords as variable prefix
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/no-keyword-prefix.md
   */
  'unicorn/no-keyword-prefix': hasReact ? 'off' : 'warn',

  /**
   * collapses nested ifs
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/no-lonely-if.md
   * @see https://eslint.org/docs/rules/no-lonely-if
   */
  'unicorn/no-lonely-if': 'warn',

  /**
   * disallows negated conditions
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/no-negated-condition.md
   * @see https://eslint.org/docs/rules/no-negated-condition
   */
  'unicorn/no-negated-condition': 'warn',

  /**
   * off because prettier takes care of it
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/no-nested-ternary.md
   */
  'unicorn/no-nested-ternary': 'off',

  /**
   * disallows `new Array()`
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/no-new-array.md
   * @see no-array-constructor
   */
  'unicorn/no-new-array': 'error',

  /**
   * use Buffer.from/Buffer.alloc instead of new Buffer (deprecated)
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/no-new-buffer.md
   */
  'unicorn/no-new-buffer': 'error',

  /**
   * off because jesus no, bad take. null conveys meaning! hard to debug
   * unintentional undefined from intentional undefined. null declares
   * active absence.
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/no-null.md
   */
  'unicorn/no-null': 'off',

  /**
   * disallows using objects as default parameter
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/no-object-as-default-parameter.md
   */
  'unicorn/no-object-as-default-parameter': 'warn',

  /**
   * makes core rule `no-process-exit` more strict
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/no-process-exit.md
   */
  'unicorn/no-process-exit': 'error',

  /**
   * forbid static-only classes, should be an object instead
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/no-static-only-class.md
   */
  'unicorn/no-static-only-class': 'error',

  /**
   * disallows custom objects with .then property
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/no-thenable.md
   */
  'unicorn/no-thenable': 'warn',

  /**
   * disallows reassigning `this` to another variable
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/no-this-assignment.md
   */
  'unicorn/no-this-assignment': 'warn',

  /**
   * disallows comparing undefined using typeof
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/no-typeof-undefined.md
   */
  'unicorn/no-typeof-undefined': 'warn',

  /**
   * disallows awaiting non-awaitables
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/no-unnecessary-await.md
   */
  'unicorn/no-unnecessary-await': hasTypeScript ? 'off' : 'warn',

  /**
   * prevents abusive destructuring
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/no-unreadable-array-destructuring.md
   */
  'unicorn/no-unreadable-array-destructuring': 'warn',

  /**
   * disallows iifes with implicit returns due to readability
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/no-unreadable-iife.md
   */
  'unicorn/no-unreadable-iife': 'warn',

  /**
   * disallows potentially very slow regex
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/no-unsafe-regex.md
   */
  'unicorn/no-unsafe-regex': 'error',

  /**
   * does exactly what the name says
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/no-useless-length-check.md
   */
  'unicorn/no-useless-length-check': 'error',

  /**
   * disallows unused properties on object constants
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/no-unused-properties.md
   */
  'unicorn/no-unused-properties': 'warn',

  /**
   * prevents unnecessary spreading
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/no-useless-fallback-in-spread.md
   */
  'unicorn/no-useless-fallback-in-spread': 'warn',

  /**
   * disallow returning/yielding Promise.resolve/reject() in async functions or promise callbacks
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/no-useless-spread.md
   */
  'unicorn/no-useless-promise-resolve-reject': 'warn',

  /**
   * disallows useless spreads
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/no-useless-spread.md
   */
  'unicorn/no-useless-spread': 'error',

  /**
   * disallows redundant switch
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/no-useless-switch-case.md
   */
  'unicorn/no-useless-switch-case': 'warn',

  /**
   * disallows useless undefined
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/no-useless-undefined.md
   */
  'unicorn/no-useless-undefined': 'error',

  /**
   * enforces no usage of 1.0
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/no-zero-fractions.md
   */
  'unicorn/no-zero-fractions': 'error',

  /**
   * off because prettier takes care of it
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/number-literal-case.md
   */
  'unicorn/number-literal-case': 'off',

  /**
   * enforces consistent numeric separators style
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/numeric-separators-style.md
   */
  'unicorn/numeric-separators-style': 'warn',

  /**
   * prefer element.addEventListener instead of element.[event]
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/prefer-add-event-listener.md
   */
  'unicorn/prefer-add-event-listener': 'error',

  /**
   * prefer Array.find over alternatives doing the same with more code
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/prefer-array-find.md
   */
  'unicorn/prefer-array-find': 'error',

  /**
   * prefer using Array.flat over older alternatives
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/prefer-array-flat.md
   */
  'unicorn/prefer-array-flat': 'warn',

  /**
   * prefer Array.flatMap over Array.flat().map() and similar
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/prefer-array-flat-map.md
   */
  'unicorn/prefer-array-flat-map': 'error',

  /**
   * use `.indexOf` where appropriate
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/prefer-array-index-of.md
   */
  'unicorn/prefer-array-index-of': 'warn',

  /**
   * prefer array.some when more appropriate than array.find
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/prefer-array-some.md
   */
  'unicorn/prefer-array-some': 'warn',

  /**
   * Prefer .at() method for index access and String#charAt()
   *
   * off because currently barely supported
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/prefer-at.md
   */
  'unicorn/prefer-at': 'off',

  /**
   * prefer using unicode-save string method
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/prefer-code-point.md
   */
  'unicorn/prefer-code-point': 'warn',

  /**
   * prefer using `Date.now()` to get unix ms over other options
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/prefer-date-now.md
   */
  'unicorn/prefer-date-now': 'warn',

  /**
   * prefer using function default parameters in contrast to reassigning
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/prefer-default-parameters.md
   */
  'unicorn/prefer-default-parameters': 'warn',

  /**
   * prefer using modern API
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/prefer-dom-node-append.md
   */
  'unicorn/prefer-dom-node-append': 'error',

  /**
   * prefer element.dataset.foo over element.setAttribute('dataset-foo')
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/prefer-dom-node-dataset.md
   */
  'unicorn/prefer-dom-node-dataset': 'error',

  /**
   * prefer using modern API
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/prefer-dom-node-remove.md
   */
  'unicorn/prefer-dom-node-remove': 'error',

  /**
   * use element.textContent over element.innerText
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/prefer-dom-node-text-content.md
   */
  'unicorn/prefer-dom-node-text-content': 'error',

  /**
   * prefer (Array|String).includes over (Array|String).indexOf
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/prefer-includes.md
   * @see @typescript-eslint/prefer-includes
   */
  'unicorn/prefer-includes': hasTypeScript ? 'off' : 'warn',

  /**
   * prefer reading a JSON file as a buffer
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/prefer-json-parse-buffer.md
   */
  'unicorn/prefer-json-parse-buffer': 'warn',

  ...(hasNodeTypes
    ? {
        /**
         * prefer EventTarget over EventEmitter
         *
         * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/prefer-event-target.md
         */
        'unicorn/prefer-event-target': 'warn',
      }
    : null),

  /**
   * prefer export...from when re-exporting instead of separately importing,
   * then exporting
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/prefer-export-from.md#prefer-exportfrom-when-re-exporting
   */
  'unicorn/prefer-export-from': 'warn',

  /**
   * prefer event.key over event.keyCode
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/prefer-keyboard-event-key.md
   */
  'unicorn/prefer-keyboard-event-key': 'error',

  /**
   * prohibit ternary if simpler version exists
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/prefer-logical-operator-over-ternary.md
   */
  'unicorn/prefer-logical-operator-over-ternary': 'warn',

  /**
   * enforces usage of `Math.trunc()` over bitwise
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/prefer-math-trunc.md
   */
  'unicorn/prefer-math-trunc': 'warn',

  /**
   * prefer using modern APIs
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/prefer-modern-dom-apis.md
   */
  'unicorn/prefer-modern-dom-apis': 'error',

  /**
   * prefer modern Math.* APIs over manual calculation
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/prefer-modern-math-apis.md
   */
  'unicorn/prefer-modern-math-apis': 'warn',

  /**
   * off because we cannot reliably detect being in a pure Node.js context
   * with TS it would be off anyways
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/prefer-module.md
   * @see import/no-commonjs
   */
  'unicorn/prefer-module': 'off',

  /**
   * disallows recreating String, Number, Boolean, etc.
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/prefer-native-coercion-functions.md
   */
  'unicorn/prefer-native-coercion-functions': 'error',

  /**
   * prefer negative index over .length - index
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/prefer-negative-index.md
   */
  'unicorn/prefer-negative-index': 'error',

  /**
   * off because we cannot reliably detect being in a pure Node.js context
   * which also supports Node 16+
   *
   * @todo check specifically in Next.js context; in 04/2021 it's not supported
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/prefer-node-protocol.md
   */
  'unicorn/prefer-node-protocol': 'off',

  /**
   * use Number.* instead of * directly because of implicit differences
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/prefer-number-properties.md
   */
  'unicorn/prefer-number-properties': ['warn', { checkInfinity: false }],

  /**
   * suggests using Object.fromEntries where detected
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/prefer-object-from-entries.md
   */
  'unicorn/prefer-object-from-entries': 'error',

  /**
   * handle error in try/catch or omit it
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/prefer-optional-catch-binding.md
   */
  'unicorn/prefer-optional-catch-binding': 'error',

  /**
   * prefer element.querySelector over element.getElementById etc.
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/prefer-query-selector.md
   */
  'unicorn/prefer-query-selector': 'error',

  /**
   * prefer borrowing methods from prototype instead of an instance
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/prefer-prototype-methods.md
   */
  'unicorn/prefer-prototype-methods': 'warn',

  /**
   * use Reflect.apply(fn) over Function.prototype.apply.call(fn)
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/prefer-reflect-apply.md
   */
  'unicorn/prefer-reflect-apply': 'error',

  /**
   * prefer the more performant, semantically clear method
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/prefer-regexp-test.md
   */
  'unicorn/prefer-regexp-test': 'error',

  /**
   * prefer Set.has over Array.includes
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/prefer-set-has.md
   */
  'unicorn/prefer-set-has': 'error',

  /**
   * avoids unnecessarily spreading a set into array to check length when .size can be used instead
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/prefer-set-size.md
   */
  'unicorn/prefer-set-size': 'warn',

  /**
   * prefer [...arr] over Array.from
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/prefer-spread.md
   */
  'unicorn/prefer-spread': 'error',

  /**
   * prefer String.replaceAll over global regex
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/prefer-string-replace-all.md
   */
  'unicorn/prefer-string-replace-all':
    // browsers support .replaceAll
    hasReact ||
    // in TS projects, availability can be inferred based on tsConfig.lib containing anything ESNext related
    (config?.compilerOptions?.lib &&
      Array.isArray(config.compilerOptions.lib) &&
      config.compilerOptions.lib.some(lib =>
        lib.toLowerCase().startsWith('esnext')
      ))
      ? 'error'
      : // Node only supports it v15+
        'off',

  /**
   * use String.slice over String.substr/.substring
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/prefer-string-slice.md
   */
  'unicorn/prefer-string-slice': 'error',

  /**
   * use String.startsWith/.endsWith over String.indexOf or regex
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/prefer-string-starts-ends-with.md
   * @see @typescript-eslint/prefer-string-starts-ends-with
   */
  'unicorn/prefer-string-starts-ends-with': hasTypeScript ? 'off' : 'error',

  /**
   * use element.trimStart/element.trimEnd over
   * elelment.trimLeft/elelment.trimRight
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/prefer-string-trim-start-end.md
   */
  'unicorn/prefer-string-trim-start-end': 'error',

  /**
   * prefer switch over multiple if-elseif
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/prefer-switch.md
   */
  'unicorn/prefer-switch': [
    'error',
    { emptyDefaultCase: 'no-default-comment' },
  ],

  /**
   * off as long as promises & generators are not excludable
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/prefer-ternary.md
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/issues/867
   */
  'unicorn/prefer-ternary': 'off',

  /**
   * Prefer top-level await over top-level promises and async function calls
   *
   * disabled unless it can be safely detected
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/prefer-top-level-await.md
   */
  'unicorn/prefer-top-level-await': detectPreferTopLevelAwait({
    hasTypeScript,
    config,
  }),

  /**
   * be more explicit about the type of error you throw
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/prefer-type-error.md
   */
  'unicorn/prefer-type-error': 'error',

  /**
   * off because too opinionated
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/prevent-abbreviations.md
   */
  'unicorn/prevent-abbreviations': 'off',

  /**
   * disallows use of relative paths passed to `new URL()`
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/relative-url-style.md
   */
  'unicorn/relative-url-style': 'warn',

  /**
   * enforces using a separator argument when using Array.join()
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/require-array-join-separator.md
   */
  'unicorn/require-array-join-separator': 'warn',

  /**
   * enforces using an argument when using Number.toFixed()
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/require-number-to-fixed-digits-argument.md
   */
  'unicorn/require-number-to-fixed-digits-argument': 'warn',

  /**
   * Enforce using the targetOrigin argument with `window.postMessage()`
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/require-post-message-target-origin.md
   */
  'unicorn/require-post-message-target-origin': 'error',

  /**
   * off because no need
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/string-content.md
   */
  'unicorn/string-content': 'off',

  /**
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/switch-case-braces.md
   */
  'unicorn/switch-case-braces': 'warn',

  /**
   * enforces braces in switch cases
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/template-indent.md
   */
  'unicorn/template-indent': 'warn',

  /**
   * enforces consistent case for text encoding identifiers
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/text-encoding-identifier-case.md
   */
  'unicorn/text-encoding-identifier-case': hasTypeScript ? 'off' : 'warn',

  /**
   * be explicit about thrown error
   *
   * @see https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/throw-new-error.md
   */
  'unicorn/throw-new-error': 'error',
});

const detectPreferTopLevelAwait = ({
  hasTypeScript,
  config,
}: Omit<Dependencies['typescript'], 'version'>) => {
  if (
    !hasTypeScript ||
    !config ||
    !config.compilerOptions ||
    !config.compilerOptions.module ||
    !config.compilerOptions.target
  ) {
    return 'off';
  }

  if (
    !(typeof config.compilerOptions.module === 'string'
      ? ['esnext', 'system'].includes(
          // @ts-expect-error it can be both since its either raw json or parsed
          config.compilerOptions.module.toLowerCase()
        )
      : [ModuleKind.System, ModuleKind.ESNext].includes(
          config.compilerOptions.module
        ))
  ) {
    return 'off';
  }

  if (
    (typeof config.compilerOptions.target === 'string' &&
      // @ts-expect-error it can be both since its either raw json or parsed
      Number.parseInt(config.compilerOptions.target.slice(2)) < 2017) ||
    config.compilerOptions.target < 4
  ) {
    return 'off';
  }

  return 'error';
};