1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 | 'use strict';
|
7 |
|
8 | const getProp = require('jsx-ast-utils/getProp');
|
9 | const getLiteralPropValue = require('jsx-ast-utils/getLiteralPropValue');
|
10 | const docsUrl = require('../util/docsUrl');
|
11 | const pragmaUtil = require('../util/pragma');
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 | function isCreateElement(node, context) {
|
18 | const pragma = pragmaUtil.getFromContext(context);
|
19 | return node.callee
|
20 | && node.callee.type === 'MemberExpression'
|
21 | && node.callee.property.name === 'createElement'
|
22 | && node.callee.object
|
23 | && node.callee.object.name === pragma
|
24 | && node.arguments.length > 0;
|
25 | }
|
26 |
|
27 |
|
28 |
|
29 |
|
30 |
|
31 | const optionDefaults = {
|
32 | button: true,
|
33 | submit: true,
|
34 | reset: true
|
35 | };
|
36 |
|
37 | module.exports = {
|
38 | meta: {
|
39 | docs: {
|
40 | description: 'Forbid "button" element without an explicit "type" attribute',
|
41 | category: 'Possible Errors',
|
42 | recommended: false,
|
43 | url: docsUrl('button-has-type')
|
44 | },
|
45 | schema: [{
|
46 | type: 'object',
|
47 | properties: {
|
48 | button: {
|
49 | default: optionDefaults.button,
|
50 | type: 'boolean'
|
51 | },
|
52 | submit: {
|
53 | default: optionDefaults.submit,
|
54 | type: 'boolean'
|
55 | },
|
56 | reset: {
|
57 | default: optionDefaults.reset,
|
58 | type: 'boolean'
|
59 | }
|
60 | },
|
61 | additionalProperties: false
|
62 | }]
|
63 | },
|
64 |
|
65 | create(context) {
|
66 | const configuration = Object.assign({}, optionDefaults, context.options[0]);
|
67 |
|
68 | function reportMissing(node) {
|
69 | context.report({
|
70 | node,
|
71 | message: 'Missing an explicit type attribute for button'
|
72 | });
|
73 | }
|
74 |
|
75 | function checkValue(node, value) {
|
76 | const q = (x) => `"${x}"`;
|
77 | if (!(value in configuration)) {
|
78 | context.report({
|
79 | node,
|
80 | message: `${q(value)} is an invalid value for button type attribute`
|
81 | });
|
82 | } else if (!configuration[value]) {
|
83 | context.report({
|
84 | node,
|
85 | message: `${q(value)} is a forbidden value for button type attribute`
|
86 | });
|
87 | }
|
88 | }
|
89 |
|
90 | return {
|
91 | JSXElement(node) {
|
92 | if (node.openingElement.name.name !== 'button') {
|
93 | return;
|
94 | }
|
95 |
|
96 | const typeProp = getProp(node.openingElement.attributes, 'type');
|
97 |
|
98 | if (!typeProp) {
|
99 | reportMissing(node);
|
100 | return;
|
101 | }
|
102 |
|
103 | if (typeProp.value.type === 'JSXExpressionContainer') {
|
104 | context.report({
|
105 | node: typeProp,
|
106 | message: 'The button type attribute must be specified by a static string'
|
107 | });
|
108 | return;
|
109 | }
|
110 |
|
111 | const propValue = getLiteralPropValue(typeProp);
|
112 | checkValue(node, propValue);
|
113 | },
|
114 | CallExpression(node) {
|
115 | if (!isCreateElement(node, context)) {
|
116 | return;
|
117 | }
|
118 |
|
119 | if (node.arguments[0].type !== 'Literal' || node.arguments[0].value !== 'button') {
|
120 | return;
|
121 | }
|
122 |
|
123 | if (!node.arguments[1] || node.arguments[1].type !== 'ObjectExpression') {
|
124 | reportMissing(node);
|
125 | return;
|
126 | }
|
127 |
|
128 | const props = node.arguments[1].properties;
|
129 | const typeProp = props.find((prop) => prop.key && prop.key.name === 'type');
|
130 |
|
131 | if (!typeProp || typeProp.value.type !== 'Literal') {
|
132 | reportMissing(node);
|
133 | return;
|
134 | }
|
135 |
|
136 | checkValue(node, typeProp.value.value);
|
137 | }
|
138 | };
|
139 | }
|
140 | };
|