eslint-config-tidal
Version:
ESLint sharable flat config for TIDAL
327 lines (322 loc) • 10.4 kB
JavaScript
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
import { fixupPluginRules } from '@eslint/compat';
import typedReduxSagaPlugin from '@jambit/eslint-plugin-typed-redux-saga';
import tsPlugin from '@typescript-eslint/eslint-plugin';
import tsParser from '@typescript-eslint/parser';
import confusingBrowserGlobals from 'confusing-browser-globals';
import importPlugin from 'eslint-plugin-import';
import jsxA11yPlugin from 'eslint-plugin-jsx-a11y';
import noOnlyTestsPlugin from 'eslint-plugin-no-only-tests';
import perfectionist from 'eslint-plugin-perfectionist';
import prettier from 'eslint-plugin-prettier';
import react from 'eslint-plugin-react';
import reactHooks from 'eslint-plugin-react-hooks';
import globals from 'globals';
import { internalRules } from '../internal-rules/index.js';
// Printable ASCII mostly sorted by code point — matches the old sort-keys-fix behavior
// (uppercase before lowercase, e.g. "OneTrust" < "console")
const alphabet =
' !"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
/** @type { import("eslint").Linter.Config } */
export const baseRuleSet = {
languageOptions: {
globals: {
...globals.browser,
},
parser: tsParser,
parserOptions: {
ecmaVersion: 'latest',
projectService: true,
sourceType: 'module',
},
},
plugins: {
'@jambit/typed-redux-saga': typedReduxSagaPlugin,
// @ts-expect-error TS plugin does not quite match expected type
'@typescript-eslint': tsPlugin,
import: fixupPluginRules(importPlugin),
'internal-rules': internalRules,
'jsx-a11y': jsxA11yPlugin,
'no-only-tests': noOnlyTestsPlugin,
perfectionist,
prettier,
react,
// @ts-expect-error react-hooks v7 has type compatibility issues
'react-hooks': reactHooks,
},
rules: {
...tsPlugin.configs['recommended-type-checked'].rules,
...tsPlugin.configs['stylistic-type-checked'].rules,
...importPlugin.configs.typescript.rules,
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
...jsxA11yPlugin.configs.recommended.rules,
...perfectionist.configs['recommended-custom'].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': 'error',
'@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',
'@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',
camelcase: 'warn',
'capitalized-comments': 'off',
complexity: 'error',
curly: 'error',
'import/default': 'off',
'import/export': 'error',
'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-amd': 'error',
'import/no-default-export': 'error',
'import/no-duplicates': 'error',
'import/no-extraneous-dependencies': 'off', // Off for now. Should ignore root deps?
'import/no-named-as-default': 'off',
'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',
'no-only-tests/no-only-tests': 'error',
'no-prototype-builtins': 'error',
'no-redeclare': 'off',
'no-restricted-globals': ['error', ...confusingBrowserGlobals],
'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',
{
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',
'no-unused-vars': 'off',
'no-var': 'error',
'no-warning-comments': 'off',
'object-shorthand': 'error',
'perfectionist/sort-classes': [
'error',
{
groups: [
'static-block',
['static-property', 'static-accessor-property'],
['property', 'accessor-property', 'function-property'],
'index-signature',
'constructor',
['static-method', 'static-get-method', 'static-set-method'],
['get-method', 'set-method', 'method'],
],
},
],
// TODO: consider switching from 'import/order' to this one?
'perfectionist/sort-imports': 'off',
'perfectionist/sort-interfaces': ['error', { ignoreCase: false }],
'perfectionist/sort-intersection-types': [
'error',
{
groups: [
[
'conditional',
'function',
'import',
'intersection',
'keyword',
'literal',
'named',
'operator',
'tuple',
'union',
],
['object'],
['nullish'],
],
},
],
'perfectionist/sort-modules': 'off',
'perfectionist/sort-named-imports': 'off',
'perfectionist/sort-objects': ['error', { ignoreCase: false }],
'perfectionist/sort-switch-case': ['error', { ignoreCase: false }],
'perfectionist/sort-union-types': [
'error',
{
groups: [
[
'conditional',
'function',
'import',
'intersection',
'keyword',
'literal',
'named',
'operator',
'tuple',
'union',
],
['object'],
['nullish'],
],
},
],
'prettier/prettier': [
'error',
{
arrowParens: 'avoid',
endOfLine: 'auto',
singleQuote: true,
trailingComma: 'all',
},
],
'react-hooks/exhaustive-deps': 'error',
'react-hooks/rules-of-hooks': 'error',
'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',
'react/prop-types': 'off',
'react/require-default-props': 'off',
'react/static-property-placement': 'error',
'valid-jsdoc': 'off',
},
settings: {
'import/resolver': {
typescript: {},
},
perfectionist: {
alphabet,
ignoreCase: false,
type: 'custom',
},
react: {
version: '18.2.0',
},
},
};