jsx-key.js 2.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. /**
  2. * @fileoverview Report missing `key` props in iterators/collection literals.
  3. * @author Ben Mosher
  4. */
  5. 'use strict';
  6. const hasProp = require('jsx-ast-utils/hasProp');
  7. const docsUrl = require('../util/docsUrl');
  8. // ------------------------------------------------------------------------------
  9. // Rule Definition
  10. // ------------------------------------------------------------------------------
  11. module.exports = {
  12. meta: {
  13. docs: {
  14. description: 'Report missing `key` props in iterators/collection literals',
  15. category: 'Possible Errors',
  16. recommended: true,
  17. url: docsUrl('jsx-key')
  18. },
  19. schema: []
  20. },
  21. create: function(context) {
  22. function checkIteratorElement(node) {
  23. if (node.type === 'JSXElement' && !hasProp(node.openingElement.attributes, 'key')) {
  24. context.report({
  25. node: node,
  26. message: 'Missing "key" prop for element in iterator'
  27. });
  28. }
  29. }
  30. function getReturnStatement(body) {
  31. return body.filter(item => item.type === 'ReturnStatement')[0];
  32. }
  33. return {
  34. JSXElement: function(node) {
  35. if (hasProp(node.openingElement.attributes, 'key')) {
  36. return;
  37. }
  38. if (node.parent.type === 'ArrayExpression') {
  39. context.report({
  40. node: node,
  41. message: 'Missing "key" prop for element in array'
  42. });
  43. }
  44. },
  45. // Array.prototype.map
  46. CallExpression: function (node) {
  47. if (node.callee && node.callee.type !== 'MemberExpression') {
  48. return;
  49. }
  50. if (node.callee && node.callee.property && node.callee.property.name !== 'map') {
  51. return;
  52. }
  53. const fn = node.arguments[0];
  54. const isFn = fn && fn.type === 'FunctionExpression';
  55. const isArrFn = fn && fn.type === 'ArrowFunctionExpression';
  56. if (isArrFn && fn.body.type === 'JSXElement') {
  57. checkIteratorElement(fn.body);
  58. }
  59. if (isFn || isArrFn) {
  60. if (fn.body.type === 'BlockStatement') {
  61. const returnStatement = getReturnStatement(fn.body.body);
  62. if (returnStatement && returnStatement.argument) {
  63. checkIteratorElement(returnStatement.argument);
  64. }
  65. }
  66. }
  67. }
  68. };
  69. }
  70. };