|                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |  | /** * @fileoverview Rule to specify spacing of object literal keys and values * @author Brandon Mills * @deprecated in ESLint v8.53.0 */"use strict";
//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------
const astUtils = require("./utils/ast-utils");const { getGraphemeCount } = require("../shared/string-utils");
/** * Checks whether a string contains a line terminator as defined in * http://www.ecma-international.org/ecma-262/5.1/#sec-7.3
 * @param {string} str String to test. * @returns {boolean} True if str contains a line terminator. */function containsLineTerminator(str) {    return astUtils.LINEBREAK_MATCHER.test(str);}
/** * Gets the last element of an array. * @param {Array} arr An array. * @returns {any} Last element of arr. */function last(arr) {    return arr[arr.length - 1];}
/** * Checks whether a node is contained on a single line. * @param {ASTNode} node AST Node being evaluated. * @returns {boolean} True if the node is a single line. */function isSingleLine(node) {    return (node.loc.end.line === node.loc.start.line);}
/** * Checks whether the properties on a single line. * @param {ASTNode[]} properties List of Property AST nodes. * @returns {boolean} True if all properties is on a single line. */function isSingleLineProperties(properties) {    const [firstProp] = properties,        lastProp = last(properties);
    return firstProp.loc.start.line === lastProp.loc.end.line;}
/** * Initializes a single option property from the configuration with defaults for undefined values * @param {Object} toOptions Object to be initialized * @param {Object} fromOptions Object to be initialized from * @returns {Object} The object with correctly initialized options and values */function initOptionProperty(toOptions, fromOptions) {    toOptions.mode = fromOptions.mode || "strict";
    // Set value of beforeColon
    if (typeof fromOptions.beforeColon !== "undefined") {        toOptions.beforeColon = +fromOptions.beforeColon;    } else {        toOptions.beforeColon = 0;    }
    // Set value of afterColon
    if (typeof fromOptions.afterColon !== "undefined") {        toOptions.afterColon = +fromOptions.afterColon;    } else {        toOptions.afterColon = 1;    }
    // Set align if exists
    if (typeof fromOptions.align !== "undefined") {        if (typeof fromOptions.align === "object") {            toOptions.align = fromOptions.align;        } else { // "string"
            toOptions.align = {                on: fromOptions.align,                mode: toOptions.mode,                beforeColon: toOptions.beforeColon,                afterColon: toOptions.afterColon            };        }    }
    return toOptions;}
/** * Initializes all the option values (singleLine, multiLine and align) from the configuration with defaults for undefined values * @param {Object} toOptions Object to be initialized * @param {Object} fromOptions Object to be initialized from * @returns {Object} The object with correctly initialized options and values */function initOptions(toOptions, fromOptions) {    if (typeof fromOptions.align === "object") {
        // Initialize the alignment configuration
        toOptions.align = initOptionProperty({}, fromOptions.align);        toOptions.align.on = fromOptions.align.on || "colon";        toOptions.align.mode = fromOptions.align.mode || "strict";
        toOptions.multiLine = initOptionProperty({}, (fromOptions.multiLine || fromOptions));        toOptions.singleLine = initOptionProperty({}, (fromOptions.singleLine || fromOptions));
    } else { // string or undefined
        toOptions.multiLine = initOptionProperty({}, (fromOptions.multiLine || fromOptions));        toOptions.singleLine = initOptionProperty({}, (fromOptions.singleLine || fromOptions));
        // If alignment options are defined in multiLine, pull them out into the general align configuration
        if (toOptions.multiLine.align) {            toOptions.align = {                on: toOptions.multiLine.align.on,                mode: toOptions.multiLine.align.mode || toOptions.multiLine.mode,                beforeColon: toOptions.multiLine.align.beforeColon,                afterColon: toOptions.multiLine.align.afterColon            };        }    }
    return toOptions;}
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
/** @type {import('../shared/types').Rule} */module.exports = {    meta: {        deprecated: true,        replacedBy: [],        type: "layout",
        docs: {            description: "Enforce consistent spacing between keys and values in object literal properties",            recommended: false,            url: "https://eslint.org/docs/latest/rules/key-spacing"        },
        fixable: "whitespace",
        schema: [{            anyOf: [                {                    type: "object",                    properties: {                        align: {                            anyOf: [                                {                                    enum: ["colon", "value"]                                },                                {                                    type: "object",                                    properties: {                                        mode: {                                            enum: ["strict", "minimum"]                                        },                                        on: {                                            enum: ["colon", "value"]                                        },                                        beforeColon: {                                            type: "boolean"                                        },                                        afterColon: {                                            type: "boolean"                                        }                                    },                                    additionalProperties: false                                }                            ]                        },                        mode: {                            enum: ["strict", "minimum"]                        },                        beforeColon: {                            type: "boolean"                        },                        afterColon: {                            type: "boolean"                        }                    },                    additionalProperties: false                },                {                    type: "object",                    properties: {                        singleLine: {                            type: "object",                            properties: {                                mode: {                                    enum: ["strict", "minimum"]                                },                                beforeColon: {                                    type: "boolean"                                },                                afterColon: {                                    type: "boolean"                                }                            },                            additionalProperties: false                        },                        multiLine: {                            type: "object",                            properties: {                                align: {                                    anyOf: [                                        {                                            enum: ["colon", "value"]                                        },                                        {                                            type: "object",                                            properties: {                                                mode: {                                                    enum: ["strict", "minimum"]                                                },                                                on: {                                                    enum: ["colon", "value"]                                                },                                                beforeColon: {                                                    type: "boolean"                                                },                                                afterColon: {                                                    type: "boolean"                                                }                                            },                                            additionalProperties: false                                        }                                    ]                                },                                mode: {                                    enum: ["strict", "minimum"]                                },                                beforeColon: {                                    type: "boolean"                                },                                afterColon: {                                    type: "boolean"                                }                            },                            additionalProperties: false                        }                    },                    additionalProperties: false                },                {                    type: "object",                    properties: {                        singleLine: {                            type: "object",                            properties: {                                mode: {                                    enum: ["strict", "minimum"]                                },                                beforeColon: {                                    type: "boolean"                                },                                afterColon: {                                    type: "boolean"                                }                            },                            additionalProperties: false                        },                        multiLine: {                            type: "object",                            properties: {                                mode: {                                    enum: ["strict", "minimum"]                                },                                beforeColon: {                                    type: "boolean"                                },                                afterColon: {                                    type: "boolean"                                }                            },                            additionalProperties: false                        },                        align: {                            type: "object",                            properties: {                                mode: {                                    enum: ["strict", "minimum"]                                },                                on: {                                    enum: ["colon", "value"]                                },                                beforeColon: {                                    type: "boolean"                                },                                afterColon: {                                    type: "boolean"                                }                            },                            additionalProperties: false                        }                    },                    additionalProperties: false                }            ]        }],        messages: {            extraKey: "Extra space after {{computed}}key '{{key}}'.",            extraValue: "Extra space before value for {{computed}}key '{{key}}'.",            missingKey: "Missing space after {{computed}}key '{{key}}'.",            missingValue: "Missing space before value for {{computed}}key '{{key}}'."        }    },
    create(context) {
        /**         * OPTIONS         * "key-spacing": [2, {         *     beforeColon: false,         *     afterColon: true,         *     align: "colon" // Optional, or "value"
         * }         */        const options = context.options[0] || {},            ruleOptions = initOptions({}, options),            multiLineOptions = ruleOptions.multiLine,            singleLineOptions = ruleOptions.singleLine,            alignmentOptions = ruleOptions.align || null;
        const sourceCode = context.sourceCode;
        /**         * Determines if the given property is key-value property.         * @param {ASTNode} property Property node to check.         * @returns {boolean} Whether the property is a key-value property.         */        function isKeyValueProperty(property) {            return !(                (property.method ||                property.shorthand ||                property.kind !== "init" || property.type !== "Property") // Could be "ExperimentalSpreadProperty" or "SpreadElement"
            );        }
        /**         * Starting from the given node (a property.key node here) looks forward         * until it finds the colon punctuator and returns it.         * @param {ASTNode} node The node to start looking from.         * @returns {ASTNode} The colon punctuator.         */        function getNextColon(node) {            return sourceCode.getTokenAfter(node, astUtils.isColonToken);        }
        /**         * Starting from the given node (a property.key node here) looks forward         * until it finds the last token before a colon punctuator and returns it.         * @param {ASTNode} node The node to start looking from.         * @returns {ASTNode} The last token before a colon punctuator.         */        function getLastTokenBeforeColon(node) {            const colonToken = getNextColon(node);
            return sourceCode.getTokenBefore(colonToken);        }
        /**         * Starting from the given node (a property.key node here) looks forward         * until it finds the first token after a colon punctuator and returns it.         * @param {ASTNode} node The node to start looking from.         * @returns {ASTNode} The first token after a colon punctuator.         */        function getFirstTokenAfterColon(node) {            const colonToken = getNextColon(node);
            return sourceCode.getTokenAfter(colonToken);        }
        /**         * Checks whether a property is a member of the property group it follows.         * @param {ASTNode} lastMember The last Property known to be in the group.         * @param {ASTNode} candidate The next Property that might be in the group.         * @returns {boolean} True if the candidate property is part of the group.         */        function continuesPropertyGroup(lastMember, candidate) {            const groupEndLine = lastMember.loc.start.line,                candidateValueStartLine = (isKeyValueProperty(candidate) ? getFirstTokenAfterColon(candidate.key) : candidate).loc.start.line;
            if (candidateValueStartLine - groupEndLine <= 1) {                return true;            }
            /*             * Check that the first comment is adjacent to the end of the group, the             * last comment is adjacent to the candidate property, and that successive             * comments are adjacent to each other.             */            const leadingComments = sourceCode.getCommentsBefore(candidate);
            if (                leadingComments.length &&                leadingComments[0].loc.start.line - groupEndLine <= 1 &&                candidateValueStartLine - last(leadingComments).loc.end.line <= 1            ) {                for (let i = 1; i < leadingComments.length; i++) {                    if (leadingComments[i].loc.start.line - leadingComments[i - 1].loc.end.line > 1) {                        return false;                    }                }                return true;            }
            return false;        }
        /**         * Gets an object literal property's key as the identifier name or string value.         * @param {ASTNode} property Property node whose key to retrieve.         * @returns {string} The property's key.         */        function getKey(property) {            const key = property.key;
            if (property.computed) {                return sourceCode.getText().slice(key.range[0], key.range[1]);            }            return astUtils.getStaticPropertyName(property);        }
        /**         * Reports an appropriately-formatted error if spacing is incorrect on one         * side of the colon.         * @param {ASTNode} property Key-value pair in an object literal.         * @param {string} side Side being verified - either "key" or "value".         * @param {string} whitespace Actual whitespace string.         * @param {int} expected Expected whitespace length.         * @param {string} mode Value of the mode as "strict" or "minimum"         * @returns {void}         */        function report(property, side, whitespace, expected, mode) {            const diff = whitespace.length - expected;
            if ((                diff && mode === "strict" ||                diff < 0 && mode === "minimum" ||                diff > 0 && !expected && mode === "minimum") &&                !(expected && containsLineTerminator(whitespace))            ) {                const nextColon = getNextColon(property.key),                    tokenBeforeColon = sourceCode.getTokenBefore(nextColon, { includeComments: true }),                    tokenAfterColon = sourceCode.getTokenAfter(nextColon, { includeComments: true }),                    isKeySide = side === "key",                    isExtra = diff > 0,                    diffAbs = Math.abs(diff),                    spaces = Array(diffAbs + 1).join(" ");
                const locStart = isKeySide ? tokenBeforeColon.loc.end : nextColon.loc.start;                const locEnd = isKeySide ? nextColon.loc.start : tokenAfterColon.loc.start;                const missingLoc = isKeySide ? tokenBeforeColon.loc : tokenAfterColon.loc;                const loc = isExtra ? { start: locStart, end: locEnd } : missingLoc;
                let fix;
                if (isExtra) {                    let range;
                    // Remove whitespace
                    if (isKeySide) {                        range = [tokenBeforeColon.range[1], tokenBeforeColon.range[1] + diffAbs];                    } else {                        range = [tokenAfterColon.range[0] - diffAbs, tokenAfterColon.range[0]];                    }                    fix = function(fixer) {                        return fixer.removeRange(range);                    };                } else {
                    // Add whitespace
                    if (isKeySide) {                        fix = function(fixer) {                            return fixer.insertTextAfter(tokenBeforeColon, spaces);                        };                    } else {                        fix = function(fixer) {                            return fixer.insertTextBefore(tokenAfterColon, spaces);                        };                    }                }
                let messageId = "";
                if (isExtra) {                    messageId = side === "key" ? "extraKey" : "extraValue";                } else {                    messageId = side === "key" ? "missingKey" : "missingValue";                }
                context.report({                    node: property[side],                    loc,                    messageId,                    data: {                        computed: property.computed ? "computed " : "",                        key: getKey(property)                    },                    fix                });            }        }
        /**         * Gets the number of characters in a key, including quotes around string         * keys and braces around computed property keys.         * @param {ASTNode} property Property of on object literal.         * @returns {int} Width of the key.         */        function getKeyWidth(property) {            const startToken = sourceCode.getFirstToken(property);            const endToken = getLastTokenBeforeColon(property.key);
            return getGraphemeCount(sourceCode.getText().slice(startToken.range[0], endToken.range[1]));        }
        /**         * Gets the whitespace around the colon in an object literal property.         * @param {ASTNode} property Property node from an object literal.         * @returns {Object} Whitespace before and after the property's colon.         */        function getPropertyWhitespace(property) {            const whitespace = /(\s*):(\s*)/u.exec(sourceCode.getText().slice(                property.key.range[1], property.value.range[0]            ));
            if (whitespace) {                return {                    beforeColon: whitespace[1],                    afterColon: whitespace[2]                };            }            return null;        }
        /**         * Creates groups of properties.         * @param {ASTNode} node ObjectExpression node being evaluated.         * @returns {Array<ASTNode[]>} Groups of property AST node lists.         */        function createGroups(node) {            if (node.properties.length === 1) {                return [node.properties];            }
            return node.properties.reduce((groups, property) => {                const currentGroup = last(groups),                    prev = last(currentGroup);
                if (!prev || continuesPropertyGroup(prev, property)) {                    currentGroup.push(property);                } else {                    groups.push([property]);                }
                return groups;            }, [                []            ]);        }
        /**         * Verifies correct vertical alignment of a group of properties.         * @param {ASTNode[]} properties List of Property AST nodes.         * @returns {void}         */        function verifyGroupAlignment(properties) {            const length = properties.length,                widths = properties.map(getKeyWidth), // Width of keys, including quotes
                align = alignmentOptions.on; // "value" or "colon"
            let targetWidth = Math.max(...widths),                beforeColon, afterColon, mode;
            if (alignmentOptions && length > 1) { // When aligning values within a group, use the alignment configuration.
                beforeColon = alignmentOptions.beforeColon;                afterColon = alignmentOptions.afterColon;                mode = alignmentOptions.mode;            } else {                beforeColon = multiLineOptions.beforeColon;                afterColon = multiLineOptions.afterColon;                mode = alignmentOptions.mode;            }
            // Conditionally include one space before or after colon
            targetWidth += (align === "colon" ? beforeColon : afterColon);
            for (let i = 0; i < length; i++) {                const property = properties[i];                const whitespace = getPropertyWhitespace(property);
                if (whitespace) { // Object literal getters/setters lack a colon
                    const width = widths[i];
                    if (align === "value") {                        report(property, "key", whitespace.beforeColon, beforeColon, mode);                        report(property, "value", whitespace.afterColon, targetWidth - width, mode);                    } else { // align = "colon"
                        report(property, "key", whitespace.beforeColon, targetWidth - width, mode);                        report(property, "value", whitespace.afterColon, afterColon, mode);                    }                }            }        }
        /**         * Verifies spacing of property conforms to specified options.         * @param {ASTNode} node Property node being evaluated.         * @param {Object} lineOptions Configured singleLine or multiLine options         * @returns {void}         */        function verifySpacing(node, lineOptions) {            const actual = getPropertyWhitespace(node);
            if (actual) { // Object literal getters/setters lack colons
                report(node, "key", actual.beforeColon, lineOptions.beforeColon, lineOptions.mode);                report(node, "value", actual.afterColon, lineOptions.afterColon, lineOptions.mode);            }        }
        /**         * Verifies spacing of each property in a list.         * @param {ASTNode[]} properties List of Property AST nodes.         * @param {Object} lineOptions Configured singleLine or multiLine options         * @returns {void}         */        function verifyListSpacing(properties, lineOptions) {            const length = properties.length;
            for (let i = 0; i < length; i++) {                verifySpacing(properties[i], lineOptions);            }        }
        /**         * Verifies vertical alignment, taking into account groups of properties.         * @param {ASTNode} node ObjectExpression node being evaluated.         * @returns {void}         */        function verifyAlignment(node) {            createGroups(node).forEach(group => {                const properties = group.filter(isKeyValueProperty);
                if (properties.length > 0 && isSingleLineProperties(properties)) {                    verifyListSpacing(properties, multiLineOptions);                } else {                    verifyGroupAlignment(properties);                }            });        }
        //--------------------------------------------------------------------------
        // Public API
        //--------------------------------------------------------------------------
        if (alignmentOptions) { // Verify vertical alignment
            return {                ObjectExpression(node) {                    if (isSingleLine(node)) {                        verifyListSpacing(node.properties.filter(isKeyValueProperty), singleLineOptions);                    } else {                        verifyAlignment(node);                    }                }            };
        }
        // Obey beforeColon and afterColon in each property as configured
        return {            Property(node) {                verifySpacing(node, isSingleLine(node.parent) ? singleLineOptions : multiLineOptions);            }        };
    }};
 |