Skip to content

Commit

Permalink
Support adding ability to enfore curly brace presence
Browse files Browse the repository at this point in the history
  • Loading branch information
jackyho112 committed Aug 7, 2017
1 parent 5605a36 commit d5e5a2e
Show file tree
Hide file tree
Showing 6 changed files with 166 additions and 82 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ Finally, enable all of the rules that you would like to use. Use [our preset](#
* [react/jsx-no-literals](docs/rules/jsx-no-literals.md): Prevent usage of unwrapped JSX strings
* [react/jsx-no-target-blank](docs/rules/jsx-no-target-blank.md): Prevent usage of unsafe `target='_blank'`
* [react/jsx-no-undef](docs/rules/jsx-no-undef.md): Disallow undeclared variables in JSX
* [react/jsx-no-unnecessary-curly-brace](docs/rules/jsx-no-unnecessary-curly-brace.md): Disallow unnecessary JSX expressions
* [react/jsx-curly-brace-presence](docs/rules/jsx-curly-brace-presence.md): Disallow unnecessary JSX expressions
* [react/jsx-pascal-case](docs/rules/jsx-pascal-case.md): Enforce PascalCase for user-defined JSX components
* [react/jsx-sort-props](docs/rules/jsx-sort-props.md): Enforce props alphabetical sorting
* [react/jsx-space-before-closing](docs/rules/jsx-space-before-closing.md): Validate spacing before closing bracket in JSX (fixable)
Expand Down
2 changes: 1 addition & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ const allRules = {
'jsx-no-literals': require('./lib/rules/jsx-no-literals'),
'jsx-no-target-blank': require('./lib/rules/jsx-no-target-blank'),
'jsx-no-undef': require('./lib/rules/jsx-no-undef'),
'jsx-no-unnecessary-curly-brace': require('./lib/rules/jsx-no-unnecessary-curly-brace'),
'jsx-curly-brace-presence': require('./lib/rules/jsx-curly-brace-presence'),
'jsx-pascal-case': require('./lib/rules/jsx-pascal-case'),
'jsx-sort-props': require('./lib/rules/jsx-sort-props'),
'jsx-space-before-closing': require('./lib/rules/jsx-space-before-closing'),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
/**
* @fileoverview Prevent using string literals in React component definition
* @author Caleb Morris
* @author David Buchan-Swanson
* @fileoverview Enfore curly brace presence or disallow unnecessary curly brace in JSX
* @author Jacky Ho
*/
'use strict';

// ------------------------------------------------------------------------------
// Rule Definition
// ------------------------------------------------------------------------------

const options = {
always: 'always',
never: 'never',
ignore: 'ignore'
}
const optionValues = ['always', 'never', 'ignore']

const optionAlways = 'always';
const optionNever = 'never';
const optionIgnore = 'ignore';
const optionValues = [optionAlways, optionNever, optionIgnore]
const defaultConfig = { props: optionNever, children: optionNever }

module.exports = {
meta: {
Expand All @@ -38,13 +37,17 @@ module.exports = {
},

create: function(context) {
const config = context.options[0] || {};
const config = Object.assign(
{},
defaultConfig,
context.options[0]
);

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

function reportStyleViolation(node) {
function reportUnecessaryCurly(node) {
context.report({
node: node,
message: 'Curly braces are unnecessary here.',
Expand All @@ -60,29 +63,48 @@ module.exports = {
});
}

function isRuleViolated(node) {
function reportMissingCurly(node) {
context.report({
node: node,
message: 'Need to wrap this literal in a JSX expression.'
});
}

function lintUnnecessaryCurly(node) {
const { expression } = node;

if (
typeof expression.value === 'string' &&
!containsBackslashForEscaping(expression.raw)
) {
reportStyleViolation(node)
reportUnecessaryCurly(node)
}
}

function shouldCheckForRule(expressionType, parentType, config) {
function areRuleConditionsSatisfied({ parentType, config, ruleCondition }) {
return (
expressionType === 'Literal' && (
parentType === 'JSXAttribute' &&
config.props !== 'never'
) || (
parentType === 'JSXElement' &&
config.children !== 'never'
)
parentType === 'JSXAttribute' && config.props === ruleCondition
) || (
parentType === 'JSXElement' && config.children === ruleCondition
)
}

function shouldCheckForUnnecessaryCurly(expressionType, parentType, config) {
if (expressionType !== 'Literal') {
return false;
}

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

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

// --------------------------------------------------------------------------
// Public
// --------------------------------------------------------------------------
Expand All @@ -94,8 +116,16 @@ module.exports = {
parent: { type: parentType }
} = node

if (shouldCheckForRule(type, parentType, config)) {
isRuleViolated(node)
if (shouldCheckForUnnecessaryCurly(type, parentType, config)) {
lintUnnecessaryCurly(node)
}
},

Literal: (node) => {
const { parent: { type: parentType }} = node;

if (shouldCheckForMissingCurly(parentType, config)) {
reportMissingCurly(node)
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"coveralls": "cat ./reports/coverage/lcov.info | coveralls",
"lint": "eslint ./",
"test": "npm run lint && npm run unit-test",
"unit-test": "istanbul cover --dir reports/coverage node_modules/mocha/bin/_mocha tests/lib/rules/jsx-no-unnecessary-curly-brace.js -- --reporter dot"
"unit-test": "istanbul cover --dir reports/coverage node_modules/mocha/bin/_mocha tests/lib/rules/jsx-curly-brace-presence.js -- --reporter dot"
},
"files": [
"LICENSE",
Expand Down
110 changes: 110 additions & 0 deletions tests/lib/rules/jsx-curly-brace-presence.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/**
* @fileoverview Enfore curly brace presence or disallow unnecessary curly brace in JSX
* @author Jacky Ho
*/
'use strict';

// ------------------------------------------------------------------------------
// Requirements
// ------------------------------------------------------------------------------

const rule = require('../../../lib/rules/jsx-curly-brace-presence');
const RuleTester = require('eslint').RuleTester;
const parserOptions = {
ecmaVersion: 8,
sourceType: 'module',
ecmaFeatures: {
experimentalObjectRestSpread: true,
jsx: true
}
};

const missingCurlyMessage = 'Need to wrap this literal in a JSX expression.';
const unnecessaryCurlyMessage = 'Curly braces are unnecessary here.';

// ------------------------------------------------------------------------------
// Tests
// ------------------------------------------------------------------------------

const ruleTester = new RuleTester({parserOptions});
ruleTester.run('jsx-curly-spacing', rule, {
valid: [
{
code: "<App>{<myApp></myApp>}</App>"
},
{
code: "<App>{[]}</App>"
},
{
code: "<App>foo</App>"
},
{
code: "<App prop='bar'>foo</App>"
},
{
code: "<App prop='bar'>{'foo \\n bar'}</App>"
},
{
code: "<App prop={'foo \\u00b7 bar'}>foo</App>"
},
{
code: "<MyComponent prop={'bar'}>foo</MyComponent>",
options: [{ props: 'always' }],
},
{
code: "<MyComponent>{'foo'}</MyComponent>",
options: [{ children: 'always' }],
},
{
code: "<MyComponent>{'foo'}</MyComponent>",
options: [{ children: 'ignore' }],
},
{
code: "<MyComponent prop={'bar'}>foo</MyComponent>",
options: [{ props: 'ignore' }],
},
{
code: "<MyComponent prop='bar'>foo</MyComponent>",
options: [{ props: 'ignore' }],
},
{
code: "<MyComponent>foo</MyComponent>",
options: [{ children: 'ignore' }],
},
],

invalid: [
{
code: "<MyComponent>{'foo'}</MyComponent>",
output: "<MyComponent>foo</MyComponent>",
errors: [{ message: unnecessaryCurlyMessage }]
},
{
code: "<MyComponent prop={'bar'}>foo</MyComponent>",
output: '<MyComponent prop="bar">foo</MyComponent>',
errors: [{ message: unnecessaryCurlyMessage }]
},
{
code: "<MyComponent>{'foo'}</MyComponent>",
output: "<MyComponent>foo</MyComponent>",
options: [{ children: 'never' }],
errors: [{ message: unnecessaryCurlyMessage }]
},
{
code: "<MyComponent prop={'bar'}>foo</MyComponent>",
output: '<MyComponent prop="bar">foo</MyComponent>',
options: [{ props: 'never' }],
errors: [{ message: unnecessaryCurlyMessage }]
},
{
code: "<MyComponent prop='bar'>foo</MyComponent>",
options: [{ props: 'always' }],
errors: [{ message: missingCurlyMessage }]
},
{
code: "<MyComponent>foo</MyComponent>",
options: [{ children: 'always' }],
errors: [{ message: missingCurlyMessage }]
},
]
})
56 changes: 0 additions & 56 deletions tests/lib/rules/jsx-no-unnecessary-curly-brace.js

This file was deleted.

0 comments on commit d5e5a2e

Please sign in to comment.