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/es/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/es/core.js
CHANGED
|
@@ -933,14 +933,23 @@ class MetaTable {
|
|
|
933
933
|
}
|
|
934
934
|
}
|
|
935
935
|
/**
|
|
936
|
-
* Get [[MetaElement]] for the given tag
|
|
936
|
+
* Get [[MetaElement]] for the given tag. If no specific metadata is present
|
|
937
|
+
* the global metadata is returned or null if no global is present.
|
|
937
938
|
*
|
|
938
939
|
* @public
|
|
939
940
|
* @returns A shallow copy of metadata.
|
|
940
941
|
*/
|
|
941
942
|
getMetaFor(tagName) {
|
|
943
|
+
/* try to locate by tagname */
|
|
942
944
|
tagName = tagName.toLowerCase();
|
|
943
|
-
|
|
945
|
+
if (this.elements[tagName]) {
|
|
946
|
+
return { ...this.elements[tagName] };
|
|
947
|
+
}
|
|
948
|
+
/* try to locate global element */
|
|
949
|
+
if (this.elements["*"]) {
|
|
950
|
+
return { ...this.elements["*"] };
|
|
951
|
+
}
|
|
952
|
+
return null;
|
|
944
953
|
}
|
|
945
954
|
/**
|
|
946
955
|
* Find all tags which has enabled given property.
|
|
@@ -1772,6 +1781,14 @@ class Selector {
|
|
|
1772
1781
|
}
|
|
1773
1782
|
|
|
1774
1783
|
const TEXT_NODE_NAME = "#text";
|
|
1784
|
+
/**
|
|
1785
|
+
* Returns true if the node is a text node.
|
|
1786
|
+
*
|
|
1787
|
+
* @public
|
|
1788
|
+
*/
|
|
1789
|
+
function isTextNode(node) {
|
|
1790
|
+
return Boolean(node && node.nodeType === NodeType.TEXT_NODE);
|
|
1791
|
+
}
|
|
1775
1792
|
/**
|
|
1776
1793
|
* Represents a text in the HTML document.
|
|
1777
1794
|
*
|
|
@@ -2928,7 +2945,7 @@ var TRANSFORMER_API;
|
|
|
2928
2945
|
/** @public */
|
|
2929
2946
|
const name = "html-validate";
|
|
2930
2947
|
/** @public */
|
|
2931
|
-
const version = "6.
|
|
2948
|
+
const version = "6.3.2";
|
|
2932
2949
|
/** @public */
|
|
2933
2950
|
const homepage = "https://html-validate.org";
|
|
2934
2951
|
/** @public */
|
|
@@ -3208,7 +3225,8 @@ function ruleDocumentationUrl(filename) {
|
|
|
3208
3225
|
const p = path.parse(filename);
|
|
3209
3226
|
const root = path.join(distFolder, "rules");
|
|
3210
3227
|
const rel = path.relative(root, path.join(p.dir, p.name));
|
|
3211
|
-
|
|
3228
|
+
const normalized = rel.replace(/\\/g, "/");
|
|
3229
|
+
return `${homepage}/rules/${normalized}.html`;
|
|
3212
3230
|
}
|
|
3213
3231
|
|
|
3214
3232
|
const defaults$p = {
|
|
@@ -3398,6 +3416,28 @@ class AllowedLinks extends Rule {
|
|
|
3398
3416
|
}
|
|
3399
3417
|
}
|
|
3400
3418
|
|
|
3419
|
+
class AriaHiddenBody extends Rule {
|
|
3420
|
+
documentation() {
|
|
3421
|
+
return {
|
|
3422
|
+
description: "`aria-hidden` must not be used on the `<body>` element as it makes the page inaccessible to assistive technology such as screenreaders",
|
|
3423
|
+
url: ruleDocumentationUrl("@/rules/aria-hidden-body.ts"),
|
|
3424
|
+
};
|
|
3425
|
+
}
|
|
3426
|
+
setup() {
|
|
3427
|
+
this.on("tag:ready", this.isRelevant, (event) => {
|
|
3428
|
+
const { target } = event;
|
|
3429
|
+
const attr = target.getAttribute("aria-hidden");
|
|
3430
|
+
if (!attr || !attr.valueMatches("true", true)) {
|
|
3431
|
+
return;
|
|
3432
|
+
}
|
|
3433
|
+
this.report(target, "aria-hidden must not be used on <body>", attr.keyLocation);
|
|
3434
|
+
});
|
|
3435
|
+
}
|
|
3436
|
+
isRelevant(event) {
|
|
3437
|
+
return event.target.is("body");
|
|
3438
|
+
}
|
|
3439
|
+
}
|
|
3440
|
+
|
|
3401
3441
|
const whitelisted = [
|
|
3402
3442
|
"main",
|
|
3403
3443
|
"nav",
|
|
@@ -4074,12 +4114,26 @@ class AttributeAllowedValues extends Rule {
|
|
|
4074
4114
|
if (!context) {
|
|
4075
4115
|
return docs;
|
|
4076
4116
|
}
|
|
4077
|
-
|
|
4078
|
-
|
|
4079
|
-
|
|
4117
|
+
const { allowed, attribute, element, value } = context;
|
|
4118
|
+
if (allowed.enum) {
|
|
4119
|
+
const allowedList = allowed.enum.map((value) => {
|
|
4120
|
+
if (typeof value === "string") {
|
|
4121
|
+
return `- \`"${value}"\``;
|
|
4122
|
+
}
|
|
4123
|
+
else {
|
|
4124
|
+
return `- \`${value.toString()}\``;
|
|
4125
|
+
}
|
|
4126
|
+
});
|
|
4127
|
+
docs.description = [
|
|
4128
|
+
`The \`<${element}>\` element does not allow the attribute \`${attribute}\` to have the value \`"${value}"\`.`,
|
|
4129
|
+
"",
|
|
4130
|
+
"It must match one of the following:",
|
|
4131
|
+
"",
|
|
4132
|
+
...allowedList,
|
|
4133
|
+
].join("\n");
|
|
4080
4134
|
}
|
|
4081
|
-
else if (
|
|
4082
|
-
docs.description = `
|
|
4135
|
+
else if (allowed.boolean) {
|
|
4136
|
+
docs.description = `The \`<${context.element}>\` attribute \`${context.attribute}\` must be a boolean attribute, e.g. \`<${context.element} ${context.attribute}>\``;
|
|
4083
4137
|
}
|
|
4084
4138
|
return docs;
|
|
4085
4139
|
}
|
|
@@ -5011,8 +5065,9 @@ class ElementRequiredAttributes extends Rule {
|
|
|
5011
5065
|
class ElementRequiredContent extends Rule {
|
|
5012
5066
|
documentation(context) {
|
|
5013
5067
|
if (context) {
|
|
5068
|
+
const { element, missing } = context;
|
|
5014
5069
|
return {
|
|
5015
|
-
description: `The
|
|
5070
|
+
description: `The \`${element}\` element requires a \`${missing}\` to be present as content.`,
|
|
5016
5071
|
url: ruleDocumentationUrl("@/rules/element-required-content.ts"),
|
|
5017
5072
|
};
|
|
5018
5073
|
}
|
|
@@ -5038,10 +5093,11 @@ class ElementRequiredContent extends Rule {
|
|
|
5038
5093
|
}
|
|
5039
5094
|
for (const missing of Validator.validateRequiredContent(node, rules)) {
|
|
5040
5095
|
const context = {
|
|
5041
|
-
|
|
5042
|
-
missing
|
|
5096
|
+
element: node.annotatedName,
|
|
5097
|
+
missing: `<${missing}>`,
|
|
5043
5098
|
};
|
|
5044
|
-
|
|
5099
|
+
const message = `${node.annotatedName} element must have <${missing}> as content`;
|
|
5100
|
+
this.report(node, message, null, context);
|
|
5045
5101
|
}
|
|
5046
5102
|
});
|
|
5047
5103
|
});
|
|
@@ -5124,7 +5180,14 @@ class EmptyHeading extends Rule {
|
|
|
5124
5180
|
class EmptyTitle extends Rule {
|
|
5125
5181
|
documentation() {
|
|
5126
5182
|
return {
|
|
5127
|
-
description:
|
|
5183
|
+
description: [
|
|
5184
|
+
"The `<title>` element cannot be empty, it must have textual content.",
|
|
5185
|
+
"",
|
|
5186
|
+
"It is used to describe the document and is shown in the browser tab and titlebar.",
|
|
5187
|
+
"WCAG and SEO requires a descriptive title and preferably unique within the site.",
|
|
5188
|
+
"",
|
|
5189
|
+
"Whitespace is ignored.",
|
|
5190
|
+
].join("\n"),
|
|
5128
5191
|
url: ruleDocumentationUrl("@/rules/empty-title.ts"),
|
|
5129
5192
|
};
|
|
5130
5193
|
}
|
|
@@ -5140,7 +5203,10 @@ class EmptyTitle extends Rule {
|
|
|
5140
5203
|
break;
|
|
5141
5204
|
case TextClassification.EMPTY_TEXT:
|
|
5142
5205
|
/* no content or whitespace only */
|
|
5143
|
-
|
|
5206
|
+
{
|
|
5207
|
+
const message = `<${node.tagName}> cannot be empty, must have text content`;
|
|
5208
|
+
this.report(node, message, node.location);
|
|
5209
|
+
}
|
|
5144
5210
|
break;
|
|
5145
5211
|
}
|
|
5146
5212
|
});
|
|
@@ -7056,9 +7122,6 @@ function hasDefaultText(node) {
|
|
|
7056
7122
|
const type = node.getAttribute("type");
|
|
7057
7123
|
return Boolean(type && type.valueMatches(/submit|reset/, false));
|
|
7058
7124
|
}
|
|
7059
|
-
function isTextNode(node) {
|
|
7060
|
-
return node.nodeType === NodeType.TEXT_NODE;
|
|
7061
|
-
}
|
|
7062
7125
|
function isNonEmptyText(node) {
|
|
7063
7126
|
if (isTextNode(node)) {
|
|
7064
7127
|
return node.isDynamic || node.textContent.trim() !== "";
|
|
@@ -9421,6 +9484,7 @@ const bundledRules$1 = {
|
|
|
9421
9484
|
|
|
9422
9485
|
const bundledRules = {
|
|
9423
9486
|
"allowed-links": AllowedLinks,
|
|
9487
|
+
"aria-hidden-body": AriaHiddenBody,
|
|
9424
9488
|
"aria-label-misuse": AriaLabelMisuse,
|
|
9425
9489
|
"attr-case": AttrCase,
|
|
9426
9490
|
"attr-delimiter": AttrDelimiter,
|
|
@@ -9491,6 +9555,7 @@ var defaultConfig = {};
|
|
|
9491
9555
|
|
|
9492
9556
|
const config$3 = {
|
|
9493
9557
|
rules: {
|
|
9558
|
+
"aria-hidden-body": "error",
|
|
9494
9559
|
"aria-label-misuse": "error",
|
|
9495
9560
|
"deprecated-rule": "warn",
|
|
9496
9561
|
"empty-heading": "error",
|
|
@@ -9525,6 +9590,7 @@ const config$2 = {
|
|
|
9525
9590
|
|
|
9526
9591
|
const config$1 = {
|
|
9527
9592
|
rules: {
|
|
9593
|
+
"aria-hidden-body": "error",
|
|
9528
9594
|
"aria-label-misuse": "error",
|
|
9529
9595
|
"attr-case": "error",
|
|
9530
9596
|
"attr-delimiter": "error",
|
|
@@ -9694,7 +9760,9 @@ class ResolvedConfig {
|
|
|
9694
9760
|
* @returns A list of transformed sources ready for validation.
|
|
9695
9761
|
*/
|
|
9696
9762
|
transformFilename(filename) {
|
|
9697
|
-
const
|
|
9763
|
+
const stdin = 0;
|
|
9764
|
+
const src = filename !== "/dev/stdin" ? filename : stdin;
|
|
9765
|
+
const data = fs.readFileSync(src, { encoding: "utf8" });
|
|
9698
9766
|
const source = {
|
|
9699
9767
|
data,
|
|
9700
9768
|
filename,
|