Skip to content

Commit ba9bd9b

Browse files
committed
feat: support TemplateLiteral + improve report
1 parent 99139ad commit ba9bd9b

File tree

3 files changed

+47
-22
lines changed

3 files changed

+47
-22
lines changed

docs/rules/no-duplicate-class-names.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ This rule prevents the same class name from appearing multiple times within the
3131
<div class="foo foo"></div>
3232
<div class="foo bar foo baz bar"></div>
3333
<div :class="'foo foo'"></div>
34+
<div :class="`foo foo`"></div>
3435
<div :class="{ 'foo foo': true }"></div>
3536
<div :class="['foo foo']"></div>
3637
<div :class="['foo foo', { 'bar bar baz': true }]"></div>

lib/rules/no-duplicate-class-names.js

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,15 @@ const utils = require('../utils')
1010
/**
1111
* @param {VDirective} node
1212
* @param {Expression} [expression]
13-
* @return {IterableIterator<{
14-
* node: Literal | VAttribute | VDirective,
15-
* target?: VLiteral | Literal
16-
* }>}
13+
* @return {IterableIterator<{ node: Literal | TemplateElement }>}
1714
*/
1815
function* extractDuplicateNode(node, expression) {
1916
const nodeExpression = expression ?? node.value?.expression
2017
if (!nodeExpression) return
2118

2219
switch (nodeExpression.type) {
2320
case 'Literal': {
24-
yield { node, target: nodeExpression }
21+
yield { node: nodeExpression }
2522
break
2623
}
2724
case 'ObjectExpression': {
@@ -51,6 +48,15 @@ function* extractDuplicateNode(node, expression) {
5148
yield* extractDuplicateNode(node, nodeExpression.alternate)
5249
break
5350
}
51+
case 'TemplateLiteral': {
52+
for (const quasi of nodeExpression.quasis) {
53+
yield { node: quasi }
54+
}
55+
for (const expr of nodeExpression.expressions) {
56+
yield* extractDuplicateNode(node, expr)
57+
}
58+
break
59+
}
5460
}
5561
}
5662

@@ -71,14 +77,17 @@ module.exports = {
7177
/** @param {RuleContext} context */
7278
create: (context) => {
7379
/**
74-
* @param {VAttribute | VDirective | Literal} node
75-
* @param {VLiteral | Literal | null} [target]
80+
* @param {VAttribute | VLiteral | Literal | TemplateElement | null} node
7681
*/
77-
function reportDuplicateClasses(node, target) {
78-
const fixTarget = target ?? node
82+
function reportDuplicateClasses(node) {
83+
const fixTarget = node
7984
if (!fixTarget || !fixTarget.value) return
8085

81-
const classList = fixTarget.value
86+
const classList =
87+
typeof fixTarget.value === 'object' && 'raw' in fixTarget.value
88+
? fixTarget.value.raw
89+
: fixTarget.value
90+
8291
if (typeof classList !== 'string') return
8392

8493
const classNames = classList.split(/\s+/).filter(Boolean)
@@ -122,14 +131,14 @@ module.exports = {
122131
"VAttribute[directive=false][key.name='class'][value.type='VLiteral']"(
123132
node
124133
) {
125-
reportDuplicateClasses(node, node.value)
134+
reportDuplicateClasses(node.value)
126135
},
127136
/** @param {VDirective} node */
128137
"VAttribute[directive=true][key.argument.name='class'][value.type='VExpressionContainer']"(
129138
node
130139
) {
131-
for (const { node: reportNode, target } of extractDuplicateNode(node)) {
132-
reportDuplicateClasses(reportNode, target)
140+
for (const { node: reportNode } of extractDuplicateNode(node)) {
141+
reportDuplicateClasses(reportNode)
133142
}
134143
}
135144
})

tests/lib/rules/no-duplicate-class-names.js

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ tester.run('no-duplicate-class-names', rule, {
3333
filename: 'no-duplicate-class-in-directive-string.vue',
3434
code: `<template><div :class="'foo bar baz'"></div></template>`
3535
},
36+
{
37+
filename: 'no-duplicate-class-in-directive-literal.vue',
38+
code: '<template><div :class="`foo bar baz`"></div></template>'
39+
},
3640
{
3741
filename: 'no-duplicate-class-in-directive-object.vue',
3842
code: `<template><div :class="{ 'foo bar baz': true }"></div></template>`
@@ -66,7 +70,7 @@ tester.run('no-duplicate-class-names', rule, {
6670
errors: [
6771
{
6872
message: "Duplicate class name 'foo'.",
69-
type: 'VAttribute'
73+
type: 'VLiteral'
7074
}
7175
]
7276
},
@@ -77,18 +81,29 @@ tester.run('no-duplicate-class-names', rule, {
7781
errors: [
7882
{
7983
message: "Duplicate class name 'foo, bar'.",
80-
type: 'VAttribute'
84+
type: 'VLiteral'
8185
}
8286
]
8387
},
8488
{
85-
filename: 'duplicate-class-in-directive-string-literal.vue',
89+
filename: 'duplicate-class-in-directive-string.vue',
8690
code: `<template><div :class="'foo foo'"></div></template>`,
8791
output: `<template><div :class="'foo'"></div></template>`,
8892
errors: [
8993
{
9094
message: "Duplicate class name 'foo'.",
91-
type: 'VAttribute'
95+
type: 'Literal'
96+
}
97+
]
98+
},
99+
{
100+
filename: 'duplicate-class-in-directive-literal.vue',
101+
code: '<template><div :class="`foo foo`"></div></template>',
102+
output: '<template><div :class="`foo`"></div></template>',
103+
errors: [
104+
{
105+
message: "Duplicate class name 'foo'.",
106+
type: 'TemplateElement'
92107
}
93108
]
94109
},
@@ -125,7 +140,7 @@ tester.run('no-duplicate-class-names', rule, {
125140
errors: [
126141
{
127142
message: "Duplicate class name 'foo'.",
128-
type: 'VAttribute'
143+
type: 'Literal'
129144
}
130145
]
131146
},
@@ -136,11 +151,11 @@ tester.run('no-duplicate-class-names', rule, {
136151
errors: [
137152
{
138153
message: "Duplicate class name 'foo'.",
139-
type: 'VAttribute'
154+
type: 'Literal'
140155
},
141156
{
142157
message: "Duplicate class name 'bar'.",
143-
type: 'VAttribute'
158+
type: 'Literal'
144159
}
145160
]
146161
},
@@ -151,7 +166,7 @@ tester.run('no-duplicate-class-names', rule, {
151166
errors: [
152167
{
153168
message: "Duplicate class name 'foo'.",
154-
type: 'VAttribute'
169+
type: 'Literal'
155170
},
156171
{
157172
message: "Duplicate class name 'bar'.",
@@ -166,7 +181,7 @@ tester.run('no-duplicate-class-names', rule, {
166181
errors: [
167182
{
168183
message: "Duplicate class name 'foo'.",
169-
type: 'VAttribute'
184+
type: 'Literal'
170185
}
171186
]
172187
}

0 commit comments

Comments
 (0)