/**
							 | 
						|
								 * @fileoverview Rule to require grouped accessor pairs in object literals and classes
							 | 
						|
								 * @author Milos Djermanovic
							 | 
						|
								 */
							 | 
						|
								
							 | 
						|
								"use strict";
							 | 
						|
								
							 | 
						|
								//------------------------------------------------------------------------------
							 | 
						|
								// Requirements
							 | 
						|
								//------------------------------------------------------------------------------
							 | 
						|
								
							 | 
						|
								const astUtils = require("./utils/ast-utils");
							 | 
						|
								
							 | 
						|
								//------------------------------------------------------------------------------
							 | 
						|
								// Typedefs
							 | 
						|
								//------------------------------------------------------------------------------
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Property name if it can be computed statically, otherwise the list of the tokens of the key node.
							 | 
						|
								 * @typedef {string|Token[]} Key
							 | 
						|
								 */
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Accessor nodes with the same key.
							 | 
						|
								 * @typedef {Object} AccessorData
							 | 
						|
								 * @property {Key} key Accessor's key
							 | 
						|
								 * @property {ASTNode[]} getters List of getter nodes.
							 | 
						|
								 * @property {ASTNode[]} setters List of setter nodes.
							 | 
						|
								 */
							 | 
						|
								
							 | 
						|
								//------------------------------------------------------------------------------
							 | 
						|
								// Helpers
							 | 
						|
								//------------------------------------------------------------------------------
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Checks whether or not the given lists represent the equal tokens in the same order.
							 | 
						|
								 * Tokens are compared by their properties, not by instance.
							 | 
						|
								 * @param {Token[]} left First list of tokens.
							 | 
						|
								 * @param {Token[]} right Second list of tokens.
							 | 
						|
								 * @returns {boolean} `true` if the lists have same tokens.
							 | 
						|
								 */
							 | 
						|
								function areEqualTokenLists(left, right) {
							 | 
						|
								    if (left.length !== right.length) {
							 | 
						|
								        return false;
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    for (let i = 0; i < left.length; i++) {
							 | 
						|
								        const leftToken = left[i],
							 | 
						|
								            rightToken = right[i];
							 | 
						|
								
							 | 
						|
								        if (leftToken.type !== rightToken.type || leftToken.value !== rightToken.value) {
							 | 
						|
								            return false;
							 | 
						|
								        }
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    return true;
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Checks whether or not the given keys are equal.
							 | 
						|
								 * @param {Key} left First key.
							 | 
						|
								 * @param {Key} right Second key.
							 | 
						|
								 * @returns {boolean} `true` if the keys are equal.
							 | 
						|
								 */
							 | 
						|
								function areEqualKeys(left, right) {
							 | 
						|
								    if (typeof left === "string" && typeof right === "string") {
							 | 
						|
								
							 | 
						|
								        // Statically computed names.
							 | 
						|
								        return left === right;
							 | 
						|
								    }
							 | 
						|
								    if (Array.isArray(left) && Array.isArray(right)) {
							 | 
						|
								
							 | 
						|
								        // Token lists.
							 | 
						|
								        return areEqualTokenLists(left, right);
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    return false;
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Checks whether or not a given node is of an accessor kind ('get' or 'set').
							 | 
						|
								 * @param {ASTNode} node A node to check.
							 | 
						|
								 * @returns {boolean} `true` if the node is of an accessor kind.
							 | 
						|
								 */
							 | 
						|
								function isAccessorKind(node) {
							 | 
						|
								    return node.kind === "get" || node.kind === "set";
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								//------------------------------------------------------------------------------
							 | 
						|
								// Rule Definition
							 | 
						|
								//------------------------------------------------------------------------------
							 | 
						|
								
							 | 
						|
								/** @type {import('../shared/types').Rule} */
							 | 
						|
								module.exports = {
							 | 
						|
								    meta: {
							 | 
						|
								        type: "suggestion",
							 | 
						|
								
							 | 
						|
								        docs: {
							 | 
						|
								            description: "Require grouped accessor pairs in object literals and classes",
							 | 
						|
								            recommended: false,
							 | 
						|
								            url: "https://eslint.org/docs/latest/rules/grouped-accessor-pairs"
							 | 
						|
								        },
							 | 
						|
								
							 | 
						|
								        schema: [
							 | 
						|
								            {
							 | 
						|
								                enum: ["anyOrder", "getBeforeSet", "setBeforeGet"]
							 | 
						|
								            }
							 | 
						|
								        ],
							 | 
						|
								
							 | 
						|
								        messages: {
							 | 
						|
								            notGrouped: "Accessor pair {{ formerName }} and {{ latterName }} should be grouped.",
							 | 
						|
								            invalidOrder: "Expected {{ latterName }} to be before {{ formerName }}."
							 | 
						|
								        }
							 | 
						|
								    },
							 | 
						|
								
							 | 
						|
								    create(context) {
							 | 
						|
								        const order = context.options[0] || "anyOrder";
							 | 
						|
								        const sourceCode = context.sourceCode;
							 | 
						|
								
							 | 
						|
								        /**
							 | 
						|
								         * Reports the given accessor pair.
							 | 
						|
								         * @param {string} messageId messageId to report.
							 | 
						|
								         * @param {ASTNode} formerNode getter/setter node that is defined before `latterNode`.
							 | 
						|
								         * @param {ASTNode} latterNode getter/setter node that is defined after `formerNode`.
							 | 
						|
								         * @returns {void}
							 | 
						|
								         * @private
							 | 
						|
								         */
							 | 
						|
								        function report(messageId, formerNode, latterNode) {
							 | 
						|
								            context.report({
							 | 
						|
								                node: latterNode,
							 | 
						|
								                messageId,
							 | 
						|
								                loc: astUtils.getFunctionHeadLoc(latterNode.value, sourceCode),
							 | 
						|
								                data: {
							 | 
						|
								                    formerName: astUtils.getFunctionNameWithKind(formerNode.value),
							 | 
						|
								                    latterName: astUtils.getFunctionNameWithKind(latterNode.value)
							 | 
						|
								                }
							 | 
						|
								            });
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        /**
							 | 
						|
								         * Checks accessor pairs in the given list of nodes.
							 | 
						|
								         * @param {ASTNode[]} nodes The list to check.
							 | 
						|
								         * @param {Function} shouldCheck – Predicate that returns `true` if the node should be checked.
							 | 
						|
								         * @returns {void}
							 | 
						|
								         * @private
							 | 
						|
								         */
							 | 
						|
								        function checkList(nodes, shouldCheck) {
							 | 
						|
								            const accessors = [];
							 | 
						|
								            let found = false;
							 | 
						|
								
							 | 
						|
								            for (let i = 0; i < nodes.length; i++) {
							 | 
						|
								                const node = nodes[i];
							 | 
						|
								
							 | 
						|
								                if (shouldCheck(node) && isAccessorKind(node)) {
							 | 
						|
								
							 | 
						|
								                    // Creates a new `AccessorData` object for the given getter or setter node.
							 | 
						|
								                    const name = astUtils.getStaticPropertyName(node);
							 | 
						|
								                    const key = (name !== null) ? name : sourceCode.getTokens(node.key);
							 | 
						|
								
							 | 
						|
								                    // Merges the given `AccessorData` object into the given accessors list.
							 | 
						|
								                    for (let j = 0; j < accessors.length; j++) {
							 | 
						|
								                        const accessor = accessors[j];
							 | 
						|
								
							 | 
						|
								                        if (areEqualKeys(accessor.key, key)) {
							 | 
						|
								                            accessor.getters.push(...node.kind === "get" ? [node] : []);
							 | 
						|
								                            accessor.setters.push(...node.kind === "set" ? [node] : []);
							 | 
						|
								                            found = true;
							 | 
						|
								                            break;
							 | 
						|
								                        }
							 | 
						|
								                    }
							 | 
						|
								                    if (!found) {
							 | 
						|
								                        accessors.push({
							 | 
						|
								                            key,
							 | 
						|
								                            getters: node.kind === "get" ? [node] : [],
							 | 
						|
								                            setters: node.kind === "set" ? [node] : []
							 | 
						|
								                        });
							 | 
						|
								                    }
							 | 
						|
								                    found = false;
							 | 
						|
								                }
							 | 
						|
								            }
							 | 
						|
								
							 | 
						|
								            for (const { getters, setters } of accessors) {
							 | 
						|
								
							 | 
						|
								                // Don't report accessor properties that have duplicate getters or setters.
							 | 
						|
								                if (getters.length === 1 && setters.length === 1) {
							 | 
						|
								                    const [getter] = getters,
							 | 
						|
								                        [setter] = setters,
							 | 
						|
								                        getterIndex = nodes.indexOf(getter),
							 | 
						|
								                        setterIndex = nodes.indexOf(setter),
							 | 
						|
								                        formerNode = getterIndex < setterIndex ? getter : setter,
							 | 
						|
								                        latterNode = getterIndex < setterIndex ? setter : getter;
							 | 
						|
								
							 | 
						|
								                    if (Math.abs(getterIndex - setterIndex) > 1) {
							 | 
						|
								                        report("notGrouped", formerNode, latterNode);
							 | 
						|
								                    } else if (
							 | 
						|
								                        (order === "getBeforeSet" && getterIndex > setterIndex) ||
							 | 
						|
								                        (order === "setBeforeGet" && getterIndex < setterIndex)
							 | 
						|
								                    ) {
							 | 
						|
								                        report("invalidOrder", formerNode, latterNode);
							 | 
						|
								                    }
							 | 
						|
								                }
							 | 
						|
								            }
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        return {
							 | 
						|
								            ObjectExpression(node) {
							 | 
						|
								                checkList(node.properties, n => n.type === "Property");
							 | 
						|
								            },
							 | 
						|
								            ClassBody(node) {
							 | 
						|
								                checkList(node.body, n => n.type === "MethodDefinition" && !n.static);
							 | 
						|
								                checkList(node.body, n => n.type === "MethodDefinition" && n.static);
							 | 
						|
								            }
							 | 
						|
								        };
							 | 
						|
								    }
							 | 
						|
								};
							 |