1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 | 'use strict';
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 | const 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 |
|
48 | const whitespaceRegex = /(?:^\s|\s$)/;
|
49 |
|
50 | function isInline(node) {
|
51 | if (node.type === 'Literal') {
|
52 |
|
53 | const value = node.value;
|
54 |
|
55 |
|
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 |
|
67 | const ERROR = 'Child elements which render as inline HTML elements should be separated by a space or wrapped in block level elements.';
|
68 |
|
69 |
|
70 |
|
71 |
|
72 |
|
73 | module.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 | };
|