/**
 * Copyright (c) 2016-present, Facebook, Inc. All rights reserved.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

const {sync: readPkg} = require('read-pkg');
const {getPackages} = require('./scripts/buildUtils');

const internalPackages = getPackages()
  .map(packageDir => {
    const pkg = readPkg({cwd: packageDir});

    return pkg.name;
  })
  .sort();

module.exports = {
  env: {
    'jest/globals': true,
    node: true,
  },
  extends: [
    'plugin:import/errors',
    'plugin:eslint-comments/recommended',
    'plugin:prettier/recommended',
  ],
  globals: {
    BigInt: 'readonly',
  },
  overrides: [
    {
      extends: [
        'plugin:@typescript-eslint/eslint-recommended',
        'plugin:import/typescript',
      ],
      files: ['*.ts', '*.tsx'],
      plugins: ['@typescript-eslint/eslint-plugin', 'local'],
      rules: {
        '@typescript-eslint/array-type': ['error', {default: 'generic'}],
        '@typescript-eslint/ban-types': 'error',
        '@typescript-eslint/no-implicit-any-catch': [
          'error',
          {allowExplicitAny: true},
        ],
        '@typescript-eslint/no-unused-vars': [
          'error',
          {argsIgnorePattern: '^_'},
        ],
        '@typescript-eslint/prefer-ts-expect-error': 'error',
        // TS verifies this
        'consistent-return': 'off',
        // Since we do `export =`. Remove for Jest 27
        'import/default': 'off',
        'no-dupe-class-members': 'off',
        'no-unused-vars': 'off',
      },
    },
    {
      files: [
        'packages/jest-jasmine2/src/jasmine/Env.ts',
        'packages/jest-jasmine2/src/jasmine/ReportDispatcher.ts',
        'packages/jest-jasmine2/src/jasmine/Spec.ts',
        'packages/jest-jasmine2/src/jasmine/SpyStrategy.ts',
        'packages/jest-jasmine2/src/jasmine/Suite.ts',
        'packages/jest-jasmine2/src/jasmine/createSpy.ts',
        'packages/jest-jasmine2/src/jasmine/jasmineLight.ts',
        'packages/jest-mock/src/__tests__/index.test.ts',
        'packages/jest-mock/src/index.ts',
        'packages/pretty-format/src/__tests__/Immutable.test.ts',
        'packages/pretty-format/src/__tests__/prettyFormat.test.ts',
      ],
      rules: {
        'local/prefer-rest-params-eventually': 'warn',
        'prefer-rest-params': 'off',
      },
    },
    {
      files: [
        'packages/expect/src/index.ts',
        'packages/jest-fake-timers/src/legacyFakeTimers.ts',
        'packages/jest-jasmine2/src/jasmine/Env.ts',
        'packages/jest-jasmine2/src/jasmine/ReportDispatcher.ts',
        'packages/jest-jasmine2/src/jasmine/Spec.ts',
        'packages/jest-jasmine2/src/jasmine/Suite.ts',
        'packages/jest-jasmine2/src/jasmine/jasmineLight.ts',
        'packages/jest-jasmine2/src/jestExpect.ts',
        'packages/jest-resolve/src/resolver.ts',
      ],
      rules: {
        'local/prefer-spread-eventually': 'warn',
        'prefer-spread': 'off',
      },
    },
    {
      files: [
        'e2e/babel-plugin-jest-hoist/__tests__/typescript.test.ts',
        'e2e/coverage-remapping/covered.ts',
        'packages/expect/src/matchers.ts',
        'packages/expect/src/print.ts',
        'packages/expect/src/toThrowMatchers.ts',
        'packages/expect/src/types.ts',
        'packages/expect/src/utils.ts',
        'packages/jest-core/src/ReporterDispatcher.ts',
        'packages/jest-core/src/TestScheduler.ts',
        'packages/jest-core/src/collectHandles.ts',
        'packages/jest-core/src/plugins/UpdateSnapshotsInteractive.ts',
        'packages/jest-fake-timers/src/legacyFakeTimers.ts',
        'packages/jest-haste-map/src/index.ts',
        'packages/jest-haste-map/src/watchers/FSEventsWatcher.ts',
        'packages/jest-jasmine2/src/jasmine/SpyStrategy.ts',
        'packages/jest-jasmine2/src/jasmine/Suite.ts',
        'packages/jest-leak-detector/src/index.ts',
        'packages/jest-matcher-utils/src/index.ts',
        'packages/jest-mock/src/__tests__/index.test.ts',
        'packages/jest-mock/src/index.ts',
        'packages/jest-snapshot/src/index.ts',
        'packages/jest-snapshot/src/printSnapshot.ts',
        'packages/jest-snapshot/src/types.ts',
        'packages/jest-util/src/convertDescriptorToString.ts',
        'packages/jest-worker/src/Farm.ts',
        'packages/jest-worker/src/index.ts',
        'packages/pretty-format/src/index.ts',
        'packages/pretty-format/src/plugins/DOMCollection.ts',
      ],
      rules: {
        '@typescript-eslint/ban-types': [
          'error',
          // TODO: remove these overrides: https://github.com/facebook/jest/issues/10177
          {types: {Function: false, object: false, '{}': false}},
        ],
        'local/ban-types-eventually': [
          'warn',
          {
            types: {
              // none of these types are in use, so can be errored on
              Boolean: false,
              Number: false,
              Object: false,
              String: false,
              Symbol: false,
            },
          },
        ],
      },
    },

    // to make it more suitable for running on code examples in docs/ folder
    {
      files: ['*.md'],
      rules: {
        'arrow-body-style': 'off',
        'consistent-return': 'off',
        'import/no-extraneous-dependencies': 'off',
        'import/no-unresolved': 'off',
        'no-console': 'off',
        'no-undef': 'off',
        'no-unused-vars': 'off',
        'prettier/prettier': 'off',
        'sort-keys': 'off',
      },
    },
    {
      files: ['website/**/*'],
      rules: {
        'import/order': 'off',
        'import/sort-keys': 'off',
        'sort-keys': 'off',
      },
    },
    {
      files: ['examples/**/*'],
      rules: {
        'import/no-unresolved': ['error', {ignore: ['^react-native$']}],
        'import/order': 'off',
      },
    },
    {
      files: 'packages/jest-types/**/*',
      rules: {
        'import/no-extraneous-dependencies': 'off',
      },
    },
    {
      files: 'packages/**/*.ts',
      rules: {
        '@typescript-eslint/explicit-module-boundary-types': 'error',
      },
    },
    {
      files: [
        '**/__tests__/**',
        '**/__mocks__/**',
        'packages/jest-jasmine2/src/jasmine/**/*',
        'packages/expect/src/jasmineUtils.ts',
        '**/vendor/**/*',
      ],
      rules: {
        '@typescript-eslint/explicit-module-boundary-types': 'off',
      },
    },
    {
      files: [
        'packages/jest-jasmine2/src/jasmine/**/*',
        'packages/expect/src/jasmineUtils.ts',
        '**/vendor/**/*',
      ],
      rules: {
        'eslint-comments/disable-enable-pair': 'off',
        'eslint-comments/no-unlimited-disable': 'off',
      },
    },
    {
      files: [
        'e2e/error-on-deprecated/__tests__/*',
        'e2e/jasmine-async/__tests__/*',
      ],
      globals: {
        fail: true,
        jasmine: true,
        pending: true,
      },
    },
    {
      files: [
        'website/**',
        '**/__tests__/**',
        'e2e/**',
        '**/pretty-format/perf/**',
      ],
      rules: {
        'import/no-extraneous-dependencies': 'off',
      },
    },
    {
      files: ['**/__typechecks__/**', '*.md'],
      rules: {
        'jest/no-focused-tests': 'off',
        'jest/no-identical-title': 'off',
        'jest/valid-expect': 'off',
      },
    },
    {
      files: [
        'scripts/*',
        'packages/jest-cli/src/init/index.ts',
        'packages/jest-repl/src/cli/runtime-cli.ts',
      ],
      rules: {
        'no-console': 'off',
      },
    },
    {
      files: [
        'e2e/**',
        'examples/**',
        'scripts/*',
        'website/**',
        '**/__mocks__/**',
        '**/__tests__/**',
        '**/__performance_tests__/**',
        'packages/diff-sequences/perf/index.js',
        'packages/pretty-format/perf/test.js',
      ],
      rules: {
        'import/no-unresolved': 'off',
        'no-console': 'off',
        'no-unused-vars': 'off',
      },
    },
  ],
  parser: '@typescript-eslint/parser',
  parserOptions: {
    sourceType: 'module',
  },
  plugins: ['markdown', 'import', 'jest'],
  rules: {
    'accessor-pairs': ['warn', {setWithoutGet: true}],
    'block-scoped-var': 'off',
    'callback-return': 'off',
    camelcase: ['off', {properties: 'always'}],
    complexity: 'off',
    'consistent-return': 'warn',
    'consistent-this': ['off', 'self'],
    'constructor-super': 'error',
    'default-case': 'off',
    'dot-notation': 'off',
    eqeqeq: ['off', 'allow-null'],
    'eslint-comments/disable-enable-pair': ['error', {allowWholeFile: true}],
    'eslint-comments/no-unused-disable': 'error',
    'func-names': 'off',
    'func-style': ['off', 'declaration'],
    'global-require': 'off',
    'guard-for-in': 'off',
    'handle-callback-err': 'off',
    'id-length': 'off',
    'id-match': 'off',
    'import/no-duplicates': 'error',
    'import/no-extraneous-dependencies': [
      'error',
      {
        devDependencies: [
          '**/__mocks__/**',
          '**/__tests__/**',
          '**/__typechecks__/**',
          '**/?(*.)(spec|test).js?(x)',
          'scripts/**',
          'babel.config.js',
          'testSetupFile.js',
          '.eslintrc.js',
        ],
      },
    ],
    'import/no-unresolved': ['error', {ignore: ['fsevents']}],
    'import/order': [
      'error',
      {
        alphabetize: {
          order: 'asc',
        },
        // this is the default order except for added `internal` in the middle
        groups: [
          'builtin',
          'external',
          'internal',
          'parent',
          'sibling',
          'index',
        ],
        'newlines-between': 'never',
      },
    ],
    'init-declarations': 'off',
    'jest/no-focused-tests': 'error',
    'jest/no-identical-title': 'error',
    'jest/valid-expect': 'error',
    'lines-around-comment': 'off',
    'max-depth': 'off',
    'max-nested-callbacks': 'off',
    'max-params': 'off',
    'max-statements': 'off',
    'new-cap': 'off',
    'new-parens': 'error',
    'newline-after-var': 'off',
    'no-alert': 'off',
    'no-array-constructor': 'error',
    'no-bitwise': 'warn',
    'no-caller': 'error',
    'no-case-declarations': 'off',
    'no-catch-shadow': 'error',
    'no-class-assign': 'warn',
    'no-cond-assign': 'off',
    'no-confusing-arrow': 'off',
    'no-console': [
      'warn',
      {allow: ['warn', 'error', 'time', 'timeEnd', 'timeStamp']},
    ],
    'no-const-assign': 'error',
    'no-constant-condition': 'off',
    'no-continue': 'off',
    'no-control-regex': 'off',
    'no-debugger': 'error',
    'no-delete-var': 'error',
    'no-div-regex': 'off',
    'no-dupe-args': 'error',
    'no-dupe-class-members': 'error',
    'no-dupe-keys': 'error',
    'no-duplicate-case': 'warn',
    'no-else-return': 'off',
    'no-empty': 'off',
    'no-empty-character-class': 'warn',
    'no-empty-pattern': 'warn',
    'no-eq-null': 'off',
    'no-eval': 'error',
    'no-ex-assign': 'warn',
    'no-extend-native': 'warn',
    'no-extra-bind': 'warn',
    'no-extra-boolean-cast': 'warn',
    'no-fallthrough': 'warn',
    'no-floating-decimal': 'error',
    'no-func-assign': 'error',
    'no-implicit-coercion': 'off',
    'no-implied-eval': 'error',
    'no-inline-comments': 'off',
    'no-inner-declarations': 'off',
    'no-invalid-regexp': 'warn',
    'no-invalid-this': 'off',
    'no-irregular-whitespace': 'error',
    'no-iterator': 'off',
    'no-label-var': 'warn',
    'no-labels': ['error', {allowLoop: true, allowSwitch: true}],
    'no-lonely-if': 'off',
    'no-loop-func': 'off',
    'no-magic-numbers': 'off',
    'no-mixed-requires': 'off',
    'no-mixed-spaces-and-tabs': 'error',
    'no-multi-str': 'error',
    'no-multiple-empty-lines': 'off',
    'no-native-reassign': ['error', {exceptions: ['Map', 'Set']}],
    'no-negated-condition': 'off',
    'no-negated-in-lhs': 'error',
    'no-nested-ternary': 'off',
    'no-new': 'warn',
    'no-new-func': 'error',
    'no-new-object': 'warn',
    'no-new-require': 'off',
    'no-new-wrappers': 'warn',
    'no-obj-calls': 'error',
    'no-octal': 'warn',
    'no-octal-escape': 'warn',
    'no-param-reassign': 'off',
    'no-path-concat': 'off',
    'no-plusplus': 'off',
    'no-process-env': 'off',
    'no-process-exit': 'off',
    'no-proto': 'error',
    'no-redeclare': 'warn',
    'no-regex-spaces': 'warn',
    'no-restricted-imports': [
      'error',
      {message: 'Please use graceful-fs instead.', name: 'fs'},
    ],
    'no-restricted-modules': 'off',
    'no-restricted-syntax': 'off',
    'no-return-assign': 'off',
    'no-script-url': 'error',
    'no-self-compare': 'warn',
    'no-sequences': 'warn',
    'no-shadow': 'off',
    'no-shadow-restricted-names': 'warn',
    'no-sparse-arrays': 'error',
    'no-sync': 'off',
    'no-ternary': 'off',
    'no-this-before-super': 'error',
    'no-throw-literal': 'error',
    'no-undef': 'error',
    'no-undef-init': 'off',
    'no-undefined': 'off',
    'no-underscore-dangle': 'off',
    'no-unneeded-ternary': 'warn',
    'no-unreachable': 'error',
    'no-unused-expressions': 'off',
    'no-unused-vars': ['error', {argsIgnorePattern: '^_'}],
    'no-use-before-define': 'off',
    'no-useless-call': 'warn',
    'no-useless-computed-key': 'error',
    'no-useless-concat': 'warn',
    'no-var': 'error',
    'no-void': 'off',
    'no-warn-comments': 'off',
    'no-with': 'off',
    'object-shorthand': 'error',
    'one-var': ['warn', {initialized: 'never'}],
    'operator-assignment': ['warn', 'always'],
    'operator-linebreak': 'off',
    'padded-blocks': 'off',
    'prefer-arrow-callback': ['error', {allowNamedFunctions: true}],
    'prefer-const': 'error',
    'prefer-template': 'off',
    quotes: [
      'error',
      'single',
      {allowTemplateLiterals: true, avoidEscape: true},
    ],
    radix: 'warn',
    'require-jsdoc': 'off',
    'require-yield': 'off',
    'sort-imports': ['error', {ignoreDeclarationSort: true}],
    'sort-keys': 'error',
    'sort-vars': 'off',
    'spaced-comment': ['off', 'always', {exceptions: ['eslint', 'global']}],
    strict: 'off',
    'use-isnan': 'error',
    'valid-jsdoc': 'off',
    'valid-typeof': 'error',
    'vars-on-top': 'off',
    'wrap-iife': 'off',
    'wrap-regex': 'off',
    yoda: 'off',
  },
  settings: {
    'import/ignore': ['react-native'],
    // using `new RegExp` makes sure to escape `/`
    'import/internal-regex': new RegExp(
      internalPackages.map(pkg => `^${pkg}$`).join('|'),
    ).source,
    'import/resolver': {
      typescript: {},
    },
  },
};
