1/** 2 * @fileoverview Enforce newlines between operands of ternary expressions 3 * @author Kai Cataldo 4 */ 5 6"use strict"; 7 8const astUtils = require("./utils/ast-utils"); 9 10//------------------------------------------------------------------------------ 11// Rule Definition 12//------------------------------------------------------------------------------ 13 14module.exports = { 15 meta: { 16 type: "layout", 17 18 docs: { 19 description: "enforce newlines between operands of ternary expressions", 20 category: "Stylistic Issues", 21 recommended: false, 22 url: "https://eslint.org/docs/rules/multiline-ternary" 23 }, 24 25 schema: [ 26 { 27 enum: ["always", "always-multiline", "never"] 28 } 29 ], 30 messages: { 31 expectedTestCons: "Expected newline between test and consequent of ternary expression.", 32 expectedConsAlt: "Expected newline between consequent and alternate of ternary expression.", 33 unexpectedTestCons: "Unexpected newline between test and consequent of ternary expression.", 34 unexpectedConsAlt: "Unexpected newline between consequent and alternate of ternary expression." 35 } 36 }, 37 38 create(context) { 39 const option = context.options[0]; 40 const multiline = option !== "never"; 41 const allowSingleLine = option === "always-multiline"; 42 const sourceCode = context.getSourceCode(); 43 44 //-------------------------------------------------------------------------- 45 // Public 46 //-------------------------------------------------------------------------- 47 48 return { 49 ConditionalExpression(node) { 50 const questionToken = sourceCode.getTokenAfter(node.test, astUtils.isNotClosingParenToken); 51 const colonToken = sourceCode.getTokenAfter(node.consequent, astUtils.isNotClosingParenToken); 52 53 const firstTokenOfTest = sourceCode.getFirstToken(node); 54 const lastTokenOfTest = sourceCode.getTokenBefore(questionToken); 55 const firstTokenOfConsequent = sourceCode.getTokenAfter(questionToken); 56 const lastTokenOfConsequent = sourceCode.getTokenBefore(colonToken); 57 const firstTokenOfAlternate = sourceCode.getTokenAfter(colonToken); 58 59 const areTestAndConsequentOnSameLine = astUtils.isTokenOnSameLine(lastTokenOfTest, firstTokenOfConsequent); 60 const areConsequentAndAlternateOnSameLine = astUtils.isTokenOnSameLine(lastTokenOfConsequent, firstTokenOfAlternate); 61 62 if (!multiline) { 63 if (!areTestAndConsequentOnSameLine) { 64 context.report({ 65 node: node.test, 66 loc: { 67 start: firstTokenOfTest.loc.start, 68 end: lastTokenOfTest.loc.end 69 }, 70 messageId: "unexpectedTestCons" 71 }); 72 } 73 74 if (!areConsequentAndAlternateOnSameLine) { 75 context.report({ 76 node: node.consequent, 77 loc: { 78 start: firstTokenOfConsequent.loc.start, 79 end: lastTokenOfConsequent.loc.end 80 }, 81 messageId: "unexpectedConsAlt" 82 }); 83 } 84 } else { 85 if (allowSingleLine && node.loc.start.line === node.loc.end.line) { 86 return; 87 } 88 89 if (areTestAndConsequentOnSameLine) { 90 context.report({ 91 node: node.test, 92 loc: { 93 start: firstTokenOfTest.loc.start, 94 end: lastTokenOfTest.loc.end 95 }, 96 messageId: "expectedTestCons" 97 }); 98 } 99 100 if (areConsequentAndAlternateOnSameLine) { 101 context.report({ 102 node: node.consequent, 103 loc: { 104 start: firstTokenOfConsequent.loc.start, 105 end: lastTokenOfConsequent.loc.end 106 }, 107 messageId: "expectedConsAlt" 108 }); 109 } 110 } 111 } 112 }; 113 } 114}; 115