UNPKG

eslint-config-niieani

Version:

Reusable ESLint config for Beemo configured projects.

309 lines (272 loc) 9.28 kB
/* eslint-disable import/no-commonjs,import/no-import-module-exports */ import type eslint from 'eslint' import {CASE_SENSITIVE} from '@niieani/scaffold-config-constants' const reactConfig: eslint.Linter.ConfigOverride = { files: ['*.tsx'], plugins: ['jsx-a11y', 'react', 'react-hooks', 'react-perf'], extends: [require.resolve('./browser.js'), 'plugin:jsx-a11y/recommended'], parserOptions: { ecmaFeatures: { jsx: true, }, }, settings: { react: { version: 'detect', }, linkComponents: ['Link', {name: 'Link', linkAttribute: 'to'}], }, rules: { // Conflicts with CSS-in-JS solutions 'sort-keys': 'off', // Disabled by Prettier: https://github.com/prettier/eslint-config-prettier/blob/main/index.js#L122 // We need to do this since React is an override and these might be re-enabled. 'react/jsx-child-element-spacing': 'off', 'react/jsx-closing-bracket-location': 'off', 'react/jsx-closing-tag-location': 'off', 'react/jsx-curly-newline': 'off', 'react/jsx-curly-spacing': 'off', 'react/jsx-equals-spacing': 'off', 'react/jsx-first-prop-new-line': 'off', 'react/jsx-indent': 'off', 'react/jsx-indent-props': 'off', 'react/jsx-max-props-per-line': 'off', 'react/jsx-newline': 'off', 'react/jsx-one-expression-per-line': 'off', 'react/jsx-props-no-multi-spaces': 'off', 'react/jsx-tag-spacing': 'off', 'react/jsx-wrap-multilines': 'off', // Support the new JSX runtime when available 'react/react-in-jsx-scope': 'off', 'react/jsx-uses-react': 'off', // Align with the DOM instead, avoid "is" prefix 'react/boolean-prop-naming': 'off', 'react/jsx-boolean-value': ['error', 'never'], // Destructure to avoid references 'react/destructuring-assignment': 'error', // Display name is inferred from function declarations 'react/display-name': 'off', // Always use function declarations for components 'react/function-component-definition': [ 'error', { namedComponents: 'function-declaration', unnamedComponents: 'arrow-function', }, ], 'react/prefer-es6-class': 'off', 'react/prefer-stateless-function': 'error', 'react/jsx-pascal-case': [ 'error', {allowAllCaps: true, allowNamespace: true}, ], // Allow multiple components in a file 'react/no-multi-comp': 'off', // Always self-close when applicable 'react/self-closing-comp': [ 'error', { component: true, html: true, }, ], 'react/void-dom-elements-no-children': 'error', // Allow component level state 'react/no-set-state': 'off', 'react/no-unused-state': 'error', // Use alternatives instead of danger 'react/no-danger': 'error', 'react/no-danger-with-children': 'error', // Avoid deprecated APIs 'react/no-deprecated': 'error', 'react/no-find-dom-node': 'error', 'react/no-is-mounted': 'error', 'react/no-unsafe': ['error', {checkAliases: true}], // Dont restrict prop/element usage (is an app concern) 'react/forbid-component-props': 'off', 'react/forbid-dom-props': 'off', 'react/forbid-elements': 'off', 'react/no-adjacent-inline-elements': 'off', // Dont use prop types since were using TypeScript 'react/default-props-match-prop-types': 'off', 'react/forbid-foreign-prop-types': 'off', 'react/forbid-prop-types': 'off', 'react/no-unused-prop-types': 'off', 'react/prefer-exact-props': 'off', 'react/prefer-read-only-props': 'off', 'react/prop-types': 'off', 'react/require-default-props': 'off', 'react/sort-prop-types': 'off', // Avoid bad or problematic patterns 'react/jsx-uses-vars': 'error', 'react/no-access-state-in-setstate': 'error', 'react/no-array-index-key': 'error', 'react/no-arrow-function-lifecycle': 'error', 'react/no-children-prop': 'error', 'react/no-did-mount-set-state': 'error', 'react/no-did-update-set-state': 'error', 'react/no-direct-mutation-state': 'error', 'react/no-namespace': 'error', 'react/no-redundant-should-component-update': 'error', 'react/no-render-return-value': 'error', 'react/no-string-refs': 'error', 'react/no-this-in-sfc': 'error', 'react/no-typos': 'error', 'react/no-unescaped-entities': 'error', 'react/no-unknown-property': 'error', 'react/no-unstable-nested-components': 'error', 'react/no-unused-class-component-methods': 'error', 'react/no-will-update-set-state': 'error', 'react/require-optimization': 'off', 'react/style-prop-object': 'error', // Accessibility requirements 'react/button-has-type': 'error', 'react/no-invalid-html-attribute': 'error', // Security requirements 'react/jsx-no-script-url': 'error', 'react/jsx-no-target-blank': 'error', // Performance requirements 'react/jsx-no-constructed-context-values': 'error', 'react-perf/jsx-no-jsx-as-prop': 'off', 'react-perf/jsx-no-new-array-as-prop': 'error', 'react-perf/jsx-no-new-function-as-prop': 'error', 'react-perf/jsx-no-new-object-as-prop': 'error', // Ensure JSX is returned 'react/require-render-return': 'error', // Initialization should happen anywhere 'react/state-in-constructor': 'off', // Enforce consistent JSX spacing and syntax 'react/jsx-no-comment-textnodes': 'error', 'react/jsx-no-duplicate-props': 'error', 'react/jsx-no-undef': 'error', 'react/jsx-space-before-closing': 'off', // Avoid interpolation as much as possible 'react/jsx-curly-brace-presence': [ 'error', {props: 'never', children: 'never'}, ], // Allow either extension for JSX code, since some components may return null 'react/jsx-filename-extension': [ 'error', {allow: 'as-needed', extensions: ['.tsx']}, ], // Always use shorthand fragments when applicable 'react/jsx-fragments': ['error', 'syntax'], 'react/jsx-no-useless-fragment': 'error', // Ensure keys are used correctly 'react/jsx-key': [ 'error', { checkFragmentShorthand: true, checkKeyMustBeforeSpread: true, }, ], // Allow any level of nesting 'react/jsx-max-depth': 'off', // Avoid inline function props 'react/jsx-no-bind': [ 'error', { ignoreDOMComponents: true, ignoreRefs: true, allowArrowFunctions: false, allowFunctions: false, allowBind: false, }, ], // Encourage the use of i18n libraries 'react/jsx-no-literals': [ 'error', { noStrings: true, ignoreProps: true, noAttributeStrings: false, }, ], // Allow spreading of props since it allows better composition, // and TypeScript will catch any issues regardless 'react/jsx-props-no-spreading': 'off', // Always sort props for better readability 'react/jsx-sort-default-props': 'off', // Handled by sort-keys 'react/jsx-sort-props': [ 'error', { callbacksLast: true, shorthandFirst: true, shorthandLast: false, ignoreCase: !CASE_SENSITIVE, noSortAlphabetically: false, reservedFirst: true, }, ], // Encourage on & handle event naming 'react/jsx-handler-names': [ 'error', { eventHandlerPrefix: 'handle', eventHandlerPropPrefix: 'on', }, ], // Sort component using React instead of TypeScript '@typescript-eslint/member-ordering': 'off', 'react/static-property-placement': 'error', 'react/sort-comp': [ 'error', { order: [ 'statics', 'properties', 'lifecycle', 'everything-else', 'handlers', 'renderers', ], groups: { statics: ['propTypes', 'defaultProps'], properties: [ '/^(?!on).+$/', '/^(?!handle).+$/', '/^(?!render).+$/', '/^.+Ref$/', 'state', ], lifecycle: [ 'constructor', 'getDerivedStateFromProps', 'componentDidMount', 'shouldComponentUpdate', 'getSnapshotBeforeUpdate', 'componentDidUpdate', 'componentDidCatch', 'componentWillUnmount', ], handlers: ['/^on.+$/', '/^handle.+$/'], renderers: ['/^render.+$/', 'render'], }, }, ], }, } const hooksConfig: eslint.Linter.ConfigOverride = { files: ['*.ts', '*.tsx'], plugins: ['react-hooks'], rules: { 'react-hooks/rules-of-hooks': 'error', 'react-hooks/exhaustive-deps': 'error', }, } const testsConfig: eslint.Linter.ConfigOverride = { files: ['*.test.tsx'], rules: { 'react/jsx-no-bind': 'off', 'react-perf/jsx-no-new-array-as-prop': 'off', 'react-perf/jsx-no-new-function-as-prop': 'off', 'react-perf/jsx-no-new-object-as-prop': 'off', }, } // We only want to apply the React plugin and rules // to TSX files. Not the entire codebase. const config: eslint.Linter.Config = { overrides: [reactConfig, hooksConfig, testsConfig], } module.exports = config