diff --git a/.editorconfig b/.editorconfig index 49c154da45..db6f34c77c 100644 --- a/.editorconfig +++ b/.editorconfig @@ -8,4 +8,7 @@ trim_trailing_whitespace = true insert_final_newline = true end_of_line = lf # editorconfig-tools is unable to ignore longs strings or urls -max_line_length = null +max_line_length = off + +[CHANGELOG.md] +indent_size = false diff --git a/.travis.yml b/.travis.yml index 7d887ca685..df4ad7cf56 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,41 +1,52 @@ language: node_js node_js: + - "12" + - "11" + - "10" + - "9" - "8" - - "7" - - "6" - - "5" - - "4" before_install: - - 'if [ "${TRAVIS_NODE_VERSION}" = "0.6" ]; then npm install -g npm@1.3 ; elif [ "${TRAVIS_NODE_VERSION}" != "0.9" ]; then case "$(npm --version)" in 1.*) npm install -g npm@1.4.28 ;; 2.*) npm install -g npm@2 ;; esac ; fi' - - 'if [ "${TRAVIS_NODE_VERSION%${TRAVIS_NODE_VERSION#[0-9]}}" = "0" ] || [ "${TRAVIS_NODE_VERSION:0:4}" = "iojs" ]; then npm install -g npm@4.5 ; elif [ "${TRAVIS_NODE_VERSION}" != "0.6" ] && [ "${TRAVIS_NODE_VERSION}" != "0.9" ]; then npm install -g npm; fi' + - 'nvm install-latest-npm' install: - - 'cd "packages/${PACKAGE}"' - - 'if [ "${TRAVIS_NODE_VERSION}" = "0.6" ]; then nvm install 0.8 && npm install -g npm@1.3 && npm install -g npm@1.4.28 && npm install -g npm@2 && npm install && nvm use "${TRAVIS_NODE_VERSION}"; else npm install; fi;' + - 'if [ -n "${PACKAGE-}" ]; then cd "packages/${PACKAGE}"; fi' + - 'if [ "${TRAVIS_NODE_VERSION}" = "0.6" ] || [ "${TRAVIS_NODE_VERSION}" = "0.9" ]; then nvm install --latest-npm 0.8 && npm install && nvm use "${TRAVIS_NODE_VERSION}"; else npm install; fi;' - 'if [ -n "${ESLINT}" ]; then npm install --no-save "eslint@${ESLINT}"; fi' script: - - 'if [ -n "${PREPUBLISH-}" ]; then npm run pretravis && npm run prepublish && npm run posttravis; else npm run travis; fi' + - 'if [ -n "${PREPUBLISH-}" ]; then npm run pretravis && npm run prepublish && npm run posttravis; elif [ -n "${LINT-}" ]; then npm run lint; else npm run travis; fi' sudo: false env: matrix: - - 'TEST=true ESLINT=3 PACKAGE=eslint-config-airbnb' - - 'TEST=true ESLINT=4 PACKAGE=eslint-config-airbnb' - - 'TEST=true ESLINT=3 PACKAGE=eslint-config-airbnb-base' - - 'TEST=true ESLINT=4 PACKAGE=eslint-config-airbnb-base' + - 'TEST=true ESLINT=6 PACKAGE=eslint-config-airbnb-base' + - 'TEST=true ESLINT=6 PACKAGE=eslint-config-airbnb' + - 'TEST=true ESLINT=5 PACKAGE=eslint-config-airbnb-base' + - 'TEST=true ESLINT=5 PACKAGE=eslint-config-airbnb' matrix: fast_finish: true include: - - node_js: "node" - env: PREPUBLISH=true ESLINT=3 PACKAGE=eslint-config-airbnb - - node_js: "node" - env: PREPUBLISH=true ESLINT=4 PACKAGE=eslint-config-airbnb - - node_js: "node" - env: PREPUBLISH=true ESLINT=3 PACKAGE=eslint-config-airbnb-base - - node_js: "node" - env: PREPUBLISH=true ESLINT=4 PACKAGE=eslint-config-airbnb-base + - node_js: "lts/*" + env: PREPUBLISH=true ESLINT=6 PACKAGE=eslint-config-airbnb-base + - node_js: "lts/*" + env: PREPUBLISH=true ESLINT=6 PACKAGE=eslint-config-airbnb + - node_js: "lts/*" + env: PREPUBLISH=true ESLINT=5 PACKAGE=eslint-config-airbnb-base + - node_js: "lts/*" + env: PREPUBLISH=true ESLINT=5 PACKAGE=eslint-config-airbnb + - node_js: "lts/*" + env: LINT=true + - node_js: "7" + env: TEST=true ESLINT=5 PACKAGE=eslint-config-airbnb-base + - node_js: "7" + env: TEST=true ESLINT=5 PACKAGE=eslint-config-airbnb + - node_js: "6" + env: TEST=true ESLINT=5 PACKAGE=eslint-config-airbnb-base + - node_js: "6" + env: TEST=true ESLINT=5 PACKAGE=eslint-config-airbnb + exclude: allow_failures: + - node_js: "11" + - node_js: "9" - node_js: "7" - - node_js: "5" - - env: PREPUBLISH=true ESLINT=3 PACKAGE=eslint-config-airbnb - - env: PREPUBLISH=true ESLINT=4 PACKAGE=eslint-config-airbnb - - env: PREPUBLISH=true ESLINT=3 PACKAGE=eslint-config-airbnb-base - - env: PREPUBLISH=true ESLINT=4 PACKAGE=eslint-config-airbnb-base + - env: PREPUBLISH=true ESLINT=6 PACKAGE=eslint-config-airbnb-base + - env: PREPUBLISH=true ESLINT=6 PACKAGE=eslint-config-airbnb + - env: PREPUBLISH=true ESLINT=5 PACKAGE=eslint-config-airbnb-base + - env: PREPUBLISH=true ESLINT=5 PACKAGE=eslint-config-airbnb diff --git a/LICENSE.md b/LICENSE.md index 2867dece2b..69d80c0252 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2017 Airbnb +Copyright (c) 2012 Airbnb Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index a48ecbcdc8..e8fe129590 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,14 @@ *A mostly reasonable approach to JavaScript (based on the [AirBnB style guide.](https://github.com/airbnb/javascript) thank you AirBnB!)* +> **Note**: this guide assumes you are using [Babel](https://babeljs.io), and requires that you use [babel-preset-airbnb](https://npmjs.com/babel-preset-airbnb) or the equivalent. It also assumes you are installing shims/polyfills in your app, with [airbnb-browser-shims](https://npmjs.com/airbnb-browser-shims) or the equivalent. + [![Downloads](https://img.shields.io/npm/dm/eslint-config-airbnb.svg)](https://www.npmjs.com/package/eslint-config-airbnb) [![Downloads](https://img.shields.io/npm/dm/eslint-config-airbnb-base.svg)](https://www.npmjs.com/package/eslint-config-airbnb-base) [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/airbnb/javascript?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) +This guide is available in other languages too. See [Translation](#translation) + Other Style Guides - [ES5 (Deprecated)](https://github.com/airbnb/javascript/tree/es5-deprecated/es5) @@ -44,6 +48,7 @@ Other Style Guides 1. [jQuery](#jquery) 1. [ECMAScript 5 Compatibility](#ecmascript-5-compatibility) 1. [ECMAScript 6+ (ES 2015+) Styles](#ecmascript-6-es-2015-styles) + 1. [Standard Library](#standard-library) 1. [Testing](#testing) 1. [Performance](#performance) 1. [Resources](#resources) @@ -53,6 +58,7 @@ Other Style Guides 1. [Chat With Us About JavaScript](#chat-with-us-about-javascript) 1. [Contributors](#contributors) 1. [License](#license) + 1. [Amendments](#amendments) ## Types @@ -64,6 +70,7 @@ Other Style Guides - `boolean` - `null` - `undefined` + - `symbol` ```javascript const foo = 1; @@ -74,6 +81,8 @@ Other Style Guides console.log(foo, bar); // => 1, 9 ``` + - Symbols cannot be faithfully polyfilled, so they should not be used when targeting browsers/environments that don’t support them natively. + - [1.2](#types--complex) **Complex**: When you access a complex type you work on a reference to its value. @@ -95,7 +104,7 @@ Other Style Guides ## References - - [2.1](#references--prefer-const) Use `const` for all of your references; avoid using `var`. eslint: [`prefer-const`](http://eslint.org/docs/rules/prefer-const.html), [`no-const-assign`](http://eslint.org/docs/rules/no-const-assign.html) + - [2.1](#references--prefer-const) Use `const` for all of your references; avoid using `var`. eslint: [`prefer-const`](https://eslint.org/docs/rules/prefer-const.html), [`no-const-assign`](https://eslint.org/docs/rules/no-const-assign.html) > Why? This ensures that you can’t reassign your references, which can lead to bugs and difficult to comprehend code. @@ -110,7 +119,7 @@ Other Style Guides ``` - - [2.2](#references--disallow-var) If you must reassign references, use `let` instead of `var`. eslint: [`no-var`](http://eslint.org/docs/rules/no-var.html) jscs: [`disallowVar`](http://jscs.info/rule/disallowVar) + - [2.2](#references--disallow-var) If you must reassign references, use `let` instead of `var`. eslint: [`no-var`](https://eslint.org/docs/rules/no-var.html) > Why? `let` is block-scoped rather than function-scoped like `var`. @@ -146,7 +155,7 @@ Other Style Guides ## Objects - - [3.1](#objects--no-new) Use the literal syntax for object creation. eslint: [`no-new-object`](http://eslint.org/docs/rules/no-new-object.html) + - [3.1](#objects--no-new) Use the literal syntax for object creation. eslint: [`no-new-object`](https://eslint.org/docs/rules/no-new-object.html) ```javascript // bad @@ -183,7 +192,7 @@ Other Style Guides ``` - - [3.3](#es6-object-shorthand) Use object method shorthand. eslint: [`object-shorthand`](http://eslint.org/docs/rules/object-shorthand.html) jscs: [`requireEnhancedObjectLiterals`](http://jscs.info/rule/requireEnhancedObjectLiterals) + - [3.3](#es6-object-shorthand) Use object method shorthand. eslint: [`object-shorthand`](https://eslint.org/docs/rules/object-shorthand.html) ```javascript // bad @@ -206,9 +215,9 @@ Other Style Guides ``` - - [3.4](#es6-object-concise) Use property value shorthand. eslint: [`object-shorthand`](http://eslint.org/docs/rules/object-shorthand.html) jscs: [`requireEnhancedObjectLiterals`](http://jscs.info/rule/requireEnhancedObjectLiterals) + - [3.4](#es6-object-concise) Use property value shorthand. eslint: [`object-shorthand`](https://eslint.org/docs/rules/object-shorthand.html) - > Why? It is shorter to write and descriptive. + > Why? It is shorter and descriptive. ```javascript const lukeSkywalker = 'Luke Skywalker'; @@ -255,7 +264,7 @@ Other Style Guides ``` - - [3.6](#objects--quoted-props) Only quote properties that are invalid identifiers. eslint: [`quote-props`](http://eslint.org/docs/rules/quote-props.html) jscs: [`disallowQuotedKeysInObjects`](http://jscs.info/rule/disallowQuotedKeysInObjects) + - [3.6](#objects--quoted-props) Only quote properties that are invalid identifiers. eslint: [`quote-props`](https://eslint.org/docs/rules/quote-props.html) > Why? In general we consider it subjectively easier to read. It improves syntax highlighting, and is also more easily optimized by many JS engines. @@ -276,7 +285,7 @@ Other Style Guides ``` - - [3.7](#objects--prototype-builtins) Do not call `Object.prototype` methods directly, such as `hasOwnProperty`, `propertyIsEnumerable`, and `isPrototypeOf`. + - [3.7](#objects--prototype-builtins) Do not call `Object.prototype` methods directly, such as `hasOwnProperty`, `propertyIsEnumerable`, and `isPrototypeOf`. eslint: [`no-prototype-builtins`](https://eslint.org/docs/rules/no-prototype-builtins) > Why? These methods may be shadowed by properties on the object in question - consider `{ hasOwnProperty: false }` - or, the object may be a null object (`Object.create(null)`). @@ -289,10 +298,10 @@ Other Style Guides // best const has = Object.prototype.hasOwnProperty; // cache the lookup once, in module scope. - /* or */ - import has from 'has'; - // ... console.log(has.call(object, key)); + /* or */ + import has from 'has'; // https://www.npmjs.com/package/has + console.log(has(object, key)); ``` @@ -320,7 +329,7 @@ Other Style Guides ## Arrays - - [4.1](#arrays--literals) Use the literal syntax for array creation. eslint: [`no-array-constructor`](http://eslint.org/docs/rules/no-array-constructor.html) + - [4.1](#arrays--literals) Use the literal syntax for array creation. eslint: [`no-array-constructor`](https://eslint.org/docs/rules/no-array-constructor.html) ```javascript // bad @@ -360,16 +369,46 @@ Other Style Guides const itemsCopy = [...items]; ``` - - - [4.4](#arrays--from) To convert an array-like object to an array, use [Array.from](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/from). + + + - [4.4](#arrays--from-iterable) To convert an iterable object to an array, use spreads `...` instead of [`Array.from`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/from). ```javascript const foo = document.querySelectorAll('.foo'); + + // good const nodes = Array.from(foo); + + // best + const nodes = [...foo]; + ``` + + + - [4.5](#arrays--from-array-like) Use [`Array.from`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/from) for converting an array-like object to an array. + + ```javascript + const arrLike = { 0: 'foo', 1: 'bar', 2: 'baz', length: 3 }; + + // bad + const arr = Array.prototype.slice.call(arrLike); + + // good + const arr = Array.from(arrLike); + ``` + + + - [4.6](#arrays--mapping) Use [`Array.from`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/from) instead of spread `...` for mapping over iterables, because it avoids creating an intermediate array. + + ```javascript + // bad + const baz = [...foo].map(bar); + + // good + const baz = Array.from(foo, bar); ``` - - [4.5](#arrays--callback-return) Use return statements in array method callbacks. It’s ok to omit the return if the function body consists of a single statement returning an expression without side effects, following [8.2](#arrows--implicit-return). eslint: [`array-callback-return`](http://eslint.org/docs/rules/array-callback-return) + - [4.7](#arrays--callback-return) Use return statements in array method callbacks. It’s ok to omit the return if the function body consists of a single statement returning an expression without side effects, following [8.2](#arrows--implicit-return). eslint: [`array-callback-return`](https://eslint.org/docs/rules/array-callback-return) ```javascript // good @@ -379,20 +418,16 @@ Other Style Guides }); // good - [1, 2, 3].map(x => x + 1); + [1, 2, 3].map((x) => x + 1); - // bad - const flat = {}; - [[0, 1], [2, 3], [4, 5]].reduce((memo, item, index) => { - const flatten = memo.concat(item); - flat[index] = flatten; + // bad - no returned value means `acc` becomes undefined after the first iteration + [[0, 1], [2, 3], [4, 5]].reduce((acc, item, index) => { + const flatten = acc.concat(item); }); // good - const flat = {}; - [[0, 1], [2, 3], [4, 5]].reduce((memo, item, index) => { - const flatten = memo.concat(item); - flat[index] = flatten; + [[0, 1], [2, 3], [4, 5]].reduce((acc, item, index) => { + const flatten = acc.concat(item); return flatten; }); @@ -417,49 +452,49 @@ Other Style Guides }); ``` - - - [4.6](#arrays--bracket-newline) Use line breaks after open and before close array brackets if an array has multiple lines + + - [4.8](#arrays--bracket-newline) Use line breaks after open and before close array brackets if an array has multiple lines - ```javascript - // bad - const arr = [ - [0, 1], [2, 3], [4, 5], - ]; + ```javascript + // bad + const arr = [ + [0, 1], [2, 3], [4, 5], + ]; - const objectInArray = [{ - id: 1, - }, { - id: 2, - }]; + const objectInArray = [{ + id: 1, + }, { + id: 2, + }]; - const numberInArray = [ - 1, 2, - ]; + const numberInArray = [ + 1, 2, + ]; - // good - const arr = [[0, 1], [2, 3], [4, 5]]; + // good + const arr = [[0, 1], [2, 3], [4, 5]]; - const objectInArray = [ - { - id: 1, - }, - { - id: 2, - }, - ]; + const objectInArray = [ + { + id: 1, + }, + { + id: 2, + }, + ]; - const numberInArray = [ - 1, - 2, - ]; - ``` + const numberInArray = [ + 1, + 2, + ]; + ``` **[⬆ back to top](#table-of-contents)** ## Destructuring - - [5.1](#destructuring--object) Use object destructuring when accessing and using multiple properties of an object. jscs: [`requireObjectDestructuring`](http://jscs.info/rule/requireObjectDestructuring) + - [5.1](#destructuring--object) Use object destructuring when accessing and using multiple properties of an object. eslint: [`prefer-destructuring`](https://eslint.org/docs/rules/prefer-destructuring) > Why? Destructuring saves you from creating temporary references for those properties. @@ -485,7 +520,7 @@ Other Style Guides ``` - - [5.2](#destructuring--array) Use array destructuring. jscs: [`requireArrayDestructuring`](http://jscs.info/rule/requireArrayDestructuring) + - [5.2](#destructuring--array) Use array destructuring. eslint: [`prefer-destructuring`](https://eslint.org/docs/rules/prefer-destructuring) ```javascript const arr = [1, 2, 3, 4]; @@ -499,7 +534,7 @@ Other Style Guides ``` - - [5.3](#destructuring--object-over-array) Use object destructuring for multiple return values, not array destructuring. jscs: [`disallowArrayDestructuringReturn`](http://jscs.info/rule/disallowArrayDestructuringReturn) + - [5.3](#destructuring--object-over-array) Use object destructuring for multiple return values, not array destructuring. > Why? You can add new properties over time or change the order of things without breaking call sites. @@ -528,7 +563,7 @@ Other Style Guides ## Strings - - [6.1](#strings--quotes) Use single quotes `''` for strings. eslint: [`quotes`](http://eslint.org/docs/rules/quotes.html) jscs: [`validateQuoteMarks`](http://jscs.info/rule/validateQuoteMarks) + - [6.1](#strings--quotes) Use single quotes `''` for strings. eslint: [`quotes`](https://eslint.org/docs/rules/quotes.html) ```javascript // bad @@ -563,7 +598,7 @@ Other Style Guides ``` - - [6.3](#es6-template-literals) When programmatically building up strings, use template strings instead of concatenation. eslint: [`prefer-template`](http://eslint.org/docs/rules/prefer-template.html) [`template-curly-spacing`](http://eslint.org/docs/rules/template-curly-spacing) jscs: [`requireTemplateStrings`](http://jscs.info/rule/requireTemplateStrings) + - [6.3](#es6-template-literals) When programmatically building up strings, use template strings instead of concatenation. eslint: [`prefer-template`](https://eslint.org/docs/rules/prefer-template.html) [`template-curly-spacing`](https://eslint.org/docs/rules/template-curly-spacing) > Why? Template strings give you a readable, concise syntax with proper newlines and string interpolation features. @@ -590,10 +625,10 @@ Other Style Guides ``` - - [6.4](#strings--eval) Never use `eval()` on a string, it opens too many vulnerabilities. eslint: [`no-eval`](http://eslint.org/docs/rules/no-eval) + - [6.4](#strings--eval) Never use `eval()` on a string, it opens too many vulnerabilities. eslint: [`no-eval`](https://eslint.org/docs/rules/no-eval) - - [6.5](#strings--escaping) Do not unnecessarily escape characters in strings. eslint: [`no-useless-escape`](http://eslint.org/docs/rules/no-useless-escape) + - [6.5](#strings--escaping) Do not unnecessarily escape characters in strings. eslint: [`no-useless-escape`](https://eslint.org/docs/rules/no-useless-escape) > Why? Backslashes harm readability, thus they should only be present when necessary. @@ -611,9 +646,9 @@ Other Style Guides ## Functions - - [7.1](#functions--declarations) Use named function expressions instead of function declarations. eslint: [`func-style`](http://eslint.org/docs/rules/func-style) jscs: [`disallowFunctionDeclarations`](http://jscs.info/rule/disallowFunctionDeclarations) + - [7.1](#functions--declarations) Use named function expressions instead of function declarations. eslint: [`func-style`](https://eslint.org/docs/rules/func-style) - > Why? Function declarations are hoisted, which means that it’s easy - too easy - to reference the function before it is defined in the file. This harms readability and maintainability. If you find that a function’s definition is large or complex enough that it is interfering with understanding the rest of the file, then perhaps it’s time to extract it to its own module! Don’t forget to name the expression - anonymous functions can make it harder to locate the problem in an Error’s call stack. ([Discussion](https://github.com/airbnb/javascript/issues/794)) + > Why? Function declarations are hoisted, which means that it’s easy - too easy - to reference the function before it is defined in the file. This harms readability and maintainability. If you find that a function’s definition is large or complex enough that it is interfering with understanding the rest of the file, then perhaps it’s time to extract it to its own module! Don’t forget to explicitly name the expression, regardless of whether or not the name is inferred from the containing variable (which is often the case in modern browsers or when using compilers such as Babel). This eliminates any assumptions made about the Error’s call stack. ([Discussion](https://github.com/airbnb/javascript/issues/794)) ```javascript // bad @@ -627,13 +662,14 @@ Other Style Guides }; // good - const foo = function bar() { + // lexical name distinguished from the variable-referenced invocation(s) + const short = function longUniqueMoreDescriptiveLexicalFoo() { // ... }; ``` - - [7.2](#functions--iife) Wrap immediately invoked function expressions in parentheses. eslint: [`wrap-iife`](http://eslint.org/docs/rules/wrap-iife.html) jscs: [`requireParenthesesAroundIIFE`](http://jscs.info/rule/requireParenthesesAroundIIFE) + - [7.2](#functions--iife) Wrap immediately invoked function expressions in parentheses. eslint: [`wrap-iife`](https://eslint.org/docs/rules/wrap-iife.html) > Why? An immediately invoked function expression is a single unit - wrapping both it, and its invocation parens, in parens, cleanly expresses this. Note that in a world with modules everywhere, you almost never need an IIFE. @@ -645,10 +681,10 @@ Other Style Guides ``` - - [7.3](#functions--in-blocks) Never declare a function in a non-function block (`if`, `while`, etc). Assign the function to a variable instead. Browsers will allow you to do it, but they all interpret it differently, which is bad news bears. eslint: [`no-loop-func`](http://eslint.org/docs/rules/no-loop-func.html) + - [7.3](#functions--in-blocks) Never declare a function in a non-function block (`if`, `while`, etc). Assign the function to a variable instead. Browsers will allow you to do it, but they all interpret it differently, which is bad news bears. eslint: [`no-loop-func`](https://eslint.org/docs/rules/no-loop-func.html) - - [7.4](#functions--note-on-blocks) **Note:** ECMA-262 defines a `block` as a list of statements. A function declaration is not a statement. [Read ECMA-262’s note on this issue](http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf#page=97). + - [7.4](#functions--note-on-blocks) **Note:** ECMA-262 defines a `block` as a list of statements. A function declaration is not a statement. ```javascript // bad @@ -683,7 +719,7 @@ Other Style Guides ``` - - [7.6](#es6-rest) Never use `arguments`, opt to use rest syntax `...` instead. eslint: [`prefer-rest-params`](http://eslint.org/docs/rules/prefer-rest-params) + - [7.6](#es6-rest) Never use `arguments`, opt to use rest syntax `...` instead. eslint: [`prefer-rest-params`](https://eslint.org/docs/rules/prefer-rest-params) > Why? `...` is explicit about which arguments you want pulled. Plus, rest arguments are a real Array, and not merely Array-like like `arguments`. @@ -760,9 +796,9 @@ Other Style Guides ``` - - [7.10](#functions--constructor) Never use the Function constructor to create a new function. eslint: [`no-new-func`](http://eslint.org/docs/rules/no-new-func) + - [7.10](#functions--constructor) Never use the Function constructor to create a new function. eslint: [`no-new-func`](https://eslint.org/docs/rules/no-new-func) - > Why? Creating a function in this way evaluates a string similarly to eval(), which opens vulnerabilities. + > Why? Creating a function in this way evaluates a string similarly to `eval()`, which opens vulnerabilities. ```javascript // bad @@ -773,7 +809,7 @@ Other Style Guides ``` - - [7.11](#functions--signature-spacing) Spacing in a function signature. eslint: [`space-before-function-paren`](http://eslint.org/docs/rules/space-before-function-paren) [`space-before-blocks`](http://eslint.org/docs/rules/space-before-blocks) + - [7.11](#functions--signature-spacing) Spacing in a function signature. eslint: [`space-before-function-paren`](https://eslint.org/docs/rules/space-before-function-paren) [`space-before-blocks`](https://eslint.org/docs/rules/space-before-blocks) > Why? Consistency is good, and you shouldn’t have to add or remove a space when adding or removing a name. @@ -789,7 +825,7 @@ Other Style Guides ``` - - [7.12](#functions--mutate-params) Never mutate parameters. eslint: [`no-param-reassign`](http://eslint.org/docs/rules/no-param-reassign.html) + - [7.12](#functions--mutate-params) Never mutate parameters. eslint: [`no-param-reassign`](https://eslint.org/docs/rules/no-param-reassign.html) > Why? Manipulating objects passed in as parameters can cause unwanted variable side effects in the original caller. @@ -806,7 +842,7 @@ Other Style Guides ``` - - [7.13](#functions--reassign-params) Never reassign parameters. eslint: [`no-param-reassign`](http://eslint.org/docs/rules/no-param-reassign.html) + - [7.13](#functions--reassign-params) Never reassign parameters. eslint: [`no-param-reassign`](https://eslint.org/docs/rules/no-param-reassign.html) > Why? Reassigning parameters can lead to unexpected behavior, especially when accessing the `arguments` object. It can also cause optimization issues, especially in V8. @@ -834,7 +870,7 @@ Other Style Guides ``` - - [7.14](#functions--spread-vs-apply) Prefer the use of the spread operator `...` to call variadic functions. eslint: [`prefer-spread`](http://eslint.org/docs/rules/prefer-spread) + - [7.14](#functions--spread-vs-apply) Prefer the use of the spread operator `...` to call variadic functions. eslint: [`prefer-spread`](https://eslint.org/docs/rules/prefer-spread) > Why? It’s cleaner, you don’t need to supply a context, and you can not easily compose `new` with `apply`. @@ -855,7 +891,7 @@ Other Style Guides ``` - - [7.15](#functions--signature-invocation-indentation) Functions with multiline signatures, or invocations, should be indented just like every other multiline list in this guide: with each item on a line by itself, with a trailing comma on the last item. + - [7.15](#functions--signature-invocation-indentation) Functions with multiline signatures, or invocations, should be indented just like every other multiline list in this guide: with each item on a line by itself, with a trailing comma on the last item. eslint: [`function-paren-newline`](https://eslint.org/docs/rules/function-paren-newline) ```javascript // bad @@ -892,11 +928,11 @@ Other Style Guides ## Arrow Functions - - [8.1](#arrows--use-them) When you must use function expressions (as when passing an anonymous function), use arrow function notation. eslint: [`prefer-arrow-callback`](http://eslint.org/docs/rules/prefer-arrow-callback.html), [`arrow-spacing`](http://eslint.org/docs/rules/arrow-spacing.html) jscs: [`requireArrowFunctions`](http://jscs.info/rule/requireArrowFunctions) + - [8.1](#arrows--use-them) When you must use an anonymous function (as when passing an inline callback), use arrow function notation. eslint: [`prefer-arrow-callback`](https://eslint.org/docs/rules/prefer-arrow-callback.html), [`arrow-spacing`](https://eslint.org/docs/rules/arrow-spacing.html) > Why? It creates a version of the function that executes in the context of `this`, which is usually what you want, and is a more concise syntax. - > Why not? If you have a fairly complicated function, you might move that logic out into its own function declaration. + > Why not? If you have a fairly complicated function, you might move that logic out into its own named function expression. ```javascript // bad @@ -913,19 +949,19 @@ Other Style Guides ``` - - [8.2](#arrows--implicit-return) If the function body consists of a single statement returning an [expression](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Expressions) without side effects, omit the braces and use the implicit return. Otherwise, keep the braces and use a `return` statement. eslint: [`arrow-parens`](http://eslint.org/docs/rules/arrow-parens.html), [`arrow-body-style`](http://eslint.org/docs/rules/arrow-body-style.html) jscs: [`disallowParenthesesAroundArrowParam`](http://jscs.info/rule/disallowParenthesesAroundArrowParam), [`requireShorthandArrowFunctions`](http://jscs.info/rule/requireShorthandArrowFunctions) + - [8.2](#arrows--implicit-return) If the function body consists of a single statement returning an [expression](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Expressions) without side effects, omit the braces and use the implicit return. Otherwise, keep the braces and use a `return` statement. eslint: [`arrow-parens`](https://eslint.org/docs/rules/arrow-parens.html), [`arrow-body-style`](https://eslint.org/docs/rules/arrow-body-style.html) > Why? Syntactic sugar. It reads well when multiple functions are chained together. ```javascript // bad - [1, 2, 3].map(number => { + [1, 2, 3].map((number) => { const nextNumber = number + 1; `A string containing the ${nextNumber}.`; }); // good - [1, 2, 3].map(number => `A string containing the ${number}.`); + [1, 2, 3].map((number) => `A string containing the ${number + 1}.`); // good [1, 2, 3].map((number) => { @@ -964,14 +1000,14 @@ Other Style Guides ```javascript // bad - ['get', 'post', 'put'].map(httpMethod => Object.prototype.hasOwnProperty.call( + ['get', 'post', 'put'].map((httpMethod) => Object.prototype.hasOwnProperty.call( httpMagicObjectWithAVeryLongName, httpMethod, ) ); // good - ['get', 'post', 'put'].map(httpMethod => ( + ['get', 'post', 'put'].map((httpMethod) => ( Object.prototype.hasOwnProperty.call( httpMagicObjectWithAVeryLongName, httpMethod, @@ -980,22 +1016,27 @@ Other Style Guides ``` - - [8.4](#arrows--one-arg-parens) If your function takes a single argument and doesn’t use braces, omit the parentheses. Otherwise, always include parentheses around arguments for clarity and consistency. Note: it is also acceptable to always use parentheses, in which case use the [“always” option](http://eslint.org/docs/rules/arrow-parens#always) for eslint or do not include [`disallowParenthesesAroundArrowParam`](http://jscs.info/rule/disallowParenthesesAroundArrowParam) for jscs. eslint: [`arrow-parens`](http://eslint.org/docs/rules/arrow-parens.html) jscs: [`disallowParenthesesAroundArrowParam`](http://jscs.info/rule/disallowParenthesesAroundArrowParam) + - [8.4](#arrows--one-arg-parens) Always include parentheses around arguments for clarity and consistency. eslint: [`arrow-parens`](https://eslint.org/docs/rules/arrow-parens.html) - > Why? Less visual clutter. + > Why? Minimizes diff churn when adding or removing arguments. ```javascript // bad - [1, 2, 3].map((x) => x * x); - - // good [1, 2, 3].map(x => x * x); // good + [1, 2, 3].map((x) => x * x); + + // bad [1, 2, 3].map(number => ( `A long string with the ${number}. It’s so long that we don’t want it to take up space on the .map line!` )); + // good + [1, 2, 3].map((number) => ( + `A long string with the ${number}. It’s so long that we don’t want it to take up space on the .map line!` + )); + // bad [1, 2, 3].map(x => { const y = x + 1; @@ -1010,25 +1051,44 @@ Other Style Guides ``` - - [8.5](#arrows--confusing) Avoid confusing arrow function syntax (`=>`) with comparison operators (`<=`, `>=`). eslint: [`no-confusing-arrow`](http://eslint.org/docs/rules/no-confusing-arrow) + - [8.5](#arrows--confusing) Avoid confusing arrow function syntax (`=>`) with comparison operators (`<=`, `>=`). eslint: [`no-confusing-arrow`](https://eslint.org/docs/rules/no-confusing-arrow) ```javascript // bad - const itemHeight = item => item.height > 256 ? item.largeSize : item.smallSize; + const itemHeight = (item) => item.height <= 256 ? item.largeSize : item.smallSize; // bad - const itemHeight = (item) => item.height > 256 ? item.largeSize : item.smallSize; + const itemHeight = (item) => item.height >= 256 ? item.largeSize : item.smallSize; // good - const itemHeight = item => (item.height > 256 ? item.largeSize : item.smallSize); + const itemHeight = (item) => (item.height <= 256 ? item.largeSize : item.smallSize); // good const itemHeight = (item) => { const { height, largeSize, smallSize } = item; - return height > 256 ? largeSize : smallSize; + return height <= 256 ? largeSize : smallSize; }; ``` + + - [8.6](#whitespace--implicit-arrow-linebreak) Enforce the location of arrow function bodies with implicit returns. eslint: [`implicit-arrow-linebreak`](https://eslint.org/docs/rules/implicit-arrow-linebreak) + + ```javascript + // bad + (foo) => + bar; + + (foo) => + (bar); + + // good + (foo) => bar; + (foo) => (bar); + (foo) => ( + bar + ) + ``` + **[⬆ back to top](#table-of-contents)** ## Classes & Constructors @@ -1124,7 +1184,7 @@ Other Style Guides ``` - - [9.4](#constructors--tostring) It’s okay to write a custom toString() method, just make sure it works successfully and causes no side effects. + - [9.4](#constructors--tostring) It’s okay to write a custom `toString()` method, just make sure it works successfully and causes no side effects. ```javascript class Jedi { @@ -1143,7 +1203,7 @@ Other Style Guides ``` - - [9.5](#constructors--no-useless) Classes have a default constructor if one is not specified. An empty constructor function or one that just delegates to a parent class is unnecessary. eslint: [`no-useless-constructor`](http://eslint.org/docs/rules/no-useless-constructor) + - [9.5](#constructors--no-useless) Classes have a default constructor if one is not specified. An empty constructor function or one that just delegates to a parent class is unnecessary. eslint: [`no-useless-constructor`](https://eslint.org/docs/rules/no-useless-constructor) ```javascript // bad @@ -1172,7 +1232,7 @@ Other Style Guides ``` - - [9.6](#classes--no-duplicate-members) Avoid duplicate class members. eslint: [`no-dupe-class-members`](http://eslint.org/docs/rules/no-dupe-class-members) + - [9.6](#classes--no-duplicate-members) Avoid duplicate class members. eslint: [`no-dupe-class-members`](https://eslint.org/docs/rules/no-dupe-class-members) > Why? Duplicate class member declarations will silently prefer the last one - having duplicates is almost certainly a bug. @@ -1248,7 +1308,7 @@ Other Style Guides - [10.4](#modules--no-duplicate-imports) Only import from a path in one place. - eslint: [`no-duplicate-imports`](http://eslint.org/docs/rules/no-duplicate-imports) + eslint: [`no-duplicate-imports`](https://eslint.org/docs/rules/no-duplicate-imports) > Why? Having multiple lines that import from the same path can make code harder to maintain. ```javascript @@ -1285,6 +1345,7 @@ Other Style Guides - [10.6](#modules--prefer-default-export) In modules with a single export, prefer default export over named export. eslint: [`import/prefer-default-export`](https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/prefer-default-export.md) + > Why? To encourage more files that only ever export one thing, which is better for readability and maintainability. ```javascript // bad @@ -1352,7 +1413,7 @@ Other Style Guides ## Iterators and Generators - - [11.1](#iterators--nope) Don’t use iterators. Prefer JavaScript’s higher-order functions instead of loops like `for-in` or `for-of`. eslint: [`no-iterator`](http://eslint.org/docs/rules/no-iterator.html) [`no-restricted-syntax`](http://eslint.org/docs/rules/no-restricted-syntax) + - [11.1](#iterators--nope) Don’t use iterators. Prefer JavaScript’s higher-order functions instead of loops like `for-in` or `for-of`. eslint: [`no-iterator`](https://eslint.org/docs/rules/no-iterator.html) [`no-restricted-syntax`](https://eslint.org/docs/rules/no-restricted-syntax) > Why? This enforces our immutable rule. Dealing with pure functions that return values is easier to reason about than side effects. @@ -1392,7 +1453,7 @@ Other Style Guides }); // best (keeping it functional) - const increasedByOne = numbers.map(num => num + 1); + const increasedByOne = numbers.map((num) => num + 1); ``` @@ -1401,7 +1462,7 @@ Other Style Guides > Why? They don’t transpile well to ES5. - - [11.3](#generators--spacing) If you must use generators, or if you disregard [our advice](#generators--nope), make sure their function signature is spaced properly. eslint: [`generator-star-spacing`](http://eslint.org/docs/rules/generator-star-spacing) + - [11.3](#generators--spacing) If you must use generators, or if you disregard [our advice](#generators--nope), make sure their function signature is spaced properly. eslint: [`generator-star-spacing`](https://eslint.org/docs/rules/generator-star-spacing) > Why? `function` and `*` are part of the same conceptual keyword - `*` is not a modifier for `function`, `function*` is a unique construct, different from `function`. @@ -1466,7 +1527,7 @@ Other Style Guides ## Properties - - [12.1](#properties--dot) Use dot notation when accessing properties. eslint: [`dot-notation`](http://eslint.org/docs/rules/dot-notation.html) jscs: [`requireDotNotation`](http://jscs.info/rule/requireDotNotation) + - [12.1](#properties--dot) Use dot notation when accessing properties. eslint: [`dot-notation`](https://eslint.org/docs/rules/dot-notation.html) ```javascript const luke = { @@ -1497,7 +1558,7 @@ Other Style Guides const isJedi = getProp('jedi'); ``` - - [12.3](#es2016-properties--exponentiation-operator) Use exponentiation operator `**` when calculating exponentiations. eslint: [`no-restricted-properties`](http://eslint.org/docs/rules/no-restricted-properties). + - [12.3](#es2016-properties--exponentiation-operator) Use exponentiation operator `**` when calculating exponentiations. eslint: [`no-restricted-properties`](https://eslint.org/docs/rules/no-restricted-properties). ```javascript // bad @@ -1512,7 +1573,7 @@ Other Style Guides ## Variables - - [13.1](#variables--const) Always use `const` or `let` to declare variables. Not doing so will result in global variables. We want to avoid polluting the global namespace. Captain Planet warned us of that. eslint: [`no-undef`](http://eslint.org/docs/rules/no-undef) [`prefer-const`](http://eslint.org/docs/rules/prefer-const) + - [13.1](#variables--const) Always use `const` or `let` to declare variables. Not doing so will result in global variables. We want to avoid polluting the global namespace. Captain Planet warned us of that. eslint: [`no-undef`](https://eslint.org/docs/rules/no-undef) [`prefer-const`](https://eslint.org/docs/rules/prefer-const) ```javascript // bad @@ -1523,7 +1584,7 @@ Other Style Guides ``` - - [13.2](#variables--one-const) Use one `const` or `let` declaration per variable. eslint: [`one-var`](http://eslint.org/docs/rules/one-var.html) jscs: [`disallowMultipleVarDecl`](http://jscs.info/rule/disallowMultipleVarDecl) + - [13.2](#variables--one-const) Use one `const` or `let` declaration per variable or assignment. eslint: [`one-var`](https://eslint.org/docs/rules/one-var.html) > Why? It’s easier to add new variable declarations this way, and you never have to worry about swapping out a `;` for a `,` or introducing punctuation-only diffs. You can also step through each declaration with the debugger, instead of jumping through all of them at once. @@ -1610,7 +1671,7 @@ Other Style Guides } ``` - - [13.5](#variables--no-chain-assignment) Don’t chain variable assignments. + - [13.5](#variables--no-chain-assignment) Don’t chain variable assignments. eslint: [`no-multi-assign`](https://eslint.org/docs/rules/no-multi-assign) > Why? Chaining variable assignments creates implicit global variables. @@ -1643,7 +1704,7 @@ Other Style Guides ``` - - [13.6](#variables--unary-increment-decrement) Avoid using unary increments and decrements (++, --). eslint [`no-plusplus`](http://eslint.org/docs/rules/no-plusplus) + - [13.6](#variables--unary-increment-decrement) Avoid using unary increments and decrements (`++`, `--`). eslint [`no-plusplus`](https://eslint.org/docs/rules/no-plusplus) > Why? Per the eslint documentation, unary increment and decrement statements are subject to automatic semicolon insertion and can cause silent errors with incrementing or decrementing values within an application. It is also more expressive to mutate your values with statements like `num += 1` instead of `num++` or `num ++`. Disallowing unary increment and decrement statements also prevents you from pre-incrementing/pre-decrementing values unintentionally which can also cause unexpected behavior in your programs. @@ -1676,12 +1737,75 @@ Other Style Guides const truthyCount = array.filter(Boolean).length; ``` + + - [13.7](#variables--linebreak) Avoid linebreaks before or after `=` in an assignment. If your assignment violates [`max-len`](https://eslint.org/docs/rules/max-len.html), surround the value in parens. eslint [`operator-linebreak`](https://eslint.org/docs/rules/operator-linebreak.html). + + > Why? Linebreaks surrounding `=` can obfuscate the value of an assignment. + + ```javascript + // bad + const foo = + superLongLongLongLongLongLongLongLongFunctionName(); + + // bad + const foo + = 'superLongLongLongLongLongLongLongLongString'; + + // good + const foo = ( + superLongLongLongLongLongLongLongLongFunctionName() + ); + + // good + const foo = 'superLongLongLongLongLongLongLongLongString'; + ``` + + + - [13.8](#variables--no-unused-vars) Disallow unused variables. eslint: [`no-unused-vars`](https://eslint.org/docs/rules/no-unused-vars) + + > Why? Variables that are declared and not used anywhere in the code are most likely an error due to incomplete refactoring. Such variables take up space in the code and can lead to confusion by readers. + + ```javascript + // bad + + var some_unused_var = 42; + + // Write-only variables are not considered as used. + var y = 10; + y = 5; + + // A read for a modification of itself is not considered as used. + var z = 0; + z = z + 1; + + // Unused function arguments. + function getX(x, y) { + return x; + } + + // good + + function getXPlusY(x, y) { + return x + y; + } + + var x = 1; + var y = a + 2; + + alert(getXPlusY(x, y)); + + // 'type' is ignored even if unused because it has a rest property sibling. + // This is a form of extracting an object that omits the specified keys. + var { type, ...coords } = data; + // 'coords' is now the 'data' object without its 'type' property. + ``` + **[⬆ back to top](#table-of-contents)** ## Hoisting - - [14.1](#hoisting--about) `var` declarations get hoisted to the top of their scope, their assignment does not. `const` and `let` declarations are blessed with a new concept called [Temporal Dead Zones (TDZ)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let#Temporal_dead_zone_and_errors_with_let). It’s important to know why [typeof is no longer safe](http://es-discourse.com/t/why-typeof-is-no-longer-safe/15). + - [14.1](#hoisting--about) `var` declarations get hoisted to the top of their closest enclosing function scope, their assignment does not. `const` and `let` declarations are blessed with a new concept called [Temporal Dead Zones (TDZ)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/let#Temporal_Dead_Zone). It’s important to know why [typeof is no longer safe](http://es-discourse.com/t/why-typeof-is-no-longer-safe/15). ```javascript // we know this wouldn’t work (assuming there @@ -1731,8 +1855,8 @@ Other Style Guides } ``` - - - [14.3](#hoisting--named-expresions) Named function expressions hoist the variable name, not the function name or the function body. + + - [14.3](#hoisting--named-expressions) Named function expressions hoist the variable name, not the function name or the function body. ```javascript function example() { @@ -1780,7 +1904,7 @@ Other Style Guides ## Comparison Operators & Equality - - [15.1](#comparison--eqeqeq) Use `===` and `!==` over `==` and `!=`. eslint: [`eqeqeq`](http://eslint.org/docs/rules/eqeqeq.html) + - [15.1](#comparison--eqeqeq) Use `===` and `!==` over `==` and `!=`. eslint: [`eqeqeq`](https://eslint.org/docs/rules/eqeqeq.html) - [15.2](#comparison--if) Conditional statements such as the `if` statement evaluate their expression using coercion with the `ToBoolean` abstract method and always follow these simple rules: @@ -1838,7 +1962,7 @@ Other Style Guides - [15.4](#comparison--moreinfo) For more information see [Truth Equality and JavaScript](https://javascriptweblog.wordpress.com/2011/02/07/truth-equality-and-javascript/#more-2108) by Angus Croll. - - [15.5](#comparison--switch-blocks) Use braces to create blocks in `case` and `default` clauses that contain lexical declarations (e.g. `let`, `const`, `function`, and `class`). eslint: [`no-case-declarations`](http://eslint.org/docs/rules/no-case-declarations.html) + - [15.5](#comparison--switch-blocks) Use braces to create blocks in `case` and `default` clauses that contain lexical declarations (e.g. `let`, `const`, `function`, and `class`). eslint: [`no-case-declarations`](https://eslint.org/docs/rules/no-case-declarations.html) > Why? Lexical declarations are visible in the entire `switch` block but only get initialized when assigned, which only happens when its `case` is reached. This causes problems when multiple `case` clauses attempt to define the same thing. @@ -1886,7 +2010,7 @@ Other Style Guides ``` - - [15.6](#comparison--nested-ternaries) Ternaries should not be nested and generally be single line expressions. eslint: [`no-nested-ternary`](http://eslint.org/docs/rules/no-nested-ternary.html) + - [15.6](#comparison--nested-ternaries) Ternaries should not be nested and generally be single line expressions. eslint: [`no-nested-ternary`](https://eslint.org/docs/rules/no-nested-ternary.html) ```javascript // bad @@ -1894,21 +2018,20 @@ Other Style Guides ? "bar" : value1 > value2 ? "baz" : null; - // better + // split into 2 separated ternary expressions const maybeNull = value1 > value2 ? 'baz' : null; + // better const foo = maybe1 > maybe2 ? 'bar' : maybeNull; // best - const maybeNull = value1 > value2 ? 'baz' : null; - const foo = maybe1 > maybe2 ? 'bar' : maybeNull; ``` - - [15.7](#comparison--unneeded-ternary) Avoid unneeded ternary statements. eslint: [`no-unneeded-ternary`](http://eslint.org/docs/rules/no-unneeded-ternary.html) + - [15.7](#comparison--unneeded-ternary) Avoid unneeded ternary statements. eslint: [`no-unneeded-ternary`](https://eslint.org/docs/rules/no-unneeded-ternary.html) ```javascript // bad @@ -1922,12 +2045,49 @@ Other Style Guides const baz = !c; ``` + + - [15.8](#comparison--no-mixed-operators) When mixing operators, enclose them in parentheses. The only exception is the standard arithmetic operators: `+`, `-`, and `**` since their precedence is broadly understood. We recommend enclosing `/` and `*` in parentheses because their precedence can be ambiguous when they are mixed. + eslint: [`no-mixed-operators`](https://eslint.org/docs/rules/no-mixed-operators.html) + + > Why? This improves readability and clarifies the developer’s intention. + + ```javascript + // bad + const foo = a && b < 0 || c > 0 || d + 1 === 0; + + // bad + const bar = a ** b - 5 % d; + + // bad + // one may be confused into thinking (a || b) && c + if (a || b && c) { + return d; + } + + // bad + const bar = a + b / c * d; + + // good + const foo = (a && b < 0) || c > 0 || (d + 1 === 0); + + // good + const bar = a ** b - (5 % d); + + // good + if (a || (b && c)) { + return d; + } + + // good + const bar = a + (b / c) * d; + ``` + **[⬆ back to top](#table-of-contents)** ## Blocks - - [16.1](#blocks--braces) Use braces with all multi-line blocks. + - [16.1](#blocks--braces) Use braces with all multi-line blocks. eslint: [`nonblock-statement-body-position`](https://eslint.org/docs/rules/nonblock-statement-body-position) ```javascript // bad @@ -1952,7 +2112,7 @@ Other Style Guides ``` - - [16.2](#blocks--cuddled-elses) If you're using multi-line blocks with `if` and `else`, put `else` on the same line as your `if` block’s closing brace. eslint: [`brace-style`](http://eslint.org/docs/rules/brace-style.html) jscs: [`disallowNewlineBeforeBlockStatements`](http://jscs.info/rule/disallowNewlineBeforeBlockStatements) + - [16.2](#blocks--cuddled-elses) If you’re using multi-line blocks with `if` and `else`, put `else` on the same line as your `if` block’s closing brace. eslint: [`brace-style`](https://eslint.org/docs/rules/brace-style.html) ```javascript // bad @@ -1973,12 +2133,79 @@ Other Style Guides } ``` + + - [16.3](#blocks--no-else-return) If an `if` block always executes a `return` statement, the subsequent `else` block is unnecessary. A `return` in an `else if` block following an `if` block that contains a `return` can be separated into multiple `if` blocks. eslint: [`no-else-return`](https://eslint.org/docs/rules/no-else-return) + + ```javascript + // bad + function foo() { + if (x) { + return x; + } else { + return y; + } + } + + // bad + function cats() { + if (x) { + return x; + } else if (y) { + return y; + } + } + + // bad + function dogs() { + if (x) { + return x; + } else { + if (y) { + return y; + } + } + } + + // good + function foo() { + if (x) { + return x; + } + + return y; + } + + // good + function cats() { + if (x) { + return x; + } + + if (y) { + return y; + } + } + + // good + function dogs(x) { + if (x) { + if (z) { + return y; + } + } else { + return z; + } + } + ``` + **[⬆ back to top](#table-of-contents)** ## Control Statements - - [17.1](#control-statements) In case your control statement (`if`, `while` etc.) gets too long or exceeds the maximum line length, each (grouped) condition could be put into a new line. It’s up to you whether the logical operator should begin or end the line. + - [17.1](#control-statements) In case your control statement (`if`, `while` etc.) gets too long or exceeds the maximum line length, each (grouped) condition could be put into a new line. The logical operator should begin the line. + + > Why? Requiring operators at the beginning of the line keeps the operators aligned and follows a pattern similar to method chaining. This also improves readability by making it easier to visually follow complex logic. ```javascript // bad @@ -1998,37 +2225,50 @@ Other Style Guides thing1(); } - // good + // bad if ( - (foo === 123 || bar === "abc") && - doesItLookGoodWhenItBecomesThatLong() && - isThisReallyHappening() + foo === 123 && + bar === 'abc' ) { thing1(); } // good - if (foo === 123 && bar === 'abc') { + if ( + foo === 123 + && bar === 'abc' + ) { thing1(); } // good if ( - foo === 123 && - bar === 'abc' + (foo === 123 || bar === 'abc') + && doesItLookGoodWhenItBecomesThatLong() + && isThisReallyHappening() ) { thing1(); } // good - if ( - foo === 123 - && bar === 'abc' - ) { + if (foo === 123 && bar === 'abc') { thing1(); } ``` + + - [17.2](#control-statements--value-selection) Don't use selection operators in place of control statements. + + ```javascript + // bad + !isRunning && startRunning(); + + // good + if (!isRunning) { + startRunning(); + } + ``` + **[⬆ back to top](#table-of-contents)** ## Comments @@ -2102,7 +2342,8 @@ Other Style Guides } ``` - - [18.3](#comments--spaces) Start all comments with a space to make it easier to read. eslint: [`spaced-comment`](http://eslint.org/docs/rules/spaced-comment) + + - [18.3](#comments--spaces) Start all comments with a space to make it easier to read. eslint: [`spaced-comment`](https://eslint.org/docs/rules/spaced-comment) ```javascript // bad @@ -2139,7 +2380,7 @@ Other Style Guides ``` - - [18.4](#comments--actionitems) Prefixing your comments with `FIXME` or `TODO` helps other developers quickly understand if you're pointing out a problem that needs to be revisited, or if you're suggesting a solution to the problem that needs to be implemented. These are different than regular comments because they are actionable. The actions are `FIXME: -- need to figure this out` or `TODO: -- need to implement`. + - [18.4](#comments--actionitems) Prefixing your comments with `FIXME` or `TODO` helps other developers quickly understand if you’re pointing out a problem that needs to be revisited, or if you’re suggesting a solution to the problem that needs to be implemented. These are different than regular comments because they are actionable. The actions are `FIXME: -- need to figure this out` or `TODO: -- need to implement`. - [18.5](#comments--fixme) Use `// FIXME:` to annotate problems. @@ -2174,7 +2415,7 @@ Other Style Guides ## Whitespace - - [19.1](#whitespace--spaces) Use soft tabs (space character) set to 2 spaces. eslint: [`indent`](http://eslint.org/docs/rules/indent.html) jscs: [`validateIndentation`](http://jscs.info/rule/validateIndentation) + - [19.1](#whitespace--spaces) Use soft tabs (space character) set to 2 spaces. eslint: [`indent`](https://eslint.org/docs/rules/indent.html) ```javascript // bad @@ -2194,7 +2435,7 @@ Other Style Guides ``` - - [19.2](#whitespace--before-blocks) Place 1 space before the leading brace. eslint: [`space-before-blocks`](http://eslint.org/docs/rules/space-before-blocks.html) jscs: [`requireSpaceBeforeBlockStatements`](http://jscs.info/rule/requireSpaceBeforeBlockStatements) + - [19.2](#whitespace--before-blocks) Place 1 space before the leading brace. eslint: [`space-before-blocks`](https://eslint.org/docs/rules/space-before-blocks.html) ```javascript // bad @@ -2221,7 +2462,7 @@ Other Style Guides ``` - - [19.3](#whitespace--around-keywords) Place 1 space before the opening parenthesis in control statements (`if`, `while` etc.). Place no space between the argument list and the function name in function calls and declarations. eslint: [`keyword-spacing`](http://eslint.org/docs/rules/keyword-spacing.html) jscs: [`requireSpaceAfterKeywords`](http://jscs.info/rule/requireSpaceAfterKeywords) + - [19.3](#whitespace--around-keywords) Place 1 space before the opening parenthesis in control statements (`if`, `while` etc.). Place no space between the argument list and the function name in function calls and declarations. eslint: [`keyword-spacing`](https://eslint.org/docs/rules/keyword-spacing.html) ```javascript // bad @@ -2246,7 +2487,7 @@ Other Style Guides ``` - - [19.4](#whitespace--infix-ops) Set off operators with spaces. eslint: [`space-infix-ops`](http://eslint.org/docs/rules/space-infix-ops.html) jscs: [`requireSpaceBeforeBinaryOperators`](http://jscs.info/rule/requireSpaceBeforeBinaryOperators), [`requireSpaceAfterBinaryOperators`](http://jscs.info/rule/requireSpaceAfterBinaryOperators) + - [19.4](#whitespace--infix-ops) Set off operators with spaces. eslint: [`space-infix-ops`](https://eslint.org/docs/rules/space-infix-ops.html) ```javascript // bad @@ -2283,7 +2524,7 @@ Other Style Guides - [19.6](#whitespace--chains) Use indentation when making long method chains (more than 2 method chains). Use a leading dot, which - emphasizes that the line is a method call, not a new statement. eslint: [`newline-per-chained-call`](http://eslint.org/docs/rules/newline-per-chained-call) [`no-whitespace-before-property`](http://eslint.org/docs/rules/no-whitespace-before-property) + emphasizes that the line is a method call, not a new statement. eslint: [`newline-per-chained-call`](https://eslint.org/docs/rules/newline-per-chained-call) [`no-whitespace-before-property`](https://eslint.org/docs/rules/no-whitespace-before-property) ```javascript // bad @@ -2326,7 +2567,7 @@ Other Style Guides ``` - - [19.7](#whitespace--after-blocks) Leave a blank line after blocks and before the next statement. jscs: [`requirePaddingNewLinesAfterBlocks`](http://jscs.info/rule/requirePaddingNewLinesAfterBlocks) + - [19.7](#whitespace--after-blocks) Leave a blank line after blocks and before the next statement. ```javascript // bad @@ -2384,7 +2625,7 @@ Other Style Guides ``` - - [19.8](#whitespace--padded-blocks) Do not pad your blocks with blank lines. eslint: [`padded-blocks`](http://eslint.org/docs/rules/padded-blocks.html) jscs: [`disallowPaddingNewlinesInBlocks`](http://jscs.info/rule/disallowPaddingNewlinesInBlocks) + - [19.8](#whitespace--padded-blocks) Do not pad your blocks with blank lines. eslint: [`padded-blocks`](https://eslint.org/docs/rules/padded-blocks.html) ```javascript // bad @@ -2394,7 +2635,7 @@ Other Style Guides } - // also bad + // bad if (baz) { console.log(qux); @@ -2403,6 +2644,14 @@ Other Style Guides } + // bad + class Foo { + + constructor(bar) { + this.bar = bar; + } + } + // good function bar() { console.log(foo); @@ -2416,8 +2665,62 @@ Other Style Guides } ``` + + - [19.9](#whitespace--no-multiple-blanks) Do not use multiple blank lines to pad your code. eslint: [`no-multiple-empty-lines`](https://eslint.org/docs/rules/no-multiple-empty-lines) + + + ```javascript + // bad + class Person { + constructor(fullName, email, birthday) { + this.fullName = fullName; + + + this.email = email; + + + this.setAge(birthday); + } + + + setAge(birthday) { + const today = new Date(); + + + const age = this.getAge(today, birthday); + + + this.age = age; + } + + + getAge(today, birthday) { + // .. + } + } + + // good + class Person { + constructor(fullName, email, birthday) { + this.fullName = fullName; + this.email = email; + this.setAge(birthday); + } + + setAge(birthday) { + const today = new Date(); + const age = getAge(today, birthday); + this.age = age; + } + + getAge(today, birthday) { + // .. + } + } + ``` + - - [19.9](#whitespace--in-parens) Do not add spaces inside parentheses. eslint: [`space-in-parens`](http://eslint.org/docs/rules/space-in-parens.html) jscs: [`disallowSpacesInsideParentheses`](http://jscs.info/rule/disallowSpacesInsideParentheses) + - [19.10](#whitespace--in-parens) Do not add spaces inside parentheses. eslint: [`space-in-parens`](https://eslint.org/docs/rules/space-in-parens.html) ```javascript // bad @@ -2442,7 +2745,7 @@ Other Style Guides ``` - - [19.10](#whitespace--in-brackets) Do not add spaces inside brackets. eslint: [`array-bracket-spacing`](http://eslint.org/docs/rules/array-bracket-spacing.html) jscs: [`disallowSpacesInsideArrayBrackets`](http://jscs.info/rule/disallowSpacesInsideArrayBrackets) + - [19.11](#whitespace--in-brackets) Do not add spaces inside brackets. eslint: [`array-bracket-spacing`](https://eslint.org/docs/rules/array-bracket-spacing.html) ```javascript // bad @@ -2455,7 +2758,7 @@ Other Style Guides ``` - - [19.11](#whitespace--in-braces) Add spaces inside curly braces. eslint: [`object-curly-spacing`](http://eslint.org/docs/rules/object-curly-spacing.html) jscs: [`requireSpacesInsideObjectBrackets`](http://jscs.info/rule/requireSpacesInsideObjectBrackets) + - [19.12](#whitespace--in-braces) Add spaces inside curly braces. eslint: [`object-curly-spacing`](https://eslint.org/docs/rules/object-curly-spacing.html) ```javascript // bad @@ -2466,7 +2769,7 @@ Other Style Guides ``` - - [19.12](#whitespace--max-len) Avoid having lines of code that are longer than 100 characters (including whitespace). Note: per [above](#strings--line-length), long strings are exempt from this rule, and should not be broken up. eslint: [`max-len`](http://eslint.org/docs/rules/max-len.html) jscs: [`maximumLineLength`](http://jscs.info/rule/maximumLineLength) + - [19.13](#whitespace--max-len) Avoid having lines of code that are longer than 100 characters (including whitespace). Note: per [above](#strings--line-length), long strings are exempt from this rule, and should not be broken up. eslint: [`max-len`](https://eslint.org/docs/rules/max-len.html) > Why? This ensures readability and maintainability. @@ -2495,12 +2798,112 @@ Other Style Guides .fail(() => console.log('You have failed this city.')); ``` + + - [19.14](#whitespace--block-spacing) Require consistent spacing inside an open block token and the next token on the same line. This rule also enforces consistent spacing inside a close block token and previous token on the same line. eslint: [`block-spacing`](https://eslint.org/docs/rules/block-spacing) + + ```javascript + // bad + function foo() {return true;} + if (foo) { bar = 0;} + + // good + function foo() { return true; } + if (foo) { bar = 0; } + ``` + + + - [19.15](#whitespace--comma-spacing) Avoid spaces before commas and require a space after commas. eslint: [`comma-spacing`](https://eslint.org/docs/rules/comma-spacing) + + ```javascript + // bad + var foo = 1,bar = 2; + var arr = [1 , 2]; + + // good + var foo = 1, bar = 2; + var arr = [1, 2]; + ``` + + + - [19.16](#whitespace--computed-property-spacing) Enforce spacing inside of computed property brackets. eslint: [`computed-property-spacing`](https://eslint.org/docs/rules/computed-property-spacing) + + ```javascript + // bad + obj[foo ] + obj[ 'foo'] + var x = {[ b ]: a} + obj[foo[ bar ]] + + // good + obj[foo] + obj['foo'] + var x = { [b]: a } + obj[foo[bar]] + ``` + + + - [19.17](#whitespace--func-call-spacing) Avoid spaces between functions and their invocations. eslint: [`func-call-spacing`](https://eslint.org/docs/rules/func-call-spacing) + + ```javascript + // bad + func (); + + func + (); + + // good + func(); + ``` + + + - [19.18](#whitespace--key-spacing) Enforce spacing between keys and values in object literal properties. eslint: [`key-spacing`](https://eslint.org/docs/rules/key-spacing) + + ```javascript + // bad + var obj = { "foo" : 42 }; + var obj2 = { "foo":42 }; + + // good + var obj = { "foo": 42 }; + ``` + + + - [19.19](#whitespace--no-trailing-spaces) Avoid trailing spaces at the end of lines. eslint: [`no-trailing-spaces`](https://eslint.org/docs/rules/no-trailing-spaces) + + + - [19.20](#whitespace--no-multiple-empty-lines) Avoid multiple empty lines, only allow one newline at the end of files, and avoid a newline at the beginning of files. eslint: [`no-multiple-empty-lines`](https://eslint.org/docs/rules/no-multiple-empty-lines) + + + ```javascript + // bad - multiple empty lines + var x = 1; + + + var y = 2; + + // bad - 2+ newlines at end of file + var x = 1; + var y = 2; + + + // bad - 1+ newline(s) at beginning of file + + var x = 1; + var y = 2; + + // good + var x = 1; + var y = 2; + + ``` + + **[⬆ back to top](#table-of-contents)** ## Commas - - - [20.1](#commas--leading-trailing) Leading commas: **Nope.** eslint: [`comma-style`](http://eslint.org/docs/rules/comma-style.html) jscs: [`requireCommaBeforeLineBreak`](http://jscs.info/rule/requireCommaBeforeLineBreak) + + - [20.1](#commas--leading-trailing) Leading commas: **Nope.** eslint: [`comma-style`](https://eslint.org/docs/rules/comma-style.html) ```javascript // bad @@ -2535,7 +2938,7 @@ Other Style Guides ``` - - [20.2](#commas--dangling) Additional trailing comma: **Yup.** eslint: [`comma-dangle`](http://eslint.org/docs/rules/comma-dangle.html) jscs: [`requireTrailingComma`](http://jscs.info/rule/requireTrailingComma) + - [20.2](#commas--dangling) Additional trailing comma: **Yup.** eslint: [`comma-dangle`](https://eslint.org/docs/rules/comma-dangle.html) > Why? This leads to cleaner git diffs. Also, transpilers like Babel will remove the additional trailing comma in the transpiled code which means you don’t have to worry about the [trailing comma problem](https://github.com/airbnb/javascript/blob/es5-deprecated/es5/README.md#commas) in legacy browsers. @@ -2635,29 +3038,50 @@ Other Style Guides ## Semicolons - - [21.1](#semicolons--required) **Yup.** eslint: [`semi`](http://eslint.org/docs/rules/semi.html) jscs: [`requireSemicolons`](http://jscs.info/rule/requireSemicolons) + - [21.1](#semicolons--required) **Yup.** eslint: [`semi`](https://eslint.org/docs/rules/semi.html) + + > Why? When JavaScript encounters a line break without a semicolon, it uses a set of rules called [Automatic Semicolon Insertion](https://tc39.github.io/ecma262/#sec-automatic-semicolon-insertion) to determine whether or not it should regard that line break as the end of a statement, and (as the name implies) place a semicolon into your code before the line break if it thinks so. ASI contains a few eccentric behaviors, though, and your code will break if JavaScript misinterprets your line break. These rules will become more complicated as new features become a part of JavaScript. Explicitly terminating your statements and configuring your linter to catch missing semicolons will help prevent you from encountering issues. ```javascript - // bad - (function () { - const name = 'Skywalker' - return name - })() + // bad - raises exception + const luke = {} + const leia = {} + [luke, leia].forEach((jedi) => jedi.father = 'vader') + + // bad - raises exception + const reaction = "No! That’s impossible!" + (async function meanwhileOnTheFalcon() { + // handle `leia`, `lando`, `chewie`, `r2`, `c3p0` + // ... + }()) + + // bad - returns `undefined` instead of the value on the next line - always happens when `return` is on a line by itself because of ASI! + function foo() { + return + 'search your feelings, you know it to be foo' + } // good - (function () { - const name = 'Skywalker'; - return name; + const luke = {}; + const leia = {}; + [luke, leia].forEach((jedi) => { + jedi.father = 'vader'; + }); + + // good + const reaction = "No! That’s impossible!"; + (async function meanwhileOnTheFalcon() { + // handle `leia`, `lando`, `chewie`, `r2`, `c3p0` + // ... }()); - // good, but legacy (guards against the function becoming an argument when two files with IIFEs are concatenated) - ;((() => { - const name = 'Skywalker'; - return name; - })()); + // good + function foo() { + return 'search your feelings, you know it to be foo'; + } ``` - [Read more](https://stackoverflow.com/questions/7365172/semicolon-before-self-invoking-function/7365214%237365214). + [Read more](https://stackoverflow.com/questions/7365172/semicolon-before-self-invoking-function/7365214#7365214). **[⬆ back to top](#table-of-contents)** @@ -2667,11 +3091,14 @@ Other Style Guides - [22.1](#coercion--explicit) Perform type coercion at the beginning of the statement. - - [22.2](#coercion--strings) Strings: + - [22.2](#coercion--strings) Strings: eslint: [`no-new-wrappers`](https://eslint.org/docs/rules/no-new-wrappers) ```javascript // => this.reviewScore = 9; + // bad + const totalScore = new String(this.reviewScore); // typeof totalScore is "object" not "string" + // bad const totalScore = this.reviewScore + ''; // invokes this.reviewScore.valueOf() @@ -2683,7 +3110,7 @@ Other Style Guides ``` - - [22.3](#coercion--numbers) Numbers: Use `Number` for type casting and `parseInt` always with a radix for parsing strings. eslint: [`radix`](http://eslint.org/docs/rules/radix) + - [22.3](#coercion--numbers) Numbers: Use `Number` for type casting and `parseInt` always with a radix for parsing strings. eslint: [`radix`](https://eslint.org/docs/rules/radix) [`no-new-wrappers`](https://eslint.org/docs/rules/no-new-wrappers) ```javascript const inputValue = '4'; @@ -2708,7 +3135,7 @@ Other Style Guides ``` - - [22.4](#coercion--comment-deviations) If for whatever reason you are doing something wild and `parseInt` is your bottleneck and need to use Bitshift for [performance reasons](https://jsperf.com/coercion-vs-casting/3), leave a comment explaining why and what you're doing. + - [22.4](#coercion--comment-deviations) If for whatever reason you are doing something wild and `parseInt` is your bottleneck and need to use Bitshift for [performance reasons](https://jsperf.com/coercion-vs-casting/3), leave a comment explaining why and what you’re doing. ```javascript // good @@ -2730,7 +3157,7 @@ Other Style Guides ``` - - [22.6](#coercion--booleans) Booleans: + - [22.6](#coercion--booleans) Booleans: eslint: [`no-new-wrappers`](https://eslint.org/docs/rules/no-new-wrappers) ```javascript const age = 0; @@ -2750,7 +3177,7 @@ Other Style Guides ## Naming Conventions - - [23.1](#naming--descriptive) Avoid single letter names. Be descriptive with your naming. eslint: [`id-length`](http://eslint.org/docs/rules/id-length) + - [23.1](#naming--descriptive) Avoid single letter names. Be descriptive with your naming. eslint: [`id-length`](https://eslint.org/docs/rules/id-length) ```javascript // bad @@ -2765,7 +3192,7 @@ Other Style Guides ``` - - [23.2](#naming--camelCase) Use camelCase when naming objects, functions, and instances. eslint: [`camelcase`](http://eslint.org/docs/rules/camelcase.html) jscs: [`requireCamelCaseOrUpperCaseIdentifiers`](http://jscs.info/rule/requireCamelCaseOrUpperCaseIdentifiers) + - [23.2](#naming--camelCase) Use camelCase when naming objects, functions, and instances. eslint: [`camelcase`](https://eslint.org/docs/rules/camelcase.html) ```javascript // bad @@ -2779,7 +3206,7 @@ Other Style Guides ``` - - [23.3](#naming--PascalCase) Use PascalCase only when naming constructors or classes. eslint: [`new-cap`](http://eslint.org/docs/rules/new-cap.html) jscs: [`requireCapitalizedConstructors`](http://jscs.info/rule/requireCapitalizedConstructors) + - [23.3](#naming--PascalCase) Use PascalCase only when naming constructors or classes. eslint: [`new-cap`](https://eslint.org/docs/rules/new-cap.html) ```javascript // bad @@ -2804,7 +3231,7 @@ Other Style Guides ``` - - [23.4](#naming--leading-underscore) Do not use trailing or leading underscores. eslint: [`no-underscore-dangle`](http://eslint.org/docs/rules/no-underscore-dangle.html) jscs: [`disallowDanglingUnderscores`](http://jscs.info/rule/disallowDanglingUnderscores) + - [23.4](#naming--leading-underscore) Do not use trailing or leading underscores. eslint: [`no-underscore-dangle`](https://eslint.org/docs/rules/no-underscore-dangle.html) > Why? JavaScript does not have the concept of privacy in terms of properties or methods. Although a leading underscore is a common convention to mean “private”, in fact, these properties are fully public, and as such, are part of your public API contract. This convention might lead developers to wrongly think that a change won’t count as breaking, or that tests aren’t needed. tl;dr: if you want something to be “private”, it must not be observably present. @@ -2816,10 +3243,15 @@ Other Style Guides // good this.firstName = 'Panda'; + + // good, in environments where WeakMaps are available + // see https://kangax.github.io/compat-table/es6/#test-WeakMap + const firstNames = new WeakMap(); + firstNames.set(this, 'Panda'); ``` - - [23.5](#naming--self-this) Don’t save references to `this`. Use arrow functions or [Function#bind](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind). jscs: [`disallowNodeTypes`](http://jscs.info/rule/disallowNodeTypes) + - [23.5](#naming--self-this) Don’t save references to `this`. Use arrow functions or [Function#bind](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind). ```javascript // bad @@ -2906,7 +3338,7 @@ Other Style Guides ``` - - [23.9](#naming--Acronyms-and-Initialisms) Acronyms and initialisms should always be all capitalized, or all lowercased. + - [23.9](#naming--Acronyms-and-Initialisms) Acronyms and initialisms should always be all uppercased, or all lowercased. > Why? Names are for readability, not to appease a computer algorithm. @@ -2927,15 +3359,58 @@ Other Style Guides // ... ]; + // also good + const httpRequests = [ + // ... + ]; + // best import TextMessageContainer from './containers/TextMessageContainer'; // best - const Requests = [ + const requests = [ // ... ]; ``` + + - [23.10](#naming--uppercase) You may optionally uppercase a constant only if it (1) is exported, (2) is a `const` (it can not be reassigned), and (3) the programmer can trust it (and its nested properties) to never change. + + > Why? This is an additional tool to assist in situations where the programmer would be unsure if a variable might ever change. UPPERCASE_VARIABLES are letting the programmer know that they can trust the variable (and its properties) not to change. + - What about all `const` variables? - This is unnecessary, so uppercasing should not be used for constants within a file. It should be used for exported constants however. + - What about exported objects? - Uppercase at the top level of export (e.g. `EXPORTED_OBJECT.key`) and maintain that all nested properties do not change. + + ```javascript + // bad + const PRIVATE_VARIABLE = 'should not be unnecessarily uppercased within a file'; + + // bad + export const THING_TO_BE_CHANGED = 'should obviously not be uppercased'; + + // bad + export let REASSIGNABLE_VARIABLE = 'do not use let with uppercase variables'; + + // --- + + // allowed but does not supply semantic value + export const apiKey = 'SOMEKEY'; + + // better in most cases + export const API_KEY = 'SOMEKEY'; + + // --- + + // bad - unnecessarily uppercases key while adding no semantic value + export const MAPPING = { + KEY: 'value' + }; + + // good + export const MAPPING = { + key: 'value' + }; + ``` + **[⬆ back to top](#table-of-contents)** ## Accessors @@ -2944,7 +3419,7 @@ Other Style Guides - [24.1](#accessors--not-required) Accessor functions for properties are not required. - - [24.2](#accessors--no-getters-setters) Do not use JavaScript getters/setters as they cause unexpected side effects and are harder to test, maintain, and reason about. Instead, if you do make accessor functions, use getVal() and setVal('hello'). + - [24.2](#accessors--no-getters-setters) Do not use JavaScript getters/setters as they cause unexpected side effects and are harder to test, maintain, and reason about. Instead, if you do make accessor functions, use `getVal()` and `setVal('hello')`. ```javascript // bad @@ -2986,7 +3461,7 @@ Other Style Guides ``` - - [24.4](#accessors--consistent) It’s okay to create get() and set() functions, but be consistent. + - [24.4](#accessors--consistent) It’s okay to create `get()` and `set()` functions, but be consistent. ```javascript class Jedi { @@ -3010,7 +3485,7 @@ Other Style Guides ## Events - - [25.1](#events--hash) When attaching data payloads to events (whether DOM events or something more proprietary like Backbone events), pass a hash instead of a raw value. This allows a subsequent contributor to add more data to the event payload without finding and updating every handler for the event. For example, instead of: + - [25.1](#events--hash) When attaching data payloads to events (whether DOM events or something more proprietary like Backbone events), pass an object literal (also known as a "hash") instead of a raw value. This allows a subsequent contributor to add more data to the event payload without finding and updating every handler for the event. For example, instead of: ```javascript // bad @@ -3018,8 +3493,8 @@ Other Style Guides // ... - $(this).on('listingUpdated', (e, listingId) => { - // do something with listingId + $(this).on('listingUpdated', (e, listingID) => { + // do something with listingID }); ``` @@ -3027,12 +3502,12 @@ Other Style Guides ```javascript // good - $(this).trigger('listingUpdated', { listingId: listing.id }); + $(this).trigger('listingUpdated', { listingID: listing.id }); // ... $(this).on('listingUpdated', (e, data) => { - // do something with data.listingId + // do something with data.listingID }); ``` @@ -3041,7 +3516,7 @@ Other Style Guides ## jQuery - - [26.1](#jquery--dollar-prefix) Prefix jQuery object variables with a `$`. jscs: [`requireDollarBeforejQueryAssignment`](http://jscs.info/rule/requireDollarBeforejQueryAssignment) + - [26.1](#jquery--dollar-prefix) Prefix jQuery object variables with a `$`. ```javascript // bad @@ -3142,10 +3617,50 @@ Other Style Guides **[⬆ back to top](#table-of-contents)** +## Standard Library + + The [Standard Library](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects) + contains utilities that are functionally broken but remain for legacy reasons. + + + - [29.1](#standard-library--isnan) Use `Number.isNaN` instead of global `isNaN`. + eslint: [`no-restricted-globals`](https://eslint.org/docs/rules/no-restricted-globals) + + > Why? The global `isNaN` coerces non-numbers to numbers, returning true for anything that coerces to NaN. + > If this behavior is desired, make it explicit. + + ```javascript + // bad + isNaN('1.2'); // false + isNaN('1.2.3'); // true + + // good + Number.isNaN('1.2.3'); // false + Number.isNaN(Number('1.2.3')); // true + ``` + + + - [29.2](#standard-library--isfinite) Use `Number.isFinite` instead of global `isFinite`. + eslint: [`no-restricted-globals`](https://eslint.org/docs/rules/no-restricted-globals) + + > Why? The global `isFinite` coerces non-numbers to numbers, returning true for anything that coerces to a finite number. + > If this behavior is desired, make it explicit. + + ```javascript + // bad + isFinite('2e3'); // true + + // good + Number.isFinite('2e3'); // false + Number.isFinite(parseInt('2e3', 10)); // true + ``` + +**[⬆ back to top](#table-of-contents)** + ## Testing - - [29.1](#testing--yup) **Yup.** + - [30.1](#testing--yup) **Yup.** ```javascript function foo() { @@ -3154,11 +3669,11 @@ Other Style Guides ``` - - [29.2](#testing--for-real) **No, but seriously**: + - [30.2](#testing--for-real) **No, but seriously**: - Whichever testing framework you use, you should be writing tests! - Strive to write many small pure functions, and minimize where mutations occur. - Be cautious about stubs and mocks - they can make your tests more brittle. - - We primarily use [`mocha`](https://www.npmjs.com/package/mocha) at Airbnb. [`tape`](https://www.npmjs.com/package/tape) is also used occasionally for small, separate modules. + - We primarily use [`mocha`](https://www.npmjs.com/package/mocha) and [`jest`](https://www.npmjs.com/package/jest) at Airbnb. [`tape`](https://www.npmjs.com/package/tape) is also used occasionally for small, separate modules. - 100% test coverage is a good goal to strive for, even if it’s not always practical to reach it. - Whenever you fix a bug, _write a regression test_. A bug fixed without a regression test is almost certainly going to break again in the future. @@ -3168,21 +3683,21 @@ Other Style Guides - [On Layout & Web Performance](https://www.kellegous.com/j/2013/01/26/layout-performance/) - [String vs Array Concat](https://jsperf.com/string-vs-array-concat/2) - - [Try/Catch Cost In a Loop](https://jsperf.com/try-catch-in-loop-cost) + - [Try/Catch Cost In a Loop](https://jsperf.com/try-catch-in-loop-cost/12) - [Bang Function](https://jsperf.com/bang-function) - - [jQuery Find vs Context, Selector](https://jsperf.com/jquery-find-vs-context-sel/13) + - [jQuery Find vs Context, Selector](https://jsperf.com/jquery-find-vs-context-sel/164) - [innerHTML vs textContent for script text](https://jsperf.com/innerhtml-vs-textcontent-for-script-text) - - [Long String Concatenation](https://jsperf.com/ya-string-concat) - - [Are Javascript functions like `map()`, `reduce()`, and `filter()` optimized for traversing arrays?](https://www.quora.com/JavaScript-programming-language-Are-Javascript-functions-like-map-reduce-and-filter-already-optimized-for-traversing-array/answer/Quildreen-Motta) + - [Long String Concatenation](https://jsperf.com/ya-string-concat/38) + - [Are JavaScript functions like `map()`, `reduce()`, and `filter()` optimized for traversing arrays?](https://www.quora.com/JavaScript-programming-language-Are-Javascript-functions-like-map-reduce-and-filter-already-optimized-for-traversing-array/answer/Quildreen-Motta) - Loading... **[⬆ back to top](#table-of-contents)** ## Resources -**Learning ES6** +**Learning ES6+** - - [Draft ECMA 2015 (ES6) Spec](https://people.mozilla.org/~jorendorff/es6-draft.html) + - [Latest ECMA spec](https://tc39.github.io/ecma262/) - [ExploringJS](http://exploringjs.com/) - [ES6 Compatibility Table](https://kangax.github.io/compat-table/es6/) - [Comprehensive Overview of ES6 Features](http://es6-features.org/) @@ -3194,16 +3709,17 @@ Other Style Guides **Tools** - Code Style Linters - - [ESlint](http://eslint.org/) - [Airbnb Style .eslintrc](https://github.com/airbnb/javascript/blob/master/linters/.eslintrc) + - [ESlint](https://eslint.org/) - [Airbnb Style .eslintrc](https://github.com/airbnb/javascript/blob/master/linters/.eslintrc) - [JSHint](http://jshint.com/) - [Airbnb Style .jshintrc](https://github.com/airbnb/javascript/blob/master/linters/.jshintrc) - - [JSCS](https://github.com/jscs-dev/node-jscs) - [Airbnb Style Preset](https://github.com/jscs-dev/node-jscs/blob/master/presets/airbnb.json) (Deprecated, please use [ESlint](https://github.com/airbnb/javascript/tree/master/packages/eslint-config-airbnb-base)) - - Neutrino preset - [neutrino-preset-airbnb-base](https://neutrino.js.org/presets/neutrino-preset-airbnb-base/) + - Neutrino Preset - [@neutrinojs/airbnb](https://neutrinojs.org/packages/airbnb/) **Other Style Guides** - - [Google JavaScript Style Guide](https://google.github.io/styleguide/javascriptguide.xml) + - [Google JavaScript Style Guide](https://google.github.io/styleguide/jsguide.html) + - [Google JavaScript Style Guide (Old)](https://google.github.io/styleguide/javascriptguide.xml) - [jQuery Core Style Guidelines](https://contribute.jquery.org/style-guide/js/) - [Principles of Writing Consistent, Idiomatic JavaScript](https://github.com/rwaldron/idiomatic.js) + - [StandardJS](https://standardjs.com) **Other Styles** @@ -3224,7 +3740,7 @@ Other Style Guides - [JavaScript: The Good Parts](https://www.amazon.com/JavaScript-Good-Parts-Douglas-Crockford/dp/0596517742) - Douglas Crockford - [JavaScript Patterns](https://www.amazon.com/JavaScript-Patterns-Stoyan-Stefanov/dp/0596806752) - Stoyan Stefanov - - [Pro JavaScript Design Patterns](https://www.amazon.com/JavaScript-Design-Patterns-Recipes-Problem-Solution/dp/159059908X) - Ross Harmes and Dustin Diaz + - [Pro JavaScript Design Patterns](https://www.amazon.com/JavaScript-Design-Patterns-Recipes-Problem-Solution/dp/159059908X) - Ross Harmes and Dustin Diaz - [High Performance Web Sites: Essential Knowledge for Front-End Engineers](https://www.amazon.com/High-Performance-Web-Sites-Essential/dp/0596529309) - Steve Souders - [Maintainable JavaScript](https://www.amazon.com/Maintainable-JavaScript-Nicholas-C-Zakas/dp/1449327680) - Nicholas C. Zakas - [JavaScript Web Applications](https://www.amazon.com/JavaScript-Web-Applications-Alex-MacCaw/dp/144930351X) - Alex MacCaw @@ -3249,7 +3765,6 @@ Other Style Guides - [Perfection Kills](http://perfectionkills.com/) - [Ben Alman](http://benalman.com/) - [Dmitry Baranovskiy](http://dmitry.baranovskiy.com/) - - [Dustin Diaz](http://dustindiaz.com/) - [nettuts](http://code.tutsplus.com/?s=javascript) **Podcasts** @@ -3263,7 +3778,8 @@ Other Style Guides This is a list of organizations that are using this style guide. Send us a pull request and we'll add you to the list. - - **3blades**: [3Blades/javascript](https://github.com/3blades/javascript) + - **123erfasst**: [123erfasst/javascript](https://github.com/123erfasst/javascript) + - **3blades**: [3Blades](https://github.com/3blades) - **4Catalyzer**: [4Catalyzer/javascript](https://github.com/4Catalyzer/javascript) - **Aan Zee**: [AanZee/javascript](https://github.com/AanZee/javascript) - **Adult Swim**: [adult-swim/javascript](https://github.com/adult-swim/javascript) @@ -3276,16 +3792,19 @@ Other Style Guides - **Axept**: [axept/javascript](https://github.com/axept/javascript) - **BashPros**: [BashPros/javascript](https://github.com/BashPros/javascript) - **Billabong**: [billabong/javascript](https://github.com/billabong/javascript) - - **Bisk**: [bisk/javascript](https://github.com/Bisk/javascript/) + - **Bisk**: [bisk](https://github.com/Bisk/) - **Bonhomme**: [bonhommeparis/javascript](https://github.com/bonhommeparis/javascript) - **Brainshark**: [brainshark/javascript](https://github.com/brainshark/javascript) - **CaseNine**: [CaseNine/javascript](https://github.com/CaseNine/javascript) + - **Cerner**: [Cerner](https://github.com/cerner/) - **Chartboost**: [ChartBoost/javascript-style-guide](https://github.com/ChartBoost/javascript-style-guide) + - **Coeur d'Alene Tribe**: [www.cdatribe-nsn.gov](https://www.cdatribe-nsn.gov) - **ComparaOnline**: [comparaonline/javascript](https://github.com/comparaonline/javascript-style-guide) - **Compass Learning**: [compasslearning/javascript-style-guide](https://github.com/compasslearning/javascript-style-guide) - **DailyMotion**: [dailymotion/javascript](https://github.com/dailymotion/javascript) - **DoSomething**: [DoSomething/eslint-config](https://github.com/DoSomething/eslint-config) - **Digitpaint** [digitpaint/javascript](https://github.com/digitpaint/javascript) + - **Drupal**: [www.drupal.org](https://www.drupal.org/project/drupal) - **Ecosia**: [ecosia/javascript](https://github.com/ecosia/javascript) - **Evernote**: [evernote/javascript-style-guide](https://github.com/evernote/javascript-style-guide) - **Evolution Gaming**: [evolution-gaming/javascript](https://github.com/evolution-gaming/javascript) @@ -3293,21 +3812,25 @@ Other Style Guides - **ExactTarget**: [ExactTarget/javascript](https://github.com/ExactTarget/javascript) - **Expensify** [Expensify/Style-Guide](https://github.com/Expensify/Style-Guide/blob/master/javascript.md) - **Flexberry**: [Flexberry/javascript-style-guide](https://github.com/Flexberry/javascript-style-guide) - - **Gawker Media**: [gawkermedia/javascript](https://github.com/gawkermedia/javascript) + - **Gawker Media**: [gawkermedia](https://github.com/gawkermedia/) - **General Electric**: [GeneralElectric/javascript](https://github.com/GeneralElectric/javascript) - **Generation Tux**: [GenerationTux/javascript](https://github.com/generationtux/styleguide) - **GoodData**: [gooddata/gdc-js-style](https://github.com/gooddata/gdc-js-style) + - **GreenChef**: [greenchef/javascript](https://github.com/greenchef/javascript) - **Grooveshark**: [grooveshark/javascript](https://github.com/grooveshark/javascript) + - **Grupo-Abraxas**: [Grupo-Abraxas/javascript](https://github.com/Grupo-Abraxas/javascript) + - **Happeo**: [happeo/javascript](https://github.com/happeo/javascript) - **Honey**: [honeyscience/javascript](https://github.com/honeyscience/javascript) - **How About We**: [howaboutwe/javascript](https://github.com/howaboutwe/javascript-style-guide) - - **Huballin**: [huballin/javascript](https://github.com/huballin/javascript) + - **Huballin**: [huballin](https://github.com/huballin/) - **HubSpot**: [HubSpot/javascript](https://github.com/HubSpot/javascript) - **Hyper**: [hyperoslo/javascript-playbook](https://github.com/hyperoslo/javascript-playbook/blob/master/style.md) - **InterCity Group**: [intercitygroup/javascript-style-guide](https://github.com/intercitygroup/javascript-style-guide) - **Jam3**: [Jam3/Javascript-Code-Conventions](https://github.com/Jam3/Javascript-Code-Conventions) - **JeopardyBot**: [kesne/jeopardy-bot](https://github.com/kesne/jeopardy-bot/blob/master/STYLEGUIDE.md) - **JSSolutions**: [JSSolutions/javascript](https://github.com/JSSolutions/javascript) - - **KickorStick**: [kickorstick/javascript](https://github.com/kickorstick/javascript) + - **Kaplan Komputing**: [kaplankomputing/javascript](https://github.com/kaplankomputing/javascript) + - **KickorStick**: [kickorstick](https://github.com/kickorstick/) - **Kinetica Solutions**: [kinetica/javascript](https://github.com/kinetica/Javascript-style-guide) - **LEINWAND**: [LEINWAND/javascript](https://github.com/LEINWAND/javascript) - **Lonely Planet**: [lonelyplanet/javascript](https://github.com/lonelyplanet/javascript) @@ -3317,24 +3840,27 @@ Other Style Guides - **MitocGroup**: [MitocGroup/javascript](https://github.com/MitocGroup/javascript) - **ModCloth**: [modcloth/javascript](https://github.com/modcloth/javascript) - **Money Advice Service**: [moneyadviceservice/javascript](https://github.com/moneyadviceservice/javascript) - - **Muber**: [muber/javascript](https://github.com/muber/javascript) - - **National Geographic**: [natgeo/javascript](https://github.com/natgeo/javascript) + - **Muber**: [muber](https://github.com/muber/) + - **National Geographic**: [natgeo](https://github.com/natgeo/) - **Nimbl3**: [nimbl3/javascript](https://github.com/nimbl3/javascript) + - **NullDev**: [NullDevCo/JavaScript-Styleguide](https://github.com/NullDevCo/JavaScript-Styleguide) - **Nulogy**: [nulogy/javascript](https://github.com/nulogy/javascript) - **Orange Hill Development**: [orangehill/javascript](https://github.com/orangehill/javascript) - **Orion Health**: [orionhealth/javascript](https://github.com/orionhealth/javascript) - **OutBoxSoft**: [OutBoxSoft/javascript](https://github.com/OutBoxSoft/javascript) - **Peerby**: [Peerby/javascript](https://github.com/Peerby/javascript) + - **Pier 1**: [Pier1/javascript](https://github.com/pier1/javascript) + - **Qotto**: [Qotto/javascript-style-guide](https://github.com/Qotto/javascript-style-guide) - **Razorfish**: [razorfish/javascript-style-guide](https://github.com/razorfish/javascript-style-guide) - **reddit**: [reddit/styleguide/javascript](https://github.com/reddit/styleguide/tree/master/javascript) - **React**: [facebook.github.io/react/contributing/how-to-contribute.html#style-guide](https://facebook.github.io/react/contributing/how-to-contribute.html#style-guide) - - **REI**: [reidev/js-style-guide](https://github.com/rei/code-style-guides/blob/master/docs/javascript.md) + - **REI**: [reidev/js-style-guide](https://github.com/rei/code-style-guides/) - **Ripple**: [ripple/javascript-style-guide](https://github.com/ripple/javascript-style-guide) - - **Sainsbury's Supermarkets**: [github/jsainsburyplc](https://github.com/jsainsburyplc) + - **Sainsbury’s Supermarkets**: [jsainsburyplc](https://github.com/jsainsburyplc) - **SeekingAlpha**: [seekingalpha/javascript-style-guide](https://github.com/seekingalpha/javascript-style-guide) - **Shutterfly**: [shutterfly/javascript](https://github.com/shutterfly/javascript) - **Sourcetoad**: [sourcetoad/javascript](https://github.com/sourcetoad/javascript) - - **Springload**: [springload/javascript](https://github.com/springload/javascript) + - **Springload**: [springload](https://github.com/springload/) - **StratoDem Analytics**: [stratodem/javascript](https://github.com/stratodem/javascript) - **SteelKiwi Development**: [steelkiwi/javascript](https://github.com/steelkiwi/javascript) - **StudentSphere**: [studentsphere/javascript](https://github.com/studentsphere/guide-javascript) @@ -3342,9 +3868,13 @@ Other Style Guides - **SysGarage**: [sysgarage/javascript-style-guide](https://github.com/sysgarage/javascript-style-guide) - **Syzygy Warsaw**: [syzygypl/javascript](https://github.com/syzygypl/javascript) - **Target**: [target/javascript](https://github.com/target/javascript) + - **Terra**: [terra](https://github.com/cerner?utf8=%E2%9C%93&q=terra&type=&language=) - **TheLadders**: [TheLadders/javascript](https://github.com/TheLadders/javascript) - **The Nerdery**: [thenerdery/javascript-standards](https://github.com/thenerdery/javascript-standards) + - **Tomify**: [tomprats](https://github.com/tomprats) + - **Traitify**: [traitify/eslint-config-traitify](https://github.com/traitify/eslint-config-traitify) - **T4R Technology**: [T4R-Technology/javascript](https://github.com/T4R-Technology/javascript) + - **UrbanSim**: [urbansim](https://github.com/urbansim/) - **VoxFeed**: [VoxFeed/javascript-style-guide](https://github.com/VoxFeed/javascript-style-guide) - **WeBox Studio**: [weboxstudio/javascript](https://github.com/weboxstudio/javascript) - **Weggo**: [Weggo/javascript](https://github.com/Weggo/javascript) @@ -3360,19 +3890,19 @@ Other Style Guides - ![br](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Brazil.png) **Brazilian Portuguese**: [armoucar/javascript-style-guide](https://github.com/armoucar/javascript-style-guide) - ![bg](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Bulgaria.png) **Bulgarian**: [borislavvv/javascript](https://github.com/borislavvv/javascript) - ![ca](https://raw.githubusercontent.com/fpmweb/javascript-style-guide/master/img/catala.png) **Catalan**: [fpmweb/javascript-style-guide](https://github.com/fpmweb/javascript-style-guide) - - ![cn](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/China.png) **Chinese (Simplified)**: [sivan/javascript-style-guide](https://github.com/sivan/javascript-style-guide) + - ![cn](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/China.png) **Chinese (Simplified)**: [lin-123/javascript](https://github.com/lin-123/javascript) - ![tw](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Taiwan.png) **Chinese (Traditional)**: [jigsawye/javascript](https://github.com/jigsawye/javascript) - ![fr](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/France.png) **French**: [nmussy/javascript-style-guide](https://github.com/nmussy/javascript-style-guide) - ![de](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Germany.png) **German**: [timofurrer/javascript-style-guide](https://github.com/timofurrer/javascript-style-guide) - ![it](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Italy.png) **Italian**: [sinkswim/javascript-style-guide](https://github.com/sinkswim/javascript-style-guide) - ![jp](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Japan.png) **Japanese**: [mitsuruog/javascript-style-guide](https://github.com/mitsuruog/javascript-style-guide) - - ![kr](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/South-Korea.png) **Korean**: [tipjs/javascript-style-guide](https://github.com/tipjs/javascript-style-guide) - - ![pl](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Poland.png) **Polish**: [mjurczyk/javascript](https://github.com/mjurczyk/javascript) + - ![kr](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/South-Korea.png) **Korean**: [ParkSB/javascript-style-guide](https://github.com/ParkSB/javascript-style-guide) - ![ru](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Russia.png) **Russian**: [leonidlebedev/javascript-airbnb](https://github.com/leonidlebedev/javascript-airbnb) - ![es](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Spain.png) **Spanish**: [paolocarrasco/javascript-style-guide](https://github.com/paolocarrasco/javascript-style-guide) - ![th](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Thailand.png) **Thai**: [lvarayut/javascript-style-guide](https://github.com/lvarayut/javascript-style-guide) + - ![tr](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Turkey.png) **Turkish**: [eraycetinay/javascript](https://github.com/eraycetinay/javascript) - ![ua](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Ukraine.png) **Ukrainian**: [ivanzusko/javascript](https://github.com/ivanzusko/javascript) - - ![vn](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Vietnam.png) **Vietnam**: [hngiang/javascript-style-guide](https://github.com/hngiang/javascript-style-guide) + - ![vn](https://raw.githubusercontent.com/gosquared/flags/master/flags/flags/shiny/24/Vietnam.png) **Vietnam**: [dangkyokhoang/javascript-style-guide](https://github.com/dangkyokhoang/javascript-style-guide) ## The JavaScript Style Guide Guide @@ -3390,7 +3920,7 @@ Other Style Guides (The MIT License) -Copyright (c) 2014-2017 Airbnb +Copyright (c) 2012 Airbnb Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/css-in-javascript/README.md b/css-in-javascript/README.md index 13099410a9..d6162df55d 100644 --- a/css-in-javascript/README.md +++ b/css-in-javascript/README.md @@ -1,6 +1,6 @@ # Airbnb CSS-in-JavaScript Style Guide -*A mostly reasonable approach to CSS-in-JavaScript +*A mostly reasonable approach to CSS-in-JavaScript* ## Table of Contents @@ -429,4 +429,4 @@ --- -CSS puns adapted from [Saijo George](http://saijogeorge.com/css-puns/). +CSS puns adapted from [Saijo George](https://saijogeorge.com/css-puns/). diff --git a/linters/.markdownlint.json b/linters/.markdownlint.json index e7a019fed8..594886042d 100644 --- a/linters/.markdownlint.json +++ b/linters/.markdownlint.json @@ -2,13 +2,13 @@ "comment": "Be explicit by listing every available rule. https://github.com/DavidAnson/markdownlint/blob/master/doc/Rules.md", "comment": "Note that there will be numeric gaps, not every MD number is implemented in markdownlint.", - "comment": "MD001: Header levels should only increment by one level at a time", + "comment": "MD001: Header levels should only increment by one level at a time.", "header-increment": true, - "comment": "MD002: First header should be a top level header", + "comment": "MD002: First header should be a top level header.", "first-header-h1": true, - "comment": "MD003: Header style: start with hashes", + "comment": "MD003: Header style: start with hashes.", "header-style": { "style": "atx" }, @@ -18,25 +18,25 @@ "style": "dash" }, - "comment": "MD005: Consistent indentation for list items at the same level", + "comment": "MD005: Consistent indentation for list items at the same level.", "list-indent": true, - "comment": "MD006: Consider starting bulleted lists at the beginning of the line", + "comment": "MD006: Consider starting bulleted lists at the beginning of the line.", "ul-start-left": false, - "comment": "MD007: Unordered list indentation: 2 spaces", + "comment": "MD007: Unordered list indentation: 2 spaces.", "ul-indent": { "indent": 2 }, - "comment": "MD009: Disallow trailing spaces", + "comment": "MD009: Disallow trailing spaces!", "no-trailing-spaces": { - "br-spaces": 0, - "comment": "Empty lines inside list items should not be indented", + "br_spaces": 0, + "comment": "Empty lines inside list items should not be indented.", "list_item_empty_lines": false }, - "comment": "MD010: No hard tabs, not even in code blocks", + "comment": "MD010: No hard tabs, not even in code blocks.", "no-hard-tabs": { "code_blocks": true }, @@ -44,7 +44,7 @@ "comment": "MD011: Prevent reversed link syntax", "no-reversed-links": true, - "comment": "MD012: Disallow multiple consecutive blank lines", + "comment": "MD012: Disallow multiple consecutive blank lines.", "no-multiple-blanks": { "maximum": 1 }, @@ -52,37 +52,37 @@ "comment": "MD013: Line length", "line-length": false, - "comment": "MD014: Disallow dollar signs used before commands without showing output", + "comment": "MD014: Disallow use of dollar signs($) before commands without showing output.", "commands-show-output": true, - "comment": "MD018: Disallow space after hash on atx style header", + "comment": "MD018: Disallow space after hash on atx style header.", "no-missing-space-atx": true, - "comment": "MD019: Dissalow multiple spaces after hash on atx style header", + "comment": "MD019: Disallow multiple spaces after hash on atx style header.", "no-multiple-space-atx": true, - "comment": "MD020: No space inside hashes on closed atx style header", + "comment": "MD020: No space should be inside hashes on closed atx style header.", "no-missing-space-closed-atx": true, - "comment": "MD021: Disallow multiple spaces inside hashes on closed atx style header", + "comment": "MD021: Disallow multiple spaces inside hashes on closed atx style header.", "no-multiple-space-closed-atx": true, - "comment": "MD022: Headers should be surrounded by blank lines", + "comment": "MD022: Headers should be surrounded by blank lines.", "comment": "Some headers have preceeding HTML anchors. Unfortunate that we have to disable this, as it otherwise catches a real problem that trips up some Markdown renderers", "blanks-around-headers": false, - "comment": "MD023: Headers must start at the beginning of the line", + "comment": "MD023: Headers must start at the beginning of the line.", "header-start-left": true, - "comment": "MD024: Disallow multiple headers with the same content", + "comment": "MD024: Disallow multiple headers with the same content.", "no-duplicate-header": true, - "comment": "MD025: Disallow multiple top level headers in the same document", - "comment": "Gotta have a matching closing brace at the end", + "comment": "MD025: Disallow multiple top level headers in the same document.", + "comment": "Gotta have a matching closing brace at the end.", "single-h1": false, - "comment": "MD026: Disallow trailing punctuation in header", - "comment": "Gotta have a semicolon after the ending closing brace", + "comment": "MD026: Disallow trailing punctuation in header.", + "comment": "You must have a semicolon after the ending closing brace.", "no-trailing-punctuation": { "punctuation" : ".,:!?" }, @@ -117,7 +117,7 @@ "comment": "HTML is needed for explicit anchors", "no-inline-html": false, - "comment": "MD034: No bare URLs used", + "comment": "MD034: No bare URLs should be used", "no-bare-urls": true, "comment": "MD035: Horizontal rule style", @@ -125,30 +125,30 @@ "style": "consistent" }, - "comment": "MD036: Do not use emphasis instead of a header", + "comment": "MD036: Do not use emphasis instead of a header.", "no-emphasis-as-header": false, - "comment": "MD037: Disallow spaces inside emphasis markers", + "comment": "MD037: Disallow spaces inside emphasis markers.", "no-space-in-emphasis": true, - "comment": "MD038: Disallow spaces inside code span elements", + "comment": "MD038: Disallow spaces inside code span elements.", "no-space-in-code": true, - "comment": "MD039: Disallow spaces inside link text", + "comment": "MD039: Disallow spaces inside link text.", "no-space-in-links": true, - "comment": "MD040: Fenced code blocks should have a language specified", + "comment": "MD040: Fenced code blocks should have a language specified.", "fenced-code-language": true, - "comment": "MD041: First line in file should be a top level header", + "comment": "MD041: First line in file should be a top level header.", "first-line-h1": true, "comment": "MD042: No empty links", "no-empty-links": true, - "comment": "MD043: Required header structure", + "comment": "MD043: Required header structure.", "required-headers": false, - "comment": "MD044: Proper names should have the correct capitalization", + "comment": "MD044: Proper names should have the correct capitalization.", "proper-names": false } diff --git a/package.json b/package.json index b50dd7a121..581c94f612 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,9 @@ "airbnb", "es6", "es2015", + "es2016", + "es2017", + "es2018", "react", "jsx" ], @@ -36,6 +39,6 @@ }, "homepage": "https://github.com/airbnb/javascript", "devDependencies": { - "markdownlint-cli": "^0.3.1" + "markdownlint-cli": "^0.13.0" } } diff --git a/packages/eslint-config-airbnb-base/.eslintrc b/packages/eslint-config-airbnb-base/.eslintrc index 224149fda6..f9bd289286 100644 --- a/packages/eslint-config-airbnb-base/.eslintrc +++ b/packages/eslint-config-airbnb-base/.eslintrc @@ -3,6 +3,8 @@ "rules": { // disable requiring trailing commas because it might be nice to revert to // being JSON at some point, and I don't want to make big changes now. - "comma-dangle": 0 + "comma-dangle": 0, + // we support node 4 + "prefer-destructuring": 0, }, } diff --git a/packages/eslint-config-airbnb-base/CHANGELOG.md b/packages/eslint-config-airbnb-base/CHANGELOG.md index b238e2e329..73105da143 100644 --- a/packages/eslint-config-airbnb-base/CHANGELOG.md +++ b/packages/eslint-config-airbnb-base/CHANGELOG.md @@ -1,3 +1,113 @@ +14.0.0 / 2019-08-09 +================== + - [breaking] `no-self-assign`: enable `props` option + - [breaking] enable `no-useless-catch` + - [breaking] enable `max-classes-per-file` + - [breaking] enable `no-misleading-character-class` + - [breaking] enable `no-async-promise-executor` + - [breaking] enable `prefer-object-spread` + - [breaking] `func-name-matching`: enable `considerPropertyDescriptor` option + - [breaking] `padded-blocks`: enable `allowSingleLineBlocks` option (#1255) + - [breaking] `no-multiple-empty-lines`: Restrict empty lines at beginning of file (#2042) + - [breaking] Set 'strict' to 'never' (#1962) + - [breaking] legacy: Enable 'strict' (#1962) + - [breaking] Simplifies `no-mixed-operators` (#1864) + - [breaking] Require parens for arrow function args (#1863) + - [breaking] add eslint v6, drop eslint v4 + - [patch] `camelcase`: enable ignoreDestructuring + - [patch] Add markers to spaced-comment block for Flow types (#1966) + - [patch] Do not prefer destructuring for object assignment expression (#1583) + - [deps] update `confusing-browser-globals`, `eslint-plugin-import`, `tape`, `babel-preset-airbnb` + - [dev deps] update babel-related deps to latest + - [dev deps] update `eslint-find-rules`, `eslint-plugin-import` + - [tests] only run tests in non-lint per-package travis job + - [tests] use `eclint` instead of `editorconfig-tools` + +13.2.0 / 2019-07-01 +================== + - [minor] Enforce dangling underscores in method names (#1907) + - [fix] disable `no-var` in legacy entry point + - [patch] Ignore property modifications of `staticContext` params (#2029) + - [patch] `no-extraneous-dependencies`: Add jest.setup.js to devDeps (#1998) + - [meta] add disabled `prefer-named-capture-group` rule + - [meta] add disabled `no-useless-catch` config + - [deps] Switch to confusing-browser-globals (#1961) + - [deps] update `object.entries`, `eslint-plugin-import`, `eslint-plugin-jsx-a11y`, `eslint-plugin-react`, `tape` + - [docs] correct JavaScript capitalization (#2046) + - [readme] Improve eslint config setup instructions for yarn (#2001) + - [docs] fix docs for whitespace config (#1914, #1871) + +13.1.0 / 2018-08-13 +================== + - [new] add eslint v5 support (#1834) + - [deps] update `eslint-plugin-import`, `eslint`, `babel-preset-airbnb`, `safe-publish-latest`, `eslint-find-rules` + - [docs] fix typo in readme (#1855) + - [new] update base ecmaVersion to 2018; remove deprecated experimentalObjectRestSpread option + +13.0.0 / 2018-06-21 +================== + - [breaking] order of import statements is ignored for unassigned imports (#1782) + - [breaking] enable `import/no-cycle`: warn on cyclical dependencies (#1779) + - [breaking] Change import/no-self-import from "off" to "error" (#1770) + - [breaking] Update `object-curly-newline` to match eslint 4.18.0 (#1761) + - [breaking] enable `no-useless-path-segments` (#1743) + - [breaking] Prevent line breaks before and after `=` (#1710) + - [breaking] Add .mjs extension support (#1634) + - [breaking] enable `implicit-arrow-linebreak` + - [breaking] Enables `nonblock-statement-body-position` rule and adds link to guide (#1618) + - [breaking] `no-mixed-operators`: only warn on `**` and `%` mixed with arithmetic operators; removes violation against mixing common math operators. (#1611) + - [breaking] `import/named`: enable + - [breaking] `lines-between-class-members`: set to “always” + - [breaking] `no-else-return`: disallow else-if (#1595) + - [breaking] Enables eslint rule for operator-linebreak + - [new] Adds config entry point with only whitespace rules enabled (#1749, #1751) + - [minor] only allow one newline at the end (#1794) + - [patch] Adjust imports for vue-cli (#1809) + - [patch] Allow devDependencies for `foo_spec.js` naming style (#1732) + - [patch] `function-paren-newline`: change to "consistent" + - [patch] avoid `__mocks__` `no-extraneous-dependencies` check (#1772) + - [patch] Include 'accumulator' exception for `no-param-reassign` (#1768) + - [patch] Set import/extensions to ignorePackages (#1652) + - [patch] properly ignore indentation on jsx + - [patch] `array-callback-return`: enable `allowImplicit` option (#1668) + - [deps] update `eslint`, `eslint-plugin-import` + - [dev deps] update `babel-preset-airbnb`, `tape`, `eslint-find-rules` + - [meta] add ES2015-2018 in npm package keywords (#1587) + - [meta] Add licenses to sub packages (#1746) + - [docs] add `npx` shortcut (#1694) + - [docs] Use HTTPS for links to ESLint documentation (#1628) + - [tests] ensure all entry points parse + +12.1.0 / 2017-10-16 +================== + - [deps] update `eslint` to `v4.9` + +12.0.2 / 2017-10-05 +================== + - [deps] update `eslint` + +12.0.1 / 2017-09-27 +================== + - [fix] ensure all JSX elements are ignored by `indent` (#1569) + - [deps] update `eslint` + +12.0.0 / 2017-09-02 +================== + - [deps] [breaking] require `eslint` v4 + - enable `function-paren-newline`, `for-direction`, `getter-return`, `no-compare-neg-zero`, `semi-style`, `object-curly-newline`, `no-buffer-constructor`, `no-restricted-globals`, `switch-colon-spacing`, `template-tag-spacing`, `prefer-promise-reject-errors`, `prefer-destructuring` + - improve `indent`, `no-multi-spaces`, `no-trailing-spaces`, `no-underscore-dangle` + - [breaking] move `comma-dangle` to Stylistic Issues (#1514) + - [breaking] Rules prohibiting global isNaN, isFinite (#1477) + - [patch] also disallow padding in classes and switches (#1403) + - [patch] support Protractor config files in import/no-extraneous-dependencies (#1543) + +11.3.2 / 2017-08-22 +================== + - [patch] Add jest.config.js to import/no-extraneous-dependencies devDeps (#1522) + - [patch] Improve Gruntfile glob pattern (#1503) + - [deps] update `eslint` v4, `tape` + - [docs] Specify yarn-specific install instructions (#1511) + 11.3.1 / 2017-07-24 ================== - [fix] `legacy`: remove top-level `ecmaFeatures` diff --git a/packages/eslint-config-airbnb-base/LICENSE.md b/packages/eslint-config-airbnb-base/LICENSE.md new file mode 100644 index 0000000000..69d80c0252 --- /dev/null +++ b/packages/eslint-config-airbnb-base/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2012 Airbnb + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/eslint-config-airbnb-base/README.md b/packages/eslint-config-airbnb-base/README.md index 968cec06cd..685e50060b 100644 --- a/packages/eslint-config-airbnb-base/README.md +++ b/packages/eslint-config-airbnb-base/README.md @@ -12,15 +12,24 @@ We export two ESLint configurations for your usage. Our default export contains all of our ESLint rules, including ECMAScript 6+. It requires `eslint` and `eslint-plugin-import`. -If you use yarn, run `yarn add --dev eslint-config-airbnb-base eslint-plugin-import`, or see below for npm instructions. - 1. Install the correct versions of each package, which are listed by the command: ```sh npm info "eslint-config-airbnb-base@latest" peerDependencies ``` - Linux/OSX users can run + If using **npm 5+**, use this shortcut + + ```sh + npx install-peerdeps --dev eslint-config-airbnb-base + ``` + + If using **yarn**, you can also use the shortcut described above if you have npm 5+ installed on your machine, as the command will detect that you are using yarn and will act accordingly. + Otherwise, run `npm info "eslint-config-airbnb-base@latest" peerDependencies` to list the peer dependencies and versions, then run `yarn add --dev @` for each listed peer dependency. + + + If using **npm < 5**, Linux/OSX users can run + ```sh ( export PKG=eslint-config-airbnb-base; @@ -34,7 +43,7 @@ If you use yarn, run `yarn add --dev eslint-config-airbnb-base eslint-plugin-imp npm install --save-dev eslint-config-airbnb-base eslint@^#.#.# eslint-plugin-import@^#.#.# ``` - Windows users can either install all the peer dependencies manually, or use the [install-peerdeps](https://github.com/nathanhleung/install-peerdeps) cli tool. + If using **npm < 5**, Windows users can either install all the peer dependencies manually, or use the [install-peerdeps](https://github.com/nathanhleung/install-peerdeps) cli tool. ```sh npm install -g install-peerdeps @@ -75,7 +84,11 @@ Lints ES5 and below. Requires `eslint` and `eslint-plugin-import`. 2. Add `"extends": "airbnb-base/legacy"` to your .eslintrc -See [Airbnb's overarching ESLint config](https://npmjs.com/eslint-config-airbnb), [Airbnb's Javascript styleguide](https://github.com/airbnb/javascript), and the [ESlint config docs](http://eslint.org/docs/user-guide/configuring#extending-configuration-files) for more information. +See [Airbnb's overarching ESLint config](https://npmjs.com/eslint-config-airbnb), [Airbnb's JavaScript styleguide](https://github.com/airbnb/javascript), and the [ESlint config docs](https://eslint.org/docs/user-guide/configuring#extending-configuration-files) for more information. + +### eslint-config-airbnb-base/whitespace + +This entry point only errors on whitespace rules and sets all other rules to warnings. View the list of whitespace rules [here](https://github.com/airbnb/javascript/blob/master/packages/eslint-config-airbnb-base/whitespace.js). ## Improving this config diff --git a/packages/eslint-config-airbnb-base/index.js b/packages/eslint-config-airbnb-base/index.js index 4de657b075..825456b8a4 100644 --- a/packages/eslint-config-airbnb-base/index.js +++ b/packages/eslint-config-airbnb-base/index.js @@ -7,15 +7,11 @@ module.exports = { './rules/variables', './rules/es6', './rules/imports', + './rules/strict', ].map(require.resolve), parserOptions: { - ecmaVersion: 2017, + ecmaVersion: 2018, sourceType: 'module', - ecmaFeatures: { - experimentalObjectRestSpread: true, - }, - }, - rules: { - strict: 'error', }, + rules: {}, }; diff --git a/packages/eslint-config-airbnb-base/legacy.js b/packages/eslint-config-airbnb-base/legacy.js index 7cc2441ab3..18576f83e5 100644 --- a/packages/eslint-config-airbnb-base/legacy.js +++ b/packages/eslint-config-airbnb-base/legacy.js @@ -27,5 +27,7 @@ module.exports = { property: '__defineSetter__', message: 'Please use Object.defineProperty instead.', }], + 'no-var': 'off', + strict: ['error', 'safe'], } }; diff --git a/packages/eslint-config-airbnb-base/package.json b/packages/eslint-config-airbnb-base/package.json index b4a676ac05..99830bcb67 100644 --- a/packages/eslint-config-airbnb-base/package.json +++ b/packages/eslint-config-airbnb-base/package.json @@ -1,17 +1,18 @@ { "name": "eslint-config-airbnb-base", - "version": "11.3.1", + "version": "14.0.0", "description": "Airbnb's base JS ESLint config, following our styleguide", "main": "index.js", "scripts": { - "prelint": "editorconfig-tools check * rules/* test/*", - "lint": "eslint .", + "prelint": "eclint check * rules/* test/*", + "lint": "eslint --report-unused-disable-directives .", + "pretests-only": "node ./test/requires", "tests-only": "babel-tape-runner ./test/test-*.js", "prepublish": "(in-install || eslint-find-rules --unused) && (not-in-publish || npm test) && safe-publish-latest", "pretest": "npm run --silent lint", "test": "npm run --silent tests-only", "pretravis": ":", - "travis": "npm run --silent test", + "travis": "npm run --silent tests-only", "posttravis": ":" }, "repository": { @@ -24,7 +25,11 @@ "config", "airbnb", "javascript", - "styleguide" + "styleguide", + "es2015", + "es2016", + "es2017", + "es2018" ], "author": "Jake Teton-Landis (https://twitter.com/@jitl)", "contributors": [ @@ -48,24 +53,27 @@ }, "homepage": "https://github.com/airbnb/javascript", "devDependencies": { - "babel-preset-airbnb": "^2.4.0", - "babel-tape-runner": "^2.0.1", - "editorconfig-tools": "^0.1.1", - "eslint": "^4.3.0", - "eslint-find-rules": "^3.1.1", - "eslint-plugin-import": "^2.7.0", + "@babel/runtime": "^7.5.5", + "babel-preset-airbnb": "^4.0.1", + "babel-tape-runner": "^3.0.0", + "eclint": "^2.8.1", + "eslint": "^5.16.0 || ^6.1.0", + "eslint-find-rules": "^3.4.0", + "eslint-plugin-import": "^2.18.2", "in-publish": "^2.0.0", - "safe-publish-latest": "^1.1.1", - "tape": "^4.7.0" + "safe-publish-latest": "^1.1.2", + "tape": "^4.11.0" }, "peerDependencies": { - "eslint": "^3.19.0 || ^4.3.0", - "eslint-plugin-import": "^2.7.0" + "eslint": "^5.16.0 || ^6.1.0", + "eslint-plugin-import": "^2.18.2" }, "engines": { - "node": ">= 4" + "node": ">= 6" }, "dependencies": { - "eslint-restricted-globals": "^0.1.1" + "confusing-browser-globals": "^1.0.7", + "object.assign": "^4.1.0", + "object.entries": "^1.1.0" } } diff --git a/packages/eslint-config-airbnb-base/rules/best-practices.js b/packages/eslint-config-airbnb-base/rules/best-practices.js index 02ea3e23b3..41f85487a9 100644 --- a/packages/eslint-config-airbnb-base/rules/best-practices.js +++ b/packages/eslint-config-airbnb-base/rules/best-practices.js @@ -4,8 +4,8 @@ module.exports = { 'accessor-pairs': 'off', // enforces return statements in callbacks of array's methods - // http://eslint.org/docs/rules/array-callback-return - 'array-callback-return': 'error', + // https://eslint.org/docs/rules/array-callback-return + 'array-callback-return': ['error', { allowImplicit: true }], // treat var statements as if they were block scoped 'block-scoped-var': 'error', @@ -14,7 +14,7 @@ module.exports = { complexity: ['off', 11], // enforce that class methods use "this" - // http://eslint.org/docs/rules/class-methods-use-this + // https://eslint.org/docs/rules/class-methods-use-this 'class-methods-use-this': ['error', { exceptMethods: [], }], @@ -32,16 +32,20 @@ module.exports = { 'dot-notation': ['error', { allowKeywords: true }], // enforces consistent newlines before or after dots - // http://eslint.org/docs/rules/dot-location + // https://eslint.org/docs/rules/dot-location 'dot-location': ['error', 'property'], // require the use of === and !== - // http://eslint.org/docs/rules/eqeqeq + // https://eslint.org/docs/rules/eqeqeq eqeqeq: ['error', 'always', { null: 'ignore' }], // make sure for-in loops have an if statement 'guard-for-in': 'error', + // enforce a maximum number of classes per file + // https://eslint.org/docs/rules/max-classes-per-file + 'max-classes-per-file': ['error', 1], + // disallow the use of alert, confirm, and prompt 'no-alert': 'warn', @@ -49,18 +53,19 @@ module.exports = { 'no-caller': 'error', // disallow lexical declarations in case/default clauses - // http://eslint.org/docs/rules/no-case-declarations.html + // https://eslint.org/docs/rules/no-case-declarations.html 'no-case-declarations': 'error', // disallow division operators explicitly at beginning of regular expression - // http://eslint.org/docs/rules/no-div-regex + // https://eslint.org/docs/rules/no-div-regex 'no-div-regex': 'off', // disallow else after a return in an if - 'no-else-return': 'error', + // https://eslint.org/docs/rules/no-else-return + 'no-else-return': ['error', { allowElseIf: false }], // disallow empty functions, except for standalone funcs/arrows - // http://eslint.org/docs/rules/no-empty-function + // https://eslint.org/docs/rules/no-empty-function 'no-empty-function': ['error', { allow: [ 'arrowFunctions', @@ -70,7 +75,7 @@ module.exports = { }], // disallow empty destructuring patterns - // http://eslint.org/docs/rules/no-empty-pattern + // https://eslint.org/docs/rules/no-empty-pattern 'no-empty-pattern': 'error', // disallow comparisons to null without a type-checking operator @@ -86,7 +91,7 @@ module.exports = { 'no-extra-bind': 'error', // disallow Unnecessary Labels - // http://eslint.org/docs/rules/no-extra-label + // https://eslint.org/docs/rules/no-extra-label 'no-extra-label': 'error', // disallow fallthrough of case statements @@ -96,13 +101,13 @@ module.exports = { 'no-floating-decimal': 'error', // disallow reassignments of native objects or read-only globals - // http://eslint.org/docs/rules/no-global-assign + // https://eslint.org/docs/rules/no-global-assign 'no-global-assign': ['error', { exceptions: [] }], // deprecated in favor of no-global-assign 'no-native-reassign': 'off', // disallow implicit type conversions - // http://eslint.org/docs/rules/no-implicit-coercion + // https://eslint.org/docs/rules/no-implicit-coercion 'no-implicit-coercion': ['off', { boolean: false, number: true, @@ -111,7 +116,7 @@ module.exports = { }], // disallow var and named functions in global scope - // http://eslint.org/docs/rules/no-implicit-globals + // https://eslint.org/docs/rules/no-implicit-globals 'no-implicit-globals': 'off', // disallow use of eval()-like methods @@ -133,7 +138,7 @@ module.exports = { 'no-loop-func': 'error', // disallow magic numbers - // http://eslint.org/docs/rules/no-magic-numbers + // https://eslint.org/docs/rules/no-magic-numbers 'no-magic-numbers': ['off', { ignore: [], ignoreArrayIndexes: true, @@ -143,7 +148,7 @@ module.exports = { // disallow use of multiple spaces 'no-multi-spaces': ['error', { - // ignoreEOLComments: false, // TODO: uncomment once v3 is dropped + ignoreEOLComments: false, }], // disallow use of multiline strings @@ -167,11 +172,12 @@ module.exports = { // disallow reassignment of function parameters // disallow parameter object manipulation except for specific exclusions - // rule: http://eslint.org/docs/rules/no-param-reassign.html + // rule: https://eslint.org/docs/rules/no-param-reassign.html 'no-param-reassign': ['error', { props: true, ignorePropertyModificationsFor: [ 'acc', // for reduce accumulators + 'accumulator', // for reduce accumulators 'e', // for e.returnvalue 'ctx', // for Koa routing 'req', // for Express requests @@ -179,6 +185,7 @@ module.exports = { 'res', // for Express responses 'response', // for Express responses '$scope', // for Angular 1 scopes + 'staticContext', // for ReactRouter context ] }], @@ -189,11 +196,35 @@ module.exports = { 'no-redeclare': 'error', // disallow certain object properties - // http://eslint.org/docs/rules/no-restricted-properties + // https://eslint.org/docs/rules/no-restricted-properties 'no-restricted-properties': ['error', { object: 'arguments', property: 'callee', message: 'arguments.callee is deprecated', + }, { + object: 'global', + property: 'isFinite', + message: 'Please use Number.isFinite instead', + }, { + object: 'self', + property: 'isFinite', + message: 'Please use Number.isFinite instead', + }, { + object: 'window', + property: 'isFinite', + message: 'Please use Number.isFinite instead', + }, { + object: 'global', + property: 'isNaN', + message: 'Please use Number.isNaN instead', + }, { + object: 'self', + property: 'isNaN', + message: 'Please use Number.isNaN instead', + }, { + object: 'window', + property: 'isNaN', + message: 'Please use Number.isNaN instead', }, { property: '__defineGetter__', message: 'Please use Object.defineProperty instead.', @@ -216,8 +247,10 @@ module.exports = { 'no-script-url': 'error', // disallow self assignment - // http://eslint.org/docs/rules/no-self-assign - 'no-self-assign': 'error', + // https://eslint.org/docs/rules/no-self-assign + 'no-self-assign': ['error', { + props: true, + }], // disallow comparisons where both sides are exactly the same 'no-self-compare': 'error', @@ -229,7 +262,7 @@ module.exports = { 'no-throw-literal': 'error', // disallow unmodified conditions of loops - // http://eslint.org/docs/rules/no-unmodified-loop-condition + // https://eslint.org/docs/rules/no-unmodified-loop-condition 'no-unmodified-loop-condition': 'off', // disallow usage of expressions in statement position @@ -240,26 +273,30 @@ module.exports = { }], // disallow unused labels - // http://eslint.org/docs/rules/no-unused-labels + // https://eslint.org/docs/rules/no-unused-labels 'no-unused-labels': 'error', // disallow unnecessary .call() and .apply() 'no-useless-call': 'off', + // Disallow unnecessary catch clauses + // https://eslint.org/docs/rules/no-useless-catch + 'no-useless-catch': 'error', + // disallow useless string concatenation - // http://eslint.org/docs/rules/no-useless-concat + // https://eslint.org/docs/rules/no-useless-concat 'no-useless-concat': 'error', // disallow unnecessary string escaping - // http://eslint.org/docs/rules/no-useless-escape + // https://eslint.org/docs/rules/no-useless-escape 'no-useless-escape': 'error', // disallow redundant return; keywords - // http://eslint.org/docs/rules/no-useless-return + // https://eslint.org/docs/rules/no-useless-return 'no-useless-return': 'error', // disallow use of void operator - // http://eslint.org/docs/rules/no-void + // https://eslint.org/docs/rules/no-void 'no-void': 'error', // disallow usage of configurable warning terms in comments: e.g. todo @@ -269,22 +306,29 @@ module.exports = { 'no-with': 'error', // require using Error objects as Promise rejection reasons - // http://eslint.org/docs/rules/prefer-promise-reject-errors - // TODO: enable, semver-major - 'prefer-promise-reject-errors': ['off', { allowEmptyReject: true }], + // https://eslint.org/docs/rules/prefer-promise-reject-errors + 'prefer-promise-reject-errors': ['error', { allowEmptyReject: true }], + + // Suggest using named capture group in regular expression + // https://eslint.org/docs/rules/prefer-named-capture-group + 'prefer-named-capture-group': 'off', // require use of the second argument for parseInt() radix: 'error', // require `await` in `async function` (note: this is a horrible rule that should never be used) - // http://eslint.org/docs/rules/require-await + // https://eslint.org/docs/rules/require-await 'require-await': 'off', + // Enforce the use of u flag on RegExp + // https://eslint.org/docs/rules/require-unicode-regexp + 'require-unicode-regexp': 'off', + // requires to declare all vars on top of their containing scope 'vars-on-top': 'error', // require immediate function invocation to be wrapped in parentheses - // http://eslint.org/docs/rules/wrap-iife.html + // https://eslint.org/docs/rules/wrap-iife.html 'wrap-iife': ['error', 'outside', { functionPrototypeMethods: false }], // require or disallow Yoda conditions diff --git a/packages/eslint-config-airbnb-base/rules/errors.js b/packages/eslint-config-airbnb-base/rules/errors.js index 02befbb5af..5a56844e7c 100644 --- a/packages/eslint-config-airbnb-base/rules/errors.js +++ b/packages/eslint-config-airbnb-base/rules/errors.js @@ -1,32 +1,24 @@ module.exports = { rules: { - // require trailing commas in multiline object literals - 'comma-dangle': ['error', { - arrays: 'always-multiline', - objects: 'always-multiline', - imports: 'always-multiline', - exports: 'always-multiline', - functions: 'always-multiline', - }], - // Enforce “for” loop update clause moving the counter in the right direction - // http://eslint.org/docs/rules/for-direction - // TODO: enable, semver-major until v3 is dropped; semver-minor otherwise - 'for-direction': 'off', + // https://eslint.org/docs/rules/for-direction + 'for-direction': 'error', // Enforces that a return statement is present in property getters - // http://eslint.org/docs/rules/getter-return - // TODO: enable, semver-major when v3 is dropped - 'getter-return': ['off', { allowImplicit: true }], + // https://eslint.org/docs/rules/getter-return + 'getter-return': ['error', { allowImplicit: true }], + + // disallow using an async function as a Promise executor + // https://eslint.org/docs/rules/no-async-promise-executor + 'no-async-promise-executor': 'error', // Disallow await inside of loops - // http://eslint.org/docs/rules/no-await-in-loop + // https://eslint.org/docs/rules/no-await-in-loop 'no-await-in-loop': 'error', // Disallow comparisons to negative zero - // http://eslint.org/docs/rules/no-compare-neg-zero - // TODO: enable (semver-major) - 'no-compare-neg-zero': 'off', + // https://eslint.org/docs/rules/no-compare-neg-zero + 'no-compare-neg-zero': 'error', // disallow assignment in conditional expressions 'no-cond-assign': ['error', 'always'], @@ -62,11 +54,11 @@ module.exports = { 'no-ex-assign': 'error', // disallow double-negation boolean casts in a boolean context - // http://eslint.org/docs/rules/no-extra-boolean-cast + // https://eslint.org/docs/rules/no-extra-boolean-cast 'no-extra-boolean-cast': 'error', // disallow unnecessary parentheses - // http://eslint.org/docs/rules/no-extra-parens + // https://eslint.org/docs/rules/no-extra-parens 'no-extra-parens': ['off', 'all', { conditionalAssign: true, nestedBinaryExpressions: false, @@ -90,11 +82,15 @@ module.exports = { // disallow irregular whitespace outside of strings and comments 'no-irregular-whitespace': 'error', + // Disallow characters which are made with multiple code points in character class syntax + // https://eslint.org/docs/rules/no-misleading-character-class + 'no-misleading-character-class': 'error', + // disallow the use of object properties of the global object (Math and JSON) as functions 'no-obj-calls': 'error', // disallow use of Object.prototypes builtins directly - // http://eslint.org/docs/rules/no-prototype-builtins + // https://eslint.org/docs/rules/no-prototype-builtins 'no-prototype-builtins': 'error', // disallow multiple spaces in a regular expression literal @@ -104,36 +100,41 @@ module.exports = { 'no-sparse-arrays': 'error', // Disallow template literal placeholder syntax in regular strings - // http://eslint.org/docs/rules/no-template-curly-in-string + // https://eslint.org/docs/rules/no-template-curly-in-string 'no-template-curly-in-string': 'error', // Avoid code that looks like two expressions but is actually one - // http://eslint.org/docs/rules/no-unexpected-multiline + // https://eslint.org/docs/rules/no-unexpected-multiline 'no-unexpected-multiline': 'error', // disallow unreachable statements after a return, throw, continue, or break statement 'no-unreachable': 'error', // disallow return/throw/break/continue inside finally blocks - // http://eslint.org/docs/rules/no-unsafe-finally + // https://eslint.org/docs/rules/no-unsafe-finally 'no-unsafe-finally': 'error', // disallow negating the left operand of relational operators - // http://eslint.org/docs/rules/no-unsafe-negation + // https://eslint.org/docs/rules/no-unsafe-negation 'no-unsafe-negation': 'error', // disallow negation of the left operand of an in expression // deprecated in favor of no-unsafe-negation 'no-negated-in-lhs': 'off', + // Disallow assignments that can lead to race conditions due to usage of await or yield + // https://eslint.org/docs/rules/require-atomic-updates + // TODO: enable, semver-major + 'require-atomic-updates': 'off', + // disallow comparisons with the value NaN 'use-isnan': 'error', // ensure JSDoc comments are valid - // http://eslint.org/docs/rules/valid-jsdoc + // https://eslint.org/docs/rules/valid-jsdoc 'valid-jsdoc': 'off', // ensure that the results of typeof are compared against a valid string - // http://eslint.org/docs/rules/valid-typeof + // https://eslint.org/docs/rules/valid-typeof 'valid-typeof': ['error', { requireStringLiterals: true }], } }; diff --git a/packages/eslint-config-airbnb-base/rules/es6.js b/packages/eslint-config-airbnb-base/rules/es6.js index a77f3a6641..bfdc6d29e4 100644 --- a/packages/eslint-config-airbnb-base/rules/es6.js +++ b/packages/eslint-config-airbnb-base/rules/es6.js @@ -13,35 +13,33 @@ module.exports = { rules: { // enforces no braces where they can be omitted - // http://eslint.org/docs/rules/arrow-body-style + // https://eslint.org/docs/rules/arrow-body-style // TODO: enable requireReturnForObjectLiteral? 'arrow-body-style': ['error', 'as-needed', { requireReturnForObjectLiteral: false, }], // require parens in arrow function arguments - // http://eslint.org/docs/rules/arrow-parens - 'arrow-parens': ['error', 'as-needed', { - requireForBlockBody: true, - }], + // https://eslint.org/docs/rules/arrow-parens + 'arrow-parens': ['error', 'always'], // require space before/after arrow function's arrow - // http://eslint.org/docs/rules/arrow-spacing + // https://eslint.org/docs/rules/arrow-spacing 'arrow-spacing': ['error', { before: true, after: true }], // verify super() callings in constructors 'constructor-super': 'error', // enforce the spacing around the * in generator functions - // http://eslint.org/docs/rules/generator-star-spacing + // https://eslint.org/docs/rules/generator-star-spacing 'generator-star-spacing': ['error', { before: false, after: true }], // disallow modifying variables of class declarations - // http://eslint.org/docs/rules/no-class-assign + // https://eslint.org/docs/rules/no-class-assign 'no-class-assign': 'error', // disallow arrow functions where they could be confused with comparisons - // http://eslint.org/docs/rules/no-confusing-arrow + // https://eslint.org/docs/rules/no-confusing-arrow 'no-confusing-arrow': ['error', { allowParens: true, }], @@ -50,36 +48,39 @@ module.exports = { 'no-const-assign': 'error', // disallow duplicate class members - // http://eslint.org/docs/rules/no-dupe-class-members + // https://eslint.org/docs/rules/no-dupe-class-members 'no-dupe-class-members': 'error', // disallow importing from the same path more than once - // http://eslint.org/docs/rules/no-duplicate-imports + // https://eslint.org/docs/rules/no-duplicate-imports // replaced by https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-duplicates.md 'no-duplicate-imports': 'off', // disallow symbol constructor - // http://eslint.org/docs/rules/no-new-symbol + // https://eslint.org/docs/rules/no-new-symbol 'no-new-symbol': 'error', // disallow specific imports - // http://eslint.org/docs/rules/no-restricted-imports - 'no-restricted-imports': 'off', + // https://eslint.org/docs/rules/no-restricted-imports + 'no-restricted-imports': ['off', { + paths: [], + patterns: [] + }], // disallow to use this/super before super() calling in constructors. - // http://eslint.org/docs/rules/no-this-before-super + // https://eslint.org/docs/rules/no-this-before-super 'no-this-before-super': 'error', // disallow useless computed property keys - // http://eslint.org/docs/rules/no-useless-computed-key + // https://eslint.org/docs/rules/no-useless-computed-key 'no-useless-computed-key': 'error', // disallow unnecessary constructor - // http://eslint.org/docs/rules/no-useless-constructor + // https://eslint.org/docs/rules/no-useless-constructor 'no-useless-constructor': 'error', // disallow renaming import, export, and destructured assignments to the same name - // http://eslint.org/docs/rules/no-useless-rename + // https://eslint.org/docs/rules/no-useless-rename 'no-useless-rename': ['error', { ignoreDestructuring: false, ignoreImport: false, @@ -90,7 +91,7 @@ module.exports = { 'no-var': 'error', // require method and property shorthand syntax for object literals - // http://eslint.org/docs/rules/object-shorthand + // https://eslint.org/docs/rules/object-shorthand 'object-shorthand': ['error', 'always', { ignoreConstructors: false, avoidQuotes: true, @@ -109,67 +110,67 @@ module.exports = { }], // Prefer destructuring from arrays and objects - // http://eslint.org/docs/rules/prefer-destructuring - // TODO: enable - 'prefer-destructuring': ['off', { + // https://eslint.org/docs/rules/prefer-destructuring + 'prefer-destructuring': ['error', { VariableDeclarator: { array: false, object: true, }, AssignmentExpression: { array: true, - object: true, + object: false, }, }, { enforceForRenamedProperties: false, }], // disallow parseInt() in favor of binary, octal, and hexadecimal literals - // http://eslint.org/docs/rules/prefer-numeric-literals + // https://eslint.org/docs/rules/prefer-numeric-literals 'prefer-numeric-literals': 'error', // suggest using Reflect methods where applicable - // http://eslint.org/docs/rules/prefer-reflect + // https://eslint.org/docs/rules/prefer-reflect 'prefer-reflect': 'off', // use rest parameters instead of arguments - // http://eslint.org/docs/rules/prefer-rest-params + // https://eslint.org/docs/rules/prefer-rest-params 'prefer-rest-params': 'error', // suggest using the spread operator instead of .apply() - // http://eslint.org/docs/rules/prefer-spread + // https://eslint.org/docs/rules/prefer-spread 'prefer-spread': 'error', // suggest using template literals instead of string concatenation - // http://eslint.org/docs/rules/prefer-template + // https://eslint.org/docs/rules/prefer-template 'prefer-template': 'error', // disallow generator functions that do not have yield - // http://eslint.org/docs/rules/require-yield + // https://eslint.org/docs/rules/require-yield 'require-yield': 'error', // enforce spacing between object rest-spread - // http://eslint.org/docs/rules/rest-spread-spacing + // https://eslint.org/docs/rules/rest-spread-spacing 'rest-spread-spacing': ['error', 'never'], // import sorting - // http://eslint.org/docs/rules/sort-imports + // https://eslint.org/docs/rules/sort-imports 'sort-imports': ['off', { ignoreCase: false, + ignoreDeclarationSort: false, ignoreMemberSort: false, memberSyntaxSortOrder: ['none', 'all', 'multiple', 'single'], }], // require a Symbol description - // http://eslint.org/docs/rules/symbol-description + // https://eslint.org/docs/rules/symbol-description 'symbol-description': 'error', // enforce usage of spacing in template strings - // http://eslint.org/docs/rules/template-curly-spacing + // https://eslint.org/docs/rules/template-curly-spacing 'template-curly-spacing': 'error', // enforce spacing around the * in yield* expressions - // http://eslint.org/docs/rules/yield-star-spacing + // https://eslint.org/docs/rules/yield-star-spacing 'yield-star-spacing': ['error', 'after'] } }; diff --git a/packages/eslint-config-airbnb-base/rules/imports.js b/packages/eslint-config-airbnb-base/rules/imports.js index 0150724f6b..1c4e08d36c 100644 --- a/packages/eslint-config-airbnb-base/rules/imports.js +++ b/packages/eslint-config-airbnb-base/rules/imports.js @@ -13,11 +13,12 @@ module.exports = { settings: { 'import/resolver': { node: { - extensions: ['.js', '.json'] + extensions: ['.mjs', '.js', '.json'] } }, 'import/extensions': [ '.js', + '.mjs', '.jsx', ], 'import/core-modules': [ @@ -37,7 +38,7 @@ module.exports = { // ensure named imports coupled with named exports // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/named.md#when-not-to-use-it - 'import/named': 'off', + 'import/named': 'error', // ensure default import coupled with default export // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/default.md#when-not-to-use-it @@ -73,16 +74,21 @@ module.exports = { 'tests/**', // also common npm pattern 'spec/**', // mocha, rspec-like pattern '**/__tests__/**', // jest pattern + '**/__mocks__/**', // jest pattern 'test.{js,jsx}', // repos with a single test file 'test-*.{js,jsx}', // repos with multiple top-level test files - '**/*.{test,spec}.{js,jsx}', // tests where the extension denotes that it is a test + '**/*{.,_}{test,spec}.{js,jsx}', // tests where the extension or filename suffix denotes that it is a test + '**/jest.config.js', // jest config + '**/jest.setup.js', // jest setup + '**/vue.config.js', // vue-cli config '**/webpack.config.js', // webpack config '**/webpack.config.*.js', // webpack config '**/rollup.config.js', // rollup config '**/rollup.config.*.js', // rollup config '**/gulpfile.js', // gulp config '**/gulpfile.*.js', // gulp config - '**/Gruntfile', // grunt config + '**/Gruntfile{,.js}', // grunt config + '**/protractor.conf.js', // protractor config '**/protractor.conf.*.js', // protractor config ], optionalDependencies: false, @@ -111,7 +117,7 @@ module.exports = { // disallow non-import statements appearing before import statements // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/first.md - 'import/first': ['error', 'absolute-first'], + 'import/first': 'error', // disallow non-import statements appearing before import statements // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/imports-first.md @@ -129,18 +135,16 @@ module.exports = { // Ensure consistent use of file extension within the import path // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/extensions.md - 'import/extensions': ['error', 'always', { + 'import/extensions': ['error', 'ignorePackages', { js: 'never', + mjs: 'never', jsx: 'never', }], - // Enforce a convention in module import order + // ensure absolute imports are above relative imports and that unassigned imports are ignored // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/order.md - // TODO: enable? - 'import/order': ['off', { - groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index'], - 'newlines-between': 'never', - }], + // TODO: enforce a stricter convention in module import order? + 'import/order': ['error', { groups: [['builtin', 'external', 'internal']] }], // Require a newline after the last import/require in a group // https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/newline-after-import.md @@ -202,5 +206,56 @@ module.exports = { allowLiteral: false, allowObject: false, }], + + // This rule enforces that all exports are declared at the bottom of the file. + // https://github.com/benmosher/eslint-plugin-import/blob/98acd6afd04dcb6920b81330114e146dc8532ea4/docs/rules/exports-last.md + // TODO: enable? + 'import/exports-last': 'off', + + // Reports when named exports are not grouped together in a single export declaration + // or when multiple assignments to CommonJS module.exports or exports object are present + // in a single file. + // https://github.com/benmosher/eslint-plugin-import/blob/44a038c06487964394b1e15b64f3bd34e5d40cde/docs/rules/group-exports.md + 'import/group-exports': 'off', + + // forbid default exports. this is a terrible rule, do not use it. + // https://github.com/benmosher/eslint-plugin-import/blob/44a038c06487964394b1e15b64f3bd34e5d40cde/docs/rules/no-default-export.md + 'import/no-default-export': 'off', + + // Prohibit named exports. this is a terrible rule, do not use it. + // https://github.com/benmosher/eslint-plugin-import/blob/1ec80fa35fa1819e2d35a70e68fb6a149fb57c5e/docs/rules/no-named-export.md + 'import/no-named-export': 'off', + + // Forbid a module from importing itself + // https://github.com/benmosher/eslint-plugin-import/blob/44a038c06487964394b1e15b64f3bd34e5d40cde/docs/rules/no-self-import.md + 'import/no-self-import': 'error', + + // Forbid cyclical dependencies between modules + // https://github.com/benmosher/eslint-plugin-import/blob/d81f48a2506182738409805f5272eff4d77c9348/docs/rules/no-cycle.md + 'import/no-cycle': ['error', { maxDepth: Infinity }], + + // Ensures that there are no useless path segments + // https://github.com/benmosher/eslint-plugin-import/blob/ebafcbf59ec9f653b2ac2a0156ca3bcba0a7cf57/docs/rules/no-useless-path-segments.md + 'import/no-useless-path-segments': 'error', + + // dynamic imports require a leading comment with a webpackChunkName + // https://github.com/benmosher/eslint-plugin-import/blob/ebafcbf59ec9f653b2ac2a0156ca3bcba0a7cf57/docs/rules/dynamic-import-chunkname.md + 'import/dynamic-import-chunkname': ['off', { + importFunctions: [], + webpackChunknameFormat: '[0-9a-zA-Z-_/.]+', + }], + + // Use this rule to prevent imports to folders in relative parent paths. + // https://github.com/benmosher/eslint-plugin-import/blob/c34f14f67f077acd5a61b3da9c0b0de298d20059/docs/rules/no-relative-parent-imports.md + 'import/no-relative-parent-imports': 'off', + + // Reports modules without any exports, or with unused exports + // https://github.com/benmosher/eslint-plugin-import/blob/f63dd261809de6883b13b6b5b960e6d7f42a7813/docs/rules/no-unused-modules.md + // TODO: enable, semver-major + 'import/no-unused-modules': ['off', { + ignoreExports: [], + missingExports: true, + unusedExports: true, + }], }, }; diff --git a/packages/eslint-config-airbnb-base/rules/node.js b/packages/eslint-config-airbnb-base/rules/node.js index d890a67c1b..b178d7f909 100644 --- a/packages/eslint-config-airbnb-base/rules/node.js +++ b/packages/eslint-config-airbnb-base/rules/node.js @@ -8,16 +8,15 @@ module.exports = { 'callback-return': 'off', // require all requires be top-level - // http://eslint.org/docs/rules/global-require + // https://eslint.org/docs/rules/global-require 'global-require': 'error', // enforces error handling in callbacks (node environment) 'handle-callback-err': 'off', // disallow use of the Buffer() constructor - // http://eslint.org/docs/rules/no-buffer-constructor - // TODO: enable, semver-major - 'no-buffer-constructor': 'off', + // https://eslint.org/docs/rules/no-buffer-constructor + 'no-buffer-constructor': 'error', // disallow mixing regular variable and require declarations 'no-mixed-requires': ['off', false], @@ -26,7 +25,7 @@ module.exports = { 'no-new-require': 'error', // disallow string concatenation with __dirname and __filename - // http://eslint.org/docs/rules/no-path-concat + // https://eslint.org/docs/rules/no-path-concat 'no-path-concat': 'error', // disallow use of process.env diff --git a/packages/eslint-config-airbnb-base/rules/style.js b/packages/eslint-config-airbnb-base/rules/style.js index 42e820870e..3aa6eeac76 100644 --- a/packages/eslint-config-airbnb-base/rules/style.js +++ b/packages/eslint-config-airbnb-base/rules/style.js @@ -1,12 +1,12 @@ module.exports = { rules: { // enforce line breaks after opening and before closing array brackets - // http://eslint.org/docs/rules/array-bracket-newline + // https://eslint.org/docs/rules/array-bracket-newline // TODO: enable? semver-major - 'array-bracket-newline': ['off', { multiline: true, minItems: 3 }], + 'array-bracket-newline': ['off', 'consistent'], // object option alternative: { multiline: true, minItems: 3 } // enforce line breaks between array elements - // http://eslint.org/docs/rules/array-element-newline + // https://eslint.org/docs/rules/array-element-newline // TODO: enable? semver-major 'array-element-newline': ['off', { multiline: true, minItems: 3 }], @@ -14,17 +14,17 @@ module.exports = { 'array-bracket-spacing': ['error', 'never'], // enforce spacing inside single-line blocks - // http://eslint.org/docs/rules/block-spacing + // https://eslint.org/docs/rules/block-spacing 'block-spacing': ['error', 'always'], // enforce one true brace style 'brace-style': ['error', '1tbs', { allowSingleLine: true }], // require camel case names - camelcase: ['error', { properties: 'never' }], + camelcase: ['error', { properties: 'never', ignoreDestructuring: false }], // enforce or disallow capitalization of the first letter of a comment - // http://eslint.org/docs/rules/capitalized-comments + // https://eslint.org/docs/rules/capitalized-comments 'capitalized-comments': ['off', 'never', { line: { ignorePattern: '.*', @@ -38,11 +38,34 @@ module.exports = { }, }], + // require trailing commas in multiline object literals + 'comma-dangle': ['error', { + arrays: 'always-multiline', + objects: 'always-multiline', + imports: 'always-multiline', + exports: 'always-multiline', + functions: 'always-multiline', + }], + // enforce spacing before and after comma 'comma-spacing': ['error', { before: false, after: true }], // enforce one true comma style - 'comma-style': ['error', 'last'], + 'comma-style': ['error', 'last', { + exceptions: { + ArrayExpression: false, + ArrayPattern: false, + ArrowFunctionExpression: false, + CallExpression: false, + FunctionDeclaration: false, + FunctionExpression: false, + ImportDeclaration: false, + ObjectExpression: false, + ObjectPattern: false, + VariableDeclaration: false, + NewExpression: false, + } + }], // disallow padding inside computed properties 'computed-property-spacing': ['error', 'never'], @@ -54,27 +77,32 @@ module.exports = { 'eol-last': ['error', 'always'], // enforce spacing between functions and their invocations - // http://eslint.org/docs/rules/func-call-spacing + // https://eslint.org/docs/rules/func-call-spacing 'func-call-spacing': ['error', 'never'], // requires function names to match the name of the variable or property to which they are // assigned - // http://eslint.org/docs/rules/func-name-matching + // https://eslint.org/docs/rules/func-name-matching 'func-name-matching': ['off', 'always', { - includeCommonJSModuleExports: false + includeCommonJSModuleExports: false, + considerPropertyDescriptor: true, }], // require function expressions to have a name - // http://eslint.org/docs/rules/func-names + // https://eslint.org/docs/rules/func-names 'func-names': 'warn', // enforces use of function declarations or expressions - // http://eslint.org/docs/rules/func-style + // https://eslint.org/docs/rules/func-style // TODO: enable 'func-style': ['off', 'expression'], + // enforce consistent line breaks inside function parentheses + // https://eslint.org/docs/rules/function-paren-newline + 'function-paren-newline': ['error', 'consistent'], + // Blacklist certain identifiers to prevent them being used - // http://eslint.org/docs/rules/id-blacklist + // https://eslint.org/docs/rules/id-blacklist 'id-blacklist': 'off', // this option enforces minimum and maximum identifier lengths @@ -84,16 +112,17 @@ module.exports = { // require identifiers to match the provided regular expression 'id-match': 'off', + // Enforce the location of arrow function bodies with implicit returns + // https://eslint.org/docs/rules/implicit-arrow-linebreak + 'implicit-arrow-linebreak': ['error', 'beside'], + // this option sets a specific tab width for your code - // http://eslint.org/docs/rules/indent + // https://eslint.org/docs/rules/indent indent: ['error', 2, { SwitchCase: 1, VariableDeclarator: 1, outerIIFEBody: 1, // MemberExpression: null, - // CallExpression: { - // parameters: null, - // }, FunctionDeclaration: { parameters: 1, body: 1 @@ -101,11 +130,21 @@ module.exports = { FunctionExpression: { parameters: 1, body: 1 - } + }, + CallExpression: { + arguments: 1 + }, + ArrayExpression: 1, + ObjectExpression: 1, + ImportDeclaration: 1, + flatTernaryExpressions: false, + // list derived from https://github.com/benjamn/ast-types/blob/HEAD/def/jsx.js + ignoredNodes: ['JSXElement', 'JSXElement > *', 'JSXAttribute', 'JSXIdentifier', 'JSXNamespacedName', 'JSXMemberExpression', 'JSXSpreadAttribute', 'JSXExpressionContainer', 'JSXOpeningElement', 'JSXClosingElement', 'JSXText', 'JSXEmptyExpression', 'JSXSpreadChild'], + ignoreComments: false }], // specify whether double or single quotes should be used in JSX attributes - // http://eslint.org/docs/rules/jsx-quotes + // https://eslint.org/docs/rules/jsx-quotes 'jsx-quotes': ['off', 'prefer-double'], // enforces spacing between keys and values in object literal properties @@ -123,7 +162,7 @@ module.exports = { }], // enforce position of line comments - // http://eslint.org/docs/rules/line-comment-position + // https://eslint.org/docs/rules/line-comment-position // TODO: enable? 'line-comment-position': ['off', { position: 'above', @@ -132,14 +171,18 @@ module.exports = { }], // disallow mixed 'LF' and 'CRLF' as linebreaks - // http://eslint.org/docs/rules/linebreak-style + // https://eslint.org/docs/rules/linebreak-style 'linebreak-style': ['error', 'unix'], + // require or disallow an empty line between class members + // https://eslint.org/docs/rules/lines-between-class-members + 'lines-between-class-members': ['error', 'always', { exceptAfterSingleLine: false }], + // enforces empty lines around comments 'lines-around-comment': 'off', // require or disallow newlines around directives - // http://eslint.org/docs/rules/lines-around-directive + // https://eslint.org/docs/rules/lines-around-directive 'lines-around-directive': ['error', { before: 'always', after: 'always', @@ -149,7 +192,7 @@ module.exports = { 'max-depth': ['off', 4], // specify the maximum length of a line in your program - // http://eslint.org/docs/rules/max-len + // https://eslint.org/docs/rules/max-len 'max-len': ['error', 100, 2, { ignoreUrls: true, ignoreComments: false, @@ -159,13 +202,22 @@ module.exports = { }], // specify the max number of lines in a file - // http://eslint.org/docs/rules/max-lines + // https://eslint.org/docs/rules/max-lines 'max-lines': ['off', { max: 300, skipBlankLines: true, skipComments: true }], + // enforce a maximum function length + // https://eslint.org/docs/rules/max-lines-per-function + 'max-lines-per-function': ['off', { + max: 50, + skipBlankLines: true, + skipComments: true, + IIFEs: true, + }], + // specify the maximum depth callbacks can be nested 'max-nested-callbacks': 'off', @@ -176,11 +228,15 @@ module.exports = { 'max-statements': ['off', 10], // restrict the number of statements per line - // http://eslint.org/docs/rules/max-statements-per-line + // https://eslint.org/docs/rules/max-statements-per-line 'max-statements-per-line': ['off', { max: 1 }], + // enforce a particular style for multiline comments + // https://eslint.org/docs/rules/multiline-comment-style + 'multiline-comment-style': ['off', 'starred-block'], + // require multiline ternary - // http://eslint.org/docs/rules/multiline-ternary + // https://eslint.org/docs/rules/multiline-ternary // TODO: enable? 'multiline-ternary': ['off', 'never'], @@ -193,47 +249,53 @@ module.exports = { }], // disallow the omission of parentheses when invoking a constructor with no arguments - // http://eslint.org/docs/rules/new-parens + // https://eslint.org/docs/rules/new-parens 'new-parens': 'error', // allow/disallow an empty newline after var statement 'newline-after-var': 'off', - // http://eslint.org/docs/rules/newline-before-return + // https://eslint.org/docs/rules/newline-before-return 'newline-before-return': 'off', // enforces new line after each method call in the chain to make it // more readable and easy to maintain - // http://eslint.org/docs/rules/newline-per-chained-call + // https://eslint.org/docs/rules/newline-per-chained-call 'newline-per-chained-call': ['error', { ignoreChainWithDepth: 4 }], // disallow use of the Array constructor 'no-array-constructor': 'error', // disallow use of bitwise operators - // http://eslint.org/docs/rules/no-bitwise + // https://eslint.org/docs/rules/no-bitwise 'no-bitwise': 'error', // disallow use of the continue statement - // http://eslint.org/docs/rules/no-continue + // https://eslint.org/docs/rules/no-continue 'no-continue': 'error', // disallow comments inline after code 'no-inline-comments': 'off', // disallow if as the only statement in an else block - // http://eslint.org/docs/rules/no-lonely-if + // https://eslint.org/docs/rules/no-lonely-if 'no-lonely-if': 'error', // disallow un-paren'd mixes of different operators - // http://eslint.org/docs/rules/no-mixed-operators + // https://eslint.org/docs/rules/no-mixed-operators 'no-mixed-operators': ['error', { + // the list of arthmetic groups disallows mixing `%` and `**` + // with other arithmetic operators. groups: [ - ['+', '-', '*', '/', '%', '**'], - ['&', '|', '^', '~', '<<', '>>', '>>>'], - ['==', '!=', '===', '!==', '>', '>=', '<', '<='], + ['%', '**'], + ['%', '+'], + ['%', '-'], + ['%', '*'], + ['%', '/'], + ['/', '*'], + ['&', '|', '<<', '>>', '>>>'], + ['==', '!=', '===', '!=='], ['&&', '||'], - ['in', 'instanceof'] ], allowSamePrecedence: false }], @@ -242,14 +304,15 @@ module.exports = { 'no-mixed-spaces-and-tabs': 'error', // disallow use of chained assignment expressions - // http://eslint.org/docs/rules/no-multi-assign + // https://eslint.org/docs/rules/no-multi-assign 'no-multi-assign': ['error'], - // disallow multiple empty lines and only one newline at the end - 'no-multiple-empty-lines': ['error', { max: 2, maxEOF: 1 }], + // disallow multiple empty lines, only one newline at the end, and no new lines at the beginning + // https://eslint.org/docs/rules/no-multiple-empty-lines + 'no-multiple-empty-lines': ['error', { max: 2, maxBOF: 1, maxEOF: 0 }], // disallow negated conditions - // http://eslint.org/docs/rules/no-negated-condition + // https://eslint.org/docs/rules/no-negated-condition 'no-negated-condition': 'off', // disallow nested ternary expressions @@ -259,11 +322,11 @@ module.exports = { 'no-new-object': 'error', // disallow use of unary operators, ++ and -- - // http://eslint.org/docs/rules/no-plusplus + // https://eslint.org/docs/rules/no-plusplus 'no-plusplus': 'error', // disallow certain syntax forms - // http://eslint.org/docs/rules/no-restricted-syntax + // https://eslint.org/docs/rules/no-restricted-syntax 'no-restricted-syntax': [ 'error', { @@ -296,77 +359,90 @@ module.exports = { // disallow trailing whitespace at the end of lines 'no-trailing-spaces': ['error', { skipBlankLines: false, - // ignoreComments: false, // TODO: uncomment once v3 is dropped + ignoreComments: false, }], // disallow dangling underscores in identifiers + // https://eslint.org/docs/rules/no-underscore-dangle 'no-underscore-dangle': ['error', { allow: [], allowAfterThis: false, allowAfterSuper: false, - // enforceInMethodNames: false, // TODO: uncoment and enable, semver-minor once v3 is dropped + enforceInMethodNames: true, }], // disallow the use of Boolean literals in conditional expressions // also, prefer `a || b` over `a ? a : b` - // http://eslint.org/docs/rules/no-unneeded-ternary + // https://eslint.org/docs/rules/no-unneeded-ternary 'no-unneeded-ternary': ['error', { defaultAssignment: false }], // disallow whitespace before properties - // http://eslint.org/docs/rules/no-whitespace-before-property + // https://eslint.org/docs/rules/no-whitespace-before-property 'no-whitespace-before-property': 'error', // enforce the location of single-line statements - // http://eslint.org/docs/rules/nonblock-statement-body-position - 'nonblock-statement-body-position': 'off', + // https://eslint.org/docs/rules/nonblock-statement-body-position + 'nonblock-statement-body-position': ['error', 'beside', { overrides: {} }], // require padding inside curly braces 'object-curly-spacing': ['error', 'always'], // enforce line breaks between braces - // http://eslint.org/docs/rules/object-curly-newline - // TODO: enable once https://github.com/eslint/eslint/issues/6488 is resolved and v3 is dropped - 'object-curly-newline': ['off', { - ObjectExpression: { minProperties: 3, multiline: true, consistent: true }, - ObjectPattern: { minProperties: 3, multiline: true, consistent: true } + // https://eslint.org/docs/rules/object-curly-newline + 'object-curly-newline': ['error', { + ObjectExpression: { minProperties: 4, multiline: true, consistent: true }, + ObjectPattern: { minProperties: 4, multiline: true, consistent: true }, + ImportDeclaration: { minProperties: 4, multiline: true, consistent: true }, + ExportDeclaration: { minProperties: 4, multiline: true, consistent: true }, }], // enforce "same line" or "multiple line" on object properties. - // http://eslint.org/docs/rules/object-property-newline + // https://eslint.org/docs/rules/object-property-newline 'object-property-newline': ['error', { - allowMultiplePropertiesPerLine: true, + allowAllPropertiesOnSameLine: true, }], // allow just one var statement per function 'one-var': ['error', 'never'], // require a newline around variable declaration - // http://eslint.org/docs/rules/one-var-declaration-per-line + // https://eslint.org/docs/rules/one-var-declaration-per-line 'one-var-declaration-per-line': ['error', 'always'], // require assignment operator shorthand where possible or prohibit it entirely - // http://eslint.org/docs/rules/operator-assignment + // https://eslint.org/docs/rules/operator-assignment 'operator-assignment': ['error', 'always'], - // enforce operators to be placed before or after line breaks - 'operator-linebreak': 'off', - - // enforce padding within blocks - 'padded-blocks': ['error', 'never'], + // Requires operator at the beginning of the line in multiline statements + // https://eslint.org/docs/rules/operator-linebreak + 'operator-linebreak': ['error', 'before', { overrides: { '=': 'none' } }], + + // disallow padding within blocks + 'padded-blocks': ['error', { + blocks: 'never', + classes: 'never', + switches: 'never', + }, { + allowSingleLineBlocks: true, + }], // Require or disallow padding lines between statements - // http://eslint.org/docs/rules/padding-line-between-statements + // https://eslint.org/docs/rules/padding-line-between-statements 'padding-line-between-statements': 'off', + // Prefer use of an object spread over Object.assign + // https://eslint.org/docs/rules/prefer-object-spread + 'prefer-object-spread': 'error', + // require quotes around object literal property names - // http://eslint.org/docs/rules/quote-props.html + // https://eslint.org/docs/rules/quote-props.html 'quote-props': ['error', 'as-needed', { keywords: false, unnecessary: true, numbers: false }], // specify whether double or single quotes should be used quotes: ['error', 'single', { avoidEscape: true }], // do not require jsdoc - // http://eslint.org/docs/rules/require-jsdoc + // https://eslint.org/docs/rules/require-jsdoc 'require-jsdoc': 'off', // require or disallow use of semicolons instead of ASI @@ -376,9 +452,8 @@ module.exports = { 'semi-spacing': ['error', { before: false, after: true }], // Enforce location of semicolons - // http://eslint.org/docs/rules/semi-style - // TODO: enable, semver-major until v3 is dropped, semver-minor otherwise - 'semi-style': ['off', 'last'], + // https://eslint.org/docs/rules/semi-style + 'semi-style': ['error', 'last'], // requires object keys to be sorted 'sort-keys': ['off', 'asc', { caseSensitive: false, natural: true }], @@ -390,7 +465,7 @@ module.exports = { 'space-before-blocks': 'error', // require or disallow space before function opening parenthesis - // http://eslint.org/docs/rules/space-before-function-paren + // https://eslint.org/docs/rules/space-before-function-paren 'space-before-function-paren': ['error', { anonymous: 'always', named: 'never', @@ -404,7 +479,7 @@ module.exports = { 'space-infix-ops': 'error', // Require or disallow spaces before/after unary operators - // http://eslint.org/docs/rules/space-unary-ops + // https://eslint.org/docs/rules/space-unary-ops 'space-unary-ops': ['error', { words: true, nonwords: false, @@ -413,7 +488,7 @@ module.exports = { }], // require or disallow a space immediately following the // or /* in a comment - // http://eslint.org/docs/rules/spaced-comment + // https://eslint.org/docs/rules/spaced-comment 'spaced-comment': ['error', 'always', { line: { exceptions: ['-', '+'], @@ -421,23 +496,21 @@ module.exports = { }, block: { exceptions: ['-', '+'], - markers: ['=', '!'], // space here to support sprockets directives + markers: ['=', '!', ':', '::'], // space here to support sprockets directives and flow comment types balanced: true, } }], // Enforce spacing around colons of switch statements - // http://eslint.org/docs/rules/switch-colon-spacing - // TODO: enable, semver-major - 'switch-colon-spacing': ['off', { after: true, before: false }], + // https://eslint.org/docs/rules/switch-colon-spacing + 'switch-colon-spacing': ['error', { after: true, before: false }], // Require or disallow spacing between template tags and their literals - // http://eslint.org/docs/rules/template-tag-spacing - // TODO: enable, semver-major - 'template-tag-spacing': ['off', 'never'], + // https://eslint.org/docs/rules/template-tag-spacing + 'template-tag-spacing': ['error', 'never'], // require or disallow the Unicode Byte Order Mark - // http://eslint.org/docs/rules/unicode-bom + // https://eslint.org/docs/rules/unicode-bom 'unicode-bom': ['error', 'never'], // require regex literals to be wrapped in parentheses diff --git a/packages/eslint-config-airbnb-base/rules/variables.js b/packages/eslint-config-airbnb-base/rules/variables.js index fe38ea5d60..701340ab56 100644 --- a/packages/eslint-config-airbnb-base/rules/variables.js +++ b/packages/eslint-config-airbnb-base/rules/variables.js @@ -1,4 +1,4 @@ -const restrictedGlobals = require('eslint-restricted-globals'); +const confusingBrowserGlobals = require('confusing-browser-globals'); module.exports = { rules: { @@ -12,12 +12,11 @@ module.exports = { 'no-delete-var': 'error', // disallow labels that share a name with a variable - // http://eslint.org/docs/rules/no-label-var + // https://eslint.org/docs/rules/no-label-var 'no-label-var': 'error', // disallow specific globals - // TODO: enable, semver-major - 'no-restricted-globals': ['off'].concat(restrictedGlobals), + 'no-restricted-globals': ['error', 'isFinite', 'isNaN'].concat(confusingBrowserGlobals), // disallow declaration of variables already declared in the outer scope 'no-shadow': 'error', @@ -32,7 +31,7 @@ module.exports = { 'no-undef-init': 'error', // disallow use of undefined variable - // http://eslint.org/docs/rules/no-undefined + // https://eslint.org/docs/rules/no-undefined // TODO: enable? 'no-undefined': 'off', diff --git a/packages/eslint-config-airbnb-base/test/requires.js b/packages/eslint-config-airbnb-base/test/requires.js new file mode 100644 index 0000000000..8176480fe1 --- /dev/null +++ b/packages/eslint-config-airbnb-base/test/requires.js @@ -0,0 +1,13 @@ +/* eslint strict: 0, global-require: 0 */ + +'use strict'; + +const test = require('tape'); + +test('all entry points parse', (t) => { + t.doesNotThrow(() => require('..'), 'index does not throw'); + t.doesNotThrow(() => require('../legacy'), 'legacy does not throw'); + t.doesNotThrow(() => require('../whitespace'), 'whitespace does not throw'); + + t.end(); +}); diff --git a/packages/eslint-config-airbnb-base/test/test-base.js b/packages/eslint-config-airbnb-base/test/test-base.js index 810c8000b0..181e04f453 100644 --- a/packages/eslint-config-airbnb-base/test/test-base.js +++ b/packages/eslint-config-airbnb-base/test/test-base.js @@ -2,7 +2,7 @@ import fs from 'fs'; import path from 'path'; import test from 'tape'; -import index from '../'; +import index from '..'; const files = { ...{ index } }; // object spread is to test parsing @@ -20,13 +20,13 @@ Object.keys(files).forEach(( t.plan(2); // scan plugins for react and fail if it is found - const hasReactPlugin = Object.prototype.hasOwnProperty.call(config, 'plugins') && - config.plugins.indexOf('react') !== -1; + const hasReactPlugin = Object.prototype.hasOwnProperty.call(config, 'plugins') + && config.plugins.indexOf('react') !== -1; t.notOk(hasReactPlugin, 'there is no react plugin'); // scan rules for react/ and fail if any exist const reactRuleIds = Object.keys(config.rules) - .filter(ruleId => ruleId.indexOf('react/') === 0); + .filter((ruleId) => ruleId.indexOf('react/') === 0); t.deepEquals(reactRuleIds, [], 'there are no react/ rules'); }); }); diff --git a/packages/eslint-config-airbnb-base/whitespace.js b/packages/eslint-config-airbnb-base/whitespace.js new file mode 100644 index 0000000000..917ee0c50e --- /dev/null +++ b/packages/eslint-config-airbnb-base/whitespace.js @@ -0,0 +1,78 @@ +const assign = require('object.assign'); +const entries = require('object.entries'); +const CLIEngine = require('eslint').CLIEngine; + +const baseConfig = require('.'); + +function onlyErrorOnRules(rulesToError, config) { + const errorsOnly = assign({}, config); + const cli = new CLIEngine({ baseConfig: config, useEslintrc: false }); + const baseRules = cli.getConfigForFile(require.resolve('./')).rules; + + entries(baseRules).forEach((rule) => { + const ruleName = rule[0]; + const ruleConfig = rule[1]; + + if (rulesToError.indexOf(ruleName) === -1) { + if (Array.isArray(ruleConfig)) { + errorsOnly.rules[ruleName] = ['warn'].concat(ruleConfig.slice(1)); + } else if (typeof ruleConfig === 'number') { + errorsOnly.rules[ruleName] = 1; + } else { + errorsOnly.rules[ruleName] = 'warn'; + } + } + }); + + return errorsOnly; +} + +module.exports = onlyErrorOnRules([ + 'array-bracket-newline', + 'array-bracket-spacing', + 'array-element-newline', + 'arrow-spacing', + 'block-spacing', + 'comma-spacing', + 'computed-property-spacing', + 'dot-location', + 'eol-last', + 'func-call-spacing', + 'function-paren-newline', + 'generator-star-spacing', + 'implicit-arrow-linebreak', + 'indent', + 'key-spacing', + 'keyword-spacing', + 'line-comment-position', + 'linebreak-style', + 'multiline-ternary', + 'newline-per-chained-call', + 'no-irregular-whitespace', + 'no-mixed-spaces-and-tabs', + 'no-multi-spaces', + 'no-regex-spaces', + 'no-spaced-func', + 'no-trailing-spaces', + 'no-whitespace-before-property', + 'nonblock-statement-body-position', + 'object-curly-newline', + 'object-curly-spacing', + 'object-property-newline', + 'one-var-declaration-per-line', + 'operator-linebreak', + 'padded-blocks', + 'padding-line-between-statements', + 'rest-spread-spacing', + 'semi-spacing', + 'semi-style', + 'space-before-blocks', + 'space-before-function-paren', + 'space-in-parens', + 'space-infix-ops', + 'space-unary-ops', + 'spaced-comment', + 'switch-colon-spacing', + 'template-tag-spacing', + 'import/newline-after-import', +], baseConfig); diff --git a/packages/eslint-config-airbnb/.eslintrc b/packages/eslint-config-airbnb/.eslintrc index 6c8556be3b..f9bd289286 100644 --- a/packages/eslint-config-airbnb/.eslintrc +++ b/packages/eslint-config-airbnb/.eslintrc @@ -3,6 +3,8 @@ "rules": { // disable requiring trailing commas because it might be nice to revert to // being JSON at some point, and I don't want to make big changes now. - "comma-dangle": 0 - } + "comma-dangle": 0, + // we support node 4 + "prefer-destructuring": 0, + }, } diff --git a/packages/eslint-config-airbnb/CHANGELOG.md b/packages/eslint-config-airbnb/CHANGELOG.md index a1c3b5d188..8c16dd3c1a 100644 --- a/packages/eslint-config-airbnb/CHANGELOG.md +++ b/packages/eslint-config-airbnb/CHANGELOG.md @@ -1,3 +1,79 @@ +18.0.1 / 2019-08-13 +================== + - [patch] `react/state-in-constructor`: fix incorrect configuration + +18.0.0 / 2019-08-10 +================== + - [breaking] add eslint v6, drop eslint v4 + - [deps] [breaking] update `eslint-config-airbnb-base`, `eslint-plugin-react`, `eslint-find-rules`, `eslint-plugin-import` + - [breaking] Remove rules/strict from 'extends' (#1962) + - [breaking] set react version to "detect" + - [breaking] disable `label-has-for`; enable `control-has-associated-label` + - [breaking] enable `react/jsx-props-no-spreading` + - [breaking] enable `react/jsx-fragments` + - [minor] enable `react/static-property-placement` + - [minor] enable `react/state-in-constructor` + - [minor] enable `react/jsx-curly-newline` + - [react] Add missing/unsafe lifecycle methods to react/sort-comp rule (#2043) + - [react] add componentDidCatch to lifecycle for react/sort-comp (#2060) + - [react] add `react-hooks` plugin (#2022) + - [dev deps] update babel-related deps to latest + - [tests] only run tests in non-lint per-package travis job + - [tests] use `eclint` instead of `editorconfig-tools` + - [meta] add disabled config for new react and a11y rules + + +17.1.1 / 2019-07-01 +================== + - [patch] Turn off `react/no-multi-comp` (#2006) + - [patch] extend `no-underscore-dangle` to allow for redux dev tools in the main config instead (#1996) + - [meta] add disabled `jsx-fragments` rule + - [deps] update `eslint-config-airbnb-base`, `object.entries`, `eslint-plugin-import`, `eslint-plugin-react`, `eslint-plugin-jsx-a11y`, `babel-preset-airbnb`, `tape` (#2005, etc) + - [docs] correct JavaScript capitalization (#2046) + - [docs] fix docs for whitespace config (#1914, #1871) + - [readme] Improve eslint config setup instructions for yarn (#2001) + +17.1.0 / 2018-08-13 +================== +- [new] add eslint v5 support +- [minor] enable `label-has-associated-control` rule +- [patch] re-enabling `jsx-one-expression-per-line` allowing single children, ignore DOM components on `jsx-no-bind` +- [deps] update `eslint`, `eslint-config-airbnb-base`, `eslint-plugin-react`, `eslint-plugin-import`, `safe-publish-latest`, `eslint-plugin-jsx-a11y`, `eslint-find-rules` +- [docs] fix readme typo (#1855) + +17.0.0 / 2018-06-21 +================== +- [breaking] update `eslint-config-airbnb-base` to v13 +- [breaking] enable `no-useless-path-segments` (#1743) +- [breaking] update `eslint-plugin-react` to `v7.6`; update rule configs (#1737) +- [breaking] bump react pragma to v16; update `class-methods-use-this`'s `exceptMethods` to include `componentDidCatch` (#1704) +- [new] Adds config entry point with only whitespace rules enabled (#1749, #1751) +- [patch] set `forbid-foreign-prop-types` to "warn" +- [patch] Add new methods introduced in react@16.3 (#1831) +- [patch] `label-has-for`: Remove redundant component (#1802) +- [patch] Add 'to' as a specialLink to the 'anchor-is-valid' a11y rule (#1648) +- [patch] disable `no-did-mount-set-state`, since it’s necessary for server-rendering. +- [deps] update `eslint`, `eslint-plugin-react`, `eslint-plugin-import`, +- [dev deps] update `babel-preset-airbnb`, `tape`, `eslint-find-rules` +- [meta] add ES2015-2018 in npm package keywords (#1587) +- [meta] Add licenses to sub packages (#1746) +- [docs] add `npx` shortcut (#1694) +- [docs] Use HTTPS for links to ESLint documentation (#1628) + +16.1.0 / 2017-10-16 +================== +- [deps] update `eslint-config-airbnb-base`, `eslint` to v4.9 + +16.0.0 / 2017-10-06 +================== +- [breaking] [deps] require `eslint` `v4`, update `eslint-config-airbnb-base` +- [breaking] [deps] Upgrade `eslint-plugin-jsx-a11y` to `v6`; enable more a11y rules (#1482) +- [breaking] enable/add react rules: `react/jsx-curly-brace-presence`, `react/no-typos`, `react/no-unused-state`, `react/no-redundant-should-component-update`, `react/default-props-match-prop-types` +- [new] add `propWrapperFunctions` default settings for `eslint-plugin-react` +- [new] Enable `react/jsx-closing-tag-location` (#1533) +- [deps] update `eslint` v4, `eslint-plugin-react`, `tape` +- [docs] Specify yarn-specific install instructions (#1511) + 15.1.0 / 2017-07-24 ================== - [deps] allow eslint v3 or v4 (#1447) @@ -294,36 +370,36 @@ [pr-modular]: https://github.com/airbnb/javascript/pull/526 [pr-legacy]: https://github.com/airbnb/javascript/pull/527 -[array-bracket-spacing]: http://eslint.org/docs/rules/array-bracket-spacing -[array-callback-return]: http://eslint.org/docs/rules/array-callback-return -[arrow-body-style]: http://eslint.org/docs/rules/arrow-body-style -[arrow-spacing]: http://eslint.org/docs/rules/arrow-spacing -[computed-property-spacing]: http://eslint.org/docs/rules/computed-property-spacing -[id-length]: http://eslint.org/docs/rules/id-length -[indent]: http://eslint.org/docs/rules/indent -[max-len]: http://eslint.org/docs/rules/max-len -[newline-per-chained-call]: http://eslint.org/docs/rules/newline-per-chained-call -[no-confusing-arrow]: http://eslint.org/docs/rules/no-confusing-arrow -[no-const-assign]: http://eslint.org/docs/rules/no-const-assign -[no-mixed-spaces-and-tabs]: http://eslint.org/docs/rules/no-mixed-spaces-and-tabs -[no-multiple-empty-lines]: http://eslint.org/docs/rules/no-multiple-empty-lines -[no-new-symbol]: http://eslint.org/docs/rules/no-new-symbol -[no-restricted-imports]: http://eslint.org/docs/rules/no-restricted-imports -[no-self-assign]: http://eslint.org/docs/rules/no-self-assign -[no-undef]: http://eslint.org/docs/rules/no-undef -[no-useless-constructor]: http://eslint.org/docs/rules/no-useless-constructor -[no-whitespace-before-property]: http://eslint.org/docs/rules/no-whitespace-before-property -[object-curly-spacing]: http://eslint.org/docs/rules/object-curly-spacing -[object-shorthand]: http://eslint.org/docs/rules/object-shorthand -[one-var-declaration-per-line]: http://eslint.org/docs/rules/one-var-declaration-per-line -[prefer-arrow-callback]: http://eslint.org/docs/rules/prefer-arrow-callback -[prefer-rest-params]: http://eslint.org/docs/rules/prefer-rest-params -[prefer-template]: http://eslint.org/docs/rules/prefer-template -[quote-props]: http://eslint.org/docs/rules/quote-props -[space-before-function-paren]: http://eslint.org/docs/rules/space-before-function-paren -[space-before-keywords]: http://eslint.org/docs/rules/space-before-keywords -[space-in-parens]: http://eslint.org/docs/rules/space-in-parens -[template-curly-spacing]: http://eslint.org/docs/rules/template-curly-spacing +[array-bracket-spacing]: https://eslint.org/docs/rules/array-bracket-spacing +[array-callback-return]: https://eslint.org/docs/rules/array-callback-return +[arrow-body-style]: https://eslint.org/docs/rules/arrow-body-style +[arrow-spacing]: https://eslint.org/docs/rules/arrow-spacing +[computed-property-spacing]: https://eslint.org/docs/rules/computed-property-spacing +[id-length]: https://eslint.org/docs/rules/id-length +[indent]: https://eslint.org/docs/rules/indent +[max-len]: https://eslint.org/docs/rules/max-len +[newline-per-chained-call]: https://eslint.org/docs/rules/newline-per-chained-call +[no-confusing-arrow]: https://eslint.org/docs/rules/no-confusing-arrow +[no-const-assign]: https://eslint.org/docs/rules/no-const-assign +[no-mixed-spaces-and-tabs]: https://eslint.org/docs/rules/no-mixed-spaces-and-tabs +[no-multiple-empty-lines]: https://eslint.org/docs/rules/no-multiple-empty-lines +[no-new-symbol]: https://eslint.org/docs/rules/no-new-symbol +[no-restricted-imports]: https://eslint.org/docs/rules/no-restricted-imports +[no-self-assign]: https://eslint.org/docs/rules/no-self-assign +[no-undef]: https://eslint.org/docs/rules/no-undef +[no-useless-constructor]: https://eslint.org/docs/rules/no-useless-constructor +[no-whitespace-before-property]: https://eslint.org/docs/rules/no-whitespace-before-property +[object-curly-spacing]: https://eslint.org/docs/rules/object-curly-spacing +[object-shorthand]: https://eslint.org/docs/rules/object-shorthand +[one-var-declaration-per-line]: https://eslint.org/docs/rules/one-var-declaration-per-line +[prefer-arrow-callback]: https://eslint.org/docs/rules/prefer-arrow-callback +[prefer-rest-params]: https://eslint.org/docs/rules/prefer-rest-params +[prefer-template]: https://eslint.org/docs/rules/prefer-template +[quote-props]: https://eslint.org/docs/rules/quote-props +[space-before-function-paren]: https://eslint.org/docs/rules/space-before-function-paren +[space-before-keywords]: https://eslint.org/docs/rules/space-before-keywords +[space-in-parens]: https://eslint.org/docs/rules/space-in-parens +[template-curly-spacing]: https://eslint.org/docs/rules/template-curly-spacing [react/jsx-space-before-closing]: https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-space-before-closing.md [react/sort-comp]: https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/sort-comp.md diff --git a/packages/eslint-config-airbnb/LICENSE.md b/packages/eslint-config-airbnb/LICENSE.md new file mode 100644 index 0000000000..69d80c0252 --- /dev/null +++ b/packages/eslint-config-airbnb/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2012 Airbnb + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/eslint-config-airbnb/README.md b/packages/eslint-config-airbnb/README.md index a3a7c71340..94e4ac54c4 100644 --- a/packages/eslint-config-airbnb/README.md +++ b/packages/eslint-config-airbnb/README.md @@ -10,9 +10,7 @@ We export three ESLint configurations for your usage. ### eslint-config-airbnb -Our default export contains all of our ESLint rules, including ECMAScript 6+ and React. It requires `eslint`, `eslint-plugin-import`, `eslint-plugin-react`, and `eslint-plugin-jsx-a11y`. If you don't need React, see [eslint-config-airbnb-base](https://npmjs.com/eslint-config-airbnb-base). - -If you use yarn, run `yarn add --dev eslint-config-airbnb-base eslint-plugin-import eslint-plugin-react eslint-plugin-jsx-a11y`, and see below for npm instructions. +Our default export contains all of our ESLint rules, including ECMAScript 6+ and React. It requires `eslint`, `eslint-plugin-import`, `eslint-plugin-react`, `eslint-plugin-react-hooks`, and `eslint-plugin-jsx-a11y`. If you don't need React, see [eslint-config-airbnb-base](https://npmjs.com/eslint-config-airbnb-base). 1. Install the correct versions of each package, which are listed by the command: @@ -20,7 +18,16 @@ If you use yarn, run `yarn add --dev eslint-config-airbnb-base eslint-plugin-imp npm info "eslint-config-airbnb@latest" peerDependencies ``` - Linux/OSX users can run + If using **npm 5+**, use this shortcut + + ```sh + npx install-peerdeps --dev eslint-config-airbnb + ``` + + If using **yarn**, you can also use the shortcut described above if you have npm 5+ installed on your machine, as the command will detect that you are using yarn and will act accordingly. + Otherwise, run `npm info "eslint-config-airbnb@latest" peerDependencies` to list the peer dependencies and versions, then run `yarn add --dev @` for each listed peer dependency. + + If using **npm < 5**, Linux/OSX users can run ```sh ( @@ -32,23 +39,30 @@ If you use yarn, run `yarn add --dev eslint-config-airbnb-base eslint-plugin-imp Which produces and runs a command like: ```sh - npm install --save-dev eslint-config-airbnb eslint@^#.#.# eslint-plugin-jsx-a11y@^#.#.# eslint-plugin-import@^#.#.# eslint-plugin-react@^#.#.# + npm install --save-dev eslint-config-airbnb eslint@^#.#.# eslint-plugin-jsx-a11y@^#.#.# eslint-plugin-import@^#.#.# eslint-plugin-react@^#.#.# eslint-plugin-react-hooks@^#.#.# ``` - Windows users can either install all the peer dependencies manually, or use the [install-peerdeps](https://github.com/nathanhleung/install-peerdeps) cli tool. + If using **npm < 5**, Windows users can either install all the peer dependencies manually, or use the [install-peerdeps](https://github.com/nathanhleung/install-peerdeps) cli tool. ```sh npm install -g install-peerdeps install-peerdeps --dev eslint-config-airbnb ``` - The cli will produce and run a command like: ```sh - npm install --save-dev eslint-config-airbnb eslint@^#.#.# eslint-plugin-jsx-a11y@^#.#.# eslint-plugin-import@^#.#.# eslint-plugin-react@^#.#.# + npm install --save-dev eslint-config-airbnb eslint@^#.#.# eslint-plugin-jsx-a11y@^#.#.# eslint-plugin-import@^#.#.# eslint-plugin-react@^#.#.# eslint-plugin-react-hooks@^#.#.# ``` -2. Add `"extends": "airbnb"` to your .eslintrc +2. Add `"extends": "airbnb"` to your `.eslintrc` + +### eslint-config/airbnb/hooks + +This entry point enables the linting rules for React hooks (requires v16.8+). To use, add `"extends": ["airbnb", "airbnb/hooks"]` to your `.eslintrc` + +### eslint-config-airbnb/whitespace + +This entry point only errors on whitespace rules and sets all other rules to warnings. View the list of whitespace rules [here](https://github.com/airbnb/javascript/blob/master/packages/eslint-config-airbnb/whitespace.js). ### eslint-config-airbnb/base @@ -58,8 +72,8 @@ This entry point is deprecated. See [eslint-config-airbnb-base](https://npmjs.co This entry point is deprecated. See [eslint-config-airbnb-base](https://npmjs.com/eslint-config-airbnb-base). -See [Airbnb's Javascript styleguide](https://github.com/airbnb/javascript) and -the [ESlint config docs](http://eslint.org/docs/user-guide/configuring#extending-configuration-files) +See [Airbnb's JavaScript styleguide](https://github.com/airbnb/javascript) and +the [ESlint config docs](https://eslint.org/docs/user-guide/configuring#extending-configuration-files) for more information. ## Improving this config diff --git a/packages/eslint-config-airbnb/hooks.js b/packages/eslint-config-airbnb/hooks.js new file mode 100644 index 0000000000..a7f70284e3 --- /dev/null +++ b/packages/eslint-config-airbnb/hooks.js @@ -0,0 +1,6 @@ +module.exports = { + extends: [ + './rules/react-hooks.js', + ].map(require.resolve), + rules: {} +}; diff --git a/packages/eslint-config-airbnb/index.js b/packages/eslint-config-airbnb/index.js index ddd3bfb712..6432e10dda 100644 --- a/packages/eslint-config-airbnb/index.js +++ b/packages/eslint-config-airbnb/index.js @@ -1,7 +1,6 @@ module.exports = { extends: [ 'eslint-config-airbnb-base', - 'eslint-config-airbnb-base/rules/strict', './rules/react', './rules/react-a11y', ].map(require.resolve), diff --git a/packages/eslint-config-airbnb/package.json b/packages/eslint-config-airbnb/package.json index 4f48b8fee2..14b51c5717 100644 --- a/packages/eslint-config-airbnb/package.json +++ b/packages/eslint-config-airbnb/package.json @@ -1,18 +1,20 @@ { "name": "eslint-config-airbnb", - "version": "15.1.0", + "version": "18.0.1", "description": "Airbnb's ESLint config, following our styleguide", "main": "index.js", "scripts": { - "prelint": "editorconfig-tools check * rules/* test/*", + "prelint": "eclint check * rules/* test/*", "lint": "eslint .", + "pretests-only": "node ./test/requires", "tests-only": "babel-tape-runner ./test/test-*.js", "prepublish": "(in-install || eslint-find-rules --unused) && (not-in-publish || npm test) && safe-publish-latest", "pretest": "npm run --silent lint", "test": "npm run --silent tests-only", - "pretravis": "cd ../eslint-config-airbnb-base && npm install && npm link && cd - && npm link --no-save eslint-config-airbnb-base", - "travis": "npm run --silent test", - "posttravis": "npm unlink eslint-config-airbnb-base >/dev/null &" + "link:eslint": "cd node_modules/eslint && npm link --production && cd -", + "pretravis": "npm run link:eslint && cd ../eslint-config-airbnb-base && npm link eslint && npm install && npm link && cd - && npm link --no-save eslint-config-airbnb-base", + "travis": "npm run --silent tests-only", + "posttravis": "npm unlink --no-save eslint-config-airbnb-base eslint >/dev/null &" }, "repository": { "type": "git", @@ -24,7 +26,11 @@ "config", "airbnb", "javascript", - "styleguide" + "styleguide", + "es2015", + "es2016", + "es2017", + "es2018" ], "author": "Jake Teton-Landis (https://twitter.com/@jitl)", "contributors": [ @@ -48,29 +54,34 @@ }, "homepage": "https://github.com/airbnb/javascript", "dependencies": { - "eslint-config-airbnb-base": "^11.3.0" + "eslint-config-airbnb-base": "^14.0.0", + "object.assign": "^4.1.0", + "object.entries": "^1.1.0" }, "devDependencies": { - "babel-preset-airbnb": "^2.4.0", - "babel-tape-runner": "^2.0.1", - "editorconfig-tools": "^0.1.1", - "eslint": "^3.19.0 || ^4.3.0", - "eslint-find-rules": "^3.1.1", - "eslint-plugin-import": "^2.7.0", - "eslint-plugin-jsx-a11y": "^5.1.1", - "eslint-plugin-react": "^7.1.0", + "@babel/runtime": "^7.5.5", + "babel-preset-airbnb": "^4.0.1", + "babel-tape-runner": "^3.0.0", + "eclint": "^2.8.1", + "eslint": "^5.16.0 || ^6.1.0", + "eslint-find-rules": "^3.4.0", + "eslint-plugin-import": "^2.18.2", + "eslint-plugin-jsx-a11y": "^6.2.3", + "eslint-plugin-react": "^7.14.3", + "eslint-plugin-react-hooks": "^1.7.0", "in-publish": "^2.0.0", "react": ">= 0.13.0", - "safe-publish-latest": "^1.1.1", - "tape": "^4.7.0" + "safe-publish-latest": "^1.1.2", + "tape": "^4.11.0" }, "peerDependencies": { - "eslint": "^3.19.0 || ^4.3.0", - "eslint-plugin-jsx-a11y": "^5.1.1", - "eslint-plugin-import": "^2.7.0", - "eslint-plugin-react": "^7.1.0" + "eslint": "^5.16.0 || ^6.1.0", + "eslint-plugin-import": "^2.18.2", + "eslint-plugin-jsx-a11y": "^6.2.3", + "eslint-plugin-react": "^7.14.3", + "eslint-plugin-react-hooks": "^1.7.0" }, "engines": { - "node": ">= 4" + "node": ">= 6" } } diff --git a/packages/eslint-config-airbnb/rules/react-a11y.js b/packages/eslint-config-airbnb/rules/react-a11y.js index 4cdf1cf39c..69cf63b01f 100644 --- a/packages/eslint-config-airbnb/rules/react-a11y.js +++ b/packages/eslint-config-airbnb/rules/react-a11y.js @@ -13,7 +13,7 @@ module.exports = { rules: { // Enforce that anchors have content // https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/anchor-has-content.md - 'jsx-a11y/anchor-has-content': ['error', { components: [''] }], + 'jsx-a11y/anchor-has-content': ['error', { components: [] }], // Require ARIA roles to be valid and non-abstract // https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/aria-role.md @@ -32,10 +32,6 @@ module.exports = { // https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/aria-unsupported-elements.md 'jsx-a11y/aria-unsupported-elements': 'error', - // disallow href "#" - // https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/href-no-hash.md - 'jsx-a11y/href-no-hash': ['error', { components: ['a'] }], - // Enforce that all elements that require alternative text have meaningful information // https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/alt-text.md 'jsx-a11y/alt-text': ['error', { @@ -52,12 +48,57 @@ module.exports = { // require that JSX labels use "htmlFor" // https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/label-has-for.md - 'jsx-a11y/label-has-for': ['error', { components: ['label'] }], + // deprecated: replaced by `label-has-associated-control` rule + 'jsx-a11y/label-has-for': ['off', { + components: [], + required: { + every: ['nesting', 'id'], + }, + allowChildren: false, + }], + + // Enforce that a label tag has a text label and an associated control. + // https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/b800f40a2a69ad48015ae9226fbe879f946757ed/docs/rules/label-has-associated-control.md + 'jsx-a11y/label-has-associated-control': ['error', { + labelComponents: [], + labelAttributes: [], + controlComponents: [], + assert: 'both', + depth: 25 + }], + + // Enforce that a control (an interactive element) has a text label. + // https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/control-has-associated-label.md + 'jsx-a11y/control-has-associated-label': ['error', { + labelAttributes: ['label'], + controlComponents: [], + ignoreElements: [ + 'audio', + 'canvas', + 'embed', + 'input', + 'textarea', + 'tr', + 'video', + ], + ignoreRoles: [ + 'grid', + 'listbox', + 'menu', + 'menubar', + 'radiogroup', + 'row', + 'tablist', + 'toolbar', + 'tree', + 'treegrid', + ], + depth: 5, + }], // require that mouseover/out come with focus/blur, for keyboard-only users - // TODO: evaluate // https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/mouse-events-have-key-events.md - 'jsx-a11y/mouse-events-have-key-events': 'off', + 'jsx-a11y/mouse-events-have-key-events': 'error', // Prevent use of `accessKey` // https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/no-access-key.md @@ -109,8 +150,7 @@ module.exports = { // require onClick be accompanied by onKeyUp/onKeyDown/onKeyPress // https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/click-events-have-key-events.md - // TODO: enable? - 'jsx-a11y/click-events-have-key-events': 'off', + 'jsx-a11y/click-events-have-key-events': 'error', // Enforce that DOM elements without semantic behavior not have interaction handlers // https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/master/docs/rules/no-static-element-interactions.md @@ -191,10 +231,9 @@ module.exports = { // ensure tags are valid // https://github.com/evcohen/eslint-plugin-jsx-a11y/blob/0745af376cdc8686d85a361ce36952b1fb1ccf6e/docs/rules/anchor-is-valid.md - // TODO: enable, semver-major - 'jsx-a11y/anchor-is-valid': ['off', { + 'jsx-a11y/anchor-is-valid': ['error', { components: ['Link'], - specialLink: [], + specialLink: ['to'], aspects: ['noHref', 'invalidHref', 'preferButton'], }], }, diff --git a/packages/eslint-config-airbnb/rules/react-hooks.js b/packages/eslint-config-airbnb/rules/react-hooks.js new file mode 100644 index 0000000000..be17c9a571 --- /dev/null +++ b/packages/eslint-config-airbnb/rules/react-hooks.js @@ -0,0 +1,21 @@ +module.exports = { + plugins: [ + 'react-hooks', + ], + + parserOptions: { + ecmaFeatures: { + jsx: true, + }, + }, + + rules: { + // Enforce Rules of Hooks + // https://github.com/facebook/react/blob/c11015ff4f610ac2924d1fc6d569a17657a404fd/packages/eslint-plugin-react-hooks/src/RulesOfHooks.js + 'react-hooks/rules-of-hooks': 'error', + + // Verify the list of the dependencies for Hooks like useEffect and similar + // https://github.com/facebook/react/blob/1204c789776cb01fbaf3e9f032e7e2ba85a44137/packages/eslint-plugin-react-hooks/src/ExhaustiveDeps.js + 'react-hooks/exhaustive-deps': 'error', + }, +}; diff --git a/packages/eslint-config-airbnb/rules/react.js b/packages/eslint-config-airbnb/rules/react.js index 96e5f4a75d..1fe097bb07 100644 --- a/packages/eslint-config-airbnb/rules/react.js +++ b/packages/eslint-config-airbnb/rules/react.js @@ -1,3 +1,8 @@ +const assign = require('object.assign'); +const baseStyleRules = require('eslint-config-airbnb-base/rules/style').rules; + +const dangleRules = baseStyleRules['no-underscore-dangle']; + module.exports = { plugins: [ 'react', @@ -12,8 +17,12 @@ module.exports = { // View link below for react rules documentation // https://github.com/yannickcr/eslint-plugin-react#list-of-supported-rules rules: { + 'no-underscore-dangle': [dangleRules[0], assign({}, dangleRules[1], { + allow: dangleRules[1].allow.concat(['__REDUX_DEVTOOLS_EXTENSION_COMPOSE__']), + })], + // Specify whether double or single quotes should be used in JSX attributes - // http://eslint.org/docs/rules/jsx-quotes + // https://eslint.org/docs/rules/jsx-quotes 'jsx-quotes': ['error', 'prefer-double'], 'class-methods-use-this': ['error', { @@ -23,12 +32,17 @@ module.exports = { 'getDefaultProps', 'getChildContext', 'componentWillMount', + 'UNSAFE_componentWillMount', 'componentDidMount', 'componentWillReceiveProps', + 'UNSAFE_componentWillReceiveProps', 'shouldComponentUpdate', 'componentWillUpdate', + 'UNSAFE_componentWillUpdate', 'componentDidUpdate', 'componentWillUnmount', + 'componentDidCatch', + 'getSnapshotBeforeUpdate' ], }], @@ -37,21 +51,28 @@ module.exports = { 'react/display-name': ['off', { ignoreTranspilerName: false }], // Forbid certain propTypes (any, array, object) - // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/forbid-prop-types.md - 'react/forbid-prop-types': ['error', { forbid: ['any', 'array', 'object'] }], + // https://github.com/yannickcr/eslint-plugin-react/blob/843d71a432baf0f01f598d7cf1eea75ad6896e4b/docs/rules/forbid-prop-types.md + 'react/forbid-prop-types': ['error', { + forbid: ['any', 'array', 'object'], + checkContextTypes: true, + checkChildContextTypes: true, + }], + + // Forbid certain props on DOM Nodes + // https://github.com/yannickcr/eslint-plugin-react/blob/843d71a432baf0f01f598d7cf1eea75ad6896e4b/docs/rules/forbid-dom-props.md + 'react/forbid-dom-props': ['off', { forbid: [] }], // Enforce boolean attributes notation in JSX // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-boolean-value.md - 'react/jsx-boolean-value': ['error', 'never'], + 'react/jsx-boolean-value': ['error', 'never', { always: [] }], // Validate closing bracket location in JSX // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-closing-bracket-location.md 'react/jsx-closing-bracket-location': ['error', 'line-aligned'], // Validate closing tag location in JSX - // https://github.com/yannickcr/eslint-plugin-react/blob/9e13ae2c51e44872b45cc15bf1ac3a72105bdd0e/docs/rules/jsx-closing-tag-location.md - // TODO: enable, semver-minor - 'react/jsx-closing-tag-location': 'off', + // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-closing-tag-location.md + 'react/jsx-closing-tag-location': 'error', // Enforce or disallow spaces inside of curly braces in JSX attributes // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-curly-spacing.md @@ -81,7 +102,9 @@ module.exports = { 'react/jsx-no-bind': ['error', { ignoreRefs: true, allowArrowFunctions: true, + allowFunctions: false, allowBind: false, + ignoreDOMComponents: true, }], // Prevent duplicate props in JSX @@ -90,7 +113,7 @@ module.exports = { // Prevent usage of unwrapped JSX strings // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-literals.md - 'react/jsx-no-literals': 'off', + 'react/jsx-no-literals': ['off', { noStrings: true }], // Disallow undeclared variables in JSX // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-undef.md @@ -109,6 +132,7 @@ module.exports = { ignoreCase: true, callbacksLast: false, requiredFirst: false, + sortShapeProp: true, }], // Deprecated in favor of react/jsx-sort-props @@ -125,6 +149,12 @@ module.exports = { reservedFirst: true, }], + // Enforce defaultProps declarations alphabetical sorting + // https://github.com/yannickcr/eslint-plugin-react/blob/843d71a432baf0f01f598d7cf1eea75ad6896e4b/docs/rules/jsx-sort-default-props.md + 'react/jsx-sort-default-props': ['off', { + ignoreCase: true, + }], + // Prevent React to be incorrectly marked as unused // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-uses-react.md 'react/jsx-uses-react': ['error'], @@ -143,7 +173,8 @@ module.exports = { // Prevent usage of setState in componentDidMount // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-did-mount-set-state.md - 'react/no-did-mount-set-state': 'error', + // this is necessary for server-rendering + 'react/no-did-mount-set-state': 'off', // Prevent usage of setState in componentDidUpdate // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-did-update-set-state.md @@ -163,7 +194,7 @@ module.exports = { // Prevent multiple component definition per file // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-multi-comp.md - 'react/no-multi-comp': ['error', { ignoreStateless: true }], + 'react/no-multi-comp': 'off', // Prevent usage of setState // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-set-state.md @@ -187,7 +218,11 @@ module.exports = { // Prevent missing props validation in a React component definition // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/prop-types.md - 'react/prop-types': ['error', { ignore: [], customValidators: [], skipUndeclared: false }], + 'react/prop-types': ['error', { + ignore: [], + customValidators: [], + skipUndeclared: false + }], // Prevent missing React when using JSX // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/react-in-jsx-scope.md @@ -202,26 +237,66 @@ module.exports = { 'react/self-closing-comp': 'error', // Enforce component methods order - // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/sort-comp.md + // https://github.com/yannickcr/eslint-plugin-react/blob/843d71a432baf0f01f598d7cf1eea75ad6896e4b/docs/rules/sort-comp.md 'react/sort-comp': ['error', { order: [ 'static-methods', + 'instance-variables', 'lifecycle', '/^on.+$/', + 'getters', + 'setters', '/^(get|set)(?!(InitialState$|DefaultProps$|ChildContext$)).+$/', + 'instance-methods', 'everything-else', - '/^render.+$/', - 'render' + 'rendering', ], + groups: { + lifecycle: [ + 'displayName', + 'propTypes', + 'contextTypes', + 'childContextTypes', + 'mixins', + 'statics', + 'defaultProps', + 'constructor', + 'getDefaultProps', + 'getInitialState', + 'state', + 'getChildContext', + 'getDerivedStateFromProps', + 'componentWillMount', + 'UNSAFE_componentWillMount', + 'componentDidMount', + 'componentWillReceiveProps', + 'UNSAFE_componentWillReceiveProps', + 'shouldComponentUpdate', + 'componentWillUpdate', + 'UNSAFE_componentWillUpdate', + 'getSnapshotBeforeUpdate', + 'componentDidUpdate', + 'componentDidCatch', + 'componentWillUnmount', + 'componentDidCatch' + ], + rendering: [ + '/^render.+$/', + 'render' + ], + }, }], // Prevent missing parentheses around multilines JSX - // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-wrap-multilines.md + // https://github.com/yannickcr/eslint-plugin-react/blob/843d71a432baf0f01f598d7cf1eea75ad6896e4b/docs/rules/jsx-wrap-multilines.md 'react/jsx-wrap-multilines': ['error', { - declaration: true, - assignment: true, - return: true, - arrow: true, + declaration: 'parens-new-line', + assignment: 'parens-new-line', + return: 'parens-new-line', + arrow: 'parens-new-line', + condition: 'parens-new-line', + logical: 'parens-new-line', + prop: 'parens-new-line', }], // Require that the first prop in a JSX element be on a new line when the element is multiline @@ -237,8 +312,8 @@ module.exports = { 'react/jsx-indent': ['error', 2], // Disallow target="_blank" on links - // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-target-blank.md - 'react/jsx-no-target-blank': 'error', + // https://github.com/yannickcr/eslint-plugin-react/blob/ac102885765be5ff37847a871f239c6703e1c7cc/docs/rules/jsx-no-target-blank.md + 'react/jsx-no-target-blank': ['error', { enforceDynamicLinks: 'always' }], // only .jsx files may have JSX // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-filename-extension.md @@ -293,11 +368,12 @@ module.exports = { 'react/no-children-prop': 'error', // Validate whitespace in and around the JSX opening and closing brackets - // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-tag-spacing.md + // https://github.com/yannickcr/eslint-plugin-react/blob/843d71a432baf0f01f598d7cf1eea75ad6896e4b/docs/rules/jsx-tag-spacing.md 'react/jsx-tag-spacing': ['error', { closingSlash: 'never', beforeSelfClosing: 'always', - afterOpening: 'never' + afterOpening: 'never', + beforeClosing: 'never', }], // Enforce spaces before the closing bracket of self-closing JSX elements @@ -310,12 +386,16 @@ module.exports = { 'react/no-array-index-key': 'error', // Enforce a defaultProps definition for every prop that is not a required prop - // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/require-default-props.md - 'react/require-default-props': 'error', + // https://github.com/yannickcr/eslint-plugin-react/blob/843d71a432baf0f01f598d7cf1eea75ad6896e4b/docs/rules/require-default-props.md + 'react/require-default-props': ['error', { + forbidDefaultForRequired: true, + }], // Forbids using non-exported propTypes // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/forbid-foreign-prop-types.md - 'react/forbid-foreign-prop-types': 'off', + // this is intentionally set to "warn". it would be "error", + // but it's only critical if you're stripping propTypes in production. + 'react/forbid-foreign-prop-types': ['warn', { allowInPropTypes: true }], // Prevent void DOM elements from receiving children // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/void-dom-elements-no-children.md @@ -323,13 +403,103 @@ module.exports = { // Enforce all defaultProps have a corresponding non-required PropType // https://github.com/yannickcr/eslint-plugin-react/blob/9e13ae2c51e44872b45cc15bf1ac3a72105bdd0e/docs/rules/default-props-match-prop-types.md - // TODO: enable, semver-minor - 'react/default-props-match-prop-types': ['off', { allowRequiredDefaults: false }], + 'react/default-props-match-prop-types': ['error', { allowRequiredDefaults: false }], // Prevent usage of shouldComponentUpdate when extending React.PureComponent // https://github.com/yannickcr/eslint-plugin-react/blob/9e13ae2c51e44872b45cc15bf1ac3a72105bdd0e/docs/rules/no-redundant-should-component-update.md - // TODO: enable, semver-major - 'react/no-redundant-should-component-update': 'off', + 'react/no-redundant-should-component-update': 'error', + + // Prevent unused state values + // https://github.com/yannickcr/eslint-plugin-react/pull/1103/ + 'react/no-unused-state': 'error', + + // Enforces consistent naming for boolean props + // https://github.com/yannickcr/eslint-plugin-react/blob/843d71a432baf0f01f598d7cf1eea75ad6896e4b/docs/rules/boolean-prop-naming.md + 'react/boolean-prop-naming': ['off', { + propTypeNames: ['bool', 'mutuallyExclusiveTrueProps'], + rule: '^(is|has)[A-Z]([A-Za-z0-9]?)+', + message: '', + }], + + // Prevents common casing typos + // https://github.com/yannickcr/eslint-plugin-react/blob/73abadb697034b5ccb514d79fb4689836fe61f91/docs/rules/no-typos.md + 'react/no-typos': 'error', + + // Enforce curly braces or disallow unnecessary curly braces in JSX props and/or children + // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-curly-brace-presence.md + 'react/jsx-curly-brace-presence': ['error', { props: 'never', children: 'never' }], + + // One JSX Element Per Line + // https://github.com/yannickcr/eslint-plugin-react/blob/843d71a432baf0f01f598d7cf1eea75ad6896e4b/docs/rules/jsx-one-expression-per-line.md + 'react/jsx-one-expression-per-line': ['error', { allow: 'single-child' }], + + // Enforce consistent usage of destructuring assignment of props, state, and context + // https://github.com/yannickcr/eslint-plugin-react/blob/843d71a432baf0f01f598d7cf1eea75ad6896e4b/docs/rules/destructuring-assignment.md + 'react/destructuring-assignment': ['error', 'always'], + + // Prevent using this.state within a this.setState + // https://github.com/yannickcr/eslint-plugin-react/blob/843d71a432baf0f01f598d7cf1eea75ad6896e4b/docs/rules/no-access-state-in-setstate.md + 'react/no-access-state-in-setstate': 'error', + + // Prevent usage of button elements without an explicit type attribute + // https://github.com/yannickcr/eslint-plugin-react/blob/843d71a432baf0f01f598d7cf1eea75ad6896e4b/docs/rules/button-has-type.md + 'react/button-has-type': ['error', { + button: true, + submit: true, + reset: false, + }], + + // Ensures inline tags are not rendered without spaces between them + 'react/jsx-child-element-spacing': 'off', + + // Prevent this from being used in stateless functional components + // https://github.com/yannickcr/eslint-plugin-react/blob/843d71a432baf0f01f598d7cf1eea75ad6896e4b/docs/rules/no-this-in-sfc.md + 'react/no-this-in-sfc': 'error', + + // Validate JSX maximum depth + // https://github.com/yannickcr/eslint-plugin-react/blob/abe8381c0d6748047224c430ce47f02e40160ed0/docs/rules/jsx-max-depth.md + 'react/jsx-max-depth': 'off', + + // Disallow multiple spaces between inline JSX props + // https://github.com/yannickcr/eslint-plugin-react/blob/ac102885765be5ff37847a871f239c6703e1c7cc/docs/rules/jsx-props-no-multi-spaces.md + 'react/jsx-props-no-multi-spaces': 'error', + + // Prevent usage of UNSAFE_ methods + // https://github.com/yannickcr/eslint-plugin-react/blob/157cc932be2cfaa56b3f5b45df6f6d4322a2f660/docs/rules/no-unsafe.md + 'react/no-unsafe': 'off', + + // Enforce shorthand or standard form for React fragments + // https://github.com/yannickcr/eslint-plugin-react/blob/bc976b837abeab1dffd90ac6168b746a83fc83cc/docs/rules/jsx-fragments.md + 'react/jsx-fragments': ['error', 'syntax'], + + // Enforce linebreaks in curly braces in JSX attributes and expressions. + // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-curly-newline.md + 'react/jsx-curly-newline': ['error', { + multiline: 'consistent', + singleline: 'consistent', + }], + + // Enforce state initialization style + // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/state-in-constructor.md + // TODO: set to "never" once babel-preset-airbnb supports public class fields + 'react/state-in-constructor': ['error', 'always'], + + // Enforces where React component static properties should be positioned + // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/static-property-placement.md + // TODO: set to "static public field" once babel-preset-airbnb supports public class fields + 'react/static-property-placement': ['error', 'property assignment'], + + // Disallow JSX props spreading + // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-props-no-spreading.md + 'react/jsx-props-no-spreading': ['error', { + html: 'enforce', + custom: 'enforce', + exceptions: [], + }], + + // Enforce that props are read-only + // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/prefer-read-only-props.md + 'react/prefer-read-only-props': 'off', }, settings: { @@ -340,7 +510,12 @@ module.exports = { }, react: { pragma: 'React', - version: '15.0' + version: 'detect', }, + propWrapperFunctions: [ + 'forbidExtraProps', // https://www.npmjs.com/package/airbnb-prop-types + 'exact', // https://www.npmjs.com/package/prop-types-exact + 'Object.freeze', // https://tc39.github.io/ecma262/#sec-object.freeze + ], } }; diff --git a/packages/eslint-config-airbnb/test/requires.js b/packages/eslint-config-airbnb/test/requires.js new file mode 100644 index 0000000000..72f4ab8078 --- /dev/null +++ b/packages/eslint-config-airbnb/test/requires.js @@ -0,0 +1,15 @@ +/* eslint strict: 0, global-require: 0 */ + +'use strict'; + +const test = require('tape'); + +test('all entry points parse', (t) => { + t.doesNotThrow(() => require('..'), 'index does not throw'); + t.doesNotThrow(() => require('../base'), 'base does not throw'); + t.doesNotThrow(() => require('../legacy'), 'legacy does not throw'); + t.doesNotThrow(() => require('../whitespace'), 'whitespace does not throw'); + t.doesNotThrow(() => require('../hooks'), 'hooks does not throw'); + + t.end(); +}); diff --git a/packages/eslint-config-airbnb/test/test-base.js b/packages/eslint-config-airbnb/test/test-base.js index 2b946a462c..c283c3410c 100644 --- a/packages/eslint-config-airbnb/test/test-base.js +++ b/packages/eslint-config-airbnb/test/test-base.js @@ -22,13 +22,13 @@ Object.keys(files).forEach((name) => { t.plan(2); // scan plugins for react and fail if it is found - const hasReactPlugin = Object.prototype.hasOwnProperty.call(config, 'plugins') && - config.plugins.indexOf('react') !== -1; + const hasReactPlugin = Object.prototype.hasOwnProperty.call(config, 'plugins') + && config.plugins.indexOf('react') !== -1; t.notOk(hasReactPlugin, 'there is no react plugin'); // scan rules for react/ and fail if any exist const reactRuleIds = Object.keys(config.rules) - .filter(ruleId => ruleId.indexOf('react/') === 0); + .filter((ruleId) => ruleId.indexOf('react/') === 0); t.deepEquals(reactRuleIds, [], 'there are no react/ rules'); }); }); diff --git a/packages/eslint-config-airbnb/test/test-react-order.js b/packages/eslint-config-airbnb/test/test-react-order.js index 0e7fd9e6ec..2527f04e19 100644 --- a/packages/eslint-config-airbnb/test/test-react-order.js +++ b/packages/eslint-config-airbnb/test/test-react-order.js @@ -1,6 +1,6 @@ import test from 'tape'; import { CLIEngine } from 'eslint'; -import eslintrc from '../'; +import eslintrc from '..'; import reactRules from '../rules/react'; import reactA11yRules from '../rules/react-a11y'; @@ -11,12 +11,14 @@ const cli = new CLIEngine({ rules: { // It is okay to import devDependencies in tests. 'import/no-extraneous-dependencies': [2, { devDependencies: true }], + // this doesn't matter for tests + 'lines-between-class-members': 0, }, }); function lint(text) { - // @see http://eslint.org/docs/developer-guide/nodejs-api.html#executeonfiles - // @see http://eslint.org/docs/developer-guide/nodejs-api.html#executeontext + // @see https://eslint.org/docs/developer-guide/nodejs-api.html#executeonfiles + // @see https://eslint.org/docs/developer-guide/nodejs-api.html#executeontext const linter = cli.executeOnText(text); return linter.results[0]; } diff --git a/packages/eslint-config-airbnb/whitespace.js b/packages/eslint-config-airbnb/whitespace.js new file mode 100644 index 0000000000..654773758d --- /dev/null +++ b/packages/eslint-config-airbnb/whitespace.js @@ -0,0 +1,92 @@ +const assign = require('object.assign'); +const entries = require('object.entries'); +const CLIEngine = require('eslint').CLIEngine; + +const baseConfig = require('.'); + +function onlyErrorOnRules(rulesToError, config) { + const errorsOnly = assign({}, config); + const cli = new CLIEngine({ baseConfig: config, useEslintrc: false }); + const baseRules = cli.getConfigForFile(require.resolve('./')).rules; + + entries(baseRules).forEach((rule) => { + const ruleName = rule[0]; + const ruleConfig = rule[1]; + + if (rulesToError.indexOf(ruleName) === -1) { + if (Array.isArray(ruleConfig)) { + errorsOnly.rules[ruleName] = ['warn'].concat(ruleConfig.slice(1)); + } else if (typeof ruleConfig === 'number') { + errorsOnly.rules[ruleName] = 1; + } else { + errorsOnly.rules[ruleName] = 'warn'; + } + } + }); + + return errorsOnly; +} + +module.exports = onlyErrorOnRules([ + 'array-bracket-newline', + 'array-bracket-spacing', + 'array-element-newline', + 'arrow-spacing', + 'block-spacing', + 'comma-spacing', + 'computed-property-spacing', + 'dot-location', + 'eol-last', + 'func-call-spacing', + 'function-paren-newline', + 'generator-star-spacing', + 'implicit-arrow-linebreak', + 'indent', + 'key-spacing', + 'keyword-spacing', + 'line-comment-position', + 'linebreak-style', + 'multiline-ternary', + 'newline-per-chained-call', + 'no-irregular-whitespace', + 'no-mixed-spaces-and-tabs', + 'no-multi-spaces', + 'no-regex-spaces', + 'no-spaced-func', + 'no-trailing-spaces', + 'no-whitespace-before-property', + 'nonblock-statement-body-position', + 'object-curly-newline', + 'object-curly-spacing', + 'object-property-newline', + 'one-var-declaration-per-line', + 'operator-linebreak', + 'padded-blocks', + 'padding-line-between-statements', + 'rest-spread-spacing', + 'semi-spacing', + 'semi-style', + 'space-before-blocks', + 'space-before-function-paren', + 'space-in-parens', + 'space-infix-ops', + 'space-unary-ops', + 'spaced-comment', + 'switch-colon-spacing', + 'template-tag-spacing', + 'import/newline-after-import', + // eslint-plugin-react rules + 'react/jsx-child-element-spacing', + 'react/jsx-closing-bracket-location', + 'react/jsx-closing-tag-location', + 'react/jsx-curly-spacing', + 'react/jsx-equals-spacing', + 'react/jsx-first-prop-newline', + 'react/jsx-indent', + 'react/jsx-indent-props', + 'react/jsx-max-props-per-line', + 'react/jsx-one-expression-per-line', + 'react/jsx-space-before-closing', + 'react/jsx-tag-spacing', + 'react/jsx-wrap-multilines', +], baseConfig); diff --git a/react/README.md b/react/README.md index b86f743f23..7e247e7ac1 100644 --- a/react/README.md +++ b/react/README.md @@ -2,6 +2,8 @@ *A mostly reasonable approach to React and JSX* +This style guide is mostly based on the standards that are currently prevalent in JavaScript, although some conventions (i.e async/await or static class fields) may still be included or prohibited on a case-by-case basis. Currently, anything prior to stage 3 is not included nor recommended in this guide. + ## Table of Contents 1. [Basic Rules](#basic-rules) @@ -25,7 +27,8 @@ - Only include one React component per file. - However, multiple [Stateless, or Pure, Components](https://facebook.github.io/react/docs/reusable-components.html#stateless-functions) are allowed per file. eslint: [`react/no-multi-comp`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-multi-comp.md#ignorestateless). - Always use JSX syntax. - - Do not use `React.createElement` unless you're initializing the app from a file that is not JSX. + - Do not use `React.createElement` unless you’re initializing the app from a file that is not JSX. + - [`react/forbid-prop-types`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/forbid-prop-types.md) will allow `arrays` and `objects` only if it is explicitly noted what `array` and `object` contains, using `arrayOf`, `objectOf`, or `shape`. ## Class vs `React.createClass` vs stateless @@ -49,7 +52,7 @@ } ``` - And if you don't have state or refs, prefer normal functions (not arrow functions) over classes: + And if you don’t have state or refs, prefer normal functions (not arrow functions) over classes: ```jsx // bad @@ -78,7 +81,7 @@ ## Naming - - **Extensions**: Use `.jsx` extension for React components. + - **Extensions**: Use `.jsx` extension for React components. eslint: [`react/jsx-filename-extension`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-filename-extension.md) - **Filename**: Use PascalCase for filenames. E.g., `ReservationCard.jsx`. - **Reference Naming**: Use PascalCase for React components and camelCase for their instances. eslint: [`react/jsx-pascal-case`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-pascal-case.md) @@ -108,9 +111,9 @@ // good import Footer from './Footer'; ``` - - **Higher-order Component Naming**: Use a composite of the higher-order component's name and the passed-in component's name as the `displayName` on the generated component. For example, the higher-order component `withFoo()`, when passed a component `Bar` should produce a component with a `displayName` of `withFoo(Bar)`. + - **Higher-order Component Naming**: Use a composite of the higher-order component’s name and the passed-in component’s name as the `displayName` on the generated component. For example, the higher-order component `withFoo()`, when passed a component `Bar` should produce a component with a `displayName` of `withFoo(Bar)`. - > Why? A component's `displayName` may be used by developer tools or in error messages, and having a value that clearly expresses this relationship helps people understand what is happening. + > Why? A component’s `displayName` may be used by developer tools or in error messages, and having a value that clearly expresses this relationship helps people understand what is happening. ```jsx // bad @@ -143,6 +146,9 @@ // bad + // bad + + // good ``` @@ -165,7 +171,7 @@ ## Alignment - - Follow these alignment styles for JSX syntax. eslint: [`react/jsx-closing-bracket-location`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-closing-bracket-location.md) + - Follow these alignment styles for JSX syntax. eslint: [`react/jsx-closing-bracket-location`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-closing-bracket-location.md) [`react/jsx-closing-tag-location`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-closing-tag-location.md) ```jsx // bad @@ -188,11 +194,30 @@ > + + // bad + {showButton && + + + ` +}) +``` + +#### Good + +```js +Vue.component('TodoItem', { + props: { + todo: { + type: Object, + required: true + } + }, + template: ` + + ` +}) +``` + +```js +Vue.component('TodoItem', { + props: { + todo: { + type: Object, + required: true + } + }, + template: ` + + {{ todo.text }} + + + ` +}) +``` + + + +### Non-flux state management (use with caution) + +**[Vuex](https://github.com/vuejs/vuex) should be preferred for global state management, instead of `this.$root` or a global event bus.** + +Managing state on `this.$root` and/or using a [global event bus](https://vuejs.org/v2/guide/migration.html#dispatch-and-broadcast-replaced) can be convenient for very simple cases, but are not appropriate for most applications. Vuex offers not only a central place to manage state, but also tools for organizing, tracking, and debugging state changes. + +#### Bad + +```js +// main.js +new Vue({ + data: { + todos: [] + }, + created: function () { + this.$on('remove-todo', this.removeTodo) + }, + methods: { + removeTodo: function (todo) { + var todoIdToRemove = todo.id + this.todos = this.todos.filter(function (todo) { + return todo.id !== todoIdToRemove + }) + } + } +}) +``` + +#### Good + +```js +// store/modules/todos.js +export default { + state: { + list: [] + }, + mutations: { + REMOVE_TODO (state, todoId) { + state.list = state.list.filter(todo => todo.id !== todoId) + } + }, + actions: { + removeTodo ({ commit, state }, todo) { + commit('REMOVE_TODO', todo.id) + } + } +} +``` + +```html + + + + +``` + +```html + +``` + +## References + +- [Style Source: VueJs.org](https://github.com/vuejs/vuejs.org/blob/master/src/v2/style-guide/index.md)