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.
@@ -1575,7 +1575,8 @@ declare class MetaTable {
1575
1575
  */
1576
1576
  loadFromFile(filename: string): void;
1577
1577
  /**
1578
- * Get [[MetaElement]] for the given tag or null if the element doesn't exist.
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 or null if the element doesn't exist.
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
- return this.elements[tagName] ? { ...this.elements[tagName] } : null;
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.1.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
- return `${homepage}/rules/${rel}.html`;
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
- if (context.allowed.enum) {
4089
- const allowed = context.allowed.enum.map((val) => `- \`${val}\``);
4090
- docs.description = `Element <${context.element}> does not allow attribute \`${context.attribute}\` to have the value \`"${context.value}"\`, it must match one of the following:\n\n${allowed.join("\n")}`;
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 (context.allowed.boolean) {
4093
- docs.description = `Element <${context.element}> attribute \`${context.attribute}\` must be a boolean attribute, e.g. \`<${context.element} ${context.attribute}>\``;
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 <${context.node} element requires a <${context.missing}> to be present as content.`,
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
- node: node.tagName,
5053
- missing,
5107
+ element: node.annotatedName,
5108
+ missing: `<${missing}>`,
5054
5109
  };
5055
- this.report(node, `${node.annotatedName} element must have <${missing}> as content`, null, context);
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: `The <title> element is used to describe the document and is shown in the browser tab and titlebar. WCAG and SEO requires a descriptive title and preferably unique within the site. Whitespace only is considered empty.`,
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
- this.report(node, `<${node.tagName}> cannot be empty, must have text content`);
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 data = fs__default["default"].readFileSync(filename, { encoding: "utf8" });
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,