Skip to content

Commit 017cc22

Browse files
Fix false positives for v-bind="obj" with v-model in vue/attributes-order rule (#1771)
* Fix false positives for `v-bind="obj"` with `v-model` in `vue/attributes-order` rule * Update lib/rules/attributes-order.js Co-authored-by: Flo Edelmann <florian-edelmann@online.de> * Update lib/rules/attributes-order.js Co-authored-by: Flo Edelmann <florian-edelmann@online.de> Co-authored-by: Flo Edelmann <florian-edelmann@online.de>
1 parent a2c4af2 commit 017cc22

File tree

2 files changed

+101
-11
lines changed

2 files changed

+101
-11
lines changed

lib/rules/attributes-order.js

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,14 @@ const ATTRS = {
3636
function isVBind(node) {
3737
return Boolean(node && node.directive && node.key.name.name === 'bind')
3838
}
39+
/**
40+
* Check whether the given attribute is `v-model` directive.
41+
* @param {VAttribute | VDirective | undefined | null} node
42+
* @returns { node is VDirective }
43+
*/
44+
function isVModel(node) {
45+
return Boolean(node && node.directive && node.key.name.name === 'model')
46+
}
3947
/**
4048
* Check whether the given attribute is plain attribute.
4149
* @param {VAttribute | VDirective | undefined | null} node
@@ -45,12 +53,12 @@ function isVAttribute(node) {
4553
return Boolean(node && !node.directive)
4654
}
4755
/**
48-
* Check whether the given attribute is plain attribute or `v-bind` directive.
56+
* Check whether the given attribute is plain attribute, `v-bind` directive or `v-model` directive.
4957
* @param {VAttribute | VDirective | undefined | null} node
5058
* @returns { node is VAttribute }
5159
*/
52-
function isVAttributeOrVBind(node) {
53-
return isVAttribute(node) || isVBind(node)
60+
function isVAttributeOrVBindOrVModel(node) {
61+
return isVAttribute(node) || isVBind(node) || isVModel(node)
5462
}
5563

5664
/**
@@ -235,8 +243,8 @@ function create(context) {
235243

236244
if (isVBindObject(node)) {
237245
// prev, v-bind:foo, v-bind -> v-bind:foo, v-bind, prev
238-
isMoveUp = isVAttributeOrVBind
239-
} else if (isVAttributeOrVBind(node)) {
246+
isMoveUp = isVAttributeOrVBindOrVModel
247+
} else if (isVAttributeOrVBindOrVModel(node)) {
240248
// prev, v-bind, v-bind:foo -> v-bind, v-bind:foo, prev
241249
isMoveUp = isVBindObject
242250
} else {
@@ -298,11 +306,13 @@ function create(context) {
298306
const attributes = node.attributes.filter((node, index, attributes) => {
299307
if (
300308
isVBindObject(node) &&
301-
(isVAttributeOrVBind(attributes[index - 1]) ||
302-
isVAttributeOrVBind(attributes[index + 1]))
309+
(isVAttributeOrVBindOrVModel(attributes[index - 1]) ||
310+
isVAttributeOrVBindOrVModel(attributes[index + 1]))
303311
) {
304-
// In Vue 3, ignore the `v-bind:foo=" ... "` and `v-bind ="object"` syntax
305-
// as they behave differently if you change the order.
312+
// In Vue 3, ignore `v-bind="object"`, which is
313+
// a pair of `v-bind:foo="..."` and `v-bind="object"` and
314+
// a pair of `v-model="..."` and `v-bind="object"`,
315+
// because changing the order behaves differently.
306316
return false
307317
}
308318
return true
@@ -330,14 +340,14 @@ function create(context) {
330340
if (isVBindObject(node)) {
331341
// node is `v-bind ="object"` syntax
332342

333-
// In Vue 3, if change the order of `v-bind:foo=" ... "` and `v-bind ="object"`,
343+
// In Vue 3, if change the order of `v-bind:foo="..."`, `v-model="..."` and `v-bind="object"`,
334344
// the behavior will be different, so adjust so that there is no change in behavior.
335345

336346
const len = attributes.length
337347
for (let nextIndex = index + 1; nextIndex < len; nextIndex++) {
338348
const next = attributes[nextIndex]
339349

340-
if (isVAttributeOrVBind(next) && !isVBindObject(next)) {
350+
if (isVAttributeOrVBindOrVModel(next) && !isVBindObject(next)) {
341351
// It is considered to be in the same order as the next bind prop node.
342352
return getPositionFromAttrIndex(nextIndex)
343353
}

tests/lib/rules/attributes-order.js

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,42 @@ tester.run('attributes-order', rule, {
442442
</template>`,
443443
options: [{ alphabetical: true }]
444444
},
445+
{
446+
filename: 'test.vue',
447+
code: `
448+
<template>
449+
<div
450+
v-if="x"
451+
v-bind="b"
452+
v-model="c"
453+
v-bind:value="a">
454+
</div>
455+
</template>`
456+
},
457+
{
458+
filename: 'test.vue',
459+
code: `
460+
<template>
461+
<div
462+
v-if="x"
463+
v-model="c"
464+
v-bind="b"
465+
v-bind:value="a">
466+
</div>
467+
</template>`
468+
},
469+
{
470+
filename: 'test.vue',
471+
code: `
472+
<template>
473+
<div
474+
v-if="x"
475+
v-bind="b"
476+
v-bind:id="a"
477+
v-model="c">
478+
</div>
479+
</template>`
480+
},
445481

446482
// omit order
447483
{
@@ -1246,6 +1282,50 @@ tester.run('attributes-order', rule, {
12461282
'Attribute "v-if" should go before "v-on:click".'
12471283
]
12481284
},
1285+
{
1286+
filename: 'test.vue',
1287+
code: `
1288+
<template>
1289+
<div
1290+
v-custom-directive="x"
1291+
v-bind="b"
1292+
v-model="c"
1293+
v-bind:value="a">
1294+
</div>
1295+
</template>`,
1296+
output: `
1297+
<template>
1298+
<div
1299+
v-bind="b"
1300+
v-model="c"
1301+
v-custom-directive="x"
1302+
v-bind:value="a">
1303+
</div>
1304+
</template>`,
1305+
errors: ['Attribute "v-model" should go before "v-custom-directive".']
1306+
},
1307+
{
1308+
filename: 'test.vue',
1309+
code: `
1310+
<template>
1311+
<div
1312+
v-if="x"
1313+
v-model="c"
1314+
v-bind="b"
1315+
v-bind:id="a">
1316+
</div>
1317+
</template>`,
1318+
output: `
1319+
<template>
1320+
<div
1321+
v-if="x"
1322+
v-bind="b"
1323+
v-bind:id="a"
1324+
v-model="c">
1325+
</div>
1326+
</template>`,
1327+
errors: ['Attribute "v-bind:id" should go before "v-model".']
1328+
},
12491329

12501330
// omit order
12511331
{

0 commit comments

Comments
 (0)