source: frontend/node_modules/eslint-plugin-react/lib/rules/jsx-handler-names.js

Last change on this file was 9af201e, checked in by MBK <marija.karapandzova@…>, 12 days ago

Fix frontend appearance

  • Property mode set to 100644
File size: 6.2 KB
Line 
1/**
2 * @fileoverview Enforce event handler naming conventions in JSX
3 * @author Jake Marsh
4 */
5
6'use strict';
7
8const minimatch = require('minimatch');
9const docsUrl = require('../util/docsUrl');
10const getText = require('../util/eslint').getText;
11const report = require('../util/report');
12
13// ------------------------------------------------------------------------------
14// Rule Definition
15// ------------------------------------------------------------------------------
16
17const messages = {
18 badHandlerName: 'Handler function for {{propKey}} prop key must be a camelCase name beginning with \'{{handlerPrefix}}\' only',
19 badPropKey: 'Prop key for {{propValue}} must begin with \'{{handlerPropPrefix}}\'',
20};
21
22function isPrefixDisabled(prefix) {
23 return prefix === false;
24}
25
26function isInlineHandler(node) {
27 return node.value.expression.type === 'ArrowFunctionExpression';
28}
29
30/** @type {import('eslint').Rule.RuleModule} */
31module.exports = {
32 meta: {
33 docs: {
34 description: 'Enforce event handler naming conventions in JSX',
35 category: 'Stylistic Issues',
36 recommended: false,
37 url: docsUrl('jsx-handler-names'),
38 },
39
40 messages,
41
42 schema: [{
43 anyOf: [
44 {
45 type: 'object',
46 properties: {
47 eventHandlerPrefix: { type: 'string' },
48 eventHandlerPropPrefix: { type: 'string' },
49 checkLocalVariables: { type: 'boolean' },
50 checkInlineFunction: { type: 'boolean' },
51 ignoreComponentNames: {
52 type: 'array',
53 uniqueItems: true,
54 items: { type: 'string' },
55 },
56 },
57 additionalProperties: false,
58 }, {
59 type: 'object',
60 properties: {
61 eventHandlerPrefix: { type: 'string' },
62 eventHandlerPropPrefix: {
63 type: 'boolean',
64 enum: [false],
65 },
66 checkLocalVariables: { type: 'boolean' },
67 checkInlineFunction: { type: 'boolean' },
68 ignoreComponentNames: {
69 type: 'array',
70 uniqueItems: true,
71 items: { type: 'string' },
72 },
73 },
74 additionalProperties: false,
75 }, {
76 type: 'object',
77 properties: {
78 eventHandlerPrefix: {
79 type: 'boolean',
80 enum: [false],
81 },
82 eventHandlerPropPrefix: { type: 'string' },
83 checkLocalVariables: { type: 'boolean' },
84 checkInlineFunction: { type: 'boolean' },
85 ignoreComponentNames: {
86 type: 'array',
87 uniqueItems: true,
88 items: { type: 'string' },
89 },
90 },
91 additionalProperties: false,
92 }, {
93 type: 'object',
94 properties: {
95 checkLocalVariables: { type: 'boolean' },
96 },
97 additionalProperties: false,
98 }, {
99 type: 'object',
100 properties: {
101 checkInlineFunction: { type: 'boolean' },
102 },
103 additionalProperties: false,
104 },
105 {
106 type: 'object',
107 properties: {
108 ignoreComponentNames: {
109 type: 'array',
110 uniqueItems: true,
111 items: { type: 'string' },
112 },
113 },
114 },
115 ],
116 }],
117 },
118
119 create(context) {
120 const configuration = context.options[0] || {};
121
122 const eventHandlerPrefix = isPrefixDisabled(configuration.eventHandlerPrefix)
123 ? null
124 : configuration.eventHandlerPrefix || 'handle';
125 const eventHandlerPropPrefix = isPrefixDisabled(configuration.eventHandlerPropPrefix)
126 ? null
127 : configuration.eventHandlerPropPrefix || 'on';
128
129 const EVENT_HANDLER_REGEX = !eventHandlerPrefix
130 ? null
131 : new RegExp(`^((props\\.${eventHandlerPropPrefix || ''})|((.*\\.)?${eventHandlerPrefix}))[0-9]*[A-Z].*$`);
132 const PROP_EVENT_HANDLER_REGEX = !eventHandlerPropPrefix
133 ? null
134 : new RegExp(`^(${eventHandlerPropPrefix}[A-Z].*|ref)$`);
135
136 const checkLocal = !!configuration.checkLocalVariables;
137
138 const checkInlineFunction = !!configuration.checkInlineFunction;
139
140 const ignoreComponentNames = configuration.ignoreComponentNames || [];
141
142 return {
143 JSXAttribute(node) {
144 const componentName = node.parent.name.name;
145
146 const isComponentNameIgnored = ignoreComponentNames.some((ignoredComponentNamePattern) => minimatch(
147 componentName,
148 ignoredComponentNamePattern
149 ));
150
151 if (
152 !node.value
153 || !node.value.expression
154 || (!checkInlineFunction && isInlineHandler(node))
155 || (
156 !checkLocal
157 && (isInlineHandler(node)
158 ? !node.value.expression.body.callee || !node.value.expression.body.callee.object
159 : !node.value.expression.object
160 )
161 )
162 || isComponentNameIgnored
163 ) {
164 return;
165 }
166
167 const propKey = typeof node.name === 'object' ? node.name.name : node.name;
168 const expression = node.value.expression;
169 const propValue = getText(
170 context,
171 checkInlineFunction && isInlineHandler(node) ? expression.body.callee : expression
172 ).replace(/\s*/g, '').replace(/^this\.|.*::/, '');
173
174 if (propKey === 'ref') {
175 return;
176 }
177
178 const propIsEventHandler = PROP_EVENT_HANDLER_REGEX && PROP_EVENT_HANDLER_REGEX.test(propKey);
179 const propFnIsNamedCorrectly = EVENT_HANDLER_REGEX && EVENT_HANDLER_REGEX.test(propValue);
180
181 if (
182 propIsEventHandler
183 && propFnIsNamedCorrectly !== null
184 && !propFnIsNamedCorrectly
185 ) {
186 report(context, messages.badHandlerName, 'badHandlerName', {
187 node,
188 data: {
189 propKey,
190 handlerPrefix: eventHandlerPrefix,
191 },
192 });
193 } else if (
194 propFnIsNamedCorrectly
195 && propIsEventHandler !== null
196 && !propIsEventHandler
197 ) {
198 report(context, messages.badPropKey, 'badPropKey', {
199 node,
200 data: {
201 propValue,
202 handlerPropPrefix: eventHandlerPropPrefix,
203 },
204 });
205 }
206 },
207 };
208 },
209};
Note: See TracBrowser for help on using the repository browser.