Skip to content

Commit a664e8a

Browse files
robertrossmanngyandeeps
authored andcommitted
Update: add ignoreJSX option to no-extra-parens (Fixes #7444) (#7926)
1 parent 8ac3518 commit a664e8a

3 files changed

Lines changed: 207 additions & 7 deletions

File tree

docs/rules/no-extra-parens.md

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ This rule has an object option for exceptions to the `"all"` option:
2323
* `"conditionalAssign": false` allows extra parentheses around assignments in conditional test expressions
2424
* `"returnAssign": false` allows extra parentheses around assignments in `return` statements
2525
* `"nestedBinaryExpressions": false` allows extra parentheses in nested binary expressions
26+
* `"ignoreJSX": "none|all|multi-line|single-line"` allows extra parentheses around no/all/multi-line/single-line JSX components. Defaults to `none`.
2627

2728
### all
2829

@@ -104,6 +105,68 @@ x = a + (b * c);
104105
x = (a * b) / c;
105106
```
106107

108+
### ignoreJSX
109+
110+
Examples of **correct** code for this rule with the `all` and `{ "ignoreJSX": "all" }` options:
111+
112+
```js
113+
/* eslint no-extra-parens: ["error", "all", { ignoreJSX: "all" }] */
114+
const Component = (<div />)
115+
const Component = (
116+
<div
117+
prop={true}
118+
/>
119+
)
120+
```
121+
122+
Examples of **incorrect** code for this rule with the `all` and `{ "ignoreJSX": "multi-line" }` options:
123+
124+
```js
125+
/* eslint no-extra-parens: ["error", "all", { ignoreJSX: "multi-line" }] */
126+
const Component = (<div />)
127+
const Component = (<div><p /></div>)
128+
```
129+
130+
Examples of **correct** code for this rule with the `all` and `{ "ignoreJSX": "multi-line" }` options:
131+
132+
```js
133+
/* eslint no-extra-parens: ["error", "all", { ignoreJSX: "multi-line" }] */
134+
const Component = (
135+
<div>
136+
<p />
137+
</div>
138+
)
139+
const Component = (
140+
<div
141+
prop={true}
142+
/>
143+
)
144+
```
145+
146+
Examples of **incorrect** code for this rule with the `all` and `{ "ignoreJSX": "single-line" }` options:
147+
148+
```js
149+
/* eslint no-extra-parens: ["error", "all", { ignoreJSX: "single-line" }] */
150+
const Component = (
151+
<div>
152+
<p />
153+
</div>
154+
)
155+
const Component = (
156+
<div
157+
prop={true}
158+
/>
159+
)
160+
```
161+
162+
Examples of **correct** code for this rule with the `all` and `{ "ignoreJSX": "single-line" }` options:
163+
164+
```js
165+
/* eslint no-extra-parens: ["error", "all", { ignoreJSX: "single-line" }] */
166+
const Component = (<div />)
167+
const Component = (<div><p /></div>)
168+
```
169+
107170
### functions
108171

109172
Examples of **incorrect** code for this rule with the `"functions"` option:

lib/rules/no-extra-parens.js

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@ module.exports = {
4444
properties: {
4545
conditionalAssign: { type: "boolean" },
4646
nestedBinaryExpressions: { type: "boolean" },
47-
returnAssign: { type: "boolean" }
47+
returnAssign: { type: "boolean" },
48+
ignoreJSX: { enum: ["none", "all", "single-line", "multi-line"] }
4849
},
4950
additionalProperties: false
5051
}
@@ -65,6 +66,7 @@ module.exports = {
6566
const EXCEPT_COND_ASSIGN = ALL_NODES && context.options[1] && context.options[1].conditionalAssign === false;
6667
const NESTED_BINARY = ALL_NODES && context.options[1] && context.options[1].nestedBinaryExpressions === false;
6768
const EXCEPT_RETURN_ASSIGN = ALL_NODES && context.options[1] && context.options[1].returnAssign === false;
69+
const IGNORE_JSX = ALL_NODES && context.options[1] && context.options[1].ignoreJSX;
6870

6971
/**
7072
* Determines if this rule should be enforced for a node given the current configuration.
@@ -73,6 +75,31 @@ module.exports = {
7375
* @private
7476
*/
7577
function ruleApplies(node) {
78+
if (node.type === "JSXElement") {
79+
const isSingleLine = node.loc.start.line === node.loc.end.line;
80+
81+
switch (IGNORE_JSX) {
82+
83+
// Exclude this JSX element from linting
84+
case "all":
85+
return false;
86+
87+
// Exclude this JSX element if it is multi-line element
88+
case "multi-line":
89+
return isSingleLine;
90+
91+
// Exclude this JSX element if it is single-line element
92+
case "single-line":
93+
return !isSingleLine;
94+
95+
// Nothing special to be done for JSX elements
96+
case "none":
97+
break;
98+
99+
// no default
100+
}
101+
}
102+
76103
return ALL_NODES || node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression";
77104
}
78105

tests/lib/rules/no-extra-parens.js

Lines changed: 116 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,14 @@ function invalid(code, output, type, line, config) {
4545
return result;
4646
}
4747

48-
const ruleTester = new RuleTester();
48+
const ruleTester = new RuleTester({
49+
parserOptions: {
50+
ecmaVersion: 6,
51+
ecmaFeatures: {
52+
jsx: true
53+
}
54+
}
55+
});
4956

5057
ruleTester.run("no-extra-parens", rule, {
5158
valid: [
@@ -291,7 +298,61 @@ ruleTester.run("no-extra-parens", rule, {
291298
{ code: "foo instanceof (bar instanceof baz)", options: ["all", { nestedBinaryExpressions: false }] },
292299
{ code: "foo in (bar in baz)", options: ["all", { nestedBinaryExpressions: false }] },
293300
{ code: "foo + (bar + baz)", options: ["all", { nestedBinaryExpressions: false }] },
294-
{ code: "foo && (bar && baz)", options: ["all", { nestedBinaryExpressions: false }] }
301+
{ code: "foo && (bar && baz)", options: ["all", { nestedBinaryExpressions: false }] },
302+
303+
// ["all", { ignoreJSX: "all" }]
304+
{ code: "const Component = (<div />)", options: ["all", { ignoreJSX: "all" }] },
305+
{ code: [
306+
"const Component = (<div>",
307+
" <p />",
308+
"</div>);"
309+
].join("\n"), options: ["all", { ignoreJSX: "all" }] },
310+
{ code: [
311+
"const Component = (",
312+
" <div />",
313+
");"
314+
].join("\n"), options: ["all", { ignoreJSX: "all" }] },
315+
{ code: [
316+
"const Component =",
317+
" (<div />)"
318+
].join("\n"), options: ["all", { ignoreJSX: "all" }] },
319+
320+
// ["all", { ignoreJSX: "single-line" }]
321+
{ code: "const Component = (<div />);", options: ["all", { ignoreJSX: "single-line" }] },
322+
{ code: [
323+
"const Component = (",
324+
" <div />",
325+
");"
326+
].join("\n"), options: ["all", { ignoreJSX: "single-line" }] },
327+
{ code: [
328+
"const Component =",
329+
"(<div />)"
330+
].join("\n"), options: ["all", { ignoreJSX: "single-line" }] },
331+
332+
// ["all", { ignoreJSX: "multi-line" }]
333+
{ code: [
334+
"const Component = (",
335+
"<div>",
336+
" <p />",
337+
"</div>",
338+
");"
339+
].join("\n"), options: ["all", { ignoreJSX: "multi-line" }] },
340+
{ code: [
341+
"const Component = (<div>",
342+
" <p />",
343+
"</div>);"
344+
].join("\n"), options: ["all", { ignoreJSX: "multi-line" }] },
345+
{ code: [
346+
"const Component =",
347+
"(<div>",
348+
" <p />",
349+
"</div>);"
350+
].join("\n"), options: ["all", { ignoreJSX: "multi-line" }] },
351+
{ code: [
352+
"const Component = (<div",
353+
" prop={true}",
354+
"/>)"
355+
].join("\n"), options: ["all", { ignoreJSX: "multi-line" }] }
295356
],
296357

297358
invalid: [
@@ -465,7 +526,7 @@ ruleTester.run("no-extra-parens", rule, {
465526
"function a() {",
466527
" return <JSX />;",
467528
"}"
468-
].join("\n"), "JSXElement", null, { parserOptions: { ecmaVersion: 6, ecmaFeatures: { jsx: true } } }),
529+
].join("\n"), "JSXElement", null),
469530
invalid([
470531
"function a() {",
471532
" return",
@@ -476,7 +537,7 @@ ruleTester.run("no-extra-parens", rule, {
476537
" return",
477538
" <JSX />;",
478539
"}"
479-
].join("\n"), "JSXElement", null, { parserOptions: { ecmaVersion: 6, ecmaFeatures: { jsx: true } } }),
540+
].join("\n"), "JSXElement", null),
480541
invalid([
481542
"function a() {",
482543
" return ((",
@@ -489,7 +550,7 @@ ruleTester.run("no-extra-parens", rule, {
489550
" <JSX />",
490551
" );",
491552
"}"
492-
].join("\n"), "JSXElement", null, { parserOptions: { ecmaVersion: 6, ecmaFeatures: { jsx: true } } }),
553+
].join("\n"), "JSXElement", null),
493554
invalid("throw (a);", "throw a;", "Identifier"),
494555
invalid([
495556
"throw ((",
@@ -706,6 +767,55 @@ ruleTester.run("no-extra-parens", rule, {
706767
invalid("foo instanceof (bar)", "foo instanceof bar", "Identifier", 1, { options: ["all", { nestedBinaryExpressions: false }] }),
707768
invalid("foo in (bar)", "foo in bar", "Identifier", 1, { options: ["all", { nestedBinaryExpressions: false }] }),
708769
invalid("foo + (bar)", "foo + bar", "Identifier", 1, { options: ["all", { nestedBinaryExpressions: false }] }),
709-
invalid("foo && (bar)", "foo && bar", "Identifier", 1, { options: ["all", { nestedBinaryExpressions: false }] })
770+
invalid("foo && (bar)", "foo && bar", "Identifier", 1, { options: ["all", { nestedBinaryExpressions: false }] }),
771+
772+
// ["all", { ignoreJSX: "multi-line" }]
773+
invalid("const Component = (<div />);", "const Component = <div />;", "JSXElement", 1, {
774+
options: ["all", { ignoreJSX: "multi-line" }]
775+
}),
776+
invalid([
777+
"const Component = (",
778+
" <div />",
779+
");"
780+
].join("\n"), "const Component = \n <div />\n;", "JSXElement", 1, {
781+
options: ["all", { ignoreJSX: "multi-line" }]
782+
}),
783+
784+
// ["all", { ignoreJSX: "single-line" }]
785+
invalid([
786+
"const Component = (",
787+
"<div>",
788+
" <p />",
789+
"</div>",
790+
");"
791+
].join("\n"), "const Component = \n<div>\n <p />\n</div>\n;", "JSXElement", 1, {
792+
options: ["all", { ignoreJSX: "single-line" }]
793+
}),
794+
invalid([
795+
"const Component = (<div>",
796+
" <p />",
797+
"</div>);"
798+
].join("\n"), "const Component = <div>\n <p />\n</div>;", "JSXElement", 1, {
799+
options: ["all", { ignoreJSX: "single-line" }]
800+
}),
801+
invalid([
802+
"const Component = (<div",
803+
" prop={true}",
804+
"/>)"
805+
].join("\n"), "const Component = <div\n prop={true}\n/>", "JSXElement", 1, {
806+
options: ["all", { ignoreJSX: "single-line" }]
807+
}),
808+
809+
// ["all", { ignoreJSX: "none" }] default, same as unspecified
810+
invalid("const Component = (<div />);", "const Component = <div />;", "JSXElement", 1, {
811+
options: ["all", { ignoreJSX: "none" }]
812+
}),
813+
invalid([
814+
"const Component = (<div>",
815+
"<p />",
816+
"</div>)"
817+
].join("\n"), "const Component = <div>\n<p />\n</div>", "JSXElement", 1, {
818+
options: ["all", { ignoreJSX: "none" }]
819+
})
710820
]
711821
});

0 commit comments

Comments
 (0)