Skip to content

Commit 9d88c95

Browse files
committed
feat(attributes-order): add ignoreVBindObject option
1 parent 138db47 commit 9d88c95

File tree

3 files changed

+115
-7
lines changed

3 files changed

+115
-7
lines changed

docs/rules/attributes-order.md

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,8 @@ Note that `v-bind="object"` syntax is considered to be the same as the next or p
144144
"CONTENT"
145145
],
146146
"alphabetical": false,
147-
"sortLineLength": false
147+
"sortLineLength": false,
148+
"ignoreVBindObject": false
148149
}]
149150
}
150151
```
@@ -275,6 +276,25 @@ When `alphabetical` and `sortLineLength` are both set to `true`, attributes with
275276

276277
</eslint-code-block>
277278

279+
### `"ignoreVBindObject": true`
280+
281+
When set to `true`, the `v-bind="object"` directive will be excluded from the attribute order check. This is useful when the spread binding is intentionally placed in a specific position for functional reasons, such as controlling the execution order of event handlers.
282+
283+
<eslint-code-block fix :rules="{'vue/attributes-order': ['error', {ignoreVBindObject: true}]}">
284+
285+
```vue
286+
<template>
287+
<!-- ✓ GOOD -->
288+
<MyButton
289+
:foo="foo"
290+
@click="onClick"
291+
v-bind="attrs"
292+
/>
293+
</template>
294+
```
295+
296+
</eslint-code-block>
297+
278298
### Custom orders
279299

280300
#### `['LIST_RENDERING', 'CONDITIONALS', 'RENDER_MODIFIERS', 'GLOBAL', 'UNIQUE', 'TWO_WAY_BINDING', 'DEFINITION', 'OTHER_DIRECTIVES', 'OTHER_ATTR', 'EVENTS', 'CONTENT']`

lib/rules/attributes-order.js

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,9 @@ function create(context) {
274274
const sortLineLength = Boolean(
275275
context.options[0] && context.options[0].sortLineLength
276276
)
277+
const ignoreVBindObject = Boolean(
278+
context.options[0] && context.options[0].ignoreVBindObject
279+
)
277280

278281
/** @type { { [key: string]: number } } */
279282
const attributePosition = {}
@@ -383,15 +386,20 @@ function create(context) {
383386
*/
384387
function getAttributeAndPositionList(node) {
385388
const attributes = node.attributes.filter((node, index, attributes) => {
389+
// If ignoreVBindObject is enabled, ignore v-bind="object"
390+
if (ignoreVBindObject && isVBindObject(node)) {
391+
return false
392+
}
393+
394+
// In Vue 3, ignore `v-bind="object"`, which is
395+
// a pair of `v-bind:foo="..."` and `v-bind="object"` and
396+
// a pair of `v-model="..."` and `v-bind="object"`,
397+
// because changing the order behaves differently.
386398
if (
387399
isVBindObject(node) &&
388400
(isVAttributeOrVBindOrVModel(attributes[index - 1]) ||
389401
isVAttributeOrVBindOrVModel(attributes[index + 1]))
390402
) {
391-
// In Vue 3, ignore `v-bind="object"`, which is
392-
// a pair of `v-bind:foo="..."` and `v-bind="object"` and
393-
// a pair of `v-model="..."` and `v-bind="object"`,
394-
// because changing the order behaves differently.
395403
return false
396404
}
397405
return true
@@ -416,7 +424,7 @@ function create(context) {
416424
function getPositionFromAttrIndex(index) {
417425
const node = attributes[index]
418426
if (isVBindObject(node)) {
419-
// node is `v-bind ="object"` syntax
427+
// node is `v-bind="object"` syntax
420428

421429
// In Vue 3, if change the order of `v-bind:foo="..."`, `v-model="..."` and `v-bind="object"`,
422430
// the behavior will be different, so adjust so that there is no change in behavior.
@@ -468,7 +476,8 @@ module.exports = {
468476
additionalItems: false
469477
},
470478
alphabetical: { type: 'boolean' },
471-
sortLineLength: { type: 'boolean' }
479+
sortLineLength: { type: 'boolean' },
480+
ignoreVBindObject: { type: 'boolean' }
472481
},
473482
additionalProperties: false
474483
}

tests/lib/rules/attributes-order.js

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -682,6 +682,31 @@ tester.run('attributes-order', rule, {
682682
></div>
683683
</template>`,
684684
options: [{ sortLineLength: true, alphabetical: true }]
685+
},
686+
687+
// ignoreVBindObject
688+
{
689+
filename: 'test.vue',
690+
code: `
691+
<template>
692+
<div
693+
@click="handleClick"
694+
v-bind="attrs"
695+
></div>
696+
</template>`,
697+
options: [{ ignoreVBindObject: true }]
698+
},
699+
{
700+
filename: 'test.vue',
701+
code: `
702+
<template>
703+
<div
704+
ref="ref"
705+
@click="handleClick"
706+
v-bind="attrs"
707+
@input="handleInput"/>
708+
</template>`,
709+
options: [{ ignoreVBindObject: true }]
685710
}
686711
],
687712

@@ -2140,6 +2165,60 @@ tester.run('attributes-order', rule, {
21402165
}
21412166
]
21422167
},
2168+
{
2169+
filename: 'test.vue',
2170+
code: `
2171+
<template>
2172+
<div
2173+
@click="handleClick"
2174+
v-bind="attrs"
2175+
></div>
2176+
</template>`,
2177+
output: `
2178+
<template>
2179+
<div
2180+
v-bind="attrs"
2181+
@click="handleClick"
2182+
></div>
2183+
</template>`,
2184+
errors: [
2185+
{
2186+
message: 'Attribute "v-bind" should go before "@click".',
2187+
line: 5,
2188+
column: 11,
2189+
endLine: 5,
2190+
endColumn: 25
2191+
}
2192+
]
2193+
},
2194+
{
2195+
filename: 'test.vue',
2196+
code: `
2197+
<template>
2198+
<div
2199+
ref="ref"
2200+
@click="handleClick"
2201+
v-bind="attrs"
2202+
></div>
2203+
</template>`,
2204+
output: `
2205+
<template>
2206+
<div
2207+
ref="ref"
2208+
v-bind="attrs"
2209+
@click="handleClick"
2210+
></div>
2211+
</template>`,
2212+
errors: [
2213+
{
2214+
message: 'Attribute "v-bind" should go before "@click".',
2215+
line: 6,
2216+
column: 11,
2217+
endLine: 6,
2218+
endColumn: 25
2219+
}
2220+
]
2221+
},
21432222
{
21442223
filename: 'test.vue',
21452224
code: `

0 commit comments

Comments
 (0)