This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// @ts-nocheck | |
import { env } from 'node:process'; | |
import comments from 'eslint-plugin-eslint-comments'; | |
import imprt from 'eslint-plugin-import'; // 'import' is ambiguous and prettier had trouble | |
import jest from 'eslint-plugin-jest'; | |
import jestFmt from 'eslint-plugin-jest-formatting'; | |
import n from 'eslint-plugin-n'; | |
import nextjs from '@next/eslint-plugin-next'; | |
import prettierConfig from 'eslint-config-prettier'; | |
import promise from 'eslint-plugin-promise'; | |
import react from 'eslint-plugin-react'; | |
import jsxa11y from 'eslint-plugin-jsx-a11y'; | |
import reactHooks from 'eslint-plugin-react-hooks'; | |
import regexp from 'eslint-plugin-regexp'; | |
import sec from 'eslint-plugin-security'; | |
import sonarjs from 'eslint-plugin-sonarjs'; | |
import tsParser from '@typescript-eslint/parser'; | |
import ts from '@typescript-eslint/eslint-plugin'; | |
import unicorn from 'eslint-plugin-unicorn'; | |
import md from 'eslint-plugin-markdown'; | |
import testLib from 'eslint-plugin-testing-library'; | |
import sb from 'eslint-plugin-storybook'; | |
import func from 'eslint-plugin-functional'; | |
// const env = process.env; | |
// const comments = require('eslint-plugin-eslint-comments'); | |
// const imprt = require('eslint-plugin-import'); | |
// const jest = require('eslint-plugin-jest'); | |
// const jestFmt = require('eslint-plugin-jest-formatting'); | |
// const n = require('eslint-plugin-n'); | |
// const nextjs = require('@next/eslint-plugin-next'); | |
// const prettierConfig = require('eslint-config-prettier'); | |
// const promise = require('eslint-plugin-promise'); | |
// const react = require('eslint-plugin-react'); | |
// const jsxa11y = require('eslint-plugin-jsx-a11y'); | |
// const reactHooks = require('eslint-plugin-react-hooks'); | |
// const regexp = require('eslint-plugin-regexp'); | |
// const sec = require('eslint-plugin-security'); | |
// const sonarjs = require('eslint-plugin-sonarjs'); | |
// const tsParser = require('@typescript-eslint/parser'); | |
// const ts = require('@typescript-eslint/eslint-plugin'); | |
// const unicorn = require('eslint-plugin-unicorn'); | |
// const md = require('eslint-plugin-markdown'); | |
// const testLib = require('eslint-plugin-testing-library'); | |
// const sb = require('eslint-plugin-storybook'); | |
// const func = require('eslint-plugin-functional'); | |
const IS_HEAVY = Boolean(env.LINT_HEAVY) || Boolean(env.CI); | |
const JS_FILES = ['**/*?.(md/){js,jsx,cjs,mjs}']; | |
const TS_FILES = ['**/*?.(md/){ts,tsx}']; | |
const JS_TS_FILES = ['**/*.?(md/){js,ts,jsx,tsx,cjs,mjs}']; | |
const REACTJS_FILES = ['**/*.?(md/){t,j}sx']; | |
// base, starting point | |
const baseConfig = [ | |
// Base config for MD | |
{ | |
files: ['**/*.md', '**/*.md/*'], | |
plugins: { | |
markdown: md, | |
}, | |
processor: 'markdown/markdown', | |
}, | |
// base config for codes | |
{ | |
languageOptions: { | |
parserOptions: { | |
ecmaVersion: 'latest', | |
sourceType: 'module', | |
}, | |
es2022: true, | |
}, | |
files: JS_TS_FILES, | |
plugins: { | |
func, | |
imprt, | |
promise, | |
regexp, | |
unicorn, | |
}, | |
rules: { | |
// disable function overloading entirely rather than defer to @typescript-eslint | |
'no-redeclare': 2, | |
'no-nested-ternary': 'off', | |
...unicorn.configs.recommended.rules, | |
'unicorn/prefer-at': 1, | |
'unicorn/better-regex': 2, | |
'unicorn/consistent-function-scoping': 2, | |
'unicorn/explicit-length-check': 2, | |
'unicorn/no-array-push-push': 2, | |
'unicorn/no-array-reduce': 2, | |
'unicorn/no-await-expression-member': 2, | |
'unicorn/no-for-loop': 2, | |
'unicorn/no-instanceof-array': 2, | |
'unicorn/no-new-array': 2, | |
'unicorn/no-new-buffer': 2, | |
'unicorn/no-unsafe-regex': 2, | |
'unicorn/no-useless-length-check': 2, | |
'unicorn/no-useless-spread': 2, | |
'unicorn/no-useless-undefined': 2, | |
'unicorn/prefer-array-find': 2, | |
'unicorn/prefer-array-flat-map': 2, | |
'unicorn/prefer-array-flat': 2, | |
'unicorn/prefer-array-index-of': 2, | |
'unicorn/prefer-array-some': 2, | |
'unicorn/prefer-date-now': 2, | |
'unicorn/prefer-default-parameters': 2, | |
'unicorn/prefer-event-target': 2, | |
'unicorn/prefer-export-from': [2, { ignoreUsedVariables: true }], | |
'unicorn/prefer-includes': 2, | |
'unicorn/prefer-logical-operator-over-ternary': 2, | |
'unicorn/prefer-native-coercion-functions': 2, | |
'unicorn/prefer-object-from-entries': 2, | |
'unicorn/prefer-prototype-methods': 2, | |
'unicorn/prefer-query-selector': 2, | |
'unicorn/prefer-spread': 2, | |
'unicorn/prefer-string-replace-all': 2, | |
'unicorn/prefer-top-level-await': 2, | |
'unicorn/prefer-type-error': 2, | |
'unicorn/throw-new-error': 2, | |
// promise rules | |
...promise.configs.recommended.rules, | |
// "promise/always-return": 2, | |
// "promise/avoid-new": 1, | |
// "promise/catch-or-return": 2, | |
// "promise/no-callback-in-promise": 1, | |
// "promise/no-native": 0, | |
// "promise/no-nesting": 1, | |
// "promise/no-new-statics": 2, | |
// "promise/no-promise-in-callback": 1, | |
// "promise/no-return-in-finally": 1, | |
// "promise/no-return-wrap": 2, | |
// "promise/param-names": 2, | |
// "promise/valid-params": 1, | |
// fp rules - strategic FP. | |
...func.configs['external-recommended'].rules, | |
...func.configs.lite.rules, | |
'func/functional-parameters': 1, | |
'func/no-conditional-statement': 1, | |
'func/no-try-statement': 2, | |
// Too much syntax noise, use an immutable lib if needed. | |
'func/prefer-readonly-type': 1, | |
'func/immutable-data': 1, | |
// import rules for all js/ts | |
...imprt.configs.recommended.rules, | |
'imprt/export': 2, // TypeScript compilation already ensures | |
'imprt/named': 2, // TypeScript compilation already ensures that named imports exist in the referenced module | |
'imprt/namespace': 2, // TypeScript compilation already ensures | |
'imprt/default': 2, // TypeScript compilation already ensures | |
'imprt/no-named-as-default-member': 1, | |
'imprt/no-unresolved': [2, { commonjs: true }], // required by eslint-import-resolver-typescript. | |
'imprt/no-unused-modules': [ | |
2, | |
{ | |
missingExports: true, | |
unusedExports: true, | |
ignoreExports: '**/*(rc,.config).{cjs,mjs,js,ts,jsx,tsx}', | |
}, | |
], | |
'imprt/no-useless-path-segments': [2, { noUselessIndex: true }], | |
'imprt/max-dependencies': [ | |
1, | |
{ | |
max: 8, | |
ignoreTypeImports: true, | |
}, | |
], | |
}, | |
}, | |
// base config and rules only for TS files | |
{ | |
files: TS_FILES, | |
languageOptions: { | |
parser: tsParser, | |
parserOptions: { | |
ecmaFeatures: { modules: true }, | |
project: './tsconfig.json', | |
}, | |
}, | |
plugins: { | |
func, | |
imprt, | |
ts, | |
}, | |
settings: { | |
'imprt/parsers': { | |
'@typescript-eslint/parser': ['.ts', '.tsx'], | |
}, | |
'imprt/resolver': { | |
typescript: { | |
alwaysTryTypes: true, | |
}, | |
node: { | |
extensions: ['.ts', '.tsx'], | |
}, | |
}, | |
'imprt/external-module-folders': ['node_modules', 'node_modules/@types'], | |
'imprt/extensions': ['.ts', '.tsx'], | |
}, | |
rules: { | |
...ts.configs['eslint-recommended'].rules, | |
...ts.configs['recommended'].rules, | |
'no-undef': 0, // typescript already takes care of this | |
'no-dupe-class-members': 0, // typescript already takes care of this | |
'no-return-await': 0, // use @typescript/eslint | |
'no-throw-literal': 0, // use @typescript/eslint | |
'no-use-before-define': 0, // use @typescript/eslint | |
'no-unused-expressions': 0, // use @typescript/eslint | |
'no-empty-function': 0, // use @typescript/eslint | |
'require-await': 0, // use @typescript/eslint | |
'no-unused-vars': 0, // use @typescript/eslint | |
'dot-notation': 0, // use @typescript/eslint | |
'no-shadow': 0, // use @typescript/eslint | |
'ts/return-await': 2, | |
'ts/no-throw-literal': 2, | |
'ts/no-use-before-define': 2, | |
'ts/consistent-type-assertions': 2, | |
'ts/consistent-type-imports': 2, | |
'ts/explicit-module-boundary-types': 2, | |
'ts/no-invalid-void-type': 2, | |
'ts/no-unnecessary-condition': 2, | |
'ts/no-unused-expressions': [ | |
2, | |
{ | |
allowShortCircuit: true, | |
allowTernary: true, | |
allowTaggedTemplates: true, | |
enforceForJSX: true, | |
}, | |
], | |
'ts/no-array-constructor': 0, | |
'ts/array-type': 2, | |
'ts/no-empty-function': 2, | |
'ts/prefer-optional-chain': 2, | |
'ts/dot-notation': 2, | |
'ts/no-unsafe-assignment': 0, | |
'ts/no-shadow': [ | |
2, | |
{ | |
hoist: 'all', | |
allow: ['resolve', 'reject', 'done', 'next', 'err', 'error'], | |
ignoreTypeValueShadow: true, | |
ignoreFunctionTypeParameterNameValueShadow: true, | |
}, | |
], | |
// Too much syntax noise, use an immutable lib if needed. | |
'ts/prefer-readonly-parameter-types': 0, | |
// Enable the FP rules that require TS. | |
'func/no-expression-statement': [2, { ignoreVoid: true }], | |
'func/no-return-void': 2, | |
'func/no-method-signature': 2, | |
'func/no-mixed-type': 2, | |
'func/prefer-tacit': 2, | |
// Import rules for import | |
...imprt.configs.typescript.rules, | |
'imprt/export': 0, // TypeScript compilation already ensures | |
'imprt/named': 0, // TypeScript compilation already ensures that named imports exist in the referenced module | |
'imprt/namespace': 0, // TypeScript compilation already ensures | |
'imprt/default': 0, // TypeScript compilation already ensures | |
'imprt/no-named-as-default-member': 1, | |
'imprt/no-unresolved': [2, { commonjs: true }], // required by eslint-import-resolver-typescript. | |
}, | |
}, | |
]; | |
// style, fmt, and controls - heavy weight, apply last | |
const summitConfig = [ | |
{ | |
linterOptions: { | |
noInlineConfig: false, | |
}, | |
plugins: { | |
comments, | |
func, | |
imprt, | |
}, | |
rules: { | |
// Require a short note when allowing inline config such as disabling a rule. | |
'comments/require-description': 2, | |
'comments/disable-enable-pair': 2, | |
'comments/no-aggregating-enable': 2, | |
'comments/no-duplicate-disable': 2, | |
'comments/no-unlimited-disable': 2, | |
'comments/no-unused-enable': 2, | |
// Function hoisting for readability | |
'no-use-before-define': [2, { functions: false, classes: true, variables: true }], | |
...func.configs.stylistic.rules, | |
// Import styling | |
'imprt/first': 1, | |
'imprt/exports-last': 0, // only works on es6 exports | |
'imprt/no-duplicates': 2, | |
'imprt/no-namespace': 1, | |
'imprt/extensions': [1, 'never'], | |
'imprt/order': [ | |
2, | |
{ | |
groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index', 'object', 'type'], | |
'newlines-between': 'always', | |
alphabetize: { order: 'asc' }, | |
}, | |
], | |
'imprt/newline-after-import': [1, { considerComments: true }], | |
'imprt/prefer-default-export': 0, | |
'imprt/no-default-export': 1, | |
'imprt/no-anonymous-default-export': 2, | |
'imprt/no-unassigned-import': 2, | |
'imprt/no-named-as-default': 1, | |
'imprt/no-named-export': 0, | |
'imprt/consistent-type-specifier-style': 1, | |
}, | |
}, | |
// TS & ES6 Modules overrides | |
{ | |
files: ['**/*{ts,tsx,mjs}'], | |
plugins: { | |
imprt, | |
}, | |
rules: { | |
'imprt/exports-last': 1, | |
}, | |
}, | |
// config file overrides | |
{ | |
files: ['**/*{.config,rc}.{cjs,mjs,js,ts,jsx,tsx}'], | |
plugins: { | |
func, | |
imprt, | |
}, | |
rules: { | |
'imprt/no-default-export': 0, | |
'func/no-expression-statement': 0, | |
'func/immutable-data': 0, | |
}, | |
}, | |
prettierConfig, | |
]; | |
const heavyConfig = [ | |
{ | |
files: JS_TS_FILES, | |
plugins: { | |
sonarjs, // works best with TS but does have to have it. | |
}, | |
rules: { | |
...sonarjs.configs.recommended.rules, | |
'sonarjs/cognitive-complexity': 0, | |
'sonarjs/prefer-immediate-return': 0, | |
}, | |
}, | |
{ | |
files: ['**/*{ts,tsx}'], | |
plugins: { | |
ts, | |
}, | |
rules: { | |
// this one is heavy, put it behind a condition | |
...ts.configs['recommended-requiring-type-checking'].rules, | |
}, | |
}, | |
]; | |
// Jest configuration | |
const testConfig = [ | |
{ | |
files: ['**/*.[spec,test].[tj]s?(x)'], | |
languageOptions: { | |
jest: true, | |
node: true, | |
browser: true, | |
}, | |
plugins: { testLib, jest, jestFmt }, | |
rules: { | |
...testLib.configs.react.rules, | |
...jest.configs.recommended.rules, | |
...jest.configs.style.rules, | |
...jestFmt.configs.strict.rules, | |
}, | |
}, | |
{ | |
// or whatever matches stories specified in .storybook/main.js | |
files: ['*.stories.@(ts|tsx|js|jsx|mjs|cjs)'], | |
plugins: { sb }, | |
rules: { | |
...sb.configs.recommended.rules, | |
...sb.configs.csf.rules, | |
...sb.configs['csf-strict'].rules, | |
}, | |
}, | |
]; | |
// NodeJS additions | |
const nodejsConfig = [ | |
{ | |
// some of this might not apply to jsx/tsx | |
files: JS_TS_FILES, | |
languageOptions: { | |
node: true, | |
}, | |
plugins: { | |
n, | |
sec, | |
imprt, | |
}, | |
rules: { | |
...n.configs.recommended.rules, | |
...sec.configs.recommended.rules, | |
'n/no-unsupported-features/es-syntax': [2, { ignores: ['modules'] }], | |
'n/no-unsupported-features/node-builtins': 0, | |
'n/no-missing-import': 2, | |
'no-path-concat': 2, | |
'no-process-exit': 2, | |
'no-sync': 1, | |
'imprt/no-nodejs-modules': 0, | |
}, | |
}, | |
{ | |
files: ['**/*.ts'], | |
plugins: { | |
n, | |
}, | |
rules: { | |
'n/no-missing-import': 0, // Typescript has this covered. | |
}, | |
}, | |
]; | |
const reactjsConfig = [ | |
{ | |
files: REACTJS_FILES, | |
plugins: { | |
react, | |
}, | |
languageOptions: { | |
node: true, | |
browser: 'true', | |
parser: tsParser, | |
parserOptions: { | |
ecmaFeatures: { modules: true, jsx: true }, | |
project: './tsconfig.json', | |
jsxPragma: null, | |
}, | |
}, | |
settings: { | |
react: { | |
version: 'detect', | |
}, | |
}, | |
rules: { | |
...react.configs.recommended.rules, | |
// https://github.com/jsx-eslint/eslint-plugin-react/blob/8cf47a8ac2242ee00ea36eac4b6ae51956ba4411/index.js#L165-L179 | |
...react.configs['jsx-runtime'].rules, | |
'react/prop-types': 0, | |
'react/no-unstable-nested-components': [2, { allowAsProps: false }], | |
'react/jsx-no-useless-fragment': [2, { allowExpressions: true }], | |
'react/jsx-filename-extension': [2, { extensions: ['.jsx', '.tsx'] }], | |
'react/jsx-boolean-value': 2, | |
'react/jsx-fragments': 2, | |
'react/destructuring-assignment': 2, | |
'react/no-multi-comp': 2, | |
'react/no-array-index-key': 2, | |
'react/jsx-props-no-spreading': 0, | |
'react/jsx-sort-props': [ | |
2, | |
{ | |
callbacksLast: true, | |
shorthandFirst: true, | |
shorthandLast: false, | |
ignoreCase: true, | |
noSortAlphabetically: true, | |
multiline: 'last', | |
reservedFirst: true, | |
}, | |
], | |
}, | |
}, | |
{ | |
files: REACTJS_FILES, | |
plugins: { | |
jsxa11y, | |
reactHooks, | |
}, | |
rules: [ | |
//...jsxa11y.configs.recommended.rules, | |
// ...reactHooks.configs.recommended.rules, | |
], | |
}, | |
]; | |
const nextjsConfig = [ | |
{ | |
files: ['apps/**/*{js,ts,jsx,tsx}'], | |
settings: { | |
next: { | |
rootDir: 'apps/*/', | |
}, | |
}, | |
plugins: { | |
'@next/next': nextjs, | |
}, | |
rules: { | |
...nextjs.configs.recommended.rules, | |
...nextjs.configs['core-web-vitals'].rules, | |
'@next/next/no-html-link-for-pages': 0, | |
'react/jsx-key': 0, | |
}, | |
}, | |
]; | |
export default [ | |
// module.exports = [ | |
...baseConfig, | |
...nodejsConfig, | |
...nextjsConfig, | |
...reactjsConfig, | |
...testConfig, | |
...(IS_HEAVY ? heavyConfig : []), | |
...summitConfig, | |
]; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment