Skip to content

Commit f834a7e

Browse files
armano2mysticatea
authored andcommitted
Update: options for no-duplicate-attributes (fixes #112)(#113)
1 parent 55d388c commit f834a7e

File tree

3 files changed

+95
-11
lines changed

3 files changed

+95
-11
lines changed

docs/rules/no-duplicate-attributes.md

+12-1
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,20 @@ This rule reports duplicate attributes.
2727

2828
## :wrench: Options
2929

30-
Nothing.
30+
`allowCoexistClass` - Enables [`v-bind:class`] directive can coexist with the plain `class` attribute.
31+
`allowCoexistStyle` - Enables [`v-bind:style`] directive can coexist with the plain `style` attribute.
32+
33+
```
34+
'vue/name-property-casing': [2, {
35+
allowCoexistClass: Boolean // default: true
36+
allowCoexistStyle: Boolean, // default: true
37+
}]
38+
```
3139

3240
## TODO: `<div foo foo></div>`
3341

3442
`parse5` remove duplicate attributes on the tokenization phase.
3543
Needs investigation to check.
44+
45+
[`v-bind:class`]: https://vuejs.org/v2/guide/class-and-style.html
46+
[`v-bind:style`]: https://vuejs.org/v2/guide/class-and-style.html

lib/rules/no-duplicate-attributes.js

+36-5
Original file line numberDiff line numberDiff line change
@@ -37,27 +37,45 @@ function getName (attribute) {
3737
* @returns {Object} AST event handlers.
3838
*/
3939
function create (context) {
40-
const names = new Set()
40+
const options = context.options[0] || {}
41+
const allowCoexistStyle = options.allowCoexistStyle !== false
42+
const allowCoexistClass = options.allowCoexistClass !== false
43+
44+
const directiveNames = new Set()
45+
const attributeNames = new Set()
46+
47+
function isDuplicate (name, isDirective) {
48+
if ((allowCoexistStyle && name === 'style') || (allowCoexistClass && name === 'class')) {
49+
return isDirective ? directiveNames.has(name) : attributeNames.has(name)
50+
}
51+
return directiveNames.has(name) || attributeNames.has(name)
52+
}
4153

4254
utils.registerTemplateBodyVisitor(context, {
4355
'VStartTag' () {
44-
names.clear()
56+
directiveNames.clear()
57+
attributeNames.clear()
4558
},
4659
'VAttribute' (node) {
4760
const name = getName(node)
4861
if (name == null) {
4962
return
5063
}
5164

52-
if (names.has(name)) {
65+
if (isDuplicate(name, node.directive)) {
5366
context.report({
5467
node,
5568
loc: node.loc,
5669
message: "Duplicate attribute '{{name}}'.",
5770
data: { name }
5871
})
5972
}
60-
names.add(name)
73+
74+
if (node.directive) {
75+
directiveNames.add(name)
76+
} else {
77+
attributeNames.add(name)
78+
}
6179
}
6280
})
6381

@@ -77,6 +95,19 @@ module.exports = {
7795
recommended: false
7896
},
7997
fixable: false,
80-
schema: []
98+
99+
schema: [
100+
{
101+
type: 'object',
102+
properties: {
103+
allowCoexistClass: {
104+
type: 'boolean'
105+
},
106+
allowCoexistStyle: {
107+
type: 'boolean'
108+
}
109+
}
110+
}
111+
]
81112
}
82113
}

tests/lib/rules/no-duplicate-attributes.js

+47-5
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,32 @@ tester.run('no-duplicate-attributes', rule, {
3434
{
3535
filename: 'test.vue',
3636
code: '<template><div><div @click="foo" @click="bar"></div></div></template>'
37+
},
38+
{
39+
filename: 'test.vue',
40+
code: '<template><div><div style :style></div></div></template>'
41+
},
42+
{
43+
filename: 'test.vue',
44+
code: '<template><div><div class :class></div></div></template>'
45+
},
46+
{
47+
filename: 'test.vue',
48+
code: '<template><div><div :class="a" class="b"></div></div></template>',
49+
options: [{ allowCoexistStyle: true }]
50+
},
51+
{
52+
filename: 'test.vue',
53+
code: '<template><div><div :style="a" style="b"></div></div></template>',
54+
options: [{ allowCoexistStyle: true }]
3755
}
3856
],
3957
invalid: [
40-
// {
41-
// filename: "test.vue",
42-
// code: "<template><div><div foo foo></div></div></template>",
43-
// errors: ["Duplicate attribute 'foo'."],
44-
// },
58+
// {
59+
// filename: 'test.vue',
60+
// code: '<template><div><div foo foo></div></div></template>',
61+
// errors: ["Duplicate attribute 'foo'."]
62+
// },
4563
{
4664
filename: 'test.vue',
4765
code: '<template><div><div foo v-bind:foo></div></div></template>',
@@ -51,6 +69,30 @@ tester.run('no-duplicate-attributes', rule, {
5169
filename: 'test.vue',
5270
code: '<template><div><div foo :foo></div></div></template>',
5371
errors: ["Duplicate attribute 'foo'."]
72+
},
73+
{
74+
filename: 'test.vue',
75+
code: '<template><div><div style :style></div></div></template>',
76+
errors: ["Duplicate attribute 'style'."],
77+
options: [{ allowCoexistStyle: false }]
78+
},
79+
{
80+
filename: 'test.vue',
81+
code: '<template><div><div class :class></div></div></template>',
82+
errors: ["Duplicate attribute 'class'."],
83+
options: [{ allowCoexistClass: false }]
84+
},
85+
{
86+
filename: 'test.vue',
87+
code: '<template><div><div :style style></div></div></template>',
88+
errors: ["Duplicate attribute 'style'."],
89+
options: [{ allowCoexistStyle: false }]
90+
},
91+
{
92+
filename: 'test.vue',
93+
code: '<template><div><div :class class></div></div></template>',
94+
errors: ["Duplicate attribute 'class'."],
95+
options: [{ allowCoexistClass: false }]
5496
}
5597
]
5698
})

0 commit comments

Comments
 (0)