Repository: eslint/eslint
Pull request #10262 (2018-04-25 00:12:59)

Build: add Node v10 to travis
<!--
    ESLint adheres to the [JS Foundation Code of Conduct](https://js.foundation/community/code-of-conduct).
-->

**What is the purpose of this pull request? (put an "X" next to item)**

[ ] 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
[X] 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)**
Add Node v10 to travis

**Is there anything you'd like reviewers to focus on?**
No



mysticatea: Thank you.

Looks to need to fix a test.

```diff
-     }, /Output is incorrect\. \(' foo = bar;' === 'invalid output'\)$/);
+     }, /Output is incorrect\. \(' foo = bar;' (?:===|strictEqual) 'invalid output'\)$/);
```

[tests/lib/testers/no-test-runners.js#L29](https://github.com/eslint/eslint/blob/b32d1f48cecc1eecd1352a978d7b393ad047337c/tests/lib/testers/no-test-runners.js#L29)
alberto: Yes, thanks for pointing it out.
mysticatea: Hmm, I'm not sure what the new test failings are.
I couldn't reproduce those failings in my PC...
alberto: Ugh, I used the wrong node version locally 🤦‍♂️ I'll have a look at it later.
not-an-aardvark: I think the test failures are fixed in https://github.com/eslint/eslint/pull/10182.
platinumazure: After merging #10182, it seems there are still some errors we might need to look at. So we decided to hold off on merging this for tonight's alpha release.
alberto: Ok, sorry it took so long. I've traced at least some of the errors down to [an issue](https://github.com/tschaub/mock-fs/issues/236) with `mockFs` and changes in `fs.statSync`.
tschaub: @alberto `mock-fs@4.5` has been published with Node 10 support.
alberto: Awesome! Thanks @tschaub
alberto: I'm sorry I had some problems getting the test to fail for the right reason and then got busy, so if anyone wants to take over this, feel free to do so.
Aladdin-ADD: Just pushed a commit to fix the message. 🚀

Patch:
diff --git a/.travis.yml b/.travis.yml
index 5763f04af..ff3e956a7 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -3,6 +3,7 @@ node_js:
     - "6"
     - "8"
     - "9"
+    - "10"
 sudo: false
 branches:
     only:
diff --git a/bin/eslint.js b/bin/eslint.js
index 1255e3d8b..adc07c2e7 100755
--- a/bin/eslint.js
+++ b/bin/eslint.js
@@ -56,7 +56,15 @@ process.once("uncaughtException", err => {
 });
 
 if (useStdIn) {
-    process.exitCode = cli.execute(process.argv, fs.readFileSync(process.stdin.fd, "utf8"));
+
+    /*
+     * Note: `process.stdin.fd` is not used here due to https://github.com/nodejs/node/issues/7439.
+     * Accessing the `process.stdin` property seems to modify the behavior of file descriptor 0, resulting
+     * in an error when stdin is piped in asynchronously.
+     */
+    const STDIN_FILE_DESCRIPTOR = 0;
+
+    process.exitCode = cli.execute(process.argv, fs.readFileSync(STDIN_FILE_DESCRIPTOR, "utf8"));
 } else if (init) {
     const configInit = require("../lib/config/config-initializer");
 
diff --git a/docs/rules/array-element-newline.md b/docs/rules/array-element-newline.md
index 21d957101..6a3a66e1d 100644
--- a/docs/rules/array-element-newline.md
+++ b/docs/rules/array-element-newline.md
@@ -12,6 +12,7 @@ This rule has either a string option:
 
 * `"always"` (default) requires line breaks between array elements
 * `"never"` disallows line breaks between array elements
+* `"consistent"` requires consistent usage of linebreaks between array elements
 
 Or an object option (Requires line breaks if any of properties is satisfied. Otherwise, disallows line breaks):
 
@@ -102,6 +103,69 @@ var e = [
 ];
 ```
 
+### consistent
+
+Examples of **incorrect** code for this rule with the `"consistent"` option:
+
+```js
+/*eslint array-element-newline: ["error", "consistent"]*/
+
+var a = [
+    1, 2,
+    3
+];
+var b = [
+    function foo() {
+        dosomething();
+    }, function bar() {
+        dosomething();
+    },
+    function baz() {
+        dosomething();
+    }
+];
+```
+
+Examples of **correct** code for this rule with the `"consistent"` option:
+
+```js
+/*eslint array-element-newline: ["error", "consistent"]*/
+
+var a = [];
+var b = [1];
+var c = [1, 2];
+var d = [1, 2, 3];
+var e = [
+    1,
+    2
+];
+var f = [
+    1,
+    2,
+    3
+];
+var g = [
+    function foo() {
+        dosomething();
+    }, function bar() {
+        dosomething();
+    }, function baz() {
+        dosomething();
+    }
+];
+var h = [
+    function foo() {
+        dosomething();
+    },
+    function bar() {
+        dosomething();
+    },
+    function baz() {
+        dosomething();
+    }
+];
+```
+
 ### multiline
 
 Examples of **incorrect** code for this rule with the `{ "multiline": true }` option:
diff --git a/lib/config/config-initializer.js b/lib/config/config-initializer.js
index fe42880b4..ab06909d8 100644
--- a/lib/config/config-initializer.js
+++ b/lib/config/config-initializer.js
@@ -504,7 +504,7 @@ function promptUser() {
                 earlyAnswers.styleguide = "airbnb-base";
             }
 
-            const config = getConfigForStyleGuide(earlyAnswers.styleguide, earlyAnswers.installESLint);
+            const config = getConfigForStyleGuide(earlyAnswers.styleguide);
             const modules = getModulesList(config);
 
             return askInstallModules(modules, earlyAnswers.packageJsonExists)
diff --git a/lib/rules/array-element-newline.js b/lib/rules/array-element-newline.js
index 359b8d436..b437c6932 100644
--- a/lib/rules/array-element-newline.js
+++ b/lib/rules/array-element-newline.js
@@ -24,7 +24,7 @@ module.exports = {
             {
                 oneOf: [
                     {
-                        enum: ["always", "never"]
+                        enum: ["always", "never", "consistent"]
                     },
                     {
                         type: "object",
@@ -63,6 +63,7 @@ module.exports = {
          * @returns {{multiline: boolean, minItems: number}} Normalized option object.
          */
         function normalizeOptionValue(providedOption) {
+            let consistent = false;
             let multiline = false;
             let minItems;
 
@@ -72,12 +73,15 @@ module.exports = {
                 minItems = 0;
             } else if (option === "never") {
                 minItems = Number.POSITIVE_INFINITY;
+            } else if (option === "consistent") {
+                consistent = true;
+                minItems = Number.POSITIVE_INFINITY;
             } else {
                 multiline = Boolean(option.multiline);
                 minItems = option.minItems || Number.POSITIVE_INFINITY;
             }
 
-            return { multiline, minItems };
+            return { consistent, multiline, minItems };
         }
 
         /**
@@ -193,11 +197,30 @@ module.exports = {
                     .some(element => element.loc.start.line !== element.loc.end.line);
             }
 
+            const linebreaksCount = node.elements.map((element, i) => {
+                const previousElement = elements[i - 1];
+
+                if (i === 0 || element === null || previousElement === null) {
+                    return false;
+                }
+
+                const commaToken = sourceCode.getFirstTokenBetween(previousElement, element, astUtils.isCommaToken);
+                const lastTokenOfPreviousElement = sourceCode.getTokenBefore(commaToken);
+                const firstTokenOfCurrentElement = sourceCode.getTokenAfter(commaToken);
+
+                return !astUtils.isTokenOnSameLine(lastTokenOfPreviousElement, firstTokenOfCurrentElement);
+            }).filter(isBreak => isBreak === true).length;
+
             const needsLinebreaks = (
                 elements.length >= options.minItems ||
                 (
                     options.multiline &&
                     elementBreak
+                ) ||
+                (
+                    options.consistent &&
+                    linebreaksCount > 0 &&
+                    linebreaksCount < node.elements.length
                 )
             );
 
diff --git a/lib/rules/one-var.js b/lib/rules/one-var.js
index 70223dd2c..412fe615d 100644
--- a/lib/rules/one-var.js
+++ b/lib/rules/one-var.js
@@ -293,7 +293,11 @@ module.exports = {
                 const res = [];
 
                 if (previousNode && previousNode.kind === sourceCode.getText(type)) {
-                    res.push(fixer.replaceText(prevSemi, ","));
+                    if (prevSemi.value === ";") {
+                        res.push(fixer.replaceText(prevSemi, ","));
+                    } else {
+                        res.push(fixer.insertTextAfter(prevSemi, ","));
+                    }
                     res.push(fixer.replaceText(type, ""));
                 }
 


Test patch:
diff --git a/tests/bin/eslint.js b/tests/bin/eslint.js
index 5fc818f28..75e7c2c4b 100644
--- a/tests/bin/eslint.js
+++ b/tests/bin/eslint.js
@@ -152,6 +152,17 @@ describe("bin/eslint.js", () => {
             }
         );
 
+        it("successfully reads from an asynchronous pipe", () => {
+            const child = runESLint(["--stdin", "--no-eslintrc"]);
+
+            child.stdin.write("var foo = bar;\n");
+            return new Promise(resolve => setTimeout(resolve, 300)).then(() => {
+                child.stdin.write("var baz = qux;\n");
+                child.stdin.end();
+
+                return assertExitCode(child, 0);
+            });
+        });
     });
 
     describe("running on files", () => {
diff --git a/tests/lib/rules/array-element-newline.js b/tests/lib/rules/array-element-newline.js
index f652105fc..c9ddd1605 100644
--- a/tests/lib/rules/array-element-newline.js
+++ b/tests/lib/rules/array-element-newline.js
@@ -53,7 +53,7 @@ ruleTester.run("array-element-newline", rule, {
         { code: "var foo = [// any comment \n1,\n2];", options: ["always"] },
         { code: "var foo = [1,\n2 // any comment\n];", options: ["always"] },
         { code: "var foo = [1,\n2,\n3];", options: ["always"] },
-        { code: "var foo = [\nfunction foo() {\ndosomething();\n},\nfunction bar() {\nosomething();\n}\n];", options: ["always"] },
+        { code: "var foo = [\nfunction foo() {\ndosomething();\n},\nfunction bar() {\ndosomething();\n}\n];", options: ["always"] },
 
         // "never"
         { code: "var foo = [];", options: ["never"] },
@@ -66,6 +66,23 @@ ruleTester.run("array-element-newline", rule, {
         { code: "var foo = [1, (\n2\n), 3];", options: ["never"] },
         { code: "var foo = [\nfunction foo() {\ndosomething();\n}, function bar() {\ndosomething();\n}\n];", options: ["never"] },
 
+        // "consistent"
+        { code: "var foo = [];", options: ["consistent"] },
+        { code: "var foo = [1];", options: ["consistent"] },
+        { code: "var foo = [1, 2];", options: ["consistent"] },
+        { code: "var foo = [1,\n2];", options: ["consistent"] },
+        { code: "var foo = [1, 2, 3];", options: ["consistent"] },
+        { code: "var foo = [1,\n2,\n3];", options: ["consistent"] },
+        { code: "var foo = [1,\n2,\n,\n3];", options: ["consistent"] },
+        { code: "var foo = [1, // any comment\n2];", options: ["consistent"] },
+        { code: "var foo = [/* any comment */ 1, 2];", options: ["consistent"] },
+        { code: "var foo = [1, (\n2\n), 3];", options: ["consistent"] },
+        { code: "var foo = [1,\n(2)\n, 3];", options: ["consistent"] },
+        { code: "var foo = [\nfunction foo() {\ndosomething();\n},\nfunction bar() {\ndosomething();\n}\n];", options: ["consistent"] },
+        { code: "var foo = [\nfunction foo() {\ndosomething();\n}, function bar() {\ndosomething();\n}\n];", options: ["consistent"] },
+        { code: "var foo = [\nfunction foo() {\ndosomething();\n},\nfunction bar() {\ndosomething();\n},\nfunction bar() {\ndosomething();\n}];", options: ["consistent"] },
+        { code: "var foo = [\nfunction foo() {\ndosomething();\n}, function bar() {\ndosomething();\n}, function bar() {\ndosomething();\n}];", options: ["consistent"] },
+
         // { multiline: true }
         { code: "var foo = [];", options: [{ multiline: true }] },
         { code: "var foo = [1];", options: [{ multiline: true }] },
@@ -450,6 +467,88 @@ ruleTester.run("array-element-newline", rule, {
             ]
         },
 
+        // "consistent"
+        {
+            code: "var foo = [1,\n2, 3];",
+            output: "var foo = [1,\n2,\n3];",
+            options: ["consistent"],
+            errors: [
+                {
+                    messageId: "missingLineBreak",
+                    line: 2,
+                    column: 3,
+                    endLine: 2,
+                    endColumn: 4
+                }
+            ]
+        },
+        {
+            code: "var foo = [1, 2,\n3];",
+            output: "var foo = [1,\n2,\n3];",
+            options: ["consistent"],
+            errors: [
+                {
+                    messageId: "missingLineBreak",
+                    line: 1,
+                    column: 14,
+                    endLine: 1,
+                    endColumn: 15
+                }
+            ]
+        },
+        {
+            code: "var foo = [1,\n(\n2), 3];",
+            output: "var foo = [1,\n(\n2),\n3];",
+            options: ["consistent"],
+            errors: [
+                {
+                    messageId: "missingLineBreak",
+                    line: 3,
+                    column: 4,
+                    endLine: 3,
+                    endColumn: 5
+                }
+            ]
+        },
+        {
+            code: "var foo = [1,        \t      (\n2\n),\n3];",
+            output: "var foo = [1,\n(\n2\n),\n3];",
+            options: ["consistent"],
+            errors: [
+                {
+                    messageId: "missingLineBreak",
+                    line: 1,
+                    column: 14,
+                    endLine: 1,
+                    endColumn: 29
+                }
+            ]
+        },
+        {
+            code: "var foo = [1, /* any comment */(2),\n3];",
+            output: "var foo = [1, /* any comment */\n(2),\n3];",
+            options: ["consistent"],
+            errors: [
+                {
+                    messageId: "missingLineBreak",
+                    line: 1,
+                    column: 32
+                }
+            ]
+        },
+        {
+            code: "var foo = [\nfunction foo() {\ndosomething();\n},function bar() {\ndosomething();\n},\nfunction bar() {\ndosomething();\n}];",
+            output: "var foo = [\nfunction foo() {\ndosomething();\n},\nfunction bar() {\ndosomething();\n},\nfunction bar() {\ndosomething();\n}];",
+            options: ["consistent"],
+            errors: [
+                {
+                    messageId: "missingLineBreak",
+                    line: 4,
+                    column: 3
+                }
+            ]
+        },
+
         // { multiline: true }
         {
             code: "var foo = [1,\n2, 3];",
diff --git a/tests/lib/rules/one-var.js b/tests/lib/rules/one-var.js
index 50a2ecf1d..1f6626ad8 100644
--- a/tests/lib/rules/one-var.js
+++ b/tests/lib/rules/one-var.js
@@ -1701,6 +1701,17 @@ ruleTester.run("one-var", rule, {
                 line: 1,
                 column: 41
             }]
+        },
+        {
+            code: "var a\nvar b",
+            output: "var a,\n b",
+            options: ["always"],
+            errors: [{
+                message: "Combine this with the previous 'var' statement.",
+                type: "VariableDeclaration",
+                line: 2,
+                column: 1
+            }]
         }
     ]
 });
diff --git a/tests/lib/testers/no-test-runners.js b/tests/lib/testers/no-test-runners.js
index 34d8a2a09..a14ac3bab 100644
--- a/tests/lib/testers/no-test-runners.js
+++ b/tests/lib/testers/no-test-runners.js
@@ -6,8 +6,8 @@
 
 /* global describe, it */
 /* eslint-disable no-global-assign*/
-const assert = require("assert"),
-    RuleTester = require("../../../lib/testers/rule-tester");
+const assert = require("assert");
+const RuleTester = require("../../../lib/testers/rule-tester");
 const tmpIt = it;
 const tmpDescribe = describe;
 
@@ -26,7 +26,7 @@ try {
                 { code: "var foo = bar;", output: "invalid output", errors: 1 }
             ]
         });
-    }, /Output is incorrect\. \(' foo = bar;' === 'invalid output'\)$/);
+    }, new assert.AssertionError({ actual: " foo = bar;", expected: "invalid output", operator: "===" }).message);
 } finally {
     it = tmpIt;
     describe = tmpDescribe;