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 diff --git a/CHANGELOG.md b/CHANGELOG.md index 32d9e1abcd..78ed2fcb5a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,35 @@ 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) +* 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 +2234,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/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) 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) { diff --git a/lib/rules/default-props-match-prop-types.js b/lib/rules/default-props-match-prop-types.js index 9e0f994878..79723f18a8 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) { @@ -92,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, @@ -190,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'; @@ -524,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; } @@ -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) { 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/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 ) { 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); + } } }; } 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 { 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') { 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); } 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)); }; }; 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); diff --git a/lib/rules/no-typos.js b/lib/rules/no-typos.js index f9c798a9fb..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') { @@ -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/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); } } diff --git a/lib/rules/no-unused-prop-types.js b/lib/rules/no-unused-prop-types.js index b73c173c01..b82c9049ec 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); @@ -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/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; + } } }; }) diff --git a/lib/rules/require-default-props.js b/lib/rules/require-default-props.js index f5cbf8695a..0704e8e323 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) { @@ -94,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, ''), @@ -165,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'; @@ -536,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; } @@ -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) { 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; } 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]; } diff --git a/package.json b/package.json index 917be882c8..3e10427753 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "eslint-plugin-react", - "version": "7.8.2", + "version": "7.9.1", "author": "Yannick Croissant ", "description": "React specific linting rules for ESLint", "main": "index.js", @@ -24,17 +24,17 @@ "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", - "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" 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..ca5a85e28a 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 @@ -126,7 +126,16 @@ 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'] }], + invalid: [{ code: `const MyComponent = (props) => { return (
) 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 0123327e1c..ba6b524bac 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 }, @@ -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: ` @@ -926,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' @@ -939,11 +929,38 @@ ruleTester.run('no-typos', rule, { a: PropTypes.number.isrequired } `, + 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.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"; @@ -1354,6 +1371,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 // --------- }, { 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..8f716a02ff 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 } }; @@ -2900,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' } ], @@ -4552,6 +4565,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' + }] } /* , { 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 } };