1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 | "use strict";
|
7 |
|
8 | const astUtils = require("./utils/ast-utils");
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 | module.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 |
|
31 | messages: {
|
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."
|
36 | },
|
37 |
|
38 | fixable: "whitespace"
|
39 | },
|
40 |
|
41 | create(context) {
|
42 | const sourceCode = context.getSourceCode();
|
43 | const option = context.options[0];
|
44 | const multiline = option !== "never";
|
45 | const allowSingleLine = option === "always-multiline";
|
46 |
|
47 |
|
48 |
|
49 |
|
50 |
|
51 | return {
|
52 | ConditionalExpression(node) {
|
53 | const questionToken = sourceCode.getTokenAfter(node.test, astUtils.isNotClosingParenToken);
|
54 | const colonToken = sourceCode.getTokenAfter(node.consequent, astUtils.isNotClosingParenToken);
|
55 |
|
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);
|
61 |
|
62 | const areTestAndConsequentOnSameLine = astUtils.isTokenOnSameLine(lastTokenOfTest, firstTokenOfConsequent);
|
63 | const areConsequentAndAlternateOnSameLine = astUtils.isTokenOnSameLine(lastTokenOfConsequent, firstTokenOfAlternate);
|
64 |
|
65 | const hasComments = !!sourceCode.getCommentsInside(node).length;
|
66 |
|
67 | if (!multiline) {
|
68 | if (!areTestAndConsequentOnSameLine) {
|
69 | context.report({
|
70 | node: node.test,
|
71 | loc: {
|
72 | start: firstTokenOfTest.loc.start,
|
73 | end: lastTokenOfTest.loc.end
|
74 | },
|
75 | messageId: "unexpectedTestCons",
|
76 | fix: fixer => {
|
77 | if (hasComments) {
|
78 | return null;
|
79 | }
|
80 | const fixers = [];
|
81 | const areTestAndQuestionOnSameLine = astUtils.isTokenOnSameLine(lastTokenOfTest, questionToken);
|
82 | const areQuestionAndConsOnSameLine = astUtils.isTokenOnSameLine(questionToken, firstTokenOfConsequent);
|
83 |
|
84 | if (!areTestAndQuestionOnSameLine) {
|
85 | fixers.push(fixer.removeRange([lastTokenOfTest.range[1], questionToken.range[0]]));
|
86 | }
|
87 | if (!areQuestionAndConsOnSameLine) {
|
88 | fixers.push(fixer.removeRange([questionToken.range[1], firstTokenOfConsequent.range[0]]));
|
89 | }
|
90 |
|
91 | return fixers;
|
92 | }
|
93 | });
|
94 | }
|
95 |
|
96 | if (!areConsequentAndAlternateOnSameLine) {
|
97 | context.report({
|
98 | node: node.consequent,
|
99 | loc: {
|
100 | start: firstTokenOfConsequent.loc.start,
|
101 | end: lastTokenOfConsequent.loc.end
|
102 | },
|
103 | messageId: "unexpectedConsAlt",
|
104 | fix: fixer => {
|
105 | if (hasComments) {
|
106 | return null;
|
107 | }
|
108 | const fixers = [];
|
109 | const areConsAndColonOnSameLine = astUtils.isTokenOnSameLine(lastTokenOfConsequent, colonToken);
|
110 | const areColonAndAltOnSameLine = astUtils.isTokenOnSameLine(colonToken, firstTokenOfAlternate);
|
111 |
|
112 | if (!areConsAndColonOnSameLine) {
|
113 | fixers.push(fixer.removeRange([lastTokenOfConsequent.range[1], colonToken.range[0]]));
|
114 | }
|
115 | if (!areColonAndAltOnSameLine) {
|
116 | fixers.push(fixer.removeRange([colonToken.range[1], firstTokenOfAlternate.range[0]]));
|
117 | }
|
118 |
|
119 | return fixers;
|
120 | }
|
121 | });
|
122 | }
|
123 | } else {
|
124 | if (allowSingleLine && node.loc.start.line === node.loc.end.line) {
|
125 | return;
|
126 | }
|
127 |
|
128 | if (areTestAndConsequentOnSameLine) {
|
129 | context.report({
|
130 | node: node.test,
|
131 | loc: {
|
132 | start: firstTokenOfTest.loc.start,
|
133 | end: lastTokenOfTest.loc.end
|
134 | },
|
135 | messageId: "expectedTestCons",
|
136 | fix: fixer => (hasComments ? null : (
|
137 | fixer.replaceTextRange(
|
138 | [
|
139 | lastTokenOfTest.range[1],
|
140 | questionToken.range[0]
|
141 | ],
|
142 | "\n"
|
143 | )
|
144 | ))
|
145 | });
|
146 | }
|
147 |
|
148 | if (areConsequentAndAlternateOnSameLine) {
|
149 | context.report({
|
150 | node: node.consequent,
|
151 | loc: {
|
152 | start: firstTokenOfConsequent.loc.start,
|
153 | end: lastTokenOfConsequent.loc.end
|
154 | },
|
155 | messageId: "expectedConsAlt",
|
156 | fix: (fixer => (hasComments ? null : (
|
157 | fixer.replaceTextRange(
|
158 | [
|
159 | lastTokenOfConsequent.range[1],
|
160 | colonToken.range[0]
|
161 | ],
|
162 | "\n"
|
163 | )
|
164 | )))
|
165 | });
|
166 | }
|
167 | }
|
168 | }
|
169 | };
|
170 | }
|
171 | };
|