diff --git a/README.md b/README.md
index 303f0b341..b01a5b728 100644
--- a/README.md
+++ b/README.md
@@ -147,7 +147,7 @@ The `--fix` option on the command line automatically fixes problems reported by
| :wrench: | [no-multi-spaces](./docs/rules/no-multi-spaces.md) | disallow multiple spaces |
| :wrench: | [v-bind-style](./docs/rules/v-bind-style.md) | enforce `v-bind` directive style |
| :wrench: | [v-on-style](./docs/rules/v-on-style.md) | enforce `v-on` directive style |
-
+| :wrench: | [attribute-order](./docs/rules/attribute-order.md) | enforce alphabetical ordering of properties and prioritizing vue-specific attributes |
### Variables
diff --git a/docs/rules/attribute-order.md b/docs/rules/attribute-order.md
new file mode 100644
index 000000000..47ec90965
--- /dev/null
+++ b/docs/rules/attribute-order.md
@@ -0,0 +1,77 @@
+# enforce alphabetical ordering of properties and prioritizing vue-specific attributes (attribute-order)
+
+## Rule Details
+
+:+1: Examples of **correct** code`:
+
+```html
+
+
+
+
+```
+
+```html
+
+
+
+
+```
+
+```html
+
+
+
+
+```
+
+:-1: Examples of **incorrect** code`:
+
+```html
+
+
+
+
+```
+
+```html
+
+
+
+
+```
+
+```html
+
+
+
+
+```
diff --git a/lib/recommended-rules.js b/lib/recommended-rules.js
index c8f6abf1c..5aaa0d7f4 100644
--- a/lib/recommended-rules.js
+++ b/lib/recommended-rules.js
@@ -5,6 +5,7 @@
*/
module.exports = {
"vue/attribute-hyphenation": "off",
+ "vue/attribute-order": "off",
"vue/html-end-tags": "off",
"vue/html-no-self-closing": "off",
"vue/html-quotes": "off",
diff --git a/lib/rules/attribute-order.js b/lib/rules/attribute-order.js
new file mode 100644
index 000000000..d7b589cc9
--- /dev/null
+++ b/lib/rules/attribute-order.js
@@ -0,0 +1,76 @@
+/**
+ * @fileoverview enforce alphabetical ordering of properties and prioritizing vue-specific attributes
+ * @author Erin Depew
+ */
+'use strict'
+const utils = require('../utils')
+
+// ------------------------------------------------------------------------------
+// Rule Definition
+// ------------------------------------------------------------------------------
+
+function getName (attribute) {
+ if (!attribute.directive) {
+ return attribute.key.name
+ }
+ if (attribute.key.name === 'bind') {
+ return attribute.key.argument || null
+ }
+ if (attribute.directive) {
+ return '@' + attribute.key.argument
+ }
+ return null
+}
+
+function create (context) {
+ const sourceCode = context.getSourceCode()
+ let attributeList
+ let previousNode
+
+ function reportIssue (node, previousNode, name) {
+ const currentNode = sourceCode.getText(node.key)
+ const prevNode = sourceCode.getText(previousNode.key)
+
+ context.report({
+ node: node.key,
+ loc: node.loc,
+ message: `Attribute ${currentNode} must go before ${prevNode}.`,
+ data: {
+ currentNode
+ }
+ })
+ }
+
+ return utils.defineTemplateBodyVisitor(context, {
+ 'VStartTag' () {
+ attributeList = []
+ },
+ 'VAttribute' (node) {
+ const name = getName(node)
+
+ if (attributeList.length && attributeList[attributeList.length - 1] < name && name) {
+ attributeList.push(name)
+ previousNode = node
+ } else if (attributeList.length === 0 && name) {
+ attributeList.push(name)
+ previousNode = node
+ } else {
+ reportIssue(node, previousNode, name)
+ }
+ }
+ })
+}
+
+module.exports = {
+ meta: {
+ docs: {
+ description: 'enforce alphabetical ordering of properties and prioritizing vue-specific attributes',
+ category: 'Fill me in',
+ recommended: false
+ },
+ fixable: null,
+ schema: []
+ },
+
+ create
+}
diff --git a/tests/lib/rules/attribute-order.js b/tests/lib/rules/attribute-order.js
new file mode 100644
index 000000000..e7884fcc3
--- /dev/null
+++ b/tests/lib/rules/attribute-order.js
@@ -0,0 +1,118 @@
+/**
+ * @fileoverview enforce alphabetical ordering of properties and prioritizing vue-specific attributes
+ * @author Erin Depew
+ */
+'use strict'
+
+// ------------------------------------------------------------------------------
+// Requirements
+// ------------------------------------------------------------------------------
+
+var rule = require('../../../lib/rules/attribute-order')
+
+var RuleTester = require('eslint').RuleTester
+
+// ------------------------------------------------------------------------------
+// Tests
+// ------------------------------------------------------------------------------
+
+var tester = new RuleTester({
+ parser: 'vue-eslint-parser',
+ parserOptions: { ecmaVersion: 2015 }
+})
+
+tester.run('attribute-order', rule, {
+ valid: [
+ {
+ filename: 'test.vue',
+ code: ''
+ },
+ {
+ filename: 'test.vue',
+ code: '
'
+ },
+ {
+ filename: 'test.vue',
+ code: '
'
+ },
+ {
+ filename: 'test.vue',
+ code: '
'
+ },
+ {
+ filename: 'test.vue',
+ code: '
'
+ },
+ {
+ filename: 'test.vue',
+ code: '
'
+ },
+ {
+ filename: 'test.vue',
+ code: '
'
+ },
+ {
+ filename: 'test.vue',
+ code: '
'
+ }
+ ],
+
+ invalid: [
+ {
+ filename: 'test.vue',
+ code: '
',
+ errors: [{
+ message: 'Attribute aria-test must go before data-id.',
+ type: 'VIdentifier'
+ }]
+ },
+ {
+ filename: 'test.vue',
+ code: '
',
+ errors: [{
+ message: 'Attribute v-if must go before myProp.',
+ type: 'VDirectiveKey'
+ }]
+ },
+ {
+ filename: 'test.vue',
+ code: '
',
+ errors: [{
+ message: 'Attribute v-if must go before :myProp.',
+ type: 'VDirectiveKey'
+ }]
+ },
+ {
+ filename: 'test.vue',
+ code: '
',
+ errors: [{
+ message: 'Attribute aria-test must go before :class.',
+ type: 'VIdentifier'
+ }]
+ },
+ {
+ filename: 'test.vue',
+ code: '
',
+ errors: [{
+ message: 'Attribute :class must go before myProp.',
+ type: 'VDirectiveKey'
+ }]
+ },
+ {
+ filename: 'test.vue',
+ code: '
',
+ errors: [{
+ message: 'Attribute @click must go before :class.',
+ type: 'VDirectiveKey'
+ }]
+ },
+ {
+ filename: 'test.vue',
+ code: '
',
+ errors: [{
+ message: 'Attribute @click must go before v-for.',
+ type: 'VDirectiveKey'
+ }]
+ }
+ ]
+})