diff --git a/index.js b/index.js
index 6c1a933ac9..765f681cd0 100644
--- a/index.js
+++ b/index.js
@@ -9,9 +9,10 @@ const allRules = {
'display-name': require('./lib/rules/display-name'),
'forbid-component-props': require('./lib/rules/forbid-component-props'),
'forbid-elements': require('./lib/rules/forbid-elements'),
- 'forbid-prop-types': require('./lib/rules/forbid-prop-types'),
'forbid-foreign-prop-types': require('./lib/rules/forbid-foreign-prop-types'),
+ 'forbid-prop-types': require('./lib/rules/forbid-prop-types'),
'jsx-boolean-value': require('./lib/rules/jsx-boolean-value'),
+ 'jsx-child-element-spacing': require('./lib/rules/jsx-child-element-spacing'),
'jsx-closing-bracket-location': require('./lib/rules/jsx-closing-bracket-location'),
'jsx-closing-tag-location': require('./lib/rules/jsx-closing-tag-location'),
'jsx-curly-spacing': require('./lib/rules/jsx-curly-spacing'),
@@ -23,12 +24,12 @@ const allRules = {
'jsx-indent-props': require('./lib/rules/jsx-indent-props'),
'jsx-key': require('./lib/rules/jsx-key'),
'jsx-max-props-per-line': require('./lib/rules/jsx-max-props-per-line'),
- 'jsx-one-expression-per-line': require('./lib/rules/jsx-one-expression-per-line'),
'jsx-no-bind': require('./lib/rules/jsx-no-bind'),
'jsx-no-comment-textnodes': require('./lib/rules/jsx-no-comment-textnodes'),
'jsx-no-duplicate-props': require('./lib/rules/jsx-no-duplicate-props'),
'jsx-no-literals': require('./lib/rules/jsx-no-literals'),
'jsx-no-target-blank': require('./lib/rules/jsx-no-target-blank'),
+ 'jsx-one-expression-per-line': require('./lib/rules/jsx-one-expression-per-line'),
'button-has-type': require('./lib/rules/button-has-type'),
'jsx-no-undef': require('./lib/rules/jsx-no-undef'),
'jsx-curly-brace-presence': require('./lib/rules/jsx-curly-brace-presence'),
diff --git a/lib/rules/jsx-child-element-spacing.js b/lib/rules/jsx-child-element-spacing.js
new file mode 100644
index 0000000000..82d56917cb
--- /dev/null
+++ b/lib/rules/jsx-child-element-spacing.js
@@ -0,0 +1,103 @@
+'use strict';
+
+// This list is taken from https://developer.mozilla.org/en-US/docs/Web/HTML/Inline_elements
+const INLINE_ELEMENTS = new Set([
+ 'a',
+ 'abbr',
+ 'acronym',
+ 'b',
+ 'bdo',
+ 'big',
+ 'br',
+ 'button',
+ 'cite',
+ 'code',
+ 'dfn',
+ 'em',
+ 'i',
+ 'img',
+ 'input',
+ 'kbd',
+ 'label',
+ 'map',
+ 'object',
+ 'q',
+ 'samp',
+ 'script',
+ 'select',
+ 'small',
+ 'span',
+ 'strong',
+ 'sub',
+ 'sup',
+ 'textarea',
+ 'tt',
+ 'var'
+]);
+
+module.exports = {
+ meta: {
+ docs: {
+ description: 'Ensures inline tags are not rendered without spaces between them',
+ category: 'Stylistic Issues',
+ recommended: false
+ },
+ fixable: false,
+ schema: [
+ {
+ type: 'object',
+ properties: {},
+ default: {},
+ additionalProperties: false
+ }
+ ]
+ },
+ create: function (context) {
+ const elementName = node => (
+ node.openingElement &&
+ node.openingElement.name &&
+ node.openingElement.name.type === 'JSXIdentifier' &&
+ node.openingElement.name.name
+ );
+
+ const isInlineElement = node => (
+ node.type === 'JSXElement' &&
+ INLINE_ELEMENTS.has(elementName(node))
+ );
+
+ const TEXT_FOLLOWING_ELEMENT_PATTERN = /^\s*\n\s*\S/;
+ const TEXT_PRECEDING_ELEMENT_PATTERN = /\S\s*\n\s*$/;
+
+ return {
+ JSXElement: function(node) {
+ let lastChild = null;
+ let child = null;
+ (node.children.concat([null])).forEach(nextChild => {
+ if (
+ (lastChild || nextChild) &&
+ (!lastChild || isInlineElement(lastChild)) &&
+ (child && child.type === 'Literal') &&
+ (!nextChild || isInlineElement(nextChild)) &&
+ true
+ ) {
+ if (lastChild && child.value.match(TEXT_FOLLOWING_ELEMENT_PATTERN)) {
+ context.report({
+ node: child,
+ loc: child.loc,
+ message: `Ambiguous spacing after previous element ${elementName(lastChild)}`
+ });
+ } else if (nextChild && child.value.match(TEXT_PRECEDING_ELEMENT_PATTERN)) {
+ context.report({
+ node: child,
+ loc: child.loc,
+ message: `Ambiguous spacing before next element ${elementName(nextChild)}`
+ });
+ }
+ }
+ lastChild = child;
+ child = nextChild;
+ });
+ }
+ };
+ }
+};
diff --git a/tests/lib/rules/jsx-child-element-spacing.js b/tests/lib/rules/jsx-child-element-spacing.js
new file mode 100644
index 0000000000..51ebfcd671
--- /dev/null
+++ b/tests/lib/rules/jsx-child-element-spacing.js
@@ -0,0 +1,196 @@
+'use strict';
+
+const rule = require('../../../lib/rules/jsx-child-element-spacing');
+const RuleTester = require('eslint').RuleTester;
+const parserOptions = {
+ sourceType: 'module',
+ ecmaFeatures: {
+ jsx: true
+ }
+};
+
+const ruleTester = new RuleTester({parserOptions});
+ruleTester.run('jsx-child-element-spacing', rule, {
+ valid: [{
+ code: `
+
+ foo
+
+ `
+ }, {
+ code: `
+
+ bar
+
+ `
+ }, {
+ code: `
+
+
+ nested
+
+
+ `
+ }, {
+ code: `
+
+ foo
+ bar
+
+ `
+ }, {
+ code: `
+
+ foobarbaz
+
+ `
+ }, {
+ code: `
+
+ foo
+ {' '}
+ bar
+ {' '}
+ baz
+
+ `
+ }, {
+ code: `
+
+ foo
+ {' '}bar{' '}
+ baz
+
+ `
+ }, {
+ code: `
+
+ foo{' '}
+ bar
+ {' '}baz
+
+ `
+ }, {
+ code: `
+
+ foo{/*
+ */}bar{/*
+ */}baz
+
+ `
+ }, {
+ code: `
+
+ Please take a look at this link.
+
+ `
+ }, {
+ code: `
+
+ Please take a look at
+ {' '}
+ this link.
+
+ `
+ }, {
+ code: `
+
+ A
+ B
+
+ `
+ }, {
+ code: `
+
+ A
B
+
+ `
+ }, {
+ code: `
+
+ foo
+ bar
+
+ `
+ }, {
+ code: `
+
+
+ nested1
+ nested2
+
+
+ `
+ }, {
+ code: `
+
+ A
+ B
+
+ `
+ }],
+
+ invalid: [{
+ code: `
+
+ foo
+ bar
+
+ `,
+ errors: [
+ {message: 'Ambiguous spacing before next element a'}
+ ]
+ }, {
+ code: `
+
+ bar
+ baz
+
+ `,
+ errors: [
+ {message: 'Ambiguous spacing after previous element a'}
+ ]
+ }, {
+ code: `
+
+ {' '}bar
+ baz
+
+ `,
+ errors: [
+ {message: 'Ambiguous spacing after previous element a'}
+ ]
+ }, {
+ code: `
+
+ Please take a look at
+ this link.
+
+ `,
+ errors: [
+ {message: 'Ambiguous spacing before next element a'}
+ ]
+ }, {
+ code: `
+
+ Some loops
and some
+ if
statements.
+
+ `,
+ errors: [
+ {message: 'Ambiguous spacing before next element code'}
+ ]
+ }, {
+ code: `
+
+ Here is
+ a link and here is
+ another
+
+ `,
+ errors: [
+ {message: 'Ambiguous spacing before next element a'},
+ {message: 'Ambiguous spacing before next element a'}
+ ]
+ }]
+});