Skip to content

Commit

Permalink
New: multiline-ternary rule (fixes #6066)
Browse files Browse the repository at this point in the history
  • Loading branch information
kaicataldo committed Jul 4, 2016
1 parent 5ec54be commit e5f7ecb
Show file tree
Hide file tree
Showing 5 changed files with 230 additions and 0 deletions.
1 change: 1 addition & 0 deletions conf/eslint.json
Expand Up @@ -174,6 +174,7 @@
"max-params": "off",
"max-statements": "off",
"max-statements-per-line": "off",
"multiline-ternary": "off",
"new-cap": "off",
"new-parens": "off",
"newline-after-var": "off",
Expand Down
1 change: 1 addition & 0 deletions docs/rules/README.md
Expand Up @@ -179,6 +179,7 @@ These rules relate to style guidelines, and are therefore quite subjective:
* [max-params](max-params.md): enforce a maximum number of parameters in `function` definitions
* [max-statements](max-statements.md): enforce a maximum number of statements allowed in `function` blocks
* [max-statements-per-line](max-statements-per-line.md): enforce a maximum number of statements allowed per line
* [multiline-ternary](multiline-ternary.md): enforce newlines between operands of ternary expressions
* [new-cap](new-cap.md): require constructor `function` names to begin with a capital letter
* [new-parens](new-parens.md): require parentheses when invoking a constructor with no arguments
* [newline-after-var](newline-after-var.md): require or disallow an empty line after `var` declarations
Expand Down
64 changes: 64 additions & 0 deletions docs/rules/multiline-ternary.md
@@ -0,0 +1,64 @@
# Enforce newlines between operands of ternary expressions (multiline-ternary)

JavaScript allows operands of ternary expressions to be separated by newlines, which can be can improve the readability of your program.

For example:

```js
var foo = bar > baz ? value1 : value2;
```

The above can be rewritten as the following to improve readability and more clearly delineate the operands:

```js
var foo = bar > baz ?
value1 :
value2;
```

## Rule Details

This rule enforces newlines between operands of a ternary expression.
Note: The location of the operators is not enforced by this rule. Please see the [operator-linebreak](operator-linebreak.md) rule if you are interested in enforcing the location of the operators themselves.

Examples of **incorrect** code for this rule:

```js
/*eslint multiline-ternary: "error"*/

foo > bar ? value1 : value2;

foo > bar ? value :
value2;

foo > bar ?
value : value2;
```

Examples of **correct** code for this rule:

```js
/*eslint newline-before-return: "error"*/

foo > bar ?
value1 :
value2;

foo > bar ?
(baz > qux ?
value1 :
value2) :
value3;
```

## When Not To Use It

You can safely disable this rule if you do not have any strict conventions about whether the operands of a ternary expression should be separated by newlines.

## Related Rules

* [operator-linebreak](operator-linebreak.md)

## Compatibility

* **JSCS**: [requireMultiLineTernary](http://http://jscs.info/rule/requireMultiLineTernary)
67 changes: 67 additions & 0 deletions lib/rules/multiline-ternary.js
@@ -0,0 +1,67 @@
/**
* @fileoverview Enforce newlines between operands of ternary expressions
* @author Kai Cataldo
*/

"use strict";

var astUtils = require("../ast-utils");

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

module.exports = {
meta: {
docs: {
description: "enforce newlines between operands of ternary expressions",
category: "Stylistic Issues",
recommended: false
},
fixable: "whitespace",
schema: []
},

create: function(context) {

//--------------------------------------------------------------------------
// Helpers
//--------------------------------------------------------------------------

/**
* Tests whether node is preceded by supplied tokens
* @param {ASTNode} node - node to check
* @param {ASTNode} parentNode - parent of node to report
* @returns {void}
* @private
*/
function reportError(node, parentNode) {
context.report({
node: node,
message: "Expected newline between {{typeOfError}} of ternary expression.",
data: {
typeOfError: node === parentNode.test ? "test and consequent" : "consequent and alternate"
}
});
}

//--------------------------------------------------------------------------
// Public
//--------------------------------------------------------------------------

return {
ConditionalExpression: function(node) {
var areTestAndConsequentOnSameLine = astUtils.isTokenOnSameLine(node.test, node.consequent);
var areConsequentAndAlternateOnSameLine = astUtils.isTokenOnSameLine(node.consequent, node.alternate);

if (areTestAndConsequentOnSameLine) {
reportError(node.test, node);
}

if (areConsequentAndAlternateOnSameLine) {
reportError(node.consequent, node);
}
}
};
}
};
97 changes: 97 additions & 0 deletions tests/lib/rules/multiline-ternary.js
@@ -0,0 +1,97 @@
/**
* @fileoverview Enforce newlines between operands of ternary expressions
* @author Kai Cataldo
*/

"use strict";

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

var rule = require("../../../lib/rules/multiline-ternary");
var RuleTester = require("../../../lib/testers/rule-tester");

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

var ruleTester = new RuleTester();
var testConsMsg = "Expected newline between test and consequent of ternary expression.";
var consAltMsg = "Expected newline between consequent and alternate of ternary expression.";

ruleTester.run("multiline-ternary", rule, {
valid: [
"a\n? b\n: c",
"a ?\nb :\nc",
"a\n? b\n? c\n: d\n: e",
],

invalid: [
{
code: "a ? b : c",
errors: [{
message: testConsMsg,
line: 1,
column: 1
},
{
message: consAltMsg,
line: 1,
column: 5
}]
},
{
code: "a\n? b : c",
errors: [{
message: consAltMsg,
line: 2,
column: 3
}]
},
{
code: "a ? b\n: c",
errors: [{
message: testConsMsg,
line: 1,
column: 1
}]
},
{
code: "a ? (b ? c : d) : e",
errors: [{
message: testConsMsg,
line: 1,
column: 1
},
{
message: consAltMsg,
line: 1,
column: 6
},
{
message: testConsMsg,
line: 1,
column: 6
},
{
message: consAltMsg,
line: 1,
column: 10
}]
},
{
code: "a ? (b\n? c\n: d) : e",
errors: [{
message: testConsMsg,
line: 1,
column: 1
},
{
message: consAltMsg,
line: 1,
column: 6
}]
}
]
});

0 comments on commit e5f7ecb

Please sign in to comment.