diff --git a/README.md b/README.md index 0ef940bf17..69571fd065 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,9 @@ - [2.1](#2.1) Use `const` for all of your references; avoid using `var`. - > Why? This ensures that you can't reassign your references (mutation), which can lead to bugs and difficult to comprehend code. + > Why? This ensures that you can't reassign your references, which can lead to bugs and difficult to comprehend code. + + eslint rules: [`prefer-const`](http://eslint.org/docs/rules/prefer-const.html), [`no-const-assign`](http://eslint.org/docs/rules/no-const-assign.html). ```javascript // bad @@ -88,9 +90,11 @@ const b = 2; ``` - - [2.2](#2.2) If you must mutate references, use `let` instead of `var`. + - [2.2](#2.2) If you must reassign references, use `let` instead of `var`. + + > Why? `let` is block-scoped rather than function-scoped like `var`. - > Why? `let` is block-scoped rather than function-scoped like `var`. + eslint rules: [`no-var`](http://eslint.org/docs/rules/no-var.html). ```javascript // bad @@ -124,6 +128,8 @@ - [3.1](#3.1) Use the literal syntax for object creation. + eslint rules: [`no-new-object`](http://eslint.org/docs/rules/no-new-object.html). + ```javascript // bad const item = new Object(); @@ -170,7 +176,7 @@ - [3.4](#3.4) Use computed property names when creating objects with dynamic property names. - > Why? They allow you to define all the properties of an object in one place. + > Why? They allow you to define all the properties of an object in one place. ```javascript @@ -196,6 +202,8 @@ - [3.5](#3.5) Use object method shorthand. + eslint rules: [`object-shorthand`](http://eslint.org/docs/rules/object-shorthand.html). + ```javascript // bad const atom = { @@ -219,7 +227,9 @@ - [3.6](#3.6) Use property value shorthand. - > Why? It is shorter to write and descriptive. + > Why? It is shorter to write and descriptive. + + eslint rules: [`object-shorthand`](http://eslint.org/docs/rules/object-shorthand.html). ```javascript const lukeSkywalker = 'Luke Skywalker'; @@ -237,7 +247,7 @@ - [3.7](#3.7) Group your shorthand properties at the beginning of your object declaration. - > Why? It's easier to tell which properties are using the shorthand. + > Why? It's easier to tell which properties are using the shorthand. ```javascript const anakinSkywalker = 'Anakin Skywalker'; @@ -264,12 +274,36 @@ }; ``` + - [3.8](#3.8) Only quote properties that are invalid identifiers. + + > Why? In general we consider it subjectively easier to read. It improves syntax highlighting, and is also more easily optimized by many JS engines. + + eslint rules: [`quote-props`](http://eslint.org/docs/rules/quote-props.html). + + ```javascript + // bad + const bad = { + 'foo': 3, + 'bar': 4, + 'data-blah': 5, + }; + + // good + const good = { + foo: 3, + bar: 4, + 'data-blah': 5, + }; + ``` + **[⬆ back to top](#table-of-contents)** ## Arrays - [4.1](#4.1) Use the literal syntax for array creation. + eslint rules: [`no-array-constructor`](http://eslint.org/docs/rules/no-array-constructor.html). + ```javascript // bad const items = new Array(); @@ -319,7 +353,7 @@ - [5.1](#5.1) Use object destructuring when accessing and using multiple properties of an object. - > Why? Destructuring saves you from creating temporary references for those properties. + > Why? Destructuring saves you from creating temporary references for those properties. ```javascript // bad @@ -331,8 +365,8 @@ } // good - function getFullName(obj) { - const { firstName, lastName } = obj; + function getFullName(user) { + const { firstName, lastName } = user; return `${firstName} ${lastName}`; } @@ -357,7 +391,7 @@ - [5.3](#5.3) 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. + > Why? You can add new properties over time or change the order of things without breaking call sites. ```javascript // bad @@ -386,6 +420,8 @@ - [6.1](#6.1) Use single quotes `''` for strings. + eslint rules: [`quotes`](http://eslint.org/docs/rules/quotes.html). + ```javascript // bad const name = "Capt. Janeway"; @@ -394,7 +430,7 @@ const name = 'Capt. Janeway'; ``` - - [6.2](#6.2) Strings longer than 100 characters should be written across multiple lines using string concatenation. + - [6.2](#6.2) Strings that cause the line to go over 100 characters should be written across multiple lines using string concatenation. - [6.3](#6.3) Note: If overused, long strings with concatenation could impact performance. [jsPerf](http://jsperf.com/ya-string-concat) & [Discussion](https://github.com/airbnb/javascript/issues/40). ```javascript @@ -416,7 +452,9 @@ - [6.4](#6.4) When programmatically building up strings, use template strings instead of concatenation. - > Why? Template strings give you a readable, concise syntax with proper newlines and string interpolation features. + > Why? Template strings give you a readable, concise syntax with proper newlines and string interpolation features. + + eslint rules: [`prefer-template`](http://eslint.org/docs/rules/prefer-template.html). ```javascript // bad @@ -434,7 +472,7 @@ return `How are you, ${name}?`; } ``` - - [6.5](#6.5) Never use eval() on a string, it opens too many vulnerabilities. + - [6.5](#6.5) Never use `eval()` on a string, it opens too many vulnerabilities. **[⬆ back to top](#table-of-contents)** @@ -443,7 +481,7 @@ - [7.1](#7.1) Use function declarations instead of function expressions. - > Why? Function declarations are named, so they're easier to identify in call stacks. Also, the whole body of a function declaration is hoisted, whereas only the reference of a function expression is hoisted. This rule makes it possible to always use [Arrow Functions](#arrow-functions) in place of function expressions. + > Why? Function declarations are named, so they're easier to identify in call stacks. Also, the whole body of a function declaration is hoisted, whereas only the reference of a function expression is hoisted. This rule makes it possible to always use [Arrow Functions](#arrow-functions) in place of function expressions. ```javascript // bad @@ -465,6 +503,7 @@ ``` - [7.3](#7.3) 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. + - [7.4](#7.4) **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). ```javascript @@ -501,7 +540,7 @@ - [7.6](#7.6) Never use `arguments`, opt to use rest syntax `...` instead. - > Why? `...` is explicit about which arguments you want pulled. Plus rest arguments are a real Array and not Array-like like `arguments`. + > Why? `...` is explicit about which arguments you want pulled. Plus rest arguments are a real Array and not Array-like like `arguments`. ```javascript // bad @@ -545,19 +584,19 @@ - [7.8](#7.8) Avoid side effects with default parameters. - > Why? They are confusing to reason about. + > Why? They are confusing to reason about. - ```javascript - var b = 1; - // bad - function count(a = b++) { - console.log(a); - } - count(); // 1 - count(); // 2 - count(3); // 3 - count(); // 3 - ``` + ```javascript + var b = 1; + // bad + function count(a = b++) { + console.log(a); + } + count(); // 1 + count(); // 2 + count(3); // 3 + count(); // 3 + ``` - [7.9](#7.9) Always put default parameters last. @@ -573,17 +612,75 @@ } ``` -- [7.10](#7.10) Never use the Function constructor to create a new function. + - [7.10](#7.10) Never use the Function constructor to create a new function. - > 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 - var add = new Function('a', 'b', 'return a + b'); + ```javascript + // bad + var add = new Function('a', 'b', 'return a + b'); - // still bad - var subtract = Function('a', 'b', 'return a - b'); - ``` + // still bad + var subtract = Function('a', 'b', 'return a - b'); + ``` + + - [7.11](#7.11) Spacing in a function signature. + + > Why? Consistency is good, and you shouldn’t have to add or remove a space when adding or removing a name. + + ```javascript + // bad + const f = function(){}; + const g = function (){}; + const h = function() {}; + + // good + const x = function () {}; + const y = function a() {}; + ``` + + - [7.12](#7.12) Never mutate parameters. + + > Why? Manipulating objects passed in as parameters can cause unwanted variable side effects in the original caller. + + eslint rules: [`no-param-reassign`](http://eslint.org/docs/rules/no-param-reassign.html). + + ```javascript + // bad + function f1(obj) { + obj.key = 1; + }; + + // good + function f2(obj) { + const key = Object.prototype.hasOwnProperty.call(obj, 'key') ? obj.key : 1; + }; + ``` + + - [7.13](#7.13) Never reassign parameters. + + > Why? Reassigning parameters can lead to unexpected behavior, especially when accessing the `arguments` object. It can also cause optimization issues, especially in V8. + + eslint rules: [`no-param-reassign`](http://eslint.org/docs/rules/no-param-reassign.html). + + ```javascript + // bad + function f1(a) { + a = 1; + } + + function f2(a) { + if (!a) { a = 1; } + } + + // good + function f3(a) { + const b = a || 1; + } + + function f4(a = 1) { + } + ``` **[⬆ back to top](#table-of-contents)** @@ -591,9 +688,11 @@ - [8.1](#8.1) When you must use function expressions (as when passing an anonymous function), use arrow function notation. - > 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? 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 function declaration. + eslint rules: [`prefer-arrow-callback`](http://eslint.org/docs/rules/prefer-arrow-callback.html), [`arrow-spacing`](http://eslint.org/docs/rules/arrow-spacing.html). ```javascript // bad @@ -609,11 +708,13 @@ }); ``` - - [8.2](#8.2) If the function body consists of a single expression, feel free to omit the braces and use the implicit return. Otherwise use a `return` statement. + - [8.2](#8.2) If the function body consists of a single expression, omit the braces and use the implicit return. Otherwise, keep the braces and use a `return` statement. - > Why? Syntactic sugar. It reads well when multiple functions are chained together. + > Why? Syntactic sugar. It reads well when multiple functions are chained together. - > Why not? If you plan on returning an object. + > Why not? If you plan on returning an object. + + eslint rules: [`arrow-parens`](http://eslint.org/docs/rules/arrow-parens.html), [`arrow-body-style`](http://eslint.org/docs/rules/arrow-body-style.html). ```javascript // good @@ -634,7 +735,7 @@ - [8.3](#8.3) In case the expression spans over multiple lines, wrap it in parentheses for better readability. - > Why? It shows clearly where the function starts and ends. + > Why? It shows clearly where the function starts and ends. ```js // bad @@ -651,16 +752,36 @@ ``` - - [8.4](#8.4) If your function only takes a single argument, feel free to omit the parentheses. + - [8.4](#8.4) If your function takes a single argument and doesn’t use braces, omit the parentheses. Otherwise, always include parentheses around arguments. + + > Why? Less visual clutter. - > Why? Less visual clutter. + eslint rules: [`arrow-parens`](http://eslint.org/docs/rules/arrow-parens.html). ```js + // bad + [1, 2, 3].map((x) => x * x); + // good [1, 2, 3].map(x => x * x); // good - [1, 2, 3].reduce((y, x) => x + y); + [1, 2, 3].map(number => ( + `A long string with the ${number}. It’s so long that we’ve broken it ` + + 'over multiple lines!' + )); + + // bad + [1, 2, 3].map(x => { + const y = x + 1; + return x * y; + }); + + // good + [1, 2, 3].map((x) => { + const y = x + 1; + return x * y; + }); ``` **[⬆ back to top](#table-of-contents)** @@ -670,14 +791,14 @@ - [9.1](#9.1) Always use `class`. Avoid manipulating `prototype` directly. - > Why? `class` syntax is more concise and easier to reason about. + > Why? `class` syntax is more concise and easier to reason about. ```javascript // bad function Queue(contents = []) { this._queue = [...contents]; } - Queue.prototype.pop = function() { + Queue.prototype.pop = function () { const value = this._queue[0]; this._queue.splice(0, 1); return value; @@ -699,7 +820,7 @@ - [9.2](#9.2) Use `extends` for inheritance. - > Why? It is a built-in way to inherit prototype functionality without breaking `instanceof`. + > Why? It is a built-in way to inherit prototype functionality without breaking `instanceof`. ```javascript // bad @@ -708,7 +829,7 @@ Queue.apply(this, contents); } inherits(PeekableQueue, Queue); - PeekableQueue.prototype.peek = function() { + PeekableQueue.prototype.peek = function () { return this._queue[0]; } @@ -724,12 +845,12 @@ ```javascript // bad - Jedi.prototype.jump = function() { + Jedi.prototype.jump = function () { this.jumping = true; return true; }; - Jedi.prototype.setHeight = function(height) { + Jedi.prototype.setHeight = function (height) { this.height = height; }; @@ -782,7 +903,7 @@ - [10.1](#10.1) Always use modules (`import`/`export`) over a non-standard module system. You can always transpile to your preferred module system. - > Why? Modules are the future, let's start using the future now. + > Why? Modules are the future, let's start using the future now. ```javascript // bad @@ -800,7 +921,7 @@ - [10.2](#10.2) Do not use wildcard imports. - > Why? This makes sure you have a single default export. + > Why? This makes sure you have a single default export. ```javascript // bad @@ -812,7 +933,7 @@ - [10.3](#10.3) And do not export directly from an import. - > Why? Although the one-liner is concise, having one clear way to import and one clear way to export makes things consistent. + > Why? Although the one-liner is concise, having one clear way to import and one clear way to export makes things consistent. ```javascript // bad @@ -831,7 +952,9 @@ - [11.1](#11.1) Don't use iterators. Prefer JavaScript's higher-order functions like `map()` and `reduce()` instead of loops like `for-of`. - > Why? This enforces our immutable rule. Dealing with pure functions that return values is easier to reason about than side-effects. + > Why? This enforces our immutable rule. Dealing with pure functions that return values is easier to reason about than side-effects. + + eslint rules: [`no-iterator`](http://eslint.org/docs/rules/no-iterator.html). ```javascript const numbers = [1, 2, 3, 4, 5]; @@ -856,7 +979,7 @@ - [11.2](#11.2) Don't use generators for now. - > Why? They don't transpile well to ES5. + > Why? They don't transpile well to ES5. **[⬆ back to top](#table-of-contents)** @@ -865,6 +988,8 @@ - [12.1](#12.1) Use dot notation when accessing properties. + eslint rules: [`dot-notation`](http://eslint.org/docs/rules/dot-notation.html). + ```javascript const luke = { jedi: true, @@ -912,6 +1037,8 @@ > 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. + eslint rules: [`one-var`](http://eslint.org/docs/rules/one-var.html). + ```javascript // bad const items = getItems(), @@ -932,7 +1059,7 @@ - [13.3](#13.3) Group all your `const`s and then group all your `let`s. - > Why? This is helpful when later on you might need to assign a variable depending on one of the previous assigned variables. + > Why? This is helpful when later on you might need to assign a variable depending on one of the previous assigned variables. ```javascript // bad @@ -957,11 +1084,11 @@ - [13.4](#13.4) Assign variables where you need them, but place them in a reasonable place. - > Why? `let` and `const` are block scoped and not function scoped. + > Why? `let` and `const` are block scoped and not function scoped. ```javascript // good - function() { + function () { test(); console.log('doing stuff..'); @@ -977,7 +1104,7 @@ } // bad - unnecessary function call - function(hasName) { + function (hasName) { const name = getName(); if (!hasName) { @@ -990,7 +1117,7 @@ } // good - function(hasName) { + function (hasName) { if (!hasName) { return false; } @@ -1050,7 +1177,7 @@ anonymous(); // => TypeError anonymous is not a function - var anonymous = function() { + var anonymous = function () { console.log('anonymous function expression'); }; } @@ -1096,7 +1223,7 @@ } ``` - - For more information refer to [JavaScript Scoping & Hoisting](http://www.adequatelygood.com/2010/2/JavaScript-Scoping-and-Hoisting) by [Ben Cherry](http://www.adequatelygood.com/). + - For more information refer to [JavaScript Scoping & Hoisting](http://www.adequatelygood.com/2010/2/JavaScript-Scoping-and-Hoisting/) by [Ben Cherry](http://www.adequatelygood.com/). **[⬆ back to top](#table-of-contents)** @@ -1104,8 +1231,11 @@ ## Comparison Operators & Equality - [15.1](#15.1) Use `===` and `!==` over `==` and `!=`. + - [15.2](#15.2) Conditional statements such as the `if` statement evaluate their expression using coercion with the `ToBoolean` abstract method and always follow these simple rules: + eslint rules: [`eqeqeq`](http://eslint.org/docs/rules/eqeqeq.html). + + **Objects** evaluate to **true** + **Undefined** evaluates to **false** + **Null** evaluates to **false** @@ -1167,10 +1297,10 @@ } // bad - function() { return false; } + function () { return false; } // good - function() { + function () { return false; } ``` @@ -1178,6 +1308,8 @@ - [16.2](#16.2) 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 rules: [`brace-style`](http://eslint.org/docs/rules/brace-style.html). + ```javascript // bad if (test) { @@ -1235,7 +1367,7 @@ } ``` - - [17.2](#17.2) Use `//` for single line comments. Place single line comments on a newline above the subject of the comment. Put an empty line before the comment. + - [17.2](#17.2) Use `//` for single line comments. Place single line comments on a newline above the subject of the comment. Put an empty line before the comment unless it's on the first line of a block. ```javascript // bad @@ -1263,6 +1395,14 @@ return type; } + + // also good + function getType() { + // set the default type to 'no type' + const type = this._type || 'no type'; + + return type; + } ``` - [17.3](#17.3) 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`. @@ -1300,25 +1440,29 @@ - [18.1](#18.1) Use soft tabs set to 2 spaces. + eslint rules: [`indent`](http://eslint.org/docs/rules/indent.html). + ```javascript // bad - function() { + function () { ∙∙∙∙const name; } // bad - function() { + function () { ∙const name; } // good - function() { + function () { ∙∙const name; } ``` - [18.2](#18.2) Place 1 space before the leading brace. + eslint rules: [`space-before-blocks`](http://eslint.org/docs/rules/space-before-blocks.html). + ```javascript // bad function test(){ @@ -1343,7 +1487,9 @@ }); ``` - - [18.3](#18.3) Place 1 space before the opening parenthesis in control statements (`if`, `while` etc.). Place no space before the argument list in function calls and declarations. + - [18.3](#18.3) 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 rules: [`space-after-keywords`](http://eslint.org/docs/rules/space-after-keywords.html), [`space-before-keywords`](http://eslint.org/docs/rules/space-before-keywords.html). ```javascript // bad @@ -1369,6 +1515,8 @@ - [18.4](#18.4) Set off operators with spaces. + eslint rules: [`space-infix-ops`](http://eslint.org/docs/rules/space-infix-ops.html). + ```javascript // bad const x=y+5; @@ -1381,14 +1529,14 @@ ```javascript // bad - (function(global) { + (function (global) { // ...stuff... })(this); ``` ```javascript // bad - (function(global) { + (function (global) { // ...stuff... })(this);↵ ↵ @@ -1396,7 +1544,7 @@ ```javascript // good - (function(global) { + (function (global) { // ...stuff... })(this);↵ ``` @@ -1498,6 +1646,118 @@ return arr; ``` + - [18.8](#18.8) Do not pad your blocks with blank lines. + + eslint rules: [`padded-blocks`](http://eslint.org/docs/rules/padded-blocks.html). + + ```javascript + // bad + function bar() { + + console.log(foo); + + } + + // also bad + if (baz) { + + console.log(qux); + } else { + console.log(foo); + + } + + // good + function bar() { + console.log(foo); + } + + // good + if (baz) { + console.log(qux); + } else { + console.log(foo); + } + ``` + + - [18.9](#18.9) Do not add spaces inside parentheses. + + eslint rules: [`space-in-parens`](http://eslint.org/docs/rules/space-in-parens.html). + + ```javascript + // bad + function bar( foo ) { + return foo; + } + + // good + function bar(foo) { + return foo; + } + + // bad + if ( foo ) { + console.log(foo); + } + + // good + if (foo) { + console.log(foo); + } + ``` + + - [18.10](#18.10) Do not add spaces inside brackets. + + eslint rules: [`array-bracket-spacing`](http://eslint.org/docs/rules/array-bracket-spacing.html). + + ```javascript + // bad + const foo = [ 1, 2, 3 ]; + console.log(foo[ 0 ]); + + // good + const foo = [1, 2, 3]; + console.log(foo[0]); + ``` + + - [18.11](#18.11) Add spaces inside curly braces. + + eslint rules: [`object-curly-spacing`](http://eslint.org/docs/rules/object-curly-spacing.html). + + ```javascript + // bad + const foo = {clark: 'kent'}; + + // good + const foo = { clark: 'kent' }; + ``` + + - [18.12](#18.12) Avoid having lines of code that are longer than 100 characters (including whitespace). + + > Why? This ensures readability and maintainability. + + eslint rules: [`max-len`](http://eslint.org/docs/rules/max-len.html). + + ```javascript + // bad + const foo = 'Whatever national crop flips the window. The cartoon reverts within the screw. Whatever wizard constrains a helpful ally. The counterpart ascends!'; + + // bad + $.ajax({ method: 'POST', url: 'https://airbnb.com/', data: { name: 'John' } }).done(() => console.log('Congratulations!')).fail(() => console.log('You have failed this city.')); + + // good + const foo = 'Whatever national crop flips the window. The cartoon reverts within the screw. ' + + 'Whatever wizard constrains a helpful ally. The counterpart ascends!'; + + // good + $.ajax({ + method: 'POST', + url: 'https://airbnb.com/', + data: { name: 'John' }, + }) + .done(() => console.log('Congratulations!')) + .fail(() => console.log('You have failed this city.')); + ``` **[⬆ back to top](#table-of-contents)** @@ -1505,6 +1765,8 @@ - [19.1](#19.1) Leading commas: **Nope.** + eslint rules: [`comma-style`](http://eslint.org/docs/rules/comma-style.html). + ```javascript // bad const story = [ @@ -1539,7 +1801,9 @@ - [19.2](#19.2) Additional trailing comma: **Yup.** - > 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](es5/README.md#commas) in legacy browsers. + eslint rules: [`comma-dangle`](http://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](es5/README.md#commas) in legacy browsers. ```javascript // bad - git diff without trailing comma @@ -1587,9 +1851,11 @@ - [20.1](#20.1) **Yup.** + eslint rules: [`semi`](http://eslint.org/docs/rules/semi.html). + ```javascript // bad - (function() { + (function () { const name = 'Skywalker' return name })() @@ -1607,7 +1873,7 @@ })(); ``` - [Read more](http://stackoverflow.com/a/7365214/1712802). + [Read more](http://stackoverflow.com/questions/7365172/semicolon-before-self-invoking-function/7365214%237365214). **[⬆ back to top](#table-of-contents)** @@ -1627,7 +1893,9 @@ const totalScore = String(this.reviewScore); ``` - - [21.3](#21.3) Use `parseInt` for Numbers and always with a radix for type casting. + - [21.3](#21.3) Numbers: Use `Number` for type casting and `parseInt` always with a radix for parsing strings. + + eslint rules: [`radix`](http://eslint.org/docs/rules/radix). ```javascript const inputValue = '4'; @@ -1707,6 +1975,8 @@ - [22.2](#22.2) Use camelCase when naming objects, functions, and instances. + eslint rules: [`camelcase`](http://eslint.org/docs/rules/camelcase.html). + ```javascript // bad const OBJEcttsssss = {}; @@ -1744,6 +2014,8 @@ - [22.4](#22.4) Use a leading underscore `_` when naming private properties. + eslint rules: [`no-underscore-dangle`](http://eslint.org/docs/rules/no-underscore-dangle.html). + ```javascript // bad this.__firstName__ = 'Panda'; @@ -1759,7 +2031,7 @@ // bad function foo() { const self = this; - return function() { + return function () { console.log(self); }; } @@ -1767,7 +2039,7 @@ // bad function foo() { const that = this; - return function() { + return function () { console.log(that); }; } @@ -1781,6 +2053,7 @@ ``` - [22.6](#22.6) If your file exports a single class, your filename should be exactly the name of the class. + ```javascript // file contents class CheckBox { @@ -1888,7 +2161,7 @@ ... - $(this).on('listingUpdated', function(e, listingId) { + $(this).on('listingUpdated', function (e, listingId) { // do something with listingId }); ``` @@ -1901,7 +2174,7 @@ ... - $(this).on('listingUpdated', function(e, data) { + $(this).on('listingUpdated', function (e, data) { // do something with data.listingId }); ``` @@ -1976,7 +2249,7 @@ ## ECMAScript 5 Compatibility - - [26.1](#26.1) Refer to [Kangax](https://twitter.com/kangax/)'s ES5 [compatibility table](http://kangax.github.com/es5-compat-table/). + - [26.1](#26.1) Refer to [Kangax](https://twitter.com/kangax/)'s ES5 [compatibility table](http://kangax.github.io/es5-compat-table/). **[⬆ back to top](#table-of-contents)** @@ -2002,20 +2275,28 @@ ## Testing - - [28.1](#28.1) **Yup.** + - [28.1](#28.1) **Yup.** ```javascript - function() { + function () { return true; } ``` + - [28.2](#28.2) **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. + - 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. + **[⬆ back to top](#table-of-contents)** ## Performance - - [On Layout & Web Performance](http://kellegous.com/j/2013/01/26/layout-performance/) + - [On Layout & Web Performance](http://www.kellegous.com/j/2013/01/26/layout-performance/) - [String vs Array Concat](http://jsperf.com/string-vs-array-concat/2) - [Try/Catch Cost In a Loop](http://jsperf.com/try-catch-in-loop-cost) - [Bang Function](http://jsperf.com/bang-function) @@ -2044,18 +2325,18 @@ - Code Style Linters + [ESlint](http://eslint.org/) - [Airbnb Style .eslintrc](https://github.com/airbnb/javascript/blob/master/linters/.eslintrc) - + [JSHint](http://www.jshint.com/) - [Airbnb Style .jshintrc](https://github.com/airbnb/javascript/blob/master/linters/jshintrc) + + [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) **Other Style Guides** - [Google JavaScript Style Guide](http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml) - - [jQuery Core Style Guidelines](http://docs.jquery.com/JQuery_Core_Style_Guidelines) - - [Principles of Writing Consistent, Idiomatic JavaScript](https://github.com/rwldrn/idiomatic.js/) + - [jQuery Core Style Guidelines](http://contribute.jquery.org/style-guide/js/) + - [Principles of Writing Consistent, Idiomatic JavaScript](https://github.com/rwaldron/idiomatic.js) **Other Styles** - - [Naming this in nested functions](https://gist.github.com/4135065) - Christian Johansen + - [Naming this in nested functions](https://gist.github.com/cjohansen/4135065) - Christian Johansen - [Conditional Callbacks](https://github.com/airbnb/javascript/issues/52) - Ross Allen - [Popular JavaScript Coding Conventions on Github](http://sideeffect.kr/popularconvention/#javascript) - JeongHoon Byun - [Multiple var statements in JavaScript, not superfluous](http://benalman.com/news/2012/05/multiple-var-statements-javascript/) - Ben Alman @@ -2082,7 +2363,7 @@ - [Human JavaScript](http://humanjavascript.com/) - Henrik Joreteg - [Superhero.js](http://superherojs.com/) - Kim Joar Bekkelund, Mads Mobæk, & Olav Bjorkoy - [JSBooks](http://jsbooks.revolunet.com/) - Julien Bouquillon - - [Third Party JavaScript](http://manning.com/vinegar/) - Ben Vinegar and Anton Kovalyov + - [Third Party JavaScript](https://www.manning.com/books/third-party-javascript) - Ben Vinegar and Anton Kovalyov - [Effective JavaScript: 68 Specific Ways to Harness the Power of JavaScript](http://amzn.com/0321812182) - David Herman - [Eloquent JavaScript](http://eloquentjavascript.net/) - Marijn Haverbeke - [You Don't Know JS: ES6 & Beyond](http://shop.oreilly.com/product/0636920033769.do) - Kyle Simpson @@ -2092,18 +2373,18 @@ - [DailyJS](http://dailyjs.com/) - [JavaScript Weekly](http://javascriptweekly.com/) - [JavaScript, JavaScript...](http://javascriptweblog.wordpress.com/) - - [Bocoup Weblog](http://weblog.bocoup.com/) + - [Bocoup Weblog](https://bocoup.com/weblog) - [Adequately Good](http://www.adequatelygood.com/) - - [NCZOnline](http://www.nczonline.net/) + - [NCZOnline](https://www.nczonline.net/) - [Perfection Kills](http://perfectionkills.com/) - [Ben Alman](http://benalman.com/) - [Dmitry Baranovskiy](http://dmitry.baranovskiy.com/) - [Dustin Diaz](http://dustindiaz.com/) - - [nettuts](http://net.tutsplus.com/?s=javascript) + - [nettuts](http://code.tutsplus.com/?s=javascript) **Podcasts** - - [JavaScript Jabber](http://devchat.tv/js-jabber/) + - [JavaScript Jabber](https://devchat.tv/js-jabber/) **[⬆ back to top](#table-of-contents)** @@ -2118,12 +2399,15 @@ - **Apartmint**: [apartmint/javascript](https://github.com/apartmint/javascript) - **Avalara**: [avalara/javascript](https://github.com/avalara/javascript) - **Billabong**: [billabong/javascript](https://github.com/billabong/javascript) + - **Bisk**: [bisk/javascript](https://github.com/Bisk/javascript/) - **Blendle**: [blendle/javascript](https://github.com/blendle/javascript) - - **ComparaOnline**: [comparaonline/javascript](https://github.com/comparaonline/javascript) + - **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) - **Digitpaint** [digitpaint/javascript](https://github.com/digitpaint/javascript) + - **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) - **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) @@ -2131,13 +2415,16 @@ - **General Electric**: [GeneralElectric/javascript](https://github.com/GeneralElectric/javascript) - **GoodData**: [gooddata/gdc-js-style](https://github.com/gooddata/gdc-js-style) - **Grooveshark**: [grooveshark/javascript](https://github.com/grooveshark/javascript) - - **How About We**: [howaboutwe/javascript](https://github.com/howaboutwe/javascript) + - **How About We**: [howaboutwe/javascript](https://github.com/howaboutwe/javascript-style-guide) - **Huballin**: [huballin/javascript](https://github.com/huballin/javascript) + - **HubSpot**: [HubSpot/javascript](https://github.com/HubSpot/javascript) + - **Hyper**: [hyperoslo/javascript-playbook](https://github.com/hyperoslo/javascript-playbook/blob/master/style.md) - **InfoJobs**: [InfoJobs/JavaScript-Style-Guide](https://github.com/InfoJobs/JavaScript-Style-Guide) - **Intent Media**: [intentmedia/javascript](https://github.com/intentmedia/javascript) - **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) - - **Kinetica Solutions**: [kinetica/javascript](https://github.com/kinetica/javascript) + - **Kinetica Solutions**: [kinetica/javascript](https://github.com/kinetica/Javascript-style-guide) - **Mighty Spring**: [mightyspring/javascript](https://github.com/mightyspring/javascript) - **MinnPost**: [MinnPost/javascript](https://github.com/MinnPost/javascript) - **MitocGroup**: [MitocGroup/javascript](https://github.com/MitocGroup/javascript) @@ -2148,15 +2435,17 @@ - **National Park Service**: [nationalparkservice/javascript](https://github.com/nationalparkservice/javascript) - **Nimbl3**: [nimbl3/javascript](https://github.com/nimbl3/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) - **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/react/blob/master/CONTRIBUTING.md#style-guide](https://github.com/facebook/react/blob/master/CONTRIBUTING.md#style-guide) - **REI**: [reidev/js-style-guide](https://github.com/reidev/js-style-guide) - **Ripple**: [ripple/javascript-style-guide](https://github.com/ripple/javascript-style-guide) - **SeekingAlpha**: [seekingalpha/javascript-style-guide](https://github.com/seekingalpha/javascript-style-guide) - **Shutterfly**: [shutterfly/javascript](https://github.com/shutterfly/javascript) - **Springload**: [springload/javascript](https://github.com/springload/javascript) - - **StudentSphere**: [studentsphere/javascript](https://github.com/studentsphere/javascript) + - **StudentSphere**: [studentsphere/javascript](https://github.com/studentsphere/guide-javascript) - **Target**: [target/javascript](https://github.com/target/javascript) - **TheLadders**: [TheLadders/javascript](https://github.com/TheLadders/javascript) - **T4R Technology**: [T4R-Technology/javascript](https://github.com/T4R-Technology/javascript) diff --git a/linters/jshintrc b/linters/.jshintrc similarity index 95% rename from linters/jshintrc rename to linters/.jshintrc index 141067fd23..4d3468cb21 100644 --- a/linters/jshintrc +++ b/linters/.jshintrc @@ -34,8 +34,8 @@ // Prohibit use of a variable before it is defined. "latedef": true, - // Enforce line length to 80 characters - "maxlen": 80, + // Enforce line length to 100 characters + "maxlen": 100, // Require capitalized names for constructor functions. "newcap": true, diff --git a/packages/eslint-config-airbnb/.eslintrc b/packages/eslint-config-airbnb/.eslintrc index cbf450f01f..4b3b1fa429 100644 --- a/packages/eslint-config-airbnb/.eslintrc +++ b/packages/eslint-config-airbnb/.eslintrc @@ -3,9 +3,6 @@ "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, - // disabled because I find it tedious to write tests while following this - // rule - "no-shadow": 0 + "comma-dangle": 0 } } diff --git a/packages/eslint-config-airbnb/CHANGELOG.md b/packages/eslint-config-airbnb/CHANGELOG.md new file mode 100644 index 0000000000..0cbe0e1357 --- /dev/null +++ b/packages/eslint-config-airbnb/CHANGELOG.md @@ -0,0 +1,88 @@ +3.1.0 / 2016-01-07 +================== + - [minor] Allow multiple stateless components in a single file + +3.0.2 / 2016-01-06 +================== + - [fix] Ignore URLs in `max-len` (#664) + +3.0.1 / 2016-01-06 +================== + - [fix] because we use babel, keywords should not be quoted + +3.0.0 / 2016-01-04 +================== + - [breaking] enable `quote-props` rule (#632) + - [breaking] Define a max line length of 100 characters (#639) + - [breaking] [react] Minor cleanup for the React styleguide, add `react/jsx-no-bind` (#619) + - [breaking] update best-practices config to prevent parameter object manipulation (#627) + - [minor] Enable react/no-is-mounted rule (#635, #633) + - [minor] Sort react/prefer-es6-class alphabetically (#634) + - [minor] enable react/prefer-es6-class rule + - Permit strict mode in "legacy" config + - [react] add missing rules from eslint-plugin-react (enforcing where necessary) (#581) + - [dev deps] update `eslint-plugin-react` + +2.1.1 / 2015-12-15 +================== + - [fix] Remove deprecated react/jsx-quotes (#622) + +2.1.0 / 2015-12-15 +================== + - [fix] use `require.resolve` to allow nested `extend`s (#582) + - [new] enable `object-shorthand` rule (#621) + - [new] enable `arrow-spacing` rule (#517) + - [docs] flesh out react rule defaults (#618) + +2.0.0 / 2015-12-03 +================== + - [breaking] `space-before-function-paren`: require function spacing: `function (` (#605) + - [breaking] `indent`: Fix switch statement indentation rule (#606) + - [breaking] `array-bracket-spacing`, `computed-property-spacing`: disallow spacing inside brackets (#594) + - [breaking] `object-curly-spacing`: require padding inside curly braces (#594) + - [breaking] `space-in-parens`: disallow spaces in parens (#594) + +1.0.2 / 2015-11-25 +================== + - [breaking] `no-multiple-empty-lines`: only allow 1 blank line at EOF (#578) + - [new] `restParams`: enable rest params (#592) + +1.0.1 / 2015-11-25 +================== + - *erroneous publish* + +1.0.0 / 2015-11-08 +================== + - require `eslint` `v1.0.0` or higher + - remove `babel-eslint` dependency + +0.1.1 / 2015-11-05 +================== + - remove id-length rule (#569) + - enable `no-mixed-spaces-and-tabs` (#539) + - enable `no-const-assign` (#560) + - enable `space-before-keywords` (#554) + +0.1.0 / 2015-11-05 +================== + - switch to modular rules files courtesy the [eslint-config-default][ecd] project and [@taion][taion]. [PR][pr-modular] + - export `eslint-config-airbnb/legacy` for ES5-only users. `eslint-config-airbnb/legacy` does not require the `babel-eslint` parser. [PR][pr-legacy] + +0.0.9 / 2015-09-24 +================== +- add rule `no-undef` +- add rule `id-length` + +0.0.8 / 2015-08-21 +================== + - now has a changelog + - now is modular (see instructions above for with react and without react versions) + +0.0.7 / 2015-07-30 +================== + - TODO: fill in + +[ecd]: https://github.com/walmartlabs/eslint-config-defaults +[taion]: https://github.com/taion +[pr-modular]: https://github.com/airbnb/javascript/pull/526 +[pr-legacy]: https://github.com/airbnb/javascript/pull/527 diff --git a/packages/eslint-config-airbnb/README.md b/packages/eslint-config-airbnb/README.md index ac184f1adb..26c795f3f8 100644 --- a/packages/eslint-config-airbnb/README.md +++ b/packages/eslint-config-airbnb/README.md @@ -1,19 +1,35 @@ # eslint-config-airbnb +[![npm version](https://badge.fury.io/js/eslint-config-airbnb.svg)](http://badge.fury.io/js/eslint-config-airbnb) + This package provides Airbnb's .eslintrc as an extensible shared config. ## Usage -### With React Style +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` and `eslint-plugin-react`. -1. `npm install --save-dev eslint-config-airbnb babel-eslint eslint-plugin-react` +1. `npm install --save-dev eslint-config-airbnb eslint-plugin-react eslint` 2. add `"extends": "airbnb"` to your .eslintrc -### Without React Style +### eslint-config-airbnb/base -1. `npm install --save-dev eslint-config-airbnb babel-eslint ` +Lints ES6+ but does not lint React. Requires `eslint`. + +1. `npm install --save-dev eslint-config-airbnb eslint` 2. add `"extends": "airbnb/base"` to your .eslintrc +### eslint-config-airbnb/legacy + +Lints ES5 and below. Only requires `eslint`. + +1. `npm install --save-dev eslint-config-airbnb eslint` +2. add `"extends": "airbnb/legacy"` to your .eslintrc + 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) for more information. @@ -27,9 +43,3 @@ programming to structure our README as test cases for our .eslintrc? You can run tests with `npm test`. You can make sure this module lints with itself using `npm run lint`. - -## Changelog - -### 0.0.8 - - now has a changelog - - now is modular (see instructions above for with react and without react versions) diff --git a/packages/eslint-config-airbnb/base.js b/packages/eslint-config-airbnb/base.js new file mode 100644 index 0000000000..093bd541be --- /dev/null +++ b/packages/eslint-config-airbnb/base.js @@ -0,0 +1,7 @@ +module.exports = { + 'extends': [ + 'eslint-config-airbnb/legacy', + 'eslint-config-airbnb/rules/es6', + ].map(require.resolve), + 'rules': {} +}; diff --git a/packages/eslint-config-airbnb/base/index.js b/packages/eslint-config-airbnb/base/index.js deleted file mode 100644 index dad7ce9e01..0000000000 --- a/packages/eslint-config-airbnb/base/index.js +++ /dev/null @@ -1,178 +0,0 @@ -module.exports = { - 'parser': 'babel-eslint', // https://github.com/babel/babel-eslint - 'env': { // http://eslint.org/docs/user-guide/configuring.html#specifying-environments - 'browser': true, // browser global variables - 'node': true // Node.js global variables and Node.js-specific rules - }, - 'ecmaFeatures': { - 'arrowFunctions': true, - 'blockBindings': true, - 'classes': true, - 'defaultParams': true, - 'destructuring': true, - 'forOf': true, - 'generators': false, - 'modules': true, - 'objectLiteralComputedProperties': true, - 'objectLiteralDuplicateProperties': false, - 'objectLiteralShorthandMethods': true, - 'objectLiteralShorthandProperties': true, - 'spread': true, - 'superInFunctions': true, - 'templateStrings': true, - 'jsx': true - }, - 'rules': { - /** - * Strict mode - */ - // babel inserts 'use strict'; for us - 'strict': [2, 'never'], // http://eslint.org/docs/rules/strict - - /** - * ES6 - */ - 'no-var': 2, // http://eslint.org/docs/rules/no-var - 'prefer-const': 2, // http://eslint.org/docs/rules/prefer-const - - /** - * Variables - */ - 'no-shadow': 2, // http://eslint.org/docs/rules/no-shadow - 'no-shadow-restricted-names': 2, // http://eslint.org/docs/rules/no-shadow-restricted-names - 'no-undef': 2, // http://eslint.org/docs/rules/no-undef - 'no-unused-vars': [2, { // http://eslint.org/docs/rules/no-unused-vars - 'vars': 'local', - 'args': 'after-used' - }], - 'no-use-before-define': 2, // http://eslint.org/docs/rules/no-use-before-define - - /** - * Possible errors - */ - 'comma-dangle': [2, 'always-multiline'], // http://eslint.org/docs/rules/comma-dangle - 'no-cond-assign': [2, 'always'], // http://eslint.org/docs/rules/no-cond-assign - 'no-console': 1, // http://eslint.org/docs/rules/no-console - 'no-debugger': 1, // http://eslint.org/docs/rules/no-debugger - 'no-alert': 1, // http://eslint.org/docs/rules/no-alert - 'no-constant-condition': 1, // http://eslint.org/docs/rules/no-constant-condition - 'no-dupe-keys': 2, // http://eslint.org/docs/rules/no-dupe-keys - 'no-duplicate-case': 2, // http://eslint.org/docs/rules/no-duplicate-case - 'no-empty': 2, // http://eslint.org/docs/rules/no-empty - 'no-ex-assign': 2, // http://eslint.org/docs/rules/no-ex-assign - 'no-extra-boolean-cast': 0, // http://eslint.org/docs/rules/no-extra-boolean-cast - 'no-extra-semi': 2, // http://eslint.org/docs/rules/no-extra-semi - 'no-func-assign': 2, // http://eslint.org/docs/rules/no-func-assign - 'no-inner-declarations': 2, // http://eslint.org/docs/rules/no-inner-declarations - 'no-invalid-regexp': 2, // http://eslint.org/docs/rules/no-invalid-regexp - 'no-irregular-whitespace': 2, // http://eslint.org/docs/rules/no-irregular-whitespace - 'no-obj-calls': 2, // http://eslint.org/docs/rules/no-obj-calls - 'no-sparse-arrays': 2, // http://eslint.org/docs/rules/no-sparse-arrays - 'no-unreachable': 2, // http://eslint.org/docs/rules/no-unreachable - 'use-isnan': 2, // http://eslint.org/docs/rules/use-isnan - 'block-scoped-var': 2, // http://eslint.org/docs/rules/block-scoped-var - - /** - * Best practices - */ - 'consistent-return': 2, // http://eslint.org/docs/rules/consistent-return - 'curly': [2, 'multi-line'], // http://eslint.org/docs/rules/curly - 'default-case': 2, // http://eslint.org/docs/rules/default-case - 'dot-notation': [2, { // http://eslint.org/docs/rules/dot-notation - 'allowKeywords': true - }], - 'eqeqeq': 2, // http://eslint.org/docs/rules/eqeqeq - 'guard-for-in': 2, // http://eslint.org/docs/rules/guard-for-in - 'no-caller': 2, // http://eslint.org/docs/rules/no-caller - 'no-else-return': 2, // http://eslint.org/docs/rules/no-else-return - 'no-eq-null': 2, // http://eslint.org/docs/rules/no-eq-null - 'no-eval': 2, // http://eslint.org/docs/rules/no-eval - 'no-extend-native': 2, // http://eslint.org/docs/rules/no-extend-native - 'no-extra-bind': 2, // http://eslint.org/docs/rules/no-extra-bind - 'no-fallthrough': 2, // http://eslint.org/docs/rules/no-fallthrough - 'no-floating-decimal': 2, // http://eslint.org/docs/rules/no-floating-decimal - 'no-implied-eval': 2, // http://eslint.org/docs/rules/no-implied-eval - 'no-lone-blocks': 2, // http://eslint.org/docs/rules/no-lone-blocks - 'no-loop-func': 2, // http://eslint.org/docs/rules/no-loop-func - 'no-multi-str': 2, // http://eslint.org/docs/rules/no-multi-str - 'no-native-reassign': 2, // http://eslint.org/docs/rules/no-native-reassign - 'no-new': 2, // http://eslint.org/docs/rules/no-new - 'no-new-func': 2, // http://eslint.org/docs/rules/no-new-func - 'no-new-wrappers': 2, // http://eslint.org/docs/rules/no-new-wrappers - 'no-octal': 2, // http://eslint.org/docs/rules/no-octal - 'no-octal-escape': 2, // http://eslint.org/docs/rules/no-octal-escape - 'no-param-reassign': 2, // http://eslint.org/docs/rules/no-param-reassign - 'no-proto': 2, // http://eslint.org/docs/rules/no-proto - 'no-redeclare': 2, // http://eslint.org/docs/rules/no-redeclare - 'no-return-assign': 2, // http://eslint.org/docs/rules/no-return-assign - 'no-script-url': 2, // http://eslint.org/docs/rules/no-script-url - 'no-self-compare': 2, // http://eslint.org/docs/rules/no-self-compare - 'no-sequences': 2, // http://eslint.org/docs/rules/no-sequences - 'no-throw-literal': 2, // http://eslint.org/docs/rules/no-throw-literal - 'no-with': 2, // http://eslint.org/docs/rules/no-with - 'radix': 2, // http://eslint.org/docs/rules/radix - 'vars-on-top': 2, // http://eslint.org/docs/rules/vars-on-top - 'wrap-iife': [2, 'any'], // http://eslint.org/docs/rules/wrap-iife - 'yoda': 2, // http://eslint.org/docs/rules/yoda - - /** - * Style - */ - 'indent': [2, 2], // http://eslint.org/docs/rules/indent - 'brace-style': [ - 2, // http://eslint.org/docs/rules/brace-style - '1tbs', { - 'allowSingleLine': true - } - ], - 'quotes': [ - 2, 'single', 'avoid-escape' // http://eslint.org/docs/rules/quotes - ], - 'id-length': [2, { // http://eslint.org/docs/rules/id-length - 'min': 2, - 'properties': 'never' - }], - 'camelcase': [2, { // http://eslint.org/docs/rules/camelcase - 'properties': 'never' - }], - 'comma-spacing': [2, { // http://eslint.org/docs/rules/comma-spacing - 'before': false, - 'after': true - }], - 'comma-style': [2, 'last'], // http://eslint.org/docs/rules/comma-style - 'eol-last': 2, // http://eslint.org/docs/rules/eol-last - 'func-names': 1, // http://eslint.org/docs/rules/func-names - 'key-spacing': [2, { // http://eslint.org/docs/rules/key-spacing - 'beforeColon': false, - 'afterColon': true - }], - 'new-cap': [2, { // http://eslint.org/docs/rules/new-cap - 'newIsCap': true - }], - 'no-multiple-empty-lines': [2, { // http://eslint.org/docs/rules/no-multiple-empty-lines - 'max': 2 - }], - 'no-nested-ternary': 2, // http://eslint.org/docs/rules/no-nested-ternary - 'no-new-object': 2, // http://eslint.org/docs/rules/no-new-object - 'no-spaced-func': 2, // http://eslint.org/docs/rules/no-spaced-func - 'no-trailing-spaces': 2, // http://eslint.org/docs/rules/no-trailing-spaces - 'no-extra-parens': [2, 'functions'], // http://eslint.org/docs/rules/no-extra-parens - 'no-underscore-dangle': 0, // http://eslint.org/docs/rules/no-underscore-dangle - 'one-var': [2, 'never'], // http://eslint.org/docs/rules/one-var - 'padded-blocks': [2, 'never'], // http://eslint.org/docs/rules/padded-blocks - 'semi': [2, 'always'], // http://eslint.org/docs/rules/semi - 'semi-spacing': [2, { // http://eslint.org/docs/rules/semi-spacing - 'before': false, - 'after': true - }], - 'space-after-keywords': 2, // http://eslint.org/docs/rules/space-after-keywords - 'space-before-blocks': 2, // http://eslint.org/docs/rules/space-before-blocks - 'space-before-function-paren': [2, 'never'], // http://eslint.org/docs/rules/space-before-function-paren - 'space-infix-ops': 2, // http://eslint.org/docs/rules/space-infix-ops - 'space-return-throw-case': 2, // http://eslint.org/docs/rules/space-return-throw-case - 'spaced-comment': [2, 'always', {// http://eslint.org/docs/rules/spaced-comment - 'exceptions': ['-', '+'], - 'markers': ['=', '!'] // space here to support sprockets directives - }], - } -}; diff --git a/packages/eslint-config-airbnb/index.js b/packages/eslint-config-airbnb/index.js index 3cda9b0f9c..5a5c535e74 100644 --- a/packages/eslint-config-airbnb/index.js +++ b/packages/eslint-config-airbnb/index.js @@ -1,13 +1,8 @@ -const reactRules = require('./react'); -const base = require('./base'); - -// clone this so we aren't mutating a module -const eslintrc = JSON.parse(JSON.stringify(base)); - -// manually merge in React rules -eslintrc.plugins = reactRules.plugins; -Object.keys(reactRules.rules).forEach(function assignRule(ruleId) { - eslintrc.rules[ruleId] = reactRules.rules[ruleId]; -}); - -module.exports = eslintrc; +module.exports = { + 'extends': [ + 'eslint-config-airbnb/base', + 'eslint-config-airbnb/rules/strict', + 'eslint-config-airbnb/rules/react', + ].map(require.resolve), + rules: {} +}; diff --git a/packages/eslint-config-airbnb/legacy.js b/packages/eslint-config-airbnb/legacy.js new file mode 100644 index 0000000000..257b9dd868 --- /dev/null +++ b/packages/eslint-config-airbnb/legacy.js @@ -0,0 +1,20 @@ +module.exports = { + 'extends': [ + 'eslint-config-airbnb/rules/best-practices', + 'eslint-config-airbnb/rules/errors', + 'eslint-config-airbnb/rules/legacy', + 'eslint-config-airbnb/rules/node', + 'eslint-config-airbnb/rules/style', + 'eslint-config-airbnb/rules/variables' + ].map(require.resolve), + 'env': { + 'browser': true, + 'node': true, + 'amd': false, + 'mocha': false, + 'jasmine': false + }, + 'ecmaFeatures': {}, + 'globals': {}, + 'rules': {} +}; diff --git a/packages/eslint-config-airbnb/package.json b/packages/eslint-config-airbnb/package.json index 050567251f..016b02098a 100644 --- a/packages/eslint-config-airbnb/package.json +++ b/packages/eslint-config-airbnb/package.json @@ -1,11 +1,11 @@ { "name": "eslint-config-airbnb", - "version": "0.0.8", + "version": "3.1.0", "description": "Airbnb's ESLint config, following our styleguide", "main": "index.js", "scripts": { - "lint": "./node_modules/.bin/eslint .", - "test": "./node_modules/.bin/babel-tape-runner ./test/test-*.js" + "lint": "eslint .", + "test": "babel-tape-runner ./test/test-*.js" }, "repository": { "type": "git", @@ -20,17 +20,30 @@ "styleguide" ], "author": "Jake Teton-Landis (https://twitter.com/@jitl)", + "contributors": [ + { + "name": "Jake Teton-Landis", + "url": "https://twitter.com/jitl" + }, + { + "name": "Jordan Harband", + "email": "ljharb@gmail.com", + "url": "http://ljharb.codes/" + } + ], "license": "MIT", "bugs": { "url": "https://github.com/airbnb/javascript/issues" }, "homepage": "https://github.com/airbnb/javascript", "devDependencies": { - "babel-eslint": "4.0.10", "babel-tape-runner": "1.2.0", - "eslint": "1.1.0", - "eslint-plugin-react": "3.2.3", - "react": "0.13.3", - "tape": "4.2.0" + "eslint": "^1.10.3", + "eslint-plugin-react": "^3.12.0", + "react": "^0.13.3", + "tape": "^4.2.2" + }, + "peerDependencies": { + "eslint": ">=1.0.0" } } diff --git a/packages/eslint-config-airbnb/react.js b/packages/eslint-config-airbnb/react.js deleted file mode 100644 index a0ab17f396..0000000000 --- a/packages/eslint-config-airbnb/react.js +++ /dev/null @@ -1,36 +0,0 @@ -module.exports = { - 'plugins': [ - 'react' // https://github.com/yannickcr/eslint-plugin-react - ], - rules: { - /** - * JSX style - */ - 'react/display-name': 0, // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/display-name.md - 'react/jsx-boolean-value': 2, // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-boolean-value.md - 'react/jsx-quotes': [2, 'double'], // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-quotes.md - 'react/jsx-no-undef': 2, // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-undef.md - 'react/jsx-sort-props': 0, // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-sort-props.md - 'react/jsx-sort-prop-types': 0, // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-sort-prop-types.md - 'react/jsx-uses-react': 2, // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-uses-react.md - 'react/jsx-uses-vars': 2, // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-uses-vars.md - 'react/no-did-mount-set-state': [2, 'allow-in-func'], // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-did-mount-set-state.md - 'react/no-did-update-set-state': 2, // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-did-update-set-state.md - 'react/no-multi-comp': 2, // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-multi-comp.md - 'react/no-unknown-property': 2, // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-unknown-property.md - 'react/prop-types': 2, // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/prop-types.md - 'react/react-in-jsx-scope': 2, // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/react-in-jsx-scope.md - 'react/self-closing-comp': 2, // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/self-closing-comp.md - 'react/wrap-multilines': 2, // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/wrap-multilines.md - 'react/sort-comp': [2, { // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/sort-comp.md - 'order': [ - 'lifecycle', - '/^on.+$/', - '/^(get|set)(?!(InitialState$|DefaultProps$|ChildContext$)).+$/', - 'everything-else', - '/^render.+$/', - 'render' - ] - }] - } -}; diff --git a/packages/eslint-config-airbnb/rules/best-practices.js b/packages/eslint-config-airbnb/rules/best-practices.js new file mode 100644 index 0000000000..82e5f9e289 --- /dev/null +++ b/packages/eslint-config-airbnb/rules/best-practices.js @@ -0,0 +1,115 @@ +module.exports = { + 'rules': { + // Enforces getter/setter pairs in objects + 'accessor-pairs': 0, + // treat var statements as if they were block scoped + 'block-scoped-var': 2, + // specify the maximum cyclomatic complexity allowed in a program + 'complexity': [0, 11], + // require return statements to either always or never specify values + 'consistent-return': 2, + // specify curly brace conventions for all control statements + 'curly': [2, 'multi-line'], + // require default case in switch statements + 'default-case': 2, + // encourages use of dot notation whenever possible + 'dot-notation': [2, { 'allowKeywords': true}], + // enforces consistent newlines before or after dots + 'dot-location': 0, + // require the use of === and !== + 'eqeqeq': 2, + // make sure for-in loops have an if statement + 'guard-for-in': 2, + // disallow the use of alert, confirm, and prompt + 'no-alert': 1, + // disallow use of arguments.caller or arguments.callee + 'no-caller': 2, + // disallow division operators explicitly at beginning of regular expression + 'no-div-regex': 0, + // disallow else after a return in an if + 'no-else-return': 2, + // disallow use of labels for anything other then loops and switches + 'no-empty-label': 2, + // disallow comparisons to null without a type-checking operator + 'no-eq-null': 0, + // disallow use of eval() + 'no-eval': 2, + // disallow adding to native types + 'no-extend-native': 2, + // disallow unnecessary function binding + 'no-extra-bind': 2, + // disallow fallthrough of case statements + 'no-fallthrough': 2, + // disallow the use of leading or trailing decimal points in numeric literals + 'no-floating-decimal': 2, + // disallow the type conversions with shorter notations + 'no-implicit-coercion': 0, + // disallow use of eval()-like methods + 'no-implied-eval': 2, + // disallow this keywords outside of classes or class-like objects + 'no-invalid-this': 0, + // disallow usage of __iterator__ property + 'no-iterator': 2, + // disallow use of labeled statements + 'no-labels': 2, + // disallow unnecessary nested blocks + 'no-lone-blocks': 2, + // disallow creation of functions within loops + 'no-loop-func': 2, + // disallow use of multiple spaces + 'no-multi-spaces': 2, + // disallow use of multiline strings + 'no-multi-str': 2, + // disallow reassignments of native objects + 'no-native-reassign': 2, + // disallow use of new operator when not part of the assignment or comparison + 'no-new': 2, + // disallow use of new operator for Function object + 'no-new-func': 2, + // disallows creating new instances of String,Number, and Boolean + 'no-new-wrappers': 2, + // disallow use of (old style) octal literals + 'no-octal': 2, + // disallow use of octal escape sequences in string literals, such as + // var foo = 'Copyright \251'; + 'no-octal-escape': 2, + // disallow reassignment of function parameters + // disallow parameter object manipulation + // rule: http://eslint.org/docs/rules/no-param-reassign.html + 'no-param-reassign': [2, { 'props': true }], + // disallow use of process.env + 'no-process-env': 0, + // disallow usage of __proto__ property + 'no-proto': 2, + // disallow declaring the same variable more then once + 'no-redeclare': 2, + // disallow use of assignment in return statement + 'no-return-assign': 2, + // disallow use of `javascript:` urls. + 'no-script-url': 2, + // disallow comparisons where both sides are exactly the same + 'no-self-compare': 2, + // disallow use of comma operator + 'no-sequences': 2, + // restrict what can be thrown as an exception + 'no-throw-literal': 2, + // disallow usage of expressions in statement position + 'no-unused-expressions': 2, + // disallow unnecessary .call() and .apply() + 'no-useless-call': 0, + // disallow use of void operator + 'no-void': 0, + // disallow usage of configurable warning terms in comments: e.g. todo + 'no-warning-comments': [0, { 'terms': ['todo', 'fixme', 'xxx'], 'location': 'start' }], + // disallow use of the with statement + 'no-with': 2, + // require use of the second argument for parseInt() + 'radix': 2, + // requires to declare all vars on top of their containing scope + 'vars-on-top': 2, + // require immediate function invocation to be wrapped in parentheses + 'wrap-iife': [2, 'any'], + // require or disallow Yoda conditions + 'yoda': 2 + } +}; diff --git a/packages/eslint-config-airbnb/rules/errors.js b/packages/eslint-config-airbnb/rules/errors.js new file mode 100644 index 0000000000..ec1b1aab0e --- /dev/null +++ b/packages/eslint-config-airbnb/rules/errors.js @@ -0,0 +1,60 @@ +module.exports = { + 'rules': { + // disallow trailing commas in object literals + 'comma-dangle': [2, 'always-multiline'], + // disallow assignment in conditional expressions + 'no-cond-assign': [2, 'always'], + // disallow use of console + 'no-console': 1, + // disallow use of constant expressions in conditions + 'no-constant-condition': 1, + // disallow control characters in regular expressions + 'no-control-regex': 2, + // disallow use of debugger + 'no-debugger': 1, + // disallow duplicate arguments in functions + 'no-dupe-args': 2, + // disallow duplicate keys when creating object literals + 'no-dupe-keys': 2, + // disallow a duplicate case label. + 'no-duplicate-case': 2, + // disallow the use of empty character classes in regular expressions + 'no-empty-character-class': 2, + // disallow empty statements + 'no-empty': 2, + // disallow assigning to the exception in a catch block + 'no-ex-assign': 2, + // disallow double-negation boolean casts in a boolean context + 'no-extra-boolean-cast': 0, + // disallow unnecessary parentheses + 'no-extra-parens': [2, 'functions'], + // disallow unnecessary semicolons + 'no-extra-semi': 2, + // disallow overwriting functions written as function declarations + 'no-func-assign': 2, + // disallow function or variable declarations in nested blocks + 'no-inner-declarations': 2, + // disallow invalid regular expression strings in the RegExp constructor + 'no-invalid-regexp': 2, + // disallow irregular whitespace outside of strings and comments + 'no-irregular-whitespace': 2, + // disallow negation of the left operand of an in expression + 'no-negated-in-lhs': 2, + // disallow the use of object properties of the global object (Math and JSON) as functions + 'no-obj-calls': 2, + // disallow multiple spaces in a regular expression literal + 'no-regex-spaces': 2, + // disallow sparse arrays + 'no-sparse-arrays': 2, + // disallow unreachable statements after a return, throw, continue, or break statement + 'no-unreachable': 2, + // disallow comparisons with the value NaN + 'use-isnan': 2, + // ensure JSDoc comments are valid + 'valid-jsdoc': 0, + // ensure that the results of typeof are compared against a valid string + 'valid-typeof': 2, + // Avoid code that looks like two expressions but is actually one + 'no-unexpected-multiline': 0 + } +}; diff --git a/packages/eslint-config-airbnb/rules/es6.js b/packages/eslint-config-airbnb/rules/es6.js new file mode 100644 index 0000000000..3c277064b6 --- /dev/null +++ b/packages/eslint-config-airbnb/rules/es6.js @@ -0,0 +1,54 @@ +module.exports = { + 'env': { + 'es6': false + }, + 'ecmaFeatures': { + 'arrowFunctions': true, + 'blockBindings': true, + 'classes': true, + 'defaultParams': true, + 'destructuring': true, + 'forOf': true, + 'generators': false, + 'modules': true, + 'objectLiteralComputedProperties': true, + 'objectLiteralDuplicateProperties': false, + 'objectLiteralShorthandMethods': true, + 'objectLiteralShorthandProperties': true, + 'restParams': true, + 'spread': true, + 'superInFunctions': true, + 'templateStrings': true, + 'jsx': true + }, + 'rules': { + // require parens in arrow function arguments + 'arrow-parens': 0, + // require space before/after arrow function's arrow + // https://github.com/eslint/eslint/blob/master/docs/rules/arrow-spacing.md + 'arrow-spacing': [2, { 'before': true, 'after': true }], + // verify super() callings in constructors + 'constructor-super': 0, + // enforce the spacing around the * in generator functions + 'generator-star-spacing': 0, + // disallow modifying variables of class declarations + 'no-class-assign': 0, + // disallow modifying variables that are declared using const + 'no-const-assign': 2, + // disallow to use this/super before super() calling in constructors. + 'no-this-before-super': 0, + // require let or const instead of var + 'no-var': 2, + // require method and property shorthand syntax for object literals + // https://github.com/eslint/eslint/blob/master/docs/rules/object-shorthand.md + 'object-shorthand': [2, 'always'], + // suggest using of const declaration for variables that are never modified after declared + 'prefer-const': 2, + // suggest using the spread operator instead of .apply() + 'prefer-spread': 0, + // suggest using Reflect methods where applicable + 'prefer-reflect': 0, + // disallow generator functions that do not have yield + 'require-yield': 0 + } +}; diff --git a/packages/eslint-config-airbnb/rules/legacy.js b/packages/eslint-config-airbnb/rules/legacy.js new file mode 100644 index 0000000000..e94c774f26 --- /dev/null +++ b/packages/eslint-config-airbnb/rules/legacy.js @@ -0,0 +1,14 @@ +module.exports = { + 'rules': { + // specify the maximum depth that blocks can be nested + 'max-depth': [0, 4], + // limits the number of parameters that can be used in the function declaration. + 'max-params': [0, 3], + // specify the maximum number of statement allowed in a function + 'max-statements': [0, 10], + // disallow use of bitwise operators + 'no-bitwise': 0, + // disallow use of unary operators, ++ and -- + 'no-plusplus': 0 + } +}; diff --git a/packages/eslint-config-airbnb/rules/node.js b/packages/eslint-config-airbnb/rules/node.js new file mode 100644 index 0000000000..16b6f20d45 --- /dev/null +++ b/packages/eslint-config-airbnb/rules/node.js @@ -0,0 +1,23 @@ +module.exports = { + 'env': { + 'node': true + }, + 'rules': { + // enforce return after a callback + 'callback-return': 0, + // enforces error handling in callbacks (node environment) + 'handle-callback-err': 0, + // disallow mixing regular variable and require declarations + 'no-mixed-requires': [0, false], + // disallow use of new operator with the require function + 'no-new-require': 0, + // disallow string concatenation with __dirname and __filename + 'no-path-concat': 0, + // disallow process.exit() + 'no-process-exit': 0, + // restrict usage of specified node modules + 'no-restricted-modules': 0, + // disallow use of synchronous methods (off by default) + 'no-sync': 0 + } +}; diff --git a/packages/eslint-config-airbnb/rules/react.js b/packages/eslint-config-airbnb/rules/react.js new file mode 100644 index 0000000000..c17328ebe8 --- /dev/null +++ b/packages/eslint-config-airbnb/rules/react.js @@ -0,0 +1,139 @@ +module.exports = { + 'plugins': [ + 'react' + ], + 'ecmaFeatures': { + 'jsx': true + }, + // View link below for react rules documentation + // https://github.com/yannickcr/eslint-plugin-react#list-of-supported-rules + 'rules': { + // Prevent missing displayName in a React component definition + // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/display-name.md + 'react/display-name': [0, {'acceptTranspilerName': 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': [0, {'forbid': ['any', 'array', 'object']}], + // 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': [2, 'never'], + // 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': [2, 'line-aligned'], + // 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 + 'react/jsx-curly-spacing': [0, 'never', {'allowMultiline': true}], + // Enforce event handler naming conventions in JSX + // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-handler-names.md + 'react/jsx-handler-names': [0, { + 'eventHandlerPrefix': 'handle', + 'eventHandlerPropPrefix': 'on', + }], + // Validate props indentation in JSX + // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-indent-props.md + 'react/jsx-indent-props': [2, 2], + // Validate JSX has key prop when in array or iterator + // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-key.md + 'react/jsx-key': 0, + // Limit maximum of props on a single line in JSX + // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-max-props-per-line.md + 'react/jsx-max-props-per-line': [0, {'maximum': 1}], + // Prevent usage of .bind() and arrow functions in JSX props + // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-bind.md + 'react/jsx-no-bind': 2, + // Prevent duplicate props in JSX + // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-duplicate-props.md + 'react/jsx-no-duplicate-props': [0, {'ignoreCase': false}], + // 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': 0, + // Disallow undeclared variables in JSX + // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-undef.md + 'react/jsx-no-undef': 2, + // Enforce PascalCase for user-defined JSX components + // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-pascal-case.md + 'react/jsx-pascal-case': 0, + // Enforce propTypes declarations alphabetical sorting + // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-sort-prop-types.md + 'react/jsx-sort-prop-types': [0, { + 'ignoreCase': false, + 'callbacksLast': false, + }], + // Enforce props alphabetical sorting + // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-sort-props.md + 'react/jsx-sort-props': [0, { + 'ignoreCase': false, + 'callbacksLast': false, + }], + // 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': [2, {'pragma': 'React'}], + // Prevent variables used in JSX to be incorrectly marked as unused + // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-uses-vars.md + 'react/jsx-uses-vars': 2, + // Prevent usage of dangerous JSX properties + // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-danger.md + 'react/no-danger': 0, + // Prevent usage of deprecated methods + // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-deprecated.md + 'react/no-deprecated': [1, {"react": "0.14.0"}], + // 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': [2, 'allow-in-func'], + // Prevent usage of setState in componentDidUpdate + // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-did-update-set-state.md + 'react/no-did-update-set-state': [2, 'allow-in-func'], + // Prevent direct mutation of this.state + // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-direct-mutation-state.md + 'react/no-direct-mutation-state': 0, + // Prevent usage of isMounted + // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-is-mounted.md + 'react/no-is-mounted': 2, + // 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': [2, {'ignoreStateless': true}], + // Prevent usage of setState + // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-set-state.md + 'react/no-set-state': 0, + // Prevent using string references + // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-string-refs.md + 'react/no-string-refs': 0, + // Prevent usage of unknown DOM property + // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/no-unknown-property.md + 'react/no-unknown-property': 2, + // Require ES6 class declarations over React.createClass + // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/prefer-es6-class.md + 'react/prefer-es6-class': [2, 'always'], + // 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': [2, {'ignore': [], customValidators: []}], + // Prevent missing React when using JSX + // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/react-in-jsx-scope.md + 'react/react-in-jsx-scope': 2, + // Restrict file extensions that may be required + // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/require-extension.md + 'react/require-extension': [0, {'extensions': ['.jsx']}], + // Prevent extra closing tags for components without children + // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/self-closing-comp.md + 'react/self-closing-comp': 2, + // Enforce component methods order + // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/sort-comp.md + 'react/sort-comp': [2, { + 'order': [ + 'lifecycle', + '/^on.+$/', + '/^(get|set)(?!(InitialState$|DefaultProps$|ChildContext$)).+$/', + 'everything-else', + '/^render.+$/', + 'render' + ] + }], + // Prevent missing parentheses around multilines JSX + // https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/wrap-multilines.md + 'react/wrap-multilines': [2, { + declaration: true, + assignment: true, + return: true + }], + } +}; diff --git a/packages/eslint-config-airbnb/rules/strict.js b/packages/eslint-config-airbnb/rules/strict.js new file mode 100644 index 0000000000..6d32f8ca59 --- /dev/null +++ b/packages/eslint-config-airbnb/rules/strict.js @@ -0,0 +1,6 @@ +module.exports = { + 'rules': { + // babel inserts `'use strict';` for us + 'strict': [2, 'never'] + } +}; diff --git a/packages/eslint-config-airbnb/rules/style.js b/packages/eslint-config-airbnb/rules/style.js new file mode 100644 index 0000000000..ec68b16e5d --- /dev/null +++ b/packages/eslint-config-airbnb/rules/style.js @@ -0,0 +1,125 @@ +module.exports = { + 'rules': { + // enforce spacing inside array brackets + 'array-bracket-spacing': [2, 'never'], + // enforce one true brace style + 'brace-style': [2, '1tbs', {'allowSingleLine': true }], + // require camel case names + 'camelcase': [2, {'properties': 'never'}], + // enforce spacing before and after comma + 'comma-spacing': [2, {'before': false, 'after': true}], + // enforce one true comma style + 'comma-style': [2, 'last'], + // disallow padding inside computed properties + 'computed-property-spacing': [2, 'never'], + // enforces consistent naming when capturing the current execution context + 'consistent-this': 0, + // enforce newline at the end of file, with no multiple empty lines + 'eol-last': 2, + // require function expressions to have a name + 'func-names': 1, + // enforces use of function declarations or expressions + 'func-style': 0, + // this option enforces minimum and maximum identifier lengths (variable names, property names etc.) + 'id-length': 0, + // this option sets a specific tab width for your code + // https://github.com/eslint/eslint/blob/master/docs/rules/indent.md + 'indent': [2, 2, { "SwitchCase": 1, "VariableDeclarator": 1 }], + // specify whether double or single quotes should be used in JSX attributes + // http://eslint.org/docs/rules/jsx-quotes + 'jsx-quotes': [2, 'prefer-double'], + // enforces spacing between keys and values in object literal properties + 'key-spacing': [2, {'beforeColon': false, 'afterColon': true}], + // enforces empty lines around comments + 'lines-around-comment': 0, + // disallow mixed 'LF' and 'CRLF' as linebreaks + 'linebreak-style': 0, + // specify the maximum length of a line in your program + // https://github.com/eslint/eslint/blob/master/docs/rules/max-len.md + 'max-len': [2, 100, 2, { + 'ignoreUrls': true, + 'ignoreComments': false + }], + // specify the maximum depth callbacks can be nested + 'max-nested-callbacks': 0, + // require a capital letter for constructors + 'new-cap': [2, {'newIsCap': true}], + // disallow the omission of parentheses when invoking a constructor with no arguments + 'new-parens': 0, + // allow/disallow an empty newline after var statement + 'newline-after-var': 0, + // disallow use of the Array constructor + 'no-array-constructor': 0, + // disallow use of the continue statement + 'no-continue': 0, + // disallow comments inline after code + 'no-inline-comments': 0, + // disallow if as the only statement in an else block + 'no-lonely-if': 0, + // disallow mixed spaces and tabs for indentation + 'no-mixed-spaces-and-tabs': 2, + // disallow multiple empty lines and only one newline at the end + 'no-multiple-empty-lines': [2, {'max': 2, 'maxEOF': 1}], + // disallow nested ternary expressions + 'no-nested-ternary': 2, + // disallow use of the Object constructor + 'no-new-object': 2, + // disallow space between function identifier and application + 'no-spaced-func': 2, + // disallow the use of ternary operators + 'no-ternary': 0, + // disallow trailing whitespace at the end of lines + 'no-trailing-spaces': 2, + // disallow dangling underscores in identifiers + 'no-underscore-dangle': 0, + // disallow the use of Boolean literals in conditional expressions + 'no-unneeded-ternary': 0, + // require padding inside curly braces + 'object-curly-spacing': [2, 'always'], + // allow just one var statement per function + 'one-var': [2, 'never'], + // require assignment operator shorthand where possible or prohibit it entirely + 'operator-assignment': 0, + // enforce operators to be placed before or after line breaks + 'operator-linebreak': 0, + // enforce padding within blocks + 'padded-blocks': [2, 'never'], + // require quotes around object literal property names + // http://eslint.org/docs/rules/quote-props.html + 'quote-props': [2, 'as-needed', { 'keywords': false, 'unnecessary': true, 'numbers': false }], + // specify whether double or single quotes should be used + 'quotes': [2, 'single', 'avoid-escape'], + // require identifiers to match the provided regular expression + 'id-match': 0, + // enforce spacing before and after semicolons + 'semi-spacing': [2, {'before': false, 'after': true}], + // require or disallow use of semicolons instead of ASI + 'semi': [2, 'always'], + // sort variables within the same declaration block + 'sort-vars': 0, + // require a space before certain keywords + 'space-before-keywords': [2, 'always'], + // require a space after certain keywords + 'space-after-keywords': [2, 'always'], + // require or disallow space before blocks + 'space-before-blocks': 2, + // require or disallow space before function opening parenthesis + // https://github.com/eslint/eslint/blob/master/docs/rules/space-before-function-paren.md + 'space-before-function-paren': [2, { 'anonymous': 'always', 'named': 'never' }], + // require or disallow spaces inside parentheses + 'space-in-parens': [2, 'never'], + // require spaces around operators + 'space-infix-ops': 2, + // require a space after return, throw, and case + 'space-return-throw-case': 2, + // Require or disallow spaces before/after unary operators + 'space-unary-ops': 0, + // require or disallow a space immediately following the // or /* in a comment + 'spaced-comment': [2, 'always', { + 'exceptions': ['-', '+'], + 'markers': ['=', '!'] // space here to support sprockets directives + }], + // require regex literals to be wrapped in parentheses + 'wrap-regex': 0 + } +}; diff --git a/packages/eslint-config-airbnb/rules/variables.js b/packages/eslint-config-airbnb/rules/variables.js new file mode 100644 index 0000000000..3da93fe826 --- /dev/null +++ b/packages/eslint-config-airbnb/rules/variables.js @@ -0,0 +1,26 @@ +module.exports = { + 'rules': { + // enforce or disallow variable initializations at definition + 'init-declarations': 0, + // disallow the catch clause parameter name being the same as a variable in the outer scope + 'no-catch-shadow': 0, + // disallow deletion of variables + 'no-delete-var': 2, + // disallow labels that share a name with a variable + 'no-label-var': 0, + // disallow shadowing of names such as arguments + 'no-shadow-restricted-names': 2, + // disallow declaration of variables already declared in the outer scope + 'no-shadow': 2, + // disallow use of undefined when initializing variables + 'no-undef-init': 0, + // disallow use of undeclared variables unless mentioned in a /*global */ block + 'no-undef': 2, + // disallow use of undefined variable + 'no-undefined': 0, + // disallow declaration of variables that are not used in the code + 'no-unused-vars': [2, {'vars': 'local', 'args': 'after-used'}], + // disallow use of variables before they are defined + 'no-use-before-define': 2 + } +}; diff --git a/packages/eslint-config-airbnb/test/.eslintrc b/packages/eslint-config-airbnb/test/.eslintrc new file mode 100644 index 0000000000..7f79874e41 --- /dev/null +++ b/packages/eslint-config-airbnb/test/.eslintrc @@ -0,0 +1,9 @@ +{ + "rules": { + // disabled because I find it tedious to write tests while following this + // rule + "no-shadow": 0, + // tests uses `t` for tape + "id-length": [2, {"min": 2, "properties": "never", "exceptions": ["t"]}] + } +} diff --git a/packages/eslint-config-airbnb/test/test-base.js b/packages/eslint-config-airbnb/test/test-base.js index ecf06cdf58..24aa884cd0 100644 --- a/packages/eslint-config-airbnb/test/test-base.js +++ b/packages/eslint-config-airbnb/test/test-base.js @@ -1,13 +1,30 @@ +import fs from 'fs'; +import path from 'path'; import test from 'tape'; -import base from '../base'; -test('base: does not reference react', t => { - t.plan(2); +const files = { + base: require('../base') +}; - t.notOk(base.plugins, 'plugins is unspecified'); +fs.readdirSync(path.join(__dirname, '../rules')).forEach(name => { + if (name === 'react.js') { + return; + } - // scan rules for react/ and fail if any exist - const reactRuleIds = Object.keys(base.rules) - .filter(ruleId => ruleId.indexOf('react/') === 0); - t.deepEquals(reactRuleIds, [], 'there are no react/ rules'); + files[name] = require(`../rules/${name}`); +}); + +Object.keys(files).forEach(name => { + const config = files[name]; + + test(`${name}: does not reference react`, t => { + t.plan(2); + + t.notOk(config.plugins, 'plugins is unspecified'); + + // scan rules for react/ and fail if any exist + const reactRuleIds = Object.keys(config.rules) + .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 9ef23f6737..1a84b59230 100644 --- a/packages/eslint-config-airbnb/test/test-react-order.js +++ b/packages/eslint-config-airbnb/test/test-react-order.js @@ -1,10 +1,15 @@ import test from 'tape'; import { CLIEngine } from 'eslint'; import eslintrc from '../'; +import baseConfig from '../base'; +import reactRules from '../rules/react'; const cli = new CLIEngine({ useEslintrc: false, baseConfig: eslintrc, + + // This rule fails when executing on text. + rules: {indent: 0}, }); function lint(text) { @@ -24,21 +29,20 @@ ${body} test('validate react prop order', t => { t.test('make sure our eslintrc has React linting dependencies', t => { - t.plan(2); - t.equal(eslintrc.parser, 'babel-eslint', 'uses babel-eslint'); - t.equal(eslintrc.plugins[0], 'react', 'uses eslint-plugin-react'); + t.plan(1); + t.equal(reactRules.plugins[0], 'react', 'uses eslint-plugin-react'); }); t.test('passes a good component', t => { t.plan(3); const result = lint(wrapComponent(` - componentWillMount() { } - componentDidMount() { } - setFoo() { } - getFoo() { } - setBar() { } - someMethod() { } - renderDogs() { } + componentWillMount() {} + componentDidMount() {} + setFoo() {} + getFoo() {} + setBar() {} + someMethod() {} + renderDogs() {} render() { return
; } `)); @@ -50,13 +54,13 @@ test('validate react prop order', t => { t.test('order: when random method is first', t => { t.plan(2); const result = lint(wrapComponent(` - someMethod() { } - componentWillMount() { } - componentDidMount() { } - setFoo() { } - getFoo() { } - setBar() { } - renderDogs() { } + someMethod() {} + componentWillMount() {} + componentDidMount() {} + setFoo() {} + getFoo() {} + setBar() {} + renderDogs() {} render() { return
; } `)); @@ -67,13 +71,13 @@ test('validate react prop order', t => { t.test('order: when random method after lifecycle methods', t => { t.plan(2); const result = lint(wrapComponent(` - componentWillMount() { } - componentDidMount() { } - someMethod() { } - setFoo() { } - getFoo() { } - setBar() { } - renderDogs() { } + componentWillMount() {} + componentDidMount() {} + someMethod() {} + setFoo() {} + getFoo() {} + setBar() {} + renderDogs() {} render() { return
; } `)); diff --git a/react/README.md b/react/README.md index d1ce8c73da..ba23784e1a 100644 --- a/react/README.md +++ b/react/README.md @@ -5,6 +5,7 @@ ## Table of Contents 1. [Basic Rules](#basic-rules) + 1. [Class vs `React.createClass`](#class-vs-reactcreateclass) 1. [Naming](#naming) 1. [Declaration](#declaration) 1. [Alignment](#alignment) @@ -15,44 +16,51 @@ 1. [Tags](#tags) 1. [Methods](#methods) 1. [Ordering](#ordering) + 1. [`isMounted`](#ismounted) ## Basic Rules - 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 rule: [`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. -## Class vs React.createClass +## Class vs `React.createClass` - - Use class extends React.Component unless you have a very good reason to use mixins. + - Use `class extends React.Component` unless you have a very good reason to use mixins. - ```javascript - // bad - const Listing = React.createClass({ - render() { - return
; - } - }); - - // good - class Listing extends React.Component { - render() { - return
; + eslint rules: [`react/prefer-es6-class`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/prefer-es6-class.md). + + ```javascript + // bad + const Listing = React.createClass({ + render() { + return
; + } + }); + + // good + class Listing extends React.Component { + render() { + return
; + } } - } - ``` + ``` ## Naming - **Extensions**: Use `.jsx` extension for React components. - **Filename**: Use PascalCase for filenames. E.g., `ReservationCard.jsx`. - - **Reference Naming**: Use PascalCase for React components and camelCase for their instances: + - **Reference Naming**: Use PascalCase for React components and camelCase for their instances. + + eslint rules: [`react/jsx-pascal-case`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-pascal-case.md). + ```javascript // bad - const reservationCard = require('./ReservationCard'); + import reservationCard from './ReservationCard'; // good - const ReservationCard = require('./ReservationCard'); + import ReservationCard from './ReservationCard'; // bad const ReservationItem = ; @@ -61,21 +69,22 @@ const reservationItem = ; ``` - **Component Naming**: Use the filename as the component name. For example, `ReservationCard.jsx` should have a reference name of `ReservationCard`. However, for root components of a directory, use `index.jsx` as the filename and use the directory name as the component name: + - **Component Naming**: Use the filename as the component name. For example, `ReservationCard.jsx` should have a reference name of `ReservationCard`. However, for root components of a directory, use `index.jsx` as the filename and use the directory name as the component name: + ```javascript // bad - const Footer = require('./Footer/Footer.jsx') + import Footer from './Footer/Footer'; // bad - const Footer = require('./Footer/index.jsx') + import Footer from './Footer/index'; // good - const Footer = require('./Footer') + import Footer from './Footer'; ``` - ## Declaration - - Do not use displayName for naming components. Instead, name the component by reference. + + - Do not use `displayName` for naming components. Instead, name the component by reference. ```javascript // bad @@ -90,8 +99,11 @@ ``` ## Alignment + - Follow these alignment styles for JSX syntax + eslint rules: [`react/jsx-closing-bracket-location`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-closing-bracket-location.md). + ```javascript // bad Why? JSX attributes [can't contain escaped quotes](http://eslint.org/docs/rules/jsx-quotes), so double quotes make conjunctions like `"don't"` easier to type. + > Regular HTML attributes also typically use double quotes instead of single, so JSX attributes mirror this convention. + + eslint rules: [`jsx-quotes`](http://eslint.org/docs/rules/jsx-quotes). + ```javascript // bad @@ -132,7 +151,9 @@ ``` ## Spacing + - Always include a single space in your self-closing tag. + ```javascript // bad @@ -149,7 +170,9 @@ ``` ## Props + - Always use camelCase for prop names. + ```javascript // bad ``` + - Omit the value of the prop when it is explicitly `true`. + + eslint rules: [`react/jsx-boolean-value`](https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-boolean-value.md). + + ```javascript + // bad +