Skip to content

Commit

Permalink
Prevent adjacent inline elements not separated by whitespace.
Browse files Browse the repository at this point in the history
  • Loading branch information
SeanHayes committed Apr 19, 2017
1 parent fd90dae commit fc3405c
Show file tree
Hide file tree
Showing 3 changed files with 159 additions and 1 deletion.
3 changes: 2 additions & 1 deletion index.js
Expand Up @@ -64,7 +64,8 @@ var allRules = {
'no-comment-textnodes': require('./lib/rules/no-comment-textnodes'),
'require-extension': require('./lib/rules/require-extension'),
'wrap-multilines': require('./lib/rules/wrap-multilines'),
'jsx-tag-spacing': require('./lib/rules/jsx-tag-spacing')
'jsx-tag-spacing': require('./lib/rules/jsx-tag-spacing'),
'no-adjacent-inline-elements': require('./lib/rules/no-adjacent-inline-elements')
};

function filterRules(rules, predicate) {
Expand Down
79 changes: 79 additions & 0 deletions lib/rules/no-adjacent-inline-elements.js
@@ -0,0 +1,79 @@
/**
* @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
var 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').split(' ');
var nbsp = ' ';

function isInline(node) {
if (node.type === 'Literal') {
// Regular whitespace will be removed.
var raw = node.raw.trim();
// To properly separate inline elements, each end of the literal will need
// a  .
return !(raw.startsWith(nbsp) && raw.endsWith(nbsp));
}
if (node.type !== 'JSXElement') {
// Skip.
return false;
}
var name = node.openingElement.name.name;
if (inlineNames.indexOf(name) > -1) {
return true;
}
return false;
}

var 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: ERROR,
meta: {
docs: {
description: 'Prevent adjacent inline elements not separated by whitespace.',
category: 'Best Practices',
recommended: false
},
schema: []
},
create: function(context) {
function validate(node) {
var currentIsInline = false;
var previousIsInline = false;
for (var i = 0; i < node.children.length; i++) {
currentIsInline = isInline(node.children[i]);
if (previousIsInline && currentIsInline) {
context.report({
node: node,
message: ERROR
});
return;
}
previousIsInline = currentIsInline;
}

}
return {
JSXElement: function(node) {
validate(node);
},
CallExpression: function(node) {
validate(node);
}
};
}
};
78 changes: 78 additions & 0 deletions tests/lib/rules/no-adjacent-inline-elements.js
@@ -0,0 +1,78 @@
/**
* @fileoverview Tests for no-adjacent-inline-elements
* @author Sean Hayes
*/

'use strict';

// -----------------------------------------------------------------------------
// Requirements
// -----------------------------------------------------------------------------

var rule = require('../../../lib/rules/no-adjacent-inline-elements');
var RuleTester = require('eslint').RuleTester;

var ERROR = rule.ERROR;

var parserOptions = {
ecmaVersion: 6,
ecmaFeatures: {
experimentalObjectRestSpread: true,
jsx: true
}
};

// -----------------------------------------------------------------------------
// Tests
// -----------------------------------------------------------------------------

var ruleTester = new RuleTester();
ruleTester.run('no-adjacent-inline-elements', rule, {
valid: [
{
code: '<div />;',
parserOptions: parserOptions
},
{
code: '<div><div></div><div></div></div>;',
parserOptions: parserOptions
},
{
code: '<div><p></p><div></div></div>;',
parserOptions: parserOptions
},
{
code: '<div><p></p><a></a></div>;',
parserOptions: parserOptions
},
{
code: '<div><a></a>&nbsp;<a></a></div>;',
parserOptions: parserOptions
},
{
code: '<div><a></a>&nbsp;some text &nbsp; <a></a></div>;',
parserOptions: parserOptions
},
{
code: '<div><ul><li><a></a></li><li><a></a></li></ul></div>;',
parserOptions: parserOptions
}
],
invalid: [
{
code: '<div><a></a><a></a></div>;',
errors: [{message: ERROR}],
parserOptions: parserOptions
},
{
code: '<div><a></a><span></span></div>;',
errors: [{message: ERROR}],
parserOptions: parserOptions
},
{
code: '<div><a></a>&nbsp;some text <a></a></div>;',
errors: [{message: ERROR}],
parserOptions: parserOptions
}
]
});

0 comments on commit fc3405c

Please sign in to comment.