From 12d1376e3c0f183b475c87a5ff50793cf9560a8d Mon Sep 17 00:00:00 2001 From: Sergei Startsev Date: Wed, 28 Mar 2018 18:33:08 +0300 Subject: [PATCH 01/34] Added `no-component-will-receive-props` rule --- index.js | 1 + lib/rules/no-component-will-receive-props.js | 77 +++++++++++++ .../rules/no-component-will-receive-props.js | 109 ++++++++++++++++++ 3 files changed, 187 insertions(+) create mode 100644 lib/rules/no-component-will-receive-props.js create mode 100644 tests/lib/rules/no-component-will-receive-props.js diff --git a/index.js b/index.js index e0b12d3feb..e5d071cc18 100644 --- a/index.js +++ b/index.js @@ -46,6 +46,7 @@ const allRules = { 'no-access-state-in-setstate': require('./lib/rules/no-access-state-in-setstate'), 'no-array-index-key': require('./lib/rules/no-array-index-key'), 'no-children-prop': require('./lib/rules/no-children-prop'), + 'no-component-will-receive-props': require('./lib/rules/no-component-will-receive-props'), 'no-danger': require('./lib/rules/no-danger'), 'no-danger-with-children': require('./lib/rules/no-danger-with-children'), 'no-deprecated': require('./lib/rules/no-deprecated'), diff --git a/lib/rules/no-component-will-receive-props.js b/lib/rules/no-component-will-receive-props.js new file mode 100644 index 0000000000..860d2c664b --- /dev/null +++ b/lib/rules/no-component-will-receive-props.js @@ -0,0 +1,77 @@ +/** + * @fileoverview Prevents usage of componentWillReceiveProps + */ +'use strict'; + +const Components = require('../util/Components'); +const astUtil = require('../util/ast'); +const docsUrl = require('../util/docsUrl'); + +function errorMessage(node) { + return `${node} should not use componentWillReceiveProps.`; +} + +// ------------------------------------------------------------------------------ +// Rule Definition +// ------------------------------------------------------------------------------ + +module.exports = { + meta: { + docs: { + description: 'Prevents usage of componentWillReceiveProps', + category: 'Possible Errors', + recommended: false, + url: docsUrl('no-component-will-receive-props') + }, + schema: [] + }, + + create: Components.detect(context => { + /** + * Checks for componentWillReceiveProps property + * @param {ASTNode} node The AST node being checked. + * @returns {Boolean} Whether or not the property exists. + */ + function hasComponentWillReceiveProps(node) { + const properties = astUtil.getComponentProperties(node); + return properties.some(property => { + const name = astUtil.getPropertyName(property); + return name === 'componentWillReceiveProps'; + }); + } + + /** + * Get name of node if available + * @param {ASTNode} node The AST node being checked. + * @return {String} The name of the node + */ + function getNodeName(node) { + if (node.id) { + return node.id.name; + } else if (node.parent && node.parent.id) { + return node.parent.id.name; + } + return ''; + } + + /** + * Checks for violation of rule + * @param {ASTNode} node The AST node being checked. + */ + function checkForViolation(node) { + const has = hasComponentWillReceiveProps(node); + if (has) { + const className = getNodeName(node); + context.report({ + node: node, + message: errorMessage(className) + }); + } + } + + return { + ClassDeclaration: checkForViolation, + ClassExpression: checkForViolation + }; + }) +}; diff --git a/tests/lib/rules/no-component-will-receive-props.js b/tests/lib/rules/no-component-will-receive-props.js new file mode 100644 index 0000000000..64663da513 --- /dev/null +++ b/tests/lib/rules/no-component-will-receive-props.js @@ -0,0 +1,109 @@ +/** + * @fileoverview Tests for no-component-will-receive-props + */ + +'use strict'; + +// ----------------------------------------------------------------------------- +// Requirements +// ----------------------------------------------------------------------------- + +const rule = require('../../../lib/rules/no-component-will-receive-props'); +const RuleTester = require('eslint').RuleTester; + +const parserOptions = { + ecmaVersion: 6, + ecmaFeatures: { + experimentalObjectRestSpread: true, + jsx: true + } +}; + +function errorMessage(node) { + return `${node} should not use componentWillReceiveProps.`; +} + +// ----------------------------------------------------------------------------- +// Tests +// ----------------------------------------------------------------------------- + +const ruleTester = new RuleTester(); +ruleTester.run('no-component-will-receive-props', rule, { + valid: [ + { + code: ` + class Foo extends React.Component { + } + `, + parserOptions: parserOptions + }, + { + code: ` + class Foo extends React.Component { + } + `, + parser: 'babel-eslint', + parserOptions: parserOptions + }, + { + code: ` + class Foo extends React.PureComponent { + } + `, + parser: 'babel-eslint', + parserOptions: parserOptions + }, + { + code: ` + function Foo() { + return class Bar extends React.Component { + }; + } + `, + parserOptions: parserOptions + }, + { + code: ` + function Foo() { + return
test
+ } + `, + parserOptions: parserOptions + } + ], + + invalid: [ + { + code: ` + class Foo extends React.PureComponent { + componentWillReceiveProps() { + } + } + `, + errors: [{message: errorMessage('Foo')}], + parserOptions: parserOptions + }, + { + code: ` + class Foo extends React.Component { + componentWillReceiveProps() { + } + } + `, + errors: [{message: errorMessage('Foo')}], + parserOptions: parserOptions + }, + { + code: ` + function Foo() { + return class Bar extends React.PureComponent { + componentWillReceiveProps() { + } + }; + } + `, + errors: [{message: errorMessage('Bar')}], + parserOptions: parserOptions + } + ] +}); From daa076fdaf93ab9949adab73922118fc98182d0a Mon Sep 17 00:00:00 2001 From: Sergei Startsev Date: Wed, 28 Mar 2018 22:48:48 +0300 Subject: [PATCH 02/34] `no-component-will-receive-props` => `no-deprecated-methods`, added docs Rebuilt rule `no-component-will-receive-props`, renamed it to `no-deprecated-methods`, added options to enable/disable particular methods; added docs --- README.md | 1 + docs/rules/no-deprecated-methods.md | 53 +++++ index.js | 2 +- lib/rules/no-component-will-receive-props.js | 77 ------ lib/rules/no-deprecated-methods.js | 104 ++++++++ .../rules/no-component-will-receive-props.js | 109 --------- tests/lib/rules/no-deprecated-methods.js | 222 ++++++++++++++++++ 7 files changed, 381 insertions(+), 187 deletions(-) create mode 100644 docs/rules/no-deprecated-methods.md delete mode 100644 lib/rules/no-component-will-receive-props.js create mode 100644 lib/rules/no-deprecated-methods.js delete mode 100644 tests/lib/rules/no-component-will-receive-props.js create mode 100644 tests/lib/rules/no-deprecated-methods.js diff --git a/README.md b/README.md index c621fb75aa..183e2b9cdd 100644 --- a/README.md +++ b/README.md @@ -103,6 +103,7 @@ Enable the rules that you would like to use. * [react/no-danger](docs/rules/no-danger.md): Prevent usage of dangerous JSX properties * [react/no-danger-with-children](docs/rules/no-danger-with-children.md): Prevent problem with children and props.dangerouslySetInnerHTML * [react/no-deprecated](docs/rules/no-deprecated.md): Prevent usage of deprecated methods +* [react/no-deprecated-methods](docs/rules/no-deprecated-methods.md): Prevents usage of deprecated component lifecycle methods * [react/no-did-mount-set-state](docs/rules/no-did-mount-set-state.md): Prevent usage of `setState` in `componentDidMount` * [react/no-did-update-set-state](docs/rules/no-did-update-set-state.md): Prevent usage of `setState` in `componentDidUpdate` * [react/no-direct-mutation-state](docs/rules/no-direct-mutation-state.md): Prevent direct mutation of `this.state` diff --git a/docs/rules/no-deprecated-methods.md b/docs/rules/no-deprecated-methods.md new file mode 100644 index 0000000000..0a30a347d8 --- /dev/null +++ b/docs/rules/no-deprecated-methods.md @@ -0,0 +1,53 @@ +# Prevents usage of deprecated component lifecycle methods (react/no-deprecated-methods) + +Warns if you have deprecated methods defined when defining a component that extends `React.Component`, `React.PureComponent` or uses ES5 syntax with `createReactClass`. See [React 16.3 details](https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html#gradual-migration-path). + +## Rule Details + +The following patterns are considered warnings: + +```jsx +class Foo extends React.Component { + componentWillReceiveProps() { } + // ... +} + +class Foo extends React.PureComponent { + componentWillReceiveProps() { } + // ... +} + +var Foo = createReactClass({ + componentWillReceiveProps: function() {}, +}) +``` + +The following patterns are **not** considered warnings: + +```jsx +class Foo { + componentWillReceiveProps() {} +} + +var Foo = createReactClassNonReact({ + componentWillReceiveProps: function() {} +}); +``` + +## Rule Options + +This rule can take options to ignore some deprecated methods. + +```js +... +"react/no-deprecated-methods": [, { + "componentWillMount": , + "componentWillReceiveProps": , + "componentWillUpdate": , +}] +... +``` + +* `componentWillMount`: `true` by default. +* `componentWillReceiveProps`: `true` by default. +* `componentWillUpdate`: `true` by default. \ No newline at end of file diff --git a/index.js b/index.js index e5d071cc18..11ef8e7561 100644 --- a/index.js +++ b/index.js @@ -46,10 +46,10 @@ const allRules = { 'no-access-state-in-setstate': require('./lib/rules/no-access-state-in-setstate'), 'no-array-index-key': require('./lib/rules/no-array-index-key'), 'no-children-prop': require('./lib/rules/no-children-prop'), - 'no-component-will-receive-props': require('./lib/rules/no-component-will-receive-props'), 'no-danger': require('./lib/rules/no-danger'), 'no-danger-with-children': require('./lib/rules/no-danger-with-children'), 'no-deprecated': require('./lib/rules/no-deprecated'), + 'no-deprecated-methods': require('./lib/rules/no-deprecated-methods'), 'no-did-mount-set-state': require('./lib/rules/no-did-mount-set-state'), 'no-did-update-set-state': require('./lib/rules/no-did-update-set-state'), 'no-direct-mutation-state': require('./lib/rules/no-direct-mutation-state'), diff --git a/lib/rules/no-component-will-receive-props.js b/lib/rules/no-component-will-receive-props.js deleted file mode 100644 index 860d2c664b..0000000000 --- a/lib/rules/no-component-will-receive-props.js +++ /dev/null @@ -1,77 +0,0 @@ -/** - * @fileoverview Prevents usage of componentWillReceiveProps - */ -'use strict'; - -const Components = require('../util/Components'); -const astUtil = require('../util/ast'); -const docsUrl = require('../util/docsUrl'); - -function errorMessage(node) { - return `${node} should not use componentWillReceiveProps.`; -} - -// ------------------------------------------------------------------------------ -// Rule Definition -// ------------------------------------------------------------------------------ - -module.exports = { - meta: { - docs: { - description: 'Prevents usage of componentWillReceiveProps', - category: 'Possible Errors', - recommended: false, - url: docsUrl('no-component-will-receive-props') - }, - schema: [] - }, - - create: Components.detect(context => { - /** - * Checks for componentWillReceiveProps property - * @param {ASTNode} node The AST node being checked. - * @returns {Boolean} Whether or not the property exists. - */ - function hasComponentWillReceiveProps(node) { - const properties = astUtil.getComponentProperties(node); - return properties.some(property => { - const name = astUtil.getPropertyName(property); - return name === 'componentWillReceiveProps'; - }); - } - - /** - * Get name of node if available - * @param {ASTNode} node The AST node being checked. - * @return {String} The name of the node - */ - function getNodeName(node) { - if (node.id) { - return node.id.name; - } else if (node.parent && node.parent.id) { - return node.parent.id.name; - } - return ''; - } - - /** - * Checks for violation of rule - * @param {ASTNode} node The AST node being checked. - */ - function checkForViolation(node) { - const has = hasComponentWillReceiveProps(node); - if (has) { - const className = getNodeName(node); - context.report({ - node: node, - message: errorMessage(className) - }); - } - } - - return { - ClassDeclaration: checkForViolation, - ClassExpression: checkForViolation - }; - }) -}; diff --git a/lib/rules/no-deprecated-methods.js b/lib/rules/no-deprecated-methods.js new file mode 100644 index 0000000000..7ea0e7f747 --- /dev/null +++ b/lib/rules/no-deprecated-methods.js @@ -0,0 +1,104 @@ +/** + * @fileoverview Prevents usage of deprecated component lifecycle methods + */ +'use strict'; + +const Components = require('../util/Components'); +const astUtil = require('../util/ast'); +const docsUrl = require('../util/docsUrl'); + +function errorMessage(node, method) { + return `${node} should not use ${method}.`; +} + +// ------------------------------------------------------------------------------ +// Rule Definition +// ------------------------------------------------------------------------------ + +module.exports = { + meta: { + docs: { + description: 'Prevents usage of deprecated component lifecycle methods', + category: 'Possible Errors', + recommended: false, + url: docsUrl('no-deprecated-methods') + }, + schema: [{ + type: 'object', + properties: { + componentWillMount: { + type: 'boolean' + }, + componentWillReceiveProps: { + type: 'boolean' + }, + componentWillUpdate: { + type: 'boolean' + } + }, + additionalProperties: false + }] + }, + + create: Components.detect((context, components, utils) => { + const defaults = { + componentWillMount: true, + componentWillReceiveProps: true, + componentWillUpdate: true + }; + const configuration = Object.assign({}, defaults, context.options[0] || {}); + const methods = Object.keys(configuration).filter(key => configuration[key]); + /** + * Returns deprecated method if available + * @param {ASTNode} node The AST node being checked. + * @returns {Boolean} Whether or not the property exists. + */ + function getDeprecatedMethods(node) { + const properties = astUtil.getComponentProperties(node); + return properties + .map(property => astUtil.getPropertyName(property)) + .filter(name => methods.indexOf(name) !== -1); + } + + /** + * Gets name of node if available + * @param {ASTNode} node The AST node being checked. + * @return {String} The name of the node + */ + function getNodeName(node) { + if (node.id) { + return node.id.name; + } else if (node.parent && node.parent.id) { + return node.parent.id.name; + } else if (node.parent && node.parent.parent && node.parent.parent.id) { + return node.parent.parent.id.name; + } + return ''; + } + + /** + * Checks for violation of rule + * @param {ASTNode} node The AST node being checked. + */ + function checkForViolation(node) { + if (utils.isES5Component(node) || utils.isES6Component(node)) { + const deprecatedMethods = getDeprecatedMethods(node); + if (deprecatedMethods && deprecatedMethods.length) { + const className = getNodeName(node); + deprecatedMethods.forEach(method => { + context.report({ + node: node, + message: errorMessage(className, method) + }); + }); + } + } + } + + return { + ClassDeclaration: checkForViolation, + ClassExpression: checkForViolation, + ObjectExpression: checkForViolation + }; + }) +}; diff --git a/tests/lib/rules/no-component-will-receive-props.js b/tests/lib/rules/no-component-will-receive-props.js deleted file mode 100644 index 64663da513..0000000000 --- a/tests/lib/rules/no-component-will-receive-props.js +++ /dev/null @@ -1,109 +0,0 @@ -/** - * @fileoverview Tests for no-component-will-receive-props - */ - -'use strict'; - -// ----------------------------------------------------------------------------- -// Requirements -// ----------------------------------------------------------------------------- - -const rule = require('../../../lib/rules/no-component-will-receive-props'); -const RuleTester = require('eslint').RuleTester; - -const parserOptions = { - ecmaVersion: 6, - ecmaFeatures: { - experimentalObjectRestSpread: true, - jsx: true - } -}; - -function errorMessage(node) { - return `${node} should not use componentWillReceiveProps.`; -} - -// ----------------------------------------------------------------------------- -// Tests -// ----------------------------------------------------------------------------- - -const ruleTester = new RuleTester(); -ruleTester.run('no-component-will-receive-props', rule, { - valid: [ - { - code: ` - class Foo extends React.Component { - } - `, - parserOptions: parserOptions - }, - { - code: ` - class Foo extends React.Component { - } - `, - parser: 'babel-eslint', - parserOptions: parserOptions - }, - { - code: ` - class Foo extends React.PureComponent { - } - `, - parser: 'babel-eslint', - parserOptions: parserOptions - }, - { - code: ` - function Foo() { - return class Bar extends React.Component { - }; - } - `, - parserOptions: parserOptions - }, - { - code: ` - function Foo() { - return
test
- } - `, - parserOptions: parserOptions - } - ], - - invalid: [ - { - code: ` - class Foo extends React.PureComponent { - componentWillReceiveProps() { - } - } - `, - errors: [{message: errorMessage('Foo')}], - parserOptions: parserOptions - }, - { - code: ` - class Foo extends React.Component { - componentWillReceiveProps() { - } - } - `, - errors: [{message: errorMessage('Foo')}], - parserOptions: parserOptions - }, - { - code: ` - function Foo() { - return class Bar extends React.PureComponent { - componentWillReceiveProps() { - } - }; - } - `, - errors: [{message: errorMessage('Bar')}], - parserOptions: parserOptions - } - ] -}); diff --git a/tests/lib/rules/no-deprecated-methods.js b/tests/lib/rules/no-deprecated-methods.js new file mode 100644 index 0000000000..efe2d3d2d6 --- /dev/null +++ b/tests/lib/rules/no-deprecated-methods.js @@ -0,0 +1,222 @@ +/** + * @fileoverview Tests for no-deprecated-methods + */ + +'use strict'; + +// ----------------------------------------------------------------------------- +// Requirements +// ----------------------------------------------------------------------------- + +const rule = require('../../../lib/rules/no-deprecated-methods'); +const RuleTester = require('eslint').RuleTester; + +const parserOptions = { + ecmaVersion: 6, + ecmaFeatures: { + experimentalObjectRestSpread: true, + jsx: true + } +}; + +function errorMessage(node, method) { + return `${node} should not use ${method}.`; +} + +// ----------------------------------------------------------------------------- +// Tests +// ----------------------------------------------------------------------------- + +const ruleTester = new RuleTester(); +ruleTester.run('no-deprecated-methods', rule, { + valid: [ + { + code: ` + var Foo = createReactClass({ + render: function() {} + }) + `, + parserOptions: parserOptions + }, + { + code: ` + var Foo = createReactClassNonReact({ + componentWillMount: function() {}, + componentWillReceiveProps: function() {}, + componentWillUpdate: function() {} + }); + `, + parserOptions: parserOptions + }, + { + code: ` + var Foo = { + componentWillMount: function() {}, + componentWillReceiveProps: function() {}, + componentWillUpdate: function() {} + }; + `, + parserOptions: parserOptions + }, + { + code: ` + class Foo { + componentWillMount() {} + componentWillReceiveProps() {} + componentWillUpdate() {} + } + `, + parserOptions: parserOptions + }, + { + code: ` + class Foo extends React.Component { + } + `, + parserOptions: parserOptions + }, + { + code: ` + class Foo extends React.Component { + } + `, + parser: 'babel-eslint', + parserOptions: parserOptions + }, + { + code: ` + class Foo extends React.PureComponent { + } + `, + parser: 'babel-eslint', + parserOptions: parserOptions + }, + { + code: ` + function Foo() { + return class Bar extends React.Component { + }; + } + `, + parserOptions: parserOptions + }, + { + code: ` + function Foo() { + return
test
+ } + `, + parserOptions: parserOptions + }, + { + code: ` + class Foo extends React.Component { + componentWillMount() {} + componentWillReceiveProps() {} + componentWillUpdate() {} + } + `, + options: [{ + componentWillMount: false, + componentWillReceiveProps: false, + componentWillUpdate: false + }], + parserOptions: parserOptions + } + ], + + invalid: [ + { + code: ` + function Foo() { + return class Bar extends React.PureComponent { + componentWillMount() {} + componentWillReceiveProps() {} + componentWillUpdate() {} + }; + } + `, + errors: [ + {message: errorMessage('Bar', 'componentWillMount')}, + {message: errorMessage('Bar', 'componentWillReceiveProps')}, + {message: errorMessage('Bar', 'componentWillUpdate')} + ], + parserOptions: parserOptions + }, + { + code: ` + var Foo = createReactClass({ + componentWillMount: function() {}, + componentWillReceiveProps: function() {}, + componentWillUpdate: function() {} + }) + `, + errors: [ + {message: errorMessage('Foo', 'componentWillMount')}, + {message: errorMessage('Foo', 'componentWillReceiveProps')}, + {message: errorMessage('Foo', 'componentWillUpdate')} + ], + parserOptions: parserOptions + }, + { + code: ` + class Foo extends React.PureComponent { + componentWillMount() {} + componentWillReceiveProps() {} + componentWillUpdate() {} + } + `, + errors: [ + {message: errorMessage('Foo', 'componentWillMount')}, + {message: errorMessage('Foo', 'componentWillReceiveProps')}, + {message: errorMessage('Foo', 'componentWillUpdate')} + ], + parserOptions: parserOptions + }, + { + code: ` + class Foo extends PureComponent { + componentWillMount() {} + componentWillReceiveProps() {} + componentWillUpdate() {} + } + `, + errors: [ + {message: errorMessage('Foo', 'componentWillMount')}, + {message: errorMessage('Foo', 'componentWillReceiveProps')}, + {message: errorMessage('Foo', 'componentWillUpdate')} + ], + parserOptions: parserOptions + }, + { + code: ` + class Foo extends React.Component { + componentWillMount() {} + componentWillReceiveProps() {} + componentWillUpdate() {} + } + `, + errors: [ + {message: errorMessage('Foo', 'componentWillMount')}, + {message: errorMessage('Foo', 'componentWillReceiveProps')}, + {message: errorMessage('Foo', 'componentWillUpdate')} + ], + parserOptions: parserOptions + }, + { + code: ` + class Foo extends Component { + componentWillMount() {} + componentWillReceiveProps() {} + componentWillUpdate() {} + } + `, + errors: [ + {message: errorMessage('Foo', 'componentWillMount')}, + {message: errorMessage('Foo', 'componentWillReceiveProps')}, + {message: errorMessage('Foo', 'componentWillUpdate')} + ], + parserOptions: parserOptions + } + ] +}); From 5c7ab5f3c0fc70cc2a0673a12676196d225372fa Mon Sep 17 00:00:00 2001 From: Sergei Startsev Date: Wed, 28 Mar 2018 22:52:47 +0300 Subject: [PATCH 03/34] Fixed jsdoc comments for `no-deprecated-methods` rule --- lib/rules/no-deprecated-methods.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/rules/no-deprecated-methods.js b/lib/rules/no-deprecated-methods.js index 7ea0e7f747..8a3b0ed913 100644 --- a/lib/rules/no-deprecated-methods.js +++ b/lib/rules/no-deprecated-methods.js @@ -49,9 +49,9 @@ module.exports = { const configuration = Object.assign({}, defaults, context.options[0] || {}); const methods = Object.keys(configuration).filter(key => configuration[key]); /** - * Returns deprecated method if available + * Returns deprecated methods if available * @param {ASTNode} node The AST node being checked. - * @returns {Boolean} Whether or not the property exists. + * @returns {Array} The array of deprecated methods. */ function getDeprecatedMethods(node) { const properties = astUtil.getComponentProperties(node); From 323dba2a9730cd4a03a9c87c23858d5c441696dc Mon Sep 17 00:00:00 2001 From: Sergei Startsev Date: Fri, 30 Mar 2018 02:07:02 +0300 Subject: [PATCH 04/34] Adjusted no-deprecated rule for React 16.3.0 Added warnings for componentWillMount, componentWillReceiveProps, componentWillUpdate --- README.md | 2 +- docs/rules/no-deprecated.md | 27 ++ lib/rules/no-deprecated.js | 39 ++- tests/lib/rules/no-deprecated.js | 442 +++++++++++++++++++++---------- 4 files changed, 369 insertions(+), 141 deletions(-) diff --git a/README.md b/README.md index c621fb75aa..c6b0889da5 100644 --- a/README.md +++ b/README.md @@ -102,7 +102,7 @@ Enable the rules that you would like to use. * [react/no-children-prop](docs/rules/no-children-prop.md): Prevent passing children as props * [react/no-danger](docs/rules/no-danger.md): Prevent usage of dangerous JSX properties * [react/no-danger-with-children](docs/rules/no-danger-with-children.md): Prevent problem with children and props.dangerouslySetInnerHTML -* [react/no-deprecated](docs/rules/no-deprecated.md): Prevent usage of deprecated methods +* [react/no-deprecated](docs/rules/no-deprecated.md): Prevent usage of deprecated methods, including component lifecyle methods * [react/no-did-mount-set-state](docs/rules/no-did-mount-set-state.md): Prevent usage of `setState` in `componentDidMount` * [react/no-did-update-set-state](docs/rules/no-did-update-set-state.md): Prevent usage of `setState` in `componentDidUpdate` * [react/no-direct-mutation-state](docs/rules/no-direct-mutation-state.md): Prevent direct mutation of `this.state` diff --git a/docs/rules/no-deprecated.md b/docs/rules/no-deprecated.md index 77cf0ff7bb..63076fed6e 100644 --- a/docs/rules/no-deprecated.md +++ b/docs/rules/no-deprecated.md @@ -27,6 +27,27 @@ const propTypes = { React.DOM.div(); import React, { PropTypes } from 'react'; + +class Foo extends React.Component { + componentWillMount() { } + componentWillReceiveProps() { } + componentWillUpdate() { } + // ... +} + +class Foo extends React.PureComponent { + componentWillMount() { } + componentWillReceiveProps() { } + componentWillUpdate() { } + // ... +} + +var Foo = createReactClass({ + componentWillMount: function() {}, + componentWillReceiveProps: function() {}, + componentWillUpdate: function() {}, + // ... +}) ``` The following patterns are **not** considered warnings: @@ -38,4 +59,10 @@ ReactDOM.render(, root); ReactDOM.findDOMNode(this.refs.foo); import { PropTypes } from 'prop-types'; + +class Foo { + componentWillMount() { } + componentWillReceiveProps() { } + componentWillUpdate() { } +} ``` diff --git a/lib/rules/no-deprecated.js b/lib/rules/no-deprecated.js index 6e8c88e265..0e332b7eb2 100644 --- a/lib/rules/no-deprecated.js +++ b/lib/rules/no-deprecated.js @@ -2,14 +2,17 @@ * @fileoverview Prevent usage of deprecated methods * @author Yannick Croissant * @author Scott Feeney + * @author Sergei Startsev */ 'use strict'; const has = require('has'); +const Components = require('../util/Components'); +const astUtil = require('../util/ast'); +const docsUrl = require('../util/docsUrl'); const pragmaUtil = require('../util/pragma'); const versionUtil = require('../util/version'); -const docsUrl = require('../util/docsUrl'); // ------------------------------------------------------------------------------ // Constants @@ -37,7 +40,7 @@ module.exports = { schema: [] }, - create: function(context) { + create: Components.detect((context, components, utils) => { const sourceCode = context.getSourceCode(); const pragma = pragmaUtil.getFromContext(context); @@ -73,6 +76,10 @@ module.exports = { deprecated[`${pragma}.PropTypes`] = ['15.5.0', 'the npm module prop-types']; // 15.6.0 deprecated[`${pragma}.DOM`] = ['15.6.0', 'the npm module react-dom-factories']; + // 16.3.0 + deprecated.componentWillMount = ['16.3.0']; + deprecated.componentWillReceiveProps = ['16.3.0']; + deprecated.componentWillUpdate = ['16.3.0']; return deprecated; } @@ -119,6 +126,27 @@ module.exports = { return moduleName; } + /** + * Returns life cycle methods if available + * @param {ASTNode} node The AST node being checked. + * @returns {Array} The array of methods. + */ + function getLifeCycleMethods(node) { + const properties = astUtil.getComponentProperties(node); + return properties.map(property => astUtil.getPropertyName(property)); + } + + /** + * Checks life cycle methods + * @param {ASTNode} node The AST node being checked. + */ + function checkLifeCycleMethods(node) { + if (utils.isES5Component(node) || utils.isES6Component(node)) { + const methods = getLifeCycleMethods(node); + methods.forEach(method => checkDeprecation(node, method)); + } + } + // -------------------------------------------------------------------------- // Public // -------------------------------------------------------------------------- @@ -160,8 +188,11 @@ module.exports = { node.id.properties.forEach(property => { checkDeprecation(node, `${reactModuleName || pragma}.${property.key.name}`); }); - } + }, + ClassDeclaration: checkLifeCycleMethods, + ClassExpression: checkLifeCycleMethods, + ObjectExpression: checkLifeCycleMethods }; - } + }) }; diff --git a/tests/lib/rules/no-deprecated.js b/tests/lib/rules/no-deprecated.js index 9ddcd6b906..37a6b7b927 100644 --- a/tests/lib/rules/no-deprecated.js +++ b/tests/lib/rules/no-deprecated.js @@ -2,6 +2,7 @@ * @fileoverview Prevent usage of deprecated methods * @author Yannick Croissant * @author Scott Feeney + * @author Sergei Startsev */ 'use strict'; @@ -23,6 +24,10 @@ const parserOptions = { require('babel-eslint'); +function errorMessage(oldMethod, version, newMethod = '') { + return `${oldMethod} is deprecated since React ${version}${newMethod}`; +} + // ------------------------------------------------------------------------------ // Tests // ------------------------------------------------------------------------------ @@ -39,151 +44,316 @@ ruleTester.run('no-deprecated', rule, { 'ReactDOM.findDOMNode(instance);', 'ReactDOMServer.renderToString(element);', 'ReactDOMServer.renderToStaticMarkup(element);', + { + code: ` + var Foo = createReactClass({ + render: function() {} + }) + ` + }, + // Non-React + { + code: ` + var Foo = createReactClassNonReact({ + componentWillMount: function() {}, + componentWillReceiveProps: function() {}, + componentWillUpdate: function() {} + }); + ` + }, + { + code: ` + var Foo = { + componentWillMount: function() {}, + componentWillReceiveProps: function() {}, + componentWillUpdate: function() {} + }; + ` + }, + { + code: ` + class Foo { + componentWillMount() {} + componentWillReceiveProps() {} + componentWillUpdate() {} + } + ` + }, // Deprecated in a later version {code: 'React.renderComponent()', settings: {react: {version: '0.11.0'}}}, {code: 'React.createClass()', settings: {react: {version: '15.4.0'}}}, - {code: 'PropTypes', settings: {react: {version: '15.4.0'}}} + {code: 'PropTypes', settings: {react: {version: '15.4.0'}}}, + { + code: ` + class Foo extends React.Component { + componentWillMount() {} + componentWillReceiveProps() {} + componentWillUpdate() {} + } + `, + settings: {react: {version: '16.2.0'}} + } ], - invalid: [{ - code: 'React.renderComponent()', - settings: {react: {version: '0.12.0'}}, - errors: [{ - message: 'React.renderComponent is deprecated since React 0.12.0, use React.render instead' - }] - }, { - code: 'Foo.renderComponent()', - settings: {react: {pragma: 'Foo', version: '0.12.0'}}, - errors: [{ - message: 'Foo.renderComponent is deprecated since React 0.12.0, use Foo.render instead' - }] - }, { - code: '/** @jsx Foo */ Foo.renderComponent()', - settings: {react: {version: '0.12.0'}}, - errors: [{ - message: 'Foo.renderComponent is deprecated since React 0.12.0, use Foo.render instead' - }] - }, { - code: 'this.transferPropsTo()', - errors: [{ - message: 'this.transferPropsTo is deprecated since React 0.12.0, use spread operator ({...}) instead' - }] - }, { - code: 'React.addons.TestUtils', - errors: [{ - message: 'React.addons.TestUtils is deprecated since React 15.5.0, use ReactDOM.TestUtils instead' - }] - }, { - code: 'React.addons.classSet()', - errors: [{ - message: 'React.addons.classSet is deprecated since React 0.13.0, use the npm module classnames instead' - }] - }, { - code: 'React.render(element, container);', - errors: [{ - message: 'React.render is deprecated since React 0.14.0, use ReactDOM.render instead' - }] - }, { - code: 'React.unmountComponentAtNode(container);', - errors: [{ - message: ( - 'React.unmountComponentAtNode is deprecated since React 0.14.0, ' + - 'use ReactDOM.unmountComponentAtNode instead' - ) - }] - }, { - code: 'React.findDOMNode(instance);', - errors: [{ - message: 'React.findDOMNode is deprecated since React 0.14.0, use ReactDOM.findDOMNode instead' - }] - }, { - code: 'React.renderToString(element);', - errors: [{ - message: 'React.renderToString is deprecated since React 0.14.0, use ReactDOMServer.renderToString instead' - }] - }, { - code: 'React.renderToStaticMarkup(element);', - errors: [{ - message: ( - 'React.renderToStaticMarkup is deprecated since React 0.14.0, ' + - 'use ReactDOMServer.renderToStaticMarkup instead' - ) - }] - }, { - code: 'React.createClass({});', - errors: [{ - message: 'React.createClass is deprecated since React 15.5.0, use the npm module create-react-class instead' - }] - }, { - code: 'Foo.createClass({});', - settings: {react: {pragma: 'Foo'}}, - errors: [{ - message: 'Foo.createClass is deprecated since React 15.5.0, use the npm module create-react-class instead' - }] - }, { - code: 'React.PropTypes', - errors: [{ - message: 'React.PropTypes is deprecated since React 15.5.0, use the npm module prop-types instead' - }] - }, { - code: 'var {createClass} = require(\'react\');', - parser: 'babel-eslint', - errors: [{ - message: 'React.createClass is deprecated since React 15.5.0, use the npm module create-react-class instead' - }] - }, { - code: 'var {createClass, PropTypes} = require(\'react\');', - parser: 'babel-eslint', - errors: [{ - message: 'React.createClass is deprecated since React 15.5.0, use the npm module create-react-class instead' - }, { - message: 'React.PropTypes is deprecated since React 15.5.0, use the npm module prop-types instead' - }] - }, { - code: 'import {createClass} from \'react\';', - parser: 'babel-eslint', - errors: [{ - message: 'React.createClass is deprecated since React 15.5.0, use the npm module create-react-class instead' - }] - }, { - code: 'import {createClass, PropTypes} from \'react\';', - parser: 'babel-eslint', - errors: [{ - message: 'React.createClass is deprecated since React 15.5.0, use the npm module create-react-class instead' - }, { - message: 'React.PropTypes is deprecated since React 15.5.0, use the npm module prop-types instead' - }] - }, { - code: ` + invalid: [ + { + code: 'React.renderComponent()', + settings: {react: {version: '0.12.0'}}, + errors: [{ + message: 'React.renderComponent is deprecated since React 0.12.0, use React.render instead' + }] + }, + { + code: 'Foo.renderComponent()', + settings: {react: {pragma: 'Foo', version: '0.12.0'}}, + errors: [{ + message: 'Foo.renderComponent is deprecated since React 0.12.0, use Foo.render instead' + }] + }, + { + code: '/** @jsx Foo */ Foo.renderComponent()', + settings: {react: {version: '0.12.0'}}, + errors: [{ + message: 'Foo.renderComponent is deprecated since React 0.12.0, use Foo.render instead' + }] + }, + { + code: 'this.transferPropsTo()', + errors: [{ + message: 'this.transferPropsTo is deprecated since React 0.12.0, use spread operator ({...}) instead' + }] + }, + { + code: 'React.addons.TestUtils', + errors: [{ + message: 'React.addons.TestUtils is deprecated since React 15.5.0, use ReactDOM.TestUtils instead' + }] + }, + { + code: 'React.addons.classSet()', + errors: [{ + message: 'React.addons.classSet is deprecated since React 0.13.0, use the npm module classnames instead' + }] + }, + { + code: 'React.render(element, container);', + errors: [{ + message: 'React.render is deprecated since React 0.14.0, use ReactDOM.render instead' + }] + }, + { + code: 'React.unmountComponentAtNode(container);', + errors: [{ + message: ( + 'React.unmountComponentAtNode is deprecated since React 0.14.0, ' + + 'use ReactDOM.unmountComponentAtNode instead' + ) + }] + }, + { + code: 'React.findDOMNode(instance);', + errors: [{ + message: 'React.findDOMNode is deprecated since React 0.14.0, use ReactDOM.findDOMNode instead' + }] + }, + { + code: 'React.renderToString(element);', + errors: [{ + message: 'React.renderToString is deprecated since React 0.14.0, use ReactDOMServer.renderToString instead' + }] + }, + { + code: 'React.renderToStaticMarkup(element);', + errors: [{ + message: ( + 'React.renderToStaticMarkup is deprecated since React 0.14.0, ' + + 'use ReactDOMServer.renderToStaticMarkup instead' + ) + }] + }, + { + code: 'React.createClass({});', + errors: [{ + message: 'React.createClass is deprecated since React 15.5.0, use the npm module create-react-class instead' + }] + }, + { + code: 'Foo.createClass({});', + settings: {react: {pragma: 'Foo'}}, + errors: [{ + message: 'Foo.createClass is deprecated since React 15.5.0, use the npm module create-react-class instead' + }] + }, + { + code: 'React.PropTypes', + errors: [{ + message: 'React.PropTypes is deprecated since React 15.5.0, use the npm module prop-types instead' + }] + }, + { + code: 'var {createClass} = require(\'react\');', + parser: 'babel-eslint', + errors: [{ + message: 'React.createClass is deprecated since React 15.5.0, use the npm module create-react-class instead' + }] + }, + { + code: 'var {createClass, PropTypes} = require(\'react\');', + parser: 'babel-eslint', + errors: [{ + message: 'React.createClass is deprecated since React 15.5.0, use the npm module create-react-class instead' + }, { + message: 'React.PropTypes is deprecated since React 15.5.0, use the npm module prop-types instead' + }] + }, + { + code: 'import {createClass} from \'react\';', + parser: 'babel-eslint', + errors: [{ + message: 'React.createClass is deprecated since React 15.5.0, use the npm module create-react-class instead' + }] + }, + { + code: 'import {createClass, PropTypes} from \'react\';', + parser: 'babel-eslint', + errors: [{ + message: 'React.createClass is deprecated since React 15.5.0, use the npm module create-react-class instead' + }, { + message: 'React.PropTypes is deprecated since React 15.5.0, use the npm module prop-types instead' + }] + }, + { + code: ` import React from 'react'; const {createClass, PropTypes} = React; `, - parser: 'babel-eslint', - errors: [{ - message: 'React.createClass is deprecated since React 15.5.0, use the npm module create-react-class instead' - }, { - message: 'React.PropTypes is deprecated since React 15.5.0, use the npm module prop-types instead' - }] - }, { - code: 'import {printDOM} from \'react-addons-perf\';', - parser: 'babel-eslint', - errors: [{ - message: 'ReactPerf.printDOM is deprecated since React 15.0.0, use ReactPerf.printOperations instead' - }] - }, { - code: ` + parser: 'babel-eslint', + errors: [{ + message: 'React.createClass is deprecated since React 15.5.0, use the npm module create-react-class instead' + }, { + message: 'React.PropTypes is deprecated since React 15.5.0, use the npm module prop-types instead' + }] + }, + { + code: 'import {printDOM} from \'react-addons-perf\';', + parser: 'babel-eslint', + errors: [{ + message: 'ReactPerf.printDOM is deprecated since React 15.0.0, use ReactPerf.printOperations instead' + }] + }, + { + code: ` import ReactPerf from 'react-addons-perf'; const {printDOM} = ReactPerf; `, - parser: 'babel-eslint', - errors: [{ - message: 'ReactPerf.printDOM is deprecated since React 15.0.0, use ReactPerf.printOperations instead' - }] - }, - { - code: 'React.DOM.div', - errors: [{ - message: 'React.DOM is deprecated since React 15.6.0, use the npm module react-dom-factories instead' - }] - }] + parser: 'babel-eslint', + errors: [{ + message: 'ReactPerf.printDOM is deprecated since React 15.0.0, use ReactPerf.printOperations instead' + }] + }, + { + code: 'React.DOM.div', + errors: [{ + message: 'React.DOM is deprecated since React 15.6.0, use the npm module react-dom-factories instead' + }] + }, + { + code: ` + class Bar extends React.PureComponent { + componentWillMount() {} + componentWillReceiveProps() {} + componentWillUpdate() {} + }; + `, + settings: {react: {version: '16.3.0'}}, + errors: [ + {message: errorMessage('componentWillMount', '16.3.0')}, + {message: errorMessage('componentWillReceiveProps', '16.3.0')}, + {message: errorMessage('componentWillUpdate', '16.3.0')} + ], + parserOptions: parserOptions + }, + { + code: ` + function Foo() { + return class Bar extends React.PureComponent { + componentWillMount() {} + componentWillReceiveProps() {} + componentWillUpdate() {} + }; + } + `, + settings: {react: {version: '16.3.0'}}, + errors: [ + {message: errorMessage('componentWillMount', '16.3.0')}, + {message: errorMessage('componentWillReceiveProps', '16.3.0')}, + {message: errorMessage('componentWillUpdate', '16.3.0')} + ], + parserOptions: parserOptions + }, + { + code: ` + class Bar extends PureComponent { + componentWillMount() {} + componentWillReceiveProps() {} + componentWillUpdate() {} + }; + `, + settings: {react: {version: '16.3.0'}}, + errors: [ + {message: errorMessage('componentWillMount', '16.3.0')}, + {message: errorMessage('componentWillReceiveProps', '16.3.0')}, + {message: errorMessage('componentWillUpdate', '16.3.0')} + ], + parserOptions: parserOptions + }, + { + code: ` + class Foo extends React.Component { + componentWillMount() {} + componentWillReceiveProps() {} + componentWillUpdate() {} + } + `, + settings: {react: {version: '16.3.0'}}, + errors: [ + {message: errorMessage('componentWillMount', '16.3.0')}, + {message: errorMessage('componentWillReceiveProps', '16.3.0')}, + {message: errorMessage('componentWillUpdate', '16.3.0')} + ], + parserOptions: parserOptions + }, + { + code: ` + class Foo extends Component { + componentWillMount() {} + componentWillReceiveProps() {} + componentWillUpdate() {} + } + `, + settings: {react: {version: '16.3.0'}}, + errors: [ + {message: errorMessage('componentWillMount', '16.3.0')}, + {message: errorMessage('componentWillReceiveProps', '16.3.0')}, + {message: errorMessage('componentWillUpdate', '16.3.0')} + ], + parserOptions: parserOptions + }, + { + code: ` + var Foo = createReactClass({ + componentWillMount: function() {}, + componentWillReceiveProps: function() {}, + componentWillUpdate: function() {} + }) + `, + settings: {react: {version: '16.3.0'}}, + errors: [ + {message: errorMessage('componentWillMount', '16.3.0')}, + {message: errorMessage('componentWillReceiveProps', '16.3.0')}, + {message: errorMessage('componentWillUpdate', '16.3.0')} + ], + parserOptions: parserOptions + } + ] }); From 69b1317532600eec0618352f30231da29f9435c7 Mon Sep 17 00:00:00 2001 From: Yannick Croissant Date: Mon, 21 May 2018 20:12:08 +0200 Subject: [PATCH 05/34] Fix default-props-match-prop-types type annotations detection with ESLint 3 --- lib/rules/default-props-match-prop-types.js | 38 ++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/lib/rules/default-props-match-prop-types.js b/lib/rules/default-props-match-prop-types.js index 9e0f994878..cc0992ec53 100644 --- a/lib/rules/default-props-match-prop-types.js +++ b/lib/rules/default-props-match-prop-types.js @@ -41,6 +41,9 @@ module.exports = { const configuration = context.options[0] || {}; const allowRequiredDefaults = configuration.allowRequiredDefaults || false; const propWrapperFunctions = new Set(context.settings.propWrapperFunctions || []); + // Used to track the type annotations in scope. + // Necessary because babel's scopes do not track type annotations. + let stack = null; /** * Try to resolve the node passed in to a variable in the current scope. If the node passed in is not @@ -62,6 +65,22 @@ module.exports = { return node; } + /** + * Helper for accessing the current scope in the stack. + * @param {string} key The name of the identifier to access. If omitted, returns the full scope. + * @param {ASTNode} value If provided sets the new value for the identifier. + * @returns {Object|ASTNode} Either the whole scope or the ASTNode associated with the given identifier. + */ + function typeScope(key, value) { + if (arguments.length === 0) { + return stack[stack.length - 1]; + } else if (arguments.length === 1) { + return stack[stack.length - 1][key]; + } + stack[stack.length - 1][key] = value; + return value; + } + /** * Tries to find the definition of a GenericTypeAnnotation in the current scope. * @param {ASTNode} node The node GenericTypeAnnotation node to resolve. @@ -72,7 +91,7 @@ module.exports = { return null; } - return variableUtil.findVariableByName(context, node.id.name); + return variableUtil.findVariableByName(context, node.id.name) || typeScope(node.id.name); } function resolveUnionTypeAnnotation(node) { @@ -551,12 +570,29 @@ module.exports = { }); }, + TypeAlias: function(node) { + typeScope(node.id.name, node.right); + }, + + Program: function() { + stack = [{}]; + }, + + BlockStatement: function () { + stack.push(Object.create(typeScope())); + }, + + 'BlockStatement:exit': function () { + stack.pop(); + }, + // Check for type annotations in stateless components FunctionDeclaration: handleStatelessComponent, ArrowFunctionExpression: handleStatelessComponent, FunctionExpression: handleStatelessComponent, 'Program:exit': function() { + stack = null; const list = components.list(); for (const component in list) { From 31e4f333ef9f46493dad510483fef38cd8c62f74 Mon Sep 17 00:00:00 2001 From: Yannick Croissant Date: Mon, 21 May 2018 20:13:00 +0200 Subject: [PATCH 06/34] Fix require-default-props type annotations detection with ESLint 3 --- lib/rules/require-default-props.js | 38 +++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/lib/rules/require-default-props.js b/lib/rules/require-default-props.js index f5cbf8695a..6bb15c4f39 100644 --- a/lib/rules/require-default-props.js +++ b/lib/rules/require-default-props.js @@ -42,6 +42,9 @@ module.exports = { const propWrapperFunctions = new Set(context.settings.propWrapperFunctions || []); const configuration = context.options[0] || {}; const forbidDefaultForRequired = configuration.forbidDefaultForRequired || false; + // Used to track the type annotations in scope. + // Necessary because babel's scopes do not track type annotations. + let stack = null; /** * Try to resolve the node passed in to a variable in the current scope. If the node passed in is not @@ -64,6 +67,22 @@ module.exports = { return node; } + /** + * Helper for accessing the current scope in the stack. + * @param {string} key The name of the identifier to access. If omitted, returns the full scope. + * @param {ASTNode} value If provided sets the new value for the identifier. + * @returns {Object|ASTNode} Either the whole scope or the ASTNode associated with the given identifier. + */ + function typeScope(key, value) { + if (arguments.length === 0) { + return stack[stack.length - 1]; + } else if (arguments.length === 1) { + return stack[stack.length - 1][key]; + } + stack[stack.length - 1][key] = value; + return value; + } + /** * Tries to find the definition of a GenericTypeAnnotation in the current scope. * @param {ASTNode} node The node GenericTypeAnnotation node to resolve. @@ -74,7 +93,7 @@ module.exports = { return null; } - return variableUtil.findVariableByName(context, node.id.name); + return variableUtil.findVariableByName(context, node.id.name) || typeScope(node.id.name); } function resolveUnionTypeAnnotation(node) { @@ -563,6 +582,22 @@ module.exports = { }); }, + TypeAlias: function(node) { + typeScope(node.id.name, node.right); + }, + + Program: function() { + stack = [{}]; + }, + + BlockStatement: function () { + stack.push(Object.create(typeScope())); + }, + + 'BlockStatement:exit': function () { + stack.pop(); + }, + // e.g.: // type HelloProps = { // foo?: string @@ -588,6 +623,7 @@ module.exports = { FunctionExpression: handleStatelessComponent, 'Program:exit': function() { + stack = null; const list = components.list(); for (const component in list) { From 677e1bd41e1711d034dddbf2cbb28698e73a72ec Mon Sep 17 00:00:00 2001 From: Yannick Croissant Date: Mon, 21 May 2018 20:19:45 +0200 Subject: [PATCH 07/34] Fix jsx-sort-props auto fix with ESLint 3 --- lib/rules/jsx-sort-props.js | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/lib/rules/jsx-sort-props.js b/lib/rules/jsx-sort-props.js index 9b9435b49c..dcabaa4523 100644 --- a/lib/rules/jsx-sort-props.js +++ b/lib/rules/jsx-sort-props.js @@ -107,19 +107,30 @@ const generateFixerFunction = (node, context, reservedList) => { return function(fixer) { const fixers = []; + let source = sourceCode.getText(); // Replace each unsorted attribute with the sorted one. sortableAttributeGroups.forEach((sortableGroup, ii) => { sortableGroup.forEach((attr, jj) => { const sortedAttr = sortedAttributeGroups[ii][jj]; const sortedAttrText = sourceCode.getText(sortedAttr); - fixers.push( - fixer.replaceTextRange([attr.range[0], attr.range[1]], sortedAttrText) - ); + fixers.push({ + range: [attr.range[0], attr.range[1]], + text: sortedAttrText + }); }); }); - return fixers; + fixers.sort((a, b) => a.range[0] < b.range[0]); + + const rangeStart = fixers[fixers.length - 1].range[0]; + const rangeEnd = fixers[0].range[1]; + + fixers.forEach(fix => { + source = `${source.substr(0, fix.range[0])}${fix.text}${source.substr(fix.range[1])}`; + }); + + return fixer.replaceTextRange([rangeStart, rangeEnd], source.substr(rangeStart, rangeEnd - rangeStart)); }; }; From cb4d21ea61cafe2593bd6bc70fcbd8630377f86d Mon Sep 17 00:00:00 2001 From: Yannick Croissant Date: Mon, 21 May 2018 20:48:48 +0200 Subject: [PATCH 08/34] Run tests against different ESLint versions on Travis --- .travis.yml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/.travis.yml b/.travis.yml index a3ec69c444..b4715855e7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,6 +9,8 @@ node_js: - '4' before_install: - 'nvm install-latest-npm' +before_script: + - 'if [ -n "${ESLINT-}" ]; then npm install --no-save "eslint@${ESLINT}" ; fi' script: - 'if [ -n "${PRETEST-}" ]; then npm run pretest ; fi' - 'if [ -n "${TEST-}" ]; then npm run unit-test ; fi' @@ -21,7 +23,28 @@ matrix: include: - node_js: 'lts/*' env: PRETEST=true + - node_js: '9' + env: TEST=true ESLINT=next + - node_js: '8' + env: TEST=true ESLINT=next + - node_js: '7' + env: TEST=true ESLINT=next + - node_js: '6' + env: TEST=true ESLINT=next + - node_js: '9' + env: TEST=true ESLINT=3 + - node_js: '8' + env: TEST=true ESLINT=3 + - node_js: '7' + env: TEST=true ESLINT=3 + - node_js: '6' + env: TEST=true ESLINT=3 + - node_js: '5' + env: TEST=true ESLINT=3 + - node_js: '4' + env: TEST=true ESLINT=3 allow_failures: - node_js: '9' - node_js: '7' - node_js: '5' + - env: TEST=true ESLINT=next From 589231a105602654f0e99ea2135cc9d2d1b8a56b Mon Sep 17 00:00:00 2001 From: Yannick Croissant Date: Tue, 29 May 2018 00:49:19 +0200 Subject: [PATCH 09/34] Add SpreadElement support to boolean-prop-naming --- lib/rules/boolean-prop-naming.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/rules/boolean-prop-naming.js b/lib/rules/boolean-prop-naming.js index 3d414a724c..fb8b204a3a 100644 --- a/lib/rules/boolean-prop-naming.js +++ b/lib/rules/boolean-prop-naming.js @@ -68,9 +68,10 @@ module.exports = { * @param {Object} node The node we're getting the name of */ function getPropKey(node) { - // Check for `ExperimentalSpreadProperty` so we can skip validation of those fields. - // Otherwise it will look for `node.value.property` which doesn't exist and breaks Eslint. - if (node.type === 'ExperimentalSpreadProperty') { + // Check for `ExperimentalSpreadProperty` (ESLint 3/4) and `SpreadElement` (ESLint 5) + // so we can skip validation of those fields. + // Otherwise it will look for `node.value.property` which doesn't exist and breaks ESLint. + if (node.type === 'ExperimentalSpreadProperty' || node.type === 'SpreadElement') { return null; } if (node.value.property) { From e6e8955d4420ba1c574065104a660615b16685ee Mon Sep 17 00:00:00 2001 From: Yannick Croissant Date: Tue, 29 May 2018 00:49:56 +0200 Subject: [PATCH 10/34] Add SpreadElement support to default-props-match-prop-types --- lib/rules/default-props-match-prop-types.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/rules/default-props-match-prop-types.js b/lib/rules/default-props-match-prop-types.js index cc0992ec53..79723f18a8 100644 --- a/lib/rules/default-props-match-prop-types.js +++ b/lib/rules/default-props-match-prop-types.js @@ -111,7 +111,7 @@ module.exports = { * @returns {Object[]} Array of PropType object representations, to be consumed by `addPropTypesToComponent`. */ function getPropTypesFromObjectExpression(objectExpression) { - const props = objectExpression.properties.filter(property => property.type !== 'ExperimentalSpreadProperty'); + const props = objectExpression.properties.filter(property => property.type !== 'ExperimentalSpreadProperty' && property.type !== 'SpreadElement'); return props.map(property => ({ name: property.key.name, @@ -209,7 +209,7 @@ module.exports = { * from this ObjectExpression can't be resolved. */ function getDefaultPropsFromObjectExpression(objectExpression) { - const hasSpread = objectExpression.properties.find(property => property.type === 'ExperimentalSpreadProperty'); + const hasSpread = objectExpression.properties.find(property => property.type === 'ExperimentalSpreadProperty' || property.type === 'SpreadElement'); if (hasSpread) { return 'unresolved'; @@ -543,7 +543,7 @@ module.exports = { // Search for the proptypes declaration node.properties.forEach(property => { - if (property.type === 'ExperimentalSpreadProperty') { + if (property.type === 'ExperimentalSpreadProperty' || property.type === 'SpreadElement') { return; } From 3762258696c0aad147eb3f38bf2579155dc98769 Mon Sep 17 00:00:00 2001 From: Yannick Croissant Date: Tue, 29 May 2018 00:50:25 +0200 Subject: [PATCH 11/34] Add JSXText support to jsx-child-element-spacing --- lib/rules/jsx-child-element-spacing.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rules/jsx-child-element-spacing.js b/lib/rules/jsx-child-element-spacing.js index 838d9de091..d6c03c59bf 100644 --- a/lib/rules/jsx-child-element-spacing.js +++ b/lib/rules/jsx-child-element-spacing.js @@ -79,7 +79,7 @@ module.exports = { if ( (lastChild || nextChild) && (!lastChild || isInlineElement(lastChild)) && - (child && child.type === 'Literal') && + (child && (child.type === 'Literal' || child.type === 'JSXText')) && (!nextChild || isInlineElement(nextChild)) && true ) { From c734901a3fe7802c801313ef1467043baed17846 Mon Sep 17 00:00:00 2001 From: Yannick Croissant Date: Tue, 29 May 2018 00:50:42 +0200 Subject: [PATCH 12/34] Add JSXText support to jsx-curly-brace-presence --- lib/rules/jsx-curly-brace-presence.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/rules/jsx-curly-brace-presence.js b/lib/rules/jsx-curly-brace-presence.js index 27ba7fd5f8..4efdb48a6a 100644 --- a/lib/rules/jsx-curly-brace-presence.js +++ b/lib/rules/jsx-curly-brace-presence.js @@ -250,6 +250,12 @@ module.exports = { if (shouldCheckForMissingCurly(node.parent, userConfig)) { reportMissingCurly(node); } + }, + + JSXText: node => { + if (shouldCheckForMissingCurly(node.parent, userConfig)) { + reportMissingCurly(node); + } } }; } From 0a6cb515a4bd2fa0c7d87d5ab78a233df6902194 Mon Sep 17 00:00:00 2001 From: Yannick Croissant Date: Tue, 29 May 2018 00:50:58 +0200 Subject: [PATCH 13/34] Add JSXText support to jsx-indent --- lib/rules/jsx-indent.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rules/jsx-indent.js b/lib/rules/jsx-indent.js index 21ae3683be..cbb9dac84e 100644 --- a/lib/rules/jsx-indent.js +++ b/lib/rules/jsx-indent.js @@ -214,7 +214,7 @@ module.exports = { // Use the parent in a list or an array if (prevToken.type === 'JSXText' || prevToken.type === 'Punctuator' && prevToken.value === ',') { prevToken = sourceCode.getNodeByRangeIndex(prevToken.range[0]); - prevToken = prevToken.type === 'Literal' ? prevToken.parent : prevToken; + prevToken = prevToken.type === 'Literal' || prevToken.type === 'JSXText' ? prevToken.parent : prevToken; // Use the first non-punctuator token in a conditional expression } else if (prevToken.type === 'Punctuator' && prevToken.value === ':') { do { From bd6caf0ad97e6c55ad7cce80b974d17d3833ee50 Mon Sep 17 00:00:00 2001 From: Yannick Croissant Date: Tue, 29 May 2018 00:51:16 +0200 Subject: [PATCH 14/34] Add JSXText support to jsx-no-literals --- lib/rules/jsx-no-literals.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/rules/jsx-no-literals.js b/lib/rules/jsx-no-literals.js index 628f8c55d9..d421e4748b 100644 --- a/lib/rules/jsx-no-literals.js +++ b/lib/rules/jsx-no-literals.js @@ -77,6 +77,12 @@ module.exports = { } }, + JSXText: function(node) { + if (getValidation(node)) { + reportLiteralNode(node); + } + }, + TemplateLiteral: function(node) { const parent = getParentIgnoringBinaryExpressions(node); if (isNoStrings && parent.type === 'JSXExpressionContainer') { From 0a028e4c605d4e35ca8ac6b04465fdbccbaeb89f Mon Sep 17 00:00:00 2001 From: Yannick Croissant Date: Tue, 29 May 2018 00:51:36 +0200 Subject: [PATCH 15/34] Add JSXText support to jsx-one-expression-per-line --- lib/rules/jsx-one-expression-per-line.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/rules/jsx-one-expression-per-line.js b/lib/rules/jsx-one-expression-per-line.js index 4e815c0013..7be01233e1 100644 --- a/lib/rules/jsx-one-expression-per-line.js +++ b/lib/rules/jsx-one-expression-per-line.js @@ -54,7 +54,7 @@ module.exports = { let countNewLinesBeforeContent = 0; let countNewLinesAfterContent = 0; - if (child.type === 'Literal') { + if (child.type === 'Literal' || child.type === 'JSXText') { if (/^\s*$/.test(child.raw)) { return; } @@ -110,14 +110,14 @@ module.exports = { } function spaceBetweenPrev () { - return (prevChild.type === 'Literal' && / $/.test(prevChild.raw)) || - (child.type === 'Literal' && /^ /.test(child.raw)) || + return ((prevChild.type === 'Literal' || prevChild.type === 'JSXText') && / $/.test(prevChild.raw)) || + ((child.type === 'Literal' || child.type === 'JSXText') && /^ /.test(child.raw)) || sourceCode.isSpaceBetweenTokens(prevChild, child); } function spaceBetweenNext () { - return (nextChild.type === 'Literal' && /^ /.test(nextChild.raw)) || - (child.type === 'Literal' && / $/.test(child.raw)) || + return ((nextChild.type === 'Literal' || nextChild.type === 'JSXText') && /^ /.test(nextChild.raw)) || + ((child.type === 'Literal' || child.type === 'JSXText') && / $/.test(child.raw)) || sourceCode.isSpaceBetweenTokens(child, nextChild); } From ed3370b4bf7c02ac656aa3d8c5855c2b980b6073 Mon Sep 17 00:00:00 2001 From: Yannick Croissant Date: Tue, 29 May 2018 00:51:58 +0200 Subject: [PATCH 16/34] Add SpreadElement and JSXText support to no-danger-with-children --- lib/rules/no-danger-with-children.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/rules/no-danger-with-children.js b/lib/rules/no-danger-with-children.js index b96e3eb6c1..d03546537c 100644 --- a/lib/rules/no-danger-with-children.js +++ b/lib/rules/no-danger-with-children.js @@ -36,7 +36,7 @@ module.exports = { return node.properties.find(prop => { if (prop.type === 'Property') { return prop.key.name === propName; - } else if (prop.type === 'ExperimentalSpreadProperty') { + } else if (prop.type === 'ExperimentalSpreadProperty' || prop.type === 'SpreadElement') { const variable = findSpreadVariable(prop.argument.name); if (variable && variable.defs.length && variable.defs[0].node.init) { if (seenProps.indexOf(prop.argument.name) > -1) { @@ -74,7 +74,7 @@ module.exports = { * @returns {Boolean} True if node is a line break, false if not */ function isLineBreak(node) { - const isLiteral = node.type === 'Literal'; + const isLiteral = node.type === 'Literal' || node.type === 'JSXText'; const isMultiline = node.loc.start.line !== node.loc.end.line; const isWhiteSpaces = /^\s*$/.test(node.value); From ce1fec734e0065101bffa469128ad47915c2e02c Mon Sep 17 00:00:00 2001 From: Yannick Croissant Date: Tue, 29 May 2018 00:52:36 +0200 Subject: [PATCH 17/34] Add JSXText support to no-unescaped-entities --- lib/rules/no-unescaped-entities.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/rules/no-unescaped-entities.js b/lib/rules/no-unescaped-entities.js index 8e4ca1f445..28de361579 100644 --- a/lib/rules/no-unescaped-entities.js +++ b/lib/rules/no-unescaped-entities.js @@ -72,7 +72,13 @@ module.exports = { return { Literal: function(node) { - if (node.type === 'Literal' && node.parent.type === 'JSXElement') { + if (node.parent.type === 'JSXElement') { + reportInvalidEntity(node); + } + }, + + JSXText: function(node) { + if (node.parent.type === 'JSXElement') { reportInvalidEntity(node); } } From b3ed9d65f107daa69283305afa873af1a24e5ddd Mon Sep 17 00:00:00 2001 From: Yannick Croissant Date: Tue, 29 May 2018 00:53:03 +0200 Subject: [PATCH 18/34] Add SpreadElement and RestElement support to no-unused-state --- lib/rules/no-unused-state.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/rules/no-unused-state.js b/lib/rules/no-unused-state.js index 88caa962aa..4d8348419c 100644 --- a/lib/rules/no-unused-state.js +++ b/lib/rules/no-unused-state.js @@ -136,7 +136,7 @@ module.exports = { if (prop.type === 'Property') { addUsedStateField(prop.key); } else if ( - prop.type === 'ExperimentalRestProperty' && + (prop.type === 'ExperimentalRestProperty' || prop.type === 'RestElement') && classInfo.aliases ) { classInfo.aliases.add(getName(prop.argument)); @@ -378,6 +378,12 @@ module.exports = { if (classInfo && isStateReference(node.argument)) { classInfo = null; } + }, + + SpreadElement(node) { + if (classInfo && isStateReference(node.argument)) { + classInfo = null; + } } }; }) From 632941cf2740437045a210214bd657bb32317a91 Mon Sep 17 00:00:00 2001 From: Yannick Croissant Date: Tue, 29 May 2018 00:53:24 +0200 Subject: [PATCH 19/34] Add SpreadElement support to require-default-props --- lib/rules/require-default-props.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/rules/require-default-props.js b/lib/rules/require-default-props.js index 6bb15c4f39..0704e8e323 100644 --- a/lib/rules/require-default-props.js +++ b/lib/rules/require-default-props.js @@ -113,7 +113,7 @@ module.exports = { * @returns {Object[]} Array of PropType object representations, to be consumed by `addPropTypesToComponent`. */ function getPropTypesFromObjectExpression(objectExpression) { - const props = objectExpression.properties.filter(property => property.type !== 'ExperimentalSpreadProperty'); + const props = objectExpression.properties.filter(property => property.type !== 'ExperimentalSpreadProperty' && property.type !== 'SpreadElement'); return props.map(property => ({ name: sourceCode.getText(property.key).replace(QUOTES_REGEX, ''), @@ -184,7 +184,7 @@ module.exports = { * from this ObjectExpression can't be resolved. */ function getDefaultPropsFromObjectExpression(objectExpression) { - const hasSpread = objectExpression.properties.find(property => property.type === 'ExperimentalSpreadProperty'); + const hasSpread = objectExpression.properties.find(property => property.type === 'ExperimentalSpreadProperty' || property.type === 'SpreadElement'); if (hasSpread) { return 'unresolved'; @@ -555,7 +555,7 @@ module.exports = { // Search for the proptypes declaration node.properties.forEach(property => { - if (property.type === 'ExperimentalSpreadProperty') { + if (property.type === 'ExperimentalSpreadProperty' || property.type === 'SpreadElement') { return; } From 29c248d1ac8346b52994485d72bf6185b4e68a24 Mon Sep 17 00:00:00 2001 From: Yannick Croissant Date: Tue, 29 May 2018 00:53:43 +0200 Subject: [PATCH 20/34] Add JSXText support to self-closing-comp --- lib/rules/self-closing-comp.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rules/self-closing-comp.js b/lib/rules/self-closing-comp.js index 43739f5e7f..e7ea52f5c6 100644 --- a/lib/rules/self-closing-comp.js +++ b/lib/rules/self-closing-comp.js @@ -52,7 +52,7 @@ module.exports = { const childrens = node.parent.children; if ( !childrens.length || - (childrens.length === 1 && childrens[0].type === 'Literal' && !childrens[0].value.replace(/(?!\xA0)\s/g, '')) + (childrens.length === 1 && (childrens[0].type === 'Literal' || childrens[0].type === 'JSXText') && !childrens[0].value.replace(/(?!\xA0)\s/g, '')) ) { return false; } From f2d8729e6ddc414d061988b8760d22729feec4fe Mon Sep 17 00:00:00 2001 From: Yannick Croissant Date: Tue, 29 May 2018 00:54:37 +0200 Subject: [PATCH 21/34] Add SpreadElement support to sort-prop-types --- lib/rules/sort-prop-types.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rules/sort-prop-types.js b/lib/rules/sort-prop-types.js index a56cf4b786..174ee43c50 100644 --- a/lib/rules/sort-prop-types.js +++ b/lib/rules/sort-prop-types.js @@ -84,7 +84,7 @@ module.exports = { } declarations.reduce((prev, curr, idx, decls) => { - if (/SpreadProperty$/.test(curr.type)) { + if (curr.type === 'ExperimentalSpreadProperty' || curr.type === 'SpreadElement') { return decls[idx + 1]; } From 9d4e27a78ebbc863f6e9a954867feefd80e5ca78 Mon Sep 17 00:00:00 2001 From: Yannick Croissant Date: Tue, 29 May 2018 01:14:11 +0200 Subject: [PATCH 22/34] Ignore no-typos test that only works with ESLint 5 --- tests/lib/rules/no-typos.js | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/tests/lib/rules/no-typos.js b/tests/lib/rules/no-typos.js index 0123327e1c..cccc87a434 100644 --- a/tests/lib/rules/no-typos.js +++ b/tests/lib/rules/no-typos.js @@ -241,15 +241,6 @@ ruleTester.run('no-typos', rule, { } `, parserOptions: parserOptions - }, { - // PropTypes declared on a component that is detected through JSDoc comments and is - // declared AFTER the PropTypes assignment does not work. - code: ` - MyComponent.PROPTYPES = {} - /** @extends React.Component */ - class MyComponent extends BaseComponent {} - `, - parserOptions: parserOptions }, { // https://github.com/yannickcr/eslint-plugin-react/issues/1353 code: ` @@ -1354,6 +1345,19 @@ ruleTester.run('no-typos', rule, { }] }] /* +// PropTypes declared on a component that is detected through JSDoc comments and is +// declared AFTER the PropTypes assignment +// Commented out since it only works with ESLint 5. + ,{ + code: ` + MyComponent.PROPTYPES = {} + \/** @extends React.Component *\/ + class MyComponent extends BaseComponent {} + `, + parserOptions: parserOptions + }, +*/ +/* // createClass tests below fail, so they're commented out // --------- }, { From ab6b41aee78e8a642084bd5657e1832162fe7a80 Mon Sep 17 00:00:00 2001 From: Yannick Croissant Date: Tue, 29 May 2018 14:44:02 +0200 Subject: [PATCH 23/34] Remove deprecated experimentalObjectRestSpread option from tests configuration --- tests/lib/rules/boolean-prop-naming.js | 3 +-- tests/lib/rules/button-has-type.js | 3 +-- tests/lib/rules/default-props-match-prop-types.js | 3 +-- tests/lib/rules/destructuring-assignment.js | 2 +- tests/lib/rules/display-name.js | 3 +-- tests/lib/rules/forbid-component-props.js | 3 +-- tests/lib/rules/forbid-dom-props.js | 3 +-- tests/lib/rules/forbid-elements.js | 3 +-- tests/lib/rules/forbid-foreign-prop-types.js | 3 +-- tests/lib/rules/forbid-prop-types.js | 3 +-- tests/lib/rules/jsx-boolean-value.js | 3 +-- tests/lib/rules/jsx-closing-bracket-location.js | 3 +-- tests/lib/rules/jsx-curly-spacing.js | 3 +-- tests/lib/rules/jsx-equals-spacing.js | 3 +-- tests/lib/rules/jsx-filename-extension.js | 3 +-- tests/lib/rules/jsx-first-prop-new-line.js | 3 +-- tests/lib/rules/jsx-handler-names.js | 3 +-- tests/lib/rules/jsx-indent-props.js | 3 +-- tests/lib/rules/jsx-indent.js | 3 +-- tests/lib/rules/jsx-key.js | 3 +-- tests/lib/rules/jsx-max-props-per-line.js | 3 +-- tests/lib/rules/jsx-no-bind.js | 3 +-- tests/lib/rules/jsx-no-comment-textnodes.js | 3 +-- tests/lib/rules/jsx-no-duplicate-props.js | 3 +-- tests/lib/rules/jsx-no-literals.js | 3 +-- tests/lib/rules/jsx-no-target-blank.js | 3 +-- tests/lib/rules/jsx-no-undef.js | 3 +-- tests/lib/rules/jsx-one-expression-per-line.js | 3 +-- tests/lib/rules/jsx-pascal-case.js | 3 +-- tests/lib/rules/jsx-props-no-multi-spaces.js | 3 +-- tests/lib/rules/jsx-sort-default-props.js | 3 +-- tests/lib/rules/jsx-sort-props.js | 3 +-- tests/lib/rules/jsx-space-before-closing.js | 3 +-- tests/lib/rules/jsx-tag-spacing.js | 3 +-- tests/lib/rules/jsx-uses-react.js | 3 +-- tests/lib/rules/jsx-uses-vars.js | 3 +-- tests/lib/rules/jsx-wrap-multilines.js | 3 +-- tests/lib/rules/no-access-state-in-setstate.js | 1 - tests/lib/rules/no-array-index-key.js | 3 +-- tests/lib/rules/no-children-prop.js | 3 +-- tests/lib/rules/no-danger-with-children.js | 3 +-- tests/lib/rules/no-danger.js | 3 +-- tests/lib/rules/no-deprecated.js | 3 +-- tests/lib/rules/no-did-mount-set-state.js | 3 +-- tests/lib/rules/no-did-update-set-state.js | 3 +-- tests/lib/rules/no-direct-mutation-state.js | 3 +-- tests/lib/rules/no-find-dom-node.js | 3 +-- tests/lib/rules/no-is-mounted.js | 3 +-- tests/lib/rules/no-multi-comp.js | 3 +-- tests/lib/rules/no-redundant-should-component-update.js | 3 +-- tests/lib/rules/no-render-return-value.js | 3 +-- tests/lib/rules/no-set-state.js | 3 +-- tests/lib/rules/no-string-refs.js | 3 +-- tests/lib/rules/no-this-in-sfc.js | 3 +-- tests/lib/rules/no-typos.js | 2 +- tests/lib/rules/no-unescaped-entities.js | 3 +-- tests/lib/rules/no-unknown-property.js | 3 +-- tests/lib/rules/no-unused-prop-types.js | 3 +-- tests/lib/rules/no-unused-state.js | 5 ++--- tests/lib/rules/no-will-update-set-state.js | 3 +-- tests/lib/rules/prefer-es6-class.js | 3 +-- tests/lib/rules/prefer-stateless-function.js | 3 +-- tests/lib/rules/prop-types.js | 3 +-- tests/lib/rules/react-in-jsx-scope.js | 3 +-- tests/lib/rules/require-default-props.js | 3 +-- tests/lib/rules/require-optimization.js | 3 +-- tests/lib/rules/require-render-return.js | 7 +++---- tests/lib/rules/self-closing-comp.js | 3 +-- tests/lib/rules/sort-comp.js | 3 +-- tests/lib/rules/sort-prop-types.js | 3 +-- tests/lib/rules/style-prop-object.js | 6 ++---- tests/lib/rules/void-dom-elements-no-children.js | 3 +-- 72 files changed, 75 insertions(+), 146 deletions(-) diff --git a/tests/lib/rules/boolean-prop-naming.js b/tests/lib/rules/boolean-prop-naming.js index e90fc39788..384557ec65 100644 --- a/tests/lib/rules/boolean-prop-naming.js +++ b/tests/lib/rules/boolean-prop-naming.js @@ -14,10 +14,9 @@ const RuleTester = require('eslint').RuleTester; require('babel-eslint'); const parserOptions = { - ecmaVersion: 6, + ecmaVersion: 2018, sourceType: 'module', ecmaFeatures: { - experimentalObjectRestSpread: true, jsx: true } }; diff --git a/tests/lib/rules/button-has-type.js b/tests/lib/rules/button-has-type.js index 151ffa9a2e..b4a4a85d26 100644 --- a/tests/lib/rules/button-has-type.js +++ b/tests/lib/rules/button-has-type.js @@ -12,10 +12,9 @@ const rule = require('../../../lib/rules/button-has-type'); const RuleTester = require('eslint').RuleTester; const parserOptions = { - ecmaVersion: 8, + ecmaVersion: 2018, sourceType: 'module', ecmaFeatures: { - experimentalObjectRestSpread: true, jsx: true } }; diff --git a/tests/lib/rules/default-props-match-prop-types.js b/tests/lib/rules/default-props-match-prop-types.js index 7c70f99dc1..d8b65302af 100644 --- a/tests/lib/rules/default-props-match-prop-types.js +++ b/tests/lib/rules/default-props-match-prop-types.js @@ -15,10 +15,9 @@ const RuleTester = require('eslint').RuleTester; require('babel-eslint'); const parserOptions = { - ecmaVersion: 6, + ecmaVersion: 2018, sourceType: 'module', ecmaFeatures: { - experimentalObjectRestSpread: true, jsx: true } }; diff --git a/tests/lib/rules/destructuring-assignment.js b/tests/lib/rules/destructuring-assignment.js index f488239ad6..6ce0e36cfb 100644 --- a/tests/lib/rules/destructuring-assignment.js +++ b/tests/lib/rules/destructuring-assignment.js @@ -10,7 +10,7 @@ const RuleTester = require('eslint').RuleTester; require('babel-eslint'); const parserOptions = { - ecmaVersion: 6, + ecmaVersion: 2018, sourceType: 'module', ecmaFeatures: { jsx: true diff --git a/tests/lib/rules/display-name.js b/tests/lib/rules/display-name.js index b3a2bce5b8..9919867361 100644 --- a/tests/lib/rules/display-name.js +++ b/tests/lib/rules/display-name.js @@ -14,10 +14,9 @@ const RuleTester = require('eslint').RuleTester; require('babel-eslint'); const parserOptions = { - ecmaVersion: 8, + ecmaVersion: 2018, sourceType: 'module', ecmaFeatures: { - experimentalObjectRestSpread: true, jsx: true } }; diff --git a/tests/lib/rules/forbid-component-props.js b/tests/lib/rules/forbid-component-props.js index 1c9fc6b805..01ea4b8c33 100644 --- a/tests/lib/rules/forbid-component-props.js +++ b/tests/lib/rules/forbid-component-props.js @@ -11,10 +11,9 @@ const rule = require('../../../lib/rules/forbid-component-props'); const RuleTester = require('eslint').RuleTester; const parserOptions = { - ecmaVersion: 8, + ecmaVersion: 2018, sourceType: 'module', ecmaFeatures: { - experimentalObjectRestSpread: true, jsx: true } }; diff --git a/tests/lib/rules/forbid-dom-props.js b/tests/lib/rules/forbid-dom-props.js index 0e2b021ee8..ca02b49002 100644 --- a/tests/lib/rules/forbid-dom-props.js +++ b/tests/lib/rules/forbid-dom-props.js @@ -11,10 +11,9 @@ const rule = require('../../../lib/rules/forbid-dom-props'); const RuleTester = require('eslint').RuleTester; const parserOptions = { - ecmaVersion: 8, + ecmaVersion: 2018, sourceType: 'module', ecmaFeatures: { - experimentalObjectRestSpread: true, jsx: true } }; diff --git a/tests/lib/rules/forbid-elements.js b/tests/lib/rules/forbid-elements.js index f9d0ca56c3..dfd155c94e 100644 --- a/tests/lib/rules/forbid-elements.js +++ b/tests/lib/rules/forbid-elements.js @@ -11,10 +11,9 @@ const rule = require('../../../lib/rules/forbid-elements'); const RuleTester = require('eslint').RuleTester; const parserOptions = { - ecmaVersion: 8, + ecmaVersion: 2018, sourceType: 'module', ecmaFeatures: { - experimentalObjectRestSpread: true, jsx: true } }; diff --git a/tests/lib/rules/forbid-foreign-prop-types.js b/tests/lib/rules/forbid-foreign-prop-types.js index 8c081b2df6..bcf6c2df5a 100644 --- a/tests/lib/rules/forbid-foreign-prop-types.js +++ b/tests/lib/rules/forbid-foreign-prop-types.js @@ -11,10 +11,9 @@ const rule = require('../../../lib/rules/forbid-foreign-prop-types'); const RuleTester = require('eslint').RuleTester; const parserOptions = { - ecmaVersion: 8, + ecmaVersion: 2018, sourceType: 'module', ecmaFeatures: { - experimentalObjectRestSpread: true, jsx: true } }; diff --git a/tests/lib/rules/forbid-prop-types.js b/tests/lib/rules/forbid-prop-types.js index f79703d3de..7006d3f023 100644 --- a/tests/lib/rules/forbid-prop-types.js +++ b/tests/lib/rules/forbid-prop-types.js @@ -11,10 +11,9 @@ const rule = require('../../../lib/rules/forbid-prop-types'); const RuleTester = require('eslint').RuleTester; const parserOptions = { - ecmaVersion: 8, + ecmaVersion: 2018, sourceType: 'module', ecmaFeatures: { - experimentalObjectRestSpread: true, jsx: true } }; diff --git a/tests/lib/rules/jsx-boolean-value.js b/tests/lib/rules/jsx-boolean-value.js index 90c0d7cc3d..eb52994c02 100644 --- a/tests/lib/rules/jsx-boolean-value.js +++ b/tests/lib/rules/jsx-boolean-value.js @@ -12,10 +12,9 @@ const rule = require('../../../lib/rules/jsx-boolean-value'); const RuleTester = require('eslint').RuleTester; const parserOptions = { - ecmaVersion: 8, + ecmaVersion: 2018, sourceType: 'module', ecmaFeatures: { - experimentalObjectRestSpread: true, jsx: true } }; diff --git a/tests/lib/rules/jsx-closing-bracket-location.js b/tests/lib/rules/jsx-closing-bracket-location.js index ab433ae986..7d9f64cafb 100644 --- a/tests/lib/rules/jsx-closing-bracket-location.js +++ b/tests/lib/rules/jsx-closing-bracket-location.js @@ -11,10 +11,9 @@ const rule = require('../../../lib/rules/jsx-closing-bracket-location'); const RuleTester = require('eslint').RuleTester; const parserOptions = { - ecmaVersion: 8, + ecmaVersion: 2018, sourceType: 'module', ecmaFeatures: { - experimentalObjectRestSpread: true, jsx: true } }; diff --git a/tests/lib/rules/jsx-curly-spacing.js b/tests/lib/rules/jsx-curly-spacing.js index 3fa35be89c..1447b7a487 100644 --- a/tests/lib/rules/jsx-curly-spacing.js +++ b/tests/lib/rules/jsx-curly-spacing.js @@ -12,10 +12,9 @@ const rule = require('../../../lib/rules/jsx-curly-spacing'); const RuleTester = require('eslint').RuleTester; const parserOptions = { - ecmaVersion: 8, + ecmaVersion: 2018, sourceType: 'module', ecmaFeatures: { - experimentalObjectRestSpread: true, jsx: true } }; diff --git a/tests/lib/rules/jsx-equals-spacing.js b/tests/lib/rules/jsx-equals-spacing.js index af71e1919b..9da8042580 100644 --- a/tests/lib/rules/jsx-equals-spacing.js +++ b/tests/lib/rules/jsx-equals-spacing.js @@ -12,10 +12,9 @@ const rule = require('../../../lib/rules/jsx-equals-spacing'); const RuleTester = require('eslint').RuleTester; const parserOptions = { - ecmaVersion: 8, + ecmaVersion: 2018, sourceType: 'module', ecmaFeatures: { - experimentalObjectRestSpread: true, jsx: true } }; diff --git a/tests/lib/rules/jsx-filename-extension.js b/tests/lib/rules/jsx-filename-extension.js index d05f56a146..02a2a616a3 100644 --- a/tests/lib/rules/jsx-filename-extension.js +++ b/tests/lib/rules/jsx-filename-extension.js @@ -12,10 +12,9 @@ const rule = require('../../../lib/rules/jsx-filename-extension'); const RuleTester = require('eslint').RuleTester; const parserOptions = { - ecmaVersion: 8, + ecmaVersion: 2018, sourceType: 'module', ecmaFeatures: { - experimentalObjectRestSpread: true, jsx: true } }; diff --git a/tests/lib/rules/jsx-first-prop-new-line.js b/tests/lib/rules/jsx-first-prop-new-line.js index 8a8c80a1f9..6a1f8b2b0b 100644 --- a/tests/lib/rules/jsx-first-prop-new-line.js +++ b/tests/lib/rules/jsx-first-prop-new-line.js @@ -12,10 +12,9 @@ const rule = require('../../../lib/rules/jsx-first-prop-new-line'); const RuleTester = require('eslint').RuleTester; const parserOptions = { - ecmaVersion: 8, + ecmaVersion: 2018, sourceType: 'module', ecmaFeatures: { - experimentalObjectRestSpread: true, jsx: true } }; diff --git a/tests/lib/rules/jsx-handler-names.js b/tests/lib/rules/jsx-handler-names.js index 720ae445aa..031b4703f2 100644 --- a/tests/lib/rules/jsx-handler-names.js +++ b/tests/lib/rules/jsx-handler-names.js @@ -12,10 +12,9 @@ const rule = require('../../../lib/rules/jsx-handler-names'); const RuleTester = require('eslint').RuleTester; const parserOptions = { - ecmaVersion: 8, + ecmaVersion: 2018, sourceType: 'module', ecmaFeatures: { - experimentalObjectRestSpread: true, jsx: true } }; diff --git a/tests/lib/rules/jsx-indent-props.js b/tests/lib/rules/jsx-indent-props.js index c4e18dd2be..dd85753ec4 100644 --- a/tests/lib/rules/jsx-indent-props.js +++ b/tests/lib/rules/jsx-indent-props.js @@ -12,10 +12,9 @@ const rule = require('../../../lib/rules/jsx-indent-props'); const RuleTester = require('eslint').RuleTester; const parserOptions = { - ecmaVersion: 8, + ecmaVersion: 2018, sourceType: 'module', ecmaFeatures: { - experimentalObjectRestSpread: true, jsx: true } }; diff --git a/tests/lib/rules/jsx-indent.js b/tests/lib/rules/jsx-indent.js index 253f7e9cef..ac8d2b6beb 100644 --- a/tests/lib/rules/jsx-indent.js +++ b/tests/lib/rules/jsx-indent.js @@ -12,10 +12,9 @@ const rule = require('../../../lib/rules/jsx-indent'); const RuleTester = require('eslint').RuleTester; const parserOptions = { - ecmaVersion: 8, + ecmaVersion: 2018, sourceType: 'module', ecmaFeatures: { - experimentalObjectRestSpread: true, jsx: true } }; diff --git a/tests/lib/rules/jsx-key.js b/tests/lib/rules/jsx-key.js index 4bea4bd5ee..c0c3d9c628 100644 --- a/tests/lib/rules/jsx-key.js +++ b/tests/lib/rules/jsx-key.js @@ -12,10 +12,9 @@ const rule = require('../../../lib/rules/jsx-key'); const RuleTester = require('eslint').RuleTester; const parserOptions = { - ecmaVersion: 8, + ecmaVersion: 2018, sourceType: 'module', ecmaFeatures: { - experimentalObjectRestSpread: true, jsx: true } }; diff --git a/tests/lib/rules/jsx-max-props-per-line.js b/tests/lib/rules/jsx-max-props-per-line.js index 7c5ae4817b..177ce9bbc7 100644 --- a/tests/lib/rules/jsx-max-props-per-line.js +++ b/tests/lib/rules/jsx-max-props-per-line.js @@ -12,10 +12,9 @@ const rule = require('../../../lib/rules/jsx-max-props-per-line'); const RuleTester = require('eslint').RuleTester; const parserOptions = { - ecmaVersion: 8, + ecmaVersion: 2018, sourceType: 'module', ecmaFeatures: { - experimentalObjectRestSpread: true, jsx: true } }; diff --git a/tests/lib/rules/jsx-no-bind.js b/tests/lib/rules/jsx-no-bind.js index 05f8b264d7..89312b068f 100644 --- a/tests/lib/rules/jsx-no-bind.js +++ b/tests/lib/rules/jsx-no-bind.js @@ -13,10 +13,9 @@ const rule = require('../../../lib/rules/jsx-no-bind'); const RuleTester = require('eslint').RuleTester; const parserOptions = { - ecmaVersion: 8, + ecmaVersion: 2018, sourceType: 'module', ecmaFeatures: { - experimentalObjectRestSpread: true, jsx: true } }; diff --git a/tests/lib/rules/jsx-no-comment-textnodes.js b/tests/lib/rules/jsx-no-comment-textnodes.js index 2638ad89b4..5691d1aaf1 100644 --- a/tests/lib/rules/jsx-no-comment-textnodes.js +++ b/tests/lib/rules/jsx-no-comment-textnodes.js @@ -12,10 +12,9 @@ const rule = require('../../../lib/rules/jsx-no-comment-textnodes'); const RuleTester = require('eslint').RuleTester; const parserOptions = { - ecmaVersion: 8, + ecmaVersion: 2018, sourceType: 'module', ecmaFeatures: { - experimentalObjectRestSpread: true, jsx: true } }; diff --git a/tests/lib/rules/jsx-no-duplicate-props.js b/tests/lib/rules/jsx-no-duplicate-props.js index c3e544cd1b..870d5b1fd4 100644 --- a/tests/lib/rules/jsx-no-duplicate-props.js +++ b/tests/lib/rules/jsx-no-duplicate-props.js @@ -13,10 +13,9 @@ const rule = require('../../../lib/rules/jsx-no-duplicate-props'); const RuleTester = require('eslint').RuleTester; const parserOptions = { - ecmaVersion: 8, + ecmaVersion: 2018, sourceType: 'module', ecmaFeatures: { - experimentalObjectRestSpread: true, jsx: true } }; diff --git a/tests/lib/rules/jsx-no-literals.js b/tests/lib/rules/jsx-no-literals.js index ac77c4375d..869466c265 100644 --- a/tests/lib/rules/jsx-no-literals.js +++ b/tests/lib/rules/jsx-no-literals.js @@ -13,10 +13,9 @@ const rule = require('../../../lib/rules/jsx-no-literals'); const RuleTester = require('eslint').RuleTester; const parserOptions = { - ecmaVersion: 8, + ecmaVersion: 2018, sourceType: 'module', ecmaFeatures: { - experimentalObjectRestSpread: true, jsx: true } }; diff --git a/tests/lib/rules/jsx-no-target-blank.js b/tests/lib/rules/jsx-no-target-blank.js index aba9d080bb..4fbbb1e0d3 100644 --- a/tests/lib/rules/jsx-no-target-blank.js +++ b/tests/lib/rules/jsx-no-target-blank.js @@ -12,10 +12,9 @@ const rule = require('../../../lib/rules/jsx-no-target-blank'); const RuleTester = require('eslint').RuleTester; const parserOptions = { - ecmaVersion: 8, + ecmaVersion: 2018, sourceType: 'module', ecmaFeatures: { - experimentalObjectRestSpread: true, jsx: true } }; diff --git a/tests/lib/rules/jsx-no-undef.js b/tests/lib/rules/jsx-no-undef.js index d81791337e..8644042777 100644 --- a/tests/lib/rules/jsx-no-undef.js +++ b/tests/lib/rules/jsx-no-undef.js @@ -14,9 +14,8 @@ const rule = require('../../../lib/rules/jsx-no-undef'); const RuleTester = eslint.RuleTester; const parserOptions = { - ecmaVersion: 8, + ecmaVersion: 2018, ecmaFeatures: { - experimentalObjectRestSpread: true, jsx: true } }; diff --git a/tests/lib/rules/jsx-one-expression-per-line.js b/tests/lib/rules/jsx-one-expression-per-line.js index c3de187517..173d611554 100644 --- a/tests/lib/rules/jsx-one-expression-per-line.js +++ b/tests/lib/rules/jsx-one-expression-per-line.js @@ -13,10 +13,9 @@ const rule = require('../../../lib/rules/jsx-one-expression-per-line'); const RuleTester = require('eslint').RuleTester; const parserOptions = { - ecmaVersion: 8, + ecmaVersion: 2018, sourceType: 'module', ecmaFeatures: { - experimentalObjectRestSpread: true, jsx: true } }; diff --git a/tests/lib/rules/jsx-pascal-case.js b/tests/lib/rules/jsx-pascal-case.js index 06a7b93f80..2a21bf949c 100644 --- a/tests/lib/rules/jsx-pascal-case.js +++ b/tests/lib/rules/jsx-pascal-case.js @@ -12,10 +12,9 @@ const rule = require('../../../lib/rules/jsx-pascal-case'); const RuleTester = require('eslint').RuleTester; const parserOptions = { - ecmaVersion: 8, + ecmaVersion: 2018, sourceType: 'module', ecmaFeatures: { - experimentalObjectRestSpread: true, jsx: true } }; diff --git a/tests/lib/rules/jsx-props-no-multi-spaces.js b/tests/lib/rules/jsx-props-no-multi-spaces.js index 10b114837e..bb0834ad87 100644 --- a/tests/lib/rules/jsx-props-no-multi-spaces.js +++ b/tests/lib/rules/jsx-props-no-multi-spaces.js @@ -12,10 +12,9 @@ const rule = require('../../../lib/rules/jsx-props-no-multi-spaces'); const RuleTester = require('eslint').RuleTester; const parserOptions = { - ecmaVersion: 8, + ecmaVersion: 2018, sourceType: 'module', ecmaFeatures: { - experimentalObjectRestSpread: true, jsx: true } }; diff --git a/tests/lib/rules/jsx-sort-default-props.js b/tests/lib/rules/jsx-sort-default-props.js index c4090487ed..508ffe80d5 100644 --- a/tests/lib/rules/jsx-sort-default-props.js +++ b/tests/lib/rules/jsx-sort-default-props.js @@ -12,10 +12,9 @@ const rule = require('../../../lib/rules/jsx-sort-default-props'); const RuleTester = require('eslint').RuleTester; const parserOptions = { - ecmaVersion: 8, + ecmaVersion: 2018, sourceType: 'module', ecmaFeatures: { - experimentalObjectRestSpread: true, jsx: true } }; diff --git a/tests/lib/rules/jsx-sort-props.js b/tests/lib/rules/jsx-sort-props.js index 7a633f1aaf..412ef4aefd 100644 --- a/tests/lib/rules/jsx-sort-props.js +++ b/tests/lib/rules/jsx-sort-props.js @@ -13,10 +13,9 @@ const rule = require('../../../lib/rules/jsx-sort-props'); const RuleTester = require('eslint').RuleTester; const parserOptions = { - ecmaVersion: 8, + ecmaVersion: 2018, sourceType: 'module', ecmaFeatures: { - experimentalObjectRestSpread: true, jsx: true } }; diff --git a/tests/lib/rules/jsx-space-before-closing.js b/tests/lib/rules/jsx-space-before-closing.js index 36e58b229c..687b0df9ee 100644 --- a/tests/lib/rules/jsx-space-before-closing.js +++ b/tests/lib/rules/jsx-space-before-closing.js @@ -12,10 +12,9 @@ const rule = require('../../../lib/rules/jsx-space-before-closing'); const RuleTester = require('eslint').RuleTester; const parserOptions = { - ecmaVersion: 8, + ecmaVersion: 2018, sourceType: 'module', ecmaFeatures: { - experimentalObjectRestSpread: true, jsx: true } }; diff --git a/tests/lib/rules/jsx-tag-spacing.js b/tests/lib/rules/jsx-tag-spacing.js index 44f0ddf12c..030851493e 100644 --- a/tests/lib/rules/jsx-tag-spacing.js +++ b/tests/lib/rules/jsx-tag-spacing.js @@ -13,10 +13,9 @@ const rule = require('../../../lib/rules/jsx-tag-spacing'); const RuleTester = require('eslint').RuleTester; const parserOptions = { - ecmaVersion: 8, + ecmaVersion: 2018, sourceType: 'module', ecmaFeatures: { - experimentalObjectRestSpread: true, jsx: true } }; diff --git a/tests/lib/rules/jsx-uses-react.js b/tests/lib/rules/jsx-uses-react.js index ac21864b2a..1cb516ba18 100644 --- a/tests/lib/rules/jsx-uses-react.js +++ b/tests/lib/rules/jsx-uses-react.js @@ -14,10 +14,9 @@ const rule = require('eslint/lib/rules/no-unused-vars'); const RuleTester = eslint.RuleTester; const parserOptions = { - ecmaVersion: 8, + ecmaVersion: 2018, sourceType: 'module', ecmaFeatures: { - experimentalObjectRestSpread: true, jsx: true } }; diff --git a/tests/lib/rules/jsx-uses-vars.js b/tests/lib/rules/jsx-uses-vars.js index 1918442dff..65fe6ec8ed 100644 --- a/tests/lib/rules/jsx-uses-vars.js +++ b/tests/lib/rules/jsx-uses-vars.js @@ -15,10 +15,9 @@ const rulePreferConst = require('eslint/lib/rules/prefer-const'); const RuleTester = eslint.RuleTester; const parserOptions = { - ecmaVersion: 8, + ecmaVersion: 2018, sourceType: 'module', ecmaFeatures: { - experimentalObjectRestSpread: true, jsx: true } }; diff --git a/tests/lib/rules/jsx-wrap-multilines.js b/tests/lib/rules/jsx-wrap-multilines.js index 14727136af..450ee782e6 100644 --- a/tests/lib/rules/jsx-wrap-multilines.js +++ b/tests/lib/rules/jsx-wrap-multilines.js @@ -12,10 +12,9 @@ const rule = require('../../../lib/rules/jsx-wrap-multilines'); const RuleTester = require('eslint').RuleTester; const parserOptions = { - ecmaVersion: 8, + ecmaVersion: 2018, sourceType: 'module', ecmaFeatures: { - experimentalObjectRestSpread: true, jsx: true } }; diff --git a/tests/lib/rules/no-access-state-in-setstate.js b/tests/lib/rules/no-access-state-in-setstate.js index 7ccd97cf86..580a48d5ff 100644 --- a/tests/lib/rules/no-access-state-in-setstate.js +++ b/tests/lib/rules/no-access-state-in-setstate.js @@ -14,7 +14,6 @@ const RuleTester = require('eslint').RuleTester; const parserOptions = { ecmaVersion: 2018, ecmaFeatures: { - experimentalObjectRestSpread: true, jsx: true } }; diff --git a/tests/lib/rules/no-array-index-key.js b/tests/lib/rules/no-array-index-key.js index 80aaf977cb..b27932ec67 100644 --- a/tests/lib/rules/no-array-index-key.js +++ b/tests/lib/rules/no-array-index-key.js @@ -13,10 +13,9 @@ const rule = require('../../../lib/rules/no-array-index-key'); const RuleTester = require('eslint').RuleTester; const parserOptions = { - ecmaVersion: 8, + ecmaVersion: 2018, sourceType: 'module', ecmaFeatures: { - experimentalObjectRestSpread: true, jsx: true } }; diff --git a/tests/lib/rules/no-children-prop.js b/tests/lib/rules/no-children-prop.js index b47e141e1b..33de83bfa6 100644 --- a/tests/lib/rules/no-children-prop.js +++ b/tests/lib/rules/no-children-prop.js @@ -13,10 +13,9 @@ const rule = require('../../../lib/rules/no-children-prop'); const RuleTester = require('eslint').RuleTester; const parserOptions = { - ecmaVersion: 8, + ecmaVersion: 2018, sourceType: 'module', ecmaFeatures: { - experimentalObjectRestSpread: true, jsx: true } }; diff --git a/tests/lib/rules/no-danger-with-children.js b/tests/lib/rules/no-danger-with-children.js index 34068fdb2b..73301db7dd 100644 --- a/tests/lib/rules/no-danger-with-children.js +++ b/tests/lib/rules/no-danger-with-children.js @@ -12,10 +12,9 @@ const rule = require('../../../lib/rules/no-danger-with-children'); const RuleTester = require('eslint').RuleTester; const parserOptions = { - ecmaVersion: 8, + ecmaVersion: 2018, sourceType: 'module', ecmaFeatures: { - experimentalObjectRestSpread: true, jsx: true } }; diff --git a/tests/lib/rules/no-danger.js b/tests/lib/rules/no-danger.js index a138144fa4..107c34ab2d 100644 --- a/tests/lib/rules/no-danger.js +++ b/tests/lib/rules/no-danger.js @@ -13,10 +13,9 @@ const rule = require('../../../lib/rules/no-danger'); const RuleTester = require('eslint').RuleTester; const parserOptions = { - ecmaVersion: 8, + ecmaVersion: 2018, sourceType: 'module', ecmaFeatures: { - experimentalObjectRestSpread: true, jsx: true } }; diff --git a/tests/lib/rules/no-deprecated.js b/tests/lib/rules/no-deprecated.js index a1165563d4..2472ce5848 100644 --- a/tests/lib/rules/no-deprecated.js +++ b/tests/lib/rules/no-deprecated.js @@ -14,10 +14,9 @@ const rule = require('../../../lib/rules/no-deprecated'); const RuleTester = require('eslint').RuleTester; const parserOptions = { - ecmaVersion: 8, + ecmaVersion: 2018, sourceType: 'module', ecmaFeatures: { - experimentalObjectRestSpread: true, jsx: true } }; diff --git a/tests/lib/rules/no-did-mount-set-state.js b/tests/lib/rules/no-did-mount-set-state.js index 8032da2a38..ae03823018 100644 --- a/tests/lib/rules/no-did-mount-set-state.js +++ b/tests/lib/rules/no-did-mount-set-state.js @@ -12,10 +12,9 @@ const rule = require('../../../lib/rules/no-did-mount-set-state'); const RuleTester = require('eslint').RuleTester; const parserOptions = { - ecmaVersion: 8, + ecmaVersion: 2018, sourceType: 'module', ecmaFeatures: { - experimentalObjectRestSpread: true, jsx: true } }; diff --git a/tests/lib/rules/no-did-update-set-state.js b/tests/lib/rules/no-did-update-set-state.js index 56de6d3306..dcf3c5ff05 100644 --- a/tests/lib/rules/no-did-update-set-state.js +++ b/tests/lib/rules/no-did-update-set-state.js @@ -12,10 +12,9 @@ const rule = require('../../../lib/rules/no-did-update-set-state'); const RuleTester = require('eslint').RuleTester; const parserOptions = { - ecmaVersion: 8, + ecmaVersion: 2018, sourceType: 'module', ecmaFeatures: { - experimentalObjectRestSpread: true, jsx: true } }; diff --git a/tests/lib/rules/no-direct-mutation-state.js b/tests/lib/rules/no-direct-mutation-state.js index b1e788992a..168aa568e8 100644 --- a/tests/lib/rules/no-direct-mutation-state.js +++ b/tests/lib/rules/no-direct-mutation-state.js @@ -12,10 +12,9 @@ const rule = require('../../../lib/rules/no-direct-mutation-state'); const RuleTester = require('eslint').RuleTester; const parserOptions = { - ecmaVersion: 8, + ecmaVersion: 2018, sourceType: 'module', ecmaFeatures: { - experimentalObjectRestSpread: true, jsx: true } }; diff --git a/tests/lib/rules/no-find-dom-node.js b/tests/lib/rules/no-find-dom-node.js index 07db36bc13..eff8dee27e 100644 --- a/tests/lib/rules/no-find-dom-node.js +++ b/tests/lib/rules/no-find-dom-node.js @@ -12,10 +12,9 @@ const rule = require('../../../lib/rules/no-find-dom-node'); const RuleTester = require('eslint').RuleTester; const parserOptions = { - ecmaVersion: 8, + ecmaVersion: 2018, sourceType: 'module', ecmaFeatures: { - experimentalObjectRestSpread: true, jsx: true } }; diff --git a/tests/lib/rules/no-is-mounted.js b/tests/lib/rules/no-is-mounted.js index 62b6b9c173..1c5180b28f 100644 --- a/tests/lib/rules/no-is-mounted.js +++ b/tests/lib/rules/no-is-mounted.js @@ -12,10 +12,9 @@ const rule = require('../../../lib/rules/no-is-mounted'); const RuleTester = require('eslint').RuleTester; const parserOptions = { - ecmaVersion: 8, + ecmaVersion: 2018, sourceType: 'module', ecmaFeatures: { - experimentalObjectRestSpread: true, jsx: true } }; diff --git a/tests/lib/rules/no-multi-comp.js b/tests/lib/rules/no-multi-comp.js index 572cc7c053..9f71cca02b 100644 --- a/tests/lib/rules/no-multi-comp.js +++ b/tests/lib/rules/no-multi-comp.js @@ -12,10 +12,9 @@ const rule = require('../../../lib/rules/no-multi-comp'); const RuleTester = require('eslint').RuleTester; const parserOptions = { - ecmaVersion: 8, + ecmaVersion: 2018, sourceType: 'module', ecmaFeatures: { - experimentalObjectRestSpread: true, jsx: true } }; diff --git a/tests/lib/rules/no-redundant-should-component-update.js b/tests/lib/rules/no-redundant-should-component-update.js index c66b21c3c8..48181ab3f3 100644 --- a/tests/lib/rules/no-redundant-should-component-update.js +++ b/tests/lib/rules/no-redundant-should-component-update.js @@ -12,9 +12,8 @@ const rule = require('../../../lib/rules/no-redundant-should-component-update'); const RuleTester = require('eslint').RuleTester; const parserOptions = { - ecmaVersion: 6, + ecmaVersion: 2018, ecmaFeatures: { - experimentalObjectRestSpread: true, jsx: true } }; diff --git a/tests/lib/rules/no-render-return-value.js b/tests/lib/rules/no-render-return-value.js index 7663710b22..210bb75ae4 100644 --- a/tests/lib/rules/no-render-return-value.js +++ b/tests/lib/rules/no-render-return-value.js @@ -12,10 +12,9 @@ const rule = require('../../../lib/rules/no-render-return-value'); const RuleTester = require('eslint').RuleTester; const parserOptions = { - ecmaVersion: 8, + ecmaVersion: 2018, sourceType: 'module', ecmaFeatures: { - experimentalObjectRestSpread: true, jsx: true } }; diff --git a/tests/lib/rules/no-set-state.js b/tests/lib/rules/no-set-state.js index ae09d2d3f0..2a1028e858 100644 --- a/tests/lib/rules/no-set-state.js +++ b/tests/lib/rules/no-set-state.js @@ -12,10 +12,9 @@ const rule = require('../../../lib/rules/no-set-state'); const RuleTester = require('eslint').RuleTester; const parserOptions = { - ecmaVersion: 8, + ecmaVersion: 2018, sourceType: 'module', ecmaFeatures: { - experimentalObjectRestSpread: true, jsx: true } }; diff --git a/tests/lib/rules/no-string-refs.js b/tests/lib/rules/no-string-refs.js index 530cc3cefd..41e2012ff5 100644 --- a/tests/lib/rules/no-string-refs.js +++ b/tests/lib/rules/no-string-refs.js @@ -12,10 +12,9 @@ const rule = require('../../../lib/rules/no-string-refs'); const RuleTester = require('eslint').RuleTester; const parserOptions = { - ecmaVersion: 8, + ecmaVersion: 2018, sourceType: 'module', ecmaFeatures: { - experimentalObjectRestSpread: true, jsx: true } }; diff --git a/tests/lib/rules/no-this-in-sfc.js b/tests/lib/rules/no-this-in-sfc.js index 350fb1fd71..026b44dc1a 100644 --- a/tests/lib/rules/no-this-in-sfc.js +++ b/tests/lib/rules/no-this-in-sfc.js @@ -17,10 +17,9 @@ const rule = require('../../../lib/rules/no-this-in-sfc'); const RuleTester = require('eslint').RuleTester; const parserOptions = { - ecmaVersion: 8, + ecmaVersion: 2018, sourceType: 'module', ecmaFeatures: { - experimentalObjectRestSpread: true, jsx: true } }; diff --git a/tests/lib/rules/no-typos.js b/tests/lib/rules/no-typos.js index cccc87a434..8ae187ff5b 100644 --- a/tests/lib/rules/no-typos.js +++ b/tests/lib/rules/no-typos.js @@ -11,7 +11,7 @@ const rule = require('../../../lib/rules/no-typos'); const RuleTester = require('eslint').RuleTester; const parserOptions = { - ecmaVersion: 6, + ecmaVersion: 2018, ecmaFeatures: { jsx: true }, diff --git a/tests/lib/rules/no-unescaped-entities.js b/tests/lib/rules/no-unescaped-entities.js index 08856c5f71..61e3a9e0a4 100644 --- a/tests/lib/rules/no-unescaped-entities.js +++ b/tests/lib/rules/no-unescaped-entities.js @@ -12,10 +12,9 @@ const rule = require('../../../lib/rules/no-unescaped-entities'); const RuleTester = require('eslint').RuleTester; const parserOptions = { - ecmaVersion: 8, + ecmaVersion: 2018, sourceType: 'module', ecmaFeatures: { - experimentalObjectRestSpread: true, jsx: true } }; diff --git a/tests/lib/rules/no-unknown-property.js b/tests/lib/rules/no-unknown-property.js index 462e4ee503..6bb38c0703 100644 --- a/tests/lib/rules/no-unknown-property.js +++ b/tests/lib/rules/no-unknown-property.js @@ -13,10 +13,9 @@ const rule = require('../../../lib/rules/no-unknown-property'); const RuleTester = require('eslint').RuleTester; const parserOptions = { - ecmaVersion: 8, + ecmaVersion: 2018, sourceType: 'module', ecmaFeatures: { - experimentalObjectRestSpread: true, jsx: true } }; diff --git a/tests/lib/rules/no-unused-prop-types.js b/tests/lib/rules/no-unused-prop-types.js index b4fb78aec6..ba3500045a 100644 --- a/tests/lib/rules/no-unused-prop-types.js +++ b/tests/lib/rules/no-unused-prop-types.js @@ -12,10 +12,9 @@ const rule = require('../../../lib/rules/no-unused-prop-types'); const RuleTester = require('eslint').RuleTester; const parserOptions = { - ecmaVersion: 8, + ecmaVersion: 2018, sourceType: 'module', ecmaFeatures: { - experimentalObjectRestSpread: true, jsx: true } }; diff --git a/tests/lib/rules/no-unused-state.js b/tests/lib/rules/no-unused-state.js index 9f286f7a9a..e26244be56 100644 --- a/tests/lib/rules/no-unused-state.js +++ b/tests/lib/rules/no-unused-state.js @@ -8,10 +8,9 @@ const rule = require('../../../lib/rules/no-unused-state'); const RuleTester = require('eslint').RuleTester; const parserOptions = { - ecmaVersion: 6, + ecmaVersion: 2018, ecmaFeatures: { - jsx: true, - experimentalObjectRestSpread: true + jsx: true } }; diff --git a/tests/lib/rules/no-will-update-set-state.js b/tests/lib/rules/no-will-update-set-state.js index 41bb274292..25afca0b42 100644 --- a/tests/lib/rules/no-will-update-set-state.js +++ b/tests/lib/rules/no-will-update-set-state.js @@ -12,10 +12,9 @@ const rule = require('../../../lib/rules/no-will-update-set-state'); const RuleTester = require('eslint').RuleTester; const parserOptions = { - ecmaVersion: 8, + ecmaVersion: 2018, sourceType: 'module', ecmaFeatures: { - experimentalObjectRestSpread: true, jsx: true } }; diff --git a/tests/lib/rules/prefer-es6-class.js b/tests/lib/rules/prefer-es6-class.js index 268c0939c1..ca22b722d4 100644 --- a/tests/lib/rules/prefer-es6-class.js +++ b/tests/lib/rules/prefer-es6-class.js @@ -12,10 +12,9 @@ const rule = require('../../../lib/rules/prefer-es6-class'); const RuleTester = require('eslint').RuleTester; const parserOptions = { - ecmaVersion: 8, + ecmaVersion: 2018, sourceType: 'module', ecmaFeatures: { - experimentalObjectRestSpread: true, jsx: true } }; diff --git a/tests/lib/rules/prefer-stateless-function.js b/tests/lib/rules/prefer-stateless-function.js index 2b7c297352..24ad7c3496 100644 --- a/tests/lib/rules/prefer-stateless-function.js +++ b/tests/lib/rules/prefer-stateless-function.js @@ -12,10 +12,9 @@ const rule = require('../../../lib/rules/prefer-stateless-function'); const RuleTester = require('eslint').RuleTester; const parserOptions = { - ecmaVersion: 8, + ecmaVersion: 2018, sourceType: 'module', ecmaFeatures: { - experimentalObjectRestSpread: true, jsx: true } }; diff --git a/tests/lib/rules/prop-types.js b/tests/lib/rules/prop-types.js index a41b86976e..443d3bd10b 100644 --- a/tests/lib/rules/prop-types.js +++ b/tests/lib/rules/prop-types.js @@ -12,10 +12,9 @@ const rule = require('../../../lib/rules/prop-types'); const RuleTester = require('eslint').RuleTester; const parserOptions = { - ecmaVersion: 8, + ecmaVersion: 2018, sourceType: 'module', ecmaFeatures: { - experimentalObjectRestSpread: true, jsx: true } }; diff --git a/tests/lib/rules/react-in-jsx-scope.js b/tests/lib/rules/react-in-jsx-scope.js index 1bd9201c96..4b347b6f9c 100644 --- a/tests/lib/rules/react-in-jsx-scope.js +++ b/tests/lib/rules/react-in-jsx-scope.js @@ -13,10 +13,9 @@ const rule = require('../../../lib/rules/react-in-jsx-scope'); const RuleTester = require('eslint').RuleTester; const parserOptions = { - ecmaVersion: 8, + ecmaVersion: 2018, sourceType: 'module', ecmaFeatures: { - experimentalObjectRestSpread: true, jsx: true } }; diff --git a/tests/lib/rules/require-default-props.js b/tests/lib/rules/require-default-props.js index 68e68ac5bb..de42f32a78 100644 --- a/tests/lib/rules/require-default-props.js +++ b/tests/lib/rules/require-default-props.js @@ -12,10 +12,9 @@ const rule = require('../../../lib/rules/require-default-props'); const RuleTester = require('eslint').RuleTester; const parserOptions = { - ecmaVersion: 8, + ecmaVersion: 2018, sourceType: 'module', ecmaFeatures: { - experimentalObjectRestSpread: true, jsx: true } }; diff --git a/tests/lib/rules/require-optimization.js b/tests/lib/rules/require-optimization.js index e66ee22b33..7a222f0a62 100644 --- a/tests/lib/rules/require-optimization.js +++ b/tests/lib/rules/require-optimization.js @@ -8,10 +8,9 @@ const rule = require('../../../lib/rules/require-optimization'); const RuleTester = require('eslint').RuleTester; const parserOptions = { - ecmaVersion: 8, + ecmaVersion: 2018, sourceType: 'module', ecmaFeatures: { - experimentalObjectRestSpread: true, jsx: true } }; diff --git a/tests/lib/rules/require-render-return.js b/tests/lib/rules/require-render-return.js index 4d6089fc63..286d5671d9 100644 --- a/tests/lib/rules/require-render-return.js +++ b/tests/lib/rules/require-render-return.js @@ -12,10 +12,9 @@ const rule = require('../../../lib/rules/require-render-return'); const RuleTester = require('eslint').RuleTester; const parserOptions = { - ecmaVersion: 8, + ecmaVersion: 2018, sourceType: 'module', ecmaFeatures: { - experimentalObjectRestSpread: true, jsx: true } }; @@ -156,7 +155,7 @@ ruleTester.run('require-render-return', rule, { // Missing return in ES6 class code: ` class Hello extends React.Component { - render() {} + render() {} } `, errors: [{ @@ -170,7 +169,7 @@ ruleTester.run('require-render-return', rule, { const names = this.props.names.map(function(name) { return
{name}
}); - } + } } `, errors: [{ diff --git a/tests/lib/rules/self-closing-comp.js b/tests/lib/rules/self-closing-comp.js index d1b9cad0c5..837c5288cf 100644 --- a/tests/lib/rules/self-closing-comp.js +++ b/tests/lib/rules/self-closing-comp.js @@ -12,10 +12,9 @@ const rule = require('../../../lib/rules/self-closing-comp'); const RuleTester = require('eslint').RuleTester; const parserOptions = { - ecmaVersion: 8, + ecmaVersion: 2018, sourceType: 'module', ecmaFeatures: { - experimentalObjectRestSpread: true, jsx: true } }; diff --git a/tests/lib/rules/sort-comp.js b/tests/lib/rules/sort-comp.js index 7468acd42b..fc26b96d2f 100644 --- a/tests/lib/rules/sort-comp.js +++ b/tests/lib/rules/sort-comp.js @@ -12,10 +12,9 @@ const rule = require('../../../lib/rules/sort-comp'); const RuleTester = require('eslint').RuleTester; const parserOptions = { - ecmaVersion: 8, + ecmaVersion: 2018, sourceType: 'module', ecmaFeatures: { - experimentalObjectRestSpread: true, jsx: true } }; diff --git a/tests/lib/rules/sort-prop-types.js b/tests/lib/rules/sort-prop-types.js index 01095ee035..26ccf8960d 100644 --- a/tests/lib/rules/sort-prop-types.js +++ b/tests/lib/rules/sort-prop-types.js @@ -11,10 +11,9 @@ const rule = require('../../../lib/rules/sort-prop-types'); const RuleTester = require('eslint').RuleTester; const parserOptions = { - ecmaVersion: 8, + ecmaVersion: 2018, sourceType: 'module', ecmaFeatures: { - experimentalObjectRestSpread: true, jsx: true } }; diff --git a/tests/lib/rules/style-prop-object.js b/tests/lib/rules/style-prop-object.js index c44c9d7dfb..c2d8b3d765 100644 --- a/tests/lib/rules/style-prop-object.js +++ b/tests/lib/rules/style-prop-object.js @@ -12,10 +12,9 @@ const rule = require('../../../lib/rules/style-prop-object'); const RuleTester = require('eslint').RuleTester; const parserOptions = { - ecmaVersion: 8, + ecmaVersion: 2018, sourceType: 'module', ecmaFeatures: { - experimentalObjectRestSpread: true, jsx: true } }; @@ -81,10 +80,9 @@ ruleTester.run('style-prop-object', rule, { '}' ].join('\n'), parserOptions: { - ecmaVersion: 8, + ecmaVersion: 2018, sourceType: 'module', ecmaFeatures: { - experimentalObjectRestSpread: true, jsx: true } } diff --git a/tests/lib/rules/void-dom-elements-no-children.js b/tests/lib/rules/void-dom-elements-no-children.js index c54e56b7ed..39e6286f50 100644 --- a/tests/lib/rules/void-dom-elements-no-children.js +++ b/tests/lib/rules/void-dom-elements-no-children.js @@ -13,10 +13,9 @@ const rule = require('../../../lib/rules/void-dom-elements-no-children'); const RuleTester = require('eslint').RuleTester; const parserOptions = { - ecmaVersion: 8, + ecmaVersion: 2018, sourceType: 'module', ecmaFeatures: { - experimentalObjectRestSpread: true, jsx: true } }; From 0d34198ff3b192880b61fc74010952b56bb91d90 Mon Sep 17 00:00:00 2001 From: Yannick Croissant Date: Sun, 3 Jun 2018 23:23:48 +0200 Subject: [PATCH 24/34] Update CHANGELOG and bump version --- CHANGELOG.md | 25 +++++++++++++++++++++++++ package.json | 2 +- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 32d9e1abcd..e8bacb7dd1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,30 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). This change log adheres to standards from [Keep a CHANGELOG](http://keepachangelog.com). +## [7.9.0] - 2018-06-03 +### Added +* Add [`jsx-props-no-multi-spaces`][] rule ([#1755][] @ThiefMaster) +* Add `first` option to [`jsx-indent-props`][] ([#398][] @ThiefMaster) +* Add `enforceDynamicLinks` option to [`jsx-no-target-blank`][] ([#1737][] @kenearley) + +### Fixed +* Fix static lifecycle methods validation in [`sort-comp`][] ([#1793][] @lynxtaa) +* Fix crash in [`no-typos`][] when encountering anonymous react imports ([#1796][] @jsg2021) +* Fix ESLint 3 support ([#1779][]) + +### Changed +* Documentation improvements ([#1794][] @lencioni) +* Update Travis CI configuration to test on multiple ESLint verions + +[7.9.0]: https://github.com/yannickcr/eslint-plugin-react/compare/v7.8.2...v7.9.0 +[#1755]: https://github.com/yannickcr/eslint-plugin-react/pull/1755 +[#398]: https://github.com/yannickcr/eslint-plugin-react/issues/398 +[#1737]: https://github.com/yannickcr/eslint-plugin-react/issues/1737 +[#1793]: https://github.com/yannickcr/eslint-plugin-react/issues/1793 +[#1796]: https://github.com/yannickcr/eslint-plugin-react/pull/1796 +[#1779]: https://github.com/yannickcr/eslint-plugin-react/issues/1779 +[#1794]: https://github.com/yannickcr/eslint-plugin-react/pull/1794 + ## [7.8.2] - 2018-05-13 ### Fixed * Fix crash in [`boolean-prop-naming`][] when encountering a required shape prop type ([#1791][] @pcorpet) @@ -2205,3 +2229,4 @@ If you're still not using React 15 you can keep the old behavior by setting the [`no-this-in-sfc`]: docs/rules/no-this-in-sfc.md [`jsx-sort-default-props`]: docs/rules/jsx-sort-default-props.md [`jsx-max-depth`]: docs/rules/jsx-max-depth.md +[`jsx-props-no-multi-spaces`]: docs/rules/jsx-props-no-multi-spaces.md diff --git a/package.json b/package.json index 917be882c8..80bb42d879 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "eslint-plugin-react", - "version": "7.8.2", + "version": "7.9.0", "author": "Yannick Croissant ", "description": "React specific linting rules for ESLint", "main": "index.js", From 9e83b7c6aee7f29d0cd98ade6342f38f4228837a Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Sun, 3 Jun 2018 21:32:29 -0700 Subject: [PATCH 25/34] [Deps] update `has`, `prop-types`, `doctrine` --- package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 80bb42d879..feae3c626e 100644 --- a/package.json +++ b/package.json @@ -24,10 +24,10 @@ "homepage": "https://github.com/yannickcr/eslint-plugin-react", "bugs": "https://github.com/yannickcr/eslint-plugin-react/issues", "dependencies": { - "doctrine": "^2.0.2", - "has": "^1.0.1", + "doctrine": "^2.1.0", + "has": "^1.0.2", "jsx-ast-utils": "^2.0.1", - "prop-types": "^15.6.0" + "prop-types": "^15.6.1" }, "devDependencies": { "babel-eslint": "^8.2.1", From df430ee7303696c3199bd6e40dcb9701e54ce11e Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Sun, 3 Jun 2018 21:32:58 -0700 Subject: [PATCH 26/34] [Dev Deps] update `babel-eslint`, `coveralls`, `eslint`, `mocha` --- package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index feae3c626e..5a089f221d 100644 --- a/package.json +++ b/package.json @@ -30,11 +30,11 @@ "prop-types": "^15.6.1" }, "devDependencies": { - "babel-eslint": "^8.2.1", - "coveralls": "^3.0.0", - "eslint": "^4.18.0", + "babel-eslint": "^8.2.3", + "coveralls": "^3.0.1", + "eslint": "^4.19.1", "istanbul": "^0.4.5", - "mocha": "^5.0.1" + "mocha": "^5.2.0" }, "peerDependencies": { "eslint": "^3.0.0 || ^4.0.0" From ac102885765be5ff37847a871f239c6703e1c7cc Mon Sep 17 00:00:00 2001 From: Jordan Harband Date: Sun, 3 Jun 2018 21:34:56 -0700 Subject: [PATCH 27/34] Update CHANGELOG and bump version --- CHANGELOG.md | 5 +++++ package.json | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e8bacb7dd1..78ed2fcb5a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,11 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). This change log adheres to standards from [Keep a CHANGELOG](http://keepachangelog.com). +## [7.9.1] - 2018-06-03 +* Nothing was fixed; this is a republish with some updated deps. ([#1804][] @ljharb) + +[#1804]: https://github.com/yannickcr/eslint-plugin-react/issues/1804 + ## [7.9.0] - 2018-06-03 ### Added * Add [`jsx-props-no-multi-spaces`][] rule ([#1755][] @ThiefMaster) diff --git a/package.json b/package.json index 5a089f221d..3e10427753 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "eslint-plugin-react", - "version": "7.9.0", + "version": "7.9.1", "author": "Yannick Croissant ", "description": "React specific linting rules for ESLint", "main": "index.js", From 7c5b8c4e7e641d7509dce803ea4878b953c50503 Mon Sep 17 00:00:00 2001 From: ferhat elmas Date: Mon, 4 Jun 2018 09:16:45 +0200 Subject: [PATCH 28/34] [Docs] Typo fixes in jsx-no-target-blank --- docs/rules/jsx-no-target-blank.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/rules/jsx-no-target-blank.md b/docs/rules/jsx-no-target-blank.md index 906e852b4a..c6664d56dd 100644 --- a/docs/rules/jsx-no-target-blank.md +++ b/docs/rules/jsx-no-target-blank.md @@ -15,8 +15,8 @@ This rule aims to prevent user generated links from creating security vulerabili There are two main options for the rule: -* `{"enforceDynamicLinks": "always"}` enforces the rule if the href is a dyanamic link (default) -* `{"enforceDynamicLinks": "never"}` does not enforce the rule if the href is a dyamic link +* `{"enforceDynamicLinks": "always"}` enforces the rule if the href is a dynamic link (default) +* `{"enforceDynamicLinks": "never"}` does not enforce the rule if the href is a dynamic link ### always (default) From fee2d1a02fe32772129973cf1054a49930934ef8 Mon Sep 17 00:00:00 2001 From: Yannick Croissant Date: Mon, 4 Jun 2018 23:20:04 +0200 Subject: [PATCH 29/34] Fix crash in no-unused-prop-types when encountering mixed union and intersection flow types (fixes #1806) --- lib/rules/no-unused-prop-types.js | 2 +- tests/lib/rules/no-unused-prop-types.js | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/lib/rules/no-unused-prop-types.js b/lib/rules/no-unused-prop-types.js index b73c173c01..cff7eecafc 100644 --- a/lib/rules/no-unused-prop-types.js +++ b/lib/rules/no-unused-prop-types.js @@ -361,7 +361,7 @@ module.exports = { and property value. (key, value) => void */ function iterateProperties(properties, fn) { - if (properties.length && typeof fn === 'function') { + if (properties && properties.length && typeof fn === 'function') { for (let i = 0, j = properties.length; i < j; i++) { const node = properties[i]; const key = getKeyValue(node); diff --git a/tests/lib/rules/no-unused-prop-types.js b/tests/lib/rules/no-unused-prop-types.js index ba3500045a..b0bc92237b 100644 --- a/tests/lib/rules/no-unused-prop-types.js +++ b/tests/lib/rules/no-unused-prop-types.js @@ -4551,6 +4551,29 @@ ruleTester.run('no-unused-prop-types', rule, { errors: [{ message: '\'defaultValue\' PropType is defined but prop is never used' }] + }, { + // Mixed union and intersection types + code: ` + import React from 'react'; + type OtherProps = { + firstname: string, + lastname: string, + } | { + fullname: string + }; + type Props = OtherProps & { + age: number + }; + class Test extends React.PureComponent { + render() { + return
Hello {this.props.firstname}
+ } + } + `, + parser: 'babel-eslint', + errors: [{ + message: '\'age\' PropType is defined but prop is never used' + }] } /* , { From c82746cc0f52ddab23cef45a217724a37bc2547b Mon Sep 17 00:00:00 2001 From: Yannick Croissant Date: Mon, 4 Jun 2018 23:20:33 +0200 Subject: [PATCH 30/34] Fix crash in no-unused-prop-types when encountering impossible intersection flow types (fixes #1806) --- lib/rules/no-unused-prop-types.js | 3 ++- tests/lib/rules/no-unused-prop-types.js | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/lib/rules/no-unused-prop-types.js b/lib/rules/no-unused-prop-types.js index cff7eecafc..b82c9049ec 100644 --- a/lib/rules/no-unused-prop-types.js +++ b/lib/rules/no-unused-prop-types.js @@ -792,7 +792,8 @@ module.exports = { return declarePropTypesForObjectTypeAnnotation(annotation, declaredPropTypes); } - if (annotation.type === 'UnionTypeAnnotation') { + // Type can't be resolved + if (!annotation.id) { return true; } diff --git a/tests/lib/rules/no-unused-prop-types.js b/tests/lib/rules/no-unused-prop-types.js index b0bc92237b..8f716a02ff 100644 --- a/tests/lib/rules/no-unused-prop-types.js +++ b/tests/lib/rules/no-unused-prop-types.js @@ -2899,6 +2899,20 @@ ruleTester.run('no-unused-prop-types', rule, { `].join('\n'), settings: {react: {version: '16.3.0'}}, parser: 'babel-eslint' + }, { + // Impossible intersection type + code: ` + import React from 'react'; + type Props = string & { + fullname: string + }; + class Test extends React.PureComponent { + render() { + return
Hello {this.props.fullname}
+ } + } + `, + parser: 'babel-eslint' } ], From fcff54ed23b6326f6c91224e2ac29b5fe1cd959f Mon Sep 17 00:00:00 2001 From: Alex Zherdev Date: Wed, 13 Jun 2018 23:09:55 -0700 Subject: [PATCH 31/34] Allow LHS in destructuring-assignment Resolves #1728 --- lib/rules/destructuring-assignment.js | 3 ++- tests/lib/rules/destructuring-assignment.js | 10 ++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/lib/rules/destructuring-assignment.js b/lib/rules/destructuring-assignment.js index 93bbaed704..45c2af91f7 100644 --- a/lib/rules/destructuring-assignment.js +++ b/lib/rules/destructuring-assignment.js @@ -78,7 +78,8 @@ module.exports = { // this.props.Aprop || this.context.aProp || this.state.aState const isPropUsed = ( node.object.type === 'MemberExpression' && node.object.object.type === 'ThisExpression' && - (node.object.property.name === 'props' || node.object.property.name === 'context' || node.object.property.name === 'state') + (node.object.property.name === 'props' || node.object.property.name === 'context' || node.object.property.name === 'state') && + !isAssignmentToProp(node) ); if (isPropUsed && configuration === 'always') { diff --git a/tests/lib/rules/destructuring-assignment.js b/tests/lib/rules/destructuring-assignment.js index 6ce0e36cfb..752a3d8fe3 100644 --- a/tests/lib/rules/destructuring-assignment.js +++ b/tests/lib/rules/destructuring-assignment.js @@ -126,7 +126,17 @@ ruleTester.run('destructuring-assignment', rule, { };`, options: ['never'], parser: 'babel-eslint' + }, { + code: `const Foo = class extends React.PureComponent { + constructor() { + this.state = {}; + this.state.foo = 'bar'; + } + };`, + options: ['always'], + parser: 'babel-eslint' }], + invalid: [{ code: `const MyComponent = (props) => { return (
) From b6e911b16c5f2337eb0e600f5307e6e0031bfbf0 Mon Sep 17 00:00:00 2001 From: Alex Zherdev Date: Wed, 13 Jun 2018 23:19:00 -0700 Subject: [PATCH 32/34] Remove unnecessary babel-eslint --- tests/lib/rules/destructuring-assignment.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/lib/rules/destructuring-assignment.js b/tests/lib/rules/destructuring-assignment.js index 752a3d8fe3..ca5a85e28a 100644 --- a/tests/lib/rules/destructuring-assignment.js +++ b/tests/lib/rules/destructuring-assignment.js @@ -133,8 +133,7 @@ ruleTester.run('destructuring-assignment', rule, { this.state.foo = 'bar'; } };`, - options: ['always'], - parser: 'babel-eslint' + options: ['always'] }], invalid: [{ From f9244147f4b3c5898ad0cb76e831b5de702b6504 Mon Sep 17 00:00:00 2001 From: Alex Zherdev Date: Wed, 13 Jun 2018 23:17:41 -0700 Subject: [PATCH 33/34] Fix static propTypes handling in no-typos Resolves #1677 --- lib/rules/no-typos.js | 10 +++++----- tests/lib/rules/no-typos.js | 28 ++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/lib/rules/no-typos.js b/lib/rules/no-typos.js index f9c798a9fb..8fb6ba27ee 100644 --- a/lib/rules/no-typos.js +++ b/lib/rules/no-typos.js @@ -126,8 +126,7 @@ module.exports = { function reportErrorIfClassPropertyCasingTypo(node, propertyName) { if (propertyName === 'propTypes' || propertyName === 'contextTypes' || propertyName === 'childContextTypes') { - const propsNode = node && node.parent && node.parent.type === 'AssignmentExpression' && node.parent.right; - checkValidPropObject(propsNode); + checkValidPropObject(node); } STATIC_CLASS_PROPERTIES.forEach(CLASS_PROP => { if (propertyName && CLASS_PROP.toLowerCase() === propertyName.toLowerCase() && CLASS_PROP !== propertyName) { @@ -176,7 +175,7 @@ module.exports = { const tokens = context.getFirstTokens(node, 2); const propertyName = tokens[1].value; - reportErrorIfClassPropertyCasingTypo(node, propertyName); + reportErrorIfClassPropertyCasingTypo(node.value, propertyName); }, MemberExpression: function(node) { @@ -193,9 +192,10 @@ module.exports = { if ( relatedComponent && - (utils.isES6Component(relatedComponent.node) || utils.isReturningJSX(relatedComponent.node)) + (utils.isES6Component(relatedComponent.node) || utils.isReturningJSX(relatedComponent.node)) && + (node.parent && node.parent.type === 'AssignmentExpression' && node.parent.right) ) { - reportErrorIfClassPropertyCasingTypo(node, propertyName); + reportErrorIfClassPropertyCasingTypo(node.parent.right, propertyName); } }, diff --git a/tests/lib/rules/no-typos.js b/tests/lib/rules/no-typos.js index 8ae187ff5b..3327b9d13d 100644 --- a/tests/lib/rules/no-typos.js +++ b/tests/lib/rules/no-typos.js @@ -935,6 +935,34 @@ ruleTester.run('no-typos', rule, { errors: [{ message: 'Typo in prop type chain qualifier: isrequired' }] + }, { + code: ` + import PropTypes from "prop-types"; + class Component extends React.Component { + static propTypes = { + a: PropTypes.number.isrequired + } + }; + `, + parser: 'babel-eslint', + parserOptions: parserOptions, + errors: [{ + message: 'Typo in prop type chain qualifier: isrequired' + }] + }, { + code: ` + import PropTypes from "prop-types"; + class Component extends React.Component { + static propTypes = { + a: PropTypes.Number + } + }; + `, + parser: 'babel-eslint', + parserOptions: parserOptions, + errors: [{ + message: 'Typo in declared prop type: Number' + }] }, { code: ` import PropTypes from "prop-types"; From d68b9e82e00c3f66b2d19031c982384f38ecbe65 Mon Sep 17 00:00:00 2001 From: Alex Zherdev Date: Thu, 14 Jun 2018 18:53:14 -0700 Subject: [PATCH 34/34] Remove unnecessary babel-eslint and fix typo --- lib/rules/no-typos.js | 6 +++--- tests/lib/rules/no-typos.js | 2 -- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/rules/no-typos.js b/lib/rules/no-typos.js index 8fb6ba27ee..4540e5a015 100644 --- a/lib/rules/no-typos.js +++ b/lib/rules/no-typos.js @@ -45,7 +45,7 @@ module.exports = { let propTypesPackageName = null; let reactPackageName = null; - function checkValidPropTypeQualfier(node) { + function checkValidPropTypeQualifier(node) { if (node.name !== 'isRequired') { context.report({ node: node, @@ -101,14 +101,14 @@ module.exports = { isPropTypesPackage(node.object.object) ) { // PropTypes.myProp.isRequired checkValidPropType(node.object.property); - checkValidPropTypeQualfier(node.property); + checkValidPropTypeQualifier(node.property); } else if ( isPropTypesPackage(node.object) && node.property.name !== 'isRequired' ) { // PropTypes.myProp checkValidPropType(node.property); } else if (node.object.type === 'CallExpression') { - checkValidPropTypeQualfier(node.property); + checkValidPropTypeQualifier(node.property); checkValidCallExpression(node.object); } } else if (node.type === 'CallExpression') { diff --git a/tests/lib/rules/no-typos.js b/tests/lib/rules/no-typos.js index 3327b9d13d..ba6b524bac 100644 --- a/tests/lib/rules/no-typos.js +++ b/tests/lib/rules/no-typos.js @@ -917,7 +917,6 @@ ruleTester.run('no-typos', rule, { a: PropTypes.Number.isRequired } `, - parser: 'babel-eslint', parserOptions: parserOptions, errors: [{ message: 'Typo in declared prop type: Number' @@ -930,7 +929,6 @@ ruleTester.run('no-typos', rule, { a: PropTypes.number.isrequired } `, - parser: 'babel-eslint', parserOptions: parserOptions, errors: [{ message: 'Typo in prop type chain qualifier: isrequired'