html-validate 7.10.0 → 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 +6 -2
  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 +6 -2
  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 +19 -19
package/dist/cjs/core.js CHANGED
@@ -7,10 +7,10 @@ var Ajv = require('ajv');
7
7
  var deepmerge = require('deepmerge');
8
8
  var espree = require('espree');
9
9
  var walk = require('acorn-walk');
10
- var elements = require('./elements.js');
11
10
  var path = require('path');
12
11
  var semver = require('semver');
13
12
  var kleur = require('kleur');
13
+ var elements = require('./elements.js');
14
14
  var codeFrame = require('@babel/code-frame');
15
15
  var stylishImpl = require('@html-validate/stylish');
16
16
 
@@ -2646,29 +2646,40 @@ class Validator {
2646
2646
  *
2647
2647
  * For instance, a `<table>` element can only contain a single `<tbody>`
2648
2648
  * child. If multiple `<tbody>` exists this test will fail both nodes.
2649
+ * Note that this is called on the parent but will fail the children violating
2650
+ * the rule.
2649
2651
  *
2650
- * @param node - Element to test.
2651
- * @param rules - List of rules.
2652
- * @param numSiblings - How many siblings of the same type as the element
2653
- * exists (including the element itself)
2654
- * @returns `true` if the element passes the test.
2652
+ * @param children - Array of children to validate.
2653
+ * @param rules - List of rules of the parent element.
2654
+ * @returns `true` if the parent element of the children passes the test.
2655
2655
  */
2656
- static validateOccurrences(node, rules, numSiblings) {
2656
+ static validateOccurrences(children, rules, cb) {
2657
2657
  if (!rules) {
2658
2658
  return true;
2659
2659
  }
2660
- const category = rules.find((cur) => {
2660
+ let valid = true;
2661
+ for (const rule of rules) {
2661
2662
  /** @todo handle complex rules and not just plain arrays (but as of now
2662
2663
  * there is no use-case for it) */
2663
2664
  // istanbul ignore next
2664
- if (typeof cur !== "string") {
2665
+ if (typeof rule !== "string") {
2665
2666
  return false;
2666
2667
  }
2667
- const match = cur.match(/^(.*?)[?*]?$/);
2668
- return match && match[1] === node.tagName;
2669
- });
2670
- const limit = parseAmountQualifier(category);
2671
- return limit === null || numSiblings <= limit;
2668
+ // Check if the rule has a quantifier
2669
+ const [, category, quantifier] = rule.match(/^(@?.*?)([?*]?)$/);
2670
+ const limit = category && quantifier && parseQuantifier(quantifier);
2671
+ if (limit) {
2672
+ const siblings = children.filter((cur) => Validator.validatePermittedCategory(cur, rule, true));
2673
+ if (siblings.length > limit) {
2674
+ // fail only the children above the limit (currently limit can only be 1)
2675
+ for (const child of siblings.slice(limit)) {
2676
+ cb(child, category);
2677
+ }
2678
+ valid = false;
2679
+ }
2680
+ }
2681
+ }
2682
+ return valid;
2672
2683
  }
2673
2684
  /**
2674
2685
  * Validate elements order.
@@ -2730,14 +2741,15 @@ class Validator {
2730
2741
  * Check if an element has the required set of elements. At least one of the
2731
2742
  * selectors must match.
2732
2743
  *
2733
- * Returns [] when valid or a list of tagNames missing as content.
2744
+ * Returns `[]` when valid or a list of required but missing tagnames or
2745
+ * categories.
2734
2746
  */
2735
2747
  static validateRequiredContent(node, rules) {
2736
2748
  if (!rules || rules.length === 0) {
2737
2749
  return [];
2738
2750
  }
2739
2751
  return rules.filter((tagName) => {
2740
- const haveMatchingChild = node.childElements.some((child) => child.is(tagName));
2752
+ const haveMatchingChild = node.childElements.some((child) => Validator.validatePermittedCategory(child, tagName, false));
2741
2753
  return !haveMatchingChild;
2742
2754
  });
2743
2755
  }
@@ -2841,16 +2853,16 @@ class Validator {
2841
2853
  */
2842
2854
  // eslint-disable-next-line complexity
2843
2855
  static validatePermittedCategory(node, category, defaultMatch) {
2856
+ const [, rawCategory] = category.match(/^(@?.*?)([?*]?)$/);
2844
2857
  /* match tagName when an explicit name is given */
2845
- if (category[0] !== "@") {
2846
- const [, tagName] = category.match(/^(.*?)[?*]?$/);
2847
- return node.tagName === tagName;
2858
+ if (rawCategory[0] !== "@") {
2859
+ return node.tagName === rawCategory;
2848
2860
  }
2849
2861
  /* if the meta entry is missing assume any content model would match */
2850
2862
  if (!node.meta) {
2851
2863
  return defaultMatch;
2852
2864
  }
2853
- switch (category) {
2865
+ switch (rawCategory) {
2854
2866
  case "@meta":
2855
2867
  return node.meta.metadata;
2856
2868
  case "@flow":
@@ -2882,23 +2894,15 @@ function validateKeys(rule) {
2882
2894
  }
2883
2895
  }
2884
2896
  }
2885
- function parseAmountQualifier(category) {
2886
- if (!category) {
2887
- /* content not allowed, catched by another rule so just assume unlimited
2888
- * usage for this purpose */
2889
- return null;
2890
- }
2891
- const [, qualifier] = category.match(/^.*?([?*]?)$/);
2892
- switch (qualifier) {
2897
+ function parseQuantifier(quantifier) {
2898
+ switch (quantifier) {
2893
2899
  case "?":
2894
2900
  return 1;
2895
- case "":
2896
- return null;
2897
2901
  case "*":
2898
2902
  return null;
2899
- /* istanbul ignore next */
2903
+ // istanbul ignore next
2900
2904
  default:
2901
- throw new Error(`Invalid amount qualifier "${qualifier}" used`);
2905
+ throw new Error(`Invalid quantifier "${quantifier}" used`);
2902
2906
  }
2903
2907
  }
2904
2908
 
@@ -3430,16 +3434,7 @@ class Rule {
3430
3434
  * `exclude`.
3431
3435
  */
3432
3436
  isKeywordIgnored(keyword, matcher = (list, it) => list.includes(it)) {
3433
- const { include, exclude } = this.options;
3434
- /* ignore keyword if not present in "include" */
3435
- if (include && !matcher(include, keyword)) {
3436
- return true;
3437
- }
3438
- /* ignore keyword if present in "excludes" */
3439
- if (exclude && matcher(exclude, keyword)) {
3440
- return true;
3441
- }
3442
- return false;
3437
+ return rulesHelper.isKeywordIgnored(this.options, keyword, matcher);
3443
3438
  }
3444
3439
  /**
3445
3440
  * Find all tags which has enabled given property.
@@ -3565,7 +3560,7 @@ class Rule {
3565
3560
  }
3566
3561
  }
3567
3562
 
3568
- const defaults$u = {
3563
+ const defaults$v = {
3569
3564
  allowExternal: true,
3570
3565
  allowRelative: true,
3571
3566
  allowAbsolute: true,
@@ -3609,7 +3604,7 @@ function matchList(value, list) {
3609
3604
  }
3610
3605
  class AllowedLinks extends Rule {
3611
3606
  constructor(options) {
3612
- super({ ...defaults$u, ...options });
3607
+ super({ ...defaults$v, ...options });
3613
3608
  this.allowExternal = parseAllow(this.options.allowExternal);
3614
3609
  this.allowRelative = parseAllow(this.options.allowRelative);
3615
3610
  this.allowAbsolute = parseAllow(this.options.allowAbsolute);
@@ -3757,7 +3752,7 @@ var RuleContext$1;
3757
3752
  RuleContext["MISSING_ALT"] = "missing-alt";
3758
3753
  RuleContext["MISSING_HREF"] = "missing-href";
3759
3754
  })(RuleContext$1 || (RuleContext$1 = {}));
3760
- const defaults$t = {
3755
+ const defaults$u = {
3761
3756
  accessible: true,
3762
3757
  };
3763
3758
  function findByTarget(target, siblings) {
@@ -3795,7 +3790,7 @@ function getDescription$1(context) {
3795
3790
  }
3796
3791
  class AreaAlt extends Rule {
3797
3792
  constructor(options) {
3798
- super({ ...defaults$t, ...options });
3793
+ super({ ...defaults$u, ...options });
3799
3794
  }
3800
3795
  static schema() {
3801
3796
  return {
@@ -3959,13 +3954,13 @@ class AriaLabelMisuse extends Rule {
3959
3954
  class ConfigError extends UserError {
3960
3955
  }
3961
3956
 
3962
- const defaults$s = {
3957
+ const defaults$t = {
3963
3958
  style: "lowercase",
3964
3959
  ignoreForeign: true,
3965
3960
  };
3966
3961
  class AttrCase extends Rule {
3967
3962
  constructor(options) {
3968
- super({ ...defaults$s, ...options });
3963
+ super({ ...defaults$t, ...options });
3969
3964
  this.style = new rulesHelper.CaseStyle(this.options.style, "attr-case");
3970
3965
  }
3971
3966
  static schema() {
@@ -4310,7 +4305,7 @@ class AttrDelimiter extends Rule {
4310
4305
  }
4311
4306
 
4312
4307
  const DEFAULT_PATTERN = "[a-z0-9-:]+";
4313
- const defaults$r = {
4308
+ const defaults$s = {
4314
4309
  pattern: DEFAULT_PATTERN,
4315
4310
  ignoreForeign: true,
4316
4311
  };
@@ -4347,7 +4342,7 @@ function generateDescription(name, pattern) {
4347
4342
  }
4348
4343
  class AttrPattern extends Rule {
4349
4344
  constructor(options) {
4350
- super({ ...defaults$r, ...options });
4345
+ super({ ...defaults$s, ...options });
4351
4346
  this.pattern = generateRegexp(this.options.pattern);
4352
4347
  }
4353
4348
  static schema() {
@@ -4408,7 +4403,7 @@ var QuoteStyle;
4408
4403
  QuoteStyle["AUTO_QUOTE"] = "auto";
4409
4404
  QuoteStyle["ANY_QUOTE"] = "any";
4410
4405
  })(QuoteStyle || (QuoteStyle = {}));
4411
- const defaults$q = {
4406
+ const defaults$r = {
4412
4407
  style: "auto",
4413
4408
  unquoted: false,
4414
4409
  };
@@ -4475,7 +4470,7 @@ class AttrQuotes extends Rule {
4475
4470
  };
4476
4471
  }
4477
4472
  constructor(options) {
4478
- super({ ...defaults$q, ...options });
4473
+ super({ ...defaults$r, ...options });
4479
4474
  this.style = parseStyle$4(this.options.style);
4480
4475
  }
4481
4476
  setup() {
@@ -4645,12 +4640,12 @@ class AttributeAllowedValues extends Rule {
4645
4640
  }
4646
4641
  }
4647
4642
 
4648
- const defaults$p = {
4643
+ const defaults$q = {
4649
4644
  style: "omit",
4650
4645
  };
4651
4646
  class AttributeBooleanStyle extends Rule {
4652
4647
  constructor(options) {
4653
- super({ ...defaults$p, ...options });
4648
+ super({ ...defaults$q, ...options });
4654
4649
  this.hasInvalidStyle = parseStyle$3(this.options.style);
4655
4650
  }
4656
4651
  static schema() {
@@ -4726,12 +4721,12 @@ function reportMessage$1(attr, style) {
4726
4721
  return "";
4727
4722
  }
4728
4723
 
4729
- const defaults$o = {
4724
+ const defaults$p = {
4730
4725
  style: "omit",
4731
4726
  };
4732
4727
  class AttributeEmptyStyle extends Rule {
4733
4728
  constructor(options) {
4734
- super({ ...defaults$o, ...options });
4729
+ super({ ...defaults$p, ...options });
4735
4730
  this.hasInvalidStyle = parseStyle$2(this.options.style);
4736
4731
  }
4737
4732
  static schema() {
@@ -4887,12 +4882,12 @@ function describePattern(pattern) {
4887
4882
  }
4888
4883
  }
4889
4884
 
4890
- const defaults$n = {
4885
+ const defaults$o = {
4891
4886
  pattern: "kebabcase",
4892
4887
  };
4893
4888
  class ClassPattern extends Rule {
4894
4889
  constructor(options) {
4895
- super({ ...defaults$n, ...options });
4890
+ super({ ...defaults$o, ...options });
4896
4891
  this.pattern = parsePattern(this.options.pattern);
4897
4892
  }
4898
4893
  static schema() {
@@ -5001,13 +4996,13 @@ class CloseOrder extends Rule {
5001
4996
  }
5002
4997
  }
5003
4998
 
5004
- const defaults$m = {
4999
+ const defaults$n = {
5005
5000
  include: null,
5006
5001
  exclude: null,
5007
5002
  };
5008
5003
  class Deprecated extends Rule {
5009
5004
  constructor(options) {
5010
- super({ ...defaults$m, ...options });
5005
+ super({ ...defaults$n, ...options });
5011
5006
  }
5012
5007
  static schema() {
5013
5008
  return {
@@ -5170,12 +5165,12 @@ let NoStyleTag$1 = class NoStyleTag extends Rule {
5170
5165
  }
5171
5166
  };
5172
5167
 
5173
- const defaults$l = {
5168
+ const defaults$m = {
5174
5169
  style: "uppercase",
5175
5170
  };
5176
5171
  class DoctypeStyle extends Rule {
5177
5172
  constructor(options) {
5178
- super({ ...defaults$l, ...options });
5173
+ super({ ...defaults$m, ...options });
5179
5174
  }
5180
5175
  static schema() {
5181
5176
  return {
@@ -5207,12 +5202,12 @@ class DoctypeStyle extends Rule {
5207
5202
  }
5208
5203
  }
5209
5204
 
5210
- const defaults$k = {
5205
+ const defaults$l = {
5211
5206
  style: "lowercase",
5212
5207
  };
5213
5208
  class ElementCase extends Rule {
5214
5209
  constructor(options) {
5215
- super({ ...defaults$k, ...options });
5210
+ super({ ...defaults$l, ...options });
5216
5211
  this.style = new rulesHelper.CaseStyle(this.options.style, "element-case");
5217
5212
  }
5218
5213
  static schema() {
@@ -5278,14 +5273,14 @@ class ElementCase extends Rule {
5278
5273
  }
5279
5274
  }
5280
5275
 
5281
- const defaults$j = {
5276
+ const defaults$k = {
5282
5277
  pattern: "^[a-z][a-z0-9\\-._]*-[a-z0-9\\-._]*$",
5283
5278
  whitelist: [],
5284
5279
  blacklist: [],
5285
5280
  };
5286
5281
  class ElementName extends Rule {
5287
5282
  constructor(options) {
5288
- super({ ...defaults$j, ...options });
5283
+ super({ ...defaults$k, ...options });
5289
5284
  // eslint-disable-next-line security/detect-non-literal-regexp
5290
5285
  this.pattern = new RegExp(this.options.pattern);
5291
5286
  }
@@ -5326,7 +5321,7 @@ class ElementName extends Rule {
5326
5321
  ...context.blacklist.map((cur) => `- ${cur}`),
5327
5322
  ];
5328
5323
  }
5329
- if (context.pattern !== defaults$j.pattern) {
5324
+ if (context.pattern !== defaults$k.pattern) {
5330
5325
  return [
5331
5326
  `<${context.tagName}> is not a valid element name. This project is configured to only allow names matching the following regular expression:`,
5332
5327
  "",
@@ -5517,24 +5512,16 @@ class ElementPermittedOccurrences extends Rule {
5517
5512
  this.on("dom:ready", (event) => {
5518
5513
  const doc = event.document;
5519
5514
  doc.visitDepthFirst((node) => {
5520
- const parent = node.parent;
5521
- if (!parent || !parent.meta) {
5515
+ if (!node || !node.meta) {
5522
5516
  return;
5523
5517
  }
5524
- const rules = parent.meta.permittedContent;
5518
+ const rules = node.meta.permittedContent;
5525
5519
  if (!rules) {
5526
5520
  return;
5527
5521
  }
5528
- const siblings = parent.childElements.filter((cur) => cur.tagName === node.tagName);
5529
- const first = node.unique === siblings[0].unique;
5530
- /* the first occurrence should not trigger any errors, only the
5531
- * subsequent occurrences should. */
5532
- if (first) {
5533
- return;
5534
- }
5535
- if (parent.meta && !Validator.validateOccurrences(node, rules, siblings.length)) {
5536
- this.report(node, `Element <${node.tagName}> can only appear once under ${parent.annotatedName}`);
5537
- }
5522
+ Validator.validateOccurrences(node.childElements, rules, (child, category) => {
5523
+ this.report(child, `Element <${category}> can only appear once under ${node.annotatedName}`);
5524
+ });
5538
5525
  });
5539
5526
  });
5540
5527
  }
@@ -5569,11 +5556,11 @@ class ElementPermittedOrder extends Rule {
5569
5556
  function isCategoryOrTag(value) {
5570
5557
  return typeof value === "string";
5571
5558
  }
5572
- function isCategory(value) {
5559
+ function isCategory$1(value) {
5573
5560
  return value[0] === "@";
5574
5561
  }
5575
5562
  function formatCategoryOrTag(value) {
5576
- return isCategory(value) ? value.slice(1) : `<${value}>`;
5563
+ return isCategory$1(value) ? value.slice(1) : `<${value}>`;
5577
5564
  }
5578
5565
  function isFormattable(rules) {
5579
5566
  return rules.length > 0 && rules.every(isCategoryOrTag);
@@ -5586,7 +5573,7 @@ function getRuleDescription$1(context) {
5586
5573
  const preamble = `The \`${child}\` element cannot have a \`${parent}\` element as parent.`;
5587
5574
  if (isFormattable(rules)) {
5588
5575
  const allowed = rules.filter(isCategoryOrTag).map((it) => {
5589
- if (isCategory(it)) {
5576
+ if (isCategory$1(it)) {
5590
5577
  return `- any ${it.slice(1)} element`;
5591
5578
  }
5592
5579
  else {
@@ -5749,6 +5736,9 @@ class ElementRequiredAttributes extends Rule {
5749
5736
  }
5750
5737
  }
5751
5738
 
5739
+ function isCategory(value) {
5740
+ return value[0] === "@";
5741
+ }
5752
5742
  class ElementRequiredContent extends Rule {
5753
5743
  documentation(context) {
5754
5744
  if (context) {
@@ -5783,7 +5773,8 @@ class ElementRequiredContent extends Rule {
5783
5773
  element: node.annotatedName,
5784
5774
  missing: `<${missing}>`,
5785
5775
  };
5786
- const message = `${node.annotatedName} element must have <${missing}> as content`;
5776
+ const tag = isCategory(missing) ? `${missing.slice(1)} element` : `<${missing}>`;
5777
+ const message = `${node.annotatedName} element must have ${tag} as content`;
5787
5778
  this.report(node, message, null, context);
5788
5779
  }
5789
5780
  });
@@ -5874,7 +5865,7 @@ class EmptyTitle extends Rule {
5874
5865
  }
5875
5866
  }
5876
5867
 
5877
- const defaults$i = {
5868
+ const defaults$j = {
5878
5869
  allowMultipleH1: false,
5879
5870
  minInitialRank: "h1",
5880
5871
  sectioningRoots: ["dialog", '[role="dialog"]'],
@@ -5905,7 +5896,7 @@ function parseMaxInitial(value) {
5905
5896
  }
5906
5897
  class HeadingLevel extends Rule {
5907
5898
  constructor(options) {
5908
- super({ ...defaults$i, ...options });
5899
+ super({ ...defaults$j, ...options });
5909
5900
  this.stack = [];
5910
5901
  this.minInitialRank = parseMaxInitial(this.options.minInitialRank);
5911
5902
  this.sectionRoots = this.options.sectioningRoots.map((it) => new Pattern(it));
@@ -6063,12 +6054,12 @@ class HeadingLevel extends Rule {
6063
6054
  }
6064
6055
  }
6065
6056
 
6066
- const defaults$h = {
6057
+ const defaults$i = {
6067
6058
  pattern: "kebabcase",
6068
6059
  };
6069
6060
  class IdPattern extends Rule {
6070
6061
  constructor(options) {
6071
- super({ ...defaults$h, ...options });
6062
+ super({ ...defaults$i, ...options });
6072
6063
  this.pattern = parsePattern(this.options.pattern);
6073
6064
  }
6074
6065
  static schema() {
@@ -6350,12 +6341,12 @@ function findLabelByParent(el) {
6350
6341
  return [];
6351
6342
  }
6352
6343
 
6353
- const defaults$g = {
6344
+ const defaults$h = {
6354
6345
  maxlength: 70,
6355
6346
  };
6356
6347
  class LongTitle extends Rule {
6357
6348
  constructor(options) {
6358
- super({ ...defaults$g, ...options });
6349
+ super({ ...defaults$h, ...options });
6359
6350
  this.maxlength = this.options.maxlength;
6360
6351
  }
6361
6352
  static schema() {
@@ -6545,13 +6536,13 @@ class MultipleLabeledControls extends Rule {
6545
6536
  }
6546
6537
  }
6547
6538
 
6548
- const defaults$f = {
6539
+ const defaults$g = {
6549
6540
  include: null,
6550
6541
  exclude: null,
6551
6542
  };
6552
6543
  class NoAutoplay extends Rule {
6553
6544
  constructor(options) {
6554
- super({ ...defaults$f, ...options });
6545
+ super({ ...defaults$g, ...options });
6555
6546
  }
6556
6547
  documentation(context) {
6557
6548
  const tagName = context ? ` on <${context.tagName}>` : "";
@@ -6792,14 +6783,14 @@ Omitted end tags can be ambigious for humans to read and many editors have troub
6792
6783
  }
6793
6784
  }
6794
6785
 
6795
- const defaults$e = {
6786
+ const defaults$f = {
6796
6787
  include: null,
6797
6788
  exclude: null,
6798
6789
  allowedProperties: ["display"],
6799
6790
  };
6800
6791
  class NoInlineStyle extends Rule {
6801
6792
  constructor(options) {
6802
- super({ ...defaults$e, ...options });
6793
+ super({ ...defaults$f, ...options });
6803
6794
  }
6804
6795
  static schema() {
6805
6796
  return {
@@ -7001,7 +6992,7 @@ class NoMultipleMain extends Rule {
7001
6992
  }
7002
6993
  }
7003
6994
 
7004
- const defaults$d = {
6995
+ const defaults$e = {
7005
6996
  relaxed: false,
7006
6997
  };
7007
6998
  const textRegexp = /([<>]|&(?![a-zA-Z0-9#]+;))/g;
@@ -7018,7 +7009,7 @@ const replacementTable = {
7018
7009
  };
7019
7010
  class NoRawCharacters extends Rule {
7020
7011
  constructor(options) {
7021
- super({ ...defaults$d, ...options });
7012
+ super({ ...defaults$e, ...options });
7022
7013
  this.relaxed = this.options.relaxed;
7023
7014
  }
7024
7015
  static schema() {
@@ -7196,13 +7187,13 @@ class NoRedundantRole extends Rule {
7196
7187
  }
7197
7188
 
7198
7189
  const xmlns = /^(.+):.+$/;
7199
- const defaults$c = {
7190
+ const defaults$d = {
7200
7191
  ignoreForeign: true,
7201
7192
  ignoreXML: true,
7202
7193
  };
7203
7194
  class NoSelfClosing extends Rule {
7204
7195
  constructor(options) {
7205
- super({ ...defaults$c, ...options });
7196
+ super({ ...defaults$d, ...options });
7206
7197
  }
7207
7198
  static schema() {
7208
7199
  return {
@@ -7291,7 +7282,44 @@ class NoTrailingWhitespace extends Rule {
7291
7282
  }
7292
7283
  }
7293
7284
 
7285
+ const defaults$c = {
7286
+ include: null,
7287
+ exclude: null,
7288
+ };
7294
7289
  class NoUnknownElements extends Rule {
7290
+ constructor(options) {
7291
+ super({ ...defaults$c, ...options });
7292
+ }
7293
+ static schema() {
7294
+ return {
7295
+ exclude: {
7296
+ anyOf: [
7297
+ {
7298
+ items: {
7299
+ type: "string",
7300
+ },
7301
+ type: "array",
7302
+ },
7303
+ {
7304
+ type: "null",
7305
+ },
7306
+ ],
7307
+ },
7308
+ include: {
7309
+ anyOf: [
7310
+ {
7311
+ items: {
7312
+ type: "string",
7313
+ },
7314
+ type: "array",
7315
+ },
7316
+ {
7317
+ type: "null",
7318
+ },
7319
+ ],
7320
+ },
7321
+ };
7322
+ }
7295
7323
  documentation(context) {
7296
7324
  const element = context ? ` <${context}>` : "";
7297
7325
  return {
@@ -7302,9 +7330,13 @@ class NoUnknownElements extends Rule {
7302
7330
  setup() {
7303
7331
  this.on("tag:start", (event) => {
7304
7332
  const node = event.target;
7305
- if (!node.meta) {
7306
- this.report(node, `Unknown element <${node.tagName}>`, null, node.tagName);
7333
+ if (node.meta) {
7334
+ return;
7307
7335
  }
7336
+ if (this.isKeywordIgnored(node.tagName, rulesHelper.keywordPatternMatcher)) {
7337
+ return;
7338
+ }
7339
+ this.report(node, `Unknown element <${node.tagName}>`, null, node.tagName);
7308
7340
  });
7309
7341
  }
7310
7342
  }
@@ -8766,6 +8798,11 @@ class H37 extends Rule {
8766
8798
  }
8767
8799
  }
8768
8800
 
8801
+ var _a;
8802
+ /* istanbul ignore next: this will always be present for the <th>
8803
+ * attribute (or the tests would fail) */
8804
+ const { enum: validScopes } = (_a = elements.html5.th.attributes) === null || _a === void 0 ? void 0 : _a.scope;
8805
+ const joinedScopes = rulesHelper.naturalJoin(validScopes);
8769
8806
  class H63 extends Rule {
8770
8807
  documentation() {
8771
8808
  return {
@@ -8775,19 +8812,25 @@ class H63 extends Rule {
8775
8812
  }
8776
8813
  setup() {
8777
8814
  this.on("tag:ready", (event) => {
8778
- var _a, _b, _c, _d;
8815
+ var _a, _b;
8779
8816
  const node = event.target;
8780
8817
  /* only validate th */
8781
8818
  if (!node || node.tagName !== "th") {
8782
8819
  return;
8783
8820
  }
8821
+ const scope = node.getAttribute("scope");
8822
+ const value = scope === null || scope === void 0 ? void 0 : scope.value;
8823
+ /* ignore dynamic scope */
8824
+ if (value instanceof DynamicValue) {
8825
+ return;
8826
+ }
8784
8827
  /* ignore elements with valid scope values */
8785
- const scope = node.getAttributeValue("scope");
8786
- const scopeMeta = (_b = (_a = elements.html5 === null || elements.html5 === void 0 ? void 0 : elements.html5.th) === null || _a === void 0 ? void 0 : _a.attributes) === null || _b === void 0 ? void 0 : _b.scope;
8787
- if (scope && ((_c = scopeMeta.enum) === null || _c === void 0 ? void 0 : _c.includes(scope))) {
8828
+ if (value && validScopes.includes(value)) {
8788
8829
  return;
8789
8830
  }
8790
- this.report(node, `<th> element must have a valid scope attribute: ${((_d = scopeMeta.enum) !== null && _d !== void 0 ? _d : []).join(", ")}`, node.location);
8831
+ const message = `<th> element must have a valid scope attribute: ${joinedScopes}`;
8832
+ 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;
8833
+ this.report(node, message, location);
8791
8834
  });
8792
8835
  }
8793
8836
  }
@@ -11158,7 +11201,7 @@ class HtmlValidate {
11158
11201
  /** @public */
11159
11202
  const name = "html-validate";
11160
11203
  /** @public */
11161
- const version = "7.10.0";
11204
+ const version = "7.11.0";
11162
11205
  /** @public */
11163
11206
  const bugs = "https://gitlab.com/html-validate/html-validate/issues/new";
11164
11207