Repository: eslint/eslint
Pull request #14916 (2021-08-11 02:40:15)

Docs: add an example `Object.assign()` for rule no-import-assign
<!--
    Thank you for contributing!

    ESLint adheres to the [JS Foundation Code of Conduct](https://js.foundation/community/code-of-conduct).
-->

#### Prerequisites checklist

- [x] I have read the [contributing guidelines](https://github.com/eslint/eslint/blob/master/CONTRIBUTING.md).

#### What is the purpose of this pull request? (put an "X" next to an item)

<!--
    The following template is intentionally not a markdown checkbox list for the reasons
    explained in https://github.com/eslint/eslint/pull/12848#issuecomment-580302888
-->

[ x] Documentation update
[ ] Bug fix ([template](https://raw.githubusercontent.com/eslint/eslint/master/templates/bug-report.md))
[ ] New rule ([template](https://raw.githubusercontent.com/eslint/eslint/master/templates/rule-proposal.md))
[ ] Changes an existing rule ([template](https://raw.githubusercontent.com/eslint/eslint/master/templates/rule-change-proposal.md))
[ ] Add autofixing to a rule
[ ] Add a CLI option
[ ] Add something to the core
[ ] Other, please explain:

<!--
    If the item you've checked above has a template, please paste the template questions below and answer them. (If this pull request is addressing an issue, you can just paste a link to the issue here instead.)
-->

<!--
    Please ensure your pull request is ready:

    - Read the pull request guide (https://eslint.org/docs/developer-guide/contributing/pull-requests)
    - Include tests for this change
    - Update documentation for this change (if appropriate)
-->

<!--
    The following is required for all pull requests:
-->

#### What changes did you make? (Give an overview)
as https://github.com/eslint/eslint/issues/14911#issuecomment-896276484, it has been supported but not documented.

#### Is there anything you'd like reviewers to focus on?



Patch:
diff --git a/docs/rules/no-import-assign.md b/docs/rules/no-import-assign.md
index 78b228dd8..49595cda2 100644
--- a/docs/rules/no-import-assign.md
+++ b/docs/rules/no-import-assign.md
@@ -16,8 +16,10 @@ import * as mod_ns from "./mod.mjs"
 
 mod = 1          // ERROR: 'mod' is readonly.
 named = 2        // ERROR: 'named' is readonly.
-mod_ns.named = 3 // ERROR: the members of 'mod_ns' is readonly.
+mod_ns.named = 3 // ERROR: The members of 'mod_ns' are readonly.
 mod_ns = {}      // ERROR: 'mod_ns' is readonly.
+// Can't extend 'mod_ns'
+Object.assign(mod_ns, { foo: "foo" }) // ERROR: The members of 'mod_ns' are readonly.
 ```
 
 Examples of **correct** code for this rule:
diff --git a/docs/rules/operator-linebreak.md b/docs/rules/operator-linebreak.md
index 159bd528a..6ae484ea5 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 40ca88057..2a0bfbddf 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, ":");
             }
         };
     }


Test patch:
diff --git a/tests/lib/rules/operator-linebreak.js b/tests/lib/rules/operator-linebreak.js
index e94d48c11..8810c0407 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
+            }]
         }
     ]
 });