prefer-destructuring.js
5.64 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
/**
* @fileoverview Prefer destructuring from arrays and objects
* @author Alex LaFroscia
*/
"use strict";
//------------------------------------------------------------------------------
// Rule Definition
//------------------------------------------------------------------------------
module.exports = {
meta: {
docs: {
description: "require destructuring from arrays and/or objects",
category: "ECMAScript 6",
recommended: false
},
schema: [
{
type: "object",
properties: {
array: {
type: "boolean"
},
object: {
type: "boolean"
}
},
additionalProperties: false
},
{
type: "object",
properties: {
enforceForRenamedProperties: {
type: "boolean"
}
},
additionalProperties: false
}
]
},
create(context) {
let checkArrays = true;
let checkObjects = true;
let enforceForRenamedProperties = false;
const enabledTypes = context.options[0];
const additionalOptions = context.options[1];
if (enabledTypes) {
if (typeof enabledTypes.array !== "undefined") {
checkArrays = enabledTypes.array;
}
if (typeof enabledTypes.object !== "undefined") {
checkObjects = enabledTypes.object;
}
}
if (additionalOptions) {
if (typeof additionalOptions.enforceForRenamedProperties !== "undefined") {
enforceForRenamedProperties = additionalOptions.enforceForRenamedProperties;
}
}
//--------------------------------------------------------------------------
// Helpers
//--------------------------------------------------------------------------
/**
* Determines if the given node 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
* @returns {void}
*/
function report(reportNode, type) {
context.report({ node: reportNode, message: "Use {{type}} destructuring.", data: { type } });
}
/**
* 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") {
return;
}
if (checkArrays && isArrayIndexAccess(rightNode)) {
report(reportNode, "array");
return;
}
if (checkObjects && enforceForRenamedProperties) {
report(reportNode, "object");
return;
}
if (checkObjects) {
const property = rightNode.property;
if ((property.type === "Literal" && leftNode.name === property.value) ||
(property.type === "Identifier" && leftNode.name === property.name)) {
report(reportNode, "object");
}
}
}
/**
* 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
};
}
};