Skip to content

Commit aadd4cf

Browse files
authored
fix: Improved attr-value-no-duplication logic (#1663)
1 parent cf955ef commit aadd4cf

File tree

3 files changed

+53
-3
lines changed

3 files changed

+53
-3
lines changed

src/core/rules/attr-value-no-duplication.ts

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,35 @@ export default {
99
let attr
1010
const col = event.col + event.tagName.length + 1
1111

12+
// Skip SVG elements entirely
13+
if (event.tagName.toLowerCase() === 'svg') {
14+
return
15+
}
16+
1217
for (let i = 0, l = attrs.length; i < l; i++) {
1318
attr = attrs[i]
1419

20+
// Skip style and media attributes entirely
21+
if (
22+
attr.name.toLowerCase() === 'style' ||
23+
attr.name.toLowerCase() === 'media'
24+
) {
25+
continue
26+
}
27+
1528
if (attr.value) {
16-
// Split attribute value by whitespace to get individual values
17-
const values = attr.value.trim().split(/\s+/)
29+
let values: string[]
30+
if (attr.name.toLowerCase() === 'media') {
31+
// For media, treat each comma-separated part as a whole
32+
values = attr.value
33+
.split(',')
34+
.map((part) => part.trim())
35+
.filter(Boolean)
36+
} else {
37+
// For other attributes, split by whitespace only
38+
values = attr.value.trim().split(/\s+/)
39+
}
40+
1841
const duplicateMap: { [value: string]: boolean } = {}
1942

2043
for (const value of values) {

test/rules/attr-value-no-duplication.spec.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,4 +64,31 @@ describe(`Rules: ${ruleId}`, () => {
6464
'Duplicate value [ btn ] was found in attribute [ class ].'
6565
)
6666
})
67+
68+
it('SVG elements should be skipped entirely', () => {
69+
const code = '<svg class="icon icon icon" viewBox="0 0 24 24"></svg>'
70+
const messages = HTMLHint.verify(code, ruleOptions)
71+
expect(messages.length).toBe(0)
72+
})
73+
74+
it('Style attributes should be skipped entirely', () => {
75+
const code =
76+
'<div style="width: 2rem; height: 2rem; width: 2rem;">Test</div>'
77+
const messages = HTMLHint.verify(code, ruleOptions)
78+
expect(messages.length).toBe(0)
79+
})
80+
81+
it('CSS media queries with commas should not be flagged as duplicates', () => {
82+
const code =
83+
'<link rel="stylesheet" href="css/test.css" media="all and (-ms-high-contrast: active), (-ms-high-contrast: none)">'
84+
const messages = HTMLHint.verify(code, ruleOptions)
85+
expect(messages.length).toBe(0)
86+
})
87+
88+
it('Media attribute with actual duplicates should be skipped', () => {
89+
const code =
90+
'<link rel="stylesheet" href="css/test.css" media="screen screen">'
91+
const messages = HTMLHint.verify(code, ruleOptions)
92+
expect(messages.length).toBe(0)
93+
})
6794
})

website/src/content/docs/index.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ hero:
1414
variant: minimal
1515
banner:
1616
content: |
17-
v1.5.1 is now available! Check the <a href="/changelog/">changelog</a> to see what's new.
17+
v1.6.0 is now available! Check the <a href="/changelog/">changelog</a> to see what's new.
1818
tableOfContents: false
1919
lastUpdated: false
2020
editUrl: false

0 commit comments

Comments
 (0)