html-validate 6.1.6 → 6.3.2
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/core.d.ts +2 -1
- package/dist/cjs/core.js +87 -19
- package/dist/cjs/core.js.map +1 -1
- package/dist/es/core.d.ts +2 -1
- package/dist/es/core.js +87 -19
- package/dist/es/core.js.map +1 -1
- package/package.json +23 -26
- package/CHANGELOG.md +0 -1724
package/dist/cjs/core.d.ts
CHANGED
|
@@ -1575,7 +1575,8 @@ declare class MetaTable {
|
|
|
1575
1575
|
*/
|
|
1576
1576
|
loadFromFile(filename: string): void;
|
|
1577
1577
|
/**
|
|
1578
|
-
* Get [[MetaElement]] for the given tag
|
|
1578
|
+
* Get [[MetaElement]] for the given tag. If no specific metadata is present
|
|
1579
|
+
* the global metadata is returned or null if no global is present.
|
|
1579
1580
|
*
|
|
1580
1581
|
* @public
|
|
1581
1582
|
* @returns A shallow copy of metadata.
|
package/dist/cjs/core.js
CHANGED
|
@@ -944,14 +944,23 @@ class MetaTable {
|
|
|
944
944
|
}
|
|
945
945
|
}
|
|
946
946
|
/**
|
|
947
|
-
* Get [[MetaElement]] for the given tag
|
|
947
|
+
* Get [[MetaElement]] for the given tag. If no specific metadata is present
|
|
948
|
+
* the global metadata is returned or null if no global is present.
|
|
948
949
|
*
|
|
949
950
|
* @public
|
|
950
951
|
* @returns A shallow copy of metadata.
|
|
951
952
|
*/
|
|
952
953
|
getMetaFor(tagName) {
|
|
954
|
+
/* try to locate by tagname */
|
|
953
955
|
tagName = tagName.toLowerCase();
|
|
954
|
-
|
|
956
|
+
if (this.elements[tagName]) {
|
|
957
|
+
return { ...this.elements[tagName] };
|
|
958
|
+
}
|
|
959
|
+
/* try to locate global element */
|
|
960
|
+
if (this.elements["*"]) {
|
|
961
|
+
return { ...this.elements["*"] };
|
|
962
|
+
}
|
|
963
|
+
return null;
|
|
955
964
|
}
|
|
956
965
|
/**
|
|
957
966
|
* Find all tags which has enabled given property.
|
|
@@ -1783,6 +1792,14 @@ class Selector {
|
|
|
1783
1792
|
}
|
|
1784
1793
|
|
|
1785
1794
|
const TEXT_NODE_NAME = "#text";
|
|
1795
|
+
/**
|
|
1796
|
+
* Returns true if the node is a text node.
|
|
1797
|
+
*
|
|
1798
|
+
* @public
|
|
1799
|
+
*/
|
|
1800
|
+
function isTextNode(node) {
|
|
1801
|
+
return Boolean(node && node.nodeType === NodeType.TEXT_NODE);
|
|
1802
|
+
}
|
|
1786
1803
|
/**
|
|
1787
1804
|
* Represents a text in the HTML document.
|
|
1788
1805
|
*
|
|
@@ -2939,7 +2956,7 @@ var TRANSFORMER_API;
|
|
|
2939
2956
|
/** @public */
|
|
2940
2957
|
const name = "html-validate";
|
|
2941
2958
|
/** @public */
|
|
2942
|
-
const version = "6.
|
|
2959
|
+
const version = "6.3.2";
|
|
2943
2960
|
/** @public */
|
|
2944
2961
|
const homepage = "https://html-validate.org";
|
|
2945
2962
|
/** @public */
|
|
@@ -3219,7 +3236,8 @@ function ruleDocumentationUrl(filename) {
|
|
|
3219
3236
|
const p = path__default["default"].parse(filename);
|
|
3220
3237
|
const root = path__default["default"].join(distFolder, "rules");
|
|
3221
3238
|
const rel = path__default["default"].relative(root, path__default["default"].join(p.dir, p.name));
|
|
3222
|
-
|
|
3239
|
+
const normalized = rel.replace(/\\/g, "/");
|
|
3240
|
+
return `${homepage}/rules/${normalized}.html`;
|
|
3223
3241
|
}
|
|
3224
3242
|
|
|
3225
3243
|
const defaults$p = {
|
|
@@ -3409,6 +3427,28 @@ class AllowedLinks extends Rule {
|
|
|
3409
3427
|
}
|
|
3410
3428
|
}
|
|
3411
3429
|
|
|
3430
|
+
class AriaHiddenBody extends Rule {
|
|
3431
|
+
documentation() {
|
|
3432
|
+
return {
|
|
3433
|
+
description: "`aria-hidden` must not be used on the `<body>` element as it makes the page inaccessible to assistive technology such as screenreaders",
|
|
3434
|
+
url: ruleDocumentationUrl("@/rules/aria-hidden-body.ts"),
|
|
3435
|
+
};
|
|
3436
|
+
}
|
|
3437
|
+
setup() {
|
|
3438
|
+
this.on("tag:ready", this.isRelevant, (event) => {
|
|
3439
|
+
const { target } = event;
|
|
3440
|
+
const attr = target.getAttribute("aria-hidden");
|
|
3441
|
+
if (!attr || !attr.valueMatches("true", true)) {
|
|
3442
|
+
return;
|
|
3443
|
+
}
|
|
3444
|
+
this.report(target, "aria-hidden must not be used on <body>", attr.keyLocation);
|
|
3445
|
+
});
|
|
3446
|
+
}
|
|
3447
|
+
isRelevant(event) {
|
|
3448
|
+
return event.target.is("body");
|
|
3449
|
+
}
|
|
3450
|
+
}
|
|
3451
|
+
|
|
3412
3452
|
const whitelisted = [
|
|
3413
3453
|
"main",
|
|
3414
3454
|
"nav",
|
|
@@ -4085,12 +4125,26 @@ class AttributeAllowedValues extends Rule {
|
|
|
4085
4125
|
if (!context) {
|
|
4086
4126
|
return docs;
|
|
4087
4127
|
}
|
|
4088
|
-
|
|
4089
|
-
|
|
4090
|
-
|
|
4128
|
+
const { allowed, attribute, element, value } = context;
|
|
4129
|
+
if (allowed.enum) {
|
|
4130
|
+
const allowedList = allowed.enum.map((value) => {
|
|
4131
|
+
if (typeof value === "string") {
|
|
4132
|
+
return `- \`"${value}"\``;
|
|
4133
|
+
}
|
|
4134
|
+
else {
|
|
4135
|
+
return `- \`${value.toString()}\``;
|
|
4136
|
+
}
|
|
4137
|
+
});
|
|
4138
|
+
docs.description = [
|
|
4139
|
+
`The \`<${element}>\` element does not allow the attribute \`${attribute}\` to have the value \`"${value}"\`.`,
|
|
4140
|
+
"",
|
|
4141
|
+
"It must match one of the following:",
|
|
4142
|
+
"",
|
|
4143
|
+
...allowedList,
|
|
4144
|
+
].join("\n");
|
|
4091
4145
|
}
|
|
4092
|
-
else if (
|
|
4093
|
-
docs.description = `
|
|
4146
|
+
else if (allowed.boolean) {
|
|
4147
|
+
docs.description = `The \`<${context.element}>\` attribute \`${context.attribute}\` must be a boolean attribute, e.g. \`<${context.element} ${context.attribute}>\``;
|
|
4094
4148
|
}
|
|
4095
4149
|
return docs;
|
|
4096
4150
|
}
|
|
@@ -5022,8 +5076,9 @@ class ElementRequiredAttributes extends Rule {
|
|
|
5022
5076
|
class ElementRequiredContent extends Rule {
|
|
5023
5077
|
documentation(context) {
|
|
5024
5078
|
if (context) {
|
|
5079
|
+
const { element, missing } = context;
|
|
5025
5080
|
return {
|
|
5026
|
-
description: `The
|
|
5081
|
+
description: `The \`${element}\` element requires a \`${missing}\` to be present as content.`,
|
|
5027
5082
|
url: ruleDocumentationUrl("@/rules/element-required-content.ts"),
|
|
5028
5083
|
};
|
|
5029
5084
|
}
|
|
@@ -5049,10 +5104,11 @@ class ElementRequiredContent extends Rule {
|
|
|
5049
5104
|
}
|
|
5050
5105
|
for (const missing of Validator.validateRequiredContent(node, rules)) {
|
|
5051
5106
|
const context = {
|
|
5052
|
-
|
|
5053
|
-
missing
|
|
5107
|
+
element: node.annotatedName,
|
|
5108
|
+
missing: `<${missing}>`,
|
|
5054
5109
|
};
|
|
5055
|
-
|
|
5110
|
+
const message = `${node.annotatedName} element must have <${missing}> as content`;
|
|
5111
|
+
this.report(node, message, null, context);
|
|
5056
5112
|
}
|
|
5057
5113
|
});
|
|
5058
5114
|
});
|
|
@@ -5135,7 +5191,14 @@ class EmptyHeading extends Rule {
|
|
|
5135
5191
|
class EmptyTitle extends Rule {
|
|
5136
5192
|
documentation() {
|
|
5137
5193
|
return {
|
|
5138
|
-
description:
|
|
5194
|
+
description: [
|
|
5195
|
+
"The `<title>` element cannot be empty, it must have textual content.",
|
|
5196
|
+
"",
|
|
5197
|
+
"It is used to describe the document and is shown in the browser tab and titlebar.",
|
|
5198
|
+
"WCAG and SEO requires a descriptive title and preferably unique within the site.",
|
|
5199
|
+
"",
|
|
5200
|
+
"Whitespace is ignored.",
|
|
5201
|
+
].join("\n"),
|
|
5139
5202
|
url: ruleDocumentationUrl("@/rules/empty-title.ts"),
|
|
5140
5203
|
};
|
|
5141
5204
|
}
|
|
@@ -5151,7 +5214,10 @@ class EmptyTitle extends Rule {
|
|
|
5151
5214
|
break;
|
|
5152
5215
|
case TextClassification.EMPTY_TEXT:
|
|
5153
5216
|
/* no content or whitespace only */
|
|
5154
|
-
|
|
5217
|
+
{
|
|
5218
|
+
const message = `<${node.tagName}> cannot be empty, must have text content`;
|
|
5219
|
+
this.report(node, message, node.location);
|
|
5220
|
+
}
|
|
5155
5221
|
break;
|
|
5156
5222
|
}
|
|
5157
5223
|
});
|
|
@@ -7067,9 +7133,6 @@ function hasDefaultText(node) {
|
|
|
7067
7133
|
const type = node.getAttribute("type");
|
|
7068
7134
|
return Boolean(type && type.valueMatches(/submit|reset/, false));
|
|
7069
7135
|
}
|
|
7070
|
-
function isTextNode(node) {
|
|
7071
|
-
return node.nodeType === NodeType.TEXT_NODE;
|
|
7072
|
-
}
|
|
7073
7136
|
function isNonEmptyText(node) {
|
|
7074
7137
|
if (isTextNode(node)) {
|
|
7075
7138
|
return node.isDynamic || node.textContent.trim() !== "";
|
|
@@ -9432,6 +9495,7 @@ const bundledRules$1 = {
|
|
|
9432
9495
|
|
|
9433
9496
|
const bundledRules = {
|
|
9434
9497
|
"allowed-links": AllowedLinks,
|
|
9498
|
+
"aria-hidden-body": AriaHiddenBody,
|
|
9435
9499
|
"aria-label-misuse": AriaLabelMisuse,
|
|
9436
9500
|
"attr-case": AttrCase,
|
|
9437
9501
|
"attr-delimiter": AttrDelimiter,
|
|
@@ -9502,6 +9566,7 @@ var defaultConfig = {};
|
|
|
9502
9566
|
|
|
9503
9567
|
const config$3 = {
|
|
9504
9568
|
rules: {
|
|
9569
|
+
"aria-hidden-body": "error",
|
|
9505
9570
|
"aria-label-misuse": "error",
|
|
9506
9571
|
"deprecated-rule": "warn",
|
|
9507
9572
|
"empty-heading": "error",
|
|
@@ -9536,6 +9601,7 @@ const config$2 = {
|
|
|
9536
9601
|
|
|
9537
9602
|
const config$1 = {
|
|
9538
9603
|
rules: {
|
|
9604
|
+
"aria-hidden-body": "error",
|
|
9539
9605
|
"aria-label-misuse": "error",
|
|
9540
9606
|
"attr-case": "error",
|
|
9541
9607
|
"attr-delimiter": "error",
|
|
@@ -9705,7 +9771,9 @@ class ResolvedConfig {
|
|
|
9705
9771
|
* @returns A list of transformed sources ready for validation.
|
|
9706
9772
|
*/
|
|
9707
9773
|
transformFilename(filename) {
|
|
9708
|
-
const
|
|
9774
|
+
const stdin = 0;
|
|
9775
|
+
const src = filename !== "/dev/stdin" ? filename : stdin;
|
|
9776
|
+
const data = fs__default["default"].readFileSync(src, { encoding: "utf8" });
|
|
9709
9777
|
const source = {
|
|
9710
9778
|
data,
|
|
9711
9779
|
filename,
|