Skip to content

Commit 87a1191

Browse files
authored
SVG elements ignored with tagname-lowercase rule (#1795)
The following SVG elements are ignored: animateMotion animateTransform clipPath feBlend feColorMatrix feComponentTransfer feComposite feConvolveMatrix feDiffuseLighting feDisplacementMap feDistantLight feDropShadow feFlood feFuncA feFuncB feFuncG feFuncR feGaussianBlur feImage feMerge feMergeNode feMorphology feOffset fePointLight feSpecularLighting feSpotLight feTile feTurbulence foreignObject linearGradient radialGradient textPath
1 parent e1d89e2 commit 87a1191

File tree

5 files changed

+167
-11
lines changed

5 files changed

+167
-11
lines changed

dist/core/rules/tagname-lowercase.js

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

src/core/rules/tagname-lowercase.ts

Lines changed: 78 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,91 @@
11
import { Rule } from '../types'
22

3+
const svgTagNameIgnores = [
4+
'animateMotion',
5+
'animateTransform',
6+
'clipPath',
7+
'feBlend',
8+
'feColorMatrix',
9+
'feComponentTransfer',
10+
'feComposite',
11+
'feConvolveMatrix',
12+
'feDiffuseLighting',
13+
'feDisplacementMap',
14+
'feDistantLight',
15+
'feDropShadow',
16+
'feFlood',
17+
'feFuncA',
18+
'feFuncB',
19+
'feFuncG',
20+
'feFuncR',
21+
'feGaussianBlur',
22+
'feImage',
23+
'feMerge',
24+
'feMergeNode',
25+
'feMorphology',
26+
'feOffset',
27+
'fePointLight',
28+
'feSpecularLighting',
29+
'feSpotLight',
30+
'feTile',
31+
'feTurbulence',
32+
'foreignObject',
33+
'linearGradient',
34+
'radialGradient',
35+
'textPath',
36+
]
37+
38+
/**
39+
* testAgainstStringOrRegExp
40+
*
41+
* @param value string to test
42+
* @param comparison raw string or regex string
43+
*/
44+
function testAgainstStringOrRegExp(value: string, comparison: string | RegExp) {
45+
// If it's a RegExp, test directly
46+
if (comparison instanceof RegExp) {
47+
return comparison.test(value)
48+
? { match: value, pattern: comparison }
49+
: false
50+
}
51+
52+
// Check if it's RegExp in a string
53+
const firstComparisonChar = comparison[0]
54+
const lastComparisonChar = comparison[comparison.length - 1]
55+
const secondToLastComparisonChar = comparison[comparison.length - 2]
56+
57+
const comparisonIsRegex =
58+
firstComparisonChar === '/' &&
59+
(lastComparisonChar === '/' ||
60+
(secondToLastComparisonChar === '/' && lastComparisonChar === 'i'))
61+
62+
const hasCaseInsensitiveFlag = comparisonIsRegex && lastComparisonChar === 'i'
63+
64+
// If so, create a new RegExp from it
65+
if (comparisonIsRegex) {
66+
const valueMatches = hasCaseInsensitiveFlag
67+
? new RegExp(comparison.slice(1, -2), 'i').test(value)
68+
: new RegExp(comparison.slice(1, -1)).test(value)
69+
70+
return valueMatches
71+
}
72+
73+
// Otherwise, it's a string. Do a strict comparison
74+
return value === comparison
75+
}
76+
377
export default {
478
id: 'tagname-lowercase',
579
description: 'All html element names must be in lowercase.',
680
init(parser, reporter, options) {
7-
const exceptions: Array<string | boolean> = Array.isArray(options)
8-
? options
9-
: []
81+
const exceptions = (Array.isArray(options) ? options : []).concat(
82+
svgTagNameIgnores
83+
)
1084

1185
parser.addListener('tagstart,tagend', (event) => {
1286
const tagName = event.tagName
1387
if (
14-
exceptions.indexOf(tagName) === -1 &&
88+
!exceptions.find((exp) => testAgainstStringOrRegExp(tagName, exp)) &&
1589
tagName !== tagName.toLowerCase()
1690
) {
1791
reporter.error(

src/core/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ export interface Ruleset {
5656
'tag-no-obsolete'?: boolean
5757
'tag-pair'?: boolean
5858
'tag-self-close'?: boolean
59-
'tagname-lowercase'?: boolean
59+
'tagname-lowercase'?: boolean | Array<string | RegExp>
6060
'tagname-specialchars'?: boolean
6161
'tags-check'?: { [tagName: string]: Record<string, unknown> }
6262
'title-require'?: boolean

test/rules/tagname-lowercase.spec.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,27 @@ describe(`Rules: ${ruleId}`, () => {
2929
const messages = HTMLHint.verify(code, ruleOptions)
3030
expect(messages.length).toBe(0)
3131
})
32+
33+
it('Known SVG elements should be ignored with no config', () => {
34+
const code =
35+
'<svg><animateMotion /><linearGradient /><foreignObject /><textPath /></svg>'
36+
const messages = HTMLHint.verify(code, ruleOptions)
37+
expect(messages.length).toBe(0)
38+
})
39+
40+
it('Known SVG elements should be ignored with a config override', () => {
41+
const code = '<svg><feGaussianBlur /><radialGradient /></svg>'
42+
ruleOptions[ruleId] = ['customTag']
43+
const messages = HTMLHint.verify(code, ruleOptions)
44+
expect(messages.length).toBe(0)
45+
ruleOptions[ruleId] = true
46+
})
47+
48+
it('Set to array list should not result in an error', () => {
49+
const code = '<CustomComponent /><AnotherCamelCase />'
50+
ruleOptions[ruleId] = ['CustomComponent', 'AnotherCamelCase']
51+
const messages = HTMLHint.verify(code, ruleOptions)
52+
expect(messages.length).toBe(0)
53+
ruleOptions[ruleId] = true
54+
})
3255
})

website/src/content/docs/rules/tagname-lowercase.mdx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,20 @@ Level: <Badge text="Error" variant="danger" />
1414

1515
- `true`: enable rule
1616
- `false`: disable rule
17-
- `['clipPath', 'data-Test']`: Ignore some tagname name
17+
- `['clipPath', 'data-Test']`: enable rule except for the given tag names. All SVG camelCase elements are included, for example `linearGradient`, `foreignObject`
1818

1919
### The following patterns are **not** considered rule violations
2020

2121
```html
2222
<span><div>
2323
```
2424

25+
SVG elements with camelCase names (e.g. `linearGradient`, `foreignObject`) are allowed:
26+
27+
```html
28+
<svg><linearGradient /><foreignObject /><textPath /></svg>
29+
```
30+
2531
### The following pattern is considered a rule violation:
2632

2733
```html

0 commit comments

Comments
 (0)