You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							268 lines
						
					
					
						
							9.7 KiB
						
					
					
				
			
		
		
	
	
							268 lines
						
					
					
						
							9.7 KiB
						
					
					
				/** | 
						|
 * @fileoverview Prefer destructuring from arrays and objects | 
						|
 * @author Alex LaFroscia | 
						|
 */ | 
						|
"use strict"; | 
						|
 | 
						|
//------------------------------------------------------------------------------ | 
						|
// Rule Definition | 
						|
//------------------------------------------------------------------------------ | 
						|
 | 
						|
module.exports = { | 
						|
    meta: { | 
						|
        type: "suggestion", | 
						|
 | 
						|
        docs: { | 
						|
            description: "require destructuring from arrays and/or objects", | 
						|
            category: "ECMAScript 6", | 
						|
            recommended: false, | 
						|
            url: "https://eslint.org/docs/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 | 
						|
            } | 
						|
        ] | 
						|
    }, | 
						|
    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 | 
						|
        //-------------------------------------------------------------------------- | 
						|
 | 
						|
        // eslint-disable-next-line jsdoc/require-description | 
						|
        /** | 
						|
         * @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, | 
						|
                message: "Use {{type}} destructuring.", | 
						|
                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 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.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.getSourceCode(); | 
						|
 | 
						|
            return fixer.replaceText( | 
						|
                node, | 
						|
                `{${rightNode.property.name}} = ${sourceCode.getText(rightNode.object)}` | 
						|
            ); | 
						|
        } | 
						|
 | 
						|
        /** | 
						|
         * 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") { | 
						|
                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 checkAssigmentExpression(node) { | 
						|
            if (node.operator === "=") { | 
						|
                performCheck(node.left, node.right, node); | 
						|
            } | 
						|
        } | 
						|
 | 
						|
        //-------------------------------------------------------------------------- | 
						|
        // Public | 
						|
        //-------------------------------------------------------------------------- | 
						|
 | 
						|
        return { | 
						|
            VariableDeclarator: checkVariableDeclarator, | 
						|
            AssignmentExpression: checkAssigmentExpression | 
						|
        }; | 
						|
    } | 
						|
};
 | 
						|
 |