@@ -62,6 +62,7 @@ module.exports = {
6262 } ,
6363
6464 create ( context ) {
65+ const HTML_ENTITY_REGEX = ( ) => / & [ A - Z a - z \d # ] + ; / g;
6566 const ruleOptions = context . options [ 0 ] ;
6667 const userConfig = typeof ruleOptions === 'string' ?
6768 { props : ruleOptions , children : ruleOptions } :
@@ -76,7 +77,11 @@ module.exports = {
7677 }
7778
7879 function containsHTMLEntity ( rawStringValue ) {
79- return / & [ A - Z a - z \d # ] + ; / . test ( rawStringValue ) ;
80+ return HTML_ENTITY_REGEX ( ) . test ( rawStringValue ) ;
81+ }
82+
83+ function containsOnlyHtmlEntities ( rawStringValue ) {
84+ return rawStringValue . replace ( HTML_ENTITY_REGEX ( ) , '' ) . trim ( ) === '' ;
8085 }
8186
8287 function containsDisallowedJSXTextChars ( rawStringValue ) {
@@ -111,6 +116,42 @@ module.exports = {
111116 return false ;
112117 }
113118
119+ function isLineBreak ( text ) {
120+ return containsLineTerminators ( text ) && text . trim ( ) === '' ;
121+ }
122+
123+ function wrapNonHTMLEntities ( text ) {
124+ const HTML_ENTITY = '<HTML_ENTITY>' ;
125+ const withCurlyBraces = text . split ( HTML_ENTITY_REGEX ( ) ) . map ( word => (
126+ word === '' ? '' : `{${ JSON . stringify ( word ) } }`
127+ ) ) . join ( HTML_ENTITY ) ;
128+
129+ const htmlEntities = text . match ( HTML_ENTITY_REGEX ( ) ) ;
130+ return htmlEntities . reduce ( ( acc , htmlEntitiy ) => (
131+ acc . replace ( HTML_ENTITY , htmlEntitiy )
132+ ) , withCurlyBraces ) ;
133+ }
134+
135+ function wrapWithCurlyBraces ( rawText ) {
136+ if ( ! containsLineTerminators ( rawText ) ) {
137+ return `{${ JSON . stringify ( rawText ) } }` ;
138+ }
139+
140+ return rawText . split ( '\n' ) . map ( ( line ) => {
141+ if ( line . trim ( ) === '' ) {
142+ return line ;
143+ }
144+ const firstCharIndex = line . search ( / [ ^ \s ] / ) ;
145+ const leftWhitespace = line . slice ( 0 , firstCharIndex ) ;
146+ const text = line . slice ( firstCharIndex ) ;
147+
148+ if ( containsHTMLEntity ( line ) ) {
149+ return `${ leftWhitespace } ${ wrapNonHTMLEntities ( text ) } ` ;
150+ }
151+ return `${ leftWhitespace } {${ JSON . stringify ( text ) } }` ;
152+ } ) . join ( '\n' ) ;
153+ }
154+
114155 /**
115156 * Report and fix an unnecessary curly brace violation on a node
116157 * @param {ASTNode } JSXExpressionNode - The AST node with an unnecessary JSX expression
@@ -153,8 +194,9 @@ module.exports = {
153194 // by either using the real character or the unicode equivalent.
154195 // If it contains any line terminator character, bail out as well.
155196 if (
156- containsHTMLEntity ( literalNode . raw ) ||
157- containsLineTerminators ( literalNode . raw )
197+ containsOnlyHtmlEntities ( literalNode . raw ) ||
198+ ( literalNode . parent . type === 'JSXAttribute' && containsLineTerminators ( literalNode . raw ) ) ||
199+ isLineBreak ( literalNode . raw )
158200 ) {
159201 return null ;
160202 }
@@ -163,7 +205,7 @@ module.exports = {
163205 `{"${ escapeDoubleQuotes ( escapeBackslashes (
164206 literalNode . raw . substring ( 1 , literalNode . raw . length - 1 )
165207 ) ) } "}` :
166- `{ ${ JSON . stringify ( literalNode . value ) } }` ;
208+ wrapWithCurlyBraces ( literalNode . raw ) ;
167209
168210 return fixer . replaceText ( literalNode , expression ) ;
169211 }
@@ -299,7 +341,10 @@ module.exports = {
299341 }
300342
301343 function shouldCheckForMissingCurly ( node , config ) {
302- if ( node . raw . trim ( ) === '' ) {
344+ if (
345+ isLineBreak ( node . raw ) ||
346+ containsOnlyHtmlEntities ( node . raw )
347+ ) {
303348 return false ;
304349 }
305350 const parent = node . parent ;
0 commit comments