@@ -11,6 +11,7 @@ const values = require('object.values');
1111const docsUrl = require ( '../util/docsUrl' ) ;
1212const pragmaUtil = require ( '../util/pragma' ) ;
1313const report = require ( '../util/report' ) ;
14+ const astUtil = require ( '../util/ast' ) ;
1415
1516// ------------------------------------------------------------------------------
1617// Rule Definition
@@ -124,6 +125,36 @@ module.exports = {
124125 } ) ;
125126 }
126127
128+ /**
129+ * Checks if the given node is a function expression or arrow function,
130+ * and checks if there is a missing key prop in return statement's arguments
131+ * @param {ASTNode } node
132+ */
133+ function checkFunctionsBlockStatement ( node ) {
134+ if ( astUtil . isFunctionLikeExpression ( node ) ) {
135+ if ( node . body . type === 'BlockStatement' ) {
136+ getReturnStatements ( node . body )
137+ . filter ( ( returnStatement ) => returnStatement && returnStatement . argument )
138+ . forEach ( ( returnStatement ) => {
139+ checkIteratorElement ( returnStatement . argument ) ;
140+ } ) ;
141+ }
142+ }
143+ }
144+
145+ /**
146+ * Checks if the given node is an arrow function that has an JSX Element or JSX Fragment in its body,
147+ * and the JSX is missing a key prop
148+ * @param {ASTNode } node
149+ */
150+ function checkArrowFunctionWithJSX ( node ) {
151+ const isArrFn = node && node . type === 'ArrowFunctionExpression' ;
152+
153+ if ( isArrFn && ( node . body . type === 'JSXElement' || node . body . type === 'JSXFragment' ) ) {
154+ checkIteratorElement ( node . body ) ;
155+ }
156+ }
157+
127158 const seen = new WeakSet ( ) ;
128159
129160 return {
@@ -196,26 +227,26 @@ module.exports = {
196227 OptionalCallExpression[callee.type="MemberExpression"][callee.property.name="map"],\
197228 OptionalCallExpression[callee.type="OptionalMemberExpression"][callee.property.name="map"]' ( node ) {
198229 const fn = node . arguments [ 0 ] ;
199- const isFn = fn && fn . type === 'FunctionExpression' ;
200- const isArrFn = fn && fn . type === 'ArrowFunctionExpression' ;
201-
202- if ( ! fn && ! isFn && ! isArrFn ) {
230+ if ( ! astUtil . isFunctionLikeExpression ( fn ) ) {
203231 return ;
204232 }
205233
206- if ( isArrFn && ( fn . body . type === 'JSXElement' || fn . body . type === 'JSXFragment' ) ) {
207- checkIteratorElement ( fn . body ) ;
208- }
234+ checkArrowFunctionWithJSX ( fn ) ;
209235
210- if ( isFn || isArrFn ) {
211- if ( fn . body . type === 'BlockStatement' ) {
212- getReturnStatements ( fn . body )
213- . filter ( ( returnStatement ) => returnStatement && returnStatement . argument )
214- . forEach ( ( returnStatement ) => {
215- checkIteratorElement ( returnStatement . argument ) ;
216- } ) ;
217- }
236+ checkFunctionsBlockStatement ( fn ) ;
237+ } ,
238+
239+ // Array.from
240+ 'CallExpression[callee.type="MemberExpression"][callee.property.name="from"]' ( node ) {
241+ const fn = node . arguments . length > 1 && node . arguments [ 1 ] ;
242+
243+ if ( ! astUtil . isFunctionLikeExpression ( fn ) ) {
244+ return ;
218245 }
246+
247+ checkArrowFunctionWithJSX ( fn ) ;
248+
249+ checkFunctionsBlockStatement ( fn ) ;
219250 } ,
220251 } ;
221252 } ,
0 commit comments