1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 | 'use strict';
|
7 |
|
8 | const docsUrl = require('../util/docsUrl');
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 | module.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 | };
|