html-validate 7.10.1 → 7.11.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.
Files changed (45) hide show
  1. package/dist/cjs/browser.d.ts +1 -1
  2. package/dist/cjs/browser.js +2 -1
  3. package/dist/cjs/browser.js.map +1 -1
  4. package/dist/cjs/cli.js +0 -1
  5. package/dist/cjs/cli.js.map +1 -1
  6. package/dist/cjs/core.d.ts +1 -4
  7. package/dist/cjs/core.js +149 -106
  8. package/dist/cjs/core.js.map +1 -1
  9. package/dist/cjs/elements.js +5 -1
  10. package/dist/cjs/elements.js.map +1 -1
  11. package/dist/cjs/html-validate.js +1 -1
  12. package/dist/cjs/index.d.ts +1 -1
  13. package/dist/cjs/index.js +2 -1
  14. package/dist/cjs/index.js.map +1 -1
  15. package/dist/cjs/jest-lib.js +0 -1
  16. package/dist/cjs/jest-lib.js.map +1 -1
  17. package/dist/cjs/jest.d.ts +1 -0
  18. package/dist/cjs/jest.js +2 -2
  19. package/dist/cjs/meta-helper.d.ts +1 -0
  20. package/dist/cjs/rules-helper.d.ts +10 -1
  21. package/dist/cjs/rules-helper.js +54 -1
  22. package/dist/cjs/rules-helper.js.map +1 -1
  23. package/dist/cjs/test-utils.d.ts +1 -0
  24. package/dist/es/browser.d.ts +1 -1
  25. package/dist/es/browser.js +2 -2
  26. package/dist/es/cli.js +0 -1
  27. package/dist/es/cli.js.map +1 -1
  28. package/dist/es/core.d.ts +1 -4
  29. package/dist/es/core.js +150 -107
  30. package/dist/es/core.js.map +1 -1
  31. package/dist/es/elements.js +5 -1
  32. package/dist/es/elements.js.map +1 -1
  33. package/dist/es/html-validate.js +2 -2
  34. package/dist/es/index.d.ts +1 -1
  35. package/dist/es/index.js +2 -2
  36. package/dist/es/jest-lib.js +0 -1
  37. package/dist/es/jest-lib.js.map +1 -1
  38. package/dist/es/jest.d.ts +1 -0
  39. package/dist/es/jest.js +2 -2
  40. package/dist/es/meta-helper.d.ts +1 -0
  41. package/dist/es/rules-helper.d.ts +10 -1
  42. package/dist/es/rules-helper.js +53 -2
  43. package/dist/es/rules-helper.js.map +1 -1
  44. package/dist/es/test-utils.d.ts +1 -0
  45. package/package.json +17 -17
package/dist/es/core.js CHANGED
@@ -1,14 +1,14 @@
1
1
  import fs from 'fs';
2
2
  import betterAjvErrors from '@sidvind/better-ajv-errors';
3
- import { C as CaseStyle, n as naturalJoin, c as classifyNodeText, T as TextClassification, h as hasAltText, i as isHTMLHidden, a as isAriaHidden, b as hasAccessibleName, d as inAccessibilityTree, e as hasAriaLabel } from './rules-helper.js';
3
+ import { i as isKeywordIgnored, C as CaseStyle, n as naturalJoin, c as classifyNodeText, T as TextClassification, h as hasAltText, a as isHTMLHidden, b as isAriaHidden, d as hasAccessibleName, k as keywordPatternMatcher, e as inAccessibilityTree, f as hasAriaLabel } from './rules-helper.js';
4
4
  import Ajv from 'ajv';
5
5
  import deepmerge from 'deepmerge';
6
6
  import * as espree from 'espree';
7
7
  import * as walk from 'acorn-walk';
8
- import { e as entities$1, h as html5, b as bundledElements } from './elements.js';
9
8
  import path from 'path';
10
9
  import semver from 'semver';
11
10
  import kleur from 'kleur';
11
+ import { e as entities$1, h as html5, b as bundledElements } from './elements.js';
12
12
  import { createRequire } from 'module';
13
13
  import { codeFrameColumns } from '@babel/code-frame';
14
14
  import stylishImpl from '@html-validate/stylish';
@@ -2614,29 +2614,40 @@ class Validator {
2614
2614
  *
2615
2615
  * For instance, a `<table>` element can only contain a single `<tbody>`
2616
2616
  * child. If multiple `<tbody>` exists this test will fail both nodes.
2617
+ * Note that this is called on the parent but will fail the children violating
2618
+ * the rule.
2617
2619
  *
2618
- * @param node - Element to test.
2619
- * @param rules - List of rules.
2620
- * @param numSiblings - How many siblings of the same type as the element
2621
- * exists (including the element itself)
2622
- * @returns `true` if the element passes the test.
2620
+ * @param children - Array of children to validate.
2621
+ * @param rules - List of rules of the parent element.
2622
+ * @returns `true` if the parent element of the children passes the test.
2623
2623
  */
2624
- static validateOccurrences(node, rules, numSiblings) {
2624
+ static validateOccurrences(children, rules, cb) {
2625
2625
  if (!rules) {
2626
2626
  return true;
2627
2627
  }
2628
- const category = rules.find((cur) => {
2628
+ let valid = true;
2629
+ for (const rule of rules) {
2629
2630
  /** @todo handle complex rules and not just plain arrays (but as of now
2630
2631
  * there is no use-case for it) */
2631
2632
  // istanbul ignore next
2632
- if (typeof cur !== "string") {
2633
+ if (typeof rule !== "string") {
2633
2634
  return false;
2634
2635
  }
2635
- const match = cur.match(/^(.*?)[?*]?$/);
2636
- return match && match[1] === node.tagName;
2637
- });
2638
- const limit = parseAmountQualifier(category);
2639
- return limit === null || numSiblings <= limit;
2636
+ // Check if the rule has a quantifier
2637
+ const [, category, quantifier] = rule.match(/^(@?.*?)([?*]?)$/);
2638
+ const limit = category && quantifier && parseQuantifier(quantifier);
2639
+ if (limit) {
2640
+ const siblings = children.filter((cur) => Validator.validatePermittedCategory(cur, rule, true));
2641
+ if (siblings.length > limit) {
2642
+ // fail only the children above the limit (currently limit can only be 1)
2643
+ for (const child of siblings.slice(limit)) {
2644
+ cb(child, category);
2645
+ }
2646
+ valid = false;
2647
+ }
2648
+ }
2649
+ }
2650
+ return valid;
2640
2651
  }
2641
2652
  /**
2642
2653
  * Validate elements order.
@@ -2698,14 +2709,15 @@ class Validator {
2698
2709
  * Check if an element has the required set of elements. At least one of the
2699
2710
  * selectors must match.
2700
2711
  *
2701
- * Returns [] when valid or a list of tagNames missing as content.
2712
+ * Returns `[]` when valid or a list of required but missing tagnames or
2713
+ * categories.
2702
2714
  */
2703
2715
  static validateRequiredContent(node, rules) {
2704
2716
  if (!rules || rules.length === 0) {
2705
2717
  return [];
2706
2718
  }
2707
2719
  return rules.filter((tagName) => {
2708
- const haveMatchingChild = node.childElements.some((child) => child.is(tagName));
2720
+ const haveMatchingChild = node.childElements.some((child) => Validator.validatePermittedCategory(child, tagName, false));
2709
2721
  return !haveMatchingChild;
2710
2722
  });
2711
2723
  }
@@ -2809,16 +2821,16 @@ class Validator {
2809
2821
  */
2810
2822
  // eslint-disable-next-line complexity
2811
2823
  static validatePermittedCategory(node, category, defaultMatch) {
2824
+ const [, rawCategory] = category.match(/^(@?.*?)([?*]?)$/);
2812
2825
  /* match tagName when an explicit name is given */
2813
- if (category[0] !== "@") {
2814
- const [, tagName] = category.match(/^(.*?)[?*]?$/);
2815
- return node.tagName === tagName;
2826
+ if (rawCategory[0] !== "@") {
2827
+ return node.tagName === rawCategory;
2816
2828
  }
2817
2829
  /* if the meta entry is missing assume any content model would match */
2818
2830
  if (!node.meta) {
2819
2831
  return defaultMatch;
2820
2832
  }
2821
- switch (category) {
2833
+ switch (rawCategory) {
2822
2834
  case "@meta":
2823
2835
  return node.meta.metadata;
2824
2836
  case "@flow":
@@ -2850,23 +2862,15 @@ function validateKeys(rule) {
2850
2862
  }
2851
2863
  }
2852
2864
  }
2853
- function parseAmountQualifier(category) {
2854
- if (!category) {
2855
- /* content not allowed, catched by another rule so just assume unlimited
2856
- * usage for this purpose */
2857
- return null;
2858
- }
2859
- const [, qualifier] = category.match(/^.*?([?*]?)$/);
2860
- switch (qualifier) {
2865
+ function parseQuantifier(quantifier) {
2866
+ switch (quantifier) {
2861
2867
  case "?":
2862
2868
  return 1;
2863
- case "":
2864
- return null;
2865
2869
  case "*":
2866
2870
  return null;
2867
- /* istanbul ignore next */
2871
+ // istanbul ignore next
2868
2872
  default:
2869
- throw new Error(`Invalid amount qualifier "${qualifier}" used`);
2873
+ throw new Error(`Invalid quantifier "${quantifier}" used`);
2870
2874
  }
2871
2875
  }
2872
2876
 
@@ -3398,16 +3402,7 @@ class Rule {
3398
3402
  * `exclude`.
3399
3403
  */
3400
3404
  isKeywordIgnored(keyword, matcher = (list, it) => list.includes(it)) {
3401
- const { include, exclude } = this.options;
3402
- /* ignore keyword if not present in "include" */
3403
- if (include && !matcher(include, keyword)) {
3404
- return true;
3405
- }
3406
- /* ignore keyword if present in "excludes" */
3407
- if (exclude && matcher(exclude, keyword)) {
3408
- return true;
3409
- }
3410
- return false;
3405
+ return isKeywordIgnored(this.options, keyword, matcher);
3411
3406
  }
3412
3407
  /**
3413
3408
  * Find all tags which has enabled given property.
@@ -3533,7 +3528,7 @@ class Rule {
3533
3528
  }
3534
3529
  }
3535
3530
 
3536
- const defaults$u = {
3531
+ const defaults$v = {
3537
3532
  allowExternal: true,
3538
3533
  allowRelative: true,
3539
3534
  allowAbsolute: true,
@@ -3577,7 +3572,7 @@ function matchList(value, list) {
3577
3572
  }
3578
3573
  class AllowedLinks extends Rule {
3579
3574
  constructor(options) {
3580
- super({ ...defaults$u, ...options });
3575
+ super({ ...defaults$v, ...options });
3581
3576
  this.allowExternal = parseAllow(this.options.allowExternal);
3582
3577
  this.allowRelative = parseAllow(this.options.allowRelative);
3583
3578
  this.allowAbsolute = parseAllow(this.options.allowAbsolute);
@@ -3725,7 +3720,7 @@ var RuleContext$1;
3725
3720
  RuleContext["MISSING_ALT"] = "missing-alt";
3726
3721
  RuleContext["MISSING_HREF"] = "missing-href";
3727
3722
  })(RuleContext$1 || (RuleContext$1 = {}));
3728
- const defaults$t = {
3723
+ const defaults$u = {
3729
3724
  accessible: true,
3730
3725
  };
3731
3726
  function findByTarget(target, siblings) {
@@ -3763,7 +3758,7 @@ function getDescription$1(context) {
3763
3758
  }
3764
3759
  class AreaAlt extends Rule {
3765
3760
  constructor(options) {
3766
- super({ ...defaults$t, ...options });
3761
+ super({ ...defaults$u, ...options });
3767
3762
  }
3768
3763
  static schema() {
3769
3764
  return {
@@ -3927,13 +3922,13 @@ class AriaLabelMisuse extends Rule {
3927
3922
  class ConfigError extends UserError {
3928
3923
  }
3929
3924
 
3930
- const defaults$s = {
3925
+ const defaults$t = {
3931
3926
  style: "lowercase",
3932
3927
  ignoreForeign: true,
3933
3928
  };
3934
3929
  class AttrCase extends Rule {
3935
3930
  constructor(options) {
3936
- super({ ...defaults$s, ...options });
3931
+ super({ ...defaults$t, ...options });
3937
3932
  this.style = new CaseStyle(this.options.style, "attr-case");
3938
3933
  }
3939
3934
  static schema() {
@@ -4278,7 +4273,7 @@ class AttrDelimiter extends Rule {
4278
4273
  }
4279
4274
 
4280
4275
  const DEFAULT_PATTERN = "[a-z0-9-:]+";
4281
- const defaults$r = {
4276
+ const defaults$s = {
4282
4277
  pattern: DEFAULT_PATTERN,
4283
4278
  ignoreForeign: true,
4284
4279
  };
@@ -4315,7 +4310,7 @@ function generateDescription(name, pattern) {
4315
4310
  }
4316
4311
  class AttrPattern extends Rule {
4317
4312
  constructor(options) {
4318
- super({ ...defaults$r, ...options });
4313
+ super({ ...defaults$s, ...options });
4319
4314
  this.pattern = generateRegexp(this.options.pattern);
4320
4315
  }
4321
4316
  static schema() {
@@ -4376,7 +4371,7 @@ var QuoteStyle;
4376
4371
  QuoteStyle["AUTO_QUOTE"] = "auto";
4377
4372
  QuoteStyle["ANY_QUOTE"] = "any";
4378
4373
  })(QuoteStyle || (QuoteStyle = {}));
4379
- const defaults$q = {
4374
+ const defaults$r = {
4380
4375
  style: "auto",
4381
4376
  unquoted: false,
4382
4377
  };
@@ -4443,7 +4438,7 @@ class AttrQuotes extends Rule {
4443
4438
  };
4444
4439
  }
4445
4440
  constructor(options) {
4446
- super({ ...defaults$q, ...options });
4441
+ super({ ...defaults$r, ...options });
4447
4442
  this.style = parseStyle$4(this.options.style);
4448
4443
  }
4449
4444
  setup() {
@@ -4613,12 +4608,12 @@ class AttributeAllowedValues extends Rule {
4613
4608
  }
4614
4609
  }
4615
4610
 
4616
- const defaults$p = {
4611
+ const defaults$q = {
4617
4612
  style: "omit",
4618
4613
  };
4619
4614
  class AttributeBooleanStyle extends Rule {
4620
4615
  constructor(options) {
4621
- super({ ...defaults$p, ...options });
4616
+ super({ ...defaults$q, ...options });
4622
4617
  this.hasInvalidStyle = parseStyle$3(this.options.style);
4623
4618
  }
4624
4619
  static schema() {
@@ -4694,12 +4689,12 @@ function reportMessage$1(attr, style) {
4694
4689
  return "";
4695
4690
  }
4696
4691
 
4697
- const defaults$o = {
4692
+ const defaults$p = {
4698
4693
  style: "omit",
4699
4694
  };
4700
4695
  class AttributeEmptyStyle extends Rule {
4701
4696
  constructor(options) {
4702
- super({ ...defaults$o, ...options });
4697
+ super({ ...defaults$p, ...options });
4703
4698
  this.hasInvalidStyle = parseStyle$2(this.options.style);
4704
4699
  }
4705
4700
  static schema() {
@@ -4855,12 +4850,12 @@ function describePattern(pattern) {
4855
4850
  }
4856
4851
  }
4857
4852
 
4858
- const defaults$n = {
4853
+ const defaults$o = {
4859
4854
  pattern: "kebabcase",
4860
4855
  };
4861
4856
  class ClassPattern extends Rule {
4862
4857
  constructor(options) {
4863
- super({ ...defaults$n, ...options });
4858
+ super({ ...defaults$o, ...options });
4864
4859
  this.pattern = parsePattern(this.options.pattern);
4865
4860
  }
4866
4861
  static schema() {
@@ -4969,13 +4964,13 @@ class CloseOrder extends Rule {
4969
4964
  }
4970
4965
  }
4971
4966
 
4972
- const defaults$m = {
4967
+ const defaults$n = {
4973
4968
  include: null,
4974
4969
  exclude: null,
4975
4970
  };
4976
4971
  class Deprecated extends Rule {
4977
4972
  constructor(options) {
4978
- super({ ...defaults$m, ...options });
4973
+ super({ ...defaults$n, ...options });
4979
4974
  }
4980
4975
  static schema() {
4981
4976
  return {
@@ -5138,12 +5133,12 @@ let NoStyleTag$1 = class NoStyleTag extends Rule {
5138
5133
  }
5139
5134
  };
5140
5135
 
5141
- const defaults$l = {
5136
+ const defaults$m = {
5142
5137
  style: "uppercase",
5143
5138
  };
5144
5139
  class DoctypeStyle extends Rule {
5145
5140
  constructor(options) {
5146
- super({ ...defaults$l, ...options });
5141
+ super({ ...defaults$m, ...options });
5147
5142
  }
5148
5143
  static schema() {
5149
5144
  return {
@@ -5175,12 +5170,12 @@ class DoctypeStyle extends Rule {
5175
5170
  }
5176
5171
  }
5177
5172
 
5178
- const defaults$k = {
5173
+ const defaults$l = {
5179
5174
  style: "lowercase",
5180
5175
  };
5181
5176
  class ElementCase extends Rule {
5182
5177
  constructor(options) {
5183
- super({ ...defaults$k, ...options });
5178
+ super({ ...defaults$l, ...options });
5184
5179
  this.style = new CaseStyle(this.options.style, "element-case");
5185
5180
  }
5186
5181
  static schema() {
@@ -5246,14 +5241,14 @@ class ElementCase extends Rule {
5246
5241
  }
5247
5242
  }
5248
5243
 
5249
- const defaults$j = {
5244
+ const defaults$k = {
5250
5245
  pattern: "^[a-z][a-z0-9\\-._]*-[a-z0-9\\-._]*$",
5251
5246
  whitelist: [],
5252
5247
  blacklist: [],
5253
5248
  };
5254
5249
  class ElementName extends Rule {
5255
5250
  constructor(options) {
5256
- super({ ...defaults$j, ...options });
5251
+ super({ ...defaults$k, ...options });
5257
5252
  // eslint-disable-next-line security/detect-non-literal-regexp
5258
5253
  this.pattern = new RegExp(this.options.pattern);
5259
5254
  }
@@ -5294,7 +5289,7 @@ class ElementName extends Rule {
5294
5289
  ...context.blacklist.map((cur) => `- ${cur}`),
5295
5290
  ];
5296
5291
  }
5297
- if (context.pattern !== defaults$j.pattern) {
5292
+ if (context.pattern !== defaults$k.pattern) {
5298
5293
  return [
5299
5294
  `<${context.tagName}> is not a valid element name. This project is configured to only allow names matching the following regular expression:`,
5300
5295
  "",
@@ -5485,24 +5480,16 @@ class ElementPermittedOccurrences extends Rule {
5485
5480
  this.on("dom:ready", (event) => {
5486
5481
  const doc = event.document;
5487
5482
  doc.visitDepthFirst((node) => {
5488
- const parent = node.parent;
5489
- if (!parent || !parent.meta) {
5483
+ if (!node || !node.meta) {
5490
5484
  return;
5491
5485
  }
5492
- const rules = parent.meta.permittedContent;
5486
+ const rules = node.meta.permittedContent;
5493
5487
  if (!rules) {
5494
5488
  return;
5495
5489
  }
5496
- const siblings = parent.childElements.filter((cur) => cur.tagName === node.tagName);
5497
- const first = node.unique === siblings[0].unique;
5498
- /* the first occurrence should not trigger any errors, only the
5499
- * subsequent occurrences should. */
5500
- if (first) {
5501
- return;
5502
- }
5503
- if (parent.meta && !Validator.validateOccurrences(node, rules, siblings.length)) {
5504
- this.report(node, `Element <${node.tagName}> can only appear once under ${parent.annotatedName}`);
5505
- }
5490
+ Validator.validateOccurrences(node.childElements, rules, (child, category) => {
5491
+ this.report(child, `Element <${category}> can only appear once under ${node.annotatedName}`);
5492
+ });
5506
5493
  });
5507
5494
  });
5508
5495
  }
@@ -5537,11 +5524,11 @@ class ElementPermittedOrder extends Rule {
5537
5524
  function isCategoryOrTag(value) {
5538
5525
  return typeof value === "string";
5539
5526
  }
5540
- function isCategory(value) {
5527
+ function isCategory$1(value) {
5541
5528
  return value[0] === "@";
5542
5529
  }
5543
5530
  function formatCategoryOrTag(value) {
5544
- return isCategory(value) ? value.slice(1) : `<${value}>`;
5531
+ return isCategory$1(value) ? value.slice(1) : `<${value}>`;
5545
5532
  }
5546
5533
  function isFormattable(rules) {
5547
5534
  return rules.length > 0 && rules.every(isCategoryOrTag);
@@ -5554,7 +5541,7 @@ function getRuleDescription$1(context) {
5554
5541
  const preamble = `The \`${child}\` element cannot have a \`${parent}\` element as parent.`;
5555
5542
  if (isFormattable(rules)) {
5556
5543
  const allowed = rules.filter(isCategoryOrTag).map((it) => {
5557
- if (isCategory(it)) {
5544
+ if (isCategory$1(it)) {
5558
5545
  return `- any ${it.slice(1)} element`;
5559
5546
  }
5560
5547
  else {
@@ -5717,6 +5704,9 @@ class ElementRequiredAttributes extends Rule {
5717
5704
  }
5718
5705
  }
5719
5706
 
5707
+ function isCategory(value) {
5708
+ return value[0] === "@";
5709
+ }
5720
5710
  class ElementRequiredContent extends Rule {
5721
5711
  documentation(context) {
5722
5712
  if (context) {
@@ -5751,7 +5741,8 @@ class ElementRequiredContent extends Rule {
5751
5741
  element: node.annotatedName,
5752
5742
  missing: `<${missing}>`,
5753
5743
  };
5754
- const message = `${node.annotatedName} element must have <${missing}> as content`;
5744
+ const tag = isCategory(missing) ? `${missing.slice(1)} element` : `<${missing}>`;
5745
+ const message = `${node.annotatedName} element must have ${tag} as content`;
5755
5746
  this.report(node, message, null, context);
5756
5747
  }
5757
5748
  });
@@ -5842,7 +5833,7 @@ class EmptyTitle extends Rule {
5842
5833
  }
5843
5834
  }
5844
5835
 
5845
- const defaults$i = {
5836
+ const defaults$j = {
5846
5837
  allowMultipleH1: false,
5847
5838
  minInitialRank: "h1",
5848
5839
  sectioningRoots: ["dialog", '[role="dialog"]'],
@@ -5873,7 +5864,7 @@ function parseMaxInitial(value) {
5873
5864
  }
5874
5865
  class HeadingLevel extends Rule {
5875
5866
  constructor(options) {
5876
- super({ ...defaults$i, ...options });
5867
+ super({ ...defaults$j, ...options });
5877
5868
  this.stack = [];
5878
5869
  this.minInitialRank = parseMaxInitial(this.options.minInitialRank);
5879
5870
  this.sectionRoots = this.options.sectioningRoots.map((it) => new Pattern(it));
@@ -6031,12 +6022,12 @@ class HeadingLevel extends Rule {
6031
6022
  }
6032
6023
  }
6033
6024
 
6034
- const defaults$h = {
6025
+ const defaults$i = {
6035
6026
  pattern: "kebabcase",
6036
6027
  };
6037
6028
  class IdPattern extends Rule {
6038
6029
  constructor(options) {
6039
- super({ ...defaults$h, ...options });
6030
+ super({ ...defaults$i, ...options });
6040
6031
  this.pattern = parsePattern(this.options.pattern);
6041
6032
  }
6042
6033
  static schema() {
@@ -6318,12 +6309,12 @@ function findLabelByParent(el) {
6318
6309
  return [];
6319
6310
  }
6320
6311
 
6321
- const defaults$g = {
6312
+ const defaults$h = {
6322
6313
  maxlength: 70,
6323
6314
  };
6324
6315
  class LongTitle extends Rule {
6325
6316
  constructor(options) {
6326
- super({ ...defaults$g, ...options });
6317
+ super({ ...defaults$h, ...options });
6327
6318
  this.maxlength = this.options.maxlength;
6328
6319
  }
6329
6320
  static schema() {
@@ -6513,13 +6504,13 @@ class MultipleLabeledControls extends Rule {
6513
6504
  }
6514
6505
  }
6515
6506
 
6516
- const defaults$f = {
6507
+ const defaults$g = {
6517
6508
  include: null,
6518
6509
  exclude: null,
6519
6510
  };
6520
6511
  class NoAutoplay extends Rule {
6521
6512
  constructor(options) {
6522
- super({ ...defaults$f, ...options });
6513
+ super({ ...defaults$g, ...options });
6523
6514
  }
6524
6515
  documentation(context) {
6525
6516
  const tagName = context ? ` on <${context.tagName}>` : "";
@@ -6760,14 +6751,14 @@ Omitted end tags can be ambigious for humans to read and many editors have troub
6760
6751
  }
6761
6752
  }
6762
6753
 
6763
- const defaults$e = {
6754
+ const defaults$f = {
6764
6755
  include: null,
6765
6756
  exclude: null,
6766
6757
  allowedProperties: ["display"],
6767
6758
  };
6768
6759
  class NoInlineStyle extends Rule {
6769
6760
  constructor(options) {
6770
- super({ ...defaults$e, ...options });
6761
+ super({ ...defaults$f, ...options });
6771
6762
  }
6772
6763
  static schema() {
6773
6764
  return {
@@ -6969,7 +6960,7 @@ class NoMultipleMain extends Rule {
6969
6960
  }
6970
6961
  }
6971
6962
 
6972
- const defaults$d = {
6963
+ const defaults$e = {
6973
6964
  relaxed: false,
6974
6965
  };
6975
6966
  const textRegexp = /([<>]|&(?![a-zA-Z0-9#]+;))/g;
@@ -6986,7 +6977,7 @@ const replacementTable = {
6986
6977
  };
6987
6978
  class NoRawCharacters extends Rule {
6988
6979
  constructor(options) {
6989
- super({ ...defaults$d, ...options });
6980
+ super({ ...defaults$e, ...options });
6990
6981
  this.relaxed = this.options.relaxed;
6991
6982
  }
6992
6983
  static schema() {
@@ -7164,13 +7155,13 @@ class NoRedundantRole extends Rule {
7164
7155
  }
7165
7156
 
7166
7157
  const xmlns = /^(.+):.+$/;
7167
- const defaults$c = {
7158
+ const defaults$d = {
7168
7159
  ignoreForeign: true,
7169
7160
  ignoreXML: true,
7170
7161
  };
7171
7162
  class NoSelfClosing extends Rule {
7172
7163
  constructor(options) {
7173
- super({ ...defaults$c, ...options });
7164
+ super({ ...defaults$d, ...options });
7174
7165
  }
7175
7166
  static schema() {
7176
7167
  return {
@@ -7259,7 +7250,44 @@ class NoTrailingWhitespace extends Rule {
7259
7250
  }
7260
7251
  }
7261
7252
 
7253
+ const defaults$c = {
7254
+ include: null,
7255
+ exclude: null,
7256
+ };
7262
7257
  class NoUnknownElements extends Rule {
7258
+ constructor(options) {
7259
+ super({ ...defaults$c, ...options });
7260
+ }
7261
+ static schema() {
7262
+ return {
7263
+ exclude: {
7264
+ anyOf: [
7265
+ {
7266
+ items: {
7267
+ type: "string",
7268
+ },
7269
+ type: "array",
7270
+ },
7271
+ {
7272
+ type: "null",
7273
+ },
7274
+ ],
7275
+ },
7276
+ include: {
7277
+ anyOf: [
7278
+ {
7279
+ items: {
7280
+ type: "string",
7281
+ },
7282
+ type: "array",
7283
+ },
7284
+ {
7285
+ type: "null",
7286
+ },
7287
+ ],
7288
+ },
7289
+ };
7290
+ }
7263
7291
  documentation(context) {
7264
7292
  const element = context ? ` <${context}>` : "";
7265
7293
  return {
@@ -7270,9 +7298,13 @@ class NoUnknownElements extends Rule {
7270
7298
  setup() {
7271
7299
  this.on("tag:start", (event) => {
7272
7300
  const node = event.target;
7273
- if (!node.meta) {
7274
- this.report(node, `Unknown element <${node.tagName}>`, null, node.tagName);
7301
+ if (node.meta) {
7302
+ return;
7275
7303
  }
7304
+ if (this.isKeywordIgnored(node.tagName, keywordPatternMatcher)) {
7305
+ return;
7306
+ }
7307
+ this.report(node, `Unknown element <${node.tagName}>`, null, node.tagName);
7276
7308
  });
7277
7309
  }
7278
7310
  }
@@ -8734,6 +8766,11 @@ class H37 extends Rule {
8734
8766
  }
8735
8767
  }
8736
8768
 
8769
+ var _a;
8770
+ /* istanbul ignore next: this will always be present for the <th>
8771
+ * attribute (or the tests would fail) */
8772
+ const { enum: validScopes } = (_a = html5.th.attributes) === null || _a === void 0 ? void 0 : _a.scope;
8773
+ const joinedScopes = naturalJoin(validScopes);
8737
8774
  class H63 extends Rule {
8738
8775
  documentation() {
8739
8776
  return {
@@ -8743,19 +8780,25 @@ class H63 extends Rule {
8743
8780
  }
8744
8781
  setup() {
8745
8782
  this.on("tag:ready", (event) => {
8746
- var _a, _b, _c, _d;
8783
+ var _a, _b;
8747
8784
  const node = event.target;
8748
8785
  /* only validate th */
8749
8786
  if (!node || node.tagName !== "th") {
8750
8787
  return;
8751
8788
  }
8789
+ const scope = node.getAttribute("scope");
8790
+ const value = scope === null || scope === void 0 ? void 0 : scope.value;
8791
+ /* ignore dynamic scope */
8792
+ if (value instanceof DynamicValue) {
8793
+ return;
8794
+ }
8752
8795
  /* ignore elements with valid scope values */
8753
- const scope = node.getAttributeValue("scope");
8754
- const scopeMeta = (_b = (_a = html5 === null || html5 === void 0 ? void 0 : html5.th) === null || _a === void 0 ? void 0 : _a.attributes) === null || _b === void 0 ? void 0 : _b.scope;
8755
- if (scope && ((_c = scopeMeta.enum) === null || _c === void 0 ? void 0 : _c.includes(scope))) {
8796
+ if (value && validScopes.includes(value)) {
8756
8797
  return;
8757
8798
  }
8758
- this.report(node, `<th> element must have a valid scope attribute: ${((_d = scopeMeta.enum) !== null && _d !== void 0 ? _d : []).join(", ")}`, node.location);
8799
+ const message = `<th> element must have a valid scope attribute: ${joinedScopes}`;
8800
+ const location = (_b = (_a = scope === null || scope === void 0 ? void 0 : scope.valueLocation) !== null && _a !== void 0 ? _a : scope === null || scope === void 0 ? void 0 : scope.keyLocation) !== null && _b !== void 0 ? _b : node.location;
8801
+ this.report(node, message, location);
8759
8802
  });
8760
8803
  }
8761
8804
  }
@@ -11126,7 +11169,7 @@ class HtmlValidate {
11126
11169
  /** @public */
11127
11170
  const name = "html-validate";
11128
11171
  /** @public */
11129
- const version = "7.10.1";
11172
+ const version = "7.11.0";
11130
11173
  /** @public */
11131
11174
  const bugs = "https://gitlab.com/html-validate/html-validate/issues/new";
11132
11175