• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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