html-validate 6.2.0 → 6.4.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/es/core.d.ts CHANGED
@@ -502,6 +502,8 @@ interface ConditionalEvent extends Event {
502
502
  location: Location;
503
503
  /** Condition including markers. */
504
504
  condition: string;
505
+ /** The element containing the conditional, if any. */
506
+ parent: HtmlElement | null;
505
507
  }
506
508
  /**
507
509
  * Event emitted when html-validate directives `<!-- [html-validate-...] -->`
@@ -651,10 +653,17 @@ declare class Parser {
651
653
  */
652
654
  private getAttributeLocation;
653
655
  protected consumeDirective(token: Token): void;
656
+ /**
657
+ * Consumes conditional comment in tag form.
658
+ *
659
+ * See also the related [[consumeCommend]] method.
660
+ */
661
+ protected consumeConditional(token: Token): void;
654
662
  /**
655
663
  * Consumes comment token.
656
664
  *
657
- * Tries to find IE conditional comments and emits conditional token if found.
665
+ * Tries to find IE conditional comments and emits conditional token if
666
+ * found. See also the related [[consumeConditional]] method.
658
667
  */
659
668
  protected consumeComment(token: Token): void;
660
669
  /**
@@ -1575,7 +1584,8 @@ declare class MetaTable {
1575
1584
  */
1576
1585
  loadFromFile(filename: string): void;
1577
1586
  /**
1578
- * Get [[MetaElement]] for the given tag or null if the element doesn't exist.
1587
+ * Get [[MetaElement]] for the given tag. If no specific metadata is present
1588
+ * the global metadata is returned or null if no global is present.
1579
1589
  *
1580
1590
  * @public
1581
1591
  * @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 or null if the element doesn't exist.
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
- return this.elements[tagName] ? { ...this.elements[tagName] } : null;
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.
@@ -2936,7 +2945,7 @@ var TRANSFORMER_API;
2936
2945
  /** @public */
2937
2946
  const name = "html-validate";
2938
2947
  /** @public */
2939
- const version = "6.2.0";
2948
+ const version = "6.4.0";
2940
2949
  /** @public */
2941
2950
  const homepage = "https://html-validate.org";
2942
2951
  /** @public */
@@ -3216,7 +3225,8 @@ function ruleDocumentationUrl(filename) {
3216
3225
  const p = path.parse(filename);
3217
3226
  const root = path.join(distFolder, "rules");
3218
3227
  const rel = path.relative(root, path.join(p.dir, p.name));
3219
- return `${homepage}/rules/${rel}.html`;
3228
+ const normalized = rel.replace(/\\/g, "/");
3229
+ return `${homepage}/rules/${normalized}.html`;
3220
3230
  }
3221
3231
 
3222
3232
  const defaults$p = {
@@ -3406,6 +3416,28 @@ class AllowedLinks extends Rule {
3406
3416
  }
3407
3417
  }
3408
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
+
3409
3441
  const whitelisted = [
3410
3442
  "main",
3411
3443
  "nav",
@@ -5033,8 +5065,9 @@ class ElementRequiredAttributes extends Rule {
5033
5065
  class ElementRequiredContent extends Rule {
5034
5066
  documentation(context) {
5035
5067
  if (context) {
5068
+ const { element, missing } = context;
5036
5069
  return {
5037
- description: `The <${context.node} element requires a <${context.missing}> to be present as content.`,
5070
+ description: `The \`${element}\` element requires a \`${missing}\` to be present as content.`,
5038
5071
  url: ruleDocumentationUrl("@/rules/element-required-content.ts"),
5039
5072
  };
5040
5073
  }
@@ -5060,10 +5093,11 @@ class ElementRequiredContent extends Rule {
5060
5093
  }
5061
5094
  for (const missing of Validator.validateRequiredContent(node, rules)) {
5062
5095
  const context = {
5063
- node: node.tagName,
5064
- missing,
5096
+ element: node.annotatedName,
5097
+ missing: `<${missing}>`,
5065
5098
  };
5066
- this.report(node, `${node.annotatedName} element must have <${missing}> as content`, null, context);
5099
+ const message = `${node.annotatedName} element must have <${missing}> as content`;
5100
+ this.report(node, message, null, context);
5067
5101
  }
5068
5102
  });
5069
5103
  });
@@ -5146,7 +5180,14 @@ class EmptyHeading extends Rule {
5146
5180
  class EmptyTitle extends Rule {
5147
5181
  documentation() {
5148
5182
  return {
5149
- 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.`,
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"),
5150
5191
  url: ruleDocumentationUrl("@/rules/empty-title.ts"),
5151
5192
  };
5152
5193
  }
@@ -5162,7 +5203,10 @@ class EmptyTitle extends Rule {
5162
5203
  break;
5163
5204
  case TextClassification.EMPTY_TEXT:
5164
5205
  /* no content or whitespace only */
5165
- this.report(node, `<${node.tagName}> cannot be empty, must have text content`);
5206
+ {
5207
+ const message = `<${node.tagName}> cannot be empty, must have text content`;
5208
+ this.report(node, message, node.location);
5209
+ }
5166
5210
  break;
5167
5211
  }
5168
5212
  });
@@ -5943,7 +5987,7 @@ class NoConditionalComment extends Rule {
5943
5987
  }
5944
5988
  setup() {
5945
5989
  this.on("conditional", (event) => {
5946
- this.report(null, "Use of conditional comments are deprecated", event.location);
5990
+ this.report(event.parent, "Use of conditional comments are deprecated", event.location);
5947
5991
  });
5948
5992
  }
5949
5993
  }
@@ -9440,6 +9484,7 @@ const bundledRules$1 = {
9440
9484
 
9441
9485
  const bundledRules = {
9442
9486
  "allowed-links": AllowedLinks,
9487
+ "aria-hidden-body": AriaHiddenBody,
9443
9488
  "aria-label-misuse": AriaLabelMisuse,
9444
9489
  "attr-case": AttrCase,
9445
9490
  "attr-delimiter": AttrDelimiter,
@@ -9510,6 +9555,7 @@ var defaultConfig = {};
9510
9555
 
9511
9556
  const config$3 = {
9512
9557
  rules: {
9558
+ "aria-hidden-body": "error",
9513
9559
  "aria-label-misuse": "error",
9514
9560
  "deprecated-rule": "warn",
9515
9561
  "empty-heading": "error",
@@ -9544,6 +9590,7 @@ const config$2 = {
9544
9590
 
9545
9591
  const config$1 = {
9546
9592
  rules: {
9593
+ "aria-hidden-body": "error",
9547
9594
  "aria-label-misuse": "error",
9548
9595
  "attr-case": "error",
9549
9596
  "attr-delimiter": "error",
@@ -9713,7 +9760,9 @@ class ResolvedConfig {
9713
9760
  * @returns A list of transformed sources ready for validation.
9714
9761
  */
9715
9762
  transformFilename(filename) {
9716
- const data = fs.readFileSync(filename, { encoding: "utf8" });
9763
+ const stdin = 0;
9764
+ const src = filename !== "/dev/stdin" ? filename : stdin;
9765
+ const data = fs.readFileSync(src, { encoding: "utf8" });
9717
9766
  const source = {
9718
9767
  data,
9719
9768
  filename,
@@ -10435,10 +10484,7 @@ class Parser {
10435
10484
  this.consumeDirective(token);
10436
10485
  break;
10437
10486
  case TokenType.CONDITIONAL:
10438
- this.trigger("conditional", {
10439
- condition: token.data[1],
10440
- location: token.location,
10441
- });
10487
+ this.consumeConditional(token);
10442
10488
  break;
10443
10489
  case TokenType.COMMENT:
10444
10490
  this.consumeComment(token);
@@ -10743,17 +10789,33 @@ class Parser {
10743
10789
  location: token.location,
10744
10790
  });
10745
10791
  }
10792
+ /**
10793
+ * Consumes conditional comment in tag form.
10794
+ *
10795
+ * See also the related [[consumeCommend]] method.
10796
+ */
10797
+ consumeConditional(token) {
10798
+ const element = this.dom.getActive();
10799
+ this.trigger("conditional", {
10800
+ condition: token.data[1],
10801
+ location: token.location,
10802
+ parent: element,
10803
+ });
10804
+ }
10746
10805
  /**
10747
10806
  * Consumes comment token.
10748
10807
  *
10749
- * Tries to find IE conditional comments and emits conditional token if found.
10808
+ * Tries to find IE conditional comments and emits conditional token if
10809
+ * found. See also the related [[consumeConditional]] method.
10750
10810
  */
10751
10811
  consumeComment(token) {
10752
10812
  const comment = token.data[0];
10813
+ const element = this.dom.getActive();
10753
10814
  for (const conditional of parseConditionalComment(comment, token.location)) {
10754
10815
  this.trigger("conditional", {
10755
10816
  condition: conditional.expression,
10756
10817
  location: conditional.location,
10818
+ parent: element,
10757
10819
  });
10758
10820
  }
10759
10821
  }