source: imaps-frontend/node_modules/eslint-plugin-react/lib/rules/jsx-no-bind.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: 5.9 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 jsxUtil = require('../util/jsx');
13const report = require('../util/report');
14const getAncestors = require('../util/eslint').getAncestors;
15
16// -----------------------------------------------------------------------------
17// Rule Definition
18// -----------------------------------------------------------------------------
19
20const messages = {
21 bindCall: 'JSX props should not use .bind()',
22 arrowFunc: 'JSX props should not use arrow functions',
23 bindExpression: 'JSX props should not use ::',
24 func: 'JSX props should not use functions',
25};
26
27module.exports = {
28 meta: {
29 docs: {
30 description: 'Disallow `.bind()` or arrow functions in JSX props',
31 category: 'Best Practices',
32 recommended: false,
33 url: docsUrl('jsx-no-bind'),
34 },
35
36 messages,
37
38 schema: [{
39 type: 'object',
40 properties: {
41 allowArrowFunctions: {
42 default: false,
43 type: 'boolean',
44 },
45 allowBind: {
46 default: false,
47 type: 'boolean',
48 },
49 allowFunctions: {
50 default: false,
51 type: 'boolean',
52 },
53 ignoreRefs: {
54 default: false,
55 type: 'boolean',
56 },
57 ignoreDOMComponents: {
58 default: false,
59 type: 'boolean',
60 },
61 },
62 additionalProperties: false,
63 }],
64 },
65
66 create(context) {
67 const configuration = context.options[0] || {};
68
69 // Keep track of all the variable names pointing to a bind call,
70 // bind expression or an arrow function in different block statements
71 const blockVariableNameSets = {};
72
73 /**
74 * @param {string | number} blockStart
75 */
76 function setBlockVariableNameSet(blockStart) {
77 blockVariableNameSets[blockStart] = {
78 arrowFunc: new Set(),
79 bindCall: new Set(),
80 bindExpression: new Set(),
81 func: new Set(),
82 };
83 }
84
85 function getNodeViolationType(node) {
86 const nodeType = node.type;
87 if (
88 !configuration.allowBind
89 && nodeType === 'CallExpression'
90 && node.callee.type === 'MemberExpression'
91 && node.callee.property.type === 'Identifier'
92 && node.callee.property.name === 'bind'
93 ) {
94 return 'bindCall';
95 }
96 if (nodeType === 'ConditionalExpression') {
97 return getNodeViolationType(node.test)
98 || getNodeViolationType(node.consequent)
99 || getNodeViolationType(node.alternate);
100 }
101 if (!configuration.allowArrowFunctions && nodeType === 'ArrowFunctionExpression') {
102 return 'arrowFunc';
103 }
104 if (
105 !configuration.allowFunctions
106 && (nodeType === 'FunctionExpression' || nodeType === 'FunctionDeclaration')
107 ) {
108 return 'func';
109 }
110 if (!configuration.allowBind && nodeType === 'BindExpression') {
111 return 'bindExpression';
112 }
113
114 return null;
115 }
116
117 /**
118 * @param {string | number} violationType
119 * @param {any} variableName
120 * @param {string | number} blockStart
121 */
122 function addVariableNameToSet(violationType, variableName, blockStart) {
123 blockVariableNameSets[blockStart][violationType].add(variableName);
124 }
125
126 function getBlockStatementAncestors(node) {
127 return getAncestors(context, node).filter(
128 (ancestor) => ancestor.type === 'BlockStatement'
129 ).reverse();
130 }
131
132 function reportVariableViolation(node, name, blockStart) {
133 const blockSets = blockVariableNameSets[blockStart];
134 const violationTypes = Object.keys(blockSets);
135
136 return violationTypes.find((type) => {
137 if (blockSets[type].has(name)) {
138 report(context, messages[type], type, {
139 node,
140 });
141 return true;
142 }
143
144 return false;
145 });
146 }
147
148 function findVariableViolation(node, name) {
149 getBlockStatementAncestors(node).find(
150 (block) => reportVariableViolation(node, name, block.range[0])
151 );
152 }
153
154 return {
155 BlockStatement(node) {
156 setBlockVariableNameSet(node.range[0]);
157 },
158
159 FunctionDeclaration(node) {
160 const blockAncestors = getBlockStatementAncestors(node);
161 const variableViolationType = getNodeViolationType(node);
162
163 if (blockAncestors.length > 0 && variableViolationType) {
164 addVariableNameToSet(variableViolationType, node.id.name, blockAncestors[0].range[0]);
165 }
166 },
167
168 VariableDeclarator(node) {
169 if (!node.init) {
170 return;
171 }
172 const blockAncestors = getBlockStatementAncestors(node);
173 const variableViolationType = getNodeViolationType(node.init);
174
175 if (
176 blockAncestors.length > 0
177 && variableViolationType
178 && node.parent.kind === 'const' // only support const right now
179 ) {
180 addVariableNameToSet(
181 variableViolationType, node.id.name, blockAncestors[0].range[0]
182 );
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.