Skip to content

Commit 5129cef

Browse files
authored
Change vue/html-button-has-type rule (#1432)
- Enable type checking - Format - Exclude from recommended category - Exclude `Button` component from check
1 parent 9a9461a commit 5129cef

File tree

5 files changed

+164
-64
lines changed

5 files changed

+164
-64
lines changed

docs/rules/README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,6 @@ Enforce all the rules in this category, as well as all higher priority rules, wi
263263
|:--------|:------------|:---|
264264
| [vue/attributes-order](./attributes-order.md) | enforce order of attributes | :wrench: |
265265
| [vue/component-tags-order](./component-tags-order.md) | enforce order of component top-level elements | |
266-
| [vue/html-button-has-type](./html-button-has-type.md) | disallow usage of button without an explicit type attribute | |
267266
| [vue/no-lone-template](./no-lone-template.md) | disallow unnecessary `<template>` | |
268267
| [vue/no-multiple-slot-args](./no-multiple-slot-args.md) | disallow to pass multiple arguments to scoped slots | |
269268
| [vue/no-v-html](./no-v-html.md) | disallow use of v-html to prevent XSS attack | |
@@ -290,6 +289,7 @@ For example:
290289
| [vue/block-tag-newline](./block-tag-newline.md) | enforce line breaks after opening and before closing block-level tags | :wrench: |
291290
| [vue/component-name-in-template-casing](./component-name-in-template-casing.md) | enforce specific casing for the component naming style in template | :wrench: |
292291
| [vue/custom-event-name-casing](./custom-event-name-casing.md) | enforce specific casing for custom event name | |
292+
| [vue/html-button-has-type](./html-button-has-type.md) | disallow usage of button without an explicit type attribute | |
293293
| [vue/html-comment-content-newline](./html-comment-content-newline.md) | enforce unified line brake in HTML comments | :wrench: |
294294
| [vue/html-comment-content-spacing](./html-comment-content-spacing.md) | enforce unified spacing in HTML comments | :wrench: |
295295
| [vue/html-comment-indent](./html-comment-indent.md) | enforce consistent indentation in HTML comments | :wrench: |

docs/rules/html-button-has-type.md

+8-9
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,8 @@ description: disallow usage of button without an explicit type attribute
99
> disallow usage of button without an explicit type attribute
1010
1111
- :exclamation: <badge text="This rule has not been released yet." vertical="middle" type="error"> ***This rule has not been released yet.*** </badge>
12-
- :gear: This rule is included in `"plugin:vue/recommended"`.
1312

14-
Forgetting the type attribute on a button defaults it to being a submit type.
13+
Forgetting the type attribute on a button defaults it to being a submit type.
1514
This is nearly never what is intended, especially in your average one-page application.
1615

1716
## :book: Rule Details
@@ -49,14 +48,14 @@ This rule aims to warn if no type or an invalid type is used on a button type at
4948
```
5049

5150
- `button` ... `<button type="button"></button>`
52-
- `true` (default) ... allow value `button`.
53-
- `false"` ... disallow value `button`.
54-
- `sumbit` ... `<button type="sumbit"></button>`
55-
- `true` (default) ... allow value `submit`.
56-
- `false"` ... disallow value `submit`.
51+
- `true` (default) ... allow value `button`.
52+
- `false"` ... disallow value `button`.
53+
- `submit` ... `<button type="submit"></button>`
54+
- `true` (default) ... allow value `submit`.
55+
- `false"` ... disallow value `submit`.
5756
- `reset` ... `<button type="reset"></button>`
58-
- `true` (default) ... allow value `reset`.
59-
- `false"` ... disallow value `reset`.
57+
- `true` (default) ... allow value `reset`.
58+
- `false"` ... disallow value `reset`.
6059

6160
## :mag: Implementation
6261

lib/configs/recommended.js

-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ module.exports = {
88
rules: {
99
'vue/attributes-order': 'warn',
1010
'vue/component-tags-order': 'warn',
11-
'vue/html-button-has-type': 'warn',
1211
'vue/no-lone-template': 'warn',
1312
'vue/no-multiple-slot-args': 'warn',
1413
'vue/no-v-html': 'warn',

lib/rules/html-button-has-type.js

+64-20
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,9 @@ module.exports = {
2424
meta: {
2525
type: 'suggestion',
2626
docs: {
27-
description: 'disallow usage of button without an explicit type attribute',
28-
category: 'recommended',
27+
description:
28+
'disallow usage of button without an explicit type attribute',
29+
categories: null,
2930
url: 'https://eslint.vuejs.org/rules/html-button-has-type.html'
3031
},
3132
fixable: null,
@@ -51,51 +52,94 @@ module.exports = {
5152
],
5253
messages: {
5354
missingTypeAttribute: 'Missing an explicit type attribute for button.',
54-
invalidTypeAttribute: '{{value}} is an invalid value for button type attribute.',
55-
forbiddenTypeAttribute: '{{value}} is a forbidden value for button type attribute.',
55+
invalidTypeAttribute:
56+
'{{value}} is an invalid value for button type attribute.',
57+
forbiddenTypeAttribute:
58+
'{{value}} is a forbidden value for button type attribute.',
5659
emptyTypeAttribute: 'A value must be set for button type attribute.'
5760
}
5861
},
5962

60-
create: function (context) {
63+
/**
64+
* @param {RuleContext} context - The rule context.
65+
* @returns {RuleListener} AST event handlers.
66+
*/
67+
create(context) {
68+
/**
69+
* @typedef {object} Configuration
70+
* @property {boolean} button
71+
* @property {boolean} submit
72+
* @property {boolean} reset
73+
*/
74+
/** @type {Configuration} */
6175
const configuration = Object.assign({}, optionDefaults, context.options[0])
6276

63-
function report (node, messageId, data) {
77+
/**
78+
*
79+
* @param {string} type
80+
* @returns {type is 'button' | 'submit' | 'reset'}
81+
*/
82+
function isButtonType(type) {
83+
return type === 'button' || type === 'submit' || type === 'reset'
84+
}
85+
/**
86+
* @param {ASTNode} node
87+
* @param {string} messageId
88+
* @param {any} [data]
89+
*/
90+
function report(node, messageId, data) {
6491
context.report({
6592
node,
6693
messageId,
6794
data
6895
})
6996
}
7097

71-
function validateAttribute (attribute) {
98+
/**
99+
* @param {VAttribute} attribute
100+
*/
101+
function validateAttribute(attribute) {
72102
const value = attribute.value
103+
if (!value || !value.value) {
104+
report(value || attribute, 'emptyTypeAttribute')
105+
return
106+
}
107+
73108
const strValue = value.value
74-
if (strValue === '') {
75-
report(value, 'emptyTypeAttribute')
76-
} else if (!(strValue in configuration)) {
109+
if (!isButtonType(strValue)) {
77110
report(value, 'invalidTypeAttribute', { value: strValue })
78111
} else if (!configuration[strValue]) {
79112
report(value, 'forbiddenTypeAttribute', { value: strValue })
80113
}
81114
}
82115

83-
function validateDirective (directive) {
116+
/**
117+
* @param {VDirective} directive
118+
*/
119+
function validateDirective(directive) {
84120
const value = directive.value
85-
if (!value.expression) {
86-
report(value, 'emptyTypeAttribute')
121+
if (!value || !value.expression) {
122+
report(value || directive, 'emptyTypeAttribute')
87123
}
88124
}
89125

90126
return utils.defineTemplateBodyVisitor(context, {
91-
"VElement[name='button']" (node) {
92-
if (utils.hasAttribute(node, 'type')) {
93-
validateAttribute(utils.getAttribute(node, 'type'))
94-
} else if (utils.hasDirective(node, 'bind', 'type')) {
95-
validateDirective(utils.getDirective(node, 'bind', 'type'))
96-
} else {
97-
report(node, 'missingTypeAttribute')
127+
/**
128+
* @param {VElement} node
129+
*/
130+
"VElement[rawName='button']"(node) {
131+
const typeAttr = utils.getAttribute(node, 'type')
132+
if (typeAttr) {
133+
validateAttribute(typeAttr)
134+
return
98135
}
136+
const typeDir = utils.getDirective(node, 'bind', 'type')
137+
if (typeDir) {
138+
validateDirective(typeDir)
139+
return
140+
}
141+
142+
report(node.startTag, 'missingTypeAttribute')
99143
}
100144
})
101145
}

0 commit comments

Comments
 (0)