source: imaps-frontend/node_modules/eslint-plugin-react/lib/rules/no-unused-class-component-methods.js@ 0c6b92a

main
Last change on this file since 0c6b92a was 0c6b92a, checked in by stefan toskovski <stefantoska84@…>, 5 weeks ago

Pred finalna verzija

  • Property mode set to 100644
File size: 6.6 KB
RevLine 
[d565449]1/**
2 * @fileoverview Prevent declaring unused methods and properties of component class
3 * @author Paweł Nowak, Berton Zhu
4 */
5
6'use strict';
7
8const docsUrl = require('../util/docsUrl');
9const componentUtil = require('../util/componentUtil');
10const report = require('../util/report');
11
12// ------------------------------------------------------------------------------
13// Rule Definition
14// ------------------------------------------------------------------------------
15
16const LIFECYCLE_METHODS = new Set([
17 'constructor',
18 'componentDidCatch',
19 'componentDidMount',
20 'componentDidUpdate',
21 'componentWillMount',
22 'componentWillReceiveProps',
23 'componentWillUnmount',
24 'componentWillUpdate',
25 'getChildContext',
26 'getSnapshotBeforeUpdate',
27 'render',
28 'shouldComponentUpdate',
29 'UNSAFE_componentWillMount',
30 'UNSAFE_componentWillReceiveProps',
31 'UNSAFE_componentWillUpdate',
32]);
33
34const ES6_LIFECYCLE = new Set([
35 'state',
36]);
37
38const ES5_LIFECYCLE = new Set([
39 'getInitialState',
40 'getDefaultProps',
41 'mixins',
42]);
43
44function isKeyLiteralLike(node, property) {
45 return property.type === 'Literal'
46 || (property.type === 'TemplateLiteral' && property.expressions.length === 0)
47 || (node.computed === false && property.type === 'Identifier');
48}
49
50// Descend through all wrapping TypeCastExpressions and return the expression
51// that was cast.
52function uncast(node) {
53 while (node.type === 'TypeCastExpression') {
54 node = node.expression;
55 }
56 return node;
57}
58
59// Return the name of an identifier or the string value of a literal. Useful
60// anywhere that a literal may be used as a key (e.g., member expressions,
61// method definitions, ObjectExpression property keys).
62function getName(node) {
63 node = uncast(node);
64 const type = node.type;
65
66 if (type === 'Identifier') {
67 return node.name;
68 }
69 if (type === 'Literal') {
70 return String(node.value);
71 }
72 if (type === 'TemplateLiteral' && node.expressions.length === 0) {
73 return node.quasis[0].value.raw;
74 }
75 return null;
76}
77
78function isThisExpression(node) {
79 return uncast(node).type === 'ThisExpression';
80}
81
82function getInitialClassInfo(node, isClass) {
83 return {
84 classNode: node,
85 isClass,
86 // Set of nodes where properties were defined.
87 properties: new Set(),
88
89 // Set of names of properties that we've seen used.
90 usedProperties: new Set(),
91
92 inStatic: false,
93 };
94}
95
96const messages = {
97 unused: 'Unused method or property "{{name}}"',
98 unusedWithClass: 'Unused method or property "{{name}}" of class "{{className}}"',
99};
100
[0c6b92a]101/** @type {import('eslint').Rule.RuleModule} */
[d565449]102module.exports = {
103 meta: {
104 docs: {
105 description: 'Disallow declaring unused methods of component class',
106 category: 'Best Practices',
107 recommended: false,
108 url: docsUrl('no-unused-class-component-methods'),
109 },
110 messages,
111 schema: [],
112 },
113
114 create: ((context) => {
115 let classInfo = null;
116
117 // Takes an ObjectExpression node and adds all named Property nodes to the
118 // current set of properties.
119 function addProperty(node) {
120 classInfo.properties.add(node);
121 }
122
123 // Adds the name of the given node as a used property if the node is an
124 // Identifier or a Literal. Other node types are ignored.
125 function addUsedProperty(node) {
126 const name = getName(node);
127 if (name) {
128 classInfo.usedProperties.add(name);
129 }
130 }
131
132 function reportUnusedProperties() {
133 // Report all unused properties.
134 for (const node of classInfo.properties) { // eslint-disable-line no-restricted-syntax
135 const name = getName(node);
136 if (
137 !classInfo.usedProperties.has(name)
138 && !LIFECYCLE_METHODS.has(name)
139 && (classInfo.isClass ? !ES6_LIFECYCLE.has(name) : !ES5_LIFECYCLE.has(name))
140 ) {
141 const className = (classInfo.classNode.id && classInfo.classNode.id.name) || '';
142
143 const messageID = className ? 'unusedWithClass' : 'unused';
144 report(
145 context,
146 messages[messageID],
147 messageID,
148 {
149 node,
150 data: {
151 name,
152 className,
153 },
154 }
155 );
156 }
157 }
158 }
159
160 function exitMethod() {
161 if (!classInfo || !classInfo.inStatic) {
162 return;
163 }
164
165 classInfo.inStatic = false;
166 }
167
168 return {
169 ClassDeclaration(node) {
170 if (componentUtil.isES6Component(node, context)) {
171 classInfo = getInitialClassInfo(node, true);
172 }
173 },
174
175 ObjectExpression(node) {
176 if (componentUtil.isES5Component(node, context)) {
177 classInfo = getInitialClassInfo(node, false);
178 }
179 },
180
181 'ClassDeclaration:exit'() {
182 if (!classInfo) {
183 return;
184 }
185 reportUnusedProperties();
186 classInfo = null;
187 },
188
189 'ObjectExpression:exit'(node) {
190 if (!classInfo || classInfo.classNode !== node) {
191 return;
192 }
193 reportUnusedProperties();
194 classInfo = null;
195 },
196
197 Property(node) {
198 if (!classInfo || classInfo.classNode !== node.parent) {
199 return;
200 }
201
202 if (isKeyLiteralLike(node, node.key)) {
203 addProperty(node.key);
204 }
205 },
206
207 'ClassProperty, MethodDefinition, PropertyDefinition'(node) {
208 if (!classInfo) {
209 return;
210 }
211
212 if (node.static) {
213 classInfo.inStatic = true;
214 return;
215 }
216
217 if (isKeyLiteralLike(node, node.key)) {
218 addProperty(node.key);
219 }
220 },
221
222 'ClassProperty:exit': exitMethod,
223 'MethodDefinition:exit': exitMethod,
224 'PropertyDefinition:exit': exitMethod,
225
226 MemberExpression(node) {
227 if (!classInfo || classInfo.inStatic) {
228 return;
229 }
230
231 if (isThisExpression(node.object) && isKeyLiteralLike(node, node.property)) {
232 if (node.parent.type === 'AssignmentExpression' && node.parent.left === node) {
233 // detect `this.property = xxx`
234 addProperty(node.property);
235 } else {
236 // detect `this.property()`, `x = this.property`, etc.
237 addUsedProperty(node.property);
238 }
239 }
240 },
241
242 VariableDeclarator(node) {
243 if (!classInfo || classInfo.inStatic) {
244 return;
245 }
246
247 // detect `{ foo, bar: baz } = this`
248 if (node.init && isThisExpression(node.init) && node.id.type === 'ObjectPattern') {
249 node.id.properties
250 .filter((prop) => prop.type === 'Property' && isKeyLiteralLike(prop, prop.key))
251 .forEach((prop) => {
[0c6b92a]252 addUsedProperty('key' in prop ? prop.key : undefined);
[d565449]253 });
254 }
255 },
256 };
257 }),
258};
Note: See TracBrowser for help on using the repository browser.