diff --git a/docs/rules/operator-linebreak.md b/docs/rules/operator-linebreak.md index 159bd528ad5..6ae484ea5cd 100644 --- a/docs/rules/operator-linebreak.md +++ b/docs/rules/operator-linebreak.md @@ -60,6 +60,16 @@ if (someCondition answer = everything ? 42 : foo; + +class Foo { + a + = 1; + [b] + = 2; + [c + ] + = 3; +} ``` Examples of **correct** code for this rule with the `"after"` option: @@ -82,6 +92,17 @@ if (someCondition || answer = everything ? 42 : foo; + +class Foo { + a = + 1; + [b] = + 2; + [c + ] = + 3; + d = 4; +} ``` ### before @@ -104,6 +125,16 @@ if (someCondition || answer = everything ? 42 : foo; + +class Foo { + a = + 1; + [b] = + 2; + [c + ] = + 3; +} ``` Examples of **correct** code for this rule with the `"before"` option: @@ -126,6 +157,17 @@ if (someCondition answer = everything ? 42 : foo; + +class Foo { + a + = 1; + [b] + = 2; + [c + ] + = 3; + d = 4; +} ``` ### none @@ -156,6 +198,23 @@ answer = everything answer = everything ? 42 : foo; + +class Foo { + a = + 1; + [b] = + 2; + [c + ] = + 3; + d + = 4; + [e] + = 5; + [f + ] + = 6; +} ``` Examples of **correct** code for this rule with the `"none"` option: @@ -171,6 +230,17 @@ if (someCondition || otherCondition) { } answer = everything ? 42 : foo; + +class Foo { + a = 1; + [b] = 2; + [c + ] = 3; + d = 4; + [e] = 5; + [f + ] = 6; +} ``` ### overrides diff --git a/lib/rules/operator-linebreak.js b/lib/rules/operator-linebreak.js index 40ca880572f..2a0bfbddf17 100644 --- a/lib/rules/operator-linebreak.js +++ b/lib/rules/operator-linebreak.js @@ -136,23 +136,21 @@ module.exports = { /** * Checks the operator placement * @param {ASTNode} node The node to check - * @param {ASTNode} leftSide The node that comes before the operator in `node` + * @param {ASTNode} rightSide The node that comes after the operator in `node` + * @param {string} operator The operator * @private * @returns {void} */ - function validateNode(node, leftSide) { + function validateNode(node, rightSide, operator) { /* - * When the left part of a binary expression is a single expression wrapped in - * parentheses (ex: `(a) + b`), leftToken will be the last token of the expression - * and operatorToken will be the closing parenthesis. - * The leftToken should be the last closing parenthesis, and the operatorToken - * should be the token right after that. + * Find the operator token by searching from the right side, because between the left side and the operator + * there could be additional tokens from type annotations. Search specifically for the token which + * value equals the operator, in order to skip possible opening parentheses before the right side node. */ - const operatorToken = sourceCode.getTokenAfter(leftSide, astUtils.isNotClosingParenToken); + const operatorToken = sourceCode.getTokenBefore(rightSide, token => token.value === operator); const leftToken = sourceCode.getTokenBefore(operatorToken); const rightToken = sourceCode.getTokenAfter(operatorToken); - const operator = operatorToken.value; const operatorStyleOverride = styleOverrides[operator]; const style = operatorStyleOverride || globalStyle; const fix = getFixer(operatorToken, style); @@ -222,7 +220,7 @@ module.exports = { * @returns {void} */ function validateBinaryExpression(node) { - validateNode(node, node.left); + validateNode(node, node.right, node.operator); } //-------------------------------------------------------------------------- @@ -235,17 +233,17 @@ module.exports = { AssignmentExpression: validateBinaryExpression, VariableDeclarator(node) { if (node.init) { - validateNode(node, node.id); + validateNode(node, node.init, "="); } }, PropertyDefinition(node) { if (node.value) { - validateNode(node, node.key); + validateNode(node, node.value, "="); } }, ConditionalExpression(node) { - validateNode(node, node.test); - validateNode(node, node.consequent); + validateNode(node, node.consequent, "?"); + validateNode(node, node.alternate, ":"); } }; } diff --git a/tests/lib/rules/operator-linebreak.js b/tests/lib/rules/operator-linebreak.js index e94d48c116b..8810c040776 100644 --- a/tests/lib/rules/operator-linebreak.js +++ b/tests/lib/rules/operator-linebreak.js @@ -56,6 +56,7 @@ ruleTester.run("operator-linebreak", rule, { { code: "\n1 + 1", options: ["none"] }, { code: "1 + 1\n", options: ["none"] }, { code: "answer = everything ? 42 : foo;", options: ["none"] }, + { code: "(a\n) + (\nb)", options: ["none"] }, { code: "answer = everything \n?\n 42 : foo;", options: [null, { overrides: { "?": "ignore" } }] }, { code: "answer = everything ? 42 \n:\n foo;", options: [null, { overrides: { ":": "ignore" } }] }, @@ -100,6 +101,7 @@ ruleTester.run("operator-linebreak", rule, { parserOptions: { ecmaVersion: 2021 } }, + // class fields { code: "class C { foo =\n0 }", parserOptions: { ecmaVersion: 2022 } @@ -108,6 +110,41 @@ ruleTester.run("operator-linebreak", rule, { code: "class C { foo\n= 0 }", options: ["before"], parserOptions: { ecmaVersion: 2022 } + }, + { + code: "class C { [foo\n]= 0 }", + options: ["before"], + parserOptions: { ecmaVersion: 2022 } + }, + { + code: "class C { [foo]\n= 0 }", + options: ["before"], + parserOptions: { ecmaVersion: 2022 } + }, + { + code: "class C { [foo\n]\n= 0 }", + options: ["before"], + parserOptions: { ecmaVersion: 2022 } + }, + { + code: "class C { [foo\n]= 0 }", + options: ["after"], + parserOptions: { ecmaVersion: 2022 } + }, + { + code: "class C { [foo\n]=\n0 }", + options: ["after"], + parserOptions: { ecmaVersion: 2022 } + }, + { + code: "class C { [foo\n]= 0 }", + options: ["none"], + parserOptions: { ecmaVersion: 2022 } + }, + { + code: "class C { foo\n=\n0 }", + options: ["none", { overrides: { "=": "ignore" } }], + parserOptions: { ecmaVersion: 2022 } } ], @@ -782,6 +819,7 @@ ruleTester.run("operator-linebreak", rule, { }] }, + // class fields { code: "class C { a\n= 0; }", output: "class C { a =\n0; }", @@ -826,6 +864,51 @@ ruleTester.run("operator-linebreak", rule, { endLine: 1, endColumn: 14 }] + }, + { + code: "class C { [a]\n= 0; }", + output: "class C { [a] =\n0; }", + options: ["after"], + parserOptions: { ecmaVersion: 2022 }, + errors: [{ + messageId: "operatorAtEnd", + data: { operator: "=" }, + type: "PropertyDefinition", + line: 2, + column: 1, + endLine: 2, + endColumn: 2 + }] + }, + { + code: "class C { [a] =\n0; }", + output: "class C { [a]\n= 0; }", + options: ["before"], + parserOptions: { ecmaVersion: 2022 }, + errors: [{ + messageId: "operatorAtBeginning", + data: { operator: "=" }, + type: "PropertyDefinition", + line: 1, + column: 15, + endLine: 1, + endColumn: 16 + }] + }, + { + code: "class C { [a]\n =0; }", + output: "class C { [a] =0; }", + options: ["none"], + parserOptions: { ecmaVersion: 2022 }, + errors: [{ + messageId: "noLinebreak", + data: { operator: "=" }, + type: "PropertyDefinition", + line: 2, + column: 2, + endLine: 2, + endColumn: 3 + }] } ] });