chromium/ash/webui/recorder_app_ui/resources/.eslintrc.js

// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// This file is also checked by ESLint, and some of the property in the
// settings doesn't follow naming convention.
/* eslint-disable @typescript-eslint/naming-convention */

// TODO(pihsun): Convert this file to ES module when we have newer version of
// ESLint that supports it.

// From
// https://github.com/google/eslint-config-google/blob/76b90900b143de061b0020637e1e24251f6199e5/index.js#L21
const googleRules = {
  // The rules below are listed in the order they appear on the eslint
  // rules page. All rules are listed to make it easier to keep in sync
  // as new ESLint rules are added.
  // http://eslint.org/docs/rules/
  // - Rules in the `eslint:recommended` ruleset that aren't specifically
  //   mentioned by the google styleguide are listed but commented out (so
  //   they don't override a base ruleset).
  // - Rules that are recommended but contradict the Google styleguide
  //   are explicitly set to the Google styleguide value.

  // Possible Errors
  // http://eslint.org/docs/rules/#possible-errors
  // ---------------------------------------------
  // 'for-direction': 'off',
  // 'no-await-in-loop': 'off',
  // 'no-compare-neg-zero': 'error', // eslint:recommended
  'no-cond-assign': 'off',  // eslint:recommended
  // 'no-console': 'error', // eslint:recommended
  // 'no-constant-condition': 'error', // eslint:recommended
  // 'no-control-regex': 'error', // eslint:recommended
  // 'no-debugger': 'error', // eslint:recommended
  // 'no-dupe-args': 'error', // eslint:recommended
  // 'no-dupe-keys': 'error', // eslint:recommended
  // 'no-duplicate-case': 'error', // eslint:recommended
  // 'no-empty': 'error', // eslint:recommended
  // 'no-empty-character-class': 'error', // eslint:recommended
  // 'no-ex-assign': 'error', // eslint:recommended
  // 'no-extra-boolean-cast': 'error', // eslint:recommended
  // 'no-extra-parens': 'off',
  // 'no-extra-semi': 'error', // eslint:recommended
  // 'no-func-assign': 'error', // eslint:recommended
  // 'no-inner-declarations': 'error', // eslint:recommended
  // 'no-invalid-regexp': 'error', // eslint:recommended
  'no-irregular-whitespace': 'error',  // eslint:recommended
  // 'no-obj-calls': 'error', // eslint:recommended
  // 'no-prototype-builtins': 'off',
  // 'no-regex-spaces': 'error', // eslint:recommended
  // 'no-sparse-arrays': 'error', // eslint:recommended
  // 'no-template-curly-in-string': 'off',
  'no-unexpected-multiline': 'error',  // eslint:recommended
  // 'no-unreachable': 'error', // eslint:recommended
  // 'no-unsafe-finally': 'error', // eslint:recommended
  // 'no-unsafe-negation': 'off',
  // 'use-isnan': 'error' // eslint:recommended
  'valid-jsdoc': [
    'error',
    {
      requireParamDescription: false,
      requireReturnDescription: false,
      requireReturn: false,
      prefer: {returns: 'return'},
    },
  ],
  // 'valid-typeof': 'error' // eslint:recommended

  // Best Practices
  // http://eslint.org/docs/rules/#best-practices
  // --------------------------------------------

  // 'accessor-pairs': 'off',
  // 'array-callback-return': 'off',
  // 'block-scoped-var': 'off',
  // 'class-methods-use-this': 'off',
  // 'complexity': 'off',
  // 'consistent-return': 'off'
  // TODO(philipwalton): add an option to enforce braces with the
  // exception of simple, single-line if statements.
  'curly': ['error', 'multi-line'],
  // 'default-case': 'off',
  // 'dot-location': 'off',
  // 'dot-notation': 'off',
  // 'eqeqeq': 'off',
  'guard-for-in': 'error',
  // 'no-alert': 'off',
  'no-caller': 'error',
  // 'no-case-declarations': 'error', // eslint:recommended
  // 'no-div-regex': 'off',
  // 'no-else-return': 'off',
  // 'no-empty-function': 'off',
  // 'no-empty-pattern': 'error', // eslint:recommended
  // 'no-eq-null': 'off',
  // 'no-eval': 'off',
  'no-extend-native': 'error',
  'no-extra-bind': 'error',
  // 'no-extra-label': 'off',
  // 'no-fallthrough': 'error', // eslint:recommended
  // 'no-floating-decimal': 'off',
  // 'no-global-assign': 'off',
  // 'no-implicit-coercion': 'off',
  // 'no-implicit-globals': 'off',
  // 'no-implied-eval': 'off',
  'no-invalid-this': 'error',
  // 'no-iterator': 'off',
  // 'no-labels': 'off',
  // 'no-lone-blocks': 'off',
  // 'no-loop-func': 'off',
  // 'no-magic-numbers': 'off',
  'no-multi-spaces': 'error',
  'no-multi-str': 'error',
  // 'no-new': 'off',
  // 'no-new-func': 'off',
  'no-new-wrappers': 'error',
  // 'no-octal': 'error', // eslint:recommended
  // 'no-octal-escape': 'off',
  // 'no-param-reassign': 'off',
  // 'no-proto': 'off',
  // 'no-redeclare': 'error', // eslint:recommended
  // 'no-restricted-properties': 'off',
  // 'no-return-assign': 'off',
  // 'no-script-url': 'off',
  // 'no-self-assign': 'error', // eslint:recommended
  // 'no-self-compare': 'off',
  // 'no-sequences': 'off',
  'no-throw-literal': 'error',  // eslint:recommended
  // 'no-unmodified-loop-condition': 'off',
  // 'no-unused-expressions': 'off',
  // 'no-unused-labels': 'error', // eslint:recommended
  // 'no-useless-call': 'off',
  // 'no-useless-concat': 'off',
  // 'no-useless-escape': 'off',
  // 'no-void': 'off',
  // 'no-warning-comments': 'off',
  'no-with': 'error',
  'prefer-promise-reject-errors': 'error',
  // 'radix': 'off',
  // 'require-await': 'off',
  // 'vars-on-top': 'off',
  // 'wrap-iife': 'off',
  // 'yoda': 'off',

  // Strict Mode
  // http://eslint.org/docs/rules/#strict-mode
  // -----------------------------------------
  // 'strict': 'off',

  // Variables
  // http://eslint.org/docs/rules/#variables
  // ---------------------------------------
  // 'init-declarations': 'off',
  // 'no-catch-shadow': 'off',
  // 'no-delete-var': 'error', // eslint:recommended
  // 'no-label-var': 'off',
  // 'no-restricted-globals': 'off',
  // 'no-shadow': 'off',
  // 'no-shadow-restricted-names': 'off',
  // 'no-undef': 'error', // eslint:recommended
  // 'no-undef-init': 'off',
  // 'no-undefined': 'off',
  'no-unused-vars': ['error', {args: 'none'}],  // eslint:recommended
  // 'no-use-before-define': 'off',

  // Node.js and CommonJS
  // http://eslint.org/docs/rules/#nodejs-and-commonjs
  // -------------------------------------------------
  // 'callback-return': 'off',
  // 'global-require': 'off',
  // 'handle-callback-err': 'off',
  // 'no-buffer-constructor': 'off',
  // 'no-mixed-requires': 'off',
  // 'no-new-require': 'off',
  // 'no-path-concat': 'off',
  // 'no-process-env': 'off',
  // 'no-process-exit': 'off',
  // 'no-restricted-modules': 'off',
  // 'no-sync': 'off',

  // Stylistic Issues
  // http://eslint.org/docs/rules/#stylistic-issues
  // ----------------------------------------------
  'array-bracket-newline': 'off',  // eslint:recommended
  'array-bracket-spacing': ['error', 'never'],
  'array-element-newline': 'off',  // eslint:recommended
  'block-spacing': ['error', 'never'],
  'brace-style': 'error',
  'camelcase': ['error', {properties: 'never'}],
  // 'capitalized-comments': 'off',
  'comma-dangle': ['error', 'always-multiline'],
  'comma-spacing': 'error',
  'comma-style': 'error',
  'computed-property-spacing': 'error',
  // 'consistent-this': 'off',
  'eol-last': 'error',
  'func-call-spacing': 'error',
  // 'func-name-matching': 'off',
  // 'func-names': 'off',
  // 'func-style': 'off',
  // 'id-denylist': 'off',
  // 'id-length': 'off',
  // 'id-match': 'off',
  'indent': [
    'error',
    2,
    {
      CallExpression: {
        arguments: 2,
      },
      FunctionDeclaration: {
        body: 1,
        parameters: 2,
      },
      FunctionExpression: {
        body: 1,
        parameters: 2,
      },
      MemberExpression: 2,
      ObjectExpression: 1,
      SwitchCase: 1,
      ignoredNodes: ['ConditionalExpression'],
    },
  ],
  // 'jsx-quotes': 'off',
  'key-spacing': 'error',
  'keyword-spacing': 'error',
  // 'line-comment-position': 'off',
  'linebreak-style': 'error',
  // 'lines-around-comment': 'off',
  // 'max-depth': 'off',
  'max-len': [
    'error',
    {
      code: 80,
      tabWidth: 2,
      ignoreUrls: true,
      ignorePattern: 'goog.(module|require)',
    },
  ],
  // 'max-lines': 'off',
  // 'max-nested-callbacks': 'off',
  // 'max-params': 'off',
  // 'max-statements': 'off',
  // 'max-statements-per-line': 'off',
  // TODO(philipwalton): add a rule to enforce the operator appearing
  // at the end of the line.
  // 'multiline-ternary': 'off',
  'new-cap': 'error',
  // 'new-parens': 'off',
  // 'newline-per-chained-call': 'off',
  'no-array-constructor': 'error',
  // 'no-bitwise': 'off',
  // 'no-continue': 'off',
  // 'no-inline-comments': 'off',
  // 'no-lonely-if': 'off',
  // 'no-mixed-operators': 'off',
  'no-mixed-spaces-and-tabs': 'error',  // eslint:recommended
  // 'no-multi-assign': 'off',
  'no-multiple-empty-lines': ['error', {max: 2}],
  // 'no-negated-condition': 'off',
  // 'no-nested-ternary': 'off',
  'no-new-object': 'error',
  // 'no-plusplus': 'off',
  // 'no-restricted-syntax': 'off',
  'no-tabs': 'error',
  // 'no-ternary': 'off',
  'no-trailing-spaces': 'error',
  // 'no-underscore-dangle': 'off',
  // 'no-unneeded-ternary': 'off',
  // 'no-whitespace-before-property': 'off',
  // 'nonblock-statement-body-position': 'off',
  // 'object-curly-newline': 'off',
  'object-curly-spacing': 'error',
  // 'object-property-newline': 'off',
  'one-var': [
    'error',
    {
      'var': 'never',
      'let': 'never',
      'const': 'never',
    },
  ],
  // 'one-var-declaration-per-line': 'off',
  // 'operator-assignment': 'off',
  'operator-linebreak': ['error', 'after'],
  'padded-blocks': ['error', 'never'],
  // 'padding-line-between-statements': 'off',
  'quote-props': ['error', 'consistent'],
  'quotes': ['error', 'single', {allowTemplateLiterals: true}],
  'require-jsdoc': [
    'error',
    {
      require: {
        FunctionDeclaration: true,
        MethodDefinition: true,
        ClassDeclaration: true,
      },
    },
  ],
  'semi': 'error',
  'semi-spacing': 'error',
  // 'semi-style': 'off',
  // 'sort-keys': 'off',
  // 'sort-vars': 'off',
  'space-before-blocks': 'error',
  'space-before-function-paren': [
    'error',
    {
      asyncArrow: 'always',
      anonymous: 'never',
      named: 'never',
    },
  ],
  // 'space-in-parens': 'off',
  // 'space-infix-ops': 'off',
  // 'space-unary-ops': 'off',
  'spaced-comment': ['error', 'always'],
  'switch-colon-spacing': 'error',
  // 'template-tag-spacing': 'off',
  // 'unicode-bom': 'off',
  // 'wrap-regex': 'off',

  // ECMAScript 6
  // http://eslint.org/docs/rules/#ecmascript-6
  // ------------------------------------------
  // 'arrow-body-style': 'off',
  // TODO(philipwalton): technically arrow parens are optional but
  // recommended. ESLint doesn't support a *consistent* setting so
  // "always" is used.
  'arrow-parens': ['error', 'always'],
  // 'arrow-spacing': 'off',
  'constructor-super': 'error',  // eslint:recommended
  'generator-star-spacing': ['error', 'after'],
  // 'no-class-assign': 'off',
  // 'no-confusing-arrow': 'off',
  // 'no-const-assign': 'off', // eslint:recommended
  // 'no-dupe-class-members': 'off', // eslint:recommended
  // 'no-duplicate-imports': 'off',
  'no-new-symbol': 'error',  // eslint:recommended
  // 'no-restricted-imports': 'off',
  'no-this-before-super': 'error',  // eslint:recommended
  // 'no-useless-computed-key': 'off',
  // 'no-useless-constructor': 'off',
  // 'no-useless-rename': 'off',
  'no-var': 'error',
  // 'object-shorthand': 'off',
  // 'prefer-arrow-callback': 'off',
  'prefer-const': ['error', {destructuring: 'all'}],
  // 'prefer-destructuring': 'off',
  // 'prefer-numeric-literals': 'off',
  'prefer-rest-params': 'error',
  'prefer-spread': 'error',
  // 'prefer-template': 'off',
  // 'require-yield': 'error', // eslint:recommended
  'rest-spread-spacing': 'error',
  // 'sort-imports': 'off',
  // 'symbol-description': 'off',
  // 'template-curly-spacing': 'off',
  'yield-star-spacing': ['error', 'after'],
};

// https://github.com/eslint/eslint/issues/8769
// Hack node module system so that eslint-plugin-cra resolves to local module.
/* global require */
/* eslint-disable-next-line @typescript-eslint/no-var-requires */
const m = require('module');
const originalResolve = m._resolveFilename;
m._resolveFilename = (request, ...args) => {
  if (request === 'eslint-plugin-cra') {
    return require.resolve('./scripts/eslint_plugin/index.js');
  } else {
    return originalResolve.call(m, request, ...args);
  }
};

const typescriptEslintDir =
    '../../../../third_party/node/node_modules/@typescript-eslint';

const config = {
  'root': true,
  'env': {
    browser: true,
    webextensions: true,
  },
  'parserOptions': {
    sourceType: 'module',
  },
  'extends': ['eslint:recommended', 'plugin:@typescript-eslint/recommended'],
  'settings': {
    jsdoc: {
      tagNamePreference: {
        returns: 'return',
        file: 'fileoverview',
      },
    },
  },
  'parser': `${typescriptEslintDir}/parser/dist/index.js`,
  'plugins': ['@typescript-eslint', 'jsdoc', 'eslint-plugin-cra'],
  // Generally, the rules should be compatible to both bundled and the newest
  // stable eslint, so it's easier to upgrade and develop without the full
  // Chromium tree.
  'rules': Object.assign({}, googleRules, {
    'curly': ['error', 'multi-line', 'consistent'],
    'eqeqeq': 'error',
    'no-console': ['error', {allow: ['warn', 'error']}],
    'no-multi-spaces': ['error', {ignoreEOLComments: true}],

    // The bundled eslint is not smart enough for this. Indentation in the new
    // code should be formatted properly by clang-format, as we required
    // `git cl format --js` before uploading.
    'indent': 'off',

    // To resolve the conflict with clang-format.
    'generator-star-spacing': [
      'error',
      {
        named: 'after',
        anonymous: 'neither',
        method: 'both',
      },
    ],

    // This doesn't work well with TypeScript files. The alternate
    // @typescript-eslint/no-unused-vars that works with TypeScript files is
    // enabled in @typescript-eslint/recommended.
    'no-unused-vars': 'off',

    // Use eslint-plugin-jsdoc instead of ESLint builtin valid-jsdoc /
    // require-jsdoc, since it has better flexibility on not requiring types
    // for jsdoc, and is also recommended on the ESLint rule page.
    'valid-jsdoc': 'off',
    'require-jsdoc': 'off',

    // This is not useful since ES6 and contradicts to
    // go/tsstyle#function-declarations.
    'no-inner-declarations': 'off',

    'jsdoc/require-jsdoc': [
      'error',
      {
        publicOnly: true,
      },
    ],
    'jsdoc/require-param': 'off',
    'jsdoc/require-param-type': 'off',
    'jsdoc/require-returns': 'off',
    'jsdoc/require-returns-type': 'off',
    'jsdoc/require-yields': 'off',

    'jsdoc/multiline-blocks': [
      'error',
      {
        noSingleLineBlocks: true,
      },
    ],
    'jsdoc/no-bad-blocks': [
      'error',
      {
        // The first four are default values, and the last one is added since
        // the lint name is too long and the eslint-disable-next-line is
        // frequently line wrapped, which cause jsdoc/no-bad-blocks to think
        // that it should be a docstring.
        ignore: [
          'ts-check',
          'ts-expect-error',
          'ts-ignore',
          'ts-nocheck',
          'typescript-eslint/consistent-type-assertions',
        ],
      },
    ],
    'jsdoc/no-defaults': 'error',
    'jsdoc/no-multi-asterisks': [
      'error',
      {
        allowWhitespace: true,
      },
    ],
    'jsdoc/require-asterisk-prefix': 'error',
    'jsdoc/require-description-complete-sentence': [
      'error',
      {
        abbreviations: ['e.g.'],
      },
    ],

    // go/tsstyle states that no variable should have _ as prefix/suffix, but
    // there's no better alternative for unused function parameters. Since the
    // convention for noUnusedParameters for TypeScript is also leading
    // underscore, we use the same ignore pattern here.  See b/173108529 and
    // g/typescript-style/uOfKsoxxWEY/HCgzNfAFAwAJ for other discussions.
    '@typescript-eslint/no-unused-vars': [
      'error',
      {
        varsIgnorePattern: '^_',
        argsIgnorePattern: '^_',
      },
    ],

    'no-restricted-syntax': [
      'error',
      // Disallow parseInt. (go/tsstyle#type-coercion)
      {
        selector: 'CallExpression[callee.name="parseInt"]',
        message: 'parseInt are not allowed, use Number() instead. ' +
            '(go/tsstyle#type-coercion)',
      },
      // Disallow Array constructor. (go/tsstyle#array-constructor)
      {
        selector: 'NewExpression[callee.name="Array"], ' +
            'CallExpression[callee.name="Array"]',
        message: 'Array constructor are not allowed. ' +
            '(go/tsstyle#array-constructor)',
      },
      // Disallow calling Error without new. (go/tsstyle#exceptions)
      {
        selector: 'CallExpression[callee.name="Error"]',
        message: 'Error constructor should be called with new Error(...). ' +
            '(go/tsstyle#exceptions)',
      },
      // Disallow for (... in ...). (go/tsstyle#iterating-objects)
      {
        selector: 'ForInStatement',
        message: 'for (... in ...) is not allowed. ' +
            '(go/tsstyle#iterating-objects)',
      },
      // Disallow forEach. (go/tsjs-practices/iteration)
      // TODO(pihsun): This was relaxed in style guide in cl/430720959,
      // consider relaxing this if there's place where forEach makes the code
      // much simpler.
      {
        selector: 'CallExpression[callee.property.name="forEach"]',
        message: 'forEach are not allowed. (go/tsstyle#iterating-containers)',
      },
      // Disallow function() {...}. (go/tsstyle#function-declarations)
      {
        selector: ':not(:matches(MethodDefinition, Property))' +
            ' > FunctionExpression:not([id])',
        message: 'Use named function or arrow function instead. ' +
            '(go/tsstyle#function-declarations)',
      },
      // Disallow local function declaration with arrow function without
      // accessing this. This might have some false negative if the "this" is
      // accessed deep inside the function in another scope, but should be
      // rare. (go/tsstyle#function-declarations)
      {
        selector: 'VariableDeclarator:not(:has(.id[typeAnnotation]))' +
            ' > ArrowFunctionExpression.init:not(:has(ThisExpression))',
        message: 'Use named function to declare local function. ' +
            '(go/tsstyle#function-declarations)',
      },
      // Disallow private fields. (go/tsstyle#private-fields)
      {
        selector: 'TSPrivateIdentifier',
        message: 'Private fields are not allowed. (go/tsstyle#private-fields)',
      },
      // Disallow explicit boolean coercions in condition.
      // (go/tsstyle#type-coercion-implicit)
      {
        selector: ':matches(IfStatement, WhileStatement)' +
            ' > UnaryExpression.test[operator="!"]' +
            ' > UnaryExpression.argument[operator="!"]',
        message: 'Explicit boolean coercion is not needed in conditions. ' +
            '(go/tsstyle#type-coercion-implicit)',
      },
    ],

    '@typescript-eslint/naming-convention': [
      'error',
      {
        selector: 'default',
        format: ['strictCamelCase'],
      },
      {
        selector: 'typeLike',
        format: ['StrictPascalCase'],
      },
      {
        selector: 'variable',
        format: ['strictCamelCase', 'UPPER_CASE'],
      },
      {
        selector: 'enumMember',
        format: ['UPPER_CASE'],
      },
      {
        // Allows leading underscore for unused parameters.
        selector: 'parameter',
        modifiers: ['unused'],
        format: ['strictCamelCase'],
        leadingUnderscore: 'allow',
      },
      {
        // Disallow 'Interface' as interface suffix. (go/tsstyle#naming-style)
        selector: 'interface',
        format: ['StrictPascalCase'],
        custom: {
          regex: 'Interface$',
          match: false,
        },
      },
      {
        // This is used in all lit file. Since the HTMLElementTagNameMap name is
        // not defined by us, ignore naming-convention lint check on it.
        selector: 'interface',
        format: [],
        filter: {
          regex: '^HTMLElementTagNameMap$',
          match: true,
        },
      },
      {
        // This is for the tag name declaration in all lit file. Added here
        // since it's cumbersome to have eslint disable in all lit files, and it
        // feels quite hard to accidentally triggers this rule.
        selector: 'typeProperty',
        format: [],
        filter: {
          regex: '^[a-z-]*$',
          match: true,
        },
      },
      {
        // This is for having CSS class names in the classMap arguments. Since
        // CSS classes use -, it doesn't follow the typical strictCamelCase
        // object literal property rule. Since it's not easy to accidentally
        // have those by mistake, and there's no good way of telling whether an
        // object literal property is used as CSS class name, we allow all
        // object literal property to match /[a-z-]*/.
        selector: 'objectLiteralProperty',
        format: [],
        filter: {
          regex: '^[a-z-]*$',
          match: true,
        },
      },
    ],

    // This is covered by @typescript-eslint/naming-convention.
    'camelcase': 'off',

    // go/tsstyle#arrayt-type
    '@typescript-eslint/array-type': [
      'error',
      {
        'default': 'array-simple',
      },
    ],

    // Using "as" type assertion should be rare and as a last resort if it's
    // really too complicated to put the constraint in type system, and it's
    // not easy to do a runtime assertion (assertInstanceof) either.
    //
    // If it's the case, please have a eslint-disable-next-line to disable the
    // lint together with some comment explaining why the assertion is safe.
    //
    // See also:
    // go/tsstyle#type-and-non-nullability-assertions
    // go/tsstyle#type-assertions-syntax
    // go/tsstyle#type-assertions-and-object-literals
    '@typescript-eslint/consistent-type-assertions': [
      'error',
      {
        assertionStyle: 'never',
      },
    ],

    // go/tsstyle#interfaces-vs-type-aliases
    '@typescript-eslint/consistent-type-definitions': ['error', 'interface'],

    // go/tsstyle#import-export-type
    '@typescript-eslint/consistent-type-imports': [
      'error',
      {
        prefer: 'no-type-imports',
      },
    ],

    'quote-props': ['error', 'consistent-as-needed', {keywords: true}],

    // go/tsstyle#visibility
    '@typescript-eslint/explicit-member-accessibility': [
      'error',
      {
        accessibility: 'no-public',
      },
    ],

    // go/tsstyle#member-property-declarations
    '@typescript-eslint/member-delimiter-style': [
      'error',
      {
        multiline: {
          delimiter: 'comma',
          requireLast: true,
        },
        singleline: {
          delimiter: 'comma',
          requireLast: false,
        },
        multilineDetection: 'last-member',
        overrides: {
          'interface': {
            multiline: {
              delimiter: 'semi',
              requireLast: true,
            },
            singleline: {
              delimiter: 'semi',
              requireLast: true,
            },
          },
        },
      },
    ],

    '@typescript-eslint/sort-type-constituents': 'error',

    'comma-dangle': 'off',
    '@typescript-eslint/comma-dangle': ['error', 'always-multiline'],

    'func-call-spacing': 'off',
    '@typescript-eslint/func-call-spacing': 'error',

    '@typescript-eslint/lines-between-class-members': 'error',

    '@typescript-eslint/no-unused-expressions': 'error',

    'cra/parameter-comment-format': 'error',

    'cra/generic-parameter-on-declaration-type': 'error',

    'cra/todo-format': 'error',

    // go/tsstyle#constructors
    'new-parens': 'error',

    // go/tsstyle#assignment-in-control-statements
    'no-cond-assign': 'error',

    // go/tsstyle#switch-statements
    'default-case': 'error',

    // go/tsstyle#return-types
    '@typescript-eslint/explicit-module-boundary-types': 'error',

    // Upgrade several warning in @typescript-eslint/recommended to error,
    // since there's no easy way to tell eslint to stop on all warning in
    // config file.
    '@typescript-eslint/no-explicit-any': 'error',
    '@typescript-eslint/no-non-null-assertion': 'error',

    // The remaining of jsdoc/recommended, with severity changed to error.
    // Since there's no easy way to tell eslint to stop on all warning in
    // config file, we manually copied all rules here.
    'jsdoc/check-access': 'error',
    'jsdoc/check-alignment': 'error',
    'jsdoc/check-param-names': 'error',
    'jsdoc/check-property-names': 'error',
    'jsdoc/check-tag-names': 'error',
    'jsdoc/check-types': 'error',
    'jsdoc/check-values': 'error',
    'jsdoc/empty-tags': 'error',
    'jsdoc/implements-on-classes': 'error',
    'jsdoc/no-undefined-types': 'error',
    'jsdoc/require-param-description': 'error',
    'jsdoc/require-param-name': 'error',
    'jsdoc/require-property': 'error',
    'jsdoc/require-property-description': 'error',
    'jsdoc/require-property-name': 'error',
    'jsdoc/require-property-type': 'error',
    'jsdoc/require-returns-check': 'error',
    'jsdoc/require-returns-description': 'error',
    'jsdoc/require-yields-check': 'error',
    'jsdoc/tag-lines': ['error', 'never', {startLines: 1}],
    'jsdoc/valid-types': 'error',

    'no-invalid-this': 'off',
    '@typescript-eslint/no-invalid-this': 'error',

    'no-constant-condition': ['error', {checkLoops: false}],
  }),
  'overrides': [
    {
      files: ['**/*.ts'],
      parserOptions: {
        // eslint-disable-next-line no-undef
        tsconfigRootDir: __dirname,
        project: './tsconfig_base.json',
      },
      // TODO(pihsun): extends @typescript-eslint/recommended-type-checked when
      // we can really have the board dependent types to be correct. Currently
      // there's a lot of false negative because of unrecognized types treated
      // as `any`. We should be able to refer most used types of lit / mwc from
      // source instead of from gen folder, and exclude mojo related things from
      // the checks.
      rules: {
        // go/tsstyle#omit-comments-that-are-redundant-with-typescript
        'jsdoc/no-types': 'error',
        'jsdoc/check-tag-names': ['error', {typed: true}],

        // go/tsstyle#use-readonly
        '@typescript-eslint/prefer-readonly': 'error',

        '@typescript-eslint/require-array-sort-compare': 'error',

        '@typescript-eslint/prefer-nullish-coalescing': 'error',

        '@typescript-eslint/prefer-optional-chain': 'error',

        // go/tsstyle#optimization-compatibility-for-property-access
        '@typescript-eslint/dot-notation': 'error',

        '@typescript-eslint/return-await': 'error',

        '@typescript-eslint/strict-boolean-expressions': [
          'error',
          {
            allowString: false,
            allowNumber: false,
            allowNullableObject: false,
            // `any` is allowed here since our .eslintrc doesn't use the full
            // tsconfig.json (contains reference to board specific files), which
            // cause this rule to have some false negative on unrecognized
            // types.
            // TODO(pihsun): Change the lint action to be board dependent if we
            // can find a way to keep running it on presubmit check.
            allowAny: true,
          },
        ],

        // Prevent floating promises, since promises that are not awaited
        // usually indicates improper sequencing that might cause race, and if
        // the promise is rejected, the error is only logged by unhandled
        // promise rejection, and not propagated to caller.
        //
        // There are several potential ways to fix the lint error if you
        // encounter this:
        // * If the caller should wait for the promise, make the caller async
        // and
        //   await the promise.
        // * If the caller doesn't want to wait for the promise, and the promise
        //   is some kind of "job" that should be run independently but multiple
        //   jobs shouldn't be run at the same time, consider using
        //   AsyncJobQueue
        //   in async_job_queue.ts.
        // * As a last resort, add "void" before the promise to suppress the
        //   lint, ideally with a comment explaining why that is needed, check
        //   that there won't be issue if multiple of those promises got created
        //   at the same time, and check that error handling with unhandled
        //   promise rejection is sufficient.
        '@typescript-eslint/no-floating-promises': 'error',
        '@typescript-eslint/require-await': 'error',
        '@typescript-eslint/await-thenable': 'error',
        '@typescript-eslint/no-meaningless-void-operator': 'error',
        '@typescript-eslint/no-misused-promises': 'error',
      },
    },
    {
      files: ['platforms/dev/*.ts'],
      rules: {
        // Many dev mocks just log a message to console.
        'no-console': 'off',
        // Many dev mocks overrides the parent async method but doesn't really
        // need to await.
        '@typescript-eslint/require-await': 'off',
      },
    },
  ],
};

/* global module */
module.exports = config;