diff --git a/lib/rules/jsx-key.js b/lib/rules/jsx-key.js index 83e7873cfd..c40e476d26 100644 --- a/lib/rules/jsx-key.js +++ b/lib/rules/jsx-key.js @@ -13,6 +13,10 @@ const docsUrl = require('../util/docsUrl'); // Rule Definition // ------------------------------------------------------------------------------ +const defaultOptions = { + checkFragmentShorthand: false +}; + module.exports = { meta: { docs: { @@ -21,16 +25,33 @@ module.exports = { recommended: true, url: docsUrl('jsx-key') }, - schema: [] + schema: [{ + type: 'object', + properties: { + checkFragmentShorthand: { + type: 'boolean', + default: defaultOptions.checkFragmentShorthand + } + }, + additionalProperties: false + }] }, create(context) { + const options = Object.assign({}, defaultOptions, context.options[0]); + const checkFragmentShorthand = options.checkFragmentShorthand; + function checkIteratorElement(node) { if (node.type === 'JSXElement' && !hasProp(node.openingElement.attributes, 'key')) { context.report({ node, message: 'Missing "key" prop for element in iterator' }); + } else if (checkFragmentShorthand && node.type === 'JSXFragment') { + context.report({ + node, + message: 'Missing "key" prop for element in iterator. Shorthand fragment syntax does support providing keys' + }); } } @@ -52,6 +73,19 @@ module.exports = { } }, + JSXFragment(node) { + if (!checkFragmentShorthand) { + return; + } + + if (node.parent.type === 'ArrayExpression') { + context.report({ + node, + message: 'Missing "key" prop for element in array. Shorthand fragment syntax does support providing keys' + }); + } + }, + // Array.prototype.map CallExpression(node) { if (node.callee && node.callee.type !== 'MemberExpression') { @@ -66,7 +100,7 @@ module.exports = { const isFn = fn && fn.type === 'FunctionExpression'; const isArrFn = fn && fn.type === 'ArrowFunctionExpression'; - if (isArrFn && fn.body.type === 'JSXElement') { + if (isArrFn && (fn.body.type === 'JSXElement' || fn.body.type === 'JSXFragment')) { checkIteratorElement(fn.body); } diff --git a/tests/lib/rules/jsx-key.js b/tests/lib/rules/jsx-key.js index d4dea2b148..fd28fa5dca 100644 --- a/tests/lib/rules/jsx-key.js +++ b/tests/lib/rules/jsx-key.js @@ -12,6 +12,8 @@ const RuleTester = require('eslint').RuleTester; const rule = require('../../../lib/rules/jsx-key'); +const parsers = require('../../helpers/parsers'); + const parserOptions = { ecmaVersion: 2018, sourceType: 'module', @@ -37,7 +39,9 @@ ruleTester.run('jsx-key', rule, { {code: '[1, 2, 3].foo(x => );'}, {code: 'var App = () =>
;'}, {code: '[1, 2, 3].map(function(x) { return; });'}, - {code: 'foo(() =>
);'} + {code: 'foo(() =>
);'}, + {code: 'foo(() => <>);', parser: parsers.BABEL_ESLINT}, + {code: '<>;', parser: parsers.BABEL_ESLINT} ], invalid: [{ code: '[];', @@ -57,5 +61,15 @@ ruleTester.run('jsx-key', rule, { }, { code: '[1, 2 ,3].map(x => { return });', errors: [{message: 'Missing "key" prop for element in iterator'}] + }, { + code: '[1, 2, 3].map(x => <>{x});', + parser: parsers.BABEL_ESLINT, + options: [{checkFragmentShorthand: true}], + errors: [{message: 'Missing "key" prop for element in iterator. Shorthand fragment syntax does support providing keys'}] + }, { + code: '[<>];', + parser: parsers.BABEL_ESLINT, + options: [{checkFragmentShorthand: true}], + errors: [{message: 'Missing "key" prop for element in array. Shorthand fragment syntax does support providing keys'}] }] });