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/cjs/core.js
CHANGED
|
@@ -404,6 +404,7 @@ function stringify(value) {
|
|
|
404
404
|
class WrappedError extends Error {
|
|
405
405
|
constructor(message) {
|
|
406
406
|
super(stringify(message));
|
|
407
|
+
this.name = "WrappedError";
|
|
407
408
|
}
|
|
408
409
|
}
|
|
409
410
|
|
|
@@ -418,8 +419,7 @@ function ensureError(value) {
|
|
|
418
419
|
class NestedError extends Error {
|
|
419
420
|
constructor(message, nested) {
|
|
420
421
|
super(message);
|
|
421
|
-
|
|
422
|
-
this.name = NestedError.name;
|
|
422
|
+
this.name = "NestedError";
|
|
423
423
|
if (nested?.stack) {
|
|
424
424
|
this.stack ??= "";
|
|
425
425
|
this.stack += `
|
|
@@ -431,8 +431,7 @@ Caused by: ${nested.stack}`;
|
|
|
431
431
|
class UserError extends NestedError {
|
|
432
432
|
constructor(message, nested) {
|
|
433
433
|
super(message, nested);
|
|
434
|
-
|
|
435
|
-
this.name = UserError.name;
|
|
434
|
+
this.name = "UserError";
|
|
436
435
|
Object.defineProperty(this, "isUserError", {
|
|
437
436
|
value: true,
|
|
438
437
|
enumerable: false,
|
|
@@ -455,8 +454,7 @@ class InheritError extends UserError {
|
|
|
455
454
|
constructor({ tagName, inherit }) {
|
|
456
455
|
const message = `Element <${tagName}> cannot inherit from <${inherit}>: no such element`;
|
|
457
456
|
super(message);
|
|
458
|
-
|
|
459
|
-
this.name = InheritError.name;
|
|
457
|
+
this.name = "InheritError";
|
|
460
458
|
this.tagName = tagName;
|
|
461
459
|
this.inherit = inherit;
|
|
462
460
|
this.filename = null;
|
|
@@ -498,6 +496,7 @@ class SchemaValidationError extends UserError {
|
|
|
498
496
|
constructor(filename, message, obj, schema, errors) {
|
|
499
497
|
const summary = getSummary(schema, obj, errors);
|
|
500
498
|
super(`${message}: ${summary}`);
|
|
499
|
+
this.name = "SchemaValidationError";
|
|
501
500
|
this.filename = filename;
|
|
502
501
|
this.obj = obj;
|
|
503
502
|
this.schema = schema;
|
|
@@ -645,6 +644,18 @@ const patternProperties = {
|
|
|
645
644
|
}
|
|
646
645
|
]
|
|
647
646
|
},
|
|
647
|
+
submitButton: {
|
|
648
|
+
title: "Mark this element as a submit button",
|
|
649
|
+
description: "This element can be used to submit forms.",
|
|
650
|
+
anyOf: [
|
|
651
|
+
{
|
|
652
|
+
type: "boolean"
|
|
653
|
+
},
|
|
654
|
+
{
|
|
655
|
+
"function": true
|
|
656
|
+
}
|
|
657
|
+
]
|
|
658
|
+
},
|
|
648
659
|
templateRoot: {
|
|
649
660
|
title: "Mark element as an element ignoring DOM ancestry, i.e. <template>.",
|
|
650
661
|
description: "The <template> element can contain any elements.",
|
|
@@ -1028,6 +1039,7 @@ const MetaCopyableProperty = [
|
|
|
1028
1039
|
"form",
|
|
1029
1040
|
"formAssociated",
|
|
1030
1041
|
"labelable",
|
|
1042
|
+
"submitButton",
|
|
1031
1043
|
"attributes",
|
|
1032
1044
|
"aria",
|
|
1033
1045
|
"permittedContent",
|
|
@@ -1042,7 +1054,7 @@ function setMetaProperty(dst, key, value) {
|
|
|
1042
1054
|
}
|
|
1043
1055
|
|
|
1044
1056
|
function isSet(value) {
|
|
1045
|
-
return
|
|
1057
|
+
return value !== void 0;
|
|
1046
1058
|
}
|
|
1047
1059
|
function flag(value) {
|
|
1048
1060
|
return value ? true : void 0;
|
|
@@ -1057,7 +1069,7 @@ function migrateSingleAttribute(src, key) {
|
|
|
1057
1069
|
result.required = flag(src.requiredAttributes?.includes(key));
|
|
1058
1070
|
result.omit = void 0;
|
|
1059
1071
|
const attr = src.attributes ? src.attributes[key] : void 0;
|
|
1060
|
-
if (
|
|
1072
|
+
if (attr === void 0) {
|
|
1061
1073
|
return stripUndefined(result);
|
|
1062
1074
|
}
|
|
1063
1075
|
if (attr === null) {
|
|
@@ -1084,7 +1096,7 @@ function migrateAttributes(src) {
|
|
|
1084
1096
|
...src.requiredAttributes ?? [],
|
|
1085
1097
|
...src.deprecatedAttributes ?? []
|
|
1086
1098
|
/* eslint-disable-next-line sonarjs/no-alphabetical-sort -- not really needed in this case, this is a-z anyway */
|
|
1087
|
-
].
|
|
1099
|
+
].toSorted();
|
|
1088
1100
|
const entries = keys.map((key) => {
|
|
1089
1101
|
return [key, migrateSingleAttribute(src, key)];
|
|
1090
1102
|
});
|
|
@@ -1112,9 +1124,7 @@ function migrateElement(src) {
|
|
|
1112
1124
|
const implicitRole = normalizeAriaImplicitRole(src.implicitRole ?? src.aria?.implicitRole);
|
|
1113
1125
|
const result = {
|
|
1114
1126
|
...src,
|
|
1115
|
-
|
|
1116
|
-
formAssociated: void 0
|
|
1117
|
-
},
|
|
1127
|
+
formAssociated: void 0,
|
|
1118
1128
|
attributes: migrateAttributes(src),
|
|
1119
1129
|
textContent: src.textContent,
|
|
1120
1130
|
focusable: src.focusable ?? false,
|
|
@@ -1149,7 +1159,8 @@ const dynamicKeys = [
|
|
|
1149
1159
|
"phrasing",
|
|
1150
1160
|
"embedded",
|
|
1151
1161
|
"interactive",
|
|
1152
|
-
"labelable"
|
|
1162
|
+
"labelable",
|
|
1163
|
+
"submitButton"
|
|
1153
1164
|
];
|
|
1154
1165
|
const schemaCache = /* @__PURE__ */ new Map();
|
|
1155
1166
|
function clone(src) {
|
|
@@ -1419,13 +1430,10 @@ class Attribute {
|
|
|
1419
1430
|
*/
|
|
1420
1431
|
constructor(key, value, keyLocation, valueLocation, originalAttribute) {
|
|
1421
1432
|
this.key = key;
|
|
1422
|
-
this.value = value;
|
|
1433
|
+
this.value = value ?? null;
|
|
1423
1434
|
this.keyLocation = keyLocation;
|
|
1424
1435
|
this.valueLocation = valueLocation;
|
|
1425
1436
|
this.originalAttribute = originalAttribute;
|
|
1426
|
-
if (typeof this.value === "undefined") {
|
|
1427
|
-
this.value = null;
|
|
1428
|
-
}
|
|
1429
1437
|
}
|
|
1430
1438
|
/**
|
|
1431
1439
|
* Flag set to true if the attribute value is static.
|
|
@@ -1569,11 +1577,11 @@ class Context {
|
|
|
1569
1577
|
while ((offset = consumed.indexOf("\n")) >= 0) {
|
|
1570
1578
|
this.line++;
|
|
1571
1579
|
this.column = 1;
|
|
1572
|
-
consumed = consumed.
|
|
1580
|
+
consumed = consumed.slice(offset + 1);
|
|
1573
1581
|
}
|
|
1574
1582
|
this.column += consumed.length;
|
|
1575
1583
|
this.offset += n;
|
|
1576
|
-
this.string = this.string.
|
|
1584
|
+
this.string = this.string.slice(n);
|
|
1577
1585
|
this.state = state;
|
|
1578
1586
|
}
|
|
1579
1587
|
getLocation(size) {
|
|
@@ -1751,7 +1759,7 @@ class DOMNode {
|
|
|
1751
1759
|
* node has no children.
|
|
1752
1760
|
*/
|
|
1753
1761
|
get lastChild() {
|
|
1754
|
-
return this.childNodes
|
|
1762
|
+
return this.childNodes.at(-1) ?? null;
|
|
1755
1763
|
}
|
|
1756
1764
|
/**
|
|
1757
1765
|
* @internal
|
|
@@ -1866,7 +1874,7 @@ function parse(text, baseLocation) {
|
|
|
1866
1874
|
begin++;
|
|
1867
1875
|
continue;
|
|
1868
1876
|
}
|
|
1869
|
-
const token = text.
|
|
1877
|
+
const token = text.slice(begin, end);
|
|
1870
1878
|
tokens.push(token);
|
|
1871
1879
|
if (locations && baseLocation) {
|
|
1872
1880
|
const location = sliceLocation(baseLocation, begin, end);
|
|
@@ -1881,7 +1889,7 @@ class DOMTokenList extends Array {
|
|
|
1881
1889
|
locations;
|
|
1882
1890
|
constructor(value, location) {
|
|
1883
1891
|
if (value && typeof value === "string") {
|
|
1884
|
-
const normalized = value.
|
|
1892
|
+
const normalized = value.replaceAll(/[\t\r\n]/g, " ");
|
|
1885
1893
|
const { tokens, locations } = parse(normalized, location);
|
|
1886
1894
|
super(...tokens);
|
|
1887
1895
|
this.locations = locations;
|
|
@@ -1971,7 +1979,7 @@ function nthChild(node, args) {
|
|
|
1971
1979
|
if (!args) {
|
|
1972
1980
|
throw new Error("Missing argument to nth-child");
|
|
1973
1981
|
}
|
|
1974
|
-
const n = parseInt(args.trim(), 10);
|
|
1982
|
+
const n = Number.parseInt(args.trim(), 10);
|
|
1975
1983
|
const cur = getNthChild(node);
|
|
1976
1984
|
return cur === n;
|
|
1977
1985
|
}
|
|
@@ -1996,7 +2004,7 @@ function factory(name, context) {
|
|
|
1996
2004
|
}
|
|
1997
2005
|
|
|
1998
2006
|
function stripslashes(value) {
|
|
1999
|
-
return value.
|
|
2007
|
+
return value.replaceAll(/\\(.)/g, "$1");
|
|
2000
2008
|
}
|
|
2001
2009
|
class Condition {
|
|
2002
2010
|
}
|
|
@@ -2195,7 +2203,7 @@ function candidatesFromCombinator(element, combinator) {
|
|
|
2195
2203
|
}
|
|
2196
2204
|
}
|
|
2197
2205
|
function matchElement(element, compounds, context) {
|
|
2198
|
-
const last = compounds
|
|
2206
|
+
const last = compounds.at(-1);
|
|
2199
2207
|
if (!last.match(element, context)) {
|
|
2200
2208
|
return false;
|
|
2201
2209
|
}
|
|
@@ -2212,7 +2220,7 @@ function matchElement(element, compounds, context) {
|
|
|
2212
2220
|
return false;
|
|
2213
2221
|
}
|
|
2214
2222
|
|
|
2215
|
-
const escapedCodepoints = ["9", "a", "d"];
|
|
2223
|
+
const escapedCodepoints = /* @__PURE__ */ new Set(["9", "a", "d"]);
|
|
2216
2224
|
function* splitSelectorElements(selector) {
|
|
2217
2225
|
let begin = 0;
|
|
2218
2226
|
let end = 0;
|
|
@@ -2227,7 +2235,7 @@ function* splitSelectorElements(selector) {
|
|
|
2227
2235
|
return 0 /* INITIAL */;
|
|
2228
2236
|
}
|
|
2229
2237
|
function escapedState(ch) {
|
|
2230
|
-
if (escapedCodepoints.
|
|
2238
|
+
if (escapedCodepoints.has(ch)) {
|
|
2231
2239
|
return 1 /* ESCAPED */;
|
|
2232
2240
|
}
|
|
2233
2241
|
return 0 /* INITIAL */;
|
|
@@ -2267,7 +2275,7 @@ function unescapeCodepoint(value) {
|
|
|
2267
2275
|
"\\a ": "\n",
|
|
2268
2276
|
"\\d ": "\r"
|
|
2269
2277
|
};
|
|
2270
|
-
return value.
|
|
2278
|
+
return value.replaceAll(
|
|
2271
2279
|
/(\\[\u0039\u0061\u0064] )/g,
|
|
2272
2280
|
(_, codepoint) => replacement[codepoint]
|
|
2273
2281
|
);
|
|
@@ -2278,7 +2286,7 @@ function escapeSelectorComponent(text) {
|
|
|
2278
2286
|
"\n": "\\a ",
|
|
2279
2287
|
"\r": "\\d "
|
|
2280
2288
|
};
|
|
2281
|
-
return text.toString().
|
|
2289
|
+
return text.toString().replaceAll(/([\t\n\r]|[^a-z0-9_-])/gi, (_, ch) => {
|
|
2282
2290
|
if (codepoints[ch]) {
|
|
2283
2291
|
return codepoints[ch];
|
|
2284
2292
|
} else {
|
|
@@ -2327,7 +2335,7 @@ class Selector {
|
|
|
2327
2335
|
}
|
|
2328
2336
|
}
|
|
2329
2337
|
static parse(selector) {
|
|
2330
|
-
selector = selector.
|
|
2338
|
+
selector = selector.replaceAll(/([+~>]) /g, "$1");
|
|
2331
2339
|
return Array.from(splitSelectorElements(selector), (element) => {
|
|
2332
2340
|
return new Compound(unescapeCodepoint(element));
|
|
2333
2341
|
});
|
|
@@ -2623,7 +2631,7 @@ class HtmlElement extends DOMNode {
|
|
|
2623
2631
|
}
|
|
2624
2632
|
parts.push(`${cur.tagName.toLowerCase()}:nth-child(${String(index + 1)})`);
|
|
2625
2633
|
}
|
|
2626
|
-
return parts.
|
|
2634
|
+
return parts.toReversed().join(" > ");
|
|
2627
2635
|
}
|
|
2628
2636
|
/**
|
|
2629
2637
|
* Tests if this element has given tagname.
|
|
@@ -2665,7 +2673,7 @@ class HtmlElement extends DOMNode {
|
|
|
2665
2673
|
this.metaElement ??= {};
|
|
2666
2674
|
for (const key of MetaCopyableProperty) {
|
|
2667
2675
|
const value = meta[key];
|
|
2668
|
-
if (
|
|
2676
|
+
if (value !== void 0) {
|
|
2669
2677
|
setMetaProperty(this.metaElement, key, value);
|
|
2670
2678
|
} else {
|
|
2671
2679
|
delete this.metaElement[key];
|
|
@@ -2769,8 +2777,8 @@ class HtmlElement extends DOMNode {
|
|
|
2769
2777
|
if (tabindex.value instanceof DynamicValue) {
|
|
2770
2778
|
return this.cacheSet(TABINDEX, 0);
|
|
2771
2779
|
}
|
|
2772
|
-
const parsed = parseInt(tabindex.value, 10);
|
|
2773
|
-
if (isNaN(parsed)) {
|
|
2780
|
+
const parsed = Number.parseInt(tabindex.value, 10);
|
|
2781
|
+
if (Number.isNaN(parsed)) {
|
|
2774
2782
|
return this.cacheSet(TABINDEX, null);
|
|
2775
2783
|
}
|
|
2776
2784
|
return this.cacheSet(TABINDEX, parsed);
|
|
@@ -2874,7 +2882,10 @@ class HtmlElement extends DOMNode {
|
|
|
2874
2882
|
*/
|
|
2875
2883
|
get lastElementChild() {
|
|
2876
2884
|
const children = this.childElements;
|
|
2877
|
-
return children.length > 0 ?
|
|
2885
|
+
return children.length > 0 ? (
|
|
2886
|
+
/* eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- it is checked right before this */
|
|
2887
|
+
children.at(-1)
|
|
2888
|
+
) : null;
|
|
2878
2889
|
}
|
|
2879
2890
|
get siblings() {
|
|
2880
2891
|
return this.parent ? this.parent.childElements : [this];
|
|
@@ -2997,7 +3008,9 @@ function depthFirst(root, callback) {
|
|
|
2997
3008
|
root = root.root;
|
|
2998
3009
|
}
|
|
2999
3010
|
function visit(node) {
|
|
3000
|
-
node.childElements
|
|
3011
|
+
for (const child of node.childElements) {
|
|
3012
|
+
visit(child);
|
|
3013
|
+
}
|
|
3001
3014
|
if (!node.isRootElement()) {
|
|
3002
3015
|
callback(node);
|
|
3003
3016
|
}
|
|
@@ -3086,7 +3099,7 @@ class DOMTree {
|
|
|
3086
3099
|
}
|
|
3087
3100
|
}
|
|
3088
3101
|
|
|
3089
|
-
const allowedKeys = ["exclude"];
|
|
3102
|
+
const allowedKeys = /* @__PURE__ */ new Set(["exclude"]);
|
|
3090
3103
|
class Validator {
|
|
3091
3104
|
/**
|
|
3092
3105
|
* Test if element is used in a proper context.
|
|
@@ -3325,7 +3338,7 @@ class Validator {
|
|
|
3325
3338
|
}
|
|
3326
3339
|
function validateKeys(rule) {
|
|
3327
3340
|
for (const key of Object.keys(rule)) {
|
|
3328
|
-
if (!allowedKeys.
|
|
3341
|
+
if (!allowedKeys.has(key)) {
|
|
3329
3342
|
const str = JSON.stringify(rule);
|
|
3330
3343
|
throw new Error(`Permitted rule "${str}" contains unknown property "${key}"`);
|
|
3331
3344
|
}
|
|
@@ -3367,7 +3380,7 @@ function parseSeverity(value) {
|
|
|
3367
3380
|
|
|
3368
3381
|
const cacheKey = /* @__PURE__ */ Symbol("aria-naming");
|
|
3369
3382
|
const defaultValue = "allowed";
|
|
3370
|
-
const prohibitedRoles = [
|
|
3383
|
+
const prohibitedRoles = /* @__PURE__ */ new Set([
|
|
3371
3384
|
"caption",
|
|
3372
3385
|
"code",
|
|
3373
3386
|
"deletion",
|
|
@@ -3379,9 +3392,9 @@ const prohibitedRoles = [
|
|
|
3379
3392
|
"strong",
|
|
3380
3393
|
"subscript",
|
|
3381
3394
|
"superscript"
|
|
3382
|
-
];
|
|
3395
|
+
]);
|
|
3383
3396
|
function byRole(role) {
|
|
3384
|
-
return prohibitedRoles.
|
|
3397
|
+
return prohibitedRoles.has(role) ? "prohibited" : "allowed";
|
|
3385
3398
|
}
|
|
3386
3399
|
function byMeta(element, meta) {
|
|
3387
3400
|
return meta.aria.naming(element._adapter);
|
|
@@ -3433,7 +3446,7 @@ function isInputDisabledImpl(node) {
|
|
|
3433
3446
|
|
|
3434
3447
|
const patternCache = /* @__PURE__ */ new Map();
|
|
3435
3448
|
function compileStringPattern(pattern) {
|
|
3436
|
-
const regexp = pattern.
|
|
3449
|
+
const regexp = pattern.replaceAll(/[*]+/g, ".+");
|
|
3437
3450
|
return new RegExp(`^${regexp}$`);
|
|
3438
3451
|
}
|
|
3439
3452
|
function compileRegExpPattern(pattern) {
|
|
@@ -3708,8 +3721,8 @@ function format(value, quote = false) {
|
|
|
3708
3721
|
return String(value);
|
|
3709
3722
|
}
|
|
3710
3723
|
function interpolate(text, data) {
|
|
3711
|
-
return text.
|
|
3712
|
-
return
|
|
3724
|
+
return text.replaceAll(/{{\s*([^\s{}]+)\s*}}/g, (match, key) => {
|
|
3725
|
+
return data[key] !== void 0 ? format(data[key]) : match;
|
|
3713
3726
|
});
|
|
3714
3727
|
}
|
|
3715
3728
|
|
|
@@ -3917,7 +3930,14 @@ class Rule {
|
|
|
3917
3930
|
});
|
|
3918
3931
|
if (enabled && !blocked) {
|
|
3919
3932
|
const interpolated = interpolate(message, context ?? {});
|
|
3920
|
-
this.reporter.add(
|
|
3933
|
+
this.reporter.add({
|
|
3934
|
+
rule: this,
|
|
3935
|
+
message: interpolated,
|
|
3936
|
+
severity: this.severity,
|
|
3937
|
+
node,
|
|
3938
|
+
location: where,
|
|
3939
|
+
context
|
|
3940
|
+
});
|
|
3921
3941
|
}
|
|
3922
3942
|
}
|
|
3923
3943
|
findLocation(src) {
|
|
@@ -4008,7 +4028,7 @@ class Rule {
|
|
|
4008
4028
|
}
|
|
4009
4029
|
}
|
|
4010
4030
|
|
|
4011
|
-
const defaults$
|
|
4031
|
+
const defaults$B = {
|
|
4012
4032
|
allowExternal: true,
|
|
4013
4033
|
allowRelative: true,
|
|
4014
4034
|
allowAbsolute: true,
|
|
@@ -4052,7 +4072,7 @@ class AllowedLinks extends Rule {
|
|
|
4052
4072
|
allowRelative;
|
|
4053
4073
|
allowAbsolute;
|
|
4054
4074
|
constructor(options) {
|
|
4055
|
-
super({ ...defaults$
|
|
4075
|
+
super({ ...defaults$B, ...options });
|
|
4056
4076
|
this.allowExternal = parseAllow(this.options.allowExternal);
|
|
4057
4077
|
this.allowRelative = parseAllow(this.options.allowRelative);
|
|
4058
4078
|
this.allowAbsolute = parseAllow(this.options.allowAbsolute);
|
|
@@ -4220,7 +4240,7 @@ class AllowedLinks extends Rule {
|
|
|
4220
4240
|
}
|
|
4221
4241
|
}
|
|
4222
4242
|
|
|
4223
|
-
const defaults$
|
|
4243
|
+
const defaults$A = {
|
|
4224
4244
|
accessible: true
|
|
4225
4245
|
};
|
|
4226
4246
|
function findByTarget(target, siblings) {
|
|
@@ -4250,7 +4270,7 @@ function getDescription$1(context) {
|
|
|
4250
4270
|
}
|
|
4251
4271
|
class AreaAlt extends Rule {
|
|
4252
4272
|
constructor(options) {
|
|
4253
|
-
super({ ...defaults$
|
|
4273
|
+
super({ ...defaults$A, ...options });
|
|
4254
4274
|
}
|
|
4255
4275
|
static schema() {
|
|
4256
4276
|
return {
|
|
@@ -4329,7 +4349,7 @@ class AriaHiddenBody extends Rule {
|
|
|
4329
4349
|
}
|
|
4330
4350
|
}
|
|
4331
4351
|
|
|
4332
|
-
const defaults$
|
|
4352
|
+
const defaults$z = {
|
|
4333
4353
|
allowAnyNamable: false,
|
|
4334
4354
|
elements: {
|
|
4335
4355
|
include: null,
|
|
@@ -4377,7 +4397,7 @@ function isValidUsage(target, meta) {
|
|
|
4377
4397
|
}
|
|
4378
4398
|
class AriaLabelMisuse extends Rule {
|
|
4379
4399
|
constructor(options) {
|
|
4380
|
-
super({ ...defaults$
|
|
4400
|
+
super({ ...defaults$z, ...options });
|
|
4381
4401
|
}
|
|
4382
4402
|
static schema() {
|
|
4383
4403
|
return {
|
|
@@ -4489,8 +4509,7 @@ class AriaLabelMisuse extends Rule {
|
|
|
4489
4509
|
class ConfigError extends UserError {
|
|
4490
4510
|
constructor(message, nested) {
|
|
4491
4511
|
super(message, nested);
|
|
4492
|
-
|
|
4493
|
-
this.name = ConfigError.name;
|
|
4512
|
+
this.name = "ConfigError";
|
|
4494
4513
|
}
|
|
4495
4514
|
}
|
|
4496
4515
|
|
|
@@ -4546,14 +4565,14 @@ class CaseStyle {
|
|
|
4546
4565
|
}
|
|
4547
4566
|
}
|
|
4548
4567
|
|
|
4549
|
-
const defaults$
|
|
4568
|
+
const defaults$y = {
|
|
4550
4569
|
style: "lowercase",
|
|
4551
4570
|
ignoreForeign: true
|
|
4552
4571
|
};
|
|
4553
4572
|
class AttrCase extends Rule {
|
|
4554
4573
|
style;
|
|
4555
4574
|
constructor(options) {
|
|
4556
|
-
super({ ...defaults$
|
|
4575
|
+
super({ ...defaults$y, ...options });
|
|
4557
4576
|
this.style = new CaseStyle(this.options.style, "attr-case");
|
|
4558
4577
|
}
|
|
4559
4578
|
static schema() {
|
|
@@ -4594,7 +4613,7 @@ class AttrCase extends Rule {
|
|
|
4594
4613
|
if (event.originalAttribute) {
|
|
4595
4614
|
return;
|
|
4596
4615
|
}
|
|
4597
|
-
const letters = event.key.
|
|
4616
|
+
const letters = event.key.replaceAll(/[^a-z]+/gi, "");
|
|
4598
4617
|
if (this.style.match(letters)) {
|
|
4599
4618
|
return;
|
|
4600
4619
|
}
|
|
@@ -4667,6 +4686,7 @@ class InvalidTokenError extends Error {
|
|
|
4667
4686
|
location;
|
|
4668
4687
|
constructor(location, message) {
|
|
4669
4688
|
super(message);
|
|
4689
|
+
this.name = "InvalidTokenError";
|
|
4670
4690
|
this.location = location;
|
|
4671
4691
|
}
|
|
4672
4692
|
}
|
|
@@ -4776,16 +4796,26 @@ class Lexer {
|
|
|
4776
4796
|
*/
|
|
4777
4797
|
enter(context, state, data) {
|
|
4778
4798
|
if (state === State.TAG && data?.[0].startsWith("<")) {
|
|
4779
|
-
|
|
4780
|
-
|
|
4781
|
-
|
|
4782
|
-
|
|
4783
|
-
|
|
4784
|
-
|
|
4785
|
-
|
|
4786
|
-
|
|
4787
|
-
|
|
4788
|
-
|
|
4799
|
+
switch (data[0]) {
|
|
4800
|
+
case "<script": {
|
|
4801
|
+
context.contentModel = ContentModel.SCRIPT;
|
|
4802
|
+
break;
|
|
4803
|
+
}
|
|
4804
|
+
case "<style": {
|
|
4805
|
+
context.contentModel = ContentModel.STYLE;
|
|
4806
|
+
break;
|
|
4807
|
+
}
|
|
4808
|
+
case "<textarea": {
|
|
4809
|
+
context.contentModel = ContentModel.TEXTAREA;
|
|
4810
|
+
break;
|
|
4811
|
+
}
|
|
4812
|
+
case "<title": {
|
|
4813
|
+
context.contentModel = ContentModel.TITLE;
|
|
4814
|
+
break;
|
|
4815
|
+
}
|
|
4816
|
+
default: {
|
|
4817
|
+
context.contentModel = ContentModel.TEXT;
|
|
4818
|
+
}
|
|
4789
4819
|
}
|
|
4790
4820
|
}
|
|
4791
4821
|
}
|
|
@@ -4958,7 +4988,7 @@ class AttrDelimiter extends Rule {
|
|
|
4958
4988
|
}
|
|
4959
4989
|
|
|
4960
4990
|
const DEFAULT_PATTERN = "[a-z0-9-:]+";
|
|
4961
|
-
const defaults$
|
|
4991
|
+
const defaults$x = {
|
|
4962
4992
|
pattern: DEFAULT_PATTERN,
|
|
4963
4993
|
ignoreForeign: true
|
|
4964
4994
|
};
|
|
@@ -4991,7 +5021,7 @@ function generateDescription(name, pattern) {
|
|
|
4991
5021
|
class AttrPattern extends Rule {
|
|
4992
5022
|
pattern;
|
|
4993
5023
|
constructor(options) {
|
|
4994
|
-
super({ ...defaults$
|
|
5024
|
+
super({ ...defaults$x, ...options });
|
|
4995
5025
|
this.pattern = generateRegexp(this.options.pattern);
|
|
4996
5026
|
}
|
|
4997
5027
|
static schema() {
|
|
@@ -5038,7 +5068,7 @@ class AttrPattern extends Rule {
|
|
|
5038
5068
|
}
|
|
5039
5069
|
}
|
|
5040
5070
|
|
|
5041
|
-
const defaults$
|
|
5071
|
+
const defaults$w = {
|
|
5042
5072
|
style: "auto",
|
|
5043
5073
|
unquoted: false
|
|
5044
5074
|
};
|
|
@@ -5104,7 +5134,7 @@ class AttrQuotes extends Rule {
|
|
|
5104
5134
|
};
|
|
5105
5135
|
}
|
|
5106
5136
|
constructor(options) {
|
|
5107
|
-
super({ ...defaults$
|
|
5137
|
+
super({ ...defaults$w, ...options });
|
|
5108
5138
|
this.style = parseStyle$3(this.options.style);
|
|
5109
5139
|
}
|
|
5110
5140
|
setup() {
|
|
@@ -5186,10 +5216,10 @@ class AttrSpacing extends Rule {
|
|
|
5186
5216
|
|
|
5187
5217
|
function pick(attr) {
|
|
5188
5218
|
const result = {};
|
|
5189
|
-
if (
|
|
5219
|
+
if (attr.enum !== void 0) {
|
|
5190
5220
|
result.enum = attr.enum;
|
|
5191
5221
|
}
|
|
5192
|
-
if (
|
|
5222
|
+
if (attr.boolean !== void 0) {
|
|
5193
5223
|
result.boolean = attr.boolean;
|
|
5194
5224
|
}
|
|
5195
5225
|
return result;
|
|
@@ -5260,13 +5290,13 @@ class AttributeAllowedValues extends Rule {
|
|
|
5260
5290
|
}
|
|
5261
5291
|
}
|
|
5262
5292
|
|
|
5263
|
-
const defaults$
|
|
5293
|
+
const defaults$v = {
|
|
5264
5294
|
style: "omit"
|
|
5265
5295
|
};
|
|
5266
5296
|
class AttributeBooleanStyle extends Rule {
|
|
5267
5297
|
hasInvalidStyle;
|
|
5268
5298
|
constructor(options) {
|
|
5269
|
-
super({ ...defaults$
|
|
5299
|
+
super({ ...defaults$v, ...options });
|
|
5270
5300
|
this.hasInvalidStyle = parseStyle$2(this.options.style);
|
|
5271
5301
|
}
|
|
5272
5302
|
static schema() {
|
|
@@ -5336,13 +5366,13 @@ function reportMessage$1(attr, style) {
|
|
|
5336
5366
|
return "";
|
|
5337
5367
|
}
|
|
5338
5368
|
|
|
5339
|
-
const defaults$
|
|
5369
|
+
const defaults$u = {
|
|
5340
5370
|
style: "omit"
|
|
5341
5371
|
};
|
|
5342
5372
|
class AttributeEmptyStyle extends Rule {
|
|
5343
5373
|
hasInvalidStyle;
|
|
5344
5374
|
constructor(options) {
|
|
5345
|
-
super({ ...defaults$
|
|
5375
|
+
super({ ...defaults$u, ...options });
|
|
5346
5376
|
this.hasInvalidStyle = parseStyle$1(this.options.style);
|
|
5347
5377
|
}
|
|
5348
5378
|
static schema() {
|
|
@@ -5459,7 +5489,7 @@ class AttributeMisuse extends Rule {
|
|
|
5459
5489
|
}
|
|
5460
5490
|
}
|
|
5461
5491
|
|
|
5462
|
-
const defaults$
|
|
5492
|
+
const defaults$t = {
|
|
5463
5493
|
preferred: void 0
|
|
5464
5494
|
};
|
|
5465
5495
|
function isPasswordInput(event) {
|
|
@@ -5476,7 +5506,7 @@ function isGroupingToken(token) {
|
|
|
5476
5506
|
class AutocompletePassword extends Rule {
|
|
5477
5507
|
preferred;
|
|
5478
5508
|
constructor(options) {
|
|
5479
|
-
super({ ...defaults$
|
|
5509
|
+
super({ ...defaults$t, ...options });
|
|
5480
5510
|
this.preferred = options.preferred?.toLowerCase();
|
|
5481
5511
|
}
|
|
5482
5512
|
static schema() {
|
|
@@ -5706,7 +5736,7 @@ class BasePatternRule extends Rule {
|
|
|
5706
5736
|
}
|
|
5707
5737
|
}
|
|
5708
5738
|
|
|
5709
|
-
const defaults$
|
|
5739
|
+
const defaults$s = {
|
|
5710
5740
|
pattern: "kebabcase"
|
|
5711
5741
|
};
|
|
5712
5742
|
class ClassPattern extends BasePatternRule {
|
|
@@ -5714,7 +5744,7 @@ class ClassPattern extends BasePatternRule {
|
|
|
5714
5744
|
super({
|
|
5715
5745
|
ruleId: "class-pattern",
|
|
5716
5746
|
attr: "class",
|
|
5717
|
-
options: { ...defaults$
|
|
5747
|
+
options: { ...defaults$s, ...options },
|
|
5718
5748
|
allowedPatterns: patternNames
|
|
5719
5749
|
// allow all patterns
|
|
5720
5750
|
});
|
|
@@ -5864,13 +5894,13 @@ class CloseOrder extends Rule {
|
|
|
5864
5894
|
}
|
|
5865
5895
|
}
|
|
5866
5896
|
|
|
5867
|
-
const defaults$
|
|
5897
|
+
const defaults$r = {
|
|
5868
5898
|
include: null,
|
|
5869
5899
|
exclude: null
|
|
5870
5900
|
};
|
|
5871
5901
|
class Deprecated extends Rule {
|
|
5872
5902
|
constructor(options) {
|
|
5873
|
-
super({ ...defaults$
|
|
5903
|
+
super({ ...defaults$r, ...options });
|
|
5874
5904
|
}
|
|
5875
5905
|
static schema() {
|
|
5876
5906
|
return {
|
|
@@ -5916,7 +5946,7 @@ class Deprecated extends Rule {
|
|
|
5916
5946
|
text.push(context.documentation);
|
|
5917
5947
|
}
|
|
5918
5948
|
const doc = {
|
|
5919
|
-
description: text.map((cur) => cur.
|
|
5949
|
+
description: text.map((cur) => cur.replaceAll("$tagname", context.tagName)).join("\n\n"),
|
|
5920
5950
|
url: "https://html-validate.org/rules/deprecated.html"
|
|
5921
5951
|
};
|
|
5922
5952
|
return doc;
|
|
@@ -5978,6 +6008,137 @@ function prettySource(source) {
|
|
|
5978
6008
|
}
|
|
5979
6009
|
}
|
|
5980
6010
|
|
|
6011
|
+
function quote(value, char = '"') {
|
|
6012
|
+
return `${char}${value}${char}`;
|
|
6013
|
+
}
|
|
6014
|
+
|
|
6015
|
+
const defaults$q = {
|
|
6016
|
+
classes: []
|
|
6017
|
+
};
|
|
6018
|
+
function isRelevant$6(event) {
|
|
6019
|
+
return event.key.toLowerCase() === "class";
|
|
6020
|
+
}
|
|
6021
|
+
function normalizeEntry(entry) {
|
|
6022
|
+
const { class: className, message, replacement = [], url } = entry;
|
|
6023
|
+
return {
|
|
6024
|
+
class: className,
|
|
6025
|
+
message,
|
|
6026
|
+
replacement: Array.isArray(replacement) ? replacement : [replacement],
|
|
6027
|
+
url
|
|
6028
|
+
};
|
|
6029
|
+
}
|
|
6030
|
+
function formatDeprecatedMessage(className, entry) {
|
|
6031
|
+
let message = `class "${className}" is deprecated`;
|
|
6032
|
+
if (entry.replacement.length > 0) {
|
|
6033
|
+
const joined = utils_naturalJoin.naturalJoin(
|
|
6034
|
+
entry.replacement.map((r) => quote(r)),
|
|
6035
|
+
"or"
|
|
6036
|
+
);
|
|
6037
|
+
message += ` and replaced with ${joined}`;
|
|
6038
|
+
}
|
|
6039
|
+
if (entry.message) {
|
|
6040
|
+
message += `: ${entry.message}`;
|
|
6041
|
+
}
|
|
6042
|
+
return message;
|
|
6043
|
+
}
|
|
6044
|
+
function formatDocumentationDescription(context) {
|
|
6045
|
+
const text = [];
|
|
6046
|
+
const className = context.class;
|
|
6047
|
+
let description = `The class \`${className}\` is deprecated and should not be used`;
|
|
6048
|
+
if (context.message) {
|
|
6049
|
+
description += `: ${context.message}.`;
|
|
6050
|
+
} else {
|
|
6051
|
+
description += ".";
|
|
6052
|
+
}
|
|
6053
|
+
text.push(description);
|
|
6054
|
+
if (context.replacement.length === 1) {
|
|
6055
|
+
text.push(`Use the replacement class ${quote(context.replacement[0], "`")} instead.`);
|
|
6056
|
+
} else if (context.replacement.length > 1) {
|
|
6057
|
+
const listItems = context.replacement.map((r) => `- ${quote(r, "`")}`);
|
|
6058
|
+
text.push(`Use one of the following replacement classes instead:
|
|
6059
|
+
${listItems.join("\n")}`);
|
|
6060
|
+
}
|
|
6061
|
+
if (context.url) {
|
|
6062
|
+
text.push(`For details see: ${context.url}`);
|
|
6063
|
+
}
|
|
6064
|
+
return text.join("\n\n");
|
|
6065
|
+
}
|
|
6066
|
+
class DeprecatedClass extends Rule {
|
|
6067
|
+
deprecatedMap;
|
|
6068
|
+
constructor(options) {
|
|
6069
|
+
super({ ...defaults$q, ...options });
|
|
6070
|
+
const { classes } = this.options;
|
|
6071
|
+
this.deprecatedMap = new Map(classes.map((entry) => [entry.class, normalizeEntry(entry)]));
|
|
6072
|
+
}
|
|
6073
|
+
static schema() {
|
|
6074
|
+
return {
|
|
6075
|
+
classes: {
|
|
6076
|
+
type: "array",
|
|
6077
|
+
items: {
|
|
6078
|
+
type: "object",
|
|
6079
|
+
properties: {
|
|
6080
|
+
class: {
|
|
6081
|
+
type: "string"
|
|
6082
|
+
},
|
|
6083
|
+
message: {
|
|
6084
|
+
type: "string"
|
|
6085
|
+
},
|
|
6086
|
+
replacement: {
|
|
6087
|
+
anyOf: [
|
|
6088
|
+
{
|
|
6089
|
+
type: "string"
|
|
6090
|
+
},
|
|
6091
|
+
{
|
|
6092
|
+
type: "array",
|
|
6093
|
+
items: {
|
|
6094
|
+
type: "string"
|
|
6095
|
+
}
|
|
6096
|
+
}
|
|
6097
|
+
]
|
|
6098
|
+
},
|
|
6099
|
+
url: {
|
|
6100
|
+
type: "string"
|
|
6101
|
+
}
|
|
6102
|
+
},
|
|
6103
|
+
required: ["class"],
|
|
6104
|
+
additionalProperties: false
|
|
6105
|
+
}
|
|
6106
|
+
}
|
|
6107
|
+
};
|
|
6108
|
+
}
|
|
6109
|
+
documentation(context) {
|
|
6110
|
+
return {
|
|
6111
|
+
description: formatDocumentationDescription(context),
|
|
6112
|
+
url: "https://html-validate.org/rules/deprecated-class.html"
|
|
6113
|
+
};
|
|
6114
|
+
}
|
|
6115
|
+
setup() {
|
|
6116
|
+
this.on("attr", isRelevant$6, (event) => {
|
|
6117
|
+
const { value, valueLocation, target } = event;
|
|
6118
|
+
const classes = new DOMTokenList(value, valueLocation);
|
|
6119
|
+
for (const { item, location } of classes.iterator()) {
|
|
6120
|
+
const deprecatedEntry = this.deprecatedMap.get(item);
|
|
6121
|
+
if (!deprecatedEntry) {
|
|
6122
|
+
continue;
|
|
6123
|
+
}
|
|
6124
|
+
const message = formatDeprecatedMessage(item, deprecatedEntry);
|
|
6125
|
+
const context = {
|
|
6126
|
+
class: item,
|
|
6127
|
+
message: deprecatedEntry.message ?? null,
|
|
6128
|
+
replacement: deprecatedEntry.replacement,
|
|
6129
|
+
url: deprecatedEntry.url ?? null
|
|
6130
|
+
};
|
|
6131
|
+
this.report({
|
|
6132
|
+
node: target,
|
|
6133
|
+
message,
|
|
6134
|
+
location,
|
|
6135
|
+
context
|
|
6136
|
+
});
|
|
6137
|
+
}
|
|
6138
|
+
});
|
|
6139
|
+
}
|
|
6140
|
+
}
|
|
6141
|
+
|
|
5981
6142
|
class DeprecatedRule extends Rule {
|
|
5982
6143
|
documentation(context) {
|
|
5983
6144
|
const preamble = context ? `The rule "${context}"` : "This rule";
|
|
@@ -6104,7 +6265,7 @@ class ElementCase extends Rule {
|
|
|
6104
6265
|
});
|
|
6105
6266
|
}
|
|
6106
6267
|
validateCase(target, targetLocation) {
|
|
6107
|
-
const letters = target.tagName.
|
|
6268
|
+
const letters = target.tagName.replaceAll(/[^a-z]+/gi, "");
|
|
6108
6269
|
if (!this.style.match(letters)) {
|
|
6109
6270
|
const location = sliceLocation(targetLocation, 1);
|
|
6110
6271
|
this.report(target, `Element "${target.tagName}" should be ${this.style.name}`, location);
|
|
@@ -6708,8 +6869,8 @@ function haveName(name) {
|
|
|
6708
6869
|
return typeof name === "string" && name !== "";
|
|
6709
6870
|
}
|
|
6710
6871
|
function allowSharedName(node, shared) {
|
|
6711
|
-
const type = node
|
|
6712
|
-
return
|
|
6872
|
+
const type = getControlType(node);
|
|
6873
|
+
return shared.includes(type);
|
|
6713
6874
|
}
|
|
6714
6875
|
function isInputHidden(element) {
|
|
6715
6876
|
return element.is("input") && element.getAttributeValue("type") === "hidden";
|
|
@@ -6717,6 +6878,13 @@ function isInputHidden(element) {
|
|
|
6717
6878
|
function isInputCheckbox(element) {
|
|
6718
6879
|
return element.is("input") && element.getAttributeValue("type") === "checkbox";
|
|
6719
6880
|
}
|
|
6881
|
+
function getControlType(element) {
|
|
6882
|
+
const type = element.getAttributeValue("type") ?? "";
|
|
6883
|
+
if (element.is("button") && type === "") {
|
|
6884
|
+
return "submit";
|
|
6885
|
+
}
|
|
6886
|
+
return type;
|
|
6887
|
+
}
|
|
6720
6888
|
function isCheckboxWithDefault(control, previous, options) {
|
|
6721
6889
|
const { allowCheckboxDefault } = options;
|
|
6722
6890
|
if (!allowCheckboxDefault) {
|
|
@@ -6854,7 +7022,7 @@ class FormDupName extends Rule {
|
|
|
6854
7022
|
validateSharedName(control, group, attr, name) {
|
|
6855
7023
|
const uniqueElements = this.getUniqueElements(group);
|
|
6856
7024
|
const sharedElements = this.getSharedElements(group);
|
|
6857
|
-
const type = control
|
|
7025
|
+
const type = getControlType(control);
|
|
6858
7026
|
if (uniqueElements.has(name) || sharedElements.has(name) && sharedElements.get(name) !== type) {
|
|
6859
7027
|
const context = {
|
|
6860
7028
|
name,
|
|
@@ -6916,7 +7084,7 @@ function isRelevant$5(event) {
|
|
|
6916
7084
|
function extractLevel(node) {
|
|
6917
7085
|
const match = /^[hH](\d)$/.exec(node.tagName);
|
|
6918
7086
|
if (match) {
|
|
6919
|
-
return parseInt(match[1], 10);
|
|
7087
|
+
return Number.parseInt(match[1], 10);
|
|
6920
7088
|
} else {
|
|
6921
7089
|
return null;
|
|
6922
7090
|
}
|
|
@@ -6929,7 +7097,7 @@ function parseMaxInitial(value) {
|
|
|
6929
7097
|
if (!match) {
|
|
6930
7098
|
return 1;
|
|
6931
7099
|
}
|
|
6932
|
-
return parseInt(match[1], 10);
|
|
7100
|
+
return Number.parseInt(match[1], 10);
|
|
6933
7101
|
}
|
|
6934
7102
|
class HeadingLevel extends Rule {
|
|
6935
7103
|
minInitialRank;
|
|
@@ -7078,10 +7246,10 @@ class HeadingLevel extends Rule {
|
|
|
7078
7246
|
this.stack.pop();
|
|
7079
7247
|
}
|
|
7080
7248
|
getPrevRoot() {
|
|
7081
|
-
return this.stack
|
|
7249
|
+
return this.stack.at(-2);
|
|
7082
7250
|
}
|
|
7083
7251
|
getCurrentRoot() {
|
|
7084
|
-
return this.stack
|
|
7252
|
+
return this.stack.at(-1);
|
|
7085
7253
|
}
|
|
7086
7254
|
isSectioningRoot(node) {
|
|
7087
7255
|
const context = {
|
|
@@ -7678,7 +7846,7 @@ function parseContent(text) {
|
|
|
7678
7846
|
const match = /^(\d+)(?:\s*;\s*url=(.*))?/i.exec(text);
|
|
7679
7847
|
if (match) {
|
|
7680
7848
|
return {
|
|
7681
|
-
delay: parseInt(match[1], 10),
|
|
7849
|
+
delay: Number.parseInt(match[1], 10),
|
|
7682
7850
|
url: match[2]
|
|
7683
7851
|
};
|
|
7684
7852
|
} else {
|
|
@@ -8002,13 +8170,12 @@ class NoDupClass extends Rule {
|
|
|
8002
8170
|
}
|
|
8003
8171
|
const classes = new DOMTokenList(event.value, event.valueLocation);
|
|
8004
8172
|
const unique = /* @__PURE__ */ new Set();
|
|
8005
|
-
classes.
|
|
8006
|
-
if (unique.has(
|
|
8007
|
-
|
|
8008
|
-
this.report(event.target, `Class "${cur}" duplicated`, location);
|
|
8173
|
+
for (const { item, location } of classes.iterator()) {
|
|
8174
|
+
if (unique.has(item)) {
|
|
8175
|
+
this.report(event.target, `Class "${item}" duplicated`, location);
|
|
8009
8176
|
}
|
|
8010
|
-
unique.add(
|
|
8011
|
-
}
|
|
8177
|
+
unique.add(item);
|
|
8178
|
+
}
|
|
8012
8179
|
});
|
|
8013
8180
|
}
|
|
8014
8181
|
}
|
|
@@ -8740,7 +8907,7 @@ class NoUnusedDisable extends Rule {
|
|
|
8740
8907
|
setup() {
|
|
8741
8908
|
}
|
|
8742
8909
|
reportUnused(unused, options, location) {
|
|
8743
|
-
const tokens = new DOMTokenList(options.
|
|
8910
|
+
const tokens = new DOMTokenList(options.replaceAll(",", " "), location);
|
|
8744
8911
|
for (const ruleId of unused) {
|
|
8745
8912
|
const index = tokens.indexOf(ruleId);
|
|
8746
8913
|
const tokenLocation = index >= 0 ? tokens.location(index) : location;
|
|
@@ -9061,19 +9228,19 @@ const supportSri = {
|
|
|
9061
9228
|
link: "href",
|
|
9062
9229
|
script: "src"
|
|
9063
9230
|
};
|
|
9064
|
-
const supportedRel = ["stylesheet", "preload", "modulepreload"];
|
|
9065
|
-
const supportedPreload = ["style", "script"];
|
|
9231
|
+
const supportedRel = /* @__PURE__ */ new Set(["stylesheet", "preload", "modulepreload"]);
|
|
9232
|
+
const supportedPreload = /* @__PURE__ */ new Set(["style", "script"]);
|
|
9066
9233
|
function linkSupportsSri(node) {
|
|
9067
9234
|
const rel = node.getAttribute("rel");
|
|
9068
9235
|
if (typeof rel?.value !== "string") {
|
|
9069
9236
|
return false;
|
|
9070
9237
|
}
|
|
9071
|
-
if (!supportedRel.
|
|
9238
|
+
if (!supportedRel.has(rel.value)) {
|
|
9072
9239
|
return false;
|
|
9073
9240
|
}
|
|
9074
9241
|
if (rel.value === "preload") {
|
|
9075
9242
|
const as = node.getAttribute("as");
|
|
9076
|
-
return typeof as?.value === "string" && supportedPreload.
|
|
9243
|
+
return typeof as?.value === "string" && supportedPreload.has(as.value);
|
|
9077
9244
|
}
|
|
9078
9245
|
return true;
|
|
9079
9246
|
}
|
|
@@ -9190,13 +9357,13 @@ class ScriptElement extends Rule {
|
|
|
9190
9357
|
}
|
|
9191
9358
|
}
|
|
9192
9359
|
|
|
9193
|
-
const javascript = [
|
|
9360
|
+
const javascript = /* @__PURE__ */ new Set([
|
|
9194
9361
|
"",
|
|
9195
9362
|
"application/ecmascript",
|
|
9196
9363
|
"application/javascript",
|
|
9197
9364
|
"text/ecmascript",
|
|
9198
9365
|
"text/javascript"
|
|
9199
|
-
];
|
|
9366
|
+
]);
|
|
9200
9367
|
class ScriptType extends Rule {
|
|
9201
9368
|
documentation() {
|
|
9202
9369
|
return {
|
|
@@ -9227,7 +9394,7 @@ class ScriptType extends Rule {
|
|
|
9227
9394
|
}
|
|
9228
9395
|
isJavascript(mime) {
|
|
9229
9396
|
const type = mime.replace(/;.*/, "");
|
|
9230
|
-
return javascript.
|
|
9397
|
+
return javascript.has(type);
|
|
9231
9398
|
}
|
|
9232
9399
|
}
|
|
9233
9400
|
|
|
@@ -9803,7 +9970,7 @@ class UnknownCharReference extends Rule {
|
|
|
9803
9970
|
}
|
|
9804
9971
|
|
|
9805
9972
|
const expectedOrder = ["section", "hint", "contact", "field1", "field2", "webauthn"];
|
|
9806
|
-
const fieldNames1 = [
|
|
9973
|
+
const fieldNames1 = /* @__PURE__ */ new Set([
|
|
9807
9974
|
"name",
|
|
9808
9975
|
"honorific-prefix",
|
|
9809
9976
|
"given-name",
|
|
@@ -9848,8 +10015,8 @@ const fieldNames1 = [
|
|
|
9848
10015
|
"sex",
|
|
9849
10016
|
"url",
|
|
9850
10017
|
"photo"
|
|
9851
|
-
];
|
|
9852
|
-
const fieldNames2 = [
|
|
10018
|
+
]);
|
|
10019
|
+
const fieldNames2 = /* @__PURE__ */ new Set([
|
|
9853
10020
|
"tel",
|
|
9854
10021
|
"tel-country-code",
|
|
9855
10022
|
"tel-national",
|
|
@@ -9860,7 +10027,7 @@ const fieldNames2 = [
|
|
|
9860
10027
|
"tel-extension",
|
|
9861
10028
|
"email",
|
|
9862
10029
|
"impp"
|
|
9863
|
-
];
|
|
10030
|
+
]);
|
|
9864
10031
|
const fieldNameGroup = {
|
|
9865
10032
|
name: "text",
|
|
9866
10033
|
"honorific-prefix": "text",
|
|
@@ -9925,14 +10092,14 @@ function matchHint(token) {
|
|
|
9925
10092
|
return token === "shipping" || token === "billing";
|
|
9926
10093
|
}
|
|
9927
10094
|
function matchFieldNames1(token) {
|
|
9928
|
-
return fieldNames1.
|
|
10095
|
+
return fieldNames1.has(token);
|
|
9929
10096
|
}
|
|
9930
10097
|
function matchContact(token) {
|
|
9931
10098
|
const haystack = ["home", "work", "mobile", "fax", "pager"];
|
|
9932
10099
|
return haystack.includes(token);
|
|
9933
10100
|
}
|
|
9934
10101
|
function matchFieldNames2(token) {
|
|
9935
|
-
return fieldNames2.
|
|
10102
|
+
return fieldNames2.has(token);
|
|
9936
10103
|
}
|
|
9937
10104
|
function matchWebauthn(token) {
|
|
9938
10105
|
return token === "webauthn";
|
|
@@ -10603,11 +10770,13 @@ class H32 extends Rule {
|
|
|
10603
10770
|
setup() {
|
|
10604
10771
|
const formTags = this.getTagsWithProperty("form");
|
|
10605
10772
|
const formSelector = formTags.join(",");
|
|
10773
|
+
const submitButtonTags = this.getTagsWithProperty("submitButton");
|
|
10774
|
+
const submitButtonSelector = submitButtonTags.join(",");
|
|
10606
10775
|
this.on("dom:ready", (event) => {
|
|
10607
10776
|
const { document } = event;
|
|
10608
10777
|
const forms = document.querySelectorAll(formSelector);
|
|
10609
10778
|
for (const form of forms) {
|
|
10610
|
-
if (hasNestedSubmit(form)) {
|
|
10779
|
+
if (hasNestedSubmit(form, submitButtonSelector)) {
|
|
10611
10780
|
continue;
|
|
10612
10781
|
}
|
|
10613
10782
|
if (hasAssociatedSubmit(document, form)) {
|
|
@@ -10619,15 +10788,15 @@ class H32 extends Rule {
|
|
|
10619
10788
|
}
|
|
10620
10789
|
}
|
|
10621
10790
|
function isSubmit(node) {
|
|
10622
|
-
const
|
|
10623
|
-
return
|
|
10791
|
+
const meta = node.meta;
|
|
10792
|
+
return Boolean(meta?.submitButton);
|
|
10624
10793
|
}
|
|
10625
10794
|
function isAssociated(id, node) {
|
|
10626
10795
|
const form = node.getAttribute("form");
|
|
10627
10796
|
return Boolean(form?.valueMatches(id, true));
|
|
10628
10797
|
}
|
|
10629
|
-
function hasNestedSubmit(form) {
|
|
10630
|
-
const matches = form.querySelectorAll(
|
|
10798
|
+
function hasNestedSubmit(form, submitButtonSelector) {
|
|
10799
|
+
const matches = form.querySelectorAll(submitButtonSelector).filter(isSubmit).filter((node) => !node.hasAttribute("form"));
|
|
10631
10800
|
return matches.length > 0;
|
|
10632
10801
|
}
|
|
10633
10802
|
function hasAssociatedSubmit(document, form) {
|
|
@@ -10918,6 +11087,7 @@ const bundledRules = {
|
|
|
10918
11087
|
"close-attr": CloseAttr,
|
|
10919
11088
|
"close-order": CloseOrder,
|
|
10920
11089
|
deprecated: Deprecated,
|
|
11090
|
+
"deprecated-class": DeprecatedClass,
|
|
10921
11091
|
"deprecated-rule": DeprecatedRule,
|
|
10922
11092
|
"doctype-html": NoStyleTag$1,
|
|
10923
11093
|
"doctype-style": DoctypeStyle,
|
|
@@ -11175,11 +11345,11 @@ function dumpTree(root) {
|
|
|
11175
11345
|
} else {
|
|
11176
11346
|
lines.push("(root)");
|
|
11177
11347
|
}
|
|
11178
|
-
node.childElements.
|
|
11348
|
+
for (const [index, child] of node.childElements.entries()) {
|
|
11179
11349
|
const s = lastSibling ? " " : "\u2502";
|
|
11180
11350
|
const i = level > 0 ? `${indent}${s} ` : "";
|
|
11181
11351
|
writeNode(child, level + 1, i, index);
|
|
11182
|
-
}
|
|
11352
|
+
}
|
|
11183
11353
|
}
|
|
11184
11354
|
writeNode(root, 0, "", 0);
|
|
11185
11355
|
return lines;
|
|
@@ -12294,7 +12464,7 @@ class EventHandler {
|
|
|
12294
12464
|
}
|
|
12295
12465
|
|
|
12296
12466
|
const name = "html-validate";
|
|
12297
|
-
const version = "10.
|
|
12467
|
+
const version = "10.10.0";
|
|
12298
12468
|
const bugs = "https://gitlab.com/html-validate/html-validate/issues/new";
|
|
12299
12469
|
|
|
12300
12470
|
function freeze(src) {
|
|
@@ -12310,7 +12480,13 @@ function isThenableArray(value) {
|
|
|
12310
12480
|
return isThenable(value[0]);
|
|
12311
12481
|
}
|
|
12312
12482
|
class Reporter {
|
|
12483
|
+
/**
|
|
12484
|
+
* @internal
|
|
12485
|
+
*/
|
|
12313
12486
|
result;
|
|
12487
|
+
/**
|
|
12488
|
+
* @internal
|
|
12489
|
+
*/
|
|
12314
12490
|
constructor() {
|
|
12315
12491
|
this.result = {};
|
|
12316
12492
|
}
|
|
@@ -12323,16 +12499,16 @@ class Reporter {
|
|
|
12323
12499
|
}
|
|
12324
12500
|
const valid = reports.every((report) => report.valid);
|
|
12325
12501
|
const merged = {};
|
|
12326
|
-
|
|
12327
|
-
report.results
|
|
12502
|
+
for (const report of reports) {
|
|
12503
|
+
for (const result of report.results) {
|
|
12328
12504
|
const key = result.filePath;
|
|
12329
12505
|
if (key in merged) {
|
|
12330
12506
|
merged[key].messages = [...merged[key].messages, ...result.messages];
|
|
12331
12507
|
} else {
|
|
12332
12508
|
merged[key] = { ...result };
|
|
12333
12509
|
}
|
|
12334
|
-
}
|
|
12335
|
-
}
|
|
12510
|
+
}
|
|
12511
|
+
}
|
|
12336
12512
|
const results = Object.values(merged).map((result) => {
|
|
12337
12513
|
result.errorCount = countErrors(result.messages);
|
|
12338
12514
|
result.warningCount = countWarnings(result.messages);
|
|
@@ -12345,8 +12521,11 @@ class Reporter {
|
|
|
12345
12521
|
warningCount: sumWarnings(results)
|
|
12346
12522
|
};
|
|
12347
12523
|
}
|
|
12348
|
-
|
|
12349
|
-
|
|
12524
|
+
/**
|
|
12525
|
+
* @internal
|
|
12526
|
+
*/
|
|
12527
|
+
add(options) {
|
|
12528
|
+
const { rule, message, severity, node, location, context } = options;
|
|
12350
12529
|
if (!(location.filename in this.result)) {
|
|
12351
12530
|
this.result[location.filename] = [];
|
|
12352
12531
|
}
|
|
@@ -12371,17 +12550,23 @@ class Reporter {
|
|
|
12371
12550
|
}
|
|
12372
12551
|
this.result[location.filename].push(entry);
|
|
12373
12552
|
}
|
|
12553
|
+
/**
|
|
12554
|
+
* @internal
|
|
12555
|
+
*/
|
|
12374
12556
|
addManual(filename, message) {
|
|
12375
12557
|
if (!(filename in this.result)) {
|
|
12376
12558
|
this.result[filename] = [];
|
|
12377
12559
|
}
|
|
12378
12560
|
this.result[filename].push(message);
|
|
12379
12561
|
}
|
|
12562
|
+
/**
|
|
12563
|
+
* @internal
|
|
12564
|
+
*/
|
|
12380
12565
|
save(sources) {
|
|
12381
12566
|
const report = {
|
|
12382
12567
|
valid: this.isValid(),
|
|
12383
12568
|
results: Object.keys(this.result).map((filePath) => {
|
|
12384
|
-
const messages = Array.from(this.result[filePath], freeze).
|
|
12569
|
+
const messages = Array.from(this.result[filePath], freeze).toSorted(messageSort);
|
|
12385
12570
|
const source = (sources ?? []).find((source2) => filePath === source2.filename);
|
|
12386
12571
|
return {
|
|
12387
12572
|
filePath,
|
|
@@ -12398,6 +12583,9 @@ class Reporter {
|
|
|
12398
12583
|
report.warningCount = sumWarnings(report.results);
|
|
12399
12584
|
return report;
|
|
12400
12585
|
}
|
|
12586
|
+
/**
|
|
12587
|
+
* @internal
|
|
12588
|
+
*/
|
|
12401
12589
|
isValid() {
|
|
12402
12590
|
const numErrors = Object.values(this.result).reduce((sum, messages) => {
|
|
12403
12591
|
return sum + countErrors(messages);
|
|
@@ -12460,6 +12648,7 @@ class ParserError extends Error {
|
|
|
12460
12648
|
location;
|
|
12461
12649
|
constructor(location, message) {
|
|
12462
12650
|
super(message);
|
|
12651
|
+
this.name = "ParserError";
|
|
12463
12652
|
this.location = location;
|
|
12464
12653
|
}
|
|
12465
12654
|
}
|
|
@@ -12616,7 +12805,7 @@ class Parser {
|
|
|
12616
12805
|
const tokens = Array.from(
|
|
12617
12806
|
this.consumeUntil(tokenStream, TokenType.TAG_CLOSE, startToken.location)
|
|
12618
12807
|
);
|
|
12619
|
-
const endToken = tokens.
|
|
12808
|
+
const endToken = tokens.at(-1);
|
|
12620
12809
|
const closeOptional = this.closeOptional(startToken);
|
|
12621
12810
|
const parent = closeOptional ? this.dom.getActive().parent : this.dom.getActive();
|
|
12622
12811
|
const node = HtmlElement.fromTokens(
|
|
@@ -12734,7 +12923,7 @@ class Parser {
|
|
|
12734
12923
|
const endTokens = Array.from(
|
|
12735
12924
|
this.consumeUntil(tokenStream, TokenType.TAG_CLOSE, last.location)
|
|
12736
12925
|
);
|
|
12737
|
-
endToken = endTokens.
|
|
12926
|
+
endToken = endTokens.at(-1);
|
|
12738
12927
|
const selfClosed = endToken.data[0] === "/>";
|
|
12739
12928
|
if (tagClosed) {
|
|
12740
12929
|
startToken = last;
|
|
@@ -13016,7 +13205,7 @@ class Parser {
|
|
|
13016
13205
|
this.event.once("*", cb);
|
|
13017
13206
|
}
|
|
13018
13207
|
trigger(event, data) {
|
|
13019
|
-
if (
|
|
13208
|
+
if (data.location === void 0) {
|
|
13020
13209
|
throw new Error("Triggered event must contain location");
|
|
13021
13210
|
}
|
|
13022
13211
|
this.event.trigger(event, data);
|
|
@@ -13144,7 +13333,9 @@ class Engine {
|
|
|
13144
13333
|
}
|
|
13145
13334
|
lines.push({ event, data });
|
|
13146
13335
|
});
|
|
13147
|
-
|
|
13336
|
+
for (const src of source) {
|
|
13337
|
+
parser.parseHtml(src);
|
|
13338
|
+
}
|
|
13148
13339
|
return lines;
|
|
13149
13340
|
}
|
|
13150
13341
|
dumpTokens(source) {
|
|
@@ -13609,7 +13800,7 @@ const entities = {
|
|
|
13609
13800
|
"&": "&"
|
|
13610
13801
|
};
|
|
13611
13802
|
function xmlescape(src) {
|
|
13612
|
-
return src.toString().
|
|
13803
|
+
return src.toString().replaceAll(/[><'"&]/g, (match) => {
|
|
13613
13804
|
return entities[match];
|
|
13614
13805
|
});
|
|
13615
13806
|
}
|
|
@@ -13629,11 +13820,11 @@ function checkstyleFormatter(results) {
|
|
|
13629
13820
|
`;
|
|
13630
13821
|
output += `<checkstyle version="4.3">
|
|
13631
13822
|
`;
|
|
13632
|
-
|
|
13823
|
+
for (const result of results) {
|
|
13633
13824
|
const messages = result.messages;
|
|
13634
13825
|
output += ` <file name="${xmlescape(result.filePath)}">
|
|
13635
13826
|
`;
|
|
13636
|
-
|
|
13827
|
+
for (const message of messages) {
|
|
13637
13828
|
const ruleId = xmlescape(`htmlvalidate.rules.${message.ruleId}`);
|
|
13638
13829
|
output += " ";
|
|
13639
13830
|
output += [
|
|
@@ -13644,9 +13835,9 @@ function checkstyleFormatter(results) {
|
|
|
13644
13835
|
`source="${ruleId}" />`
|
|
13645
13836
|
].join(" ");
|
|
13646
13837
|
output += "\n";
|
|
13647
|
-
}
|
|
13838
|
+
}
|
|
13648
13839
|
output += " </file>\n";
|
|
13649
|
-
}
|
|
13840
|
+
}
|
|
13650
13841
|
output += "</checkstyle>\n";
|
|
13651
13842
|
return output;
|
|
13652
13843
|
}
|
|
@@ -13716,11 +13907,11 @@ function codeFrameColumns(rawLines, loc) {
|
|
|
13716
13907
|
if (hasMarker) {
|
|
13717
13908
|
let markerLine = "";
|
|
13718
13909
|
if (Array.isArray(hasMarker)) {
|
|
13719
|
-
const markerSpacing = line.slice(0, Math.max(hasMarker[0] - 1, 0)).
|
|
13910
|
+
const markerSpacing = line.slice(0, Math.max(hasMarker[0] - 1, 0)).replaceAll(/[^\t]/g, " ");
|
|
13720
13911
|
const numberOfMarkers = hasMarker[1] || 1;
|
|
13721
13912
|
markerLine = [
|
|
13722
13913
|
"\n ",
|
|
13723
|
-
gutter.
|
|
13914
|
+
gutter.replaceAll(/\d/g, " "),
|
|
13724
13915
|
" ",
|
|
13725
13916
|
markerSpacing,
|
|
13726
13917
|
"^".repeat(numberOfMarkers)
|
|
@@ -13858,10 +14049,10 @@ function stylish(results) {
|
|
|
13858
14049
|
function textFormatter(results) {
|
|
13859
14050
|
let output = "";
|
|
13860
14051
|
let total = 0;
|
|
13861
|
-
|
|
14052
|
+
for (const result of results) {
|
|
13862
14053
|
const messages = result.messages;
|
|
13863
14054
|
if (messages.length === 0) {
|
|
13864
|
-
|
|
14055
|
+
continue;
|
|
13865
14056
|
}
|
|
13866
14057
|
total += messages.length;
|
|
13867
14058
|
output += messages.map((message) => {
|
|
@@ -13877,7 +14068,7 @@ function textFormatter(results) {
|
|
|
13877
14068
|
return `${location}: ${messageType} [${message.ruleId}] ${message.message}
|
|
13878
14069
|
`;
|
|
13879
14070
|
}).join("");
|
|
13880
|
-
}
|
|
14071
|
+
}
|
|
13881
14072
|
return total > 0 ? output : "";
|
|
13882
14073
|
}
|
|
13883
14074
|
const formatter = textFormatter;
|
|
@@ -14709,7 +14900,7 @@ var ignoreExports = /*@__PURE__*/ requireIgnore();
|
|
|
14709
14900
|
var ignore = /*@__PURE__*/getDefaultExportFromCjs(ignoreExports);
|
|
14710
14901
|
|
|
14711
14902
|
const engines = {
|
|
14712
|
-
node: "^20.19.0 || >= 22.
|
|
14903
|
+
node: "^20.19.0 || >= 22.16.0"
|
|
14713
14904
|
};
|
|
14714
14905
|
|
|
14715
14906
|
var workerPath = "./jest-worker.js";
|