116 lines
2.7 KiB
JavaScript
116 lines
2.7 KiB
JavaScript
/**
|
|
* @fileoverview Prevent adjacent inline elements not separated by whitespace.
|
|
* @author Sean Hayes
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
// ------------------------------------------------------------------------------
|
|
// Helpers
|
|
// ------------------------------------------------------------------------------
|
|
|
|
// https://developer.mozilla.org/en-US/docs/Web/HTML/Inline_elements
|
|
const inlineNames = [
|
|
'a',
|
|
'b',
|
|
'big',
|
|
'i',
|
|
'small',
|
|
'tt',
|
|
'abbr',
|
|
'acronym',
|
|
'cite',
|
|
'code',
|
|
'dfn',
|
|
'em',
|
|
'kbd',
|
|
'strong',
|
|
'samp',
|
|
'time',
|
|
'var',
|
|
'bdo',
|
|
'br',
|
|
'img',
|
|
'map',
|
|
'object',
|
|
'q',
|
|
'script',
|
|
'span',
|
|
'sub',
|
|
'sup',
|
|
'button',
|
|
'input',
|
|
'label',
|
|
'select',
|
|
'textarea'
|
|
];
|
|
// Note: raw will be transformed into \u00a0.
|
|
const whitespaceRegex = /(?:^\s|\s$)/;
|
|
|
|
function isInline(node) {
|
|
if (node.type === 'Literal') {
|
|
// Regular whitespace will be removed.
|
|
const value = node.value;
|
|
// To properly separate inline elements, each end of the literal will need
|
|
// whitespace.
|
|
return !whitespaceRegex.test(value);
|
|
}
|
|
if (node.type === 'JSXElement' && inlineNames.indexOf(node.openingElement.name.name) > -1) {
|
|
return true;
|
|
}
|
|
if (node.type === 'CallExpression' && inlineNames.indexOf(node.arguments[0].value) > -1) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
const ERROR = 'Child elements which render as inline HTML elements should be separated by a space or wrapped in block level elements.';
|
|
|
|
// ------------------------------------------------------------------------------
|
|
// Rule Definition
|
|
// ------------------------------------------------------------------------------
|
|
|
|
module.exports = {
|
|
ERROR,
|
|
meta: {
|
|
docs: {
|
|
description: 'Prevent adjacent inline elements not separated by whitespace.',
|
|
category: 'Best Practices',
|
|
recommended: false
|
|
},
|
|
schema: []
|
|
},
|
|
create(context) {
|
|
function validate(node, children) {
|
|
let currentIsInline = false;
|
|
let previousIsInline = false;
|
|
for (let i = 0; i < children.length; i++) {
|
|
currentIsInline = isInline(children[i]);
|
|
if (previousIsInline && currentIsInline) {
|
|
context.report({
|
|
node,
|
|
message: ERROR
|
|
});
|
|
return;
|
|
}
|
|
previousIsInline = currentIsInline;
|
|
}
|
|
}
|
|
return {
|
|
JSXElement(node) {
|
|
validate(node, node.children);
|
|
},
|
|
CallExpression(node) {
|
|
if (!node.callee || node.callee.type !== 'MemberExpression' || node.callee.property.name !== 'createElement') {
|
|
return;
|
|
}
|
|
if (node.arguments.length < 2 || !node.arguments[2]) {
|
|
return;
|
|
}
|
|
const children = node.arguments[2].elements;
|
|
validate(node, children);
|
|
}
|
|
};
|
|
}
|
|
};
|