]>
git.proxmox.com Git - pve-eslint.git/blob - eslint/lib/rules/operator-linebreak.js
2 * @fileoverview Operator linebreak - enforces operator linebreak style of two types: after and before
3 * @author BenoƮt Zugmeyer
8 //------------------------------------------------------------------------------
10 //------------------------------------------------------------------------------
12 const astUtils
= require("./utils/ast-utils");
14 //------------------------------------------------------------------------------
16 //------------------------------------------------------------------------------
18 /** @type {import('../shared/types').Rule} */
24 description
: "enforce consistent linebreak style for operators",
26 url
: "https://eslint.org/docs/rules/operator-linebreak"
31 enum: ["after", "before", "none", null]
38 additionalProperties
: {
39 enum: ["after", "before", "none", "ignore"]
43 additionalProperties
: false
50 operatorAtBeginning
: "'{{operator}}' should be placed at the beginning of the line.",
51 operatorAtEnd
: "'{{operator}}' should be placed at the end of the line.",
52 badLinebreak
: "Bad line breaking before and after '{{operator}}'.",
53 noLinebreak
: "There should be no line break before or after '{{operator}}'."
59 const usedDefaultGlobal
= !context
.options
[0];
60 const globalStyle
= context
.options
[0] || "after";
61 const options
= context
.options
[1] || {};
62 const styleOverrides
= options
.overrides
? Object
.assign({}, options
.overrides
) : {};
64 if (usedDefaultGlobal
&& !styleOverrides
["?"]) {
65 styleOverrides
["?"] = "before";
68 if (usedDefaultGlobal
&& !styleOverrides
[":"]) {
69 styleOverrides
[":"] = "before";
72 const sourceCode
= context
.getSourceCode();
74 //--------------------------------------------------------------------------
76 //--------------------------------------------------------------------------
79 * Gets a fixer function to fix rule issues
80 * @param {Token} operatorToken The operator token of an expression
81 * @param {string} desiredStyle The style for the rule. One of 'before', 'after', 'none'
82 * @returns {Function} A fixer function
84 function getFixer(operatorToken
, desiredStyle
) {
86 const tokenBefore
= sourceCode
.getTokenBefore(operatorToken
);
87 const tokenAfter
= sourceCode
.getTokenAfter(operatorToken
);
88 const textBefore
= sourceCode
.text
.slice(tokenBefore
.range
[1], operatorToken
.range
[0]);
89 const textAfter
= sourceCode
.text
.slice(operatorToken
.range
[1], tokenAfter
.range
[0]);
90 const hasLinebreakBefore
= !astUtils
.isTokenOnSameLine(tokenBefore
, operatorToken
);
91 const hasLinebreakAfter
= !astUtils
.isTokenOnSameLine(operatorToken
, tokenAfter
);
92 let newTextBefore
, newTextAfter
;
94 if (hasLinebreakBefore
!== hasLinebreakAfter
&& desiredStyle
!== "none") {
96 // If there is a comment before and after the operator, don't do a fix.
97 if (sourceCode
.getTokenBefore(operatorToken
, { includeComments
: true }) !== tokenBefore
&&
98 sourceCode
.getTokenAfter(operatorToken
, { includeComments
: true }) !== tokenAfter
) {
104 * If there is only one linebreak and it's on the wrong side of the operator, swap the text before and after the operator.
111 newTextBefore
= textAfter
;
112 newTextAfter
= textBefore
;
114 const LINEBREAK_REGEX
= astUtils
.createGlobalLinebreakMatcher();
116 // Otherwise, if no linebreak is desired and no comments interfere, replace the linebreaks with empty strings.
117 newTextBefore
= desiredStyle
=== "before" || textBefore
.trim() ? textBefore
: textBefore
.replace(LINEBREAK_REGEX
, "");
118 newTextAfter
= desiredStyle
=== "after" || textAfter
.trim() ? textAfter
: textAfter
.replace(LINEBREAK_REGEX
, "");
120 // If there was no change (due to interfering comments), don't output a fix.
121 if (newTextBefore
=== textBefore
&& newTextAfter
=== textAfter
) {
126 if (newTextAfter
=== "" && tokenAfter
.type
=== "Punctuator" && "+-".includes(operatorToken
.value
) && tokenAfter
.value
=== operatorToken
.value
) {
128 // To avoid accidentally creating a ++ or -- operator, insert a space if the operator is a +/- and the following token is a unary +/-.
132 return fixer
.replaceTextRange([tokenBefore
.range
[1], tokenAfter
.range
[0]], newTextBefore
+ operatorToken
.value
+ newTextAfter
);
137 * Checks the operator placement
138 * @param {ASTNode} node The node to check
139 * @param {ASTNode} rightSide The node that comes after the operator in `node`
140 * @param {string} operator The operator
144 function validateNode(node
, rightSide
, operator
) {
147 * Find the operator token by searching from the right side, because between the left side and the operator
148 * there could be additional tokens from type annotations. Search specifically for the token which
149 * value equals the operator, in order to skip possible opening parentheses before the right side node.
151 const operatorToken
= sourceCode
.getTokenBefore(rightSide
, token
=> token
.value
=== operator
);
152 const leftToken
= sourceCode
.getTokenBefore(operatorToken
);
153 const rightToken
= sourceCode
.getTokenAfter(operatorToken
);
154 const operatorStyleOverride
= styleOverrides
[operator
];
155 const style
= operatorStyleOverride
|| globalStyle
;
156 const fix
= getFixer(operatorToken
, style
);
159 if (astUtils
.isTokenOnSameLine(leftToken
, operatorToken
) &&
160 astUtils
.isTokenOnSameLine(operatorToken
, rightToken
)) {
164 } else if (operatorStyleOverride
!== "ignore" && !astUtils
.isTokenOnSameLine(leftToken
, operatorToken
) &&
165 !astUtils
.isTokenOnSameLine(operatorToken
, rightToken
)) {
170 loc
: operatorToken
.loc
,
171 messageId
: "badLinebreak",
178 } else if (style
=== "before" && astUtils
.isTokenOnSameLine(leftToken
, operatorToken
)) {
182 loc
: operatorToken
.loc
,
183 messageId
: "operatorAtBeginning",
190 } else if (style
=== "after" && astUtils
.isTokenOnSameLine(operatorToken
, rightToken
)) {
194 loc
: operatorToken
.loc
,
195 messageId
: "operatorAtEnd",
202 } else if (style
=== "none") {
206 loc
: operatorToken
.loc
,
207 messageId
: "noLinebreak",
218 * Validates a binary expression using `validateNode`
219 * @param {BinaryExpression|LogicalExpression|AssignmentExpression} node node to be validated
222 function validateBinaryExpression(node
) {
223 validateNode(node
, node
.right
, node
.operator
);
226 //--------------------------------------------------------------------------
228 //--------------------------------------------------------------------------
231 BinaryExpression
: validateBinaryExpression
,
232 LogicalExpression
: validateBinaryExpression
,
233 AssignmentExpression
: validateBinaryExpression
,
234 VariableDeclarator(node
) {
236 validateNode(node
, node
.init
, "=");
239 PropertyDefinition(node
) {
241 validateNode(node
, node
.value
, "=");
244 ConditionalExpression(node
) {
245 validateNode(node
, node
.consequent
, "?");
246 validateNode(node
, node
.alternate
, ":");