Repository: eslint/eslint
Pull request #10898 (2018-09-27 06:36:49)

Build: Add karma-chrome-launcher support 
Removed phantomjs dependency from package.json and karma.conf.js.

Signed-off-by: ossdev07 <ossdev@puresoftware.com>

**What is the purpose of this pull request? (put an "X" next to item)**

[ ] Documentation update
[ ] Bug fix [template]
[ ] New rule [template]
[X] Changes an existing rule [template]
[ ] Add autofixing to a rule
[ ] Add a CLI option
[ ] Add something to the core
[ ] Other, please explain:

What changes did you make? (Give an overview)
Just added the "customLauncher" support which allow to use HeadlessChrome with --no-sandbox flag. 

Is there anything you'd like reviewers to focus on?
Phantomjs is not supported anymore should use chrome instead.



jsf-clabot: [![CLA assistant check](https://cla.js.foundation/pull/badge/not_signed)](https://cla.js.foundation/eslint/eslint?pullRequest=10898) <br/>Thank you for your submission, we really appreciate it. Like many open source projects, we ask that you sign our [Contributor License Agreement](https://cla.js.foundation/eslint/eslint?pullRequest=10898) before we can accept your contribution.<br/>
kaicataldo: One more thing: would you mind formatting the commit message following [our guidelines](https://eslint.org/docs/developer-guide/contributing/pull-requests#step-2-make-your-changes)? We ask this of committers so that we can keep the generation of our changelog automated.

Patch:
diff --git a/.github/ISSUE_TEMPLATE/BUG_REPORT.md b/.github/ISSUE_TEMPLATE/BUG_REPORT.md
index ef8c5101c..47c0c84c3 100644
--- a/.github/ISSUE_TEMPLATE/BUG_REPORT.md
+++ b/.github/ISSUE_TEMPLATE/BUG_REPORT.md
@@ -1,5 +1,5 @@
 ---
-name: Bug report
+name: "\U0001F41E  Bug report"
 about: Report an issue with ESLint or rules bundled with ESLint
 
 ---
@@ -51,4 +51,4 @@ about: Report an issue with ESLint or rules bundled with ESLint
 
 **What did you expect to happen?**
 
-**What actually happened? Please include the actual, raw output from ESLint.**
\ No newline at end of file
+**What actually happened? Please include the actual, raw output from ESLint.**
diff --git a/.github/ISSUE_TEMPLATE/CHANGE.md b/.github/ISSUE_TEMPLATE/CHANGE.md
index 14cff91b0..b5ae2ae83 100644
--- a/.github/ISSUE_TEMPLATE/CHANGE.md
+++ b/.github/ISSUE_TEMPLATE/CHANGE.md
@@ -1,5 +1,5 @@
 ---
-name: Non-rule change request
+name: "\U0001F4DD Non-rule change request"
 about: Request a change that is not a bug fix, rule change, or new rule
 
 ---
@@ -22,4 +22,3 @@ about: Request a change that is not a bug fix, rule change, or new rule
 **The problem you want to solve.**
 
 **Your take on the correct solution to problem.**
-
diff --git a/.github/ISSUE_TEMPLATE/NEW_RULE.md b/.github/ISSUE_TEMPLATE/NEW_RULE.md
index 454b28e40..3a04dc58a 100644
--- a/.github/ISSUE_TEMPLATE/NEW_RULE.md
+++ b/.github/ISSUE_TEMPLATE/NEW_RULE.md
@@ -1,5 +1,5 @@
 ---
-name: New rule proposal
+/name: "\U0001F680 New rule proposal"
 about: Propose a new rule to be added to ESLint
 
 ---
@@ -34,4 +34,3 @@ about: Propose a new rule to be added to ESLint
 ```
 
 **Why should this rule be included in ESLint (instead of a plugin)?**
-
diff --git a/.github/ISSUE_TEMPLATE/QUESTION.md b/.github/ISSUE_TEMPLATE/QUESTION.md
new file mode 100644
index 000000000..ba96b4104
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/QUESTION.md
@@ -0,0 +1,22 @@
+---
+name: "⛔ Question"
+about: Please go to https://gitter.im/eslint/eslint
+
+---
+
+<!--
+    ESLint adheres to the [JS Foundation Code of Conduct](https://js.foundation/community/code-of-conduct).
+
+
+    Please do not use GitHub for asking questions. Instead,
+    please visit our chatroom:
+
+        https://gitter.im/eslint/eslint
+
+    Or our mailing list:
+
+        https://groups.google.com/group/eslint
+
+    Thanks in advance for helping us keep the issue tracker
+    clean!
+-->
diff --git a/.github/ISSUE_TEMPLATE/RULE_CHANGE.md b/.github/ISSUE_TEMPLATE/RULE_CHANGE.md
index 7b0c65379..cc57bf532 100644
--- a/.github/ISSUE_TEMPLATE/RULE_CHANGE.md
+++ b/.github/ISSUE_TEMPLATE/RULE_CHANGE.md
@@ -1,5 +1,5 @@
 ---
-name: Rule change request
+name: "\U0001F4DD Rule change request"
 about: Request a change to an existing rule
 
 ---
@@ -33,4 +33,3 @@ about: Request a change to an existing rule
 **What does the rule currently do for this code?**
 
 **What will the rule do after it's changed?**
-
diff --git a/.github/ISSUE_TEMPLATE/SECURITY.md b/.github/ISSUE_TEMPLATE/SECURITY.md
new file mode 100644
index 000000000..8cc33ec6e
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/SECURITY.md
@@ -0,0 +1,19 @@
+---
+name: "⛔ Security issue"
+about: Please file security issues at https://hackerone.com/eslint
+
+---
+
+<!--
+    ESLint adheres to the [JS Foundation Code of Conduct](https://js.foundation/community/code-of-conduct).
+
+
+    STOP! Please do not use GitHub for filing security issues.
+    Doing so is insecure and may put other ESLint users at risk.
+    To securely notify us of a security issue, please go to:
+        
+        https://hackerone.com/eslint
+
+    Thanks in advance for helping us keep the ESLint community
+    safe and secure.
+-->
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0a372690c..2848b4c91 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,17 @@
+v5.6.1 - September 28, 2018
+
+* 9b26bdb Fix: avoid exponential require-atomic-updates traversal (fixes #10893) (#10894) (Teddy Katz)
+* 9432b10 Fix: make separateRequires work in consecutive mode (fixes #10784) (#10886) (Pig Fang)
+* e51868d Upgrade: debug@4 (fixes #10854) (#10887) (薛定谔的猫)
+* d3f3994 Docs: add information about reporting security issues (#10889) (Teddy Katz)
+* cc458f4 Build: fix failing tests on master (#10890) (Teddy Katz)
+* a6ebfd3 Docs: clarify defaultAssignment option, fix no-unneeded-ternary examples (#10874) (CoffeeTableEspresso)
+* 9d52541 Fix: Remove duplicate error message on crash (fixes #8964) (#10865) (Nicholas C. Zakas)
+* 4eb9a49 Docs: Update quotes.md (#10862) (The Jared Wilcurt)
+* 9159e9b Docs: Update complexity.md (#10867) (Szymon Przybylski)
+* 14f4e46 Docs: Use Linter instead of linter in Nodejs API page (#10864) (Nicholas C. Zakas)
+* b3e3cb1 Chore: Update debug log name to match filename (#10863) (Nicholas C. Zakas)
+
 v5.6.0 - September 14, 2018
 
 * c5b688e Update: Added generators option to func-names (fixes #9511) (#10697) (Oscar Barrett)
diff --git a/karma.conf.js b/karma.conf.js
index 0ffab8bc5..29a8cdc8f 100644
--- a/karma.conf.js
+++ b/karma.conf.js
@@ -67,8 +67,13 @@ module.exports = function(config) {
 
         // start these browsers
         // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
-        browsers: ["PhantomJS"],
-
+        browsers: ["HeadlessChrome"],
+        customLaunchers: {
+          HeadlessChrome: {
+            base: 'ChromeHeadless',
+            flags: [ '--no-sandbox', ],
+             },
+            },
 
         // Continuous Integration mode
         // if true, Karma captures browsers, runs the tests and exits
diff --git a/lib/rules/one-var.js b/lib/rules/one-var.js
index 13ab72b04..15b664d77 100644
--- a/lib/rules/one-var.js
+++ b/lib/rules/one-var.js
@@ -384,8 +384,13 @@ module.exports = {
             if (nodeIndex > 0) {
                 const previousNode = parent.body[nodeIndex - 1];
                 const isPreviousNodeDeclaration = previousNode.type === "VariableDeclaration";
+                const declarationsWithPrevious = declarations.concat(previousNode.declarations || []);
 
-                if (isPreviousNodeDeclaration && previousNode.kind === type) {
+                if (
+                    isPreviousNodeDeclaration &&
+                    previousNode.kind === type &&
+                    !(declarationsWithPrevious.some(isRequire) && !declarationsWithPrevious.every(isRequire))
+                ) {
                     const previousDeclCounts = countDeclarations(previousNode.declarations);
 
                     if (options[type].initialized === MODE_CONSECUTIVE && options[type].uninitialized === MODE_CONSECUTIVE) {
diff --git a/lib/rules/require-atomic-updates.js b/lib/rules/require-atomic-updates.js
index 5d7d71514..c6cf0d747 100644
--- a/lib/rules/require-atomic-updates.js
+++ b/lib/rules/require-atomic-updates.js
@@ -119,6 +119,66 @@ module.exports = {
             });
         }
 
+        const alreadyReportedAssignments = new WeakSet();
+
+        class AssignmentTrackerState {
+            constructor({ openAssignmentsWithoutReads = new Set(), openAssignmentsWithReads = new Set() } = {}) {
+                this.openAssignmentsWithoutReads = openAssignmentsWithoutReads;
+                this.openAssignmentsWithReads = openAssignmentsWithReads;
+            }
+
+            copy() {
+                return new AssignmentTrackerState({
+                    openAssignmentsWithoutReads: new Set(this.openAssignmentsWithoutReads),
+                    openAssignmentsWithReads: new Set(this.openAssignmentsWithReads)
+                });
+            }
+
+            merge(other) {
+                const initialAssignmentsWithoutReadsCount = this.openAssignmentsWithoutReads.size;
+                const initialAssignmentsWithReadsCount = this.openAssignmentsWithReads.size;
+
+                other.openAssignmentsWithoutReads.forEach(assignment => this.openAssignmentsWithoutReads.add(assignment));
+                other.openAssignmentsWithReads.forEach(assignment => this.openAssignmentsWithReads.add(assignment));
+
+                return this.openAssignmentsWithoutReads.size > initialAssignmentsWithoutReadsCount ||
+                    this.openAssignmentsWithReads.size > initialAssignmentsWithReadsCount;
+            }
+
+            enterAssignment(assignmentExpression) {
+                (assignmentExpression.operator === "=" ? this.openAssignmentsWithoutReads : this.openAssignmentsWithReads).add(assignmentExpression);
+            }
+
+            exitAssignment(assignmentExpression) {
+                this.openAssignmentsWithoutReads.delete(assignmentExpression);
+                this.openAssignmentsWithReads.delete(assignmentExpression);
+            }
+
+            exitAwaitOrYield(node, surroundingFunction) {
+                return [...this.openAssignmentsWithReads]
+                    .filter(assignment => !isLocalVariableWithoutEscape(assignment.left, surroundingFunction))
+                    .forEach(assignment => {
+                        if (!alreadyReportedAssignments.has(assignment)) {
+                            reportAssignment(assignment);
+                            alreadyReportedAssignments.add(assignment);
+                        }
+                    });
+            }
+
+            exitIdentifierOrMemberExpression(node) {
+                [...this.openAssignmentsWithoutReads]
+                    .filter(assignment => (
+                        assignment.left !== node &&
+                        assignment.left.type === node.type &&
+                        astUtils.equalTokens(assignment.left, node, sourceCode)
+                    ))
+                    .forEach(assignment => {
+                        this.openAssignmentsWithoutReads.delete(assignment);
+                        this.openAssignmentsWithReads.add(assignment);
+                    });
+            }
+        }
+
         /**
          * If the control flow graph of a function enters an assignment expression, then does the
          * both of the following steps in order (possibly with other steps in between) before exiting the
@@ -135,54 +195,51 @@ module.exports = {
             codePathSegment,
             surroundingFunction,
             {
-                seenSegments = new Set(),
-                openAssignmentsWithoutReads = new Set(),
-                openAssignmentsWithReads = new Set()
+                stateBySegmentStart = new WeakMap(),
+                stateBySegmentEnd = new WeakMap()
             } = {}
         ) {
-            if (seenSegments.has(codePathSegment)) {
-
-                // An AssignmentExpression can't contain loops, so it's not necessary to reenter them with new state.
-                return;
+            if (!stateBySegmentStart.has(codePathSegment)) {
+                stateBySegmentStart.set(codePathSegment, new AssignmentTrackerState());
             }
 
+            const currentState = stateBySegmentStart.get(codePathSegment).copy();
+
             expressionsByCodePathSegment.get(codePathSegment).forEach(({ entering, node }) => {
                 if (node.type === "AssignmentExpression") {
                     if (entering) {
-                        (node.operator === "=" ? openAssignmentsWithoutReads : openAssignmentsWithReads).add(node);
+                        currentState.enterAssignment(node);
                     } else {
-                        openAssignmentsWithoutReads.delete(node);
-                        openAssignmentsWithReads.delete(node);
+                        currentState.exitAssignment(node);
                     }
                 } else if (!entering && (node.type === "AwaitExpression" || node.type === "YieldExpression")) {
-                    [...openAssignmentsWithReads]
-                        .filter(assignment => !isLocalVariableWithoutEscape(assignment.left, surroundingFunction))
-                        .forEach(reportAssignment);
-
-                    openAssignmentsWithReads.clear();
+                    currentState.exitAwaitOrYield(node, surroundingFunction);
                 } else if (!entering && (node.type === "Identifier" || node.type === "MemberExpression")) {
-                    [...openAssignmentsWithoutReads]
-                        .filter(assignment => (
-                            assignment.left !== node &&
-                            assignment.left.type === node.type &&
-                            astUtils.equalTokens(assignment.left, node, sourceCode)
-                        ))
-                        .forEach(assignment => {
-                            openAssignmentsWithoutReads.delete(assignment);
-                            openAssignmentsWithReads.add(assignment);
-                        });
+                    currentState.exitIdentifierOrMemberExpression(node);
                 }
             });
 
+            stateBySegmentEnd.set(codePathSegment, currentState);
+
             codePathSegment.nextSegments.forEach(nextSegment => {
+                if (stateBySegmentStart.has(nextSegment)) {
+                    if (!stateBySegmentStart.get(nextSegment).merge(currentState)) {
+
+                        /*
+                         * This segment has already been processed with the given set of inputs;
+                         * no need to do it again. After no new state is available to process
+                         * for any control flow segment in the graph, the analysis reaches a fixpoint and
+                         * traversal stops.
+                         */
+                        return;
+                    }
+                } else {
+                    stateBySegmentStart.set(nextSegment, currentState.copy());
+                }
                 findOutdatedReads(
                     nextSegment,
                     surroundingFunction,
-                    {
-                        seenSegments: new Set(seenSegments).add(codePathSegment),
-                        openAssignmentsWithoutReads: new Set(openAssignmentsWithoutReads),
-                        openAssignmentsWithReads: new Set(openAssignmentsWithReads)
-                    }
+                    { stateBySegmentStart, stateBySegmentEnd }
                 );
             });
         }
diff --git a/package.json b/package.json
index ebee08a93..1ecad57cb 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "eslint",
-  "version": "5.6.0",
+  "version": "5.6.1",
   "author": "Nicholas C. Zakas <nicholas+npm@nczconsulting.com>",
   "description": "An AST-based pattern checker for JavaScript.",
   "bin": {
@@ -100,14 +100,13 @@
     "karma-babel-preprocessor": "^7.0.0",
     "karma-mocha": "^1.3.0",
     "karma-mocha-reporter": "^2.2.3",
-    "karma-phantomjs-launcher": "^1.0.4",
+    "karma-chrome-launcher": "^2.2.0",
     "leche": "^2.2.3",
     "load-perf": "^0.2.0",
     "markdownlint": "^0.11.0",
     "mocha": "^5.0.5",
     "mock-fs": "^4.6.0",
     "npm-license": "^0.3.3",
-    "phantomjs-prebuilt": "^2.1.16",
     "proxyquire": "^2.0.1",
     "shelljs": "^0.8.2",
     "sinon": "^3.3.0",


Test patch:
diff --git a/tests/lib/rules/one-var.js b/tests/lib/rules/one-var.js
index 1f6626ad8..b628f512a 100644
--- a/tests/lib/rules/one-var.js
+++ b/tests/lib/rules/one-var.js
@@ -263,6 +263,13 @@ ruleTester.run("one-var", rule, {
             options: ["consecutive"],
             parserOptions: { ecmaVersion: 6 }
         },
+
+        // https://github.com/eslint/eslint/issues/10784
+        {
+            code: "const foo = require('foo'); const bar = 'bar';",
+            options: [{ const: "consecutive", separateRequires: true }],
+            parserOptions: { ecmaVersion: 6 }
+        },
         {
             code: "var a = 0, b = 1; var c, d;",
             options: [{ initialized: "consecutive", uninitialized: "always" }]
diff --git a/tests/lib/rules/require-atomic-updates.js b/tests/lib/rules/require-atomic-updates.js
index eabd52149..412835777 100644
--- a/tests/lib/rules/require-atomic-updates.js
+++ b/tests/lib/rules/require-atomic-updates.js
@@ -51,7 +51,64 @@ ruleTester.run("require-atomic-updates", rule, {
         "let foo; function* x() { foo = bar + foo; }",
         "async function x() { let foo; bar(() => baz += 1); foo += await amount; }",
         "let foo; async function x() { foo = condition ? foo : await bar; }",
-        "async function x() { let foo; bar(() => { let foo; blah(foo); }); foo += await result; }"
+        "async function x() { let foo; bar(() => { let foo; blah(foo); }); foo += await result; }",
+        "let foo; async function x() { foo = foo + 1; await bar; }",
+
+
+        /*
+         * Ensure rule doesn't take exponential time in the number of branches
+         * (see https://github.com/eslint/eslint/issues/10893)
+         */
+        `
+            async function foo() {
+                if (1);
+                if (2);
+                if (3);
+                if (4);
+                if (5);
+                if (6);
+                if (7);
+                if (8);
+                if (9);
+                if (10);
+                if (11);
+                if (12);
+                if (13);
+                if (14);
+                if (15);
+                if (16);
+                if (17);
+                if (18);
+                if (19);
+                if (20);
+            }
+        `,
+        `
+            async function foo() {
+                return [
+                    1 ? a : b,
+                    2 ? a : b,
+                    3 ? a : b,
+                    4 ? a : b,
+                    5 ? a : b,
+                    6 ? a : b,
+                    7 ? a : b,
+                    8 ? a : b,
+                    9 ? a : b,
+                    10 ? a : b,
+                    11 ? a : b,
+                    12 ? a : b,
+                    13 ? a : b,
+                    14 ? a : b,
+                    15 ? a : b,
+                    16 ? a : b,
+                    17 ? a : b,
+                    18 ? a : b,
+                    19 ? a : b,
+                    20 ? a : b
+                ];
+            }
+        `
     ],
 
     invalid: [
@@ -59,6 +116,10 @@ ruleTester.run("require-atomic-updates", rule, {
             code: "let foo; async function x() { foo += await amount; }",
             errors: [{ messageId: "nonAtomicUpdate", data: { value: "foo" } }]
         },
+        {
+            code: "if (1); let foo; async function x() { foo += await amount; }",
+            errors: [{ messageId: "nonAtomicUpdate", data: { value: "foo" } }]
+        },
         {
             code: "let foo; async function x() { while (condition) { foo += await amount; } }",
             errors: [VARIABLE_ERROR]
@@ -138,6 +199,14 @@ ruleTester.run("require-atomic-updates", rule, {
         {
             code: "async function x() { foo += await bar; }",
             errors: [VARIABLE_ERROR]
+        },
+        {
+            code: "let foo = 0; async function x() { foo = (a ? b : foo) + await bar; if (baz); }",
+            errors: [VARIABLE_ERROR]
+        },
+        {
+            code: "let foo = 0; async function x() { foo = (a ? b ? c ? d ? foo : e : f : g : h) + await bar; if (baz); }",
+            errors: [VARIABLE_ERROR]
         }
     ]
 });