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.
@@ -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/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.
@@ -2947,7 +2956,7 @@ var TRANSFORMER_API;
2947
2956
  /** @public */
2948
2957
  const name = "html-validate";
2949
2958
  /** @public */
2950
- const version = "6.2.0";
2959
+ const version = "6.4.0";
2951
2960
  /** @public */
2952
2961
  const homepage = "https://html-validate.org";
2953
2962
  /** @public */
@@ -3227,7 +3236,8 @@ function ruleDocumentationUrl(filename) {
3227
3236
  const p = path__default["default"].parse(filename);
3228
3237
  const root = path__default["default"].join(distFolder, "rules");
3229
3238
  const rel = path__default["default"].relative(root, path__default["default"].join(p.dir, p.name));
3230
- return `${homepage}/rules/${rel}.html`;
3239
+ const normalized = rel.replace(/\\/g, "/");
3240
+ return `${homepage}/rules/${normalized}.html`;
3231
3241
  }
3232
3242
 
3233
3243
  const defaults$p = {
@@ -3417,6 +3427,28 @@ class AllowedLinks extends Rule {
3417
3427
  }
3418
3428
  }
3419
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
+
3420
3452
  const whitelisted = [
3421
3453
  "main",
3422
3454
  "nav",
@@ -5044,8 +5076,9 @@ class ElementRequiredAttributes extends Rule {
5044
5076
  class ElementRequiredContent extends Rule {
5045
5077
  documentation(context) {
5046
5078
  if (context) {
5079
+ const { element, missing } = context;
5047
5080
  return {
5048
- 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.`,
5049
5082
  url: ruleDocumentationUrl("@/rules/element-required-content.ts"),
5050
5083
  };
5051
5084
  }
@@ -5071,10 +5104,11 @@ class ElementRequiredContent extends Rule {
5071
5104
  }
5072
5105
  for (const missing of Validator.validateRequiredContent(node, rules)) {
5073
5106
  const context = {
5074
- node: node.tagName,
5075
- missing,
5107
+ element: node.annotatedName,
5108
+ missing: `<${missing}>`,
5076
5109
  };
5077
- 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);
5078
5112
  }
5079
5113
  });
5080
5114
  });
@@ -5157,7 +5191,14 @@ class EmptyHeading extends Rule {
5157
5191
  class EmptyTitle extends Rule {
5158
5192
  documentation() {
5159
5193
  return {
5160
- 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"),
5161
5202
  url: ruleDocumentationUrl("@/rules/empty-title.ts"),
5162
5203
  };
5163
5204
  }
@@ -5173,7 +5214,10 @@ class EmptyTitle extends Rule {
5173
5214
  break;
5174
5215
  case TextClassification.EMPTY_TEXT:
5175
5216
  /* no content or whitespace only */
5176
- 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
+ }
5177
5221
  break;
5178
5222
  }
5179
5223
  });
@@ -5954,7 +5998,7 @@ class NoConditionalComment extends Rule {
5954
5998
  }
5955
5999
  setup() {
5956
6000
  this.on("conditional", (event) => {
5957
- this.report(null, "Use of conditional comments are deprecated", event.location);
6001
+ this.report(event.parent, "Use of conditional comments are deprecated", event.location);
5958
6002
  });
5959
6003
  }
5960
6004
  }
@@ -9451,6 +9495,7 @@ const bundledRules$1 = {
9451
9495
 
9452
9496
  const bundledRules = {
9453
9497
  "allowed-links": AllowedLinks,
9498
+ "aria-hidden-body": AriaHiddenBody,
9454
9499
  "aria-label-misuse": AriaLabelMisuse,
9455
9500
  "attr-case": AttrCase,
9456
9501
  "attr-delimiter": AttrDelimiter,
@@ -9521,6 +9566,7 @@ var defaultConfig = {};
9521
9566
 
9522
9567
  const config$3 = {
9523
9568
  rules: {
9569
+ "aria-hidden-body": "error",
9524
9570
  "aria-label-misuse": "error",
9525
9571
  "deprecated-rule": "warn",
9526
9572
  "empty-heading": "error",
@@ -9555,6 +9601,7 @@ const config$2 = {
9555
9601
 
9556
9602
  const config$1 = {
9557
9603
  rules: {
9604
+ "aria-hidden-body": "error",
9558
9605
  "aria-label-misuse": "error",
9559
9606
  "attr-case": "error",
9560
9607
  "attr-delimiter": "error",
@@ -9724,7 +9771,9 @@ class ResolvedConfig {
9724
9771
  * @returns A list of transformed sources ready for validation.
9725
9772
  */
9726
9773
  transformFilename(filename) {
9727
- 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" });
9728
9777
  const source = {
9729
9778
  data,
9730
9779
  filename,
@@ -10446,10 +10495,7 @@ class Parser {
10446
10495
  this.consumeDirective(token);
10447
10496
  break;
10448
10497
  case exports.TokenType.CONDITIONAL:
10449
- this.trigger("conditional", {
10450
- condition: token.data[1],
10451
- location: token.location,
10452
- });
10498
+ this.consumeConditional(token);
10453
10499
  break;
10454
10500
  case exports.TokenType.COMMENT:
10455
10501
  this.consumeComment(token);
@@ -10754,17 +10800,33 @@ class Parser {
10754
10800
  location: token.location,
10755
10801
  });
10756
10802
  }
10803
+ /**
10804
+ * Consumes conditional comment in tag form.
10805
+ *
10806
+ * See also the related [[consumeCommend]] method.
10807
+ */
10808
+ consumeConditional(token) {
10809
+ const element = this.dom.getActive();
10810
+ this.trigger("conditional", {
10811
+ condition: token.data[1],
10812
+ location: token.location,
10813
+ parent: element,
10814
+ });
10815
+ }
10757
10816
  /**
10758
10817
  * Consumes comment token.
10759
10818
  *
10760
- * Tries to find IE conditional comments and emits conditional token if found.
10819
+ * Tries to find IE conditional comments and emits conditional token if
10820
+ * found. See also the related [[consumeConditional]] method.
10761
10821
  */
10762
10822
  consumeComment(token) {
10763
10823
  const comment = token.data[0];
10824
+ const element = this.dom.getActive();
10764
10825
  for (const conditional of parseConditionalComment(comment, token.location)) {
10765
10826
  this.trigger("conditional", {
10766
10827
  condition: conditional.expression,
10767
10828
  location: conditional.location,
10829
+ parent: element,
10768
10830
  });
10769
10831
  }
10770
10832
  }