UNPKG

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