Repository: eslint/eslint
Pull request #8632 (2017-05-21 20:35:10)

Docs: suggest pushing new commits to a PR instead of amending
**What is the purpose of this pull request? (put an "X" next to item)**

[x] Documentation update

<!--
    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 (http://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)**

This updates the contribution guidelines to suggest adding new commits to a PR when changes are requested, rather than amending the original commit.

As a reviewer, adding additional commits makes it easier to tell what has changed. For example, if I suggest a very small change on a large PR, and a commit is added to address the change, I can easily look at the diff for that commit and make sure it looks okay. On the other hand, if the original commit is amended, it is harder to tell what was changed, so I might have to look through the entire PR again. 

I think the original guideline here was added before GitHub had the "squash and merge" feature, so we needed all PRs to contain a single commit.

**Is there anything you'd like reviewers to focus on?**

Do you agree with this suggestion?

mention-bot: @not-an-aardvark, thanks for your PR! By analyzing the history of the files in this pull request, we identified @nzakas to be a potential reviewer.
eslintbot: LGTM

Patch:
diff --git a/README.md b/README.md
index cdf5cabd9..7fb4fa343 100644
--- a/README.md
+++ b/README.md
@@ -124,7 +124,7 @@ These folks keep the project moving and are resources for help.
 * Michael Ficarra ([@michaelficarra](https://github.com/michaelficarra))
 * Mark Pedrotti ([@pedrottimark](https://github.com/pedrottimark))
 * Oleg Gaidarenko ([@markelog](https://github.com/markelog))
-* Mike Sherov [@mikesherov](https://github.com/mikesherov))
+* Mike Sherov ([@mikesherov](https://github.com/mikesherov))
 * Henry Zhu ([@hzoo](https://github.com/hzoo))
 * Marat Dulin ([@mdevils](https://github.com/mdevils))
 * Alexej Yaroshevich ([@zxqfox](https://github.com/zxqfox))
diff --git a/conf/eslint-recommended.js b/conf/eslint-recommended.js
index 62ce4760e..0d8d19e44 100755
--- a/conf/eslint-recommended.js
+++ b/conf/eslint-recommended.js
@@ -17,6 +17,7 @@ module.exports = {
         "array-bracket-newline": "off",
         "array-bracket-spacing": "off",
         "array-callback-return": "off",
+        "array-element-newline": "off",
         "arrow-body-style": "off",
         "arrow-parens": "off",
         "arrow-spacing": "off",
diff --git a/docs/developer-guide/contributing/pull-requests.md b/docs/developer-guide/contributing/pull-requests.md
index ff6c864d9..3fb32146d 100644
--- a/docs/developer-guide/contributing/pull-requests.md
+++ b/docs/developer-guide/contributing/pull-requests.md
@@ -6,7 +6,7 @@ If you want to contribute to an ESLint repo, please use a GitHub pull request. T
 
 If you'd like to work on a pull request and you've never submitted code before, follow these steps:
 
-1. Sign our [Contributor License Agreement](https://contribute.jquery.org/cla).
+1. Sign our [Contributor License Agreement](https://cla.js.foundation/eslint/eslint).
 1. Set up a [development environment](../development-environment).
 1. If you want to implement a breaking change or a change to the core, ensure there's an issue that describes what you're doing and the issue has been accepted. You can create a new issue or just indicate you're [working on an existing issue](working-on-issues). Bug fixes, documentation changes, and other pull requests do not require an issue.
 
@@ -160,11 +160,11 @@ If we ask you to make code changes, there's no need to close the pull request an
 
 ```
 $ git add -A
-$ git commit --amend --no-edit
-$ git push origin issue1234 -f
+$ git commit
+$ git push origin issue1234
 ```
 
-This snippets adds all your new changes, then amends the previous commit with them. The `--no-edit` means you don't want to edit the commit message; you can omit that option if you need to make commit message changes as well.
+When updating the code, it's usually better to add additional commits to your branch rather than amending the original commit, because reviewers can easily tell which changes were made in response to a particular review. When we merge pull requests, we will squash all the commits from your branch into a single commit on the `master` branch.
 
 ### Rebasing
 
diff --git a/docs/rules/array-element-newline.md b/docs/rules/array-element-newline.md
new file mode 100644
index 000000000..21d957101
--- /dev/null
+++ b/docs/rules/array-element-newline.md
@@ -0,0 +1,240 @@
+# enforce line breaks between array elements (array-element-newline)
+
+A number of style guides require or disallow line breaks between array elements.
+
+## Rule Details
+
+This rule enforces line breaks between array elements.
+
+## Options
+
+This rule has either a string option:
+
+* `"always"` (default) requires line breaks between array elements
+* `"never"` disallows line breaks between array elements
+
+Or an object option (Requires line breaks if any of properties is satisfied. Otherwise, disallows line breaks):
+
+* `"multiline": <boolean>` requires line breaks if there are line breaks inside elements. If this is false, this condition is disabled.
+* `"minItems": <number>` requires line breaks if the number of elements is at least the given integer. If this is 0, this condition will act the same as the option `"always"`. If this is `null` (the default), this condition is disabled.
+
+### always
+
+Examples of **incorrect** code for this rule with the default `"always"` option:
+
+```js
+/*eslint array-element-newline: ["error", "always"]*/
+
+var c = [1, 2];
+var d = [1, 2, 3];
+var e = [
+    function foo() {
+        dosomething();
+    }, function bar() {
+        dosomething();
+    }
+];
+```
+
+Examples of **correct** code for this rule with the default `"always"` option:
+
+```js
+/*eslint array-element-newline: ["error", "always"]*/
+
+var a = [];
+var b = [1];
+var c = [1,
+    2];
+var d = [1,
+    2,
+    3];
+var e = [
+    function foo() {
+        dosomething();
+    },
+    function bar() {
+        dosomething();
+    }
+];
+```
+
+### never
+
+Examples of **incorrect** code for this rule with the default `"never"` option:
+
+```js
+/*eslint array-element-newline: ["error", "never"]*/
+
+var c = [
+    1,
+    2
+];
+var d = [
+    1,
+    2,
+    3
+];
+var e = [
+    function foo() {
+        dosomething();
+    },
+    function bar() {
+        dosomething();
+    }
+];
+```
+
+Examples of **correct** code for this rule with the `"never"` option:
+
+```js
+/*eslint array-element-newline: ["error", "never"]*/
+
+var a = [];
+var b = [1];
+var c = [1, 2];
+var d = [1, 2, 3];
+var e = [
+    function foo() {
+        dosomething();
+    }, function bar() {
+        dosomething();
+    }
+];
+```
+
+### multiline
+
+Examples of **incorrect** code for this rule with the `{ "multiline": true }` option:
+
+```js
+/*eslint array-element-newline: ["error", { "multiline": true }]*/
+
+var d = [1,
+    2, 3];
+var e = [
+    function foo() {
+        dosomething();
+    }, function bar() {
+        dosomething();
+    }
+];
+```
+
+Examples of **correct** code for this rule with the `{ "multiline": true }` option:
+
+```js
+/*eslint array-element-newline: ["error", { "multiline": true }]*/
+
+var a = [];
+var b = [1];
+var c = [1, 2];
+var d = [1, 2, 3];
+var e = [
+    function foo() {
+        dosomething();
+    },
+    function bar() {
+        dosomething();
+    }
+];
+```
+
+### minItems
+
+Examples of **incorrect** code for this rule with the `{ "minItems": 3 }` option:
+
+```js
+/*eslint array-element-newline: ["error", { "minItems": 3 }]*/
+
+var c = [1,
+    2];
+var d = [1, 2, 3];
+var e = [
+    function foo() {
+        dosomething();
+    },
+    function bar() {
+        dosomething();
+    }
+];
+```
+
+Examples of **correct** code for this rule with the `{ "minItems": 3 }` option:
+
+```js
+/*eslint array-element-newline: ["error", { "minItems": 3 }]*/
+
+var a = [];
+var b = [1];
+var c = [1, 2];
+var d = [1,
+    2,
+    3];
+var e = [
+    function foo() {
+        dosomething();
+    }, function bar() {
+        dosomething();
+    }
+];
+```
+
+### multiline and minItems
+
+Examples of **incorrect** code for this rule with the `{ "multiline": true, "minItems": 3 }` options:
+
+```js
+/*eslint array-element-newline: ["error", { "multiline": true, "minItems": 3 }]*/
+
+var c = [1,
+2];
+var d = [1, 2, 3];
+var e = [
+    function foo() {
+        dosomething();
+    }, function bar() {
+        dosomething();
+    }
+];
+```
+
+Examples of **correct** code for this rule with the `{ "multiline": true, "minItems": 3 }` options:
+
+```js
+/*eslint array-element-newline: ["error", { "multiline": true, "minItems": 3 }]*/
+
+var a = [];
+var b = [1];
+var c = [1, 2];
+var d = [1,
+    2,
+    3];
+var e = [
+    function foo() {
+        dosomething();
+    },
+    function bar() {
+        dosomething();
+    }
+];
+```
+
+
+## When Not To Use It
+
+If you don't want to enforce linebreaks between array elements, don't enable this rule.
+
+## Compatibility
+
+* **JSCS:** [validateNewlineAfterArrayElements](http://jscs.info/rule/validateNewlineAfterArrayElements)
+
+## Related Rules
+
+* [array-bracket-spacing](array-bracket-spacing.md)
+* [array-bracket-newline](array-bracket-newline.md)
+* [object-property-newline](object-property-newline.md)
+* [object-curly-spacing](object-curly-spacing.md)
+* [object-curly-newline](object-curly-newline.md)
+* [max-statements-per-line](max-statements-per-line.md)
+* [block-spacing](block-spacing.md)
+* [brace-style](brace-style.md)
diff --git a/lib/config/config-ops.js b/lib/config/config-ops.js
index 79a2f7f41..e1d9a9013 100644
--- a/lib/config/config-ops.js
+++ b/lib/config/config-ops.js
@@ -175,7 +175,7 @@ module.exports = {
             }
             Object.keys(src).forEach(key => {
                 if (Array.isArray(src[key]) || Array.isArray(target[key])) {
-                    dst[key] = deepmerge(target[key], src[key], key === "plugins", isRule);
+                    dst[key] = deepmerge(target[key], src[key], key === "plugins" || key === "extends", isRule);
                 } else if (typeof src[key] !== "object" || !src[key] || key === "exported" || key === "astGlobals") {
                     dst[key] = src[key];
                 } else {
diff --git a/lib/rules/array-element-newline.js b/lib/rules/array-element-newline.js
new file mode 100644
index 000000000..8cccae082
--- /dev/null
+++ b/lib/rules/array-element-newline.js
@@ -0,0 +1,230 @@
+/**
+ * @fileoverview Rule to enforce line breaks after each array element
+ * @author Jan Peer Stöcklmair <https://github.com/JPeer264>
+ */
+
+"use strict";
+
+const astUtils = require("../ast-utils");
+
+//------------------------------------------------------------------------------
+// Rule Definition
+//------------------------------------------------------------------------------
+
+module.exports = {
+    meta: {
+        docs: {
+            description: "enforce line breaks after each array element",
+            category: "Stylistic Issues",
+            recommended: false
+        },
+        fixable: "whitespace",
+        schema: [
+            {
+                oneOf: [
+                    {
+                        enum: ["always", "never"]
+                    },
+                    {
+                        type: "object",
+                        properties: {
+                            multiline: {
+                                type: "boolean"
+                            },
+                            minItems: {
+                                type: ["integer", "null"],
+                                minimum: 0
+                            }
+                        },
+                        additionalProperties: false
+                    }
+                ]
+            }
+        ]
+    },
+
+    create(context) {
+        const sourceCode = context.getSourceCode();
+
+        //----------------------------------------------------------------------
+        // Helpers
+        //----------------------------------------------------------------------
+
+        /**
+         * Normalizes a given option value.
+         *
+         * @param {string|Object|undefined} option - An option value to parse.
+         * @returns {{multiline: boolean, minItems: number}} Normalized option object.
+         */
+        function normalizeOptionValue(option) {
+            let multiline = false;
+            let minItems;
+
+            option = option || "always";
+
+            if (option === "always" || option.minItems === 0) {
+                minItems = 0;
+            } else if (option === "never") {
+                minItems = Number.POSITIVE_INFINITY;
+            } else {
+                multiline = Boolean(option.multiline);
+                minItems = option.minItems || Number.POSITIVE_INFINITY;
+            }
+
+            return { multiline, minItems };
+        }
+
+        /**
+         * Normalizes a given option value.
+         *
+         * @param {string|Object|undefined} options - An option value to parse.
+         * @returns {{ArrayExpression: {multiline: boolean, minItems: number}, ArrayPattern: {multiline: boolean, minItems: number}}} Normalized option object.
+         */
+        function normalizeOptions(options) {
+            const value = normalizeOptionValue(options);
+
+            return { ArrayExpression: value, ArrayPattern: value };
+        }
+
+        /**
+        * Reports that there shouldn't be a line break after the first token
+        * @param {Token} token - The token to use for the report.
+        * @returns {void}
+        */
+        function reportNoLineBreak(token) {
+            const tokenBefore = sourceCode.getTokenBefore(token, { includeComments: true });
+
+            context.report({
+                loc: {
+                    start: tokenBefore.loc.end,
+                    end: token.loc.start
+                },
+                message: "There should be no linebreak here.",
+                fix(fixer) {
+                    if (astUtils.isCommentToken(tokenBefore)) {
+                        return null;
+                    }
+
+                    if (!astUtils.isTokenOnSameLine(tokenBefore, token)) {
+                        return fixer.replaceTextRange([tokenBefore.range[1], token.range[0]], " ");
+                    }
+
+                    /*
+                     * This will check if the comma is on the same line as the next element
+                     * Following array:
+                     * [
+                     *     1
+                     *     , 2
+                     *     , 3
+                     * ]
+                     *
+                     * will be fixed to:
+                     * [
+                     *     1, 2, 3
+                     * ]
+                     */
+                    const twoTokensBefore = sourceCode.getTokenBefore(tokenBefore, { includeComments: true });
+
+                    if (astUtils.isCommentToken(twoTokensBefore)) {
+                        return null;
+                    }
+
+                    return fixer.replaceTextRange([twoTokensBefore.range[1], tokenBefore.range[0]], "");
+
+                }
+            });
+        }
+
+        /**
+        * Reports that there should be a line break after the first token
+        * @param {Token} token - The token to use for the report.
+        * @returns {void}
+        */
+        function reportRequiredLineBreak(token) {
+            const tokenBefore = sourceCode.getTokenBefore(token, { includeComments: true });
+
+            context.report({
+                loc: {
+                    start: tokenBefore.loc.end,
+                    end: token.loc.start
+                },
+                message: "There should be a linebreak after this element.",
+                fix(fixer) {
+                    return fixer.replaceTextRange([tokenBefore.range[1], token.range[0]], "\n");
+                }
+            });
+        }
+
+        /**
+         * Reports a given node if it violated this rule.
+         *
+         * @param {ASTNode} node - A node to check. This is an ObjectExpression node or an ObjectPattern node.
+         * @param {{multiline: boolean, minItems: number}} options - An option object.
+         * @returns {void}
+         */
+        function check(node) {
+            const elements = node.elements;
+            const normalizedOptions = normalizeOptions(context.options[0]);
+            const options = normalizedOptions[node.type];
+
+            let elementBreak = false;
+
+            /*
+             * MULTILINE: true
+             * loop through every element and check
+             * if at least one element has linebreaks inside
+             * this ensures that following is not valid (due to elements are on the same line):
+             *
+             * [
+             *      1,
+             *      2,
+             *      3
+             * ]
+             */
+            if (options.multiline) {
+                elementBreak = elements
+                    .filter(element => element !== null)
+                    .some(element => element.loc.start.line !== element.loc.end.line);
+            }
+
+            const needsLinebreaks = (
+                elements.length >= options.minItems ||
+                (
+                    options.multiline &&
+                    elementBreak
+                )
+            );
+
+            elements.forEach((element, i) => {
+                const previousElement = elements[i - 1];
+
+                if (i === 0 || element === null || previousElement === null) {
+                    return;
+                }
+
+                const commaToken = sourceCode.getFirstTokenBetween(previousElement, element, astUtils.isCommaToken);
+                const lastTokenOfPreviousElement = sourceCode.getTokenBefore(commaToken);
+                const firstTokenOfCurrentElement = sourceCode.getTokenAfter(commaToken);
+
+                if (needsLinebreaks) {
+                    if (astUtils.isTokenOnSameLine(lastTokenOfPreviousElement, firstTokenOfCurrentElement)) {
+                        reportRequiredLineBreak(firstTokenOfCurrentElement);
+                    }
+                } else {
+                    if (!astUtils.isTokenOnSameLine(lastTokenOfPreviousElement, firstTokenOfCurrentElement)) {
+                        reportNoLineBreak(firstTokenOfCurrentElement);
+                    }
+                }
+            });
+        }
+
+        //----------------------------------------------------------------------
+        // Public
+        //----------------------------------------------------------------------
+
+        return {
+            ArrayPattern: check,
+            ArrayExpression: check
+        };
+    }
+};


Test patch:
diff --git a/tests/lib/config/config-ops.js b/tests/lib/config/config-ops.js
index 46ee8c8ea..f0c72c488 100644
--- a/tests/lib/config/config-ops.js
+++ b/tests/lib/config/config-ops.js
@@ -316,6 +316,18 @@ describe("ConfigOps", () => {
             assert.deepEqual(config[1], { rules: { "no-mixed-requires": "error" } });
         });
 
+        it("should combine extends correctly", () => {
+
+            const config = [
+                { extends: ["a", "b", "c", "d", "e"] },
+                { extends: ["f", "g", "h", "i"] }
+            ];
+
+            const result = ConfigOps.merge(config[0], config[1]);
+
+            assert.sameDeepMembers(result.extends, ["a", "b", "c", "d", "e", "f", "g", "h", "i"]);
+        });
+
         it("should combine configs correctly", () => {
 
             const config = [
diff --git a/tests/lib/rules/array-element-newline.js b/tests/lib/rules/array-element-newline.js
new file mode 100644
index 000000000..d1c8b7c77
--- /dev/null
+++ b/tests/lib/rules/array-element-newline.js
@@ -0,0 +1,745 @@
+/**
+ * @fileoverview Tests for array-element-newline rule.
+ * @author Jan Peer Stöcklmair <https://github.com/JPeer264>
+ */
+
+"use strict";
+
+//------------------------------------------------------------------------------
+// Requirements
+//------------------------------------------------------------------------------
+
+const rule = require("../../../lib/rules/array-element-newline");
+const RuleTester = require("../../../lib/testers/rule-tester");
+
+
+//------------------------------------------------------------------------------
+// Tests
+//------------------------------------------------------------------------------
+
+const ruleTester = new RuleTester();
+
+const ERR_NO_BREAK_HERE = "There should be no linebreak here.";
+const ERR_BREAK_HERE = "There should be a linebreak after this element.";
+
+ruleTester.run("array-element-newline", rule, {
+
+    valid: [
+
+        // ArrayExpression
+        // "always"
+        "var foo = [];",
+        "var foo = [1];",
+        "var foo = [1,\n2];",
+        "var foo = [1, // any comment\n2];",
+        "var foo = [// any comment \n1,\n2];",
+        "var foo = [1,\n2 // any comment\n];",
+        "var foo = [1,\n2,\n3];",
+        "var foo = [1\n, (2\n, 3)];",
+        "var foo = [1,\n(  2   ),\n3];",
+        "var foo = [1,\n((((2)))),\n3];",
+        "var foo = [1,\n(\n2\n),\n3];",
+        "var foo = [1,\n(2),\n3];",
+        "var foo = [1,\n(2)\n, 3];",
+        "var foo = [1\n, 2\n, 3];",
+        "var foo = [1,\n2,\n,\n3];",
+        "var foo = [\nfunction foo() {\ndosomething();\n},\nfunction bar() {\nosomething();\n}\n];",
+
+        { code: "var foo = [];", options: ["always"] },
+        { code: "var foo = [1];", options: ["always"] },
+        { code: "var foo = [1,\n2];", options: ["always"] },
+        { code: "var foo = [1,\n(2)];", options: ["always"] },
+        { code: "var foo = [1\n, (2)];", options: ["always"] },
+        { code: "var foo = [1, // any comment\n2];", options: ["always"] },
+        { 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"] },
+
+        // "never"
+        { code: "var foo = [];", options: ["never"] },
+        { code: "var foo = [1];", options: ["never"] },
+        { code: "var foo = [1, 2];", options: ["never"] },
+        { code: "var foo = [1, /* any comment */ 2];", options: ["never"] },
+        { code: "var foo = [/* any comment */ 1, 2];", options: ["never"] },
+        { code: "var foo = /* any comment */ [1, 2];", options: ["never"] },
+        { code: "var foo = [1, 2, 3];", options: ["never"] },
+        { code: "var foo = [1, (\n2\n), 3];", options: ["never"] },
+        { code: "var foo = [\nfunction foo() {\ndosomething();\n}, function bar() {\ndosomething();\n}\n];", options: ["never"] },
+
+        // { multiline: true }
+        { code: "var foo = [];", options: [{ multiline: true }] },
+        { code: "var foo = [1];", options: [{ multiline: true }] },
+        { code: "var foo = [1, 2];", options: [{ multiline: true }] },
+        { code: "var foo = [1, 2, 3];", options: [{ multiline: true }] },
+        { code: "var f = [\nfunction foo() {\ndosomething();\n},\nfunction bar() {\ndosomething();\n}\n];", options: [{ multiline: true }] },
+
+        // { minItems: null }
+        { code: "var foo = [];", options: [{ minItems: null }] },
+        { code: "var foo = [1];", options: [{ minItems: null }] },
+        { code: "var foo = [1, 2];", options: [{ minItems: null }] },
+        { code: "var foo = [1, 2, 3];", options: [{ minItems: null }] },
+        { code: "var foo = [\nfunction foo() {\ndosomething();\n}, function bar() {\ndosomething();\n}\n];", options: [{ minItems: null }] },
+
+        // { minItems: 0 }
+        { code: "var foo = [];", options: [{ minItems: 0 }] },
+        { code: "var foo = [1];", options: [{ minItems: 0 }] },
+        { code: "var foo = [1,\n2];", options: [{ minItems: 0 }] },
+        { code: "var foo = [1,\n2,\n3];", options: [{ minItems: 0 }] },
+        { code: "var foo = [\nfunction foo() {\ndosomething();\n},\nfunction bar() {\ndosomething();\n}\n];", options: [{ minItems: 0 }] },
+
+        // { minItems: 3 }
+        { code: "var foo = [];", options: [{ minItems: 3 }] },
+        { code: "var foo = [1];", options: [{ minItems: 3 }] },
+        { code: "var foo = [1, 2];", options: [{ minItems: 3 }] },
+        { code: "var foo = [1,\n2,\n3];", options: [{ minItems: 3 }] },
+        { code: "var foo = [\nfunction foo() {\ndosomething();\n}, function bar() {\ndosomething();\n}\n];", options: [{ minItems: 3 }] },
+
+        // { multiline: true, minItems: 3 }
+        { code: "var foo = [];", options: [{ multiline: true, minItems: 3 }] },
+        { code: "var foo = [1];", options: [{ multiline: true, minItems: 3 }] },
+        { code: "var foo = [1, 2];", options: [{ multiline: true, minItems: 3 }] },
+        { code: "var foo = [1, // any comment\n2,\n, 3];", options: [{ multiline: true, minItems: 3 }] },
+        { code: "var foo = [1,\n2,\n// any comment\n, 3];", options: [{ multiline: true, minItems: 3 }] },
+        { code: "var foo = [\nfunction foo() {\ndosomething();\n},\nfunction bar() {\ndosomething();\n}\n];", options: [{ multiline: true, minItems: 3 }] },
+
+        // ArrayPattern
+        // "always"
+        { code: "var [] = foo;", parserOptions: { ecmaVersion: 6 } },
+        { code: "var [a] = foo;", parserOptions: { ecmaVersion: 6 } },
+        { code: "var [a,\nb] = foo;", parserOptions: { ecmaVersion: 6 } },
+        { code: "var [a, // any comment\nb] = foo;", parserOptions: { ecmaVersion: 6 } },
+        { code: "var [// any comment \na,\nb] = foo;", parserOptions: { ecmaVersion: 6 } },
+        { code: "var [a,\nb // any comment\n] = foo;", parserOptions: { ecmaVersion: 6 } },
+        { code: "var [a,\nb,\nb] = foo;", parserOptions: { ecmaVersion: 6 } },
+
+        // { minItems: 3 }
+        { code: "var [] = foo;", options: [{ minItems: 3 }], parserOptions: { ecmaVersion: 6 } },
+        { code: "var [a] = foo;", options: [{ minItems: 3 }], parserOptions: { ecmaVersion: 6 } },
+        { code: "var [a, b] = foo;", options: [{ minItems: 3 }], parserOptions: { ecmaVersion: 6 } },
+        { code: "var [a,\nb,\nc] = foo;", options: [{ minItems: 3 }], parserOptions: { ecmaVersion: 6 } }
+
+    ],
+
+    invalid: [
+
+        // ArrayExpression
+        // "always"
+        {
+            code: "var foo = [1, 2];",
+            options: ["always"],
+            output: "var foo = [1,\n2];",
+            errors: [
+                {
+                    message: ERR_BREAK_HERE,
+                    line: 1,
+                    column: 14,
+                    endLine: 1,
+                    endColumn: 15
+                }
+            ]
+        },
+        {
+            code: "var foo = [1, 2, 3];",
+            options: ["always"],
+            output: "var foo = [1,\n2,\n3];",
+            errors: [
+                {
+                    message: ERR_BREAK_HERE,
+                    line: 1,
+                    column: 14,
+                    endLine: 1,
+                    endColumn: 15
+                },
+                {
+                    message: ERR_BREAK_HERE,
+                    line: 1,
+                    column: 17,
+                    endLine: 1,
+                    endColumn: 18
+                }
+            ]
+        },
+        {
+            code: "var foo = [1,2, 3];",
+            options: ["always"],
+            output: "var foo = [1,\n2,\n3];",
+            errors: [
+                {
+                    message: ERR_BREAK_HERE,
+                    line: 1,
+                    column: 14,
+                    endLine: 1,
+                    endColumn: 14
+                },
+                {
+                    message: ERR_BREAK_HERE,
+                    line: 1,
+                    column: 16,
+                    endLine: 1,
+                    endColumn: 17
+                }
+            ]
+        },
+        {
+            code: "var foo = [1, (2), 3];",
+            options: ["always"],
+            output: "var foo = [1,\n(2),\n3];",
+            errors: [
+                {
+                    message: ERR_BREAK_HERE,
+                    line: 1,
+                    column: 14,
+                    endLine: 1,
+                    endColumn: 15
+                },
+                {
+                    message: ERR_BREAK_HERE,
+                    line: 1,
+                    column: 19,
+                    endLine: 1,
+                    endColumn: 20
+                }
+            ]
+        },
+        {
+            code: "var foo = [1,(\n2\n), 3];",
+            options: ["always"],
+            output: "var foo = [1,\n(\n2\n),\n3];",
+            errors: [
+                {
+                    message: ERR_BREAK_HERE,
+                    line: 1,
+                    column: 14
+                },
+                {
+                    message: ERR_BREAK_HERE,
+                    line: 3,
+                    column: 3
+                }
+            ]
+        },
+        {
+            code: "var foo = [1,(\n2\n), 3];",
+            options: ["always"],
+            output: "var foo = [1,\n(\n2\n),\n3];",
+            errors: [
+                {
+                    message: ERR_BREAK_HERE,
+                    line: 1,
+                    column: 14
+                },
+                {
+                    message: ERR_BREAK_HERE,
+                    line: 3,
+                    column: 3
+                }
+            ]
+        },
+        {
+            code: "var foo = [1,        \t      (\n2\n),\n3];",
+            options: ["always"],
+            output: "var foo = [1,\n(\n2\n),\n3];",
+            errors: [
+                {
+                    message: ERR_BREAK_HERE,
+                    line: 1,
+                    column: 14
+                }
+            ]
+        },
+        {
+            code: "var foo = [1, ((((2)))), 3];",
+            options: ["always"],
+            output: "var foo = [1,\n((((2)))),\n3];",
+            errors: [
+                {
+                    message: ERR_BREAK_HERE,
+                    line: 1,
+                    column: 14,
+                    endLine: 1,
+                    endColumn: 15
+                },
+                {
+                    message: ERR_BREAK_HERE,
+                    line: 1,
+                    column: 25,
+                    endLine: 1,
+                    endColumn: 26
+                }
+            ]
+        },
+        {
+            code: "var foo = [1,/* any comment */(2), 3];",
+            options: ["always"],
+            output: "var foo = [1,/* any comment */\n(2),\n3];",
+            errors: [
+                {
+                    message: ERR_BREAK_HERE,
+                    line: 1,
+                    column: 31,
+                    endLine: 1,
+                    endColumn: 31
+                },
+                {
+                    message: ERR_BREAK_HERE,
+                    line: 1,
+                    column: 35,
+                    endLine: 1,
+                    endColumn: 36
+                }
+            ]
+        },
+        {
+            code: "var foo = [1,(  2), 3];",
+            options: ["always"],
+            output: "var foo = [1,\n(  2),\n3];",
+            errors: [
+                {
+                    message: ERR_BREAK_HERE,
+                    line: 1,
+                    column: 14,
+                    endLine: 1,
+                    endColumn: 14
+                },
+                {
+                    message: ERR_BREAK_HERE,
+                    line: 1,
+                    column: 20,
+                    endLine: 1,
+                    endColumn: 21
+                }
+            ]
+        },
+        {
+            code: "var foo = [1, [2], 3];",
+            options: ["always"],
+            output: "var foo = [1,\n[2],\n3];",
+            errors: [
+                {
+                    message: ERR_BREAK_HERE,
+                    line: 1,
+                    column: 14,
+                    endLine: 1,
+                    endColumn: 15
+                },
+                {
+                    message: ERR_BREAK_HERE,
+                    line: 1,
+                    column: 19,
+                    endLine: 1,
+                    endColumn: 20
+                }
+            ]
+        },
+        {
+            code: "var foo = [\nfunction foo() {\ndosomething();\n}, function bar() {\ndosomething();\n}\n];",
+            options: ["always"],
+            output: "var foo = [\nfunction foo() {\ndosomething();\n},\nfunction bar() {\ndosomething();\n}\n];",
+            errors: [
+                {
+                    message: ERR_BREAK_HERE,
+                    line: 4,
+                    column: 3
+                }
+            ]
+        },
+        {
+            code: "var foo = [\n(function foo() {\ndosomething();\n}), function bar() {\ndosomething();\n}\n];",
+            options: ["always"],
+            output: "var foo = [\n(function foo() {\ndosomething();\n}),\nfunction bar() {\ndosomething();\n}\n];",
+            errors: [
+                {
+                    message: ERR_BREAK_HERE,
+                    line: 4,
+                    column: 4
+                }
+            ]
+        },
+
+        // "never"
+        {
+            code: "var foo = [\n1,\n2\n];",
+            options: ["never"],
+            output: "var foo = [\n1, 2\n];",
+            errors: [
+                {
+                    message: ERR_NO_BREAK_HERE,
+                    line: 2,
+                    column: 3
+                }
+            ]
+        },
+        {
+            code: "var foo = [\n1\n, 2\n];",
+            options: ["never"],
+            output: "var foo = [\n1, 2\n];",
+            errors: [
+                {
+                    message: ERR_NO_BREAK_HERE,
+                    line: 3,
+                    column: 2
+                }
+            ]
+        },
+        {
+            code: "var foo = [\n1 // any comment\n, 2\n];",
+            options: ["never"],
+            output: "var foo = [\n1 // any comment\n, 2\n];",
+            errors: [
+                {
+                    message: ERR_NO_BREAK_HERE,
+                    line: 3,
+                    column: 2
+                }
+            ]
+        },
+        {
+            code: "var foo = [\n1, // any comment\n2\n];",
+            options: ["never"],
+            output: "var foo = [\n1, // any comment\n2\n];",
+            errors: [
+                {
+                    message: ERR_NO_BREAK_HERE,
+                    line: 2,
+                    column: 18
+                }
+            ]
+        },
+        {
+            code: "var foo = [\n1,\n2 // any comment\n];",
+            options: ["never"],
+            output: "var foo = [\n1, 2 // any comment\n];",
+            errors: [
+                {
+                    message: ERR_NO_BREAK_HERE,
+                    line: 2,
+                    column: 3
+                }
+            ]
+        },
+        {
+            code: "var foo = [\n1,\n2,\n3\n];",
+            options: ["never"],
+            output: "var foo = [\n1, 2, 3\n];",
+            errors: [
+                {
+                    message: ERR_NO_BREAK_HERE,
+                    line: 2,
+                    column: 3,
+                    endLine: 3,
+                    endColumn: 1
+                },
+                {
+                    message: ERR_NO_BREAK_HERE,
+                    line: 3,
+                    column: 3,
+                    endLine: 4,
+                    endColumn: 1
+                }
+            ]
+        },
+        {
+            code: "var foo = [\nfunction foo() {\ndosomething();\n},\nfunction bar() {\ndosomething();\n}\n];",
+            options: ["never"],
+            output: "var foo = [\nfunction foo() {\ndosomething();\n}, function bar() {\ndosomething();\n}\n];",
+            errors: [
+                {
+                    message: ERR_NO_BREAK_HERE,
+                    line: 4,
+                    column: 3
+                }
+            ]
+        },
+        {
+            code: "var foo = [\nfunction foo() {\ndosomething();\n}, /* any comment */\nfunction bar() {\ndosomething();\n}\n];",
+            options: ["never"],
+            output: "var foo = [\nfunction foo() {\ndosomething();\n}, /* any comment */\nfunction bar() {\ndosomething();\n}\n];",
+            errors: [
+                {
+                    message: ERR_NO_BREAK_HERE,
+                    line: 4,
+                    column: 21
+                }
+            ]
+        },
+
+        // { multiline: true }
+        {
+            code: "var foo = [1,\n2, 3];",
+            options: [{ multiline: true }],
+            output: "var foo = [1, 2, 3];",
+            errors: [
+                {
+                    message: ERR_NO_BREAK_HERE,
+                    line: 1,
+                    column: 14
+                }
+            ]
+        },
+        {
+            code: "var foo = [\nfunction foo() {\ndosomething();\n}, function bar() {\ndosomething();\n}\n];",
+            options: [{ multiline: true }],
+            output: "var foo = [\nfunction foo() {\ndosomething();\n},\nfunction bar() {\ndosomething();\n}\n];",
+            errors: [
+                {
+                    message: ERR_BREAK_HERE,
+                    line: 4,
+                    column: 3
+                }
+            ]
+        },
+        {
+            code: "var foo = [\nfunction foo() {\ndosomething();\n}, /* any comment */ function bar() {\ndosomething();\n}\n];",
+            options: [{ multiline: true }],
+            output: "var foo = [\nfunction foo() {\ndosomething();\n}, /* any comment */\nfunction bar() {\ndosomething();\n}\n];",
+            errors: [
+                {
+                    message: ERR_BREAK_HERE,
+                    line: 4,
+                    column: 21
+                }
+            ]
+        },
+
+        // { minItems: null }
+        {
+            code: "var foo = [1,\n2];",
+            options: [{ minItems: null }],
+            output: "var foo = [1, 2];",
+            errors: [
+                {
+                    message: ERR_NO_BREAK_HERE,
+                    line: 1,
+                    column: 14
+                }
+            ]
+        },
+        {
+            code: "var foo = [1,\n2,\n3];",
+            options: [{ minItems: null }],
+            output: "var foo = [1, 2, 3];",
+            errors: [
+                {
+                    message: ERR_NO_BREAK_HERE,
+                    line: 1,
+                    column: 14
+                },
+                {
+                    message: ERR_NO_BREAK_HERE,
+                    line: 2,
+                    column: 3
+                }
+            ]
+        },
+        {
+            code: "var foo = [\nfunction foo() {\ndosomething();\n},\nfunction bar() {\ndosomething();\n}\n];",
+            options: [{ minItems: null }],
+            output: "var foo = [\nfunction foo() {\ndosomething();\n}, function bar() {\ndosomething();\n}\n];",
+            errors: [
+                {
+                    message: ERR_NO_BREAK_HERE,
+                    line: 4,
+                    column: 3
+                }
+            ]
+        },
+
+        // { minItems: 0 }
+        {
+            code: "var foo = [1, 2];",
+            options: [{ minItems: 0 }],
+            output: "var foo = [1,\n2];",
+            errors: [
+                {
+                    message: ERR_BREAK_HERE,
+                    line: 1,
+                    column: 14
+                }
+            ]
+        },
+        {
+            code: "var foo = [1, 2, 3];",
+            options: [{ minItems: 0 }],
+            output: "var foo = [1,\n2,\n3];",
+            errors: [
+                {
+                    message: ERR_BREAK_HERE,
+                    line: 1,
+                    column: 14
+                },
+                {
+                    message: ERR_BREAK_HERE,
+                    line: 1,
+                    column: 17
+                }
+            ]
+        },
+        {
+            code: "var foo = [\nfunction foo() {\ndosomething();\n}, function bar() {\ndosomething();\n}\n];",
+            options: [{ minItems: 0 }],
+            output: "var foo = [\nfunction foo() {\ndosomething();\n},\nfunction bar() {\ndosomething();\n}\n];",
+            errors: [
+                {
+                    message: ERR_BREAK_HERE,
+                    line: 4,
+                    column: 3
+                }
+            ]
+        },
+
+        // { minItems: 3 }
+        {
+            code: "var foo = [1,\n2];",
+            options: [{ minItems: 3 }],
+            output: "var foo = [1, 2];",
+            errors: [
+                {
+                    message: ERR_NO_BREAK_HERE,
+                    line: 1,
+                    column: 14
+                }
+            ]
+        },
+        {
+            code: "var foo = [1, 2, 3];",
+            options: [{ minItems: 3 }],
+            output: "var foo = [1,\n2,\n3];",
+            errors: [
+                {
+                    message: ERR_BREAK_HERE,
+                    line: 1,
+                    column: 14
+                },
+                {
+                    message: ERR_BREAK_HERE,
+                    line: 1,
+                    column: 17
+                }
+            ]
+        },
+        {
+            code: "var foo = [\nfunction foo() {\ndosomething();\n},\nfunction bar() {\ndosomething();\n}\n];",
+            options: [{ minItems: 3 }],
+            output: "var foo = [\nfunction foo() {\ndosomething();\n}, function bar() {\ndosomething();\n}\n];",
+            errors: [
+                {
+                    message: ERR_NO_BREAK_HERE,
+                    line: 4,
+                    column: 3
+                }
+            ]
+        },
+
+        // { multiline: true, minItems: 3 }
+        {
+            code: "var foo = [1, 2, 3];",
+            options: [{ multiline: true, minItems: 3 }],
+            output: "var foo = [1,\n2,\n3];",
+            errors: [
+                {
+                    message: ERR_BREAK_HERE,
+                    line: 1,
+                    column: 14
+                },
+                {
+                    message: ERR_BREAK_HERE,
+                    line: 1,
+                    column: 17
+                }
+            ]
+        },
+        {
+            code: "var foo = [1,\n2];",
+            options: [{ multiline: true, minItems: 3 }],
+            output: "var foo = [1, 2];",
+            errors: [
+                {
+                    message: ERR_NO_BREAK_HERE,
+                    line: 1,
+                    column: 14
+                }
+            ]
+        },
+        {
+            code: "var foo = [\nfunction foo() {\ndosomething();\n}, function bar() {\ndosomething();\n}\n];",
+            options: [{ multiline: true, minItems: 3 }],
+            output: "var foo = [\nfunction foo() {\ndosomething();\n},\nfunction bar() {\ndosomething();\n}\n];",
+            errors: [
+                {
+                    message: ERR_BREAK_HERE,
+                    line: 4,
+                    column: 3
+                }
+            ]
+        },
+
+        // ArrayPattern
+        // "always"
+        {
+            code: "var [a, b] = foo;",
+            options: ["always"],
+            output: "var [a,\nb] = foo;",
+            parserOptions: { ecmaVersion: 6 },
+            errors: [
+                {
+                    message: ERR_BREAK_HERE,
+                    line: 1,
+                    column: 8
+                }
+            ]
+        },
+        {
+            code: "var [a, b, c] = foo;",
+            options: ["always"],
+            output: "var [a,\nb,\nc] = foo;",
+            parserOptions: { ecmaVersion: 6 },
+            errors: [
+                {
+                    message: ERR_BREAK_HERE,
+                    line: 1,
+                    column: 8
+                },
+                {
+                    message: ERR_BREAK_HERE,
+                    line: 1,
+                    column: 11
+                }
+            ]
+        },
+
+        // { minItems: 3 }
+        {
+            code: "var [a,\nb] = foo;",
+            options: [{ minItems: 3 }],
+            output: "var [a, b] = foo;",
+            parserOptions: { ecmaVersion: 6 },
+            errors: [
+                {
+                    message: ERR_NO_BREAK_HERE,
+                    line: 1,
+                    column: 8
+                }
+            ]
+        },
+        {
+            code: "var [a, b, c] = foo;",
+            options: [{ minItems: 3 }],
+            output: "var [a,\nb,\nc] = foo;",
+            parserOptions: { ecmaVersion: 6 },
+            errors: [
+                {
+                    message: ERR_BREAK_HERE,
+                    line: 1,
+                    column: 8
+                },
+                {
+                    message: ERR_BREAK_HERE,
+                    line: 1,
+                    column: 11
+                }
+            ]
+        }
+    ]
+
+});