diff --git a/conf/eslint.json b/conf/eslint.json index 94317bcd89aa..2c74c8cdff68 100755 --- a/conf/eslint.json +++ b/conf/eslint.json @@ -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", diff --git a/docs/rules/README.md b/docs/rules/README.md index 91c376841b74..e5953d5a7c56 100644 --- a/docs/rules/README.md +++ b/docs/rules/README.md @@ -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 diff --git a/docs/rules/multiline-ternary.md b/docs/rules/multiline-ternary.md new file mode 100644 index 000000000000..b29ce4252cd7 --- /dev/null +++ b/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) diff --git a/lib/rules/multiline-ternary.js b/lib/rules/multiline-ternary.js new file mode 100644 index 000000000000..c43794d65b73 --- /dev/null +++ b/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); + } + } + }; + } +}; diff --git a/tests/lib/rules/multiline-ternary.js b/tests/lib/rules/multiline-ternary.js new file mode 100644 index 000000000000..5bcb46dafa8c --- /dev/null +++ b/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 + }] + } + ] +});