Skip to content

Commit

Permalink
Add ability to fix for missing curly and option for quotes
Browse files Browse the repository at this point in the history
  • Loading branch information
jackyho112 committed Aug 10, 2017
1 parent c237913 commit 3a03eea
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 21 deletions.
2 changes: 1 addition & 1 deletion docs/rules/jsx-curly-brace-presence.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ This rule allows you to enforce curly braces or disallow unnecessary curly brace

For situations where JSX expressions are unnecessary, please refer to [the React doc](https://facebook.github.io/react/docs/jsx-in-depth.html) and [this page about JSX gotchas](https://github.com/facebook/react/blob/v15.4.0-rc.3/docs/docs/02.3-jsx-gotchas.md#html-entities).

**Fixable:** This rule is automatically fixable using the `--fix` flag on the command line but only for fixing unnecessary curly braces.
**Fixable:** This rule is automatically fixable using the `--fix` flag on the command line

## Rule Details

Expand Down
84 changes: 66 additions & 18 deletions lib/rules/jsx-curly-brace-presence.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@
const OPTION_ALWAYS = 'always';
const OPTION_NEVER = 'never';
const OPTION_IGNORE = 'ignore';
const OPTION_SINGLE_QUOTE = 'single';
const OPTION_DOUBLE_QUOTE = 'double';
const optionValues = [OPTION_ALWAYS, OPTION_NEVER, OPTION_IGNORE];
const quoteOptionsValues = [OPTION_SINGLE_QUOTE, OPTION_DOUBLE_QUOTE];
const defaultConfig = {props: OPTION_NEVER, children: OPTION_NEVER};

module.exports = {
Expand All @@ -23,28 +26,38 @@ module.exports = {
},
fixable: 'code',

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

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

function containsBackslashForEscaping(rawStringValue) {
return JSON.stringify(rawStringValue).includes('\\');
Expand All @@ -70,10 +83,45 @@ module.exports = {
});
}

function translateQuoteOptionToStringLiteral(option) {
return option === OPTION_SINGLE_QUOTE ? '\'' : '"';
}

function wrapLiteralNodeInJSXExpression(fixer, literalNode) {
let quoteStyle = translateQuoteOptionToStringLiteral(OPTION_DOUBLE_QUOTE);
const ruleOptionSlotOne = context.options[0];
const ruleOptionSlotTwo = context.options[1];

if (ruleOptionSlotTwo) {
quoteStyle = translateQuoteOptionToStringLiteral(ruleOptionSlotTwo);
} else if (
typeof ruleOptionSlotOne === 'string' &&
quoteOptionsValues.includes(ruleOptionSlotOne)
) {
quoteStyle = translateQuoteOptionToStringLiteral(ruleOptionSlotOne);
}

// The only possible case here is a string literal as a JSX child
return fixer.replaceText(
literalNode,
`{${quoteStyle}${literalNode.raw}${quoteStyle}}`
);
}

function reportMissingCurly(literalNode) {
context.report({
node: literalNode,
message: 'Need to wrap this literal in a JSX expression.'
message: 'Need to wrap this literal in a JSX expression.',
fix: function(fixer) {
if (literalNode.parent.type === 'JSXAttribute') {
return [
fixer.insertTextBefore(literalNode, '{'),
fixer.insertTextAfter(literalNode, '}')
];
}

return wrapLiteralNodeInJSXExpression(fixer, literalNode);
}
});
}

Expand Down
19 changes: 17 additions & 2 deletions tests/lib/rules/jsx-curly-brace-presence.js
Original file line number Diff line number Diff line change
Expand Up @@ -180,11 +180,25 @@ ruleTester.run('jsx-curly-brace-presence', rule, {
},
{
code: '<MyComponent prop=\'bar\'>foo</MyComponent>',
output: '<MyComponent prop={\'bar\'}>foo</MyComponent>',
options: [{props: 'always'}],
errors: [{message: missingCurlyMessage}]
},
{
code: '<MyComponent>foo</MyComponent>',
code: '<MyComponent>foo bar </MyComponent>',
output: '<MyComponent>{\"foo bar \"}</MyComponent>',
options: [{children: 'always'}],
errors: [{message: missingCurlyMessage}]
},
{
code: '<MyComponent>foo bar <App/></MyComponent>',
output: '<MyComponent>{\"foo bar \"}<App/></MyComponent>',
options: [{children: 'always'}],
errors: [{message: missingCurlyMessage}]
},
{
code: '<MyComponent>foo\\nbar</MyComponent>',
output: '<MyComponent>{\"foo\\nbar\"}</MyComponent>',
options: [{children: 'always'}],
errors: [{message: missingCurlyMessage}]
},
Expand All @@ -197,7 +211,8 @@ ruleTester.run('jsx-curly-brace-presence', rule, {
]
},
{
code: '<MyComponent prop=\'bar\'>\'foo\'</MyComponent>',
code: '<MyComponent prop=\'bar\'>foo</MyComponent>',
output: '<MyComponent prop={\'bar\'}>{\"foo\"}</MyComponent>',
options: ['always'],
errors: [
{message: missingCurlyMessage}, {message: missingCurlyMessage}
Expand Down

0 comments on commit 3a03eea

Please sign in to comment.