| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236 | /** * @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,            url: "https://eslint.org/docs/rules/array-element-newline"        },        fixable: "whitespace",        schema: [            {                oneOf: [                    {                        enum: ["always", "never"]                    },                    {                        type: "object",                        properties: {                            multiline: {                                type: "boolean"                            },                            minItems: {                                type: ["integer", "null"],                                minimum: 0                            }                        },                        additionalProperties: false                    }                ]            }        ],        messages: {            unexpectedLineBreak: "There should be no linebreak here.",            missingLineBreak: "There should be a linebreak after this element."        }    },    create(context) {        const sourceCode = context.getSourceCode();        //----------------------------------------------------------------------        // Helpers        //----------------------------------------------------------------------        /**         * Normalizes a given option value.         *         * @param {string|Object|undefined} providedOption - An option value to parse.         * @returns {{multiline: boolean, minItems: number}} Normalized option object.         */        function normalizeOptionValue(providedOption) {            let multiline = false;            let minItems;            const option = providedOption || "always";            if (!option || 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                },                messageId: "unexpectedLineBreak",                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                },                messageId: "missingLineBreak",                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        };    }};
 |