]>
Commit | Line | Data |
---|---|---|
eb39fafa DC |
1 | /** |
2 | * @fileoverview Enforce newlines between operands of ternary expressions | |
3 | * @author Kai Cataldo | |
4 | */ | |
5 | ||
6 | "use strict"; | |
7 | ||
8 | const astUtils = require("./utils/ast-utils"); | |
9 | ||
10 | //------------------------------------------------------------------------------ | |
11 | // Rule Definition | |
12 | //------------------------------------------------------------------------------ | |
13 | ||
14 | module.exports = { | |
15 | meta: { | |
16 | type: "layout", | |
17 | ||
18 | docs: { | |
19 | description: "enforce newlines between operands of ternary expressions", | |
eb39fafa DC |
20 | recommended: false, |
21 | url: "https://eslint.org/docs/rules/multiline-ternary" | |
22 | }, | |
23 | ||
24 | schema: [ | |
25 | { | |
26 | enum: ["always", "always-multiline", "never"] | |
27 | } | |
28 | ], | |
456be15e | 29 | |
eb39fafa DC |
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." | |
456be15e TL |
35 | }, |
36 | ||
37 | fixable: "whitespace" | |
eb39fafa DC |
38 | }, |
39 | ||
40 | create(context) { | |
456be15e | 41 | const sourceCode = context.getSourceCode(); |
eb39fafa DC |
42 | const option = context.options[0]; |
43 | const multiline = option !== "never"; | |
44 | const allowSingleLine = option === "always-multiline"; | |
eb39fafa DC |
45 | |
46 | //-------------------------------------------------------------------------- | |
47 | // Public | |
48 | //-------------------------------------------------------------------------- | |
49 | ||
50 | return { | |
51 | ConditionalExpression(node) { | |
d3726936 TL |
52 | const questionToken = sourceCode.getTokenAfter(node.test, astUtils.isNotClosingParenToken); |
53 | const colonToken = sourceCode.getTokenAfter(node.consequent, astUtils.isNotClosingParenToken); | |
54 | ||
55 | const firstTokenOfTest = sourceCode.getFirstToken(node); | |
56 | const lastTokenOfTest = sourceCode.getTokenBefore(questionToken); | |
57 | const firstTokenOfConsequent = sourceCode.getTokenAfter(questionToken); | |
58 | const lastTokenOfConsequent = sourceCode.getTokenBefore(colonToken); | |
59 | const firstTokenOfAlternate = sourceCode.getTokenAfter(colonToken); | |
60 | ||
61 | const areTestAndConsequentOnSameLine = astUtils.isTokenOnSameLine(lastTokenOfTest, firstTokenOfConsequent); | |
62 | const areConsequentAndAlternateOnSameLine = astUtils.isTokenOnSameLine(lastTokenOfConsequent, firstTokenOfAlternate); | |
eb39fafa | 63 | |
456be15e TL |
64 | const hasComments = !!sourceCode.getCommentsInside(node).length; |
65 | ||
eb39fafa DC |
66 | if (!multiline) { |
67 | if (!areTestAndConsequentOnSameLine) { | |
d3726936 TL |
68 | context.report({ |
69 | node: node.test, | |
70 | loc: { | |
71 | start: firstTokenOfTest.loc.start, | |
72 | end: lastTokenOfTest.loc.end | |
73 | }, | |
456be15e TL |
74 | messageId: "unexpectedTestCons", |
75 | fix: fixer => { | |
76 | if (hasComments) { | |
77 | return null; | |
78 | } | |
79 | const fixers = []; | |
80 | const areTestAndQuestionOnSameLine = astUtils.isTokenOnSameLine(lastTokenOfTest, questionToken); | |
81 | const areQuestionAndConsOnSameLine = astUtils.isTokenOnSameLine(questionToken, firstTokenOfConsequent); | |
82 | ||
83 | if (!areTestAndQuestionOnSameLine) { | |
84 | fixers.push(fixer.removeRange([lastTokenOfTest.range[1], questionToken.range[0]])); | |
85 | } | |
86 | if (!areQuestionAndConsOnSameLine) { | |
87 | fixers.push(fixer.removeRange([questionToken.range[1], firstTokenOfConsequent.range[0]])); | |
88 | } | |
89 | ||
90 | return fixers; | |
91 | } | |
d3726936 | 92 | }); |
eb39fafa DC |
93 | } |
94 | ||
95 | if (!areConsequentAndAlternateOnSameLine) { | |
d3726936 TL |
96 | context.report({ |
97 | node: node.consequent, | |
98 | loc: { | |
99 | start: firstTokenOfConsequent.loc.start, | |
100 | end: lastTokenOfConsequent.loc.end | |
101 | }, | |
456be15e TL |
102 | messageId: "unexpectedConsAlt", |
103 | fix: fixer => { | |
104 | if (hasComments) { | |
105 | return null; | |
106 | } | |
107 | const fixers = []; | |
108 | const areConsAndColonOnSameLine = astUtils.isTokenOnSameLine(lastTokenOfConsequent, colonToken); | |
109 | const areColonAndAltOnSameLine = astUtils.isTokenOnSameLine(colonToken, firstTokenOfAlternate); | |
110 | ||
111 | if (!areConsAndColonOnSameLine) { | |
112 | fixers.push(fixer.removeRange([lastTokenOfConsequent.range[1], colonToken.range[0]])); | |
113 | } | |
114 | if (!areColonAndAltOnSameLine) { | |
115 | fixers.push(fixer.removeRange([colonToken.range[1], firstTokenOfAlternate.range[0]])); | |
116 | } | |
117 | ||
118 | return fixers; | |
119 | } | |
d3726936 | 120 | }); |
eb39fafa DC |
121 | } |
122 | } else { | |
123 | if (allowSingleLine && node.loc.start.line === node.loc.end.line) { | |
124 | return; | |
125 | } | |
126 | ||
127 | if (areTestAndConsequentOnSameLine) { | |
d3726936 TL |
128 | context.report({ |
129 | node: node.test, | |
130 | loc: { | |
131 | start: firstTokenOfTest.loc.start, | |
132 | end: lastTokenOfTest.loc.end | |
133 | }, | |
456be15e TL |
134 | messageId: "expectedTestCons", |
135 | fix: fixer => (hasComments ? null : ( | |
136 | fixer.replaceTextRange( | |
137 | [ | |
138 | lastTokenOfTest.range[1], | |
139 | questionToken.range[0] | |
140 | ], | |
141 | "\n" | |
142 | ) | |
143 | )) | |
d3726936 | 144 | }); |
eb39fafa DC |
145 | } |
146 | ||
147 | if (areConsequentAndAlternateOnSameLine) { | |
d3726936 TL |
148 | context.report({ |
149 | node: node.consequent, | |
150 | loc: { | |
151 | start: firstTokenOfConsequent.loc.start, | |
152 | end: lastTokenOfConsequent.loc.end | |
153 | }, | |
456be15e TL |
154 | messageId: "expectedConsAlt", |
155 | fix: (fixer => (hasComments ? null : ( | |
156 | fixer.replaceTextRange( | |
157 | [ | |
158 | lastTokenOfConsequent.range[1], | |
159 | colonToken.range[0] | |
160 | ], | |
161 | "\n" | |
162 | ) | |
163 | ))) | |
d3726936 | 164 | }); |
eb39fafa DC |
165 | } |
166 | } | |
167 | } | |
168 | }; | |
169 | } | |
170 | }; |