forbid-elements.js 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. /**
  2. * @fileoverview Forbid certain elements
  3. * @author Kenneth Chung
  4. */
  5. 'use strict';
  6. const has = require('has');
  7. const docsUrl = require('../util/docsUrl');
  8. // ------------------------------------------------------------------------------
  9. // Rule Definition
  10. // ------------------------------------------------------------------------------
  11. module.exports = {
  12. meta: {
  13. docs: {
  14. description: 'Forbid certain elements',
  15. category: 'Best Practices',
  16. recommended: false,
  17. url: docsUrl('forbid-elements')
  18. },
  19. messages: {
  20. forbiddenElement: '<{{element}}> is forbidden',
  21. forbiddenElement_message: '<{{element}}> is forbidden, {{message}}'
  22. },
  23. schema: [{
  24. type: 'object',
  25. properties: {
  26. forbid: {
  27. type: 'array',
  28. items: {
  29. anyOf: [
  30. {type: 'string'},
  31. {
  32. type: 'object',
  33. properties: {
  34. element: {type: 'string'},
  35. message: {type: 'string'}
  36. },
  37. required: ['element'],
  38. additionalProperties: false
  39. }
  40. ]
  41. }
  42. }
  43. },
  44. additionalProperties: false
  45. }]
  46. },
  47. create(context) {
  48. const configuration = context.options[0] || {};
  49. const forbidConfiguration = configuration.forbid || [];
  50. const indexedForbidConfigs = {};
  51. forbidConfiguration.forEach((item) => {
  52. if (typeof item === 'string') {
  53. indexedForbidConfigs[item] = {element: item};
  54. } else {
  55. indexedForbidConfigs[item.element] = item;
  56. }
  57. });
  58. function isValidCreateElement(node) {
  59. return node.callee
  60. && node.callee.type === 'MemberExpression'
  61. && node.callee.object.name === 'React'
  62. && node.callee.property.name === 'createElement'
  63. && node.arguments.length > 0;
  64. }
  65. function reportIfForbidden(element, node) {
  66. if (has(indexedForbidConfigs, element)) {
  67. const message = indexedForbidConfigs[element].message;
  68. context.report({
  69. node,
  70. messageId: message ? 'forbiddenElement_message' : 'forbiddenElement',
  71. data: {
  72. element,
  73. message
  74. }
  75. });
  76. }
  77. }
  78. return {
  79. JSXOpeningElement(node) {
  80. reportIfForbidden(context.getSourceCode().getText(node.name), node.name);
  81. },
  82. CallExpression(node) {
  83. if (!isValidCreateElement(node)) {
  84. return;
  85. }
  86. const argument = node.arguments[0];
  87. const argType = argument.type;
  88. if (argType === 'Identifier' && /^[A-Z_]/.test(argument.name)) {
  89. reportIfForbidden(argument.name, argument);
  90. } else if (argType === 'Literal' && /^[a-z][^.]*$/.test(argument.value)) {
  91. reportIfForbidden(argument.value, argument);
  92. } else if (argType === 'MemberExpression') {
  93. reportIfForbidden(context.getSourceCode().getText(argument), argument);
  94. }
  95. }
  96. };
  97. }
  98. };