Skip to content

Commit 306c9e7

Browse files
fiskersindresorhusnoftaly
authored
prefer-ternary: Add only-single-line option (#1025)
Co-authored-by: Sindre Sorhus <[email protected]> Co-authored-by: Elliot <[email protected]>
1 parent 88018ca commit 306c9e7

File tree

3 files changed

+174
-1
lines changed

3 files changed

+174
-1
lines changed

docs/rules/prefer-ternary.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,3 +120,26 @@ if (test) {
120120
baz = 2;
121121
}
122122
```
123+
124+
## Options
125+
126+
Type: `string`\
127+
Default: `'always'`
128+
129+
- `'always'` (default)
130+
- Always report when using an `IfStatement` where a ternary expression can be used.
131+
- `'only-single-line'`
132+
- Only check if the content of the `if` and/or `else` block is less than one line long.
133+
134+
The following case is considered valid:
135+
136+
```js
137+
// eslint unicorn/prefer-ternary: ["error", "only-single-line"]
138+
if (test) {
139+
foo = [
140+
'multiple line array'
141+
];
142+
} else {
143+
bar = baz;
144+
}
145+
```

rules/prefer-ternary.js

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ const getScopes = scope => [
5656
];
5757

5858
const create = context => {
59+
const onlySingleLine = context.options[0] === 'only-single-line';
5960
const sourceCode = context.getSourceCode();
6061
const scopeToNamesGeneratedByFixer = new WeakMap();
6162
const isSafeName = (name, scopes) => scopes.every(scope => {
@@ -76,6 +77,11 @@ const create = context => {
7677
`(${text})` : text;
7778
};
7879

80+
const isSingleLineNode = node => {
81+
const [start, end] = node.range.map(index => sourceCode.getLocFromIndex(index));
82+
return start.line === end.line;
83+
};
84+
7985
function merge(options, mergeOptions) {
8086
const {
8187
before = '',
@@ -189,6 +195,13 @@ const create = context => {
189195
const consequent = getNodeBody(node.consequent);
190196
const alternate = getNodeBody(node.alternate);
191197

198+
if (
199+
onlySingleLine &&
200+
[consequent, alternate, node.test].some(node => !isSingleLineNode(node))
201+
) {
202+
return;
203+
}
204+
192205
const result = merge({node, consequent, alternate}, {
193206
checkThrowStatement: true,
194207
returnFalseIfNotMergeable: true
@@ -199,7 +212,6 @@ const create = context => {
199212
}
200213

201214
const scope = context.getScope();
202-
const sourceCode = context.getSourceCode();
203215

204216
context.report({
205217
node,
@@ -252,6 +264,13 @@ const create = context => {
252264
};
253265
};
254266

267+
const schema = [
268+
{
269+
enum: ['always', 'only-single-line'],
270+
default: 'always'
271+
}
272+
];
273+
255274
module.exports = {
256275
create,
257276
meta: {
@@ -262,6 +281,7 @@ module.exports = {
262281
messages: {
263282
[messageId]: 'This `if` statement can be replaced by a ternary expression.'
264283
},
284+
schema,
265285
fixable: 'code'
266286
}
267287
};

test/prefer-ternary.js

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import {test} from './utils/test.js';
44
const messageId = 'prefer-ternary';
55
const errors = [{messageId}];
66

7+
const onlySingleLineOptions = ['only-single-line'];
8+
79
// ReturnStatement
810
test({
911
valid: [
@@ -957,6 +959,134 @@ test({
957959
]
958960
});
959961

962+
// `only-single-line`
963+
test({
964+
valid: [
965+
{
966+
code: outdent`
967+
if (test) {
968+
a = {
969+
multiline: 'in consequent'
970+
};
971+
} else{
972+
a = foo;
973+
}
974+
`,
975+
options: onlySingleLineOptions
976+
},
977+
{
978+
code: outdent`
979+
if (test) {
980+
a = foo;
981+
} else{
982+
a = {
983+
multiline: 'in alternate'
984+
};
985+
}
986+
`,
987+
options: onlySingleLineOptions
988+
},
989+
{
990+
code: outdent`
991+
if (
992+
test({
993+
multiline: 'in test'
994+
})
995+
) {
996+
a = foo;
997+
} else{
998+
a = bar;
999+
}
1000+
`,
1001+
options: onlySingleLineOptions
1002+
},
1003+
{
1004+
code: outdent`
1005+
if (test) {
1006+
a = foo; b = 1;
1007+
} else{
1008+
a = bar;
1009+
}
1010+
`,
1011+
options: onlySingleLineOptions
1012+
}
1013+
],
1014+
invalid: [
1015+
{
1016+
code: outdent`
1017+
if (test) {
1018+
a = foo;
1019+
} else {
1020+
a = bar;
1021+
}
1022+
`,
1023+
output: 'a = test ? foo : bar;',
1024+
options: onlySingleLineOptions,
1025+
errors
1026+
},
1027+
// Parentheses are not considered part of `Node`
1028+
{
1029+
code: outdent`
1030+
if (
1031+
(
1032+
test
1033+
)
1034+
) {
1035+
a = foo;
1036+
} else {
1037+
a = bar;
1038+
}
1039+
`,
1040+
output: 'a = (test) ? foo : bar;',
1041+
options: onlySingleLineOptions,
1042+
errors
1043+
},
1044+
{
1045+
code: outdent`
1046+
if (test) {
1047+
(
1048+
a = foo
1049+
);
1050+
} else {
1051+
a = bar;
1052+
}
1053+
`,
1054+
output: 'a = test ? foo : bar;',
1055+
options: onlySingleLineOptions,
1056+
errors
1057+
},
1058+
// Semicolon of `ExpressionStatement` is not considered part of `Node`
1059+
{
1060+
code: outdent`
1061+
if (test) {
1062+
a = foo
1063+
;
1064+
} else {
1065+
a = bar;
1066+
}
1067+
`,
1068+
output: 'a = test ? foo : bar;',
1069+
options: onlySingleLineOptions,
1070+
errors
1071+
},
1072+
// `EmptyStatement`s are excluded
1073+
{
1074+
code: outdent`
1075+
if (test) {
1076+
;;;;;;
1077+
a = foo;
1078+
;;;;;;
1079+
} else {
1080+
a = bar;
1081+
}
1082+
`,
1083+
output: 'a = test ? foo : bar;',
1084+
options: onlySingleLineOptions,
1085+
errors
1086+
}
1087+
]
1088+
});
1089+
9601090
test({
9611091
valid: [
9621092
// No `consequent` / `alternate`

0 commit comments

Comments
 (0)