UNPKG

3.79 kBJavaScriptView Raw
1/**
2 * @fileoverview Enforce event handler naming conventions in JSX
3 * @author Jake Marsh
4 */
5
6'use strict';
7
8const docsUrl = require('../util/docsUrl');
9
10// ------------------------------------------------------------------------------
11// Rule Definition
12// ------------------------------------------------------------------------------
13
14module.exports = {
15 meta: {
16 docs: {
17 description: 'Enforce event handler naming conventions in JSX',
18 category: 'Stylistic Issues',
19 recommended: false,
20 url: docsUrl('jsx-handler-names')
21 },
22
23 schema: [{
24 anyOf: [
25 {
26 type: 'object',
27 properties: {
28 eventHandlerPrefix: {type: 'string'},
29 eventHandlerPropPrefix: {type: 'string'},
30 checkLocalVariables: {type: 'boolean'}
31 },
32 additionalProperties: false
33 }, {
34 type: 'object',
35 properties: {
36 eventHandlerPrefix: {type: 'string'},
37 eventHandlerPropPrefix: {
38 type: 'boolean',
39 enum: [false]
40 },
41 checkLocalVariables: {type: 'boolean'}
42 },
43 additionalProperties: false
44 }, {
45 type: 'object',
46 properties: {
47 eventHandlerPrefix: {
48 type: 'boolean',
49 enum: [false]
50 },
51 eventHandlerPropPrefix: {type: 'string'},
52 checkLocalVariables: {type: 'boolean'}
53 },
54 additionalProperties: false
55 }, {
56 type: 'object',
57 properties: {
58 checkLocalVariables: {type: 'boolean'}
59 },
60 additionalProperties: false
61 }
62 ]
63 }]
64 },
65
66 create(context) {
67 function isPrefixDisabled(prefix) {
68 return prefix === false;
69 }
70
71 const configuration = context.options[0] || {};
72
73 const eventHandlerPrefix = isPrefixDisabled(configuration.eventHandlerPrefix)
74 ? null
75 : configuration.eventHandlerPrefix || 'handle';
76 const eventHandlerPropPrefix = isPrefixDisabled(configuration.eventHandlerPropPrefix)
77 ? null
78 : configuration.eventHandlerPropPrefix || 'on';
79
80 const EVENT_HANDLER_REGEX = !eventHandlerPrefix
81 ? null
82 : new RegExp(`^((props\\.${eventHandlerPropPrefix || ''})|((.*\\.)?${eventHandlerPrefix}))[A-Z].*$`);
83 const PROP_EVENT_HANDLER_REGEX = !eventHandlerPropPrefix
84 ? null
85 : new RegExp(`^(${eventHandlerPropPrefix}[A-Z].*|ref)$`);
86
87 const checkLocal = !!configuration.checkLocalVariables;
88
89 return {
90 JSXAttribute(node) {
91 if (!node.value || !node.value.expression || (!checkLocal && !node.value.expression.object)) {
92 return;
93 }
94
95 const propKey = typeof node.name === 'object' ? node.name.name : node.name;
96 const propValue = context.getSourceCode().getText(node.value.expression).replace(/^this\.|.*::/, '');
97
98 if (propKey === 'ref') {
99 return;
100 }
101
102 const propIsEventHandler = PROP_EVENT_HANDLER_REGEX && PROP_EVENT_HANDLER_REGEX.test(propKey);
103 const propFnIsNamedCorrectly = EVENT_HANDLER_REGEX && EVENT_HANDLER_REGEX.test(propValue);
104
105 if (
106 propIsEventHandler
107 && propFnIsNamedCorrectly !== null
108 && !propFnIsNamedCorrectly
109 ) {
110 context.report({
111 node,
112 message: `Handler function for ${propKey} prop key must begin with '${eventHandlerPrefix}'`
113 });
114 } else if (
115 propFnIsNamedCorrectly
116 && propIsEventHandler !== null
117 && !propIsEventHandler
118 ) {
119 context.report({
120 node,
121 message: `Prop key for ${propValue} must begin with '${eventHandlerPropPrefix}'`
122 });
123 }
124 }
125 };
126 }
127};