README
eslint-config-torchbox
Shareable ESLint config following Torchbox’s code style.
Usage
Install the config along with its peer dependencies:
# npm v7:
npm install --save-dev eslint-config-torchbox@latest
# npm v6 and below:
npx install-peerdeps --dev eslint-config-torchbox@latest
Then configure ESLint to use this config. As a .eslintrc.js
in the root of your project:
module.exports = {
// See https://github.com/torchbox/eslint-config-torchbox for rules.
extends: 'torchbox',
};
Tips
- Use ESLint’s
--report-unused-disable-directives
flag to ensure you do not use moreeslint-disable
comments than needed. - This config is Prettier-compatible, but it is still usable by projects which do not wish to use Prettier.
- We recommend using a
.eslintignore
so ESLint can be targeted at all files, with a blacklist of files to ignore. - If relevant, use ESLint’s
overrides
feature to make it more permissive for certain files – for example Storybook stories or unit tests, where code standards are different.
Here’s a package.json run
script and an ignore file to get you started:
"lint:js": "eslint --report-unused-disable-directives .",
.eslintignore
(adapt to your project):
.git
node_modules
coverage
static_compiled
venv
Note the point of the ignore file isn’t just to determine which JS files we don’t want to be linted, but also speed up linting by excluding large folders.
Here is an example of using overrides
to disable a few specific rules for stories:
module.exports = {
// […]
overrides: [
{
files: ['*.stories.tsx'],
rules: {
// Don’t mandate typing for Storybook stories.
'@typescript-eslint/explicit-module-boundary-types': 0,
'@typescript-eslint/explicit-function-return-type': 0,
},
},
],
};
TypeScript
This config doesn’t include TypeScript support out of the box. We can install and configure a TypeScript parser and ESLint plugin to make it compatible. Here is how to proceed:
npm install --save-dev @typescript-eslint/parser @typescript-eslint/eslint-plugin
And update your ESLint configuration to:
module.exports = {
// See https://github.com/torchbox/eslint-config-torchbox for rules.
extends: 'torchbox',
// Using Babel parser for experimental syntax
parser: 'babel-eslint',
overrides: [
{
files: ['*.ts', '*.tsx'],
extends: [
'torchbox',
'plugin:import/typescript',
'plugin:@typescript-eslint/eslint-recommended',
'plugin:@typescript-eslint/recommended',
],
parser: '@typescript-eslint/parser',
plugins: ['@typescript-eslint'],
rules: {
'react/jsx-filename-extension': [
2,
// Allow mixed JSX in JavaScript files.
{ extensions: ['.js', '.tsx'] },
],
'import/extensions': [
2,
'always',
{
ignorePackages: true,
pattern: {
js: 'never',
jsx: 'never',
ts: 'never',
tsx: 'never',
},
},
],
},
},
],
};
As of ESLint v6, you will also need to tell ESLint to parse TypeScript files with the --ext
flag:
eslint --report-unused-disable-directives --ext .js,.jsx,.ts,.tsx .
Note that the TypeScript-friendly rules included in the config above aren’t as strict as our baseline config. To bridge this gap, consider using --max-warnings 0
to treat all warnings as errors:
eslint --max-warnings 0 --report-unused-disable-directives --ext .js,.jsx,.ts,.tsx .
React
This config is meant first and foremost for React projects, where it will detect which rules to apply based on the version of React used on the project. The config can also be used on non-React projects – just make sure to disable the version check by adding: the following in your config:
module.exports = {
// [...]
settings: {
// Manually set the version to disable automated detection of the "react" dependency.
react: { version: 'latest' },
},
};
Vue
We do not include linting for Vue out of the box. Have a look at the eslint-plugin-vue user guide, in particular:
- Choose the right predefined configuration based on the Vue version and desired level of strictness
- Use the
--ext
flag to lint.vue
files. - Make sure pre-commit hooks are configured to run ESLint on
.vue
files. - If using a custom parser (for example TypeScript’s), make sure to set it up alongside
vue-eslint-parser
as documented.
Experimental syntax
By default, this config uses ESLint’s built-in parser, which doesn’t support experimental ECMAScript features. If your code uses experimental syntax transpiled with Babel, make sure to set the ESLint parser to babel-eslint:
module.exports = {
// See https://github.com/torchbox/eslint-config-torchbox for rules.
extends: 'torchbox',
// Support non-standard, experimental JS features that Babel knows how to process.
parser: 'babel-eslint',
};
What’s included
See
config.js
for the config definition, andsemver.test.js.snap
for the whole set of rules and settings.
Extends
Custom rules
- no-param-reassign:
2, props: false
- no-warning-comments:
1, terms: todo, fixme, xxx, location: start
- no-underscore-dangle:
2, allow: __REDUX_DEVTOOLS_EXTENSION__, __webpack_hash__, al…
- camelcase:
0
- no-console:
2, allow: error
- react/forbid-prop-types:
2, forbid: any
- react/jsx-no-bind:
0
- react/jsx-filename-extension:
2, extensions: .js
- react/prefer-exact-props:
0
- react/function-component-definition:
0
- import/no-extraneous-dependencies:
2, devDependencies: **/tests/**, **/stories/**, **/storybook…
- import/prefer-default-export:
0
Inherited rules
- react-hooks/rules-of-hooks
- react-hooks/exhaustive-deps
- jsx-a11y/alt-text:
error, elements: img, object, area, inputtype=\image\, img: …
- jsx-a11y/anchor-has-content:
error, components:
- jsx-a11y/anchor-is-valid:
error, components: Link, specialLink: to, aspects: noHref, i…
- jsx-a11y/aria-activedescendant-has-tabindex
- jsx-a11y/aria-props
- jsx-a11y/aria-proptypes
- jsx-a11y/aria-role:
error, ignoreNonDOM: false
- jsx-a11y/aria-unsupported-elements
- jsx-a11y/click-events-have-key-events
- jsx-a11y/control-has-associated-label:
error, labelAttributes: label, controlComponents: , ignoreEl…
- jsx-a11y/heading-has-content:
error, components:
- jsx-a11y/html-has-lang
- jsx-a11y/iframe-has-title
- jsx-a11y/img-redundant-alt
- jsx-a11y/interactive-supports-focus
- jsx-a11y/label-has-associated-control:
error, labelComponents: , labelAttributes: , controlComponen…
- jsx-a11y/lang
- jsx-a11y/media-has-caption:
error, audio: , video: , track:
- jsx-a11y/mouse-events-have-key-events
- jsx-a11y/no-access-key
- jsx-a11y/no-autofocus:
error, ignoreNonDOM: true
- jsx-a11y/no-distracting-elements:
error, elements: marquee, blink
- jsx-a11y/no-interactive-element-to-noninteractive-role:
error, tr: none, presentation
- jsx-a11y/no-noninteractive-element-interactions:
error, handlers: onClick, onMouseDown, onMouseUp, onKeyPress…
- jsx-a11y/no-noninteractive-element-to-interactive-role:
error, ul: listbox, menu, menubar, radiogroup, tablist, tree…
- jsx-a11y/no-noninteractive-tabindex:
error, tags: , roles: tabpanel
- jsx-a11y/no-redundant-roles
- jsx-a11y/no-static-element-interactions:
error, handlers: onClick, onMouseDown, onMouseUp, onKeyPress…
- jsx-a11y/role-has-required-aria-props
- jsx-a11y/role-supports-aria-props
- jsx-a11y/scope
- jsx-a11y/tabindex-no-positive
- class-methods-use-this:
error, exceptMethods: render, getInitialState, getDefaultPro…
- react/jsx-boolean-value:
error, never, always:
- react/jsx-no-duplicate-props:
error, ignoreCase: true
- react/jsx-no-undef
- react/jsx-pascal-case:
error, allowAllCaps: true, ignore:
- react/jsx-uses-react
- react/jsx-uses-vars
- react/no-danger:
warn
- react/no-deprecated
- react/no-did-update-set-state
- react/no-will-update-set-state
- react/no-is-mounted
- react/no-string-refs
- react/no-unknown-property
- react/prefer-es6-class:
error, always
- react/prefer-stateless-function:
error, ignorePureComponents: true
- react/prop-types:
error, ignore: , customValidators: , skipUndeclared: false
- react/react-in-jsx-scope
- react/require-render-return
- react/self-closing-comp
- react/sort-comp:
error, order: static-variables, static-methods, instance-var…
- react/jsx-no-target-blank:
error, enforceDynamicLinks: always, links: true, forms: false
- react/jsx-no-comment-textnodes
- react/no-render-return-value
- react/no-find-dom-node
- react/no-danger-with-children
- react/no-unused-prop-types:
error, customValidators: , skipShapeProps: true
- react/style-prop-object
- react/no-unescaped-entities
- react/no-children-prop
- react/no-array-index-key
- react/require-default-props:
error, forbidDefaultForRequired: true
- react/forbid-foreign-prop-types:
warn, allowInPropTypes: true
- react/void-dom-elements-no-children
- react/default-props-match-prop-types:
error, allowRequiredDefaults: false
- react/no-redundant-should-component-update
- react/no-unused-state
- react/no-typos
- react/jsx-curly-brace-presence:
error, props: never, children: never
- react/destructuring-assignment:
error, always
- react/no-access-state-in-setstate
- react/button-has-type:
error, button: true, submit: true, reset: false
- react/no-this-in-sfc
- react/jsx-fragments:
error, syntax
- react/state-in-constructor:
error, always
- react/static-property-placement:
error, property assignment
- react/jsx-props-no-spreading:
error, html: enforce, custom: enforce, explicitSpread: ignor…
- react/jsx-no-script-url:
error, name: Link, props: to
- react/jsx-no-useless-fragment
- react/jsx-no-constructed-context-values
- react/no-unstable-nested-components
- react/no-namespace
- react/no-arrow-function-lifecycle
- react/no-invalid-html-attribute
- react/no-unused-class-component-methods
- strict:
error, never
- import/no-unresolved:
error, commonjs: true, caseSensitive: true, caseSensitiveStrict: false
- import/named
- import/export
- import/no-named-as-default
- import/no-named-as-default-member
- import/no-mutable-exports
- import/no-amd
- import/first
- import/no-duplicates
- import/extensions:
error, ignorePackages, js: never, mjs: never, jsx: never
- import/order:
error, groups: builtin, external, internal, warnOnUnassigned…
- import/newline-after-import
- import/no-absolute-path
- import/no-dynamic-require
- import/no-webpack-loader-syntax
- import/no-named-default
- import/no-self-import
- import/no-cycle:
error, maxDepth: ∞, ignoreExternal: false
- import/no-useless-path-segments:
error, commonjs: true
- import/no-import-module-exports:
error, exceptions:
- import/no-relative-packages
- arrow-body-style:
error, as-needed, requireReturnForObjectLiteral: false
- constructor-super
- no-class-assign
- no-const-assign
- no-dupe-class-members
- no-new-symbol
- no-restricted-exports:
error, restrictedNamedExports: default, then
- no-this-before-super
- no-useless-computed-key
- no-useless-constructor
- no-useless-rename:
error, ignoreDestructuring: false, ignoreImport: false, igno…
- no-var
- object-shorthand:
error, always, ignoreConstructors: false, avoidQuotes: true
- prefer-arrow-callback:
error, allowNamedFunctions: false, allowUnboundThis: true
- prefer-const:
error, destructuring: any, ignoreReadBeforeAssign: true
- prefer-destructuring:
error, VariableDeclarator: array: false, object: true, Assig…
- prefer-numeric-literals
- prefer-rest-params
- prefer-spread
- prefer-template
- require-yield
- symbol-description
- no-delete-var
- no-label-var
- no-restricted-globals:
error, name: isFinite, message: Use Number.isFinite instead …
- no-shadow
- no-shadow-restricted-names
- no-undef
- no-undef-init
- no-unused-vars:
error, vars: all, args: after-used, ignoreRestSiblings: true
- no-use-before-define:
error, functions: true, classes: true, variables: true
- func-names:
warn
- lines-between-class-members:
error, always, exceptAfterSingleLine: false
- lines-around-directive:
error, before: always, after: always
- new-cap:
error, newIsCap: true, newIsCapExceptions: , capIsNew: false…
- no-array-constructor
- no-bitwise
- no-continue
- no-lonely-if
- no-multi-assign
- no-nested-ternary
- no-new-object
- no-plusplus
- no-restricted-syntax:
error, selector: ForInStatement, message: for..in loops iter…
- no-unneeded-ternary:
error, defaultAssignment: false
- one-var:
error, never
- operator-assignment:
error, always
- prefer-exponentiation-operator
- prefer-object-spread
- spaced-comment:
error, always, line: exceptions: -, +, markers: =, !, /, blo…
- global-require
- no-buffer-constructor
- no-new-require
- no-path-concat
- for-direction
- getter-return:
error, allowImplicit: true
- no-async-promise-executor
- no-await-in-loop
- no-compare-neg-zero
- no-cond-assign:
error, always
- no-constant-condition:
warn
- no-control-regex
- no-debugger
- no-dupe-args
- no-dupe-else-if
- no-dupe-keys
- no-duplicate-case
- no-empty
- no-empty-character-class
- no-ex-assign
- no-extra-boolean-cast
- no-func-assign
- no-import-assign
- no-inner-declarations
- no-invalid-regexp
- no-irregular-whitespace
- no-loss-of-precision
- no-misleading-character-class
- no-obj-calls
- no-promise-executor-return
- no-prototype-builtins
- no-regex-spaces
- no-setter-return
- no-sparse-arrays
- no-template-curly-in-string
- no-unreachable
- no-unreachable-loop:
error, ignore:
- no-unsafe-finally
- no-unsafe-negation
- no-unsafe-optional-chaining:
error, disallowArithmeticOperators: true
- no-useless-backreference
- use-isnan
- valid-typeof:
error, requireStringLiterals: true
- array-callback-return:
error, allowImplicit: true, checkForEach: false
- block-scoped-var
- consistent-return
- default-case:
error, commentPattern: ^no default$
- default-case-last
- default-param-last
- dot-notation:
error, allowKeywords: true, allowPattern:
- eqeqeq:
error, always, null: ignore
- grouped-accessor-pairs
- guard-for-in
- max-classes-per-file:
error, 1
- no-alert:
warn
- no-caller
- no-case-declarations
- no-constructor-return
- no-else-return:
error, allowElseIf: false
- no-empty-function:
error, allow: arrowFunctions, functions, methods
- no-empty-pattern
- no-eval
- no-extend-native
- no-extra-bind
- no-extra-label
- no-fallthrough
- no-global-assign:
error, exceptions:
- no-implied-eval
- no-iterator
- no-labels:
error, allowLoop: false, allowSwitch: false
- no-lone-blocks
- no-loop-func
- no-multi-str
- no-new
- no-new-func
- no-new-wrappers
- no-nonoctal-decimal-escape
- no-octal
- no-octal-escape
- no-proto
- no-redeclare
- no-restricted-properties:
error, object: arguments, property: callee, message: argumen…
- no-return-assign:
error, always
- no-return-await
- no-script-url
- no-self-assign:
error, props: true
- no-self-compare
- no-sequences
- no-throw-literal
- no-unused-expressions:
error, allowShortCircuit: false, allowTernary: false, allowT…
- no-unused-labels
- no-useless-catch
- no-useless-concat
- no-useless-escape
- no-useless-return
- no-void
- no-with
- prefer-promise-reject-errors:
error, allowEmptyReject: true
- prefer-regex-literals:
error, disallowRedundantWrapping: true
- radix
- vars-on-top
- yoda
Contributing
See the contribution guidelines for guidance and setup instructions.