From da3c0005b2d1904047046dd0383be7f47e01885a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=98yvind=20Smestad?= Date: Tue, 19 Sep 2023 09:55:13 +0200 Subject: [PATCH] Split config in rule-set files --- index.js | 332 ++--------------------------------------- rule-sets/base.js | 272 +++++++++++++++++++++++++++++++++ rule-sets/cypress.js | 18 +++ rule-sets/dts.js | 12 ++ rule-sets/node.js | 15 ++ rule-sets/unit-test.js | 17 +++ 6 files changed, 345 insertions(+), 321 deletions(-) create mode 100644 rule-sets/base.js create mode 100644 rule-sets/cypress.js create mode 100644 rule-sets/dts.js create mode 100644 rule-sets/node.js create mode 100644 rule-sets/unit-test.js diff --git a/index.js b/index.js index 3a744f4..b000d83 100644 --- a/index.js +++ b/index.js @@ -3,20 +3,14 @@ import { fileURLToPath } from 'url'; import { FlatCompat } from '@eslint/eslintrc'; import js from '@eslint/js'; -import typedReduxSagaPlugin from '@jambit/eslint-plugin-typed-redux-saga'; -import tsPlugin from '@typescript-eslint/eslint-plugin'; -import tsParser from '@typescript-eslint/parser'; import eslintConfigPrettier from 'eslint-config-prettier'; -import cypress from 'eslint-plugin-cypress'; -import importPlugin from 'eslint-plugin-import'; -import noOnlyTestsPlugin from 'eslint-plugin-no-only-tests'; -import perfectionist from 'eslint-plugin-perfectionist'; import perfectionistNatural from 'eslint-plugin-perfectionist/configs/recommended-natural'; -import prettier from 'eslint-plugin-prettier'; -import vitest from 'eslint-plugin-vitest'; -import globals from 'globals'; -import internalRules from './internal-rules/index.js'; +import baseRuleSet from './rule-sets/base.js'; +import cypressRuleSet from './rule-sets/cypress.js'; +import dtsRuleSet from './rule-sets/dts.js'; +import nodeRuleSet from './rule-sets/node.js'; +import unitTestRuleSet from './rule-sets/unit-test.js'; // mimic CommonJS variables -- not needed if using CommonJS const __filename = fileURLToPath(import.meta.url); @@ -35,315 +29,11 @@ export default [ eslintConfigPrettier, //compat.extends("eslint-plugin-prettier/configs/recommended")[0], perfectionistNatural, - { - languageOptions: { - globals: { - ...globals.browser, - }, - parser: tsParser, - parserOptions: { - project: true, - }, - }, - plugins: { - '@jambit/typed-redux-saga': typedReduxSagaPlugin, - '@typescript-eslint': tsPlugin, - import: importPlugin, - 'internal-rules': internalRules, - 'no-only-tests': noOnlyTestsPlugin, - perfectionist, - prettier, - }, - rules: { - ...tsPlugin.configs['recommended-type-checked'].rules, - ...tsPlugin.configs['stylistic-type-checked'].rules, - ...importPlugin.configs.typescript.rules, - '@jambit/typed-redux-saga/delegate-effects': 'error', - '@jambit/typed-redux-saga/use-typed-effects': ['error', 'macro'], - '@typescript-eslint/array-type': ['error', { default: 'generic' }], - '@typescript-eslint/await-thenable': 'error', - '@typescript-eslint/ban-ts-comment': 'warn', - '@typescript-eslint/ban-types': [ - 'warn', - { - /* Disable this for now, we need null more places than we first thought - types: { - null: "Use 'undefined' instead of 'null', or better yet '?' optional syntax", - }, */ - }, - ], - '@typescript-eslint/consistent-type-definitions': ['error', 'type'], - '@typescript-eslint/explicit-module-boundary-types': 'off', - '@typescript-eslint/naming-convention': [ - 'error', - { - format: ['PascalCase'], - selector: 'typeLike', - }, - ], - '@typescript-eslint/no-empty-function': 'warn', - '@typescript-eslint/no-explicit-any': 'warn', - '@typescript-eslint/no-floating-promises': 'warn', - '@typescript-eslint/no-misused-promises': - 'error' /* Off for now. Should ignore root deps [ - 'error', - { - devDependencies: false, - optionalDependencies: false, - peerDependencies: false, - }, - ], */, - '@typescript-eslint/no-redeclare': 'error', - '@typescript-eslint/no-shadow': 'error', - '@typescript-eslint/no-unsafe-argument': 'warn', - '@typescript-eslint/no-unsafe-assignment': 'warn', - '@typescript-eslint/no-unsafe-call': 'warn', - '@typescript-eslint/no-unsafe-member-access': 'warn', - '@typescript-eslint/no-unsafe-return': 'warn', - '@typescript-eslint/no-unused-vars': [ - 'error', - { - ignoreRestSiblings: true, - }, - ], - '@typescript-eslint/no-var-requires': 'error', - '@typescript-eslint/prefer-nullish-coalescing': 'warn', - '@typescript-eslint/prefer-regexp-exec': 'warn', - '@typescript-eslint/require-await': 'warn', - '@typescript-eslint/restrict-plus-operands': 'warn', - '@typescript-eslint/restrict-template-expressions': 'warn', - '@typescript-eslint/sort-type-constituents': 'off', - '@typescript-eslint/triple-slash-reference': 'error', - '@typescript-eslint/unbound-method': 'warn', - // To allow deprecated React hooks for now, like: 'UNSAFE_componentWillReceiveProps' - camelcase: 'warn', - 'capitalized-comments': 'off', - complexity: 'error', - curly: 'error', - // From typescript-eslint: We recommend you do not use the following rules, as TypeScript provides the same checks as part of standard type checking: - // import/named, import/namespace, import/default, import/no-named-as-default-member - 'import/default': 'off', - 'import/export': 'error', - // Consistent file extensions on imports - 'import/extensions': [ - 'error', - 'never', - { - js: 'ignorePackages', - json: 'always', - }, - ], - 'import/named': 'off', - 'import/namespace': 'off', - 'import/newline-after-import': 'error', - 'import/no-absolute-path': 'error', - // "import/no-commonjs": "error", // Should add? (used in tests? specify as Node?) - 'import/no-amd': 'error', - //FIXME heap overflow this line 'import/no-cycle': ['error', { maxDepth: 3 }], - 'import/no-default-export': 'error', - //FIXME heap overflow this line 'import/no-deprecated': 'error', - 'import/no-duplicates': 'error', - //FIXME heap overflow this line 'import/no-dynamic-require': 'error', - 'import/no-extraneous-dependencies': 'off', // Off for now. Should ignore root deps? - 'import/no-named-as-default': 'off', - //FIXME this line causes the stack to overflow? - //'import/no-deprecated': 'warn', - 'import/no-named-as-default-member': 'off', - 'import/no-restricted-paths': 'error', - 'import/no-self-import': 'error', - 'import/no-unresolved': 'error', - 'import/no-webpack-loader-syntax': 'error', - 'import/order': [ - 'error', - { - alphabetize: { order: 'asc' }, - groups: [ - 'builtin', - 'external', - 'internal', - 'parent', - 'sibling', - 'index', - 'object', - 'unknown', - ], - 'newlines-between': 'always', - pathGroups: [ - { - group: 'internal', - pattern: '@tidal/core{,/**}', - position: 'before', - }, - { - group: 'internal', - pattern: '@tidal/event-tracking{,/**}', - position: 'before', - }, - { - group: 'internal', - pattern: '@tidal/product-analytics{,/**}', - position: 'before', - }, - { - group: 'internal', - pattern: '@tidal/react-icons{,/**}', - position: 'before', - }, - { - group: 'internal', - pattern: '@tidal/tv{,/**}', - position: 'before', - }, - { - group: 'internal', - pattern: '@tidal/web{,/**}', - position: 'before', - }, - ], - }, - ], - 'import/unambiguous': 'off', - 'internal-rules/require-coverage-ignore-reason': 'error', - 'max-depth': 'error', - 'max-param': 'off', - 'max-params': 'off', - 'newline-after-var': 'off', - 'newline-before-return': 'off', - 'no-console': ['error', { allow: ['error', 'warn', 'debug', 'table'] }], - 'no-duplicate-imports': 'off', // We use the rule from eslint-plugin-import - 'no-only-tests/no-only-tests': 'error', - 'no-prototype-builtins': 'error', - 'no-redeclare': 'off', - 'no-restricted-imports': [ - 'warn', - { - message: 'TIDAL: Please use CSS Modules instead!', - name: '@emotion/css', - }, - { - message: 'TIDAL: Please upgrade to @wave/react!', - name: '@tidal/ui/src', - }, - { - message: - 'TIDAL: Please use TS read-only to ensure immutability instead!', - name: 'immutable', - }, - ], - 'no-restricted-syntax': [ - 'warn', - // ban all enums - { - message: - 'Prefer string unions over enums, avoids possible Babel problems and: https://blog.bam.tech/developer-news/should-you-use-enums-or-union-types-in-typescript', - selector: 'TSEnumDeclaration', - }, - ], - 'no-shadow': 'off', - 'no-undef': 'off', - // https://typescript-eslint.io/linting/troubleshooting/#i-get-errors-from-the-no-undef-rule-about-global-variables-not-being-defined-even-though-there-are-no-typescript-errors - // note you must disable the base rule as it can report incorrect errors - 'no-unused-vars': 'off', - 'no-warning-comments': 'off', - // TODO: consider switching from 'import/order' to this one? - 'perfectionist/sort-imports': 'off', - 'prettier/prettier': [ - 'error', - { - arrowParens: 'avoid', - singleQuote: true, - trailingComma: 'all', - }, - ], - 'react-hooks/exhaustive-deps': 'error', - 'react-hooks/rules-of-hooks': 'error', - // Broken completely here: `tv/src/components/artistHeader/artistHeader.js:20`: - 'react/boolean-prop-naming': 'off', - 'react/default-props-match-prop-types': 'off', - 'react/forbid-component-props': 'off', - 'react/function-component-definition': 'off', - 'react/jsx-curly-brace-presence': [ - 'error', - { children: 'never', propElementValues: 'always', props: 'never' }, - ], - 'react/jsx-handler-names': 'warn', - 'react/jsx-no-useless-fragment': ['error', { allowExpressions: true }], - 'react/jsx-props-no-spreading': 'warn', - 'react/jsx-sort-props': 'off', - 'react/no-array-index-key': 'off', - 'react/no-unsafe': 'error', - 'react/no-unused-prop-types': 'error', - 'react/no-unused-state': 'error', - // fails if using object level read only: https://github.com/yannickcr/eslint-plugin-react/issues/2472 - 'react/prefer-read-only-props': 'off', - 'react/prop-types': 'off', - 'react/require-default-props': 'off', - 'react/state-in-constructor': 'warn', // TODO: make error again and fix all - 'react/static-property-placement': 'error', - 'tidal-extras/no-get-artist': 'off', - 'valid-jsdoc': 'off', - }, - settings: { - 'import/resolver': { - typescript: {}, - }, - react: { - version: '18.2.0', - }, - }, - }, + // Base Tidal rule set: + baseRuleSet, // Following are some file type specific overrides: - // Cypress - { - files: ['**/cypress/**/*'], - languageOptions: { - globals: cypress.environments.globals.globals, - }, - plugins: { - cypress, - }, - rules: { - ...cypress.configs.recommended.rules, - 'cypress/no-force': 'error', - 'cypress/unsafe-to-chain-command': 'warn', - }, - }, - // For unit tests - { - files: ['**/*.test.ts', '**/*.test.tsx', '__mocks__/**/*.ts'], - ignores: ['**/*.auto.test.tsx'], - languageOptions: { - // @ts-expect-error - globals: vitest.environments.env.globals, - }, - plugins: { vitest }, - rules: { - // @ts-expect-error - ...vitest.configs.recommended.rules, - 'vitest/prefer-to-be': 'off', - 'vitest/prefer-todo': 'error', - }, - }, - // Typescript type definitions - { - files: ['**/*.d.ts'], - rules: { - '@typescript-eslint/consistent-type-definitions': 'off', - 'no-var': 'off', - 'one-var': 'off', - 'vars-on-top': 'off', - }, - }, - // For Node.js scripts - { - files: ['**/scripts/*'], - languageOptions: { - globals: { - ...globals.node, - }, - }, - rules: { - 'no-console': 'off', - }, - }, + cypressRuleSet, + dtsRuleSet, + nodeRuleSet, + unitTestRuleSet, ]; diff --git a/rule-sets/base.js b/rule-sets/base.js new file mode 100644 index 0000000..95ab430 --- /dev/null +++ b/rule-sets/base.js @@ -0,0 +1,272 @@ +/* eslint-disable @typescript-eslint/no-unsafe-assignment */ +import typedReduxSagaPlugin from '@jambit/eslint-plugin-typed-redux-saga'; +import tsPlugin from '@typescript-eslint/eslint-plugin'; +import tsParser from '@typescript-eslint/parser'; +import importPlugin from 'eslint-plugin-import'; +import noOnlyTestsPlugin from 'eslint-plugin-no-only-tests'; +import perfectionist from 'eslint-plugin-perfectionist'; +import prettier from 'eslint-plugin-prettier'; +import globals from 'globals'; + +import internalRules from '../internal-rules/index.js'; + +/** @type { import("eslint").Linter.FlatConfig } */ +// @ts-expect-error the TSParser seems to have some unmatching types +export default { + languageOptions: { + globals: { + ...globals.browser, + }, + parser: tsParser, + parserOptions: { + project: true, + }, + }, + plugins: { + '@jambit/typed-redux-saga': typedReduxSagaPlugin, + '@typescript-eslint': tsPlugin, + import: importPlugin, + 'internal-rules': internalRules, + 'no-only-tests': noOnlyTestsPlugin, + perfectionist, + prettier, + }, + rules: { + ...tsPlugin.configs['recommended-type-checked'].rules, + ...tsPlugin.configs['stylistic-type-checked'].rules, + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + ...importPlugin.configs.typescript.rules, + '@jambit/typed-redux-saga/delegate-effects': 'error', + '@jambit/typed-redux-saga/use-typed-effects': ['error', 'macro'], + '@typescript-eslint/array-type': ['error', { default: 'generic' }], + '@typescript-eslint/await-thenable': 'error', + '@typescript-eslint/ban-ts-comment': 'warn', + '@typescript-eslint/ban-types': [ + 'warn', + { + /* Disable this for now, we need null more places than we first thought + types: { + null: "Use 'undefined' instead of 'null', or better yet '?' optional syntax", + }, */ + }, + ], + '@typescript-eslint/consistent-type-definitions': ['error', 'type'], + '@typescript-eslint/explicit-module-boundary-types': 'off', + '@typescript-eslint/naming-convention': [ + 'error', + { + format: ['PascalCase'], + selector: 'typeLike', + }, + ], + '@typescript-eslint/no-empty-function': 'warn', + '@typescript-eslint/no-explicit-any': 'warn', + '@typescript-eslint/no-floating-promises': 'warn', + '@typescript-eslint/no-misused-promises': + 'error' /* Off for now. Should ignore root deps [ + 'error', + { + devDependencies: false, + optionalDependencies: false, + peerDependencies: false, + }, + ], */, + '@typescript-eslint/no-redeclare': 'error', + '@typescript-eslint/no-shadow': 'error', + '@typescript-eslint/no-unsafe-argument': 'warn', + '@typescript-eslint/no-unsafe-assignment': 'warn', + '@typescript-eslint/no-unsafe-call': 'warn', + '@typescript-eslint/no-unsafe-member-access': 'warn', + '@typescript-eslint/no-unsafe-return': 'warn', + '@typescript-eslint/no-unused-vars': [ + 'error', + { + ignoreRestSiblings: true, + }, + ], + '@typescript-eslint/no-var-requires': 'error', + '@typescript-eslint/prefer-nullish-coalescing': 'warn', + '@typescript-eslint/prefer-regexp-exec': 'warn', + '@typescript-eslint/require-await': 'warn', + '@typescript-eslint/restrict-plus-operands': 'warn', + '@typescript-eslint/restrict-template-expressions': 'warn', + '@typescript-eslint/sort-type-constituents': 'off', + '@typescript-eslint/triple-slash-reference': 'error', + '@typescript-eslint/unbound-method': 'warn', + // To allow deprecated React hooks for now, like: 'UNSAFE_componentWillReceiveProps' + camelcase: 'warn', + 'capitalized-comments': 'off', + complexity: 'error', + curly: 'error', + // From typescript-eslint: We recommend you do not use the following rules, as TypeScript provides the same checks as part of standard type checking: + // import/named, import/namespace, import/default, import/no-named-as-default-member + 'import/default': 'off', + 'import/export': 'error', + // Consistent file extensions on imports + 'import/extensions': [ + 'error', + 'never', + { + js: 'ignorePackages', + json: 'always', + }, + ], + 'import/named': 'off', + 'import/namespace': 'off', + 'import/newline-after-import': 'error', + 'import/no-absolute-path': 'error', + // "import/no-commonjs": "error", // Should add? (used in tests? specify as Node?) + 'import/no-amd': 'error', + //FIXME heap overflow this line 'import/no-cycle': ['error', { maxDepth: 3 }], + 'import/no-default-export': 'error', + //FIXME heap overflow this line 'import/no-deprecated': 'error', + 'import/no-duplicates': 'error', + //FIXME heap overflow this line 'import/no-dynamic-require': 'error', + 'import/no-extraneous-dependencies': 'off', // Off for now. Should ignore root deps? + 'import/no-named-as-default': 'off', + //FIXME this line causes the stack to overflow? + //'import/no-deprecated': 'warn', + 'import/no-named-as-default-member': 'off', + 'import/no-restricted-paths': 'error', + 'import/no-self-import': 'error', + 'import/no-unresolved': 'error', + 'import/no-webpack-loader-syntax': 'error', + 'import/order': [ + 'error', + { + alphabetize: { order: 'asc' }, + groups: [ + 'builtin', + 'external', + 'internal', + 'parent', + 'sibling', + 'index', + 'object', + 'unknown', + ], + 'newlines-between': 'always', + pathGroups: [ + { + group: 'internal', + pattern: '@tidal/core{,/**}', + position: 'before', + }, + { + group: 'internal', + pattern: '@tidal/event-tracking{,/**}', + position: 'before', + }, + { + group: 'internal', + pattern: '@tidal/product-analytics{,/**}', + position: 'before', + }, + { + group: 'internal', + pattern: '@tidal/react-icons{,/**}', + position: 'before', + }, + { + group: 'internal', + pattern: '@tidal/tv{,/**}', + position: 'before', + }, + { + group: 'internal', + pattern: '@tidal/web{,/**}', + position: 'before', + }, + ], + }, + ], + 'import/unambiguous': 'off', + 'internal-rules/require-coverage-ignore-reason': 'error', + 'max-depth': 'error', + 'max-param': 'off', + 'max-params': 'off', + 'newline-after-var': 'off', + 'newline-before-return': 'off', + 'no-console': ['error', { allow: ['error', 'warn', 'debug', 'table'] }], + 'no-duplicate-imports': 'off', // We use the rule from eslint-plugin-import + 'no-only-tests/no-only-tests': 'error', + 'no-prototype-builtins': 'error', + 'no-redeclare': 'off', + 'no-restricted-imports': [ + 'warn', + { + message: 'TIDAL: Please use CSS Modules instead!', + name: '@emotion/css', + }, + { + message: 'TIDAL: Please upgrade to @wave/react!', + name: '@tidal/ui/src', + }, + { + message: + 'TIDAL: Please use TS read-only to ensure immutability instead!', + name: 'immutable', + }, + ], + 'no-restricted-syntax': [ + 'warn', + // ban all enums + { + message: + 'Prefer string unions over enums, avoids possible Babel problems and: https://blog.bam.tech/developer-news/should-you-use-enums-or-union-types-in-typescript', + selector: 'TSEnumDeclaration', + }, + ], + 'no-shadow': 'off', + 'no-undef': 'off', + // https://typescript-eslint.io/linting/troubleshooting/#i-get-errors-from-the-no-undef-rule-about-global-variables-not-being-defined-even-though-there-are-no-typescript-errors + // note you must disable the base rule as it can report incorrect errors + 'no-unused-vars': 'off', + 'no-warning-comments': 'off', + // TODO: consider switching from 'import/order' to this one? + 'perfectionist/sort-imports': 'off', + 'prettier/prettier': [ + 'error', + { + arrowParens: 'avoid', + singleQuote: true, + trailingComma: 'all', + }, + ], + 'react-hooks/exhaustive-deps': 'error', + 'react-hooks/rules-of-hooks': 'error', + // Broken completely here: `tv/src/components/artistHeader/artistHeader.js:20`: + 'react/boolean-prop-naming': 'off', + 'react/default-props-match-prop-types': 'off', + 'react/forbid-component-props': 'off', + 'react/function-component-definition': 'off', + 'react/jsx-curly-brace-presence': [ + 'error', + { children: 'never', propElementValues: 'always', props: 'never' }, + ], + 'react/jsx-handler-names': 'warn', + 'react/jsx-no-useless-fragment': ['error', { allowExpressions: true }], + 'react/jsx-props-no-spreading': 'warn', + 'react/jsx-sort-props': 'off', + 'react/no-array-index-key': 'off', + 'react/no-unsafe': 'error', + 'react/no-unused-prop-types': 'error', + 'react/no-unused-state': 'error', + // fails if using object level read only: https://github.com/yannickcr/eslint-plugin-react/issues/2472 + 'react/prefer-read-only-props': 'off', + 'react/prop-types': 'off', + 'react/require-default-props': 'off', + 'react/state-in-constructor': 'warn', // TODO: make error again and fix all + 'react/static-property-placement': 'error', + 'tidal-extras/no-get-artist': 'off', + 'valid-jsdoc': 'off', + }, + settings: { + 'import/resolver': { + typescript: {}, + }, + react: { + version: '18.2.0', + }, + }, +}; diff --git a/rule-sets/cypress.js b/rule-sets/cypress.js new file mode 100644 index 0000000..a9204d3 --- /dev/null +++ b/rule-sets/cypress.js @@ -0,0 +1,18 @@ +// Cypress +import cypress from 'eslint-plugin-cypress'; + +/** @type { import("eslint").Linter.FlatConfig } */ +export default { + files: ['**/cypress/**/*'], + languageOptions: { + globals: cypress.environments.globals.globals, + }, + plugins: { + cypress, + }, + rules: { + ...cypress.configs.recommended.rules, + 'cypress/no-force': 'error', + 'cypress/unsafe-to-chain-command': 'warn', + }, +}; diff --git a/rule-sets/dts.js b/rule-sets/dts.js new file mode 100644 index 0000000..41555e7 --- /dev/null +++ b/rule-sets/dts.js @@ -0,0 +1,12 @@ +// Typescript type definitions + +/** @type { import("eslint").Linter.FlatConfig } */ +export default { + files: ['**/*.d.ts'], + rules: { + '@typescript-eslint/consistent-type-definitions': 'off', + 'no-var': 'off', + 'one-var': 'off', + 'vars-on-top': 'off', + }, +}; diff --git a/rule-sets/node.js b/rule-sets/node.js new file mode 100644 index 0000000..b2087dc --- /dev/null +++ b/rule-sets/node.js @@ -0,0 +1,15 @@ +// For Node.js scripts +import globals from 'globals'; + +/** @type { import("eslint").Linter.FlatConfig } */ +export default { + files: ['**/scripts/*'], + languageOptions: { + globals: { + ...globals.node, + }, + }, + rules: { + 'no-console': 'off', + }, +}; diff --git a/rule-sets/unit-test.js b/rule-sets/unit-test.js new file mode 100644 index 0000000..0d94470 --- /dev/null +++ b/rule-sets/unit-test.js @@ -0,0 +1,17 @@ +// For unit tests +import vitest from 'eslint-plugin-vitest'; + +/** @type { import("eslint").Linter.FlatConfig } */ +export default { + files: ['**/*.test.ts', '**/*.test.tsx', '__mocks__/**/*.ts'], + ignores: ['**/*.auto.test.tsx'], + languageOptions: { + globals: vitest.environments.env.globals, + }, + plugins: { vitest }, + rules: { + ...vitest.configs.recommended.rules, + 'vitest/prefer-to-be': 'off', + 'vitest/prefer-todo': 'error', + }, +};