Skip to content

Commit

Permalink
Account for template literal and a single string option to set default
Browse files Browse the repository at this point in the history
  • Loading branch information
jackyho112 committed Aug 8, 2017
1 parent 2d695c4 commit ba3dc83
Show file tree
Hide file tree
Showing 2 changed files with 107 additions and 35 deletions.
76 changes: 41 additions & 35 deletions lib/rules/jsx-curly-brace-presence.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,41 +23,49 @@ module.exports = {
},
fixable: 'code',

schema: [
{
type: 'object',
properties: {
props: {enum: optionValues, default: optionNever},
children: {enum: optionValues, default: optionNever}
schema: [{
oneOf: [
{
type: 'object',
properties: {
props: {enum: optionValues, default: optionNever},
children: {enum: optionValues, default: optionNever}
},
additionalProperties: false
},
additionalProperties: false
}
]
{
enum: optionValues
}
]
}]
},

create: function(context) {
const userConfig = Object.assign(
{},
defaultConfig,
context.options[0]
);
const ruleOptions = context.options[0];
const userConfig = typeof ruleOptions === 'string' ?
{props: ruleOptions, children: ruleOptions} :
Object.assign({}, defaultConfig, ruleOptions);

function containsBackslashForEscaping(rawStringValue) {
return JSON.stringify(rawStringValue).includes('\\');
}

function reportUnnecessaryCurly(node) {
/**
* Report an unnecessary curly brace violation on a node
* @param {ASTNode} node - The AST node with an unnecessary JSX expression
* @param {String} text - The text to replace the unnecessary JSX expression
*/
function reportUnnecessaryCurly(node, text) {
context.report({
node: node,
message: 'Curly braces are unnecessary here.',
fix: function(fixer) {
let {expression: {value}} = node;

let textToReplace = text;
if (node.parent.type === 'JSXAttribute') {
value = `"${value}"`;
textToReplace = `"${text}"`;
}

return fixer.replaceText(node, value);
return fixer.replaceText(node, textToReplace);
}
});
}
Expand All @@ -70,17 +78,24 @@ module.exports = {
}

function lintUnnecessaryCurly(node) {
const {expression} = node;
const {expression, expression: {type}} = node;

if (
typeof expression.value === 'string' &&
type === 'Literal' &&
typeof expression.value === 'string' &&
!containsBackslashForEscaping(expression.raw)
) {
reportUnnecessaryCurly(node);
reportUnnecessaryCurly(node, expression.value);
} else if (
type === 'TemplateLiteral' &&
expression.expressions.length === 0 &&
!containsBackslashForEscaping(expression.quasis[0].value.raw)
) {
reportUnnecessaryCurly(node, expression.quasis[0].value.cooked);
}
}

function areRuleConditionsSatisfied({parentType, config, ruleCondition}) {
function areRuleConditionsSatisfied(parentType, config, ruleCondition) {
return (
parentType === 'JSXAttribute' && config.props === ruleCondition
) || (
Expand All @@ -91,24 +106,15 @@ module.exports = {
function shouldCheckForUnnecessaryCurly(expressionType, parent, config) {
const {type: parentType} = parent;

if (
expressionType !== 'Literal' || (
parentType === 'JSXElement' &&
parent.children.length !== 1
)
) {
if (parentType === 'JSXElement' && parent.children.length !== 1) {
return false;
}

return areRuleConditionsSatisfied({
parentType, config, ruleCondition: optionNever
});
return areRuleConditionsSatisfied(parentType, config, optionNever);
}

function shouldCheckForMissingCurly(parentType, config) {
return areRuleConditionsSatisfied({
parentType, config, ruleCondition: optionAlways
});
return areRuleConditionsSatisfied(parentType, config, optionAlways);
}

// --------------------------------------------------------------------------
Expand Down
66 changes: 66 additions & 0 deletions tests/lib/rules/jsx-curly-brace-presence.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,37 @@ const unnecessaryCurlyMessage = 'Curly braces are unnecessary here.';
const ruleTester = new RuleTester({parserOptions});
ruleTester.run('jsx-curly-brace-presence', rule, {
valid: [
{
code: '<App {...props}>foo</App>'
},
{
code: '<App {...props}>foo</App>',
options: [{props: 'never'}]
},
{
code: '<App {...props}>foo</App>',
options: [{props: 'always'}]
},
{
code: '<App {...props}>{`Hello ${word} World`}</App>',
options: [{children: 'never'}]
},
{
code: '<App {...props}>{`Hello \\n World`}</App>',
options: [{children: 'never'}]
},
{
code: '<App {...props}>{`Hello ${word} World`}{`foo`}</App>',
options: [{children: 'never'}]
},
{
code: '<App prop={`foo ${word} bar`}>foo</App>',
options: [{props: 'never'}]
},
{
code: '<App prop={`foo \\n bar`}>foo</App>',
options: [{props: 'never'}]
},
{
code: '<App>{<myApp></myApp>}</App>'
},
Expand Down Expand Up @@ -97,10 +128,30 @@ ruleTester.run('jsx-curly-brace-presence', rule, {
{
code: '<MyComponent prop={\'bar\'}>foo</MyComponent>',
options: [{children: 'never', props: 'always'}]
},
{
code: '<MyComponent prop={\'bar\'}>{\'foo\'}</MyComponent>',
options: ['always']
},
{
code: '<MyComponent prop=\'bar\'>\'foo\'</MyComponent>',
options: ['never']
}
],

invalid: [
{
code: '<App prop={`foo`}>foo</App>',
output: '<App prop="foo">foo</App>',
options: [{props: 'never'}],
errors: [{message: unnecessaryCurlyMessage}]
},
{
code: '<App>{`foo`}</App>',
output: '<App>foo</App>',
options: [{children: 'never'}],
errors: [{message: unnecessaryCurlyMessage}]
},
{
code: '<MyComponent>{\'foo\'}</MyComponent>',
output: '<MyComponent>foo</MyComponent>',
Expand Down Expand Up @@ -132,6 +183,21 @@ ruleTester.run('jsx-curly-brace-presence', rule, {
code: '<MyComponent>foo</MyComponent>',
options: [{children: 'always'}],
errors: [{message: missingCurlyMessage}]
},
{
code: '<MyComponent prop={\'bar\'}>{\'foo\'}</MyComponent>',
output: '<MyComponent prop="bar">foo</MyComponent>',
options: ['never'],
errors: [
{message: unnecessaryCurlyMessage}, {message: unnecessaryCurlyMessage}
]
},
{
code: '<MyComponent prop=\'bar\'>\'foo\'</MyComponent>',
options: ['always'],
errors: [
{message: missingCurlyMessage}, {message: missingCurlyMessage}
]
}
]
});

0 comments on commit ba3dc83

Please sign in to comment.