source: imaps-frontend/node_modules/eslint-plugin-react/lib/rules/require-optimization.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.9 KB
Line 
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 const expression = node.decorators[j].expression;
89 if (
90 expression
91 && expression.name === allowDecorators[i]
92 ) {
93 return true;
94 }
95 }
96 }
97 }
98
99 return false;
100 }
101
102 /**
103 * Checks if we are declaring a shouldComponentUpdate method
104 * @param {ASTNode} node The AST node being checked.
105 * @returns {boolean} True if we are declaring a shouldComponentUpdate method, false if not.
106 */
107 function isSCUDeclared(node) {
108 return !!node && node.name === 'shouldComponentUpdate';
109 }
110
111 /**
112 * Checks if we are declaring a PureRenderMixin mixin
113 * @param {ASTNode} node The AST node being checked.
114 * @returns {boolean} True if we are declaring a PureRenderMixin method, false if not.
115 */
116 function isPureRenderDeclared(node) {
117 let hasPR = false;
118 if (node.value && node.value.elements) {
119 for (let i = 0, l = node.value.elements.length; i < l; i++) {
120 if (node.value.elements[i] && node.value.elements[i].name === 'PureRenderMixin') {
121 hasPR = true;
122 break;
123 }
124 }
125 }
126
127 return (
128 !!node
129 && node.key.name === 'mixins'
130 && hasPR
131 );
132 }
133
134 /**
135 * Mark shouldComponentUpdate as declared
136 * @param {ASTNode} node The AST node being checked.
137 */
138 function markSCUAsDeclared(node) {
139 components.set(node, {
140 hasSCU: true,
141 });
142 }
143
144 /**
145 * Reports missing optimization for a given component
146 * @param {Object} component The component to process
147 */
148 function reportMissingOptimization(component) {
149 report(context, messages.noShouldComponentUpdate, 'noShouldComponentUpdate', {
150 node: component.node,
151 });
152 }
153
154 /**
155 * Checks if we are declaring function in class
156 * @param {ASTNode} node
157 * @returns {boolean} True if we are declaring function in class, false if not.
158 */
159 function isFunctionInClass(node) {
160 let blockNode;
161 let scope = getScope(context, node);
162 while (scope) {
163 blockNode = scope.block;
164 if (blockNode && blockNode.type === 'ClassDeclaration') {
165 return true;
166 }
167 scope = scope.upper;
168 }
169
170 return false;
171 }
172
173 return {
174 ArrowFunctionExpression(node) {
175 // Skip if the function is declared in the class
176 if (isFunctionInClass(node)) {
177 return;
178 }
179 // Stateless Functional Components cannot be optimized (yet)
180 markSCUAsDeclared(node);
181 },
182
183 ClassDeclaration(node) {
184 if (!(
185 hasPureRenderDecorator(node)
186 || hasCustomDecorator(node)
187 || componentUtil.isPureComponent(node, context)
188 )) {
189 return;
190 }
191 markSCUAsDeclared(node);
192 },
193
194 FunctionDeclaration(node) {
195 // Skip if the function is declared in the class
196 if (isFunctionInClass(node)) {
197 return;
198 }
199 // Stateless Functional Components cannot be optimized (yet)
200 markSCUAsDeclared(node);
201 },
202
203 FunctionExpression(node) {
204 // Skip if the function is declared in the class
205 if (isFunctionInClass(node)) {
206 return;
207 }
208 // Stateless Functional Components cannot be optimized (yet)
209 markSCUAsDeclared(node);
210 },
211
212 MethodDefinition(node) {
213 if (!isSCUDeclared(node.key)) {
214 return;
215 }
216 markSCUAsDeclared(node);
217 },
218
219 ObjectExpression(node) {
220 // Search for the shouldComponentUpdate declaration
221 const found = node.properties.some((property) => (
222 property.key
223 && (isSCUDeclared(property.key) || isPureRenderDeclared(property))
224 ));
225 if (found) {
226 markSCUAsDeclared(node);
227 }
228 },
229
230 'Program:exit'() {
231 // Report missing shouldComponentUpdate for all components
232 values(components.list())
233 .filter((component) => !component.hasSCU)
234 .forEach((component) => {
235 reportMissingOptimization(component);
236 });
237 },
238 };
239 }),
240};
Note: See TracBrowser for help on using the repository browser.