source: imaps-frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-bind.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.0 KB
Line 
1/**
2 * @fileoverview Prevents usage of Function.prototype.bind and arrow functions
3 * in React component props.
4 * @author Daniel Lo Nigro <dan.cx>
5 * @author Jacky Ho
6 */
7
8'use strict';
9
10const propName = require('jsx-ast-utils/propName');
11const docsUrl = require('../util/docsUrl');
12const astUtil = require('../util/ast');
13const jsxUtil = require('../util/jsx');
14const report = require('../util/report');
15const getAncestors = require('../util/eslint').getAncestors;
16
17// -----------------------------------------------------------------------------
18// Rule Definition
19// -----------------------------------------------------------------------------
20
21const messages = {
22 bindCall: 'JSX props should not use .bind()',
23 arrowFunc: 'JSX props should not use arrow functions',
24 bindExpression: 'JSX props should not use ::',
25 func: 'JSX props should not use functions',
26};
27
28/** @type {import('eslint').Rule.RuleModule} */
29module.exports = {
30 meta: {
31 docs: {
32 description: 'Disallow `.bind()` or arrow functions in JSX props',
33 category: 'Best Practices',
34 recommended: false,
35 url: docsUrl('jsx-no-bind'),
36 },
37
38 messages,
39
40 schema: [{
41 type: 'object',
42 properties: {
43 allowArrowFunctions: {
44 default: false,
45 type: 'boolean',
46 },
47 allowBind: {
48 default: false,
49 type: 'boolean',
50 },
51 allowFunctions: {
52 default: false,
53 type: 'boolean',
54 },
55 ignoreRefs: {
56 default: false,
57 type: 'boolean',
58 },
59 ignoreDOMComponents: {
60 default: false,
61 type: 'boolean',
62 },
63 },
64 additionalProperties: false,
65 }],
66 },
67
68 create(context) {
69 const configuration = context.options[0] || {};
70
71 // Keep track of all the variable names pointing to a bind call,
72 // bind expression or an arrow function in different block statements
73 const blockVariableNameSets = {};
74
75 /**
76 * @param {string | number} blockStart
77 */
78 function setBlockVariableNameSet(blockStart) {
79 blockVariableNameSets[blockStart] = {
80 arrowFunc: new Set(),
81 bindCall: new Set(),
82 bindExpression: new Set(),
83 func: new Set(),
84 };
85 }
86
87 function getNodeViolationType(node) {
88 if (
89 !configuration.allowBind
90 && astUtil.isCallExpression(node)
91 && node.callee.type === 'MemberExpression'
92 && node.callee.property.type === 'Identifier'
93 && node.callee.property.name === 'bind'
94 ) {
95 return 'bindCall';
96 }
97 if (node.type === 'ConditionalExpression') {
98 return getNodeViolationType(node.test)
99 || getNodeViolationType(node.consequent)
100 || getNodeViolationType(node.alternate);
101 }
102 if (!configuration.allowArrowFunctions && node.type === 'ArrowFunctionExpression') {
103 return 'arrowFunc';
104 }
105 if (
106 !configuration.allowFunctions
107 && (node.type === 'FunctionExpression' || node.type === 'FunctionDeclaration')
108 ) {
109 return 'func';
110 }
111 if (!configuration.allowBind && node.type === 'BindExpression') {
112 return 'bindExpression';
113 }
114
115 return null;
116 }
117
118 /**
119 * @param {string | number} violationType
120 * @param {unknown} variableName
121 * @param {string | number} blockStart
122 */
123 function addVariableNameToSet(violationType, variableName, blockStart) {
124 blockVariableNameSets[blockStart][violationType].add(variableName);
125 }
126
127 function getBlockStatementAncestors(node) {
128 return getAncestors(context, node).filter(
129 (ancestor) => ancestor.type === 'BlockStatement'
130 ).reverse();
131 }
132
133 function reportVariableViolation(node, name, blockStart) {
134 const blockSets = blockVariableNameSets[blockStart];
135 const violationTypes = Object.keys(blockSets);
136
137 return violationTypes.find((type) => {
138 if (blockSets[type].has(name)) {
139 report(context, messages[type], type, {
140 node,
141 });
142 return true;
143 }
144
145 return false;
146 });
147 }
148
149 function findVariableViolation(node, name) {
150 getBlockStatementAncestors(node).find(
151 (block) => reportVariableViolation(node, name, block.range[0])
152 );
153 }
154
155 return {
156 BlockStatement(node) {
157 setBlockVariableNameSet(node.range[0]);
158 },
159
160 FunctionDeclaration(node) {
161 const blockAncestors = getBlockStatementAncestors(node);
162 const variableViolationType = getNodeViolationType(node);
163
164 if (blockAncestors.length > 0 && variableViolationType) {
165 addVariableNameToSet(variableViolationType, node.id.name, blockAncestors[0].range[0]);
166 }
167 },
168
169 VariableDeclarator(node) {
170 if (!node.init) {
171 return;
172 }
173 const blockAncestors = getBlockStatementAncestors(node);
174 const variableViolationType = getNodeViolationType(node.init);
175
176 if (
177 blockAncestors.length > 0
178 && variableViolationType
179 && 'kind' in node.parent
180 && node.parent.kind === 'const' // only support const right now
181 ) {
182 addVariableNameToSet(variableViolationType, 'name' in node.id ? node.id.name : undefined, blockAncestors[0].range[0]);
183 }
184 },
185
186 JSXAttribute(node) {
187 const isRef = configuration.ignoreRefs && propName(node) === 'ref';
188 if (isRef || !node.value || !node.value.expression) {
189 return;
190 }
191 const isDOMComponent = jsxUtil.isDOMComponent(node.parent);
192 if (configuration.ignoreDOMComponents && isDOMComponent) {
193 return;
194 }
195 const valueNode = node.value.expression;
196 const valueNodeType = valueNode.type;
197 const nodeViolationType = getNodeViolationType(valueNode);
198
199 if (valueNodeType === 'Identifier') {
200 findVariableViolation(node, valueNode.name);
201 } else if (nodeViolationType) {
202 report(context, messages[nodeViolationType], nodeViolationType, {
203 node,
204 });
205 }
206 },
207 };
208 },
209};
Note: See TracBrowser for help on using the repository browser.