Skip to content

Commit 4d05d57

Browse files
committed
Skip non-HTML elements
Signed-off-by: Sebastian Malton <[email protected]>
1 parent ac61a70 commit 4d05d57

File tree

2 files changed

+811
-645
lines changed

2 files changed

+811
-645
lines changed

lib/rules/no-invalid-html-attribute.js

Lines changed: 174 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,155 @@ const rel = new Map([
4747
]);
4848
VALID_VALUES.set('rel', rel);
4949

50+
/**
51+
* The set of all possible HTML elements. Used for skipping custom types
52+
* @type {Set<string>}
53+
*/
54+
const HTML_ELEMENTS = new Set([
55+
'html',
56+
'base',
57+
'head',
58+
'link',
59+
'meta',
60+
'style',
61+
'title',
62+
'body',
63+
'address',
64+
'article',
65+
'aside',
66+
'footer',
67+
'header',
68+
'h1',
69+
'h2',
70+
'h3',
71+
'h4',
72+
'h5',
73+
'h6',
74+
'main',
75+
'nav',
76+
'section',
77+
'blockquote',
78+
'dd',
79+
'div',
80+
'dl',
81+
'dt',
82+
'figcaption',
83+
'figure',
84+
'hr',
85+
'li',
86+
'ol',
87+
'p',
88+
'pre',
89+
'ul',
90+
'a',
91+
'abbr',
92+
'b',
93+
'bdi',
94+
'bdo',
95+
'br',
96+
'cite',
97+
'code',
98+
'data',
99+
'dfn',
100+
'em',
101+
'i',
102+
'kbd',
103+
'mark',
104+
'q',
105+
'rp',
106+
'rt',
107+
'ruby',
108+
's',
109+
'samp',
110+
'small',
111+
'span',
112+
'strong',
113+
'sub',
114+
'sup',
115+
'time',
116+
'u',
117+
'var',
118+
'wbr',
119+
'area',
120+
'audio',
121+
'img',
122+
'map',
123+
'track',
124+
'video',
125+
'embed',
126+
'iframe',
127+
'object',
128+
'param',
129+
'picture',
130+
'portal',
131+
'source',
132+
'svg',
133+
'math',
134+
'canvas',
135+
'noscript',
136+
'script',
137+
'del',
138+
'ins',
139+
'caption',
140+
'col',
141+
'colgroup',
142+
'table',
143+
'tbody',
144+
'td',
145+
'tfoot',
146+
'th',
147+
'thead',
148+
'tr',
149+
'button',
150+
'datalist',
151+
'fieldset',
152+
'form',
153+
'input',
154+
'label',
155+
'legend',
156+
'meter',
157+
'optgroup',
158+
'option',
159+
'output',
160+
'progress',
161+
'select',
162+
'textarea',
163+
'details',
164+
'dialog',
165+
'menu',
166+
'summary',
167+
'slot',
168+
'template',
169+
'acronym',
170+
'applet',
171+
'basefont',
172+
'bgsound',
173+
'big',
174+
'blink',
175+
'center',
176+
'content',
177+
'dir',
178+
'font',
179+
'frame',
180+
'frameset',
181+
'hgroup',
182+
'image',
183+
'keygen',
184+
'marquee',
185+
'menuitem',
186+
'nobr',
187+
'noembed',
188+
'noframes',
189+
'plaintext',
190+
'rb',
191+
'rtc',
192+
'shadow',
193+
'spacer',
194+
'strike',
195+
'tt',
196+
'xmp'
197+
]);
198+
50199
/**
51200
* Map between attributes and set of tags that the attribute is valid on
52201
* @type {Map<string, Set<string>>}
@@ -205,14 +354,14 @@ function checkPropValidValue(context, node, value, attribute) {
205354
return context.report({
206355
node: value,
207356
message: `${value.raw} is never a valid "${attribute}" attribute value.`
208-
})
357+
});
209358
}
210359

211360
if (!validTagSet.has(node.arguments[0].value)) {
212361
return context.report({
213362
node: value,
214363
message: `${value.raw} is not a valid value of "${attribute}" for a ${node.arguments[0].raw} element`
215-
})
364+
});
216365
}
217366
}
218367

@@ -223,23 +372,20 @@ function checkPropValidValue(context, node, value, attribute) {
223372
* @param {string} attribute
224373
*/
225374
function checkCreateProps(context, node, attribute) {
226-
if (node.arguments[0].type !== 'Literal') {
227-
return; // can only check literals
228-
}
229-
230375
const propsArg = node.arguments[1];
231376

232-
if (!propsArg || propsArg.type !== 'ObjectExpression'
233-
) {
377+
if (!propsArg || propsArg.type !== 'ObjectExpression') {
234378
return; // can't check variables, computed, or shorthands
235379
}
236380

237381
for (const prop of propsArg.properties) {
238382
if (prop.key.type !== 'Identifier') {
383+
// eslint-disable-next-line no-continue
239384
continue; // cannot check computed keys
240385
}
241386

242387
if (prop.key.name !== attribute) {
388+
// eslint-disable-next-line no-continue
243389
continue; // ignore not this attribute
244390
}
245391

@@ -254,6 +400,7 @@ function checkCreateProps(context, node, attribute) {
254400
message: `The "${attribute}" attribute only has meaning on the tags: ${tagNames}`
255401
});
256402

403+
// eslint-disable-next-line no-continue
257404
continue;
258405
}
259406

@@ -263,10 +410,12 @@ function checkCreateProps(context, node, attribute) {
263410
message: `The "${attribute}" attribute cannot be a method.`
264411
});
265412

413+
// eslint-disable-next-line no-continue
266414
continue;
267415
}
268416

269417
if (prop.shorthand || prop.computed) {
418+
// eslint-disable-next-line no-continue
270419
continue; // cannot check these
271420
}
272421

@@ -275,6 +424,7 @@ function checkCreateProps(context, node, attribute) {
275424
checkPropValidValue(context, node, value, attribute);
276425
}
277426

427+
// eslint-disable-next-line no-continue
278428
continue;
279429
}
280430

@@ -309,6 +459,11 @@ module.exports = {
309459
return;
310460
}
311461

462+
// ignore non-HTML elements
463+
if (!HTML_ELEMENTS.has(node.parent.name.name)) {
464+
return;
465+
}
466+
312467
checkAttribute(context, node);
313468
},
314469

@@ -317,6 +472,17 @@ module.exports = {
317472
return;
318473
}
319474

475+
const elemNameArg = node.arguments[0];
476+
477+
if (!elemNameArg || elemNameArg.type !== 'Literal') {
478+
return; // can only check literals
479+
}
480+
481+
// ignore non-HTML elements
482+
if (!HTML_ELEMENTS.has(elemNameArg.value)) {
483+
return;
484+
}
485+
320486
const attributes = new Set(context.options[0] || DEFAULT_ATTRIBUTES);
321487

322488
for (const attribute of attributes) {

0 commit comments

Comments
 (0)