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'}]
})
});