Skip to content

Commit

Permalink
[Fix] jsx-curly-brace-presence: Fix curly-brace-presence edge cases
Browse files Browse the repository at this point in the history
  • Loading branch information
rafbgarcia authored and ljharb committed Dec 13, 2019
1 parent 1ef658a commit 7f87310
Show file tree
Hide file tree
Showing 2 changed files with 113 additions and 5 deletions.
55 changes: 50 additions & 5 deletions lib/rules/jsx-curly-brace-presence.js
Expand Up @@ -62,6 +62,7 @@ module.exports = {
},

create(context) {
const HTML_ENTITY_REGEX = () => /&[A-Za-z\d#]+;/g;
const ruleOptions = context.options[0];
const userConfig = typeof ruleOptions === 'string' ?
{props: ruleOptions, children: ruleOptions} :
Expand All @@ -76,7 +77,11 @@ module.exports = {
}

function containsHTMLEntity(rawStringValue) {
return /&[A-Za-z\d#]+;/.test(rawStringValue);
return HTML_ENTITY_REGEX().test(rawStringValue);
}

function containsOnlyHtmlEntities(rawStringValue) {
return rawStringValue.replace(HTML_ENTITY_REGEX(), '').trim() === '';
}

function containsDisallowedJSXTextChars(rawStringValue) {
Expand Down Expand Up @@ -111,6 +116,42 @@ module.exports = {
return false;
}

function isLineBreak(text) {
return containsLineTerminators(text) && text.trim() === '';
}

function wrapNonHTMLEntities(text) {
const HTML_ENTITY = '<HTML_ENTITY>';
const withCurlyBraces = text.split(HTML_ENTITY_REGEX()).map(word => (
word === '' ? '' : `{${JSON.stringify(word)}}`
)).join(HTML_ENTITY);

const htmlEntities = text.match(HTML_ENTITY_REGEX());
return htmlEntities.reduce((acc, htmlEntitiy) => (
acc.replace(HTML_ENTITY, htmlEntitiy)
), withCurlyBraces);
}

function wrapWithCurlyBraces(rawText) {
if (!containsLineTerminators(rawText)) {
return `{${JSON.stringify(rawText)}}`;
}

return rawText.split('\n').map((line) => {
if (line.trim() === '') {
return line;
}
const firstCharIndex = line.search(/[^\s]/);
const leftWhitespace = line.slice(0, firstCharIndex);
const text = line.slice(firstCharIndex);

if (containsHTMLEntity(line)) {
return `${leftWhitespace}${wrapNonHTMLEntities(text)}`;
}
return `${leftWhitespace}{${JSON.stringify(text)}}`;
}).join('\n');
}

/**
* Report and fix an unnecessary curly brace violation on a node
* @param {ASTNode} JSXExpressionNode - The AST node with an unnecessary JSX expression
Expand Down Expand Up @@ -153,8 +194,9 @@ module.exports = {
// by either using the real character or the unicode equivalent.
// If it contains any line terminator character, bail out as well.
if (
containsHTMLEntity(literalNode.raw) ||
containsLineTerminators(literalNode.raw)
containsOnlyHtmlEntities(literalNode.raw) ||
(literalNode.parent.type === 'JSXAttribute' && containsLineTerminators(literalNode.raw)) ||
isLineBreak(literalNode.raw)
) {
return null;
}
Expand All @@ -163,7 +205,7 @@ module.exports = {
`{"${escapeDoubleQuotes(escapeBackslashes(
literalNode.raw.substring(1, literalNode.raw.length - 1)
))}"}` :
`{${JSON.stringify(literalNode.value)}}`;
wrapWithCurlyBraces(literalNode.raw);

return fixer.replaceText(literalNode, expression);
}
Expand Down Expand Up @@ -299,7 +341,10 @@ module.exports = {
}

function shouldCheckForMissingCurly(node, config) {
if (node.raw.trim() === '') {
if (
isLineBreak(node.raw) ||
containsOnlyHtmlEntities(node.raw)
) {
return false;
}
const parent = node.parent;
Expand Down
63 changes: 63 additions & 0 deletions tests/lib/rules/jsx-curly-brace-presence.js
Expand Up @@ -391,6 +391,15 @@ ruleTester.run('jsx-curly-brace-presence', rule, {
`,
parser: parsers.BABEL_ESLINT,
options: [{children: 'always'}]
},
{
code: `
<App>
<Component />&nbsp;
&nbsp;
</App>
`,
options: [{children: 'always'}]
}
],

Expand Down Expand Up @@ -698,6 +707,60 @@ ruleTester.run('jsx-curly-brace-presence', rule, {
{message: missingCurlyMessage}, {message: missingCurlyMessage}
],
options: ['always']
},
{
code: `
<App>
foo bar
<div>foo bar foo</div>
<span>
foo bar <i>foo bar</i>
<strong>
foo bar
</strong>
</span>
</App>
`,
output: `
<App>
{"foo bar"}
<div>{"foo bar foo"}</div>
<span>
{"foo bar "}<i>{"foo bar"}</i>
<strong>
{"foo bar"}
</strong>
</span>
</App>
`,
errors: [
{message: missingCurlyMessage},
{message: missingCurlyMessage},
{message: missingCurlyMessage},
{message: missingCurlyMessage},
{message: missingCurlyMessage}
],
options: [{children: 'always'}]
},
{
code: `
<App>
&lt;Component&gt;
&nbsp;<Component />&nbsp;
&nbsp;
</App>
`,
output: `
<App>
&lt;{"Component"}&gt;
&nbsp;<Component />&nbsp;
&nbsp;
</App>
`,
errors: [
{message: missingCurlyMessage}
],
options: [{children: 'always'}]
}
]
});

0 comments on commit 7f87310

Please sign in to comment.