Skip to content

Commit 702ebca

Browse files
authored
fix: attr-value-no-duplication (#1667)
1 parent 0812502 commit 702ebca

File tree

8 files changed

+54
-10
lines changed

8 files changed

+54
-10
lines changed

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "htmlhint",
3-
"version": "1.6.1",
3+
"version": "1.6.2",
44
"description": "The Static Code Analysis Tool for your HTML",
55
"keywords": [
66
"html",

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,12 @@ export default {
2020
// Skip content, media, and style attributes entirely
2121
if (
2222
attr.name.toLowerCase() === 'content' ||
23+
attr.name.toLowerCase() === 'd' ||
2324
attr.name.toLowerCase() === 'media' ||
24-
attr.name.toLowerCase() === 'style'
25+
attr.name.toLowerCase() === 'sizes' ||
26+
attr.name.toLowerCase() === 'src' ||
27+
attr.name.toLowerCase() === 'style' ||
28+
attr.name.toLowerCase().startsWith('on') // Skip all event handlers (onclick, onchange, etc.)
2529
) {
2630
continue
2731
}

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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,4 +98,25 @@ describe(`Rules: ${ruleId}`, () => {
9898
const messages = HTMLHint.verify(code, ruleOptions)
9999
expect(messages.length).toBe(0)
100100
})
101+
102+
it('Script src attribute should be skipped entirely', () => {
103+
const code =
104+
'<script src="data:text/javascript,window.analytics = window.analytics || function() { (window.analytics.q = window.analytics.q || []).push(arguments) }"></script>'
105+
const messages = HTMLHint.verify(code, ruleOptions)
106+
expect(messages.length).toBe(0)
107+
})
108+
109+
it('Sizes attribute should be skipped entirely', () => {
110+
const code =
111+
'<source type="" sizes="(min-width: 1rem) 1vw, (min-width: 2rem) 2vw" srcset="">'
112+
const messages = HTMLHint.verify(code, ruleOptions)
113+
expect(messages.length).toBe(0)
114+
})
115+
116+
it('Event handler attributes should be skipped entirely', () => {
117+
const code =
118+
"<button onclick=\"trackEvent('click'); validateForm(); trackEvent('click');\">Submit</button>"
119+
const messages = HTMLHint.verify(code, ruleOptions)
120+
expect(messages.length).toBe(0)
121+
})
101122
})

website/src/content/docs/changelog.mdx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ description: The release notes for HTMLHint, see what's changed with each new ve
55

66
import { Badge } from '@astrojs/starlight/components'
77

8+
## 1.6.2 _(2025-06-18)_
9+
10+
- <Badge text="Fix" size="small" variant="danger" /> Improve [`attr-value-no-duplication`](/rules/attr-value-no-duplication/) logic
11+
812
## 1.6.1 _(2025-06-17)_
913

1014
- <Badge text="Fix" size="small" variant="danger" /> Improve SARIF report formatting

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.6.1 is now available! Check the <a href="/changelog/">changelog</a> to see what's new.
17+
v1.6.2 is now available! Check the <a href="/changelog/">changelog</a> to see what's new.
1818
tableOfContents: false
1919
lastUpdated: false
2020
editUrl: false

website/src/content/docs/rules/attr-value-no-duplication.mdx

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,22 @@ Level: <Badge text="Error" variant="danger" />
3131
<div class="btn btn-primary btn-large">Button</div>
3232
```
3333

34+
```html
35+
<source type="" sizes="(min-width: 1rem) 1vw, (min-width: 2rem) 2vw" srcset="">
36+
```
37+
38+
## Excluded attributes
39+
40+
The following attributes are excluded from this rule and will not trigger errors even if they contain duplicate values:
41+
42+
- `content` - Common for meta tags that may contain duplicate keywords
43+
- `d` - Used in SVG path data which may contain repetitive pattern data
44+
- `media` - Used for media queries which have special parsing rules
45+
- `on*` - All event handlers (onclick, onkeydown, etc.) that may contain legitimate duplicate JavaScript operations
46+
- `sizes` - Used in responsive images with complex viewport conditions
47+
- `src` - URLs and data URIs that might legitimately contain repeated patterns
48+
- `style` - Inline CSS which has its own syntax and may contain duplicate property names
49+
3450
### The following patterns are considered rule violations:
3551

3652
```html
@@ -42,7 +58,7 @@ Level: <Badge text="Error" variant="danger" />
4258
```
4359

4460
```html
45-
<div class="btn primary btn">Button</div>
61+
<div class="btn primary btn">Button</div>
4662
```
4763

4864
## Why does this rule exist?

website/src/content/docs/rules/tag-no-obsolete.mdx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,9 @@ This rule prevents the use of HTML tags that have been deprecated and are consid
2323

2424
### Obsolete tags include:
2525

26-
- `applet`, `acronym`, `bgsound`, `dir`, `frame`, `frameset`, `noframes`
27-
- `isindex`, `keygen`, `listing`, `menuitem`, `nextid`, `noembed`
28-
- `plaintext`, `rb`, `rtc`, `strike`, `xmp`, `basefont`, `big`
29-
- `blink`, `center`, `font`, `marquee`, `multicol`, `nobr`, `spacer`, `tt`
26+
`acronym`, `applet`, `basefont`, `bgsound`, `big`, `blink`, `center`, `dir`, `font`, `frame`, `frameset`,
27+
`isindex`, `keygen`, `listing`, `marquee`, `menuitem`, `multicol`, `nextid`, `nobr`, `noembed`, `noframes`,
28+
`plaintext`, `rb`, `rtc`, `spacer`, `strike`, `tt`, `xmp`
3029

3130
### The following patterns are **not** considered rule violations
3231

0 commit comments

Comments
 (0)