2 * @fileoverview Enforce newlines between operands of ternary expressions
8 const astUtils = require("./utils/ast-utils");
10 //------------------------------------------------------------------------------
12 //------------------------------------------------------------------------------
19 description: "enforce newlines between operands of ternary expressions",
20 category: "Stylistic Issues",
22 url: "https://eslint.org/docs/rules/multiline-ternary"
27 enum: ["always", "always-multiline", "never"]
32 expectedTestCons: "Expected newline between test and consequent of ternary expression.",
33 expectedConsAlt: "Expected newline between consequent and alternate of ternary expression.",
34 unexpectedTestCons: "Unexpected newline between test and consequent of ternary expression.",
35 unexpectedConsAlt: "Unexpected newline between consequent and alternate of ternary expression."
42 const sourceCode = context.getSourceCode();
43 const option = context.options[0];
44 const multiline = option !== "never";
45 const allowSingleLine = option === "always-multiline";
47 //--------------------------------------------------------------------------
49 //--------------------------------------------------------------------------
52 ConditionalExpression(node) {
53 const questionToken = sourceCode.getTokenAfter(node.test, astUtils.isNotClosingParenToken);
54 const colonToken = sourceCode.getTokenAfter(node.consequent, astUtils.isNotClosingParenToken);
56 const firstTokenOfTest = sourceCode.getFirstToken(node);
57 const lastTokenOfTest = sourceCode.getTokenBefore(questionToken);
58 const firstTokenOfConsequent = sourceCode.getTokenAfter(questionToken);
59 const lastTokenOfConsequent = sourceCode.getTokenBefore(colonToken);
60 const firstTokenOfAlternate = sourceCode.getTokenAfter(colonToken);
62 const areTestAndConsequentOnSameLine = astUtils.isTokenOnSameLine(lastTokenOfTest, firstTokenOfConsequent);
63 const areConsequentAndAlternateOnSameLine = astUtils.isTokenOnSameLine(lastTokenOfConsequent, firstTokenOfAlternate);
65 const hasComments = !!sourceCode.getCommentsInside(node).length;
68 if (!areTestAndConsequentOnSameLine) {
72 start: firstTokenOfTest.loc.start,
73 end: lastTokenOfTest.loc.end
75 messageId: "unexpectedTestCons",
81 const areTestAndQuestionOnSameLine = astUtils.isTokenOnSameLine(lastTokenOfTest, questionToken);
82 const areQuestionAndConsOnSameLine = astUtils.isTokenOnSameLine(questionToken, firstTokenOfConsequent);
84 if (!areTestAndQuestionOnSameLine) {
85 fixers.push(fixer.removeRange([lastTokenOfTest.range[1], questionToken.range[0]]));
87 if (!areQuestionAndConsOnSameLine) {
88 fixers.push(fixer.removeRange([questionToken.range[1], firstTokenOfConsequent.range[0]]));
96 if (!areConsequentAndAlternateOnSameLine) {
98 node: node.consequent,
100 start: firstTokenOfConsequent.loc.start,
101 end: lastTokenOfConsequent.loc.end
103 messageId: "unexpectedConsAlt",
109 const areConsAndColonOnSameLine = astUtils.isTokenOnSameLine(lastTokenOfConsequent, colonToken);
110 const areColonAndAltOnSameLine = astUtils.isTokenOnSameLine(colonToken, firstTokenOfAlternate);
112 if (!areConsAndColonOnSameLine) {
113 fixers.push(fixer.removeRange([lastTokenOfConsequent.range[1], colonToken.range[0]]));
115 if (!areColonAndAltOnSameLine) {
116 fixers.push(fixer.removeRange([colonToken.range[1], firstTokenOfAlternate.range[0]]));
124 if (allowSingleLine && node.loc.start.line === node.loc.end.line) {
128 if (areTestAndConsequentOnSameLine) {
132 start: firstTokenOfTest.loc.start,
133 end: lastTokenOfTest.loc.end
135 messageId: "expectedTestCons",
136 fix: fixer => (hasComments ? null : (
137 fixer.replaceTextRange(
139 lastTokenOfTest.range[1],
140 questionToken.range[0]
148 if (areConsequentAndAlternateOnSameLine) {
150 node: node.consequent,
152 start: firstTokenOfConsequent.loc.start,
153 end: lastTokenOfConsequent.loc.end
155 messageId: "expectedConsAlt",
156 fix: (fixer => (hasComments ? null : (
157 fixer.replaceTextRange(
159 lastTokenOfConsequent.range[1],