diff --git a/docs/rules/attribute-hyphenation.md b/docs/rules/attribute-hyphenation.md
new file mode 100644
index 000000000..7d83c9fad
--- /dev/null
+++ b/docs/rules/attribute-hyphenation.md
@@ -0,0 +1,53 @@
+# Define if attributes on cusom components can be hyphened. (attribute-hyphenation)
+
+## :wrench: Options
+
+Default casing is set to `always`
+
+```
+'vue/attribute-hyphenation': [2, 'always'|'never']
+```
+
+### `"always"` - Use hyphenated name. (It errors on upper case letters.)
+
+:+1: Examples of **correct** code`:
+
+```html
+
+
+
+
+
+```
+
+:-1: Examples of **incorrect** code`:
+
+```html
+
+
+
+
+
+```
+
+### `"never"` - Don't use hyphenated name. (It errors on hyphens except `data-` and `aria-`.)
+
+:+1: Examples of **correct** code`:
+
+```html
+
+
+
+
+
+```
+
+:-1: Examples of **incorrect** code`:
+
+```html
+
+
+
+
+
+```
diff --git a/lib/rules/attribute-hyphenation.js b/lib/rules/attribute-hyphenation.js
new file mode 100644
index 000000000..177b7a10a
--- /dev/null
+++ b/lib/rules/attribute-hyphenation.js
@@ -0,0 +1,76 @@
+/**
+ * @fileoverview Define a style for the props casing in templates.
+ * @author Armano
+ */
+'use strict'
+
+const utils = require('../utils')
+const casing = require('../utils/casing')
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+function create (context) {
+ const sourceCode = context.getSourceCode()
+ const options = context.options[0]
+ const useHyphenated = options !== 'never'
+
+ const caseConverter = casing.getConverter(useHyphenated ? 'kebab-case' : 'camelCase')
+
+ function reportIssue (node, name) {
+ const text = sourceCode.getText(node.key)
+
+ context.report({
+ node: node.key,
+ loc: node.loc,
+ message: useHyphenated ? "Attribute '{{text}}' must be hyphenated." : "Attribute '{{text}}' cann't be hyphenated.",
+ data: {
+ text
+ },
+ fix: fixer => fixer.replaceText(node.key, text.replace(name, caseConverter(name)))
+ })
+ }
+
+ function isIgnoredAttribute (value) {
+ if (value.indexOf('data-') !== -1 || value.indexOf('aria-') !== -1) {
+ return true
+ }
+ return useHyphenated ? value.toLowerCase() === value : !/-/.test(value)
+ }
+
+ // ----------------------------------------------------------------------
+ // Public
+ // ----------------------------------------------------------------------
+
+ utils.registerTemplateBodyVisitor(context, {
+ VAttribute (node) {
+ if (!utils.isCustomComponent(node.parent.parent)) return
+
+ const name = !node.directive ? node.key.rawName : node.key.name === 'bind' ? node.key.raw.argument : false
+ if (!name || isIgnoredAttribute(name)) return
+
+ reportIssue(node, name)
+ }
+ })
+
+ return {}
+}
+
+module.exports = {
+ meta: {
+ docs: {
+ description: 'Define a style for the props casing in templates.',
+ category: 'Stylistic Issues',
+ recommended: false
+ },
+ fixable: 'code',
+ schema: [
+ {
+ enum: ['always', 'never']
+ }
+ ]
+ },
+
+ create
+}
diff --git a/package-lock.json b/package-lock.json
index 3496e7ced..21b7a2a59 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -3198,9 +3198,9 @@
"dev": true
},
"vue-eslint-parser": {
- "version": "2.0.0-beta.2",
- "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-2.0.0-beta.2.tgz",
- "integrity": "sha512-bcoX+jDpF7xY3nbPK9W6yBiH7I0WOFA1SU0LnYkusZzY6ZRGbJWOiyNpl1TWAkNmLH0Az2uGVVnzsBVB1aSl6w==",
+ "version": "2.0.0-beta.3",
+ "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-2.0.0-beta.3.tgz",
+ "integrity": "sha512-/h9CORTgmIPEsu6PKXY5IbNzcwCkb1aV5mpnSojwNcShdWi+VKXi95HMnpiWuLAa3M6Hh2tMENwR/twyrX3h1w==",
"requires": {
"debug": "2.6.8",
"eslint-scope": "3.7.1",
diff --git a/package.json b/package.json
index c6bc95c0a..2c1457f89 100644
--- a/package.json
+++ b/package.json
@@ -45,7 +45,7 @@
},
"dependencies": {
"requireindex": "^1.1.0",
- "vue-eslint-parser": "^2.0.0-beta.2"
+ "vue-eslint-parser": "^2.0.0-beta.3"
},
"devDependencies": {
"@types/node": "^4.2.16",
diff --git a/tests/lib/rules/attribute-hyphenation.js b/tests/lib/rules/attribute-hyphenation.js
new file mode 100644
index 000000000..b5dabe4a0
--- /dev/null
+++ b/tests/lib/rules/attribute-hyphenation.js
@@ -0,0 +1,116 @@
+/**
+ * @fileoverview Define a style for the props casing in templates.
+ * @author Armano
+ */
+'use strict'
+
+// ------------------------------------------------------------------------------
+// Requirements
+// ------------------------------------------------------------------------------
+
+const rule = require('../../../lib/rules/attribute-hyphenation')
+
+const RuleTester = require('eslint').RuleTester
+
+// ------------------------------------------------------------------------------
+// Tests
+// ------------------------------------------------------------------------------
+
+const ruleTester = new RuleTester({
+ parser: 'vue-eslint-parser',
+ parserOptions: { ecmaVersion: 2015 }
+})
+
+ruleTester.run('attribute-hyphenation', rule, {
+
+ valid: [
+ {
+ filename: 'test.vue',
+ code: ''
+ },
+ {
+ filename: 'test.vue',
+ code: '
',
+ options: ['always']
+ },
+ {
+ filename: 'test.vue',
+ code: '
',
+ options: ['never']
+ },
+ {
+ filename: 'test.vue',
+ code: '',
+ options: ['never']
+ }
+ ],
+
+ invalid: [
+ {
+ filename: 'test.vue',
+ code: '
',
+ output: '
',
+ options: ['never'],
+ errors: [{
+ message: "Attribute 'my-prop' cann't be hyphenated.",
+ type: 'VIdentifier',
+ line: 1
+ }]
+ },
+ {
+ filename: 'test.vue',
+ code: '
',
+ output: '
',
+ options: ['always'],
+ errors: [{
+ message: "Attribute 'MyProp' must be hyphenated.",
+ type: 'VIdentifier',
+ line: 1
+ }]
+ },
+ {
+ filename: 'test.vue',
+ code: '
',
+ output: '
',
+ options: ['never'],
+ errors: [{
+ message: "Attribute ':my-prop' cann't be hyphenated.",
+ type: 'VDirectiveKey',
+ line: 1
+ }]
+ },
+ {
+ filename: 'test.vue',
+ code: '
',
+ output: '
',
+ options: ['always'],
+ errors: [{
+ message: "Attribute ':MyProp' must be hyphenated.",
+ type: 'VDirectiveKey',
+ line: 1
+ }]
+ },
+ {
+ filename: 'test.vue',
+ code: '
',
+ output: '
',
+ options: ['never'],
+ errors: [{
+ message: "Attribute 'v-bind:my-prop' cann't be hyphenated.",
+ type: 'VDirectiveKey',
+ line: 1
+ }]
+ },
+ {
+ filename: 'test.vue',
+ code: '
',
+ output: '
',
+ options: ['always'],
+ errors: [{
+ message: "Attribute 'v-bind:MyProp' must be hyphenated.",
+ type: 'VDirectiveKey',
+ line: 1
+ }]
+ }
+ ]
+})