diff --git a/docs/rules/README.md b/docs/rules/README.md
index 6b9680a97..d00e5478b 100644
--- a/docs/rules/README.md
+++ b/docs/rules/README.md
@@ -263,7 +263,6 @@ Enforce all the rules in this category, as well as all higher priority rules, wi
|:--------|:------------|:---|
| [vue/attributes-order](./attributes-order.md) | enforce order of attributes | :wrench: |
| [vue/component-tags-order](./component-tags-order.md) | enforce order of component top-level elements | |
-| [vue/html-button-has-type](./html-button-has-type.md) | disallow usage of button without an explicit type attribute | |
| [vue/no-lone-template](./no-lone-template.md) | disallow unnecessary `` | |
| [vue/no-multiple-slot-args](./no-multiple-slot-args.md) | disallow to pass multiple arguments to scoped slots | |
| [vue/no-v-html](./no-v-html.md) | disallow use of v-html to prevent XSS attack | |
@@ -290,6 +289,7 @@ For example:
| [vue/block-tag-newline](./block-tag-newline.md) | enforce line breaks after opening and before closing block-level tags | :wrench: |
| [vue/component-name-in-template-casing](./component-name-in-template-casing.md) | enforce specific casing for the component naming style in template | :wrench: |
| [vue/custom-event-name-casing](./custom-event-name-casing.md) | enforce specific casing for custom event name | |
+| [vue/html-button-has-type](./html-button-has-type.md) | disallow usage of button without an explicit type attribute | |
| [vue/html-comment-content-newline](./html-comment-content-newline.md) | enforce unified line brake in HTML comments | :wrench: |
| [vue/html-comment-content-spacing](./html-comment-content-spacing.md) | enforce unified spacing in HTML comments | :wrench: |
| [vue/html-comment-indent](./html-comment-indent.md) | enforce consistent indentation in HTML comments | :wrench: |
diff --git a/docs/rules/html-button-has-type.md b/docs/rules/html-button-has-type.md
index 580e6fd43..af81eb9ed 100644
--- a/docs/rules/html-button-has-type.md
+++ b/docs/rules/html-button-has-type.md
@@ -9,9 +9,8 @@ description: disallow usage of button without an explicit type attribute
> disallow usage of button without an explicit type attribute
- :exclamation: ***This rule has not been released yet.***
-- :gear: This rule is included in `"plugin:vue/recommended"`.
-Forgetting the type attribute on a button defaults it to being a submit type.
+Forgetting the type attribute on a button defaults it to being a submit type.
This is nearly never what is intended, especially in your average one-page application.
## :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
```
- `button` ... ``
- - `true` (default) ... allow value `button`.
- - `false"` ... disallow value `button`.
-- `sumbit` ... ``
- - `true` (default) ... allow value `submit`.
- - `false"` ... disallow value `submit`.
+ - `true` (default) ... allow value `button`.
+ - `false"` ... disallow value `button`.
+- `submit` ... ``
+ - `true` (default) ... allow value `submit`.
+ - `false"` ... disallow value `submit`.
- `reset` ... ``
- - `true` (default) ... allow value `reset`.
- - `false"` ... disallow value `reset`.
+ - `true` (default) ... allow value `reset`.
+ - `false"` ... disallow value `reset`.
## :mag: Implementation
diff --git a/lib/configs/recommended.js b/lib/configs/recommended.js
index c425c60ef..61781f754 100644
--- a/lib/configs/recommended.js
+++ b/lib/configs/recommended.js
@@ -8,7 +8,6 @@ module.exports = {
rules: {
'vue/attributes-order': 'warn',
'vue/component-tags-order': 'warn',
- 'vue/html-button-has-type': 'warn',
'vue/no-lone-template': 'warn',
'vue/no-multiple-slot-args': 'warn',
'vue/no-v-html': 'warn',
diff --git a/lib/rules/html-button-has-type.js b/lib/rules/html-button-has-type.js
index fdf06b212..4ef7d2feb 100644
--- a/lib/rules/html-button-has-type.js
+++ b/lib/rules/html-button-has-type.js
@@ -24,8 +24,9 @@ module.exports = {
meta: {
type: 'suggestion',
docs: {
- description: 'disallow usage of button without an explicit type attribute',
- category: 'recommended',
+ description:
+ 'disallow usage of button without an explicit type attribute',
+ categories: null,
url: 'https://eslint.vuejs.org/rules/html-button-has-type.html'
},
fixable: null,
@@ -51,16 +52,42 @@ module.exports = {
],
messages: {
missingTypeAttribute: 'Missing an explicit type attribute for button.',
- invalidTypeAttribute: '{{value}} is an invalid value for button type attribute.',
- forbiddenTypeAttribute: '{{value}} is a forbidden value for button type attribute.',
+ invalidTypeAttribute:
+ '{{value}} is an invalid value for button type attribute.',
+ forbiddenTypeAttribute:
+ '{{value}} is a forbidden value for button type attribute.',
emptyTypeAttribute: 'A value must be set for button type attribute.'
}
},
- create: function (context) {
+ /**
+ * @param {RuleContext} context - The rule context.
+ * @returns {RuleListener} AST event handlers.
+ */
+ create(context) {
+ /**
+ * @typedef {object} Configuration
+ * @property {boolean} button
+ * @property {boolean} submit
+ * @property {boolean} reset
+ */
+ /** @type {Configuration} */
const configuration = Object.assign({}, optionDefaults, context.options[0])
- function report (node, messageId, data) {
+ /**
+ *
+ * @param {string} type
+ * @returns {type is 'button' | 'submit' | 'reset'}
+ */
+ function isButtonType(type) {
+ return type === 'button' || type === 'submit' || type === 'reset'
+ }
+ /**
+ * @param {ASTNode} node
+ * @param {string} messageId
+ * @param {any} [data]
+ */
+ function report(node, messageId, data) {
context.report({
node,
messageId,
@@ -68,34 +95,51 @@ module.exports = {
})
}
- function validateAttribute (attribute) {
+ /**
+ * @param {VAttribute} attribute
+ */
+ function validateAttribute(attribute) {
const value = attribute.value
+ if (!value || !value.value) {
+ report(value || attribute, 'emptyTypeAttribute')
+ return
+ }
+
const strValue = value.value
- if (strValue === '') {
- report(value, 'emptyTypeAttribute')
- } else if (!(strValue in configuration)) {
+ if (!isButtonType(strValue)) {
report(value, 'invalidTypeAttribute', { value: strValue })
} else if (!configuration[strValue]) {
report(value, 'forbiddenTypeAttribute', { value: strValue })
}
}
- function validateDirective (directive) {
+ /**
+ * @param {VDirective} directive
+ */
+ function validateDirective(directive) {
const value = directive.value
- if (!value.expression) {
- report(value, 'emptyTypeAttribute')
+ if (!value || !value.expression) {
+ report(value || directive, 'emptyTypeAttribute')
}
}
return utils.defineTemplateBodyVisitor(context, {
- "VElement[name='button']" (node) {
- if (utils.hasAttribute(node, 'type')) {
- validateAttribute(utils.getAttribute(node, 'type'))
- } else if (utils.hasDirective(node, 'bind', 'type')) {
- validateDirective(utils.getDirective(node, 'bind', 'type'))
- } else {
- report(node, 'missingTypeAttribute')
+ /**
+ * @param {VElement} node
+ */
+ "VElement[rawName='button']"(node) {
+ const typeAttr = utils.getAttribute(node, 'type')
+ if (typeAttr) {
+ validateAttribute(typeAttr)
+ return
}
+ const typeDir = utils.getDirective(node, 'bind', 'type')
+ if (typeDir) {
+ validateDirective(typeDir)
+ return
+ }
+
+ report(node.startTag, 'missingTypeAttribute')
}
})
}
diff --git a/tests/lib/rules/html-button-has-type.js b/tests/lib/rules/html-button-has-type.js
index 4fd40d2ad..d6f40487f 100644
--- a/tests/lib/rules/html-button-has-type.js
+++ b/tests/lib/rules/html-button-has-type.js
@@ -8,20 +8,19 @@
// Requirements
// ------------------------------------------------------------------------------
-var rule = require('../../../lib/rules/html-button-has-type')
+const rule = require('../../../lib/rules/html-button-has-type')
-var RuleTester = require('eslint').RuleTester
+const RuleTester = require('eslint').RuleTester
// ------------------------------------------------------------------------------
// Tests
// ------------------------------------------------------------------------------
-var ruleTester = new RuleTester({
+const ruleTester = new RuleTester({
parser: require.resolve('vue-eslint-parser'),
parserOptions: { ecmaVersion: 2015 }
})
ruleTester.run('html-button-has-type', rule, {
-
valid: [
{
filename: 'test.vue',
@@ -37,7 +36,8 @@ ruleTester.run('html-button-has-type', rule, {
},
{
filename: 'test.vue',
- code: ''
+ code:
+ ''
},
{
filename: 'test.vue',
@@ -54,6 +54,10 @@ ruleTester.run('html-button-has-type', rule, {
{
filename: 'test.vue',
code: ''
+ },
+ {
+ filename: 'test.vue',
+ code: ``
}
],
@@ -61,47 +65,75 @@ ruleTester.run('html-button-has-type', rule, {
{
filename: 'test.vue',
code: '',
- errors: [{
- message: 'Missing an explicit type attribute for button.'
- }]
+ errors: [
+ {
+ message: 'Missing an explicit type attribute for button.',
+ column: 11
+ }
+ ]
},
{
filename: 'test.vue',
code: '',
- errors: [{
- message: 'A value must be set for button type attribute.'
- }]
+ errors: [
+ {
+ message: 'A value must be set for button type attribute.',
+ column: 24
+ }
+ ]
+ },
+ {
+ filename: 'test.vue',
+ code: '',
+ errors: [
+ {
+ message: 'A value must be set for button type attribute.',
+ column: 19
+ }
+ ]
},
{
filename: 'test.vue',
code: '',
- errors: [{
- message: 'foo is an invalid value for button type attribute.'
- }]
+ errors: [
+ {
+ message: 'foo is an invalid value for button type attribute.',
+ column: 24
+ }
+ ]
},
{
filename: 'test.vue',
options: [{ button: false }],
code: '',
- errors: [{
- message: 'button is a forbidden value for button type attribute.'
- }]
+ errors: [
+ {
+ message: 'button is a forbidden value for button type attribute.',
+ column: 24
+ }
+ ]
},
{
filename: 'test.vue',
options: [{ submit: false }],
code: '',
- errors: [{
- message: 'submit is a forbidden value for button type attribute.'
- }]
+ errors: [
+ {
+ message: 'submit is a forbidden value for button type attribute.',
+ column: 24
+ }
+ ]
},
{
filename: 'test.vue',
options: [{ reset: false }],
code: '',
- errors: [{
- message: 'reset is a forbidden value for button type attribute.'
- }]
+ errors: [
+ {
+ message: 'reset is a forbidden value for button type attribute.',
+ column: 24
+ }
+ ]
},
{
filename: 'test.vue',
@@ -113,13 +145,19 @@ ruleTester.run('html-button-has-type', rule, {
`,
errors: [
{
- message: 'button is a forbidden value for button type attribute.'
+ message: 'button is a forbidden value for button type attribute.',
+ line: 2,
+ column: 30
},
{
- message: 'submit is a forbidden value for button type attribute.'
+ message: 'submit is a forbidden value for button type attribute.',
+ line: 3,
+ column: 30
},
{
- message: 'reset is a forbidden value for button type attribute.'
+ message: 'reset is a forbidden value for button type attribute.',
+ line: 4,
+ column: 30
}
]
},
@@ -135,25 +173,34 @@ ruleTester.run('html-button-has-type', rule, {
`,
errors: [
{
- message: 'reset is a forbidden value for button type attribute.'
+ message: 'reset is a forbidden value for button type attribute.',
+ line: 4,
+ column: 30
},
{
- message: 'A value must be set for button type attribute.'
+ message: 'A value must be set for button type attribute.',
+ line: 5,
+ column: 30
},
{
- message: 'foo is an invalid value for button type attribute.'
+ message: 'foo is an invalid value for button type attribute.',
+ line: 6,
+ column: 30
}
]
},
{
filename: 'test.vue',
- code: '',
+ code:
+ '',
errors: [
{
- message: 'Missing an explicit type attribute for button.'
+ message: 'Missing an explicit type attribute for button.',
+ column: 11
},
{
- message: 'Missing an explicit type attribute for button.'
+ message: 'Missing an explicit type attribute for button.',
+ column: 39
}
]
},
@@ -162,7 +209,18 @@ ruleTester.run('html-button-has-type', rule, {
code: ``,
errors: [
{
- message: 'A value must be set for button type attribute.'
+ message: 'A value must be set for button type attribute.',
+ column: 25
+ }
+ ]
+ },
+ {
+ filename: 'test.vue',
+ code: ``,
+ errors: [
+ {
+ message: 'A value must be set for button type attribute.',
+ column: 19
}
]
}