forbid-elements.js 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  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. schema: [{
  20. type: 'object',
  21. properties: {
  22. forbid: {
  23. type: 'array',
  24. items: {
  25. anyOf: [
  26. {type: 'string'},
  27. {
  28. type: 'object',
  29. properties: {
  30. element: {type: 'string'},
  31. message: {type: 'string'}
  32. },
  33. required: ['element'],
  34. additionalProperties: false
  35. }
  36. ]
  37. }
  38. }
  39. },
  40. additionalProperties: false
  41. }]
  42. },
  43. create: function(context) {
  44. const sourceCode = context.getSourceCode();
  45. const configuration = context.options[0] || {};
  46. const forbidConfiguration = configuration.forbid || [];
  47. const indexedForbidConfigs = {};
  48. forbidConfiguration.forEach(item => {
  49. if (typeof item === 'string') {
  50. indexedForbidConfigs[item] = {element: item};
  51. } else {
  52. indexedForbidConfigs[item.element] = item;
  53. }
  54. });
  55. function errorMessageForElement(name) {
  56. const message = `<${name}> is forbidden`;
  57. const additionalMessage = indexedForbidConfigs[name].message;
  58. if (additionalMessage) {
  59. return `${message}, ${additionalMessage}`;
  60. }
  61. return message;
  62. }
  63. function isValidCreateElement(node) {
  64. return node.callee
  65. && node.callee.type === 'MemberExpression'
  66. && node.callee.object.name === 'React'
  67. && node.callee.property.name === 'createElement'
  68. && node.arguments.length > 0;
  69. }
  70. function reportIfForbidden(element, node) {
  71. if (has(indexedForbidConfigs, element)) {
  72. context.report({
  73. node: node,
  74. message: errorMessageForElement(element)
  75. });
  76. }
  77. }
  78. return {
  79. JSXOpeningElement: function(node) {
  80. reportIfForbidden(sourceCode.getText(node.name), node.name);
  81. },
  82. CallExpression: function(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(sourceCode.getText(argument), argument);
  94. }
  95. }
  96. };
  97. }
  98. };