diff --git a/lib/rules/attributes-order.js b/lib/rules/attributes-order.js
index 5af387db0..0d9ea9ce8 100644
--- a/lib/rules/attributes-order.js
+++ b/lib/rules/attributes-order.js
@@ -36,6 +36,14 @@ const ATTRS = {
function isVBind(node) {
return Boolean(node && node.directive && node.key.name.name === 'bind')
}
+/**
+ * Check whether the given attribute is `v-model` directive.
+ * @param {VAttribute | VDirective | undefined | null} node
+ * @returns { node is VDirective }
+ */
+function isVModel(node) {
+ return Boolean(node && node.directive && node.key.name.name === 'model')
+}
/**
* Check whether the given attribute is plain attribute.
* @param {VAttribute | VDirective | undefined | null} node
@@ -45,12 +53,12 @@ function isVAttribute(node) {
return Boolean(node && !node.directive)
}
/**
- * Check whether the given attribute is plain attribute or `v-bind` directive.
+ * Check whether the given attribute is plain attribute, `v-bind` directive or `v-model` directive.
* @param {VAttribute | VDirective | undefined | null} node
* @returns { node is VAttribute }
*/
-function isVAttributeOrVBind(node) {
- return isVAttribute(node) || isVBind(node)
+function isVAttributeOrVBindOrVModel(node) {
+ return isVAttribute(node) || isVBind(node) || isVModel(node)
}
/**
@@ -235,8 +243,8 @@ function create(context) {
if (isVBindObject(node)) {
// prev, v-bind:foo, v-bind -> v-bind:foo, v-bind, prev
- isMoveUp = isVAttributeOrVBind
- } else if (isVAttributeOrVBind(node)) {
+ isMoveUp = isVAttributeOrVBindOrVModel
+ } else if (isVAttributeOrVBindOrVModel(node)) {
// prev, v-bind, v-bind:foo -> v-bind, v-bind:foo, prev
isMoveUp = isVBindObject
} else {
@@ -298,11 +306,13 @@ function create(context) {
const attributes = node.attributes.filter((node, index, attributes) => {
if (
isVBindObject(node) &&
- (isVAttributeOrVBind(attributes[index - 1]) ||
- isVAttributeOrVBind(attributes[index + 1]))
+ (isVAttributeOrVBindOrVModel(attributes[index - 1]) ||
+ isVAttributeOrVBindOrVModel(attributes[index + 1]))
) {
- // In Vue 3, ignore the `v-bind:foo=" ... "` and `v-bind ="object"` syntax
- // as they behave differently if you change the order.
+ // In Vue 3, ignore `v-bind="object"`, which is
+ // a pair of `v-bind:foo="..."` and `v-bind="object"` and
+ // a pair of `v-model="..."` and `v-bind="object"`,
+ // because changing the order behaves differently.
return false
}
return true
@@ -330,14 +340,14 @@ function create(context) {
if (isVBindObject(node)) {
// node is `v-bind ="object"` syntax
- // In Vue 3, if change the order of `v-bind:foo=" ... "` and `v-bind ="object"`,
+ // In Vue 3, if change the order of `v-bind:foo="..."`, `v-model="..."` and `v-bind="object"`,
// the behavior will be different, so adjust so that there is no change in behavior.
const len = attributes.length
for (let nextIndex = index + 1; nextIndex < len; nextIndex++) {
const next = attributes[nextIndex]
- if (isVAttributeOrVBind(next) && !isVBindObject(next)) {
+ if (isVAttributeOrVBindOrVModel(next) && !isVBindObject(next)) {
// It is considered to be in the same order as the next bind prop node.
return getPositionFromAttrIndex(nextIndex)
}
diff --git a/tests/lib/rules/attributes-order.js b/tests/lib/rules/attributes-order.js
index b08ab1612..3797b9181 100644
--- a/tests/lib/rules/attributes-order.js
+++ b/tests/lib/rules/attributes-order.js
@@ -442,6 +442,42 @@ tester.run('attributes-order', rule, {
`,
options: [{ alphabetical: true }]
},
+ {
+ filename: 'test.vue',
+ code: `
+
+
+
+ `
+ },
+ {
+ filename: 'test.vue',
+ code: `
+
+
+
+ `
+ },
+ {
+ filename: 'test.vue',
+ code: `
+
+
+
+ `
+ },
// omit order
{
@@ -1246,6 +1282,50 @@ tester.run('attributes-order', rule, {
'Attribute "v-if" should go before "v-on:click".'
]
},
+ {
+ filename: 'test.vue',
+ code: `
+
+
+
+ `,
+ output: `
+
+
+
+ `,
+ errors: ['Attribute "v-model" should go before "v-custom-directive".']
+ },
+ {
+ filename: 'test.vue',
+ code: `
+
+
+
+ `,
+ output: `
+
+
+
+ `,
+ errors: ['Attribute "v-bind:id" should go before "v-model".']
+ },
// omit order
{