html-validate 10.8.0 → 10.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/cli.js +10 -10
- package/dist/cjs/cli.js.map +1 -1
- package/dist/cjs/core-browser.js +1 -1
- package/dist/cjs/core-browser.js.map +1 -1
- package/dist/cjs/core-nodejs.js +1 -1
- package/dist/cjs/core-nodejs.js.map +1 -1
- package/dist/cjs/core.js +333 -142
- package/dist/cjs/core.js.map +1 -1
- package/dist/cjs/elements.js +30 -18
- package/dist/cjs/elements.js.map +1 -1
- package/dist/cjs/html-validate.js +23 -15
- package/dist/cjs/html-validate.js.map +1 -1
- package/dist/cjs/matchers-jestonly.js +2 -2
- package/dist/cjs/matchers-jestonly.js.map +1 -1
- package/dist/cjs/tsdoc-metadata.json +1 -1
- package/dist/cjs/utils/natural-join.js +1 -1
- package/dist/cjs/utils/natural-join.js.map +1 -1
- package/dist/esm/cli.js +10 -10
- package/dist/esm/cli.js.map +1 -1
- package/dist/esm/core-browser.js +1 -1
- package/dist/esm/core-browser.js.map +1 -1
- package/dist/esm/core-nodejs.js +1 -1
- package/dist/esm/core-nodejs.js.map +1 -1
- package/dist/esm/core.js +333 -142
- package/dist/esm/core.js.map +1 -1
- package/dist/esm/elements.js +30 -18
- package/dist/esm/elements.js.map +1 -1
- package/dist/esm/html-validate.js +24 -16
- package/dist/esm/html-validate.js.map +1 -1
- package/dist/esm/matchers-jestonly.js +2 -2
- package/dist/esm/matchers-jestonly.js.map +1 -1
- package/dist/esm/utils/natural-join.js +1 -1
- package/dist/esm/utils/natural-join.js.map +1 -1
- package/dist/schema/elements.json +6 -0
- package/dist/tsdoc-metadata.json +1 -1
- package/dist/types/browser.d.ts +24 -9
- package/dist/types/index.d.ts +24 -9
- package/package.json +3 -3
package/dist/esm/core.js
CHANGED
|
@@ -395,6 +395,7 @@ function stringify(value) {
|
|
|
395
395
|
class WrappedError extends Error {
|
|
396
396
|
constructor(message) {
|
|
397
397
|
super(stringify(message));
|
|
398
|
+
this.name = "WrappedError";
|
|
398
399
|
}
|
|
399
400
|
}
|
|
400
401
|
|
|
@@ -409,8 +410,7 @@ function ensureError(value) {
|
|
|
409
410
|
class NestedError extends Error {
|
|
410
411
|
constructor(message, nested) {
|
|
411
412
|
super(message);
|
|
412
|
-
|
|
413
|
-
this.name = NestedError.name;
|
|
413
|
+
this.name = "NestedError";
|
|
414
414
|
if (nested?.stack) {
|
|
415
415
|
this.stack ??= "";
|
|
416
416
|
this.stack += `
|
|
@@ -422,8 +422,7 @@ Caused by: ${nested.stack}`;
|
|
|
422
422
|
class UserError extends NestedError {
|
|
423
423
|
constructor(message, nested) {
|
|
424
424
|
super(message, nested);
|
|
425
|
-
|
|
426
|
-
this.name = UserError.name;
|
|
425
|
+
this.name = "UserError";
|
|
427
426
|
Object.defineProperty(this, "isUserError", {
|
|
428
427
|
value: true,
|
|
429
428
|
enumerable: false,
|
|
@@ -446,8 +445,7 @@ class InheritError extends UserError {
|
|
|
446
445
|
constructor({ tagName, inherit }) {
|
|
447
446
|
const message = `Element <${tagName}> cannot inherit from <${inherit}>: no such element`;
|
|
448
447
|
super(message);
|
|
449
|
-
|
|
450
|
-
this.name = InheritError.name;
|
|
448
|
+
this.name = "InheritError";
|
|
451
449
|
this.tagName = tagName;
|
|
452
450
|
this.inherit = inherit;
|
|
453
451
|
this.filename = null;
|
|
@@ -489,6 +487,7 @@ class SchemaValidationError extends UserError {
|
|
|
489
487
|
constructor(filename, message, obj, schema, errors) {
|
|
490
488
|
const summary = getSummary(schema, obj, errors);
|
|
491
489
|
super(`${message}: ${summary}`);
|
|
490
|
+
this.name = "SchemaValidationError";
|
|
492
491
|
this.filename = filename;
|
|
493
492
|
this.obj = obj;
|
|
494
493
|
this.schema = schema;
|
|
@@ -636,6 +635,18 @@ const patternProperties = {
|
|
|
636
635
|
}
|
|
637
636
|
]
|
|
638
637
|
},
|
|
638
|
+
submitButton: {
|
|
639
|
+
title: "Mark this element as a submit button",
|
|
640
|
+
description: "This element can be used to submit forms.",
|
|
641
|
+
anyOf: [
|
|
642
|
+
{
|
|
643
|
+
type: "boolean"
|
|
644
|
+
},
|
|
645
|
+
{
|
|
646
|
+
"function": true
|
|
647
|
+
}
|
|
648
|
+
]
|
|
649
|
+
},
|
|
639
650
|
templateRoot: {
|
|
640
651
|
title: "Mark element as an element ignoring DOM ancestry, i.e. <template>.",
|
|
641
652
|
description: "The <template> element can contain any elements.",
|
|
@@ -1019,6 +1030,7 @@ const MetaCopyableProperty = [
|
|
|
1019
1030
|
"form",
|
|
1020
1031
|
"formAssociated",
|
|
1021
1032
|
"labelable",
|
|
1033
|
+
"submitButton",
|
|
1022
1034
|
"attributes",
|
|
1023
1035
|
"aria",
|
|
1024
1036
|
"permittedContent",
|
|
@@ -1033,7 +1045,7 @@ function setMetaProperty(dst, key, value) {
|
|
|
1033
1045
|
}
|
|
1034
1046
|
|
|
1035
1047
|
function isSet(value) {
|
|
1036
|
-
return
|
|
1048
|
+
return value !== void 0;
|
|
1037
1049
|
}
|
|
1038
1050
|
function flag(value) {
|
|
1039
1051
|
return value ? true : void 0;
|
|
@@ -1048,7 +1060,7 @@ function migrateSingleAttribute(src, key) {
|
|
|
1048
1060
|
result.required = flag(src.requiredAttributes?.includes(key));
|
|
1049
1061
|
result.omit = void 0;
|
|
1050
1062
|
const attr = src.attributes ? src.attributes[key] : void 0;
|
|
1051
|
-
if (
|
|
1063
|
+
if (attr === void 0) {
|
|
1052
1064
|
return stripUndefined(result);
|
|
1053
1065
|
}
|
|
1054
1066
|
if (attr === null) {
|
|
@@ -1075,7 +1087,7 @@ function migrateAttributes(src) {
|
|
|
1075
1087
|
...src.requiredAttributes ?? [],
|
|
1076
1088
|
...src.deprecatedAttributes ?? []
|
|
1077
1089
|
/* eslint-disable-next-line sonarjs/no-alphabetical-sort -- not really needed in this case, this is a-z anyway */
|
|
1078
|
-
].
|
|
1090
|
+
].toSorted();
|
|
1079
1091
|
const entries = keys.map((key) => {
|
|
1080
1092
|
return [key, migrateSingleAttribute(src, key)];
|
|
1081
1093
|
});
|
|
@@ -1103,9 +1115,7 @@ function migrateElement(src) {
|
|
|
1103
1115
|
const implicitRole = normalizeAriaImplicitRole(src.implicitRole ?? src.aria?.implicitRole);
|
|
1104
1116
|
const result = {
|
|
1105
1117
|
...src,
|
|
1106
|
-
|
|
1107
|
-
formAssociated: void 0
|
|
1108
|
-
},
|
|
1118
|
+
formAssociated: void 0,
|
|
1109
1119
|
attributes: migrateAttributes(src),
|
|
1110
1120
|
textContent: src.textContent,
|
|
1111
1121
|
focusable: src.focusable ?? false,
|
|
@@ -1140,7 +1150,8 @@ const dynamicKeys = [
|
|
|
1140
1150
|
"phrasing",
|
|
1141
1151
|
"embedded",
|
|
1142
1152
|
"interactive",
|
|
1143
|
-
"labelable"
|
|
1153
|
+
"labelable",
|
|
1154
|
+
"submitButton"
|
|
1144
1155
|
];
|
|
1145
1156
|
const schemaCache = /* @__PURE__ */ new Map();
|
|
1146
1157
|
function clone(src) {
|
|
@@ -1410,13 +1421,10 @@ class Attribute {
|
|
|
1410
1421
|
*/
|
|
1411
1422
|
constructor(key, value, keyLocation, valueLocation, originalAttribute) {
|
|
1412
1423
|
this.key = key;
|
|
1413
|
-
this.value = value;
|
|
1424
|
+
this.value = value ?? null;
|
|
1414
1425
|
this.keyLocation = keyLocation;
|
|
1415
1426
|
this.valueLocation = valueLocation;
|
|
1416
1427
|
this.originalAttribute = originalAttribute;
|
|
1417
|
-
if (typeof this.value === "undefined") {
|
|
1418
|
-
this.value = null;
|
|
1419
|
-
}
|
|
1420
1428
|
}
|
|
1421
1429
|
/**
|
|
1422
1430
|
* Flag set to true if the attribute value is static.
|
|
@@ -1560,11 +1568,11 @@ class Context {
|
|
|
1560
1568
|
while ((offset = consumed.indexOf("\n")) >= 0) {
|
|
1561
1569
|
this.line++;
|
|
1562
1570
|
this.column = 1;
|
|
1563
|
-
consumed = consumed.
|
|
1571
|
+
consumed = consumed.slice(offset + 1);
|
|
1564
1572
|
}
|
|
1565
1573
|
this.column += consumed.length;
|
|
1566
1574
|
this.offset += n;
|
|
1567
|
-
this.string = this.string.
|
|
1575
|
+
this.string = this.string.slice(n);
|
|
1568
1576
|
this.state = state;
|
|
1569
1577
|
}
|
|
1570
1578
|
getLocation(size) {
|
|
@@ -1742,7 +1750,7 @@ class DOMNode {
|
|
|
1742
1750
|
* node has no children.
|
|
1743
1751
|
*/
|
|
1744
1752
|
get lastChild() {
|
|
1745
|
-
return this.childNodes
|
|
1753
|
+
return this.childNodes.at(-1) ?? null;
|
|
1746
1754
|
}
|
|
1747
1755
|
/**
|
|
1748
1756
|
* @internal
|
|
@@ -1857,7 +1865,7 @@ function parse(text, baseLocation) {
|
|
|
1857
1865
|
begin++;
|
|
1858
1866
|
continue;
|
|
1859
1867
|
}
|
|
1860
|
-
const token = text.
|
|
1868
|
+
const token = text.slice(begin, end);
|
|
1861
1869
|
tokens.push(token);
|
|
1862
1870
|
if (locations && baseLocation) {
|
|
1863
1871
|
const location = sliceLocation(baseLocation, begin, end);
|
|
@@ -1872,7 +1880,7 @@ class DOMTokenList extends Array {
|
|
|
1872
1880
|
locations;
|
|
1873
1881
|
constructor(value, location) {
|
|
1874
1882
|
if (value && typeof value === "string") {
|
|
1875
|
-
const normalized = value.
|
|
1883
|
+
const normalized = value.replaceAll(/[\t\r\n]/g, " ");
|
|
1876
1884
|
const { tokens, locations } = parse(normalized, location);
|
|
1877
1885
|
super(...tokens);
|
|
1878
1886
|
this.locations = locations;
|
|
@@ -1962,7 +1970,7 @@ function nthChild(node, args) {
|
|
|
1962
1970
|
if (!args) {
|
|
1963
1971
|
throw new Error("Missing argument to nth-child");
|
|
1964
1972
|
}
|
|
1965
|
-
const n = parseInt(args.trim(), 10);
|
|
1973
|
+
const n = Number.parseInt(args.trim(), 10);
|
|
1966
1974
|
const cur = getNthChild(node);
|
|
1967
1975
|
return cur === n;
|
|
1968
1976
|
}
|
|
@@ -1987,7 +1995,7 @@ function factory(name, context) {
|
|
|
1987
1995
|
}
|
|
1988
1996
|
|
|
1989
1997
|
function stripslashes(value) {
|
|
1990
|
-
return value.
|
|
1998
|
+
return value.replaceAll(/\\(.)/g, "$1");
|
|
1991
1999
|
}
|
|
1992
2000
|
class Condition {
|
|
1993
2001
|
}
|
|
@@ -2186,7 +2194,7 @@ function candidatesFromCombinator(element, combinator) {
|
|
|
2186
2194
|
}
|
|
2187
2195
|
}
|
|
2188
2196
|
function matchElement(element, compounds, context) {
|
|
2189
|
-
const last = compounds
|
|
2197
|
+
const last = compounds.at(-1);
|
|
2190
2198
|
if (!last.match(element, context)) {
|
|
2191
2199
|
return false;
|
|
2192
2200
|
}
|
|
@@ -2203,7 +2211,7 @@ function matchElement(element, compounds, context) {
|
|
|
2203
2211
|
return false;
|
|
2204
2212
|
}
|
|
2205
2213
|
|
|
2206
|
-
const escapedCodepoints = ["9", "a", "d"];
|
|
2214
|
+
const escapedCodepoints = /* @__PURE__ */ new Set(["9", "a", "d"]);
|
|
2207
2215
|
function* splitSelectorElements(selector) {
|
|
2208
2216
|
let begin = 0;
|
|
2209
2217
|
let end = 0;
|
|
@@ -2218,7 +2226,7 @@ function* splitSelectorElements(selector) {
|
|
|
2218
2226
|
return 0 /* INITIAL */;
|
|
2219
2227
|
}
|
|
2220
2228
|
function escapedState(ch) {
|
|
2221
|
-
if (escapedCodepoints.
|
|
2229
|
+
if (escapedCodepoints.has(ch)) {
|
|
2222
2230
|
return 1 /* ESCAPED */;
|
|
2223
2231
|
}
|
|
2224
2232
|
return 0 /* INITIAL */;
|
|
@@ -2258,7 +2266,7 @@ function unescapeCodepoint(value) {
|
|
|
2258
2266
|
"\\a ": "\n",
|
|
2259
2267
|
"\\d ": "\r"
|
|
2260
2268
|
};
|
|
2261
|
-
return value.
|
|
2269
|
+
return value.replaceAll(
|
|
2262
2270
|
/(\\[\u0039\u0061\u0064] )/g,
|
|
2263
2271
|
(_, codepoint) => replacement[codepoint]
|
|
2264
2272
|
);
|
|
@@ -2269,7 +2277,7 @@ function escapeSelectorComponent(text) {
|
|
|
2269
2277
|
"\n": "\\a ",
|
|
2270
2278
|
"\r": "\\d "
|
|
2271
2279
|
};
|
|
2272
|
-
return text.toString().
|
|
2280
|
+
return text.toString().replaceAll(/([\t\n\r]|[^a-z0-9_-])/gi, (_, ch) => {
|
|
2273
2281
|
if (codepoints[ch]) {
|
|
2274
2282
|
return codepoints[ch];
|
|
2275
2283
|
} else {
|
|
@@ -2318,7 +2326,7 @@ class Selector {
|
|
|
2318
2326
|
}
|
|
2319
2327
|
}
|
|
2320
2328
|
static parse(selector) {
|
|
2321
|
-
selector = selector.
|
|
2329
|
+
selector = selector.replaceAll(/([+~>]) /g, "$1");
|
|
2322
2330
|
return Array.from(splitSelectorElements(selector), (element) => {
|
|
2323
2331
|
return new Compound(unescapeCodepoint(element));
|
|
2324
2332
|
});
|
|
@@ -2614,7 +2622,7 @@ class HtmlElement extends DOMNode {
|
|
|
2614
2622
|
}
|
|
2615
2623
|
parts.push(`${cur.tagName.toLowerCase()}:nth-child(${String(index + 1)})`);
|
|
2616
2624
|
}
|
|
2617
|
-
return parts.
|
|
2625
|
+
return parts.toReversed().join(" > ");
|
|
2618
2626
|
}
|
|
2619
2627
|
/**
|
|
2620
2628
|
* Tests if this element has given tagname.
|
|
@@ -2656,7 +2664,7 @@ class HtmlElement extends DOMNode {
|
|
|
2656
2664
|
this.metaElement ??= {};
|
|
2657
2665
|
for (const key of MetaCopyableProperty) {
|
|
2658
2666
|
const value = meta[key];
|
|
2659
|
-
if (
|
|
2667
|
+
if (value !== void 0) {
|
|
2660
2668
|
setMetaProperty(this.metaElement, key, value);
|
|
2661
2669
|
} else {
|
|
2662
2670
|
delete this.metaElement[key];
|
|
@@ -2760,8 +2768,8 @@ class HtmlElement extends DOMNode {
|
|
|
2760
2768
|
if (tabindex.value instanceof DynamicValue) {
|
|
2761
2769
|
return this.cacheSet(TABINDEX, 0);
|
|
2762
2770
|
}
|
|
2763
|
-
const parsed = parseInt(tabindex.value, 10);
|
|
2764
|
-
if (isNaN(parsed)) {
|
|
2771
|
+
const parsed = Number.parseInt(tabindex.value, 10);
|
|
2772
|
+
if (Number.isNaN(parsed)) {
|
|
2765
2773
|
return this.cacheSet(TABINDEX, null);
|
|
2766
2774
|
}
|
|
2767
2775
|
return this.cacheSet(TABINDEX, parsed);
|
|
@@ -2865,7 +2873,10 @@ class HtmlElement extends DOMNode {
|
|
|
2865
2873
|
*/
|
|
2866
2874
|
get lastElementChild() {
|
|
2867
2875
|
const children = this.childElements;
|
|
2868
|
-
return children.length > 0 ?
|
|
2876
|
+
return children.length > 0 ? (
|
|
2877
|
+
/* eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- it is checked right before this */
|
|
2878
|
+
children.at(-1)
|
|
2879
|
+
) : null;
|
|
2869
2880
|
}
|
|
2870
2881
|
get siblings() {
|
|
2871
2882
|
return this.parent ? this.parent.childElements : [this];
|
|
@@ -2988,7 +2999,9 @@ function depthFirst(root, callback) {
|
|
|
2988
2999
|
root = root.root;
|
|
2989
3000
|
}
|
|
2990
3001
|
function visit(node) {
|
|
2991
|
-
node.childElements
|
|
3002
|
+
for (const child of node.childElements) {
|
|
3003
|
+
visit(child);
|
|
3004
|
+
}
|
|
2992
3005
|
if (!node.isRootElement()) {
|
|
2993
3006
|
callback(node);
|
|
2994
3007
|
}
|
|
@@ -3077,7 +3090,7 @@ class DOMTree {
|
|
|
3077
3090
|
}
|
|
3078
3091
|
}
|
|
3079
3092
|
|
|
3080
|
-
const allowedKeys = ["exclude"];
|
|
3093
|
+
const allowedKeys = /* @__PURE__ */ new Set(["exclude"]);
|
|
3081
3094
|
class Validator {
|
|
3082
3095
|
/**
|
|
3083
3096
|
* Test if element is used in a proper context.
|
|
@@ -3316,7 +3329,7 @@ class Validator {
|
|
|
3316
3329
|
}
|
|
3317
3330
|
function validateKeys(rule) {
|
|
3318
3331
|
for (const key of Object.keys(rule)) {
|
|
3319
|
-
if (!allowedKeys.
|
|
3332
|
+
if (!allowedKeys.has(key)) {
|
|
3320
3333
|
const str = JSON.stringify(rule);
|
|
3321
3334
|
throw new Error(`Permitted rule "${str}" contains unknown property "${key}"`);
|
|
3322
3335
|
}
|
|
@@ -3358,7 +3371,7 @@ function parseSeverity(value) {
|
|
|
3358
3371
|
|
|
3359
3372
|
const cacheKey = /* @__PURE__ */ Symbol("aria-naming");
|
|
3360
3373
|
const defaultValue = "allowed";
|
|
3361
|
-
const prohibitedRoles = [
|
|
3374
|
+
const prohibitedRoles = /* @__PURE__ */ new Set([
|
|
3362
3375
|
"caption",
|
|
3363
3376
|
"code",
|
|
3364
3377
|
"deletion",
|
|
@@ -3370,9 +3383,9 @@ const prohibitedRoles = [
|
|
|
3370
3383
|
"strong",
|
|
3371
3384
|
"subscript",
|
|
3372
3385
|
"superscript"
|
|
3373
|
-
];
|
|
3386
|
+
]);
|
|
3374
3387
|
function byRole(role) {
|
|
3375
|
-
return prohibitedRoles.
|
|
3388
|
+
return prohibitedRoles.has(role) ? "prohibited" : "allowed";
|
|
3376
3389
|
}
|
|
3377
3390
|
function byMeta(element, meta) {
|
|
3378
3391
|
return meta.aria.naming(element._adapter);
|
|
@@ -3424,7 +3437,7 @@ function isInputDisabledImpl(node) {
|
|
|
3424
3437
|
|
|
3425
3438
|
const patternCache = /* @__PURE__ */ new Map();
|
|
3426
3439
|
function compileStringPattern(pattern) {
|
|
3427
|
-
const regexp = pattern.
|
|
3440
|
+
const regexp = pattern.replaceAll(/[*]+/g, ".+");
|
|
3428
3441
|
return new RegExp(`^${regexp}$`);
|
|
3429
3442
|
}
|
|
3430
3443
|
function compileRegExpPattern(pattern) {
|
|
@@ -3699,8 +3712,8 @@ function format(value, quote = false) {
|
|
|
3699
3712
|
return String(value);
|
|
3700
3713
|
}
|
|
3701
3714
|
function interpolate(text, data) {
|
|
3702
|
-
return text.
|
|
3703
|
-
return
|
|
3715
|
+
return text.replaceAll(/{{\s*([^\s{}]+)\s*}}/g, (match, key) => {
|
|
3716
|
+
return data[key] !== void 0 ? format(data[key]) : match;
|
|
3704
3717
|
});
|
|
3705
3718
|
}
|
|
3706
3719
|
|
|
@@ -3908,7 +3921,14 @@ class Rule {
|
|
|
3908
3921
|
});
|
|
3909
3922
|
if (enabled && !blocked) {
|
|
3910
3923
|
const interpolated = interpolate(message, context ?? {});
|
|
3911
|
-
this.reporter.add(
|
|
3924
|
+
this.reporter.add({
|
|
3925
|
+
rule: this,
|
|
3926
|
+
message: interpolated,
|
|
3927
|
+
severity: this.severity,
|
|
3928
|
+
node,
|
|
3929
|
+
location: where,
|
|
3930
|
+
context
|
|
3931
|
+
});
|
|
3912
3932
|
}
|
|
3913
3933
|
}
|
|
3914
3934
|
findLocation(src) {
|
|
@@ -3999,7 +4019,7 @@ class Rule {
|
|
|
3999
4019
|
}
|
|
4000
4020
|
}
|
|
4001
4021
|
|
|
4002
|
-
const defaults$
|
|
4022
|
+
const defaults$B = {
|
|
4003
4023
|
allowExternal: true,
|
|
4004
4024
|
allowRelative: true,
|
|
4005
4025
|
allowAbsolute: true,
|
|
@@ -4043,7 +4063,7 @@ class AllowedLinks extends Rule {
|
|
|
4043
4063
|
allowRelative;
|
|
4044
4064
|
allowAbsolute;
|
|
4045
4065
|
constructor(options) {
|
|
4046
|
-
super({ ...defaults$
|
|
4066
|
+
super({ ...defaults$B, ...options });
|
|
4047
4067
|
this.allowExternal = parseAllow(this.options.allowExternal);
|
|
4048
4068
|
this.allowRelative = parseAllow(this.options.allowRelative);
|
|
4049
4069
|
this.allowAbsolute = parseAllow(this.options.allowAbsolute);
|
|
@@ -4211,7 +4231,7 @@ class AllowedLinks extends Rule {
|
|
|
4211
4231
|
}
|
|
4212
4232
|
}
|
|
4213
4233
|
|
|
4214
|
-
const defaults$
|
|
4234
|
+
const defaults$A = {
|
|
4215
4235
|
accessible: true
|
|
4216
4236
|
};
|
|
4217
4237
|
function findByTarget(target, siblings) {
|
|
@@ -4241,7 +4261,7 @@ function getDescription$1(context) {
|
|
|
4241
4261
|
}
|
|
4242
4262
|
class AreaAlt extends Rule {
|
|
4243
4263
|
constructor(options) {
|
|
4244
|
-
super({ ...defaults$
|
|
4264
|
+
super({ ...defaults$A, ...options });
|
|
4245
4265
|
}
|
|
4246
4266
|
static schema() {
|
|
4247
4267
|
return {
|
|
@@ -4320,7 +4340,7 @@ class AriaHiddenBody extends Rule {
|
|
|
4320
4340
|
}
|
|
4321
4341
|
}
|
|
4322
4342
|
|
|
4323
|
-
const defaults$
|
|
4343
|
+
const defaults$z = {
|
|
4324
4344
|
allowAnyNamable: false,
|
|
4325
4345
|
elements: {
|
|
4326
4346
|
include: null,
|
|
@@ -4368,7 +4388,7 @@ function isValidUsage(target, meta) {
|
|
|
4368
4388
|
}
|
|
4369
4389
|
class AriaLabelMisuse extends Rule {
|
|
4370
4390
|
constructor(options) {
|
|
4371
|
-
super({ ...defaults$
|
|
4391
|
+
super({ ...defaults$z, ...options });
|
|
4372
4392
|
}
|
|
4373
4393
|
static schema() {
|
|
4374
4394
|
return {
|
|
@@ -4480,8 +4500,7 @@ class AriaLabelMisuse extends Rule {
|
|
|
4480
4500
|
class ConfigError extends UserError {
|
|
4481
4501
|
constructor(message, nested) {
|
|
4482
4502
|
super(message, nested);
|
|
4483
|
-
|
|
4484
|
-
this.name = ConfigError.name;
|
|
4503
|
+
this.name = "ConfigError";
|
|
4485
4504
|
}
|
|
4486
4505
|
}
|
|
4487
4506
|
|
|
@@ -4537,14 +4556,14 @@ class CaseStyle {
|
|
|
4537
4556
|
}
|
|
4538
4557
|
}
|
|
4539
4558
|
|
|
4540
|
-
const defaults$
|
|
4559
|
+
const defaults$y = {
|
|
4541
4560
|
style: "lowercase",
|
|
4542
4561
|
ignoreForeign: true
|
|
4543
4562
|
};
|
|
4544
4563
|
class AttrCase extends Rule {
|
|
4545
4564
|
style;
|
|
4546
4565
|
constructor(options) {
|
|
4547
|
-
super({ ...defaults$
|
|
4566
|
+
super({ ...defaults$y, ...options });
|
|
4548
4567
|
this.style = new CaseStyle(this.options.style, "attr-case");
|
|
4549
4568
|
}
|
|
4550
4569
|
static schema() {
|
|
@@ -4585,7 +4604,7 @@ class AttrCase extends Rule {
|
|
|
4585
4604
|
if (event.originalAttribute) {
|
|
4586
4605
|
return;
|
|
4587
4606
|
}
|
|
4588
|
-
const letters = event.key.
|
|
4607
|
+
const letters = event.key.replaceAll(/[^a-z]+/gi, "");
|
|
4589
4608
|
if (this.style.match(letters)) {
|
|
4590
4609
|
return;
|
|
4591
4610
|
}
|
|
@@ -4658,6 +4677,7 @@ class InvalidTokenError extends Error {
|
|
|
4658
4677
|
location;
|
|
4659
4678
|
constructor(location, message) {
|
|
4660
4679
|
super(message);
|
|
4680
|
+
this.name = "InvalidTokenError";
|
|
4661
4681
|
this.location = location;
|
|
4662
4682
|
}
|
|
4663
4683
|
}
|
|
@@ -4767,16 +4787,26 @@ class Lexer {
|
|
|
4767
4787
|
*/
|
|
4768
4788
|
enter(context, state, data) {
|
|
4769
4789
|
if (state === State.TAG && data?.[0].startsWith("<")) {
|
|
4770
|
-
|
|
4771
|
-
|
|
4772
|
-
|
|
4773
|
-
|
|
4774
|
-
|
|
4775
|
-
|
|
4776
|
-
|
|
4777
|
-
|
|
4778
|
-
|
|
4779
|
-
|
|
4790
|
+
switch (data[0]) {
|
|
4791
|
+
case "<script": {
|
|
4792
|
+
context.contentModel = ContentModel.SCRIPT;
|
|
4793
|
+
break;
|
|
4794
|
+
}
|
|
4795
|
+
case "<style": {
|
|
4796
|
+
context.contentModel = ContentModel.STYLE;
|
|
4797
|
+
break;
|
|
4798
|
+
}
|
|
4799
|
+
case "<textarea": {
|
|
4800
|
+
context.contentModel = ContentModel.TEXTAREA;
|
|
4801
|
+
break;
|
|
4802
|
+
}
|
|
4803
|
+
case "<title": {
|
|
4804
|
+
context.contentModel = ContentModel.TITLE;
|
|
4805
|
+
break;
|
|
4806
|
+
}
|
|
4807
|
+
default: {
|
|
4808
|
+
context.contentModel = ContentModel.TEXT;
|
|
4809
|
+
}
|
|
4780
4810
|
}
|
|
4781
4811
|
}
|
|
4782
4812
|
}
|
|
@@ -4949,7 +4979,7 @@ class AttrDelimiter extends Rule {
|
|
|
4949
4979
|
}
|
|
4950
4980
|
|
|
4951
4981
|
const DEFAULT_PATTERN = "[a-z0-9-:]+";
|
|
4952
|
-
const defaults$
|
|
4982
|
+
const defaults$x = {
|
|
4953
4983
|
pattern: DEFAULT_PATTERN,
|
|
4954
4984
|
ignoreForeign: true
|
|
4955
4985
|
};
|
|
@@ -4982,7 +5012,7 @@ function generateDescription(name, pattern) {
|
|
|
4982
5012
|
class AttrPattern extends Rule {
|
|
4983
5013
|
pattern;
|
|
4984
5014
|
constructor(options) {
|
|
4985
|
-
super({ ...defaults$
|
|
5015
|
+
super({ ...defaults$x, ...options });
|
|
4986
5016
|
this.pattern = generateRegexp(this.options.pattern);
|
|
4987
5017
|
}
|
|
4988
5018
|
static schema() {
|
|
@@ -5029,7 +5059,7 @@ class AttrPattern extends Rule {
|
|
|
5029
5059
|
}
|
|
5030
5060
|
}
|
|
5031
5061
|
|
|
5032
|
-
const defaults$
|
|
5062
|
+
const defaults$w = {
|
|
5033
5063
|
style: "auto",
|
|
5034
5064
|
unquoted: false
|
|
5035
5065
|
};
|
|
@@ -5095,7 +5125,7 @@ class AttrQuotes extends Rule {
|
|
|
5095
5125
|
};
|
|
5096
5126
|
}
|
|
5097
5127
|
constructor(options) {
|
|
5098
|
-
super({ ...defaults$
|
|
5128
|
+
super({ ...defaults$w, ...options });
|
|
5099
5129
|
this.style = parseStyle$3(this.options.style);
|
|
5100
5130
|
}
|
|
5101
5131
|
setup() {
|
|
@@ -5177,10 +5207,10 @@ class AttrSpacing extends Rule {
|
|
|
5177
5207
|
|
|
5178
5208
|
function pick(attr) {
|
|
5179
5209
|
const result = {};
|
|
5180
|
-
if (
|
|
5210
|
+
if (attr.enum !== void 0) {
|
|
5181
5211
|
result.enum = attr.enum;
|
|
5182
5212
|
}
|
|
5183
|
-
if (
|
|
5213
|
+
if (attr.boolean !== void 0) {
|
|
5184
5214
|
result.boolean = attr.boolean;
|
|
5185
5215
|
}
|
|
5186
5216
|
return result;
|
|
@@ -5251,13 +5281,13 @@ class AttributeAllowedValues extends Rule {
|
|
|
5251
5281
|
}
|
|
5252
5282
|
}
|
|
5253
5283
|
|
|
5254
|
-
const defaults$
|
|
5284
|
+
const defaults$v = {
|
|
5255
5285
|
style: "omit"
|
|
5256
5286
|
};
|
|
5257
5287
|
class AttributeBooleanStyle extends Rule {
|
|
5258
5288
|
hasInvalidStyle;
|
|
5259
5289
|
constructor(options) {
|
|
5260
|
-
super({ ...defaults$
|
|
5290
|
+
super({ ...defaults$v, ...options });
|
|
5261
5291
|
this.hasInvalidStyle = parseStyle$2(this.options.style);
|
|
5262
5292
|
}
|
|
5263
5293
|
static schema() {
|
|
@@ -5327,13 +5357,13 @@ function reportMessage$1(attr, style) {
|
|
|
5327
5357
|
return "";
|
|
5328
5358
|
}
|
|
5329
5359
|
|
|
5330
|
-
const defaults$
|
|
5360
|
+
const defaults$u = {
|
|
5331
5361
|
style: "omit"
|
|
5332
5362
|
};
|
|
5333
5363
|
class AttributeEmptyStyle extends Rule {
|
|
5334
5364
|
hasInvalidStyle;
|
|
5335
5365
|
constructor(options) {
|
|
5336
|
-
super({ ...defaults$
|
|
5366
|
+
super({ ...defaults$u, ...options });
|
|
5337
5367
|
this.hasInvalidStyle = parseStyle$1(this.options.style);
|
|
5338
5368
|
}
|
|
5339
5369
|
static schema() {
|
|
@@ -5450,7 +5480,7 @@ class AttributeMisuse extends Rule {
|
|
|
5450
5480
|
}
|
|
5451
5481
|
}
|
|
5452
5482
|
|
|
5453
|
-
const defaults$
|
|
5483
|
+
const defaults$t = {
|
|
5454
5484
|
preferred: void 0
|
|
5455
5485
|
};
|
|
5456
5486
|
function isPasswordInput(event) {
|
|
@@ -5467,7 +5497,7 @@ function isGroupingToken(token) {
|
|
|
5467
5497
|
class AutocompletePassword extends Rule {
|
|
5468
5498
|
preferred;
|
|
5469
5499
|
constructor(options) {
|
|
5470
|
-
super({ ...defaults$
|
|
5500
|
+
super({ ...defaults$t, ...options });
|
|
5471
5501
|
this.preferred = options.preferred?.toLowerCase();
|
|
5472
5502
|
}
|
|
5473
5503
|
static schema() {
|
|
@@ -5697,7 +5727,7 @@ class BasePatternRule extends Rule {
|
|
|
5697
5727
|
}
|
|
5698
5728
|
}
|
|
5699
5729
|
|
|
5700
|
-
const defaults$
|
|
5730
|
+
const defaults$s = {
|
|
5701
5731
|
pattern: "kebabcase"
|
|
5702
5732
|
};
|
|
5703
5733
|
class ClassPattern extends BasePatternRule {
|
|
@@ -5705,7 +5735,7 @@ class ClassPattern extends BasePatternRule {
|
|
|
5705
5735
|
super({
|
|
5706
5736
|
ruleId: "class-pattern",
|
|
5707
5737
|
attr: "class",
|
|
5708
|
-
options: { ...defaults$
|
|
5738
|
+
options: { ...defaults$s, ...options },
|
|
5709
5739
|
allowedPatterns: patternNames
|
|
5710
5740
|
// allow all patterns
|
|
5711
5741
|
});
|
|
@@ -5855,13 +5885,13 @@ class CloseOrder extends Rule {
|
|
|
5855
5885
|
}
|
|
5856
5886
|
}
|
|
5857
5887
|
|
|
5858
|
-
const defaults$
|
|
5888
|
+
const defaults$r = {
|
|
5859
5889
|
include: null,
|
|
5860
5890
|
exclude: null
|
|
5861
5891
|
};
|
|
5862
5892
|
class Deprecated extends Rule {
|
|
5863
5893
|
constructor(options) {
|
|
5864
|
-
super({ ...defaults$
|
|
5894
|
+
super({ ...defaults$r, ...options });
|
|
5865
5895
|
}
|
|
5866
5896
|
static schema() {
|
|
5867
5897
|
return {
|
|
@@ -5907,7 +5937,7 @@ class Deprecated extends Rule {
|
|
|
5907
5937
|
text.push(context.documentation);
|
|
5908
5938
|
}
|
|
5909
5939
|
const doc = {
|
|
5910
|
-
description: text.map((cur) => cur.
|
|
5940
|
+
description: text.map((cur) => cur.replaceAll("$tagname", context.tagName)).join("\n\n"),
|
|
5911
5941
|
url: "https://html-validate.org/rules/deprecated.html"
|
|
5912
5942
|
};
|
|
5913
5943
|
return doc;
|
|
@@ -5969,6 +5999,137 @@ function prettySource(source) {
|
|
|
5969
5999
|
}
|
|
5970
6000
|
}
|
|
5971
6001
|
|
|
6002
|
+
function quote(value, char = '"') {
|
|
6003
|
+
return `${char}${value}${char}`;
|
|
6004
|
+
}
|
|
6005
|
+
|
|
6006
|
+
const defaults$q = {
|
|
6007
|
+
classes: []
|
|
6008
|
+
};
|
|
6009
|
+
function isRelevant$6(event) {
|
|
6010
|
+
return event.key.toLowerCase() === "class";
|
|
6011
|
+
}
|
|
6012
|
+
function normalizeEntry(entry) {
|
|
6013
|
+
const { class: className, message, replacement = [], url } = entry;
|
|
6014
|
+
return {
|
|
6015
|
+
class: className,
|
|
6016
|
+
message,
|
|
6017
|
+
replacement: Array.isArray(replacement) ? replacement : [replacement],
|
|
6018
|
+
url
|
|
6019
|
+
};
|
|
6020
|
+
}
|
|
6021
|
+
function formatDeprecatedMessage(className, entry) {
|
|
6022
|
+
let message = `class "${className}" is deprecated`;
|
|
6023
|
+
if (entry.replacement.length > 0) {
|
|
6024
|
+
const joined = naturalJoin(
|
|
6025
|
+
entry.replacement.map((r) => quote(r)),
|
|
6026
|
+
"or"
|
|
6027
|
+
);
|
|
6028
|
+
message += ` and replaced with ${joined}`;
|
|
6029
|
+
}
|
|
6030
|
+
if (entry.message) {
|
|
6031
|
+
message += `: ${entry.message}`;
|
|
6032
|
+
}
|
|
6033
|
+
return message;
|
|
6034
|
+
}
|
|
6035
|
+
function formatDocumentationDescription(context) {
|
|
6036
|
+
const text = [];
|
|
6037
|
+
const className = context.class;
|
|
6038
|
+
let description = `The class \`${className}\` is deprecated and should not be used`;
|
|
6039
|
+
if (context.message) {
|
|
6040
|
+
description += `: ${context.message}.`;
|
|
6041
|
+
} else {
|
|
6042
|
+
description += ".";
|
|
6043
|
+
}
|
|
6044
|
+
text.push(description);
|
|
6045
|
+
if (context.replacement.length === 1) {
|
|
6046
|
+
text.push(`Use the replacement class ${quote(context.replacement[0], "`")} instead.`);
|
|
6047
|
+
} else if (context.replacement.length > 1) {
|
|
6048
|
+
const listItems = context.replacement.map((r) => `- ${quote(r, "`")}`);
|
|
6049
|
+
text.push(`Use one of the following replacement classes instead:
|
|
6050
|
+
${listItems.join("\n")}`);
|
|
6051
|
+
}
|
|
6052
|
+
if (context.url) {
|
|
6053
|
+
text.push(`For details see: ${context.url}`);
|
|
6054
|
+
}
|
|
6055
|
+
return text.join("\n\n");
|
|
6056
|
+
}
|
|
6057
|
+
class DeprecatedClass extends Rule {
|
|
6058
|
+
deprecatedMap;
|
|
6059
|
+
constructor(options) {
|
|
6060
|
+
super({ ...defaults$q, ...options });
|
|
6061
|
+
const { classes } = this.options;
|
|
6062
|
+
this.deprecatedMap = new Map(classes.map((entry) => [entry.class, normalizeEntry(entry)]));
|
|
6063
|
+
}
|
|
6064
|
+
static schema() {
|
|
6065
|
+
return {
|
|
6066
|
+
classes: {
|
|
6067
|
+
type: "array",
|
|
6068
|
+
items: {
|
|
6069
|
+
type: "object",
|
|
6070
|
+
properties: {
|
|
6071
|
+
class: {
|
|
6072
|
+
type: "string"
|
|
6073
|
+
},
|
|
6074
|
+
message: {
|
|
6075
|
+
type: "string"
|
|
6076
|
+
},
|
|
6077
|
+
replacement: {
|
|
6078
|
+
anyOf: [
|
|
6079
|
+
{
|
|
6080
|
+
type: "string"
|
|
6081
|
+
},
|
|
6082
|
+
{
|
|
6083
|
+
type: "array",
|
|
6084
|
+
items: {
|
|
6085
|
+
type: "string"
|
|
6086
|
+
}
|
|
6087
|
+
}
|
|
6088
|
+
]
|
|
6089
|
+
},
|
|
6090
|
+
url: {
|
|
6091
|
+
type: "string"
|
|
6092
|
+
}
|
|
6093
|
+
},
|
|
6094
|
+
required: ["class"],
|
|
6095
|
+
additionalProperties: false
|
|
6096
|
+
}
|
|
6097
|
+
}
|
|
6098
|
+
};
|
|
6099
|
+
}
|
|
6100
|
+
documentation(context) {
|
|
6101
|
+
return {
|
|
6102
|
+
description: formatDocumentationDescription(context),
|
|
6103
|
+
url: "https://html-validate.org/rules/deprecated-class.html"
|
|
6104
|
+
};
|
|
6105
|
+
}
|
|
6106
|
+
setup() {
|
|
6107
|
+
this.on("attr", isRelevant$6, (event) => {
|
|
6108
|
+
const { value, valueLocation, target } = event;
|
|
6109
|
+
const classes = new DOMTokenList(value, valueLocation);
|
|
6110
|
+
for (const { item, location } of classes.iterator()) {
|
|
6111
|
+
const deprecatedEntry = this.deprecatedMap.get(item);
|
|
6112
|
+
if (!deprecatedEntry) {
|
|
6113
|
+
continue;
|
|
6114
|
+
}
|
|
6115
|
+
const message = formatDeprecatedMessage(item, deprecatedEntry);
|
|
6116
|
+
const context = {
|
|
6117
|
+
class: item,
|
|
6118
|
+
message: deprecatedEntry.message ?? null,
|
|
6119
|
+
replacement: deprecatedEntry.replacement,
|
|
6120
|
+
url: deprecatedEntry.url ?? null
|
|
6121
|
+
};
|
|
6122
|
+
this.report({
|
|
6123
|
+
node: target,
|
|
6124
|
+
message,
|
|
6125
|
+
location,
|
|
6126
|
+
context
|
|
6127
|
+
});
|
|
6128
|
+
}
|
|
6129
|
+
});
|
|
6130
|
+
}
|
|
6131
|
+
}
|
|
6132
|
+
|
|
5972
6133
|
class DeprecatedRule extends Rule {
|
|
5973
6134
|
documentation(context) {
|
|
5974
6135
|
const preamble = context ? `The rule "${context}"` : "This rule";
|
|
@@ -6095,7 +6256,7 @@ class ElementCase extends Rule {
|
|
|
6095
6256
|
});
|
|
6096
6257
|
}
|
|
6097
6258
|
validateCase(target, targetLocation) {
|
|
6098
|
-
const letters = target.tagName.
|
|
6259
|
+
const letters = target.tagName.replaceAll(/[^a-z]+/gi, "");
|
|
6099
6260
|
if (!this.style.match(letters)) {
|
|
6100
6261
|
const location = sliceLocation(targetLocation, 1);
|
|
6101
6262
|
this.report(target, `Element "${target.tagName}" should be ${this.style.name}`, location);
|
|
@@ -6699,8 +6860,8 @@ function haveName(name) {
|
|
|
6699
6860
|
return typeof name === "string" && name !== "";
|
|
6700
6861
|
}
|
|
6701
6862
|
function allowSharedName(node, shared) {
|
|
6702
|
-
const type = node
|
|
6703
|
-
return
|
|
6863
|
+
const type = getControlType(node);
|
|
6864
|
+
return shared.includes(type);
|
|
6704
6865
|
}
|
|
6705
6866
|
function isInputHidden(element) {
|
|
6706
6867
|
return element.is("input") && element.getAttributeValue("type") === "hidden";
|
|
@@ -6708,6 +6869,13 @@ function isInputHidden(element) {
|
|
|
6708
6869
|
function isInputCheckbox(element) {
|
|
6709
6870
|
return element.is("input") && element.getAttributeValue("type") === "checkbox";
|
|
6710
6871
|
}
|
|
6872
|
+
function getControlType(element) {
|
|
6873
|
+
const type = element.getAttributeValue("type") ?? "";
|
|
6874
|
+
if (element.is("button") && type === "") {
|
|
6875
|
+
return "submit";
|
|
6876
|
+
}
|
|
6877
|
+
return type;
|
|
6878
|
+
}
|
|
6711
6879
|
function isCheckboxWithDefault(control, previous, options) {
|
|
6712
6880
|
const { allowCheckboxDefault } = options;
|
|
6713
6881
|
if (!allowCheckboxDefault) {
|
|
@@ -6845,7 +7013,7 @@ class FormDupName extends Rule {
|
|
|
6845
7013
|
validateSharedName(control, group, attr, name) {
|
|
6846
7014
|
const uniqueElements = this.getUniqueElements(group);
|
|
6847
7015
|
const sharedElements = this.getSharedElements(group);
|
|
6848
|
-
const type = control
|
|
7016
|
+
const type = getControlType(control);
|
|
6849
7017
|
if (uniqueElements.has(name) || sharedElements.has(name) && sharedElements.get(name) !== type) {
|
|
6850
7018
|
const context = {
|
|
6851
7019
|
name,
|
|
@@ -6907,7 +7075,7 @@ function isRelevant$5(event) {
|
|
|
6907
7075
|
function extractLevel(node) {
|
|
6908
7076
|
const match = /^[hH](\d)$/.exec(node.tagName);
|
|
6909
7077
|
if (match) {
|
|
6910
|
-
return parseInt(match[1], 10);
|
|
7078
|
+
return Number.parseInt(match[1], 10);
|
|
6911
7079
|
} else {
|
|
6912
7080
|
return null;
|
|
6913
7081
|
}
|
|
@@ -6920,7 +7088,7 @@ function parseMaxInitial(value) {
|
|
|
6920
7088
|
if (!match) {
|
|
6921
7089
|
return 1;
|
|
6922
7090
|
}
|
|
6923
|
-
return parseInt(match[1], 10);
|
|
7091
|
+
return Number.parseInt(match[1], 10);
|
|
6924
7092
|
}
|
|
6925
7093
|
class HeadingLevel extends Rule {
|
|
6926
7094
|
minInitialRank;
|
|
@@ -7069,10 +7237,10 @@ class HeadingLevel extends Rule {
|
|
|
7069
7237
|
this.stack.pop();
|
|
7070
7238
|
}
|
|
7071
7239
|
getPrevRoot() {
|
|
7072
|
-
return this.stack
|
|
7240
|
+
return this.stack.at(-2);
|
|
7073
7241
|
}
|
|
7074
7242
|
getCurrentRoot() {
|
|
7075
|
-
return this.stack
|
|
7243
|
+
return this.stack.at(-1);
|
|
7076
7244
|
}
|
|
7077
7245
|
isSectioningRoot(node) {
|
|
7078
7246
|
const context = {
|
|
@@ -7669,7 +7837,7 @@ function parseContent(text) {
|
|
|
7669
7837
|
const match = /^(\d+)(?:\s*;\s*url=(.*))?/i.exec(text);
|
|
7670
7838
|
if (match) {
|
|
7671
7839
|
return {
|
|
7672
|
-
delay: parseInt(match[1], 10),
|
|
7840
|
+
delay: Number.parseInt(match[1], 10),
|
|
7673
7841
|
url: match[2]
|
|
7674
7842
|
};
|
|
7675
7843
|
} else {
|
|
@@ -7993,13 +8161,12 @@ class NoDupClass extends Rule {
|
|
|
7993
8161
|
}
|
|
7994
8162
|
const classes = new DOMTokenList(event.value, event.valueLocation);
|
|
7995
8163
|
const unique = /* @__PURE__ */ new Set();
|
|
7996
|
-
classes.
|
|
7997
|
-
if (unique.has(
|
|
7998
|
-
|
|
7999
|
-
this.report(event.target, `Class "${cur}" duplicated`, location);
|
|
8164
|
+
for (const { item, location } of classes.iterator()) {
|
|
8165
|
+
if (unique.has(item)) {
|
|
8166
|
+
this.report(event.target, `Class "${item}" duplicated`, location);
|
|
8000
8167
|
}
|
|
8001
|
-
unique.add(
|
|
8002
|
-
}
|
|
8168
|
+
unique.add(item);
|
|
8169
|
+
}
|
|
8003
8170
|
});
|
|
8004
8171
|
}
|
|
8005
8172
|
}
|
|
@@ -8731,7 +8898,7 @@ class NoUnusedDisable extends Rule {
|
|
|
8731
8898
|
setup() {
|
|
8732
8899
|
}
|
|
8733
8900
|
reportUnused(unused, options, location) {
|
|
8734
|
-
const tokens = new DOMTokenList(options.
|
|
8901
|
+
const tokens = new DOMTokenList(options.replaceAll(",", " "), location);
|
|
8735
8902
|
for (const ruleId of unused) {
|
|
8736
8903
|
const index = tokens.indexOf(ruleId);
|
|
8737
8904
|
const tokenLocation = index >= 0 ? tokens.location(index) : location;
|
|
@@ -9052,19 +9219,19 @@ const supportSri = {
|
|
|
9052
9219
|
link: "href",
|
|
9053
9220
|
script: "src"
|
|
9054
9221
|
};
|
|
9055
|
-
const supportedRel = ["stylesheet", "preload", "modulepreload"];
|
|
9056
|
-
const supportedPreload = ["style", "script"];
|
|
9222
|
+
const supportedRel = /* @__PURE__ */ new Set(["stylesheet", "preload", "modulepreload"]);
|
|
9223
|
+
const supportedPreload = /* @__PURE__ */ new Set(["style", "script"]);
|
|
9057
9224
|
function linkSupportsSri(node) {
|
|
9058
9225
|
const rel = node.getAttribute("rel");
|
|
9059
9226
|
if (typeof rel?.value !== "string") {
|
|
9060
9227
|
return false;
|
|
9061
9228
|
}
|
|
9062
|
-
if (!supportedRel.
|
|
9229
|
+
if (!supportedRel.has(rel.value)) {
|
|
9063
9230
|
return false;
|
|
9064
9231
|
}
|
|
9065
9232
|
if (rel.value === "preload") {
|
|
9066
9233
|
const as = node.getAttribute("as");
|
|
9067
|
-
return typeof as?.value === "string" && supportedPreload.
|
|
9234
|
+
return typeof as?.value === "string" && supportedPreload.has(as.value);
|
|
9068
9235
|
}
|
|
9069
9236
|
return true;
|
|
9070
9237
|
}
|
|
@@ -9181,13 +9348,13 @@ class ScriptElement extends Rule {
|
|
|
9181
9348
|
}
|
|
9182
9349
|
}
|
|
9183
9350
|
|
|
9184
|
-
const javascript = [
|
|
9351
|
+
const javascript = /* @__PURE__ */ new Set([
|
|
9185
9352
|
"",
|
|
9186
9353
|
"application/ecmascript",
|
|
9187
9354
|
"application/javascript",
|
|
9188
9355
|
"text/ecmascript",
|
|
9189
9356
|
"text/javascript"
|
|
9190
|
-
];
|
|
9357
|
+
]);
|
|
9191
9358
|
class ScriptType extends Rule {
|
|
9192
9359
|
documentation() {
|
|
9193
9360
|
return {
|
|
@@ -9218,7 +9385,7 @@ class ScriptType extends Rule {
|
|
|
9218
9385
|
}
|
|
9219
9386
|
isJavascript(mime) {
|
|
9220
9387
|
const type = mime.replace(/;.*/, "");
|
|
9221
|
-
return javascript.
|
|
9388
|
+
return javascript.has(type);
|
|
9222
9389
|
}
|
|
9223
9390
|
}
|
|
9224
9391
|
|
|
@@ -9794,7 +9961,7 @@ class UnknownCharReference extends Rule {
|
|
|
9794
9961
|
}
|
|
9795
9962
|
|
|
9796
9963
|
const expectedOrder = ["section", "hint", "contact", "field1", "field2", "webauthn"];
|
|
9797
|
-
const fieldNames1 = [
|
|
9964
|
+
const fieldNames1 = /* @__PURE__ */ new Set([
|
|
9798
9965
|
"name",
|
|
9799
9966
|
"honorific-prefix",
|
|
9800
9967
|
"given-name",
|
|
@@ -9839,8 +10006,8 @@ const fieldNames1 = [
|
|
|
9839
10006
|
"sex",
|
|
9840
10007
|
"url",
|
|
9841
10008
|
"photo"
|
|
9842
|
-
];
|
|
9843
|
-
const fieldNames2 = [
|
|
10009
|
+
]);
|
|
10010
|
+
const fieldNames2 = /* @__PURE__ */ new Set([
|
|
9844
10011
|
"tel",
|
|
9845
10012
|
"tel-country-code",
|
|
9846
10013
|
"tel-national",
|
|
@@ -9851,7 +10018,7 @@ const fieldNames2 = [
|
|
|
9851
10018
|
"tel-extension",
|
|
9852
10019
|
"email",
|
|
9853
10020
|
"impp"
|
|
9854
|
-
];
|
|
10021
|
+
]);
|
|
9855
10022
|
const fieldNameGroup = {
|
|
9856
10023
|
name: "text",
|
|
9857
10024
|
"honorific-prefix": "text",
|
|
@@ -9916,14 +10083,14 @@ function matchHint(token) {
|
|
|
9916
10083
|
return token === "shipping" || token === "billing";
|
|
9917
10084
|
}
|
|
9918
10085
|
function matchFieldNames1(token) {
|
|
9919
|
-
return fieldNames1.
|
|
10086
|
+
return fieldNames1.has(token);
|
|
9920
10087
|
}
|
|
9921
10088
|
function matchContact(token) {
|
|
9922
10089
|
const haystack = ["home", "work", "mobile", "fax", "pager"];
|
|
9923
10090
|
return haystack.includes(token);
|
|
9924
10091
|
}
|
|
9925
10092
|
function matchFieldNames2(token) {
|
|
9926
|
-
return fieldNames2.
|
|
10093
|
+
return fieldNames2.has(token);
|
|
9927
10094
|
}
|
|
9928
10095
|
function matchWebauthn(token) {
|
|
9929
10096
|
return token === "webauthn";
|
|
@@ -10594,11 +10761,13 @@ class H32 extends Rule {
|
|
|
10594
10761
|
setup() {
|
|
10595
10762
|
const formTags = this.getTagsWithProperty("form");
|
|
10596
10763
|
const formSelector = formTags.join(",");
|
|
10764
|
+
const submitButtonTags = this.getTagsWithProperty("submitButton");
|
|
10765
|
+
const submitButtonSelector = submitButtonTags.join(",");
|
|
10597
10766
|
this.on("dom:ready", (event) => {
|
|
10598
10767
|
const { document } = event;
|
|
10599
10768
|
const forms = document.querySelectorAll(formSelector);
|
|
10600
10769
|
for (const form of forms) {
|
|
10601
|
-
if (hasNestedSubmit(form)) {
|
|
10770
|
+
if (hasNestedSubmit(form, submitButtonSelector)) {
|
|
10602
10771
|
continue;
|
|
10603
10772
|
}
|
|
10604
10773
|
if (hasAssociatedSubmit(document, form)) {
|
|
@@ -10610,15 +10779,15 @@ class H32 extends Rule {
|
|
|
10610
10779
|
}
|
|
10611
10780
|
}
|
|
10612
10781
|
function isSubmit(node) {
|
|
10613
|
-
const
|
|
10614
|
-
return
|
|
10782
|
+
const meta = node.meta;
|
|
10783
|
+
return Boolean(meta?.submitButton);
|
|
10615
10784
|
}
|
|
10616
10785
|
function isAssociated(id, node) {
|
|
10617
10786
|
const form = node.getAttribute("form");
|
|
10618
10787
|
return Boolean(form?.valueMatches(id, true));
|
|
10619
10788
|
}
|
|
10620
|
-
function hasNestedSubmit(form) {
|
|
10621
|
-
const matches = form.querySelectorAll(
|
|
10789
|
+
function hasNestedSubmit(form, submitButtonSelector) {
|
|
10790
|
+
const matches = form.querySelectorAll(submitButtonSelector).filter(isSubmit).filter((node) => !node.hasAttribute("form"));
|
|
10622
10791
|
return matches.length > 0;
|
|
10623
10792
|
}
|
|
10624
10793
|
function hasAssociatedSubmit(document, form) {
|
|
@@ -10909,6 +11078,7 @@ const bundledRules = {
|
|
|
10909
11078
|
"close-attr": CloseAttr,
|
|
10910
11079
|
"close-order": CloseOrder,
|
|
10911
11080
|
deprecated: Deprecated,
|
|
11081
|
+
"deprecated-class": DeprecatedClass,
|
|
10912
11082
|
"deprecated-rule": DeprecatedRule,
|
|
10913
11083
|
"doctype-html": NoStyleTag$1,
|
|
10914
11084
|
"doctype-style": DoctypeStyle,
|
|
@@ -11166,11 +11336,11 @@ function dumpTree(root) {
|
|
|
11166
11336
|
} else {
|
|
11167
11337
|
lines.push("(root)");
|
|
11168
11338
|
}
|
|
11169
|
-
node.childElements.
|
|
11339
|
+
for (const [index, child] of node.childElements.entries()) {
|
|
11170
11340
|
const s = lastSibling ? " " : "\u2502";
|
|
11171
11341
|
const i = level > 0 ? `${indent}${s} ` : "";
|
|
11172
11342
|
writeNode(child, level + 1, i, index);
|
|
11173
|
-
}
|
|
11343
|
+
}
|
|
11174
11344
|
}
|
|
11175
11345
|
writeNode(root, 0, "", 0);
|
|
11176
11346
|
return lines;
|
|
@@ -12285,7 +12455,7 @@ class EventHandler {
|
|
|
12285
12455
|
}
|
|
12286
12456
|
|
|
12287
12457
|
const name = "html-validate";
|
|
12288
|
-
const version = "10.
|
|
12458
|
+
const version = "10.10.0";
|
|
12289
12459
|
const bugs = "https://gitlab.com/html-validate/html-validate/issues/new";
|
|
12290
12460
|
|
|
12291
12461
|
function freeze(src) {
|
|
@@ -12301,7 +12471,13 @@ function isThenableArray(value) {
|
|
|
12301
12471
|
return isThenable(value[0]);
|
|
12302
12472
|
}
|
|
12303
12473
|
class Reporter {
|
|
12474
|
+
/**
|
|
12475
|
+
* @internal
|
|
12476
|
+
*/
|
|
12304
12477
|
result;
|
|
12478
|
+
/**
|
|
12479
|
+
* @internal
|
|
12480
|
+
*/
|
|
12305
12481
|
constructor() {
|
|
12306
12482
|
this.result = {};
|
|
12307
12483
|
}
|
|
@@ -12314,16 +12490,16 @@ class Reporter {
|
|
|
12314
12490
|
}
|
|
12315
12491
|
const valid = reports.every((report) => report.valid);
|
|
12316
12492
|
const merged = {};
|
|
12317
|
-
|
|
12318
|
-
report.results
|
|
12493
|
+
for (const report of reports) {
|
|
12494
|
+
for (const result of report.results) {
|
|
12319
12495
|
const key = result.filePath;
|
|
12320
12496
|
if (key in merged) {
|
|
12321
12497
|
merged[key].messages = [...merged[key].messages, ...result.messages];
|
|
12322
12498
|
} else {
|
|
12323
12499
|
merged[key] = { ...result };
|
|
12324
12500
|
}
|
|
12325
|
-
}
|
|
12326
|
-
}
|
|
12501
|
+
}
|
|
12502
|
+
}
|
|
12327
12503
|
const results = Object.values(merged).map((result) => {
|
|
12328
12504
|
result.errorCount = countErrors(result.messages);
|
|
12329
12505
|
result.warningCount = countWarnings(result.messages);
|
|
@@ -12336,8 +12512,11 @@ class Reporter {
|
|
|
12336
12512
|
warningCount: sumWarnings(results)
|
|
12337
12513
|
};
|
|
12338
12514
|
}
|
|
12339
|
-
|
|
12340
|
-
|
|
12515
|
+
/**
|
|
12516
|
+
* @internal
|
|
12517
|
+
*/
|
|
12518
|
+
add(options) {
|
|
12519
|
+
const { rule, message, severity, node, location, context } = options;
|
|
12341
12520
|
if (!(location.filename in this.result)) {
|
|
12342
12521
|
this.result[location.filename] = [];
|
|
12343
12522
|
}
|
|
@@ -12362,17 +12541,23 @@ class Reporter {
|
|
|
12362
12541
|
}
|
|
12363
12542
|
this.result[location.filename].push(entry);
|
|
12364
12543
|
}
|
|
12544
|
+
/**
|
|
12545
|
+
* @internal
|
|
12546
|
+
*/
|
|
12365
12547
|
addManual(filename, message) {
|
|
12366
12548
|
if (!(filename in this.result)) {
|
|
12367
12549
|
this.result[filename] = [];
|
|
12368
12550
|
}
|
|
12369
12551
|
this.result[filename].push(message);
|
|
12370
12552
|
}
|
|
12553
|
+
/**
|
|
12554
|
+
* @internal
|
|
12555
|
+
*/
|
|
12371
12556
|
save(sources) {
|
|
12372
12557
|
const report = {
|
|
12373
12558
|
valid: this.isValid(),
|
|
12374
12559
|
results: Object.keys(this.result).map((filePath) => {
|
|
12375
|
-
const messages = Array.from(this.result[filePath], freeze).
|
|
12560
|
+
const messages = Array.from(this.result[filePath], freeze).toSorted(messageSort);
|
|
12376
12561
|
const source = (sources ?? []).find((source2) => filePath === source2.filename);
|
|
12377
12562
|
return {
|
|
12378
12563
|
filePath,
|
|
@@ -12389,6 +12574,9 @@ class Reporter {
|
|
|
12389
12574
|
report.warningCount = sumWarnings(report.results);
|
|
12390
12575
|
return report;
|
|
12391
12576
|
}
|
|
12577
|
+
/**
|
|
12578
|
+
* @internal
|
|
12579
|
+
*/
|
|
12392
12580
|
isValid() {
|
|
12393
12581
|
const numErrors = Object.values(this.result).reduce((sum, messages) => {
|
|
12394
12582
|
return sum + countErrors(messages);
|
|
@@ -12451,6 +12639,7 @@ class ParserError extends Error {
|
|
|
12451
12639
|
location;
|
|
12452
12640
|
constructor(location, message) {
|
|
12453
12641
|
super(message);
|
|
12642
|
+
this.name = "ParserError";
|
|
12454
12643
|
this.location = location;
|
|
12455
12644
|
}
|
|
12456
12645
|
}
|
|
@@ -12607,7 +12796,7 @@ class Parser {
|
|
|
12607
12796
|
const tokens = Array.from(
|
|
12608
12797
|
this.consumeUntil(tokenStream, TokenType.TAG_CLOSE, startToken.location)
|
|
12609
12798
|
);
|
|
12610
|
-
const endToken = tokens.
|
|
12799
|
+
const endToken = tokens.at(-1);
|
|
12611
12800
|
const closeOptional = this.closeOptional(startToken);
|
|
12612
12801
|
const parent = closeOptional ? this.dom.getActive().parent : this.dom.getActive();
|
|
12613
12802
|
const node = HtmlElement.fromTokens(
|
|
@@ -12725,7 +12914,7 @@ class Parser {
|
|
|
12725
12914
|
const endTokens = Array.from(
|
|
12726
12915
|
this.consumeUntil(tokenStream, TokenType.TAG_CLOSE, last.location)
|
|
12727
12916
|
);
|
|
12728
|
-
endToken = endTokens.
|
|
12917
|
+
endToken = endTokens.at(-1);
|
|
12729
12918
|
const selfClosed = endToken.data[0] === "/>";
|
|
12730
12919
|
if (tagClosed) {
|
|
12731
12920
|
startToken = last;
|
|
@@ -13007,7 +13196,7 @@ class Parser {
|
|
|
13007
13196
|
this.event.once("*", cb);
|
|
13008
13197
|
}
|
|
13009
13198
|
trigger(event, data) {
|
|
13010
|
-
if (
|
|
13199
|
+
if (data.location === void 0) {
|
|
13011
13200
|
throw new Error("Triggered event must contain location");
|
|
13012
13201
|
}
|
|
13013
13202
|
this.event.trigger(event, data);
|
|
@@ -13135,7 +13324,9 @@ class Engine {
|
|
|
13135
13324
|
}
|
|
13136
13325
|
lines.push({ event, data });
|
|
13137
13326
|
});
|
|
13138
|
-
|
|
13327
|
+
for (const src of source) {
|
|
13328
|
+
parser.parseHtml(src);
|
|
13329
|
+
}
|
|
13139
13330
|
return lines;
|
|
13140
13331
|
}
|
|
13141
13332
|
dumpTokens(source) {
|
|
@@ -13600,7 +13791,7 @@ const entities = {
|
|
|
13600
13791
|
"&": "&"
|
|
13601
13792
|
};
|
|
13602
13793
|
function xmlescape(src) {
|
|
13603
|
-
return src.toString().
|
|
13794
|
+
return src.toString().replaceAll(/[><'"&]/g, (match) => {
|
|
13604
13795
|
return entities[match];
|
|
13605
13796
|
});
|
|
13606
13797
|
}
|
|
@@ -13620,11 +13811,11 @@ function checkstyleFormatter(results) {
|
|
|
13620
13811
|
`;
|
|
13621
13812
|
output += `<checkstyle version="4.3">
|
|
13622
13813
|
`;
|
|
13623
|
-
|
|
13814
|
+
for (const result of results) {
|
|
13624
13815
|
const messages = result.messages;
|
|
13625
13816
|
output += ` <file name="${xmlescape(result.filePath)}">
|
|
13626
13817
|
`;
|
|
13627
|
-
|
|
13818
|
+
for (const message of messages) {
|
|
13628
13819
|
const ruleId = xmlescape(`htmlvalidate.rules.${message.ruleId}`);
|
|
13629
13820
|
output += " ";
|
|
13630
13821
|
output += [
|
|
@@ -13635,9 +13826,9 @@ function checkstyleFormatter(results) {
|
|
|
13635
13826
|
`source="${ruleId}" />`
|
|
13636
13827
|
].join(" ");
|
|
13637
13828
|
output += "\n";
|
|
13638
|
-
}
|
|
13829
|
+
}
|
|
13639
13830
|
output += " </file>\n";
|
|
13640
|
-
}
|
|
13831
|
+
}
|
|
13641
13832
|
output += "</checkstyle>\n";
|
|
13642
13833
|
return output;
|
|
13643
13834
|
}
|
|
@@ -13707,11 +13898,11 @@ function codeFrameColumns(rawLines, loc) {
|
|
|
13707
13898
|
if (hasMarker) {
|
|
13708
13899
|
let markerLine = "";
|
|
13709
13900
|
if (Array.isArray(hasMarker)) {
|
|
13710
|
-
const markerSpacing = line.slice(0, Math.max(hasMarker[0] - 1, 0)).
|
|
13901
|
+
const markerSpacing = line.slice(0, Math.max(hasMarker[0] - 1, 0)).replaceAll(/[^\t]/g, " ");
|
|
13711
13902
|
const numberOfMarkers = hasMarker[1] || 1;
|
|
13712
13903
|
markerLine = [
|
|
13713
13904
|
"\n ",
|
|
13714
|
-
gutter.
|
|
13905
|
+
gutter.replaceAll(/\d/g, " "),
|
|
13715
13906
|
" ",
|
|
13716
13907
|
markerSpacing,
|
|
13717
13908
|
"^".repeat(numberOfMarkers)
|
|
@@ -13849,10 +14040,10 @@ function stylish(results) {
|
|
|
13849
14040
|
function textFormatter(results) {
|
|
13850
14041
|
let output = "";
|
|
13851
14042
|
let total = 0;
|
|
13852
|
-
|
|
14043
|
+
for (const result of results) {
|
|
13853
14044
|
const messages = result.messages;
|
|
13854
14045
|
if (messages.length === 0) {
|
|
13855
|
-
|
|
14046
|
+
continue;
|
|
13856
14047
|
}
|
|
13857
14048
|
total += messages.length;
|
|
13858
14049
|
output += messages.map((message) => {
|
|
@@ -13868,7 +14059,7 @@ function textFormatter(results) {
|
|
|
13868
14059
|
return `${location}: ${messageType} [${message.ruleId}] ${message.message}
|
|
13869
14060
|
`;
|
|
13870
14061
|
}).join("");
|
|
13871
|
-
}
|
|
14062
|
+
}
|
|
13872
14063
|
return total > 0 ? output : "";
|
|
13873
14064
|
}
|
|
13874
14065
|
const formatter = textFormatter;
|
|
@@ -14700,7 +14891,7 @@ var ignoreExports = /*@__PURE__*/ requireIgnore();
|
|
|
14700
14891
|
var ignore = /*@__PURE__*/getDefaultExportFromCjs(ignoreExports);
|
|
14701
14892
|
|
|
14702
14893
|
const engines = {
|
|
14703
|
-
node: "^20.19.0 || >= 22.
|
|
14894
|
+
node: "^20.19.0 || >= 22.16.0"
|
|
14704
14895
|
};
|
|
14705
14896
|
|
|
14706
14897
|
var workerPath = "./jest-worker.js";
|