/**
							 | 
						|
								 * @fileoverview Prefer destructuring from arrays and objects
							 | 
						|
								 * @author Alex LaFroscia
							 | 
						|
								 */
							 | 
						|
								"use strict";
							 | 
						|
								
							 | 
						|
								//------------------------------------------------------------------------------
							 | 
						|
								// Requirements
							 | 
						|
								//------------------------------------------------------------------------------
							 | 
						|
								
							 | 
						|
								const astUtils = require("./utils/ast-utils");
							 | 
						|
								
							 | 
						|
								//------------------------------------------------------------------------------
							 | 
						|
								// Helpers
							 | 
						|
								//------------------------------------------------------------------------------
							 | 
						|
								
							 | 
						|
								const PRECEDENCE_OF_ASSIGNMENT_EXPR = astUtils.getPrecedence({ type: "AssignmentExpression" });
							 | 
						|
								
							 | 
						|
								//------------------------------------------------------------------------------
							 | 
						|
								// Rule Definition
							 | 
						|
								//------------------------------------------------------------------------------
							 | 
						|
								
							 | 
						|
								/** @type {import('../shared/types').Rule} */
							 | 
						|
								module.exports = {
							 | 
						|
								    meta: {
							 | 
						|
								        type: "suggestion",
							 | 
						|
								
							 | 
						|
								        docs: {
							 | 
						|
								            description: "Require destructuring from arrays and/or objects",
							 | 
						|
								            recommended: false,
							 | 
						|
								            url: "https://eslint.org/docs/latest/rules/prefer-destructuring"
							 | 
						|
								        },
							 | 
						|
								
							 | 
						|
								        fixable: "code",
							 | 
						|
								
							 | 
						|
								        schema: [
							 | 
						|
								            {
							 | 
						|
								
							 | 
						|
								                /*
							 | 
						|
								                 * old support {array: Boolean, object: Boolean}
							 | 
						|
								                 * new support {VariableDeclarator: {}, AssignmentExpression: {}}
							 | 
						|
								                 */
							 | 
						|
								                oneOf: [
							 | 
						|
								                    {
							 | 
						|
								                        type: "object",
							 | 
						|
								                        properties: {
							 | 
						|
								                            VariableDeclarator: {
							 | 
						|
								                                type: "object",
							 | 
						|
								                                properties: {
							 | 
						|
								                                    array: {
							 | 
						|
								                                        type: "boolean"
							 | 
						|
								                                    },
							 | 
						|
								                                    object: {
							 | 
						|
								                                        type: "boolean"
							 | 
						|
								                                    }
							 | 
						|
								                                },
							 | 
						|
								                                additionalProperties: false
							 | 
						|
								                            },
							 | 
						|
								                            AssignmentExpression: {
							 | 
						|
								                                type: "object",
							 | 
						|
								                                properties: {
							 | 
						|
								                                    array: {
							 | 
						|
								                                        type: "boolean"
							 | 
						|
								                                    },
							 | 
						|
								                                    object: {
							 | 
						|
								                                        type: "boolean"
							 | 
						|
								                                    }
							 | 
						|
								                                },
							 | 
						|
								                                additionalProperties: false
							 | 
						|
								                            }
							 | 
						|
								                        },
							 | 
						|
								                        additionalProperties: false
							 | 
						|
								                    },
							 | 
						|
								                    {
							 | 
						|
								                        type: "object",
							 | 
						|
								                        properties: {
							 | 
						|
								                            array: {
							 | 
						|
								                                type: "boolean"
							 | 
						|
								                            },
							 | 
						|
								                            object: {
							 | 
						|
								                                type: "boolean"
							 | 
						|
								                            }
							 | 
						|
								                        },
							 | 
						|
								                        additionalProperties: false
							 | 
						|
								                    }
							 | 
						|
								                ]
							 | 
						|
								            },
							 | 
						|
								            {
							 | 
						|
								                type: "object",
							 | 
						|
								                properties: {
							 | 
						|
								                    enforceForRenamedProperties: {
							 | 
						|
								                        type: "boolean"
							 | 
						|
								                    }
							 | 
						|
								                },
							 | 
						|
								                additionalProperties: false
							 | 
						|
								            }
							 | 
						|
								        ],
							 | 
						|
								
							 | 
						|
								        messages: {
							 | 
						|
								            preferDestructuring: "Use {{type}} destructuring."
							 | 
						|
								        }
							 | 
						|
								    },
							 | 
						|
								    create(context) {
							 | 
						|
								
							 | 
						|
								        const enabledTypes = context.options[0];
							 | 
						|
								        const enforceForRenamedProperties = context.options[1] && context.options[1].enforceForRenamedProperties;
							 | 
						|
								        let normalizedOptions = {
							 | 
						|
								            VariableDeclarator: { array: true, object: true },
							 | 
						|
								            AssignmentExpression: { array: true, object: true }
							 | 
						|
								        };
							 | 
						|
								
							 | 
						|
								        if (enabledTypes) {
							 | 
						|
								            normalizedOptions = typeof enabledTypes.array !== "undefined" || typeof enabledTypes.object !== "undefined"
							 | 
						|
								                ? { VariableDeclarator: enabledTypes, AssignmentExpression: enabledTypes }
							 | 
						|
								                : enabledTypes;
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        //--------------------------------------------------------------------------
							 | 
						|
								        // Helpers
							 | 
						|
								        //--------------------------------------------------------------------------
							 | 
						|
								
							 | 
						|
								        /**
							 | 
						|
								         * Checks if destructuring type should be checked.
							 | 
						|
								         * @param {string} nodeType "AssignmentExpression" or "VariableDeclarator"
							 | 
						|
								         * @param {string} destructuringType "array" or "object"
							 | 
						|
								         * @returns {boolean} `true` if the destructuring type should be checked for the given node
							 | 
						|
								         */
							 | 
						|
								        function shouldCheck(nodeType, destructuringType) {
							 | 
						|
								            return normalizedOptions &&
							 | 
						|
								                normalizedOptions[nodeType] &&
							 | 
						|
								                normalizedOptions[nodeType][destructuringType];
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        /**
							 | 
						|
								         * Determines if the given node is accessing an array index
							 | 
						|
								         *
							 | 
						|
								         * This is used to differentiate array index access from object property
							 | 
						|
								         * access.
							 | 
						|
								         * @param {ASTNode} node the node to evaluate
							 | 
						|
								         * @returns {boolean} whether or not the node is an integer
							 | 
						|
								         */
							 | 
						|
								        function isArrayIndexAccess(node) {
							 | 
						|
								            return Number.isInteger(node.property.value);
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        /**
							 | 
						|
								         * Report that the given node should use destructuring
							 | 
						|
								         * @param {ASTNode} reportNode the node to report
							 | 
						|
								         * @param {string} type the type of destructuring that should have been done
							 | 
						|
								         * @param {Function|null} fix the fix function or null to pass to context.report
							 | 
						|
								         * @returns {void}
							 | 
						|
								         */
							 | 
						|
								        function report(reportNode, type, fix) {
							 | 
						|
								            context.report({
							 | 
						|
								                node: reportNode,
							 | 
						|
								                messageId: "preferDestructuring",
							 | 
						|
								                data: { type },
							 | 
						|
								                fix
							 | 
						|
								            });
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        /**
							 | 
						|
								         * Determines if a node should be fixed into object destructuring
							 | 
						|
								         *
							 | 
						|
								         * The fixer only fixes the simplest case of object destructuring,
							 | 
						|
								         * like: `let x = a.x`;
							 | 
						|
								         *
							 | 
						|
								         * Assignment expression is not fixed.
							 | 
						|
								         * Array destructuring is not fixed.
							 | 
						|
								         * Renamed property is not fixed.
							 | 
						|
								         * @param {ASTNode} node the node to evaluate
							 | 
						|
								         * @returns {boolean} whether or not the node should be fixed
							 | 
						|
								         */
							 | 
						|
								        function shouldFix(node) {
							 | 
						|
								            return node.type === "VariableDeclarator" &&
							 | 
						|
								                node.id.type === "Identifier" &&
							 | 
						|
								                node.init.type === "MemberExpression" &&
							 | 
						|
								                !node.init.computed &&
							 | 
						|
								                node.init.property.type === "Identifier" &&
							 | 
						|
								                node.id.name === node.init.property.name;
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        /**
							 | 
						|
								         * Fix a node into object destructuring.
							 | 
						|
								         * This function only handles the simplest case of object destructuring,
							 | 
						|
								         * see {@link shouldFix}.
							 | 
						|
								         * @param {SourceCodeFixer} fixer the fixer object
							 | 
						|
								         * @param {ASTNode} node the node to be fixed.
							 | 
						|
								         * @returns {Object} a fix for the node
							 | 
						|
								         */
							 | 
						|
								        function fixIntoObjectDestructuring(fixer, node) {
							 | 
						|
								            const rightNode = node.init;
							 | 
						|
								            const sourceCode = context.sourceCode;
							 | 
						|
								
							 | 
						|
								            // Don't fix if that would remove any comments. Only comments inside `rightNode.object` can be preserved.
							 | 
						|
								            if (sourceCode.getCommentsInside(node).length > sourceCode.getCommentsInside(rightNode.object).length) {
							 | 
						|
								                return null;
							 | 
						|
								            }
							 | 
						|
								
							 | 
						|
								            let objectText = sourceCode.getText(rightNode.object);
							 | 
						|
								
							 | 
						|
								            if (astUtils.getPrecedence(rightNode.object) < PRECEDENCE_OF_ASSIGNMENT_EXPR) {
							 | 
						|
								                objectText = `(${objectText})`;
							 | 
						|
								            }
							 | 
						|
								
							 | 
						|
								            return fixer.replaceText(
							 | 
						|
								                node,
							 | 
						|
								                `{${rightNode.property.name}} = ${objectText}`
							 | 
						|
								            );
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        /**
							 | 
						|
								         * Check that the `prefer-destructuring` rules are followed based on the
							 | 
						|
								         * given left- and right-hand side of the assignment.
							 | 
						|
								         *
							 | 
						|
								         * Pulled out into a separate method so that VariableDeclarators and
							 | 
						|
								         * AssignmentExpressions can share the same verification logic.
							 | 
						|
								         * @param {ASTNode} leftNode the left-hand side of the assignment
							 | 
						|
								         * @param {ASTNode} rightNode the right-hand side of the assignment
							 | 
						|
								         * @param {ASTNode} reportNode the node to report the error on
							 | 
						|
								         * @returns {void}
							 | 
						|
								         */
							 | 
						|
								        function performCheck(leftNode, rightNode, reportNode) {
							 | 
						|
								            if (
							 | 
						|
								                rightNode.type !== "MemberExpression" ||
							 | 
						|
								                rightNode.object.type === "Super" ||
							 | 
						|
								                rightNode.property.type === "PrivateIdentifier"
							 | 
						|
								            ) {
							 | 
						|
								                return;
							 | 
						|
								            }
							 | 
						|
								
							 | 
						|
								            if (isArrayIndexAccess(rightNode)) {
							 | 
						|
								                if (shouldCheck(reportNode.type, "array")) {
							 | 
						|
								                    report(reportNode, "array", null);
							 | 
						|
								                }
							 | 
						|
								                return;
							 | 
						|
								            }
							 | 
						|
								
							 | 
						|
								            const fix = shouldFix(reportNode)
							 | 
						|
								                ? fixer => fixIntoObjectDestructuring(fixer, reportNode)
							 | 
						|
								                : null;
							 | 
						|
								
							 | 
						|
								            if (shouldCheck(reportNode.type, "object") && enforceForRenamedProperties) {
							 | 
						|
								                report(reportNode, "object", fix);
							 | 
						|
								                return;
							 | 
						|
								            }
							 | 
						|
								
							 | 
						|
								            if (shouldCheck(reportNode.type, "object")) {
							 | 
						|
								                const property = rightNode.property;
							 | 
						|
								
							 | 
						|
								                if (
							 | 
						|
								                    (property.type === "Literal" && leftNode.name === property.value) ||
							 | 
						|
								                    (property.type === "Identifier" && leftNode.name === property.name && !rightNode.computed)
							 | 
						|
								                ) {
							 | 
						|
								                    report(reportNode, "object", fix);
							 | 
						|
								                }
							 | 
						|
								            }
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        /**
							 | 
						|
								         * Check if a given variable declarator is coming from an property access
							 | 
						|
								         * that should be using destructuring instead
							 | 
						|
								         * @param {ASTNode} node the variable declarator to check
							 | 
						|
								         * @returns {void}
							 | 
						|
								         */
							 | 
						|
								        function checkVariableDeclarator(node) {
							 | 
						|
								
							 | 
						|
								            // Skip if variable is declared without assignment
							 | 
						|
								            if (!node.init) {
							 | 
						|
								                return;
							 | 
						|
								            }
							 | 
						|
								
							 | 
						|
								            // We only care about member expressions past this point
							 | 
						|
								            if (node.init.type !== "MemberExpression") {
							 | 
						|
								                return;
							 | 
						|
								            }
							 | 
						|
								
							 | 
						|
								            performCheck(node.id, node.init, node);
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        /**
							 | 
						|
								         * Run the `prefer-destructuring` check on an AssignmentExpression
							 | 
						|
								         * @param {ASTNode} node the AssignmentExpression node
							 | 
						|
								         * @returns {void}
							 | 
						|
								         */
							 | 
						|
								        function checkAssignmentExpression(node) {
							 | 
						|
								            if (node.operator === "=") {
							 | 
						|
								                performCheck(node.left, node.right, node);
							 | 
						|
								            }
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        //--------------------------------------------------------------------------
							 | 
						|
								        // Public
							 | 
						|
								        //--------------------------------------------------------------------------
							 | 
						|
								
							 | 
						|
								        return {
							 | 
						|
								            VariableDeclarator: checkVariableDeclarator,
							 | 
						|
								            AssignmentExpression: checkAssignmentExpression
							 | 
						|
								        };
							 | 
						|
								    }
							 | 
						|
								};
							 |