Skip to content

Commit

Permalink
Update: add ignoreJSX option to no-extra-parens (Fixes #7444) (#7926)
Browse files Browse the repository at this point in the history
  • Loading branch information
robertrossmann authored and gyandeeps committed Jan 19, 2017
1 parent 8ac3518 commit a664e8a
Show file tree
Hide file tree
Showing 3 changed files with 207 additions and 7 deletions.
63 changes: 63 additions & 0 deletions docs/rules/no-extra-parens.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ This rule has an object option for exceptions to the `"all"` option:
* `"conditionalAssign": false` allows extra parentheses around assignments in conditional test expressions
* `"returnAssign": false` allows extra parentheses around assignments in `return` statements
* `"nestedBinaryExpressions": false` allows extra parentheses in nested binary expressions
* `"ignoreJSX": "none|all|multi-line|single-line"` allows extra parentheses around no/all/multi-line/single-line JSX components. Defaults to `none`.

### all

Expand Down Expand Up @@ -104,6 +105,68 @@ x = a + (b * c);
x = (a * b) / c;
```

### ignoreJSX

Examples of **correct** code for this rule with the `all` and `{ "ignoreJSX": "all" }` options:

```js
/* eslint no-extra-parens: ["error", "all", { ignoreJSX: "all" }] */
const Component = (<div />)
const Component = (
<div
prop={true}
/>
)
```

Examples of **incorrect** code for this rule with the `all` and `{ "ignoreJSX": "multi-line" }` options:

```js
/* eslint no-extra-parens: ["error", "all", { ignoreJSX: "multi-line" }] */
const Component = (<div />)
const Component = (<div><p /></div>)
```

Examples of **correct** code for this rule with the `all` and `{ "ignoreJSX": "multi-line" }` options:

```js
/* eslint no-extra-parens: ["error", "all", { ignoreJSX: "multi-line" }] */
const Component = (
<div>
<p />
</div>
)
const Component = (
<div
prop={true}
/>
)
```

Examples of **incorrect** code for this rule with the `all` and `{ "ignoreJSX": "single-line" }` options:

```js
/* eslint no-extra-parens: ["error", "all", { ignoreJSX: "single-line" }] */
const Component = (
<div>
<p />
</div>
)
const Component = (
<div
prop={true}
/>
)
```

Examples of **correct** code for this rule with the `all` and `{ "ignoreJSX": "single-line" }` options:

```js
/* eslint no-extra-parens: ["error", "all", { ignoreJSX: "single-line" }] */
const Component = (<div />)
const Component = (<div><p /></div>)
```

### functions

Examples of **incorrect** code for this rule with the `"functions"` option:
Expand Down
29 changes: 28 additions & 1 deletion lib/rules/no-extra-parens.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ module.exports = {
properties: {
conditionalAssign: { type: "boolean" },
nestedBinaryExpressions: { type: "boolean" },
returnAssign: { type: "boolean" }
returnAssign: { type: "boolean" },
ignoreJSX: { enum: ["none", "all", "single-line", "multi-line"] }
},
additionalProperties: false
}
Expand All @@ -65,6 +66,7 @@ module.exports = {
const EXCEPT_COND_ASSIGN = ALL_NODES && context.options[1] && context.options[1].conditionalAssign === false;
const NESTED_BINARY = ALL_NODES && context.options[1] && context.options[1].nestedBinaryExpressions === false;
const EXCEPT_RETURN_ASSIGN = ALL_NODES && context.options[1] && context.options[1].returnAssign === false;
const IGNORE_JSX = ALL_NODES && context.options[1] && context.options[1].ignoreJSX;

/**
* Determines if this rule should be enforced for a node given the current configuration.
Expand All @@ -73,6 +75,31 @@ module.exports = {
* @private
*/
function ruleApplies(node) {
if (node.type === "JSXElement") {
const isSingleLine = node.loc.start.line === node.loc.end.line;

switch (IGNORE_JSX) {

// Exclude this JSX element from linting
case "all":
return false;

// Exclude this JSX element if it is multi-line element
case "multi-line":
return isSingleLine;

// Exclude this JSX element if it is single-line element
case "single-line":
return !isSingleLine;

// Nothing special to be done for JSX elements
case "none":
break;

// no default
}
}

return ALL_NODES || node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression";
}

Expand Down
122 changes: 116 additions & 6 deletions tests/lib/rules/no-extra-parens.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,14 @@ function invalid(code, output, type, line, config) {
return result;
}

const ruleTester = new RuleTester();
const ruleTester = new RuleTester({
parserOptions: {
ecmaVersion: 6,
ecmaFeatures: {
jsx: true
}
}
});

ruleTester.run("no-extra-parens", rule, {
valid: [
Expand Down Expand Up @@ -291,7 +298,61 @@ ruleTester.run("no-extra-parens", rule, {
{ code: "foo instanceof (bar instanceof baz)", options: ["all", { nestedBinaryExpressions: false }] },
{ code: "foo in (bar in baz)", options: ["all", { nestedBinaryExpressions: false }] },
{ code: "foo + (bar + baz)", options: ["all", { nestedBinaryExpressions: false }] },
{ code: "foo && (bar && baz)", options: ["all", { nestedBinaryExpressions: false }] }
{ code: "foo && (bar && baz)", options: ["all", { nestedBinaryExpressions: false }] },

// ["all", { ignoreJSX: "all" }]
{ code: "const Component = (<div />)", options: ["all", { ignoreJSX: "all" }] },
{ code: [
"const Component = (<div>",
" <p />",
"</div>);"
].join("\n"), options: ["all", { ignoreJSX: "all" }] },
{ code: [
"const Component = (",
" <div />",
");"
].join("\n"), options: ["all", { ignoreJSX: "all" }] },
{ code: [
"const Component =",
" (<div />)"
].join("\n"), options: ["all", { ignoreJSX: "all" }] },

// ["all", { ignoreJSX: "single-line" }]
{ code: "const Component = (<div />);", options: ["all", { ignoreJSX: "single-line" }] },
{ code: [
"const Component = (",
" <div />",
");"
].join("\n"), options: ["all", { ignoreJSX: "single-line" }] },
{ code: [
"const Component =",
"(<div />)"
].join("\n"), options: ["all", { ignoreJSX: "single-line" }] },

// ["all", { ignoreJSX: "multi-line" }]
{ code: [
"const Component = (",
"<div>",
" <p />",
"</div>",
");"
].join("\n"), options: ["all", { ignoreJSX: "multi-line" }] },
{ code: [
"const Component = (<div>",
" <p />",
"</div>);"
].join("\n"), options: ["all", { ignoreJSX: "multi-line" }] },
{ code: [
"const Component =",
"(<div>",
" <p />",
"</div>);"
].join("\n"), options: ["all", { ignoreJSX: "multi-line" }] },
{ code: [
"const Component = (<div",
" prop={true}",
"/>)"
].join("\n"), options: ["all", { ignoreJSX: "multi-line" }] }
],

invalid: [
Expand Down Expand Up @@ -465,7 +526,7 @@ ruleTester.run("no-extra-parens", rule, {
"function a() {",
" return <JSX />;",
"}"
].join("\n"), "JSXElement", null, { parserOptions: { ecmaVersion: 6, ecmaFeatures: { jsx: true } } }),
].join("\n"), "JSXElement", null),
invalid([
"function a() {",
" return",
Expand All @@ -476,7 +537,7 @@ ruleTester.run("no-extra-parens", rule, {
" return",
" <JSX />;",
"}"
].join("\n"), "JSXElement", null, { parserOptions: { ecmaVersion: 6, ecmaFeatures: { jsx: true } } }),
].join("\n"), "JSXElement", null),
invalid([
"function a() {",
" return ((",
Expand All @@ -489,7 +550,7 @@ ruleTester.run("no-extra-parens", rule, {
" <JSX />",
" );",
"}"
].join("\n"), "JSXElement", null, { parserOptions: { ecmaVersion: 6, ecmaFeatures: { jsx: true } } }),
].join("\n"), "JSXElement", null),
invalid("throw (a);", "throw a;", "Identifier"),
invalid([
"throw ((",
Expand Down Expand Up @@ -706,6 +767,55 @@ ruleTester.run("no-extra-parens", rule, {
invalid("foo instanceof (bar)", "foo instanceof bar", "Identifier", 1, { options: ["all", { nestedBinaryExpressions: false }] }),
invalid("foo in (bar)", "foo in bar", "Identifier", 1, { options: ["all", { nestedBinaryExpressions: false }] }),
invalid("foo + (bar)", "foo + bar", "Identifier", 1, { options: ["all", { nestedBinaryExpressions: false }] }),
invalid("foo && (bar)", "foo && bar", "Identifier", 1, { options: ["all", { nestedBinaryExpressions: false }] })
invalid("foo && (bar)", "foo && bar", "Identifier", 1, { options: ["all", { nestedBinaryExpressions: false }] }),

// ["all", { ignoreJSX: "multi-line" }]
invalid("const Component = (<div />);", "const Component = <div />;", "JSXElement", 1, {
options: ["all", { ignoreJSX: "multi-line" }]
}),
invalid([
"const Component = (",
" <div />",
");"
].join("\n"), "const Component = \n <div />\n;", "JSXElement", 1, {
options: ["all", { ignoreJSX: "multi-line" }]
}),

// ["all", { ignoreJSX: "single-line" }]
invalid([
"const Component = (",
"<div>",
" <p />",
"</div>",
");"
].join("\n"), "const Component = \n<div>\n <p />\n</div>\n;", "JSXElement", 1, {
options: ["all", { ignoreJSX: "single-line" }]
}),
invalid([
"const Component = (<div>",
" <p />",
"</div>);"
].join("\n"), "const Component = <div>\n <p />\n</div>;", "JSXElement", 1, {
options: ["all", { ignoreJSX: "single-line" }]
}),
invalid([
"const Component = (<div",
" prop={true}",
"/>)"
].join("\n"), "const Component = <div\n prop={true}\n/>", "JSXElement", 1, {
options: ["all", { ignoreJSX: "single-line" }]
}),

// ["all", { ignoreJSX: "none" }] default, same as unspecified
invalid("const Component = (<div />);", "const Component = <div />;", "JSXElement", 1, {
options: ["all", { ignoreJSX: "none" }]
}),
invalid([
"const Component = (<div>",
"<p />",
"</div>)"
].join("\n"), "const Component = <div>\n<p />\n</div>", "JSXElement", 1, {
options: ["all", { ignoreJSX: "none" }]
})
]
});

0 comments on commit a664e8a

Please sign in to comment.