Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

prefer-ternary: Add only-single-line option #1025

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
23 changes: 23 additions & 0 deletions docs/rules/prefer-ternary.md
Expand Up @@ -120,3 +120,26 @@ if (test) {
baz = 2;
}
```

## Options

Type: `string`\
Default: `'always'`

- `'always'` (default)
- Always report when using an `IfStatement` where a ternary expression can be used.
- `'only-single-line'`
- Only check if the content of the `if` and/or `else` block is less than one line long.

The following case is considered valid:

```js
// eslint unicorn/prefer-ternary: ["error", "only-single-line"]
if (test) {
foo = [
'multiple line array'
];
} else {
bar = baz;
}
```
22 changes: 21 additions & 1 deletion rules/prefer-ternary.js
Expand Up @@ -56,6 +56,7 @@ const getScopes = scope => [
];

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

const isSingleLineNode = node => {
const [start, end] = node.range.map(index => sourceCode.getLocFromIndex(index));
return start.line === end.line;
};

function merge(options, mergeOptions) {
const {
before = '',
Expand Down Expand Up @@ -189,6 +195,13 @@ const create = context => {
const consequent = getNodeBody(node.consequent);
const alternate = getNodeBody(node.alternate);

if (
onlySingleLine &&
[consequent, alternate, node.test].some(node => !isSingleLineNode(node))
) {
return;
}

const result = merge({node, consequent, alternate}, {
checkThrowStatement: true,
returnFalseIfNotMergeable: true
Expand All @@ -199,7 +212,6 @@ const create = context => {
}

const scope = context.getScope();
const sourceCode = context.getSourceCode();

context.report({
node,
Expand Down Expand Up @@ -252,6 +264,13 @@ const create = context => {
};
};

const schema = [
{
enum: ['always', 'only-single-line'],
default: 'always'
}
];

module.exports = {
create,
meta: {
Expand All @@ -262,6 +281,7 @@ module.exports = {
messages: {
[messageId]: 'This `if` statement can be replaced by a ternary expression.'
},
schema,
fixable: 'code'
}
};
130 changes: 130 additions & 0 deletions test/prefer-ternary.js
Expand Up @@ -4,6 +4,8 @@ import {test} from './utils/test.js';
const messageId = 'prefer-ternary';
const errors = [{messageId}];

const onlySingleLineOptions = ['only-single-line'];

// ReturnStatement
test({
valid: [
Expand Down Expand Up @@ -957,6 +959,134 @@ test({
]
});

// `only-single-line`
test({
valid: [
{
code: outdent`
if (test) {
a = {
multiline: 'in consequent'
};
} else{
a = foo;
}
`,
options: onlySingleLineOptions
},
{
code: outdent`
if (test) {
a = foo;
} else{
a = {
multiline: 'in alternate'
};
}
`,
options: onlySingleLineOptions
},
{
code: outdent`
if (
test({
multiline: 'in test'
})
) {
a = foo;
} else{
a = bar;
}
`,
options: onlySingleLineOptions
},
{
code: outdent`
if (test) {
a = foo; b = 1;
} else{
a = bar;
}
`,
options: onlySingleLineOptions
}
],
invalid: [
{
code: outdent`
if (test) {
a = foo;
} else {
a = bar;
}
`,
output: 'a = test ? foo : bar;',
options: onlySingleLineOptions,
errors
},
// Parentheses are not considered part of `Node`
{
code: outdent`
if (
(
test
)
) {
a = foo;
} else {
a = bar;
}
`,
output: 'a = (test) ? foo : bar;',
options: onlySingleLineOptions,
errors
},
{
code: outdent`
if (test) {
(
a = foo
);
} else {
a = bar;
}
`,
output: 'a = test ? foo : bar;',
options: onlySingleLineOptions,
errors
},
// Semicolon of `ExpressionStatement` is not considered part of `Node`
{
code: outdent`
if (test) {
a = foo
;
} else {
a = bar;
}
`,
output: 'a = test ? foo : bar;',
options: onlySingleLineOptions,
errors
},
// `EmptyStatement`s are excluded
{
code: outdent`
if (test) {
;;;;;;
a = foo;
;;;;;;
} else {
a = bar;
}
`,
output: 'a = test ? foo : bar;',
options: onlySingleLineOptions,
errors
}
]
});

test({
valid: [
// No `consequent` / `alternate`
Expand Down