You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
123 lines
2.9 KiB
123 lines
2.9 KiB
'use strict'; |
|
|
|
const docsUrl = require('../util/docsUrl'); |
|
const report = require('../util/report'); |
|
|
|
// This list is taken from https://developer.mozilla.org/en-US/docs/Web/HTML/Inline_elements |
|
|
|
// Note: 'br' is not included because whitespace around br tags is inconsequential to the rendered output |
|
const INLINE_ELEMENTS = new Set([ |
|
'a', |
|
'abbr', |
|
'acronym', |
|
'b', |
|
'bdo', |
|
'big', |
|
'button', |
|
'cite', |
|
'code', |
|
'dfn', |
|
'em', |
|
'i', |
|
'img', |
|
'input', |
|
'kbd', |
|
'label', |
|
'map', |
|
'object', |
|
'q', |
|
'samp', |
|
'script', |
|
'select', |
|
'small', |
|
'span', |
|
'strong', |
|
'sub', |
|
'sup', |
|
'textarea', |
|
'tt', |
|
'var', |
|
]); |
|
|
|
const messages = { |
|
spacingAfterPrev: 'Ambiguous spacing after previous element {{element}}', |
|
spacingBeforeNext: 'Ambiguous spacing before next element {{element}}', |
|
}; |
|
|
|
module.exports = { |
|
meta: { |
|
docs: { |
|
description: 'Ensures inline tags are not rendered without spaces between them', |
|
category: 'Stylistic Issues', |
|
recommended: false, |
|
url: docsUrl('jsx-child-element-spacing'), |
|
}, |
|
fixable: null, |
|
|
|
messages, |
|
|
|
schema: [ |
|
{ |
|
type: 'object', |
|
properties: {}, |
|
default: {}, |
|
additionalProperties: false, |
|
}, |
|
], |
|
}, |
|
create(context) { |
|
const TEXT_FOLLOWING_ELEMENT_PATTERN = /^\s*\n\s*\S/; |
|
const TEXT_PRECEDING_ELEMENT_PATTERN = /\S\s*\n\s*$/; |
|
|
|
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 handleJSX = (node) => { |
|
let lastChild = null; |
|
let child = null; |
|
(node.children.concat([null])).forEach((nextChild) => { |
|
if ( |
|
(lastChild || nextChild) |
|
&& (!lastChild || isInlineElement(lastChild)) |
|
&& (child && (child.type === 'Literal' || child.type === 'JSXText')) |
|
&& (!nextChild || isInlineElement(nextChild)) |
|
&& true |
|
) { |
|
if (lastChild && child.value.match(TEXT_FOLLOWING_ELEMENT_PATTERN)) { |
|
report(context, messages.spacingAfterPrev, 'spacingAfterPrev', { |
|
node: lastChild, |
|
loc: lastChild.loc.end, |
|
data: { |
|
element: elementName(lastChild), |
|
}, |
|
}); |
|
} else if (nextChild && child.value.match(TEXT_PRECEDING_ELEMENT_PATTERN)) { |
|
report(context, messages.spacingBeforeNext, 'spacingBeforeNext', { |
|
node: nextChild, |
|
loc: nextChild.loc.start, |
|
data: { |
|
element: elementName(nextChild), |
|
}, |
|
}); |
|
} |
|
} |
|
lastChild = child; |
|
child = nextChild; |
|
}); |
|
}; |
|
|
|
return { |
|
JSXElement: handleJSX, |
|
JSXFragment: handleJSX, |
|
}; |
|
}, |
|
};
|
|
|