source: imaps-frontend/node_modules/eslint-plugin-react/lib/rules/require-optimization.js@ d565449

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

Update repo after prototype presentation

  • Property mode set to 100644
File size: 6.9 KB
RevLine 
[d565449]1/**
2 * @fileoverview Enforce React components to have a shouldComponentUpdate method
3 * @author Evgueni Naverniouk
4 */
5
6'use strict';
7
8const values = require('object.values');
9
10const Components = require('../util/Components');
11const componentUtil = require('../util/componentUtil');
12const docsUrl = require('../util/docsUrl');
13const report = require('../util/report');
14const getScope = require('../util/eslint').getScope;
15
16const messages = {
17 noShouldComponentUpdate: 'Component is not optimized. Please add a shouldComponentUpdate method.',
18};
19
20/** @type {import('eslint').Rule.RuleModule} */
21module.exports = {
22 meta: {
23 docs: {
24 description: 'Enforce React components to have a shouldComponentUpdate method',
25 category: 'Best Practices',
26 recommended: false,
27 url: docsUrl('require-optimization'),
28 },
29
30 messages,
31
32 schema: [{
33 type: 'object',
34 properties: {
35 allowDecorators: {
36 type: 'array',
37 items: {
38 type: 'string',
39 },
40 },
41 },
42 additionalProperties: false,
43 }],
44 },
45
46 create: Components.detect((context, components) => {
47 const configuration = context.options[0] || {};
48 const allowDecorators = configuration.allowDecorators || [];
49
50 /**
51 * Checks to see if our component is decorated by PureRenderMixin via reactMixin
52 * @param {ASTNode} node The AST node being checked.
53 * @returns {Boolean} True if node is decorated with a PureRenderMixin, false if not.
54 */
55 function hasPureRenderDecorator(node) {
56 if (node.decorators && node.decorators.length) {
57 for (let i = 0, l = node.decorators.length; i < l; i++) {
58 if (
59 node.decorators[i].expression
60 && node.decorators[i].expression.callee
61 && node.decorators[i].expression.callee.object
62 && node.decorators[i].expression.callee.object.name === 'reactMixin'
63 && node.decorators[i].expression.callee.property
64 && node.decorators[i].expression.callee.property.name === 'decorate'
65 && node.decorators[i].expression.arguments
66 && node.decorators[i].expression.arguments.length
67 && node.decorators[i].expression.arguments[0].name === 'PureRenderMixin'
68 ) {
69 return true;
70 }
71 }
72 }
73
74 return false;
75 }
76
77 /**
78 * Checks to see if our component is custom decorated
79 * @param {ASTNode} node The AST node being checked.
80 * @returns {Boolean} True if node is decorated name with a custom decorated, false if not.
81 */
82 function hasCustomDecorator(node) {
83 const allowLength = allowDecorators.length;
84
85 if (allowLength && node.decorators && node.decorators.length) {
86 for (let i = 0; i < allowLength; i++) {
87 for (let j = 0, l = node.decorators.length; j < l; j++) {
88 if (
89 node.decorators[j].expression
90 && node.decorators[j].expression.name === allowDecorators[i]
91 ) {
92 return true;
93 }
94 }
95 }
96 }
97
98 return false;
99 }
100
101 /**
102 * Checks if we are declaring a shouldComponentUpdate method
103 * @param {ASTNode} node The AST node being checked.
104 * @returns {Boolean} True if we are declaring a shouldComponentUpdate method, false if not.
105 */
106 function isSCUDeclared(node) {
107 return Boolean(
108 node
109 && node.name === 'shouldComponentUpdate'
110 );
111 }
112
113 /**
114 * Checks if we are declaring a PureRenderMixin mixin
115 * @param {ASTNode} node The AST node being checked.
116 * @returns {Boolean} True if we are declaring a PureRenderMixin method, false if not.
117 */
118 function isPureRenderDeclared(node) {
119 let hasPR = false;
120 if (node.value && node.value.elements) {
121 for (let i = 0, l = node.value.elements.length; i < l; i++) {
122 if (node.value.elements[i] && node.value.elements[i].name === 'PureRenderMixin') {
123 hasPR = true;
124 break;
125 }
126 }
127 }
128
129 return Boolean(
130 node
131 && node.key.name === 'mixins'
132 && hasPR
133 );
134 }
135
136 /**
137 * Mark shouldComponentUpdate as declared
138 * @param {ASTNode} node The AST node being checked.
139 */
140 function markSCUAsDeclared(node) {
141 components.set(node, {
142 hasSCU: true,
143 });
144 }
145
146 /**
147 * Reports missing optimization for a given component
148 * @param {Object} component The component to process
149 */
150 function reportMissingOptimization(component) {
151 report(context, messages.noShouldComponentUpdate, 'noShouldComponentUpdate', {
152 node: component.node,
153 });
154 }
155
156 /**
157 * Checks if we are declaring function in class
158 * @param {ASTNode} node
159 * @returns {boolean} True if we are declaring function in class, false if not.
160 */
161 function isFunctionInClass(node) {
162 let blockNode;
163 let scope = getScope(context, node);
164 while (scope) {
165 blockNode = scope.block;
166 if (blockNode && blockNode.type === 'ClassDeclaration') {
167 return true;
168 }
169 scope = scope.upper;
170 }
171
172 return false;
173 }
174
175 return {
176 ArrowFunctionExpression(node) {
177 // Skip if the function is declared in the class
178 if (isFunctionInClass(node)) {
179 return;
180 }
181 // Stateless Functional Components cannot be optimized (yet)
182 markSCUAsDeclared(node);
183 },
184
185 ClassDeclaration(node) {
186 if (!(
187 hasPureRenderDecorator(node)
188 || hasCustomDecorator(node)
189 || componentUtil.isPureComponent(node, context)
190 )) {
191 return;
192 }
193 markSCUAsDeclared(node);
194 },
195
196 FunctionDeclaration(node) {
197 // Skip if the function is declared in the class
198 if (isFunctionInClass(node)) {
199 return;
200 }
201 // Stateless Functional Components cannot be optimized (yet)
202 markSCUAsDeclared(node);
203 },
204
205 FunctionExpression(node) {
206 // Skip if the function is declared in the class
207 if (isFunctionInClass(node)) {
208 return;
209 }
210 // Stateless Functional Components cannot be optimized (yet)
211 markSCUAsDeclared(node);
212 },
213
214 MethodDefinition(node) {
215 if (!isSCUDeclared(node.key)) {
216 return;
217 }
218 markSCUAsDeclared(node);
219 },
220
221 ObjectExpression(node) {
222 // Search for the shouldComponentUpdate declaration
223 const found = node.properties.some((property) => (
224 property.key
225 && (isSCUDeclared(property.key) || isPureRenderDeclared(property))
226 ));
227 if (found) {
228 markSCUAsDeclared(node);
229 }
230 },
231
232 'Program:exit'() {
233 // Report missing shouldComponentUpdate for all components
234 values(components.list())
235 .filter((component) => !component.hasSCU)
236 .forEach((component) => {
237 reportMissingOptimization(component);
238 });
239 },
240 };
241 }),
242};
Note: See TracBrowser for help on using the repository browser.