diff --git a/lib/rules/jsx-key.js b/lib/rules/jsx-key.js index 25405bb13b..8a05a31621 100644 --- a/lib/rules/jsx-key.js +++ b/lib/rules/jsx-key.js @@ -15,7 +15,8 @@ const pragmaUtil = require('../util/pragma'); // ------------------------------------------------------------------------------ const defaultOptions = { - checkFragmentShorthand: false + checkFragmentShorthand: false, + allowSpreadKeys: true // TODO: When React separates keys from props this should become false }; module.exports = { @@ -32,6 +33,10 @@ module.exports = { checkFragmentShorthand: { type: 'boolean', default: defaultOptions.checkFragmentShorthand + }, + allowSpreadKeys: { + type: 'boolean', + default: defaultOptions.allowSpreadKeys } }, additionalProperties: false @@ -41,11 +46,30 @@ module.exports = { create(context) { const options = Object.assign({}, defaultOptions, context.options[0]); const checkFragmentShorthand = options.checkFragmentShorthand; + const allowSpreadKeys = options.allowSpreadKeys; const reactPragma = pragmaUtil.getFromContext(context); const fragmentPragma = pragmaUtil.getFragmentFromContext(context); function checkIteratorElement(node) { - if (node.type === 'JSXElement' && !hasProp(node.openingElement.attributes, 'key')) { + if (node.type === 'JSXElement') { + const hasAKeyAttribute = hasProp(node.openingElement.attributes, 'key'); + + const hasASpreadArgumentWithAKeyProperty = allowSpreadKeys && node.openingElement + && node.openingElement.attributes + && node.openingElement.attributes.some( + ({argument}) => argument && argument.properties && argument.properties.some( + (property) => property.key.name === 'key')); + + const hasAnObjectSpreadArgument = allowSpreadKeys && node.openingElement + && node.openingElement.attributes + && node.openingElement.attributes.some( + ({argument}) => argument && argument.type === 'Identifier'); + + const isValidElement = hasAKeyAttribute + || hasASpreadArgumentWithAKeyProperty + || hasAnObjectSpreadArgument; + if (isValidElement) return; + context.report({ node, message: 'Missing "key" prop for element in iterator' diff --git a/tests/lib/rules/jsx-key.js b/tests/lib/rules/jsx-key.js index 49632f59e9..a3a983ddd0 100644 --- a/tests/lib/rules/jsx-key.js +++ b/tests/lib/rules/jsx-key.js @@ -39,6 +39,14 @@ ruleTester.run('jsx-key', rule, { {code: 'fn()'}, {code: '[1, 2, 3].map(function () {})'}, {code: ';'}, + { + code: '[1, 2, 3].map(x => );', + options: [{allowSpreadKeys: true}] + }, + { + code: '[1, 2, 3].map(x => );', + options: [{allowSpreadKeys: true}] + }, {code: '[, ];'}, {code: '[1, 2, 3].map(function(x) { return });'}, {code: '[1, 2, 3].map(x => );'}, @@ -88,5 +96,14 @@ ruleTester.run('jsx-key', rule, { options: [{checkFragmentShorthand: true}], settings, errors: [{message: 'Missing "key" prop for element in array. Shorthand fragment syntax does not support providing keys. Use Act.Frag instead'}] + }, { + code: '[1, 2, 3].map(x => );', + options: [{allowSpreadKeys: false}], + errors: [{message: 'Missing "key" prop for element in iterator'}] + }, + { + code: '[1, 2, 3].map(x => );', + options: [{allowSpreadKeys: false}], + errors: [{message: 'Missing "key" prop for element in iterator'}] }) });