forbid-elements.js 3.1 KB

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