no-adjacent-inline-elements.js 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. /**
  2. * @fileoverview Prevent adjacent inline elements not separated by whitespace.
  3. * @author Sean Hayes
  4. */
  5. 'use strict';
  6. const docsUrl = require('../util/docsUrl');
  7. // ------------------------------------------------------------------------------
  8. // Helpers
  9. // ------------------------------------------------------------------------------
  10. // https://developer.mozilla.org/en-US/docs/Web/HTML/Inline_elements
  11. const inlineNames = [
  12. 'a',
  13. 'b',
  14. 'big',
  15. 'i',
  16. 'small',
  17. 'tt',
  18. 'abbr',
  19. 'acronym',
  20. 'cite',
  21. 'code',
  22. 'dfn',
  23. 'em',
  24. 'kbd',
  25. 'strong',
  26. 'samp',
  27. 'time',
  28. 'var',
  29. 'bdo',
  30. 'br',
  31. 'img',
  32. 'map',
  33. 'object',
  34. 'q',
  35. 'script',
  36. 'span',
  37. 'sub',
  38. 'sup',
  39. 'button',
  40. 'input',
  41. 'label',
  42. 'select',
  43. 'textarea'
  44. ];
  45. // Note: raw   will be transformed into \u00a0.
  46. const whitespaceRegex = /(?:^\s|\s$)/;
  47. function isInline(node) {
  48. if (node.type === 'Literal') {
  49. // Regular whitespace will be removed.
  50. const value = node.value;
  51. // To properly separate inline elements, each end of the literal will need
  52. // whitespace.
  53. return !whitespaceRegex.test(value);
  54. }
  55. if (node.type === 'JSXElement' && inlineNames.indexOf(node.openingElement.name.name) > -1) {
  56. return true;
  57. }
  58. if (node.type === 'CallExpression' && inlineNames.indexOf(node.arguments[0].value) > -1) {
  59. return true;
  60. }
  61. return false;
  62. }
  63. // ------------------------------------------------------------------------------
  64. // Rule Definition
  65. // ------------------------------------------------------------------------------
  66. module.exports = {
  67. meta: {
  68. docs: {
  69. description: 'Prevent adjacent inline elements not separated by whitespace.',
  70. category: 'Best Practices',
  71. recommended: false,
  72. url: docsUrl('no-adjacent-inline-elements')
  73. },
  74. schema: [],
  75. messages: {
  76. inlineElement: 'Child elements which render as inline HTML elements should be separated by a space or wrapped in block level elements.'
  77. }
  78. },
  79. create(context) {
  80. function validate(node, children) {
  81. let currentIsInline = false;
  82. let previousIsInline = false;
  83. if (!children) {
  84. return;
  85. }
  86. for (let i = 0; i < children.length; i++) {
  87. currentIsInline = isInline(children[i]);
  88. if (previousIsInline && currentIsInline) {
  89. context.report({
  90. node,
  91. messageId: 'inlineElement'
  92. });
  93. return;
  94. }
  95. previousIsInline = currentIsInline;
  96. }
  97. }
  98. return {
  99. JSXElement(node) {
  100. validate(node, node.children);
  101. },
  102. CallExpression(node) {
  103. if (!node.callee || node.callee.type !== 'MemberExpression' || node.callee.property.name !== 'createElement') {
  104. return;
  105. }
  106. if (node.arguments.length < 2 || !node.arguments[2]) {
  107. return;
  108. }
  109. const children = node.arguments[2].elements;
  110. validate(node, children);
  111. }
  112. };
  113. }
  114. };