UNPKG

8.08 kBJavaScriptView Raw
1/**
2 * @fileoverview Prefer destructuring from arrays and objects
3 * @author Alex LaFroscia
4 */
5"use strict";
6
7//------------------------------------------------------------------------------
8// Rule Definition
9//------------------------------------------------------------------------------
10
11module.exports = {
12 meta: {
13 docs: {
14 description: "require destructuring from arrays and/or objects",
15 category: "ECMAScript 6",
16 recommended: false,
17 url: "https://eslint.org/docs/rules/prefer-destructuring"
18 },
19 schema: [
20 {
21
22 /*
23 * old support {array: Boolean, object: Boolean}
24 * new support {VariableDeclarator: {}, AssignmentExpression: {}}
25 */
26 oneOf: [
27 {
28 type: "object",
29 properties: {
30 VariableDeclarator: {
31 type: "object",
32 properties: {
33 array: {
34 type: "boolean"
35 },
36 object: {
37 type: "boolean"
38 }
39 },
40 additionalProperties: false
41 },
42 AssignmentExpression: {
43 type: "object",
44 properties: {
45 array: {
46 type: "boolean"
47 },
48 object: {
49 type: "boolean"
50 }
51 },
52 additionalProperties: false
53 }
54 },
55 additionalProperties: false
56 },
57 {
58 type: "object",
59 properties: {
60 array: {
61 type: "boolean"
62 },
63 object: {
64 type: "boolean"
65 }
66 },
67 additionalProperties: false
68 }
69 ]
70 },
71 {
72 type: "object",
73 properties: {
74 enforceForRenamedProperties: {
75 type: "boolean"
76 }
77 },
78 additionalProperties: false
79 }
80 ]
81 },
82 create(context) {
83
84 const enabledTypes = context.options[0];
85 const enforceForRenamedProperties = context.options[1] && context.options[1].enforceForRenamedProperties;
86 let normalizedOptions = {
87 VariableDeclarator: { array: true, object: true },
88 AssignmentExpression: { array: true, object: true }
89 };
90
91 if (enabledTypes) {
92 normalizedOptions = typeof enabledTypes.array !== "undefined" || typeof enabledTypes.object !== "undefined"
93 ? { VariableDeclarator: enabledTypes, AssignmentExpression: enabledTypes }
94 : enabledTypes;
95 }
96
97 //--------------------------------------------------------------------------
98 // Helpers
99 //--------------------------------------------------------------------------
100
101 /**
102 * @param {string} nodeType "AssignmentExpression" or "VariableDeclarator"
103 * @param {string} destructuringType "array" or "object"
104 * @returns {boolean} `true` if the destructuring type should be checked for the given node
105 */
106 function shouldCheck(nodeType, destructuringType) {
107 return normalizedOptions &&
108 normalizedOptions[nodeType] &&
109 normalizedOptions[nodeType][destructuringType];
110 }
111
112 /**
113 * Determines if the given node is accessing an array index
114 *
115 * This is used to differentiate array index access from object property
116 * access.
117 *
118 * @param {ASTNode} node the node to evaluate
119 * @returns {boolean} whether or not the node is an integer
120 */
121 function isArrayIndexAccess(node) {
122 return Number.isInteger(node.property.value);
123 }
124
125 /**
126 * Report that the given node should use destructuring
127 *
128 * @param {ASTNode} reportNode the node to report
129 * @param {string} type the type of destructuring that should have been done
130 * @returns {void}
131 */
132 function report(reportNode, type) {
133 context.report({ node: reportNode, message: "Use {{type}} destructuring.", data: { type } });
134 }
135
136 /**
137 * Check that the `prefer-destructuring` rules are followed based on the
138 * given left- and right-hand side of the assignment.
139 *
140 * Pulled out into a separate method so that VariableDeclarators and
141 * AssignmentExpressions can share the same verification logic.
142 *
143 * @param {ASTNode} leftNode the left-hand side of the assignment
144 * @param {ASTNode} rightNode the right-hand side of the assignment
145 * @param {ASTNode} reportNode the node to report the error on
146 * @returns {void}
147 */
148 function performCheck(leftNode, rightNode, reportNode) {
149 if (rightNode.type !== "MemberExpression" || rightNode.object.type === "Super") {
150 return;
151 }
152
153 if (isArrayIndexAccess(rightNode)) {
154 if (shouldCheck(reportNode.type, "array")) {
155 report(reportNode, "array");
156 }
157 return;
158 }
159
160 if (shouldCheck(reportNode.type, "object") && enforceForRenamedProperties) {
161 report(reportNode, "object");
162 return;
163 }
164
165 if (shouldCheck(reportNode.type, "object")) {
166 const property = rightNode.property;
167
168 if (
169 (property.type === "Literal" && leftNode.name === property.value) ||
170 (property.type === "Identifier" && leftNode.name === property.name && !rightNode.computed)
171 ) {
172 report(reportNode, "object");
173 }
174 }
175 }
176
177 /**
178 * Check if a given variable declarator is coming from an property access
179 * that should be using destructuring instead
180 *
181 * @param {ASTNode} node the variable declarator to check
182 * @returns {void}
183 */
184 function checkVariableDeclarator(node) {
185
186 // Skip if variable is declared without assignment
187 if (!node.init) {
188 return;
189 }
190
191 // We only care about member expressions past this point
192 if (node.init.type !== "MemberExpression") {
193 return;
194 }
195
196 performCheck(node.id, node.init, node);
197 }
198
199 /**
200 * Run the `prefer-destructuring` check on an AssignmentExpression
201 *
202 * @param {ASTNode} node the AssignmentExpression node
203 * @returns {void}
204 */
205 function checkAssigmentExpression(node) {
206 if (node.operator === "=") {
207 performCheck(node.left, node.right, node);
208 }
209 }
210
211 //--------------------------------------------------------------------------
212 // Public
213 //--------------------------------------------------------------------------
214
215 return {
216 VariableDeclarator: checkVariableDeclarator,
217 AssignmentExpression: checkAssigmentExpression
218 };
219 }
220};