Skip to content

Commit c7cb25c

Browse files
authored
feat: handle more specific cases in use-baseline (#318)
* feat: handle input type in use-baseline * feat: implement * Update .cspell.json * Update use-baseline.js * add test cases * Update use-baseline.test.js * apply reviews
1 parent b25ab1b commit c7cb25c

File tree

5 files changed

+181
-14
lines changed

5 files changed

+181
-14
lines changed

.cspell.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
],
1717
"words": [
1818
"foofoo",
19+
"rowspan",
1920
"tseslint",
2021
"frontmatter",
2122
"rehype",

packages/eslint-plugin/lib/rules/use-baseline.js

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,65 @@ module.exports = {
178178
return isSupported(globalAttrStatus);
179179
}
180180

181+
/**
182+
*
183+
* @param {string} element
184+
* @param {string} key
185+
* @param {string} value
186+
* @returns {string | null}
187+
*/
188+
function getElementAttributeSpecificStatusKey(element, key, value) {
189+
const elementName = element.toLowerCase();
190+
const attributeKey = key.toLowerCase();
191+
const attributeValue = value.toLowerCase();
192+
193+
// <input type="...">
194+
if (elementName === "input" && attributeKey === "type") {
195+
return `input.type_${attributeValue}`;
196+
}
197+
198+
// <a href="sms:0000..">
199+
if (
200+
elementName === "a" &&
201+
attributeKey === "href" &&
202+
attributeValue.trim().startsWith("sms:")
203+
) {
204+
return "a.href.href_sms";
205+
}
206+
207+
// <td rowspan="0"> <th rowspan="0">
208+
if (
209+
(elementName === "td" || elementName === "th") &&
210+
attributeKey === "rowspan" &&
211+
attributeValue === "0"
212+
) {
213+
return `${elementName}.rowspan.rowspan_zero`;
214+
}
215+
return null;
216+
}
217+
218+
/**
219+
* @param {string} element
220+
* @param {string} key
221+
* @param {string} value
222+
* @returns {boolean}
223+
*/
224+
function isSupportedElementSpecificAttributeKeyValue(element, key, value) {
225+
const statusKey = getElementAttributeSpecificStatusKey(
226+
element,
227+
key,
228+
value
229+
);
230+
if (!statusKey) {
231+
return true;
232+
}
233+
const elementStatus = elements.get(statusKey);
234+
if (!elementStatus) {
235+
return true;
236+
}
237+
return isSupported(elementStatus);
238+
}
239+
181240
/**
182241
* @param {Tag | ScriptTag | StyleTag} node
183242
* @param {string} elementName
@@ -224,6 +283,11 @@ module.exports = {
224283
elementName,
225284
attribute.key.value,
226285
attribute.value.value
286+
) ||
287+
!isSupportedElementSpecificAttributeKeyValue(
288+
elementName,
289+
attribute.key.value,
290+
attribute.value.value
227291
)
228292
) {
229293
context.report({

packages/eslint-plugin/lib/rules/utils/baseline.js

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@ const elements = new Map([
1111
["a.attributionsrc", "0:"],
1212
["a.download", "10:2019"],
1313
["a.href", "10:2015"],
14+
["a.href.href_sms", "0:"],
15+
["a.href.href_top", "10:2015"],
1416
["a.hreflang", "10:2015"],
17+
["a.implicit_noopener", "10:2021"],
1518
["a.ping", "0:"],
1619
["a.referrerpolicy", "10:2020"],
1720
["a.referrerpolicy.no-referrer-when-downgrade", "0:"],
@@ -22,6 +25,7 @@ const elements = new Map([
2225
["a.rel.noreferrer", "10:2015"],
2326
["a.target", "10:2015"],
2427
["a.target.unfencedTop", "0:"],
28+
["a.text_fragments", "5:2024"],
2529
["a.type", "10:2015"],
2630
["abbr", "10:2015"],
2731
["address", "10:2015"],
@@ -31,6 +35,7 @@ const elements = new Map([
3135
["area.coords", "10:2015"],
3236
["area.download", "10:2017"],
3337
["area.href", "10:2015"],
38+
["area.implicit_noopener", "10:2021"],
3439
["area.ping", "0:"],
3540
["area.referrerpolicy", "10:2020"],
3641
["area.referrerpolicy.no-referrer-when-downgrade", "0:"],
@@ -56,6 +61,8 @@ const elements = new Map([
5661
["b", "10:2015"],
5762
["base", "10:2015"],
5863
["base.href", "10:2015"],
64+
["base.href.forbid_data_javascript_urls", "5:2024"],
65+
["base.href.relative_url", "10:2015"],
5966
["base.target", "10:2015"],
6067
["bdi", "10:2020"],
6168
["bdo", "10:2015"],
@@ -75,6 +82,7 @@ const elements = new Map([
7582
["button.formtarget", "10:2015"],
7683
["button.name", "10:2015"],
7784
["button.popovertarget", "5:2024"],
85+
["button.popovertarget.implicit_anchor_reference", "0:"],
7886
["button.popovertargetaction", "5:2024"],
7987
["button.type", "10:2015"],
8088
["button.value", "10:2015"],
@@ -203,6 +211,7 @@ const elements = new Map([
203211
["iframe.width", "10:2015"],
204212
["img", "10:2015"],
205213
["img.alt", "10:2015"],
214+
["img.aspect_ratio_computed_from_attributes", "10:2021"],
206215
["img.attributionsrc", "0:"],
207216
["img.crossorigin", "10:2015"],
208217
["img.decoding", "10:2020"],
@@ -244,12 +253,38 @@ const elements = new Map([
244253
["input.pattern", "10:2015"],
245254
["input.placeholder", "10:2015"],
246255
["input.popovertarget", "5:2024"],
256+
["input.popovertarget.implicit_anchor_reference", "0:"],
247257
["input.popovertargetaction", "5:2024"],
248258
["input.readonly", "10:2015"],
249259
["input.required", "10:2015"],
250260
["input.size", "10:2015"],
251261
["input.src", "10:2015"],
252262
["input.step", "10:2015"],
263+
["input.type_button", "10:2015"],
264+
["input.type_checkbox", "10:2015"],
265+
["input.type_color", "0:"],
266+
["input.type_date", "10:2021"],
267+
["input.type_datetime-local", "10:2021"],
268+
["input.type_email", "10:2015"],
269+
["input.type_file", "10:2015"],
270+
["input.type_hidden", "10:2015"],
271+
["input.type_image", "10:2015"],
272+
["input.type_month", "0:"],
273+
["input.type_number", "10:2015"],
274+
["input.type_password", "10:2015"],
275+
["input.type_password.insecure_login_handling", "0:"],
276+
["input.type_radio", "10:2015"],
277+
["input.type_range", "10:2017"],
278+
["input.type_range.tick_marks", "5:2023"],
279+
["input.type_range.vertical_orientation", "5:2024"],
280+
["input.type_reset", "10:2015"],
281+
["input.type_search", "10:2015"],
282+
["input.type_submit", "10:2015"],
283+
["input.type_tel", "10:2015"],
284+
["input.type_text", "10:2015"],
285+
["input.type_time", "10:2021"],
286+
["input.type_url", "10:2015"],
287+
["input.type_week", "0:"],
253288
["ins", "10:2015"],
254289
["ins.cite", "10:2015"],
255290
["ins.datetime", "10:2015"],
@@ -276,6 +311,7 @@ const elements = new Map([
276311
["link.referrerpolicy.origin-when-cross-origin", "0:"],
277312
["link.referrerpolicy.unsafe-url", "0:"],
278313
["link.rel", "10:2015"],
314+
["link.rel.alternate_stylesheet", "0:"],
279315
["link.rel.dns-prefetch", "5:2024"],
280316
["link.rel.expect", "0:"],
281317
["link.rel.manifest", "0:"],
@@ -372,20 +408,25 @@ const elements = new Map([
372408
["script.type.module", "10:2018"],
373409
["script.type.speculationrules", "0:"],
374410
["script.type.speculationrules.eagerness", "0:"],
411+
["script.type.speculationrules.expects_no_vary_search", "0:"],
375412
["script.type.speculationrules.prefetch", "0:"],
376413
["script.type.speculationrules.prerender", "0:"],
414+
["script.type.speculationrules.referrer_policy", "0:"],
415+
["script.type.speculationrules.relative_to", "0:"],
377416
["script.type.speculationrules.requires", "0:"],
378417
[
379418
"script.type.speculationrules.requires.anonymous-client-ip-when-cross-origin",
380419
"0:",
381420
],
421+
["script.type.speculationrules.source_optional", "0:"],
382422
["script.type.speculationrules.urls", "0:"],
383423
["script.type.speculationrules.where", "0:"],
384424
["search", "5:2023"],
385425
["section", "10:2015"],
386426
["select", "10:2015"],
387427
["select.disabled", "10:2015"],
388428
["select.form", "10:2015"],
429+
["select.hr_in_select", "0:"],
389430
["select.multiple", "10:2015"],
390431
["select.name", "10:2015"],
391432
["select.required", "10:2015"],
@@ -409,13 +450,15 @@ const elements = new Map([
409450
["style.media", "10:2015"],
410451
["sub", "10:2015"],
411452
["summary", "10:2020"],
453+
["summary.display_list_item", "0:"],
412454
["sup", "10:2015"],
413455
["table", "10:2015"],
414456
["tbody", "10:2015"],
415457
["td", "10:2015"],
416458
["td.colspan", "10:2015"],
417459
["td.headers", "10:2015"],
418460
["td.rowspan", "10:2015"],
461+
["td.rowspan.rowspan_zero", "0:"],
419462
["template", "10:2015"],
420463
["template.shadowrootclonable", "0:"],
421464
["template.shadowrootdelegatesfocus", "0:"],
@@ -431,6 +474,7 @@ const elements = new Map([
431474
["textarea.minlength", "10:2018"],
432475
["textarea.name", "10:2015"],
433476
["textarea.placeholder", "10:2015"],
477+
["textarea.placeholder.line_breaks", "10:2022"],
434478
["textarea.readonly", "10:2015"],
435479
["textarea.required", "10:2015"],
436480
["textarea.rows", "10:2015"],
@@ -443,6 +487,7 @@ const elements = new Map([
443487
["th.colspan", "10:2015"],
444488
["th.headers", "10:2015"],
445489
["th.rowspan", "10:2015"],
490+
["th.rowspan.rowspan_zero", "0:"],
446491
["th.scope", "10:2015"],
447492
["thead", "10:2015"],
448493
["time", "10:2017"],
@@ -459,6 +504,7 @@ const elements = new Map([
459504
["ul", "10:2015"],
460505
["var", "10:2015"],
461506
["video", "10:2015"],
507+
["video.aspect_ratio_computed_from_attributes", "10:2020"],
462508
["video.autoplay", "10:2016"],
463509
["video.controls", "10:2015"],
464510
["video.controlslist", "0:"],
@@ -484,10 +530,12 @@ const globalAttributes = new Map([
484530
["enterkeyhint", "10:2021"],
485531
["exportparts", "10:2020"],
486532
["inert", "5:2023"],
533+
["inert.ignores_find_in_page", "0:"],
487534
["inputmode", "10:2021"],
488535
["is", "0:"],
489536
["lang", "10:2015"],
490537
["nonce", "10:2022"],
538+
["nonce.nonce_hiding", "10:2022"],
491539
["part", "10:2020"],
492540
["popover", "5:2024"],
493541
["popover.hint", "0:"],
@@ -496,6 +544,7 @@ const globalAttributes = new Map([
496544
["style", "10:2015"],
497545
["tabindex", "10:2015"],
498546
["title", "10:2015"],
547+
["title.multi-line_titles", "10:2018"],
499548
["translate", "5:2023"],
500549
["virtualkeyboardpolicy", "0:"],
501550
["writingsuggestions", "0:"],

packages/eslint-plugin/tests/rules/use-baseline.test.js

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,27 @@ ruleTester.run("use-baseline", rule, {
4040
},
4141
],
4242
},
43+
{
44+
code: `<input type="number"></input>`,
45+
},
46+
{
47+
code: `<input type="unknown"></input>`,
48+
},
49+
{
50+
code: `<input type="tel"></input>`,
51+
},
52+
{
53+
code: `<a href="https://html-eslint.org"></a>`,
54+
},
55+
{
56+
code: `<td></td>`,
57+
},
58+
{
59+
code: `<td rowspan="1"></td>`,
60+
},
61+
{
62+
code: `<th rowspan="2"></th>`,
63+
},
4364
],
4465
invalid: [
4566
{
@@ -185,6 +206,50 @@ ruleTester.run("use-baseline", rule, {
185206
},
186207
],
187208
},
209+
{
210+
code: `<input type="week"></input>`,
211+
errors: [
212+
{
213+
message:
214+
"Attribute 'type=\"week\"' on '<input>' is not a widely available baseline feature.",
215+
column: 14,
216+
endColumn: 18,
217+
},
218+
],
219+
},
220+
{
221+
code: `<a href="sms:00000"></a>`,
222+
errors: [
223+
{
224+
message:
225+
"Attribute 'href=\"sms:00000\"' on '<a>' is not a widely available baseline feature.",
226+
column: 10,
227+
endColumn: 19,
228+
},
229+
],
230+
},
231+
{
232+
code: `<td rowspan="0"></td>`,
233+
errors: [
234+
{
235+
message:
236+
"Attribute 'rowspan=\"0\"' on '<td>' is not a widely available baseline feature.",
237+
column: 14,
238+
endColumn: 15,
239+
},
240+
],
241+
},
242+
{
243+
code: `<th rowspan="0"></th>`,
244+
errors: [
245+
{
246+
message:
247+
"Attribute 'rowspan=\"0\"' on '<th>' is not a widely available baseline feature.",
248+
column: 14,
249+
endColumn: 15,
250+
},
251+
],
252+
},
188253
],
189254
});
190255

tools/base-line/generate-baseline.mjs

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -115,33 +115,21 @@ function shortenKey(key) {
115115
return rest.join(".");
116116
}
117117

118-
/**
119-
* @param {string} shortenKey
120-
* @returns {boolean}
121-
*/
122-
function shouldIgnore(shortenKey) {
123-
return shortenKey.includes("_");
124-
}
125-
126118
async function generateBaseline() {
127119
const { elements, globalAttributes } = getFeatureIdAndCompatKeys();
128120
const elementsStatus = {};
129121
const globalAttributesStatus = {};
130122
for (const [featureId, compatKey] of elements) {
131123
const status = getStatus(featureId, compatKey);
132124
const key = shortenKey(compatKey);
133-
if (shouldIgnore(key)) {
134-
continue;
135-
}
125+
136126
elementsStatus[key] = mapFeatureStatus(status);
137127
}
138128

139129
for (const [featureId, compatKey] of globalAttributes) {
140130
const status = getStatus(featureId, compatKey);
141131
const key = shortenKey(compatKey);
142-
if (shouldIgnore(key)) {
143-
continue;
144-
}
132+
145133
globalAttributesStatus[key] = mapFeatureStatus(status);
146134
}
147135

0 commit comments

Comments
 (0)