html-validate 9.6.1 → 9.7.1

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 (42) hide show
  1. package/dist/cjs/cli.js.map +1 -1
  2. package/dist/cjs/core-browser.js.map +1 -1
  3. package/dist/cjs/core-nodejs.js.map +1 -1
  4. package/dist/cjs/core.js +151 -77
  5. package/dist/cjs/core.js.map +1 -1
  6. package/dist/cjs/elements.js +1 -0
  7. package/dist/cjs/elements.js.map +1 -1
  8. package/dist/cjs/html-validate.js.map +1 -1
  9. package/dist/cjs/html5.js.map +1 -1
  10. package/dist/cjs/jest-diff.js.map +1 -1
  11. package/dist/cjs/jest-worker.js.map +1 -1
  12. package/dist/cjs/jest.js.map +1 -1
  13. package/dist/cjs/matcher-utils.js.map +1 -1
  14. package/dist/cjs/matchers-jestonly.js.map +1 -1
  15. package/dist/cjs/matchers.js.map +1 -1
  16. package/dist/cjs/meta-helper.js.map +1 -1
  17. package/dist/cjs/utils/natural-join.js.map +1 -1
  18. package/dist/cjs/vitest.js.map +1 -1
  19. package/dist/es/cli.js.map +1 -1
  20. package/dist/es/core-browser.js.map +1 -1
  21. package/dist/es/core-nodejs.js.map +1 -1
  22. package/dist/es/core.js +151 -77
  23. package/dist/es/core.js.map +1 -1
  24. package/dist/es/elements.js +1 -0
  25. package/dist/es/elements.js.map +1 -1
  26. package/dist/es/html-validate.js.map +1 -1
  27. package/dist/es/html5.js.map +1 -1
  28. package/dist/es/jest-diff.js.map +1 -1
  29. package/dist/es/jest-worker.js.map +1 -1
  30. package/dist/es/jest.js.map +1 -1
  31. package/dist/es/matcher-utils.js.map +1 -1
  32. package/dist/es/matchers-jestonly.js.map +1 -1
  33. package/dist/es/matchers.js.map +1 -1
  34. package/dist/es/meta-helper.js.map +1 -1
  35. package/dist/es/utils/natural-join.js.map +1 -1
  36. package/dist/es/vitest.js.map +1 -1
  37. package/dist/schema/elements.json +6 -0
  38. package/dist/types/browser.d.ts +16 -0
  39. package/dist/types/index.d.ts +16 -0
  40. package/dist/types/vitest.d.ts +1 -0
  41. package/elements/html5.d.ts +1 -1
  42. package/package.json +1 -1
package/dist/es/core.js CHANGED
@@ -657,6 +657,11 @@ const patternProperties = {
657
657
  }
658
658
  ]
659
659
  },
660
+ templateRoot: {
661
+ title: "Mark element as an element ignoring DOM ancestry, i.e. <template>.",
662
+ description: "The <template> element can contain any elements.",
663
+ type: "boolean"
664
+ },
660
665
  deprecatedAttributes: {
661
666
  title: "List of deprecated attributes",
662
667
  type: "array",
@@ -1098,6 +1103,7 @@ function migrateElement(src) {
1098
1103
  textContent: src.textContent,
1099
1104
  focusable: src.focusable ?? false,
1100
1105
  implicitRole,
1106
+ templateRoot: src.templateRoot === true,
1101
1107
  aria: {
1102
1108
  implicitRole,
1103
1109
  naming: normalizeAriaNaming(src.aria?.naming)
@@ -4104,7 +4110,7 @@ class Rule {
4104
4110
  }
4105
4111
  }
4106
4112
 
4107
- const defaults$x = {
4113
+ const defaults$y = {
4108
4114
  allowExternal: true,
4109
4115
  allowRelative: true,
4110
4116
  allowAbsolute: true,
@@ -4148,7 +4154,7 @@ class AllowedLinks extends Rule {
4148
4154
  allowRelative;
4149
4155
  allowAbsolute;
4150
4156
  constructor(options) {
4151
- super({ ...defaults$x, ...options });
4157
+ super({ ...defaults$y, ...options });
4152
4158
  this.allowExternal = parseAllow(this.options.allowExternal);
4153
4159
  this.allowRelative = parseAllow(this.options.allowRelative);
4154
4160
  this.allowAbsolute = parseAllow(this.options.allowAbsolute);
@@ -4316,7 +4322,7 @@ class AllowedLinks extends Rule {
4316
4322
  }
4317
4323
  }
4318
4324
 
4319
- const defaults$w = {
4325
+ const defaults$x = {
4320
4326
  accessible: true
4321
4327
  };
4322
4328
  function findByTarget(target, siblings) {
@@ -4346,7 +4352,7 @@ function getDescription$1(context) {
4346
4352
  }
4347
4353
  class AreaAlt extends Rule {
4348
4354
  constructor(options) {
4349
- super({ ...defaults$w, ...options });
4355
+ super({ ...defaults$x, ...options });
4350
4356
  }
4351
4357
  static schema() {
4352
4358
  return {
@@ -4425,7 +4431,7 @@ class AriaHiddenBody extends Rule {
4425
4431
  }
4426
4432
  }
4427
4433
 
4428
- const defaults$v = {
4434
+ const defaults$w = {
4429
4435
  allowAnyNamable: false
4430
4436
  };
4431
4437
  const whitelisted = [
@@ -4439,6 +4445,7 @@ const whitelisted = [
4439
4445
  "footer",
4440
4446
  "section",
4441
4447
  "article",
4448
+ "dialog",
4442
4449
  "form",
4443
4450
  "img",
4444
4451
  "area",
@@ -4467,7 +4474,7 @@ function isValidUsage(target, meta) {
4467
4474
  }
4468
4475
  class AriaLabelMisuse extends Rule {
4469
4476
  constructor(options) {
4470
- super({ ...defaults$v, ...options });
4477
+ super({ ...defaults$w, ...options });
4471
4478
  }
4472
4479
  documentation() {
4473
4480
  const valid = [
@@ -4476,6 +4483,7 @@ class AriaLabelMisuse extends Rule {
4476
4483
  "Landmark elements",
4477
4484
  "Elements with roles inheriting from widget",
4478
4485
  "`<area>`",
4486
+ "`<dialog>`",
4479
4487
  "`<form>` and `<fieldset>`",
4480
4488
  "`<iframe>`",
4481
4489
  "`<img>` and `<figure>`",
@@ -4578,14 +4586,14 @@ class CaseStyle {
4578
4586
  }
4579
4587
  }
4580
4588
 
4581
- const defaults$u = {
4589
+ const defaults$v = {
4582
4590
  style: "lowercase",
4583
4591
  ignoreForeign: true
4584
4592
  };
4585
4593
  class AttrCase extends Rule {
4586
4594
  style;
4587
4595
  constructor(options) {
4588
- super({ ...defaults$u, ...options });
4596
+ super({ ...defaults$v, ...options });
4589
4597
  this.style = new CaseStyle(this.options.style, "attr-case");
4590
4598
  }
4591
4599
  static schema() {
@@ -4943,7 +4951,7 @@ class AttrDelimiter extends Rule {
4943
4951
  }
4944
4952
 
4945
4953
  const DEFAULT_PATTERN = "[a-z0-9-:]+";
4946
- const defaults$t = {
4954
+ const defaults$u = {
4947
4955
  pattern: DEFAULT_PATTERN,
4948
4956
  ignoreForeign: true
4949
4957
  };
@@ -4976,7 +4984,7 @@ function generateDescription(name, pattern) {
4976
4984
  class AttrPattern extends Rule {
4977
4985
  pattern;
4978
4986
  constructor(options) {
4979
- super({ ...defaults$t, ...options });
4987
+ super({ ...defaults$u, ...options });
4980
4988
  this.pattern = generateRegexp(this.options.pattern);
4981
4989
  }
4982
4990
  static schema() {
@@ -5023,7 +5031,7 @@ class AttrPattern extends Rule {
5023
5031
  }
5024
5032
  }
5025
5033
 
5026
- const defaults$s = {
5034
+ const defaults$t = {
5027
5035
  style: "auto",
5028
5036
  unquoted: false
5029
5037
  };
@@ -5089,7 +5097,7 @@ class AttrQuotes extends Rule {
5089
5097
  };
5090
5098
  }
5091
5099
  constructor(options) {
5092
- super({ ...defaults$s, ...options });
5100
+ super({ ...defaults$t, ...options });
5093
5101
  this.style = parseStyle$3(this.options.style);
5094
5102
  }
5095
5103
  setup() {
@@ -5246,13 +5254,13 @@ class AttributeAllowedValues extends Rule {
5246
5254
  }
5247
5255
  }
5248
5256
 
5249
- const defaults$r = {
5257
+ const defaults$s = {
5250
5258
  style: "omit"
5251
5259
  };
5252
5260
  class AttributeBooleanStyle extends Rule {
5253
5261
  hasInvalidStyle;
5254
5262
  constructor(options) {
5255
- super({ ...defaults$r, ...options });
5263
+ super({ ...defaults$s, ...options });
5256
5264
  this.hasInvalidStyle = parseStyle$2(this.options.style);
5257
5265
  }
5258
5266
  static schema() {
@@ -5318,13 +5326,13 @@ function reportMessage$1(attr, style) {
5318
5326
  return "";
5319
5327
  }
5320
5328
 
5321
- const defaults$q = {
5329
+ const defaults$r = {
5322
5330
  style: "omit"
5323
5331
  };
5324
5332
  class AttributeEmptyStyle extends Rule {
5325
5333
  hasInvalidStyle;
5326
5334
  constructor(options) {
5327
- super({ ...defaults$q, ...options });
5335
+ super({ ...defaults$r, ...options });
5328
5336
  this.hasInvalidStyle = parseStyle$1(this.options.style);
5329
5337
  }
5330
5338
  static schema() {
@@ -5519,12 +5527,12 @@ class BasePatternRule extends Rule {
5519
5527
  }
5520
5528
  }
5521
5529
 
5522
- const defaults$p = {
5530
+ const defaults$q = {
5523
5531
  pattern: "kebabcase"
5524
5532
  };
5525
5533
  class ClassPattern extends BasePatternRule {
5526
5534
  constructor(options) {
5527
- super("class", { ...defaults$p, ...options });
5535
+ super("class", { ...defaults$q, ...options });
5528
5536
  }
5529
5537
  static schema() {
5530
5538
  return BasePatternRule.schema();
@@ -5671,13 +5679,13 @@ class CloseOrder extends Rule {
5671
5679
  }
5672
5680
  }
5673
5681
 
5674
- const defaults$o = {
5682
+ const defaults$p = {
5675
5683
  include: null,
5676
5684
  exclude: null
5677
5685
  };
5678
5686
  class Deprecated extends Rule {
5679
5687
  constructor(options) {
5680
- super({ ...defaults$o, ...options });
5688
+ super({ ...defaults$p, ...options });
5681
5689
  }
5682
5690
  static schema() {
5683
5691
  return {
@@ -5831,12 +5839,12 @@ let NoStyleTag$1 = class NoStyleTag extends Rule {
5831
5839
  }
5832
5840
  };
5833
5841
 
5834
- const defaults$n = {
5842
+ const defaults$o = {
5835
5843
  style: "uppercase"
5836
5844
  };
5837
5845
  class DoctypeStyle extends Rule {
5838
5846
  constructor(options) {
5839
- super({ ...defaults$n, ...options });
5847
+ super({ ...defaults$o, ...options });
5840
5848
  }
5841
5849
  static schema() {
5842
5850
  return {
@@ -5864,13 +5872,13 @@ class DoctypeStyle extends Rule {
5864
5872
  }
5865
5873
  }
5866
5874
 
5867
- const defaults$m = {
5875
+ const defaults$n = {
5868
5876
  style: "lowercase"
5869
5877
  };
5870
5878
  class ElementCase extends Rule {
5871
5879
  style;
5872
5880
  constructor(options) {
5873
- super({ ...defaults$m, ...options });
5881
+ super({ ...defaults$n, ...options });
5874
5882
  this.style = new CaseStyle(this.options.style, "element-case");
5875
5883
  }
5876
5884
  static schema() {
@@ -5930,7 +5938,7 @@ class ElementCase extends Rule {
5930
5938
  }
5931
5939
  }
5932
5940
 
5933
- const defaults$l = {
5941
+ const defaults$m = {
5934
5942
  pattern: "^[a-z][a-z0-9\\-._]*-[a-z0-9\\-._]*$",
5935
5943
  whitelist: [],
5936
5944
  blacklist: []
@@ -5938,7 +5946,7 @@ const defaults$l = {
5938
5946
  class ElementName extends Rule {
5939
5947
  pattern;
5940
5948
  constructor(options) {
5941
- super({ ...defaults$l, ...options });
5949
+ super({ ...defaults$m, ...options });
5942
5950
  this.pattern = new RegExp(this.options.pattern);
5943
5951
  }
5944
5952
  static schema() {
@@ -5975,7 +5983,7 @@ class ElementName extends Rule {
5975
5983
  ...context.blacklist.map((cur) => `- ${cur}`)
5976
5984
  ];
5977
5985
  }
5978
- if (context.pattern !== defaults$l.pattern) {
5986
+ if (context.pattern !== defaults$m.pattern) {
5979
5987
  return [
5980
5988
  `<${context.tagName}> is not a valid element name. This project is configured to only allow names matching the following regular expression:`,
5981
5989
  "",
@@ -6021,7 +6029,8 @@ class ElementName extends Rule {
6021
6029
  }
6022
6030
 
6023
6031
  function isNativeTemplate(node) {
6024
- return Boolean(node.tagName === "template" && node.meta?.scriptSupporting);
6032
+ const { tagName, meta } = node;
6033
+ return Boolean(tagName === "template" && meta?.templateRoot && meta?.scriptSupporting);
6025
6034
  }
6026
6035
  function getTransparentChildren(node, transparent) {
6027
6036
  if (typeof transparent === "boolean") {
@@ -6464,7 +6473,7 @@ class EmptyTitle extends Rule {
6464
6473
  }
6465
6474
  }
6466
6475
 
6467
- const defaults$k = {
6476
+ const defaults$l = {
6468
6477
  allowArrayBrackets: true,
6469
6478
  allowCheckboxDefault: true,
6470
6479
  shared: ["radio", "button", "reset", "submit"]
@@ -6512,7 +6521,7 @@ function getDocumentation(context) {
6512
6521
  }
6513
6522
  class FormDupName extends Rule {
6514
6523
  constructor(options) {
6515
- super({ ...defaults$k, ...options });
6524
+ super({ ...defaults$l, ...options });
6516
6525
  }
6517
6526
  static schema() {
6518
6527
  return {
@@ -6671,7 +6680,7 @@ class FormDupName extends Rule {
6671
6680
  }
6672
6681
  }
6673
6682
 
6674
- const defaults$j = {
6683
+ const defaults$k = {
6675
6684
  allowMultipleH1: false,
6676
6685
  minInitialRank: "h1",
6677
6686
  sectioningRoots: ["dialog", '[role="dialog"]', '[role="alertdialog"]']
@@ -6703,7 +6712,7 @@ class HeadingLevel extends Rule {
6703
6712
  sectionRoots;
6704
6713
  stack = [];
6705
6714
  constructor(options) {
6706
- super({ ...defaults$j, ...options });
6715
+ super({ ...defaults$k, ...options });
6707
6716
  this.minInitialRank = parseMaxInitial(this.options.minInitialRank);
6708
6717
  this.sectionRoots = this.options.sectioningRoots.map((it) => new Compound(it));
6709
6718
  this.stack.push({
@@ -6940,12 +6949,12 @@ class HiddenFocusable extends Rule {
6940
6949
  }
6941
6950
  }
6942
6951
 
6943
- const defaults$i = {
6952
+ const defaults$j = {
6944
6953
  pattern: "kebabcase"
6945
6954
  };
6946
6955
  class IdPattern extends BasePatternRule {
6947
6956
  constructor(options) {
6948
- super("id", { ...defaults$i, ...options });
6957
+ super("id", { ...defaults$j, ...options });
6949
6958
  }
6950
6959
  static schema() {
6951
6960
  return BasePatternRule.schema();
@@ -7263,13 +7272,13 @@ function findLabelByParent(el) {
7263
7272
  return [];
7264
7273
  }
7265
7274
 
7266
- const defaults$h = {
7275
+ const defaults$i = {
7267
7276
  maxlength: 70
7268
7277
  };
7269
7278
  class LongTitle extends Rule {
7270
7279
  maxlength;
7271
7280
  constructor(options) {
7272
- super({ ...defaults$h, ...options });
7281
+ super({ ...defaults$i, ...options });
7273
7282
  this.maxlength = this.options.maxlength;
7274
7283
  }
7275
7284
  static schema() {
@@ -7297,12 +7306,12 @@ class LongTitle extends Rule {
7297
7306
  }
7298
7307
  }
7299
7308
 
7300
- const defaults$g = {
7309
+ const defaults$h = {
7301
7310
  allowLongDelay: false
7302
7311
  };
7303
7312
  class MetaRefresh extends Rule {
7304
7313
  constructor(options) {
7305
- super({ ...defaults$g, ...options });
7314
+ super({ ...defaults$h, ...options });
7306
7315
  }
7307
7316
  documentation() {
7308
7317
  return {
@@ -7487,12 +7496,12 @@ class MultipleLabeledControls extends Rule {
7487
7496
  }
7488
7497
  }
7489
7498
 
7490
- const defaults$f = {
7499
+ const defaults$g = {
7491
7500
  pattern: "camelcase"
7492
7501
  };
7493
7502
  class NamePattern extends BasePatternRule {
7494
7503
  constructor(options) {
7495
- super("name", { ...defaults$f, ...options });
7504
+ super("name", { ...defaults$g, ...options });
7496
7505
  }
7497
7506
  static schema() {
7498
7507
  return BasePatternRule.schema();
@@ -7581,13 +7590,13 @@ class NoAbstractRole extends Rule {
7581
7590
  }
7582
7591
  }
7583
7592
 
7584
- const defaults$e = {
7593
+ const defaults$f = {
7585
7594
  include: null,
7586
7595
  exclude: null
7587
7596
  };
7588
7597
  class NoAutoplay extends Rule {
7589
7598
  constructor(options) {
7590
- super({ ...defaults$e, ...options });
7599
+ super({ ...defaults$f, ...options });
7591
7600
  }
7592
7601
  documentation(context) {
7593
7602
  return {
@@ -7906,14 +7915,14 @@ Omitted end tags can be ambigious for humans to read and many editors have troub
7906
7915
  }
7907
7916
  }
7908
7917
 
7909
- const defaults$d = {
7918
+ const defaults$e = {
7910
7919
  include: null,
7911
7920
  exclude: null,
7912
7921
  allowedProperties: ["display"]
7913
7922
  };
7914
7923
  class NoInlineStyle extends Rule {
7915
7924
  constructor(options) {
7916
- super({ ...defaults$d, ...options });
7925
+ super({ ...defaults$e, ...options });
7917
7926
  }
7918
7927
  static schema() {
7919
7928
  return {
@@ -8099,7 +8108,7 @@ class NoMultipleMain extends Rule {
8099
8108
  }
8100
8109
  }
8101
8110
 
8102
- const defaults$c = {
8111
+ const defaults$d = {
8103
8112
  relaxed: false
8104
8113
  };
8105
8114
  const textRegexp = /([<>]|&(?![a-zA-Z0-9#]+;))/g;
@@ -8117,7 +8126,7 @@ const replacementTable = {
8117
8126
  class NoRawCharacters extends Rule {
8118
8127
  relaxed;
8119
8128
  constructor(options) {
8120
- super({ ...defaults$c, ...options });
8129
+ super({ ...defaults$d, ...options });
8121
8130
  this.relaxed = this.options.relaxed;
8122
8131
  }
8123
8132
  static schema() {
@@ -8147,12 +8156,16 @@ class NoRawCharacters extends Rule {
8147
8156
  }
8148
8157
  });
8149
8158
  this.on("attr", (event) => {
8159
+ const { meta } = event;
8150
8160
  if (!event.value) {
8151
8161
  return;
8152
8162
  }
8153
8163
  if (event.quote) {
8154
8164
  return;
8155
8165
  }
8166
+ if (meta?.boolean) {
8167
+ return;
8168
+ }
8156
8169
  this.findRawChars(
8157
8170
  event.target,
8158
8171
  event.value.toString(),
@@ -8290,13 +8303,13 @@ class NoRedundantRole extends Rule {
8290
8303
  }
8291
8304
 
8292
8305
  const xmlns = /^(.+):.+$/;
8293
- const defaults$b = {
8306
+ const defaults$c = {
8294
8307
  ignoreForeign: true,
8295
8308
  ignoreXML: true
8296
8309
  };
8297
8310
  class NoSelfClosing extends Rule {
8298
8311
  constructor(options) {
8299
- super({ ...defaults$b, ...options });
8312
+ super({ ...defaults$c, ...options });
8300
8313
  }
8301
8314
  static schema() {
8302
8315
  return {
@@ -8380,13 +8393,13 @@ class NoTrailingWhitespace extends Rule {
8380
8393
  }
8381
8394
  }
8382
8395
 
8383
- const defaults$a = {
8396
+ const defaults$b = {
8384
8397
  include: null,
8385
8398
  exclude: null
8386
8399
  };
8387
8400
  class NoUnknownElements extends Rule {
8388
8401
  constructor(options) {
8389
- super({ ...defaults$a, ...options });
8402
+ super({ ...defaults$b, ...options });
8390
8403
  }
8391
8404
  static schema() {
8392
8405
  return {
@@ -8492,13 +8505,13 @@ const replacement = {
8492
8505
  reset: '<button type="reset">',
8493
8506
  image: '<button type="button">'
8494
8507
  };
8495
- const defaults$9 = {
8508
+ const defaults$a = {
8496
8509
  include: null,
8497
8510
  exclude: null
8498
8511
  };
8499
8512
  class PreferButton extends Rule {
8500
8513
  constructor(options) {
8501
- super({ ...defaults$9, ...options });
8514
+ super({ ...defaults$a, ...options });
8502
8515
  }
8503
8516
  static schema() {
8504
8517
  return {
@@ -8564,7 +8577,7 @@ class PreferButton extends Rule {
8564
8577
  }
8565
8578
  }
8566
8579
 
8567
- const defaults$8 = {
8580
+ const defaults$9 = {
8568
8581
  mapping: {
8569
8582
  article: "article",
8570
8583
  banner: "header",
@@ -8594,7 +8607,7 @@ const defaults$8 = {
8594
8607
  };
8595
8608
  class PreferNativeElement extends Rule {
8596
8609
  constructor(options) {
8597
- super({ ...defaults$8, ...options });
8610
+ super({ ...defaults$9, ...options });
8598
8611
  }
8599
8612
  static schema() {
8600
8613
  return {
@@ -8708,12 +8721,12 @@ class PreferTbody extends Rule {
8708
8721
  }
8709
8722
  }
8710
8723
 
8711
- const defaults$7 = {
8724
+ const defaults$8 = {
8712
8725
  tags: ["script", "style"]
8713
8726
  };
8714
8727
  class RequireCSPNonce extends Rule {
8715
8728
  constructor(options) {
8716
- super({ ...defaults$7, ...options });
8729
+ super({ ...defaults$8, ...options });
8717
8730
  }
8718
8731
  static schema() {
8719
8732
  return {
@@ -8760,7 +8773,7 @@ class RequireCSPNonce extends Rule {
8760
8773
  }
8761
8774
  }
8762
8775
 
8763
- const defaults$6 = {
8776
+ const defaults$7 = {
8764
8777
  target: "all",
8765
8778
  include: null,
8766
8779
  exclude: null
@@ -8789,7 +8802,7 @@ function linkSupportsSri(node) {
8789
8802
  class RequireSri extends Rule {
8790
8803
  target;
8791
8804
  constructor(options) {
8792
- super({ ...defaults$6, ...options });
8805
+ super({ ...defaults$7, ...options });
8793
8806
  this.target = this.options.target;
8794
8807
  }
8795
8808
  static schema() {
@@ -8962,7 +8975,7 @@ class SvgFocusable extends Rule {
8962
8975
  }
8963
8976
  }
8964
8977
 
8965
- const defaults$5 = {
8978
+ const defaults$6 = {
8966
8979
  characters: [
8967
8980
  { pattern: " ", replacement: "&nbsp;", description: "non-breaking space" },
8968
8981
  { pattern: "-", replacement: "&#8209;", description: "non-breaking hyphen" }
@@ -8994,7 +9007,7 @@ function matchAll(text, regexp) {
8994
9007
  class TelNonBreaking extends Rule {
8995
9008
  regex;
8996
9009
  constructor(options) {
8997
- super({ ...defaults$5, ...options });
9010
+ super({ ...defaults$6, ...options });
8998
9011
  this.regex = constructRegex(this.options.characters);
8999
9012
  }
9000
9013
  static schema() {
@@ -9369,7 +9382,7 @@ class UniqueLandmark extends Rule {
9369
9382
  }
9370
9383
  }
9371
9384
 
9372
- const defaults$4 = {
9385
+ const defaults$5 = {
9373
9386
  ignoreCase: false,
9374
9387
  requireSemicolon: true
9375
9388
  };
@@ -9403,7 +9416,7 @@ function getDescription(context, options) {
9403
9416
  }
9404
9417
  class UnknownCharReference extends Rule {
9405
9418
  constructor(options) {
9406
- super({ ...defaults$4, ...options });
9419
+ super({ ...defaults$5, ...options });
9407
9420
  }
9408
9421
  static schema() {
9409
9422
  return {
@@ -10019,12 +10032,12 @@ class ValidAutocomplete extends Rule {
10019
10032
  }
10020
10033
  }
10021
10034
 
10022
- const defaults$3 = {
10035
+ const defaults$4 = {
10023
10036
  relaxed: false
10024
10037
  };
10025
10038
  class ValidID extends Rule {
10026
10039
  constructor(options) {
10027
- super({ ...defaults$3, ...options });
10040
+ super({ ...defaults$4, ...options });
10028
10041
  }
10029
10042
  static schema() {
10030
10043
  return {
@@ -10130,13 +10143,13 @@ class VoidContent extends Rule {
10130
10143
  }
10131
10144
  }
10132
10145
 
10133
- const defaults$2 = {
10146
+ const defaults$3 = {
10134
10147
  style: "omit"
10135
10148
  };
10136
10149
  class VoidStyle extends Rule {
10137
10150
  style;
10138
10151
  constructor(options) {
10139
- super({ ...defaults$2, ...options });
10152
+ super({ ...defaults$3, ...options });
10140
10153
  this.style = parseStyle(this.options.style);
10141
10154
  }
10142
10155
  static schema() {
@@ -10337,13 +10350,13 @@ class H36 extends Rule {
10337
10350
  }
10338
10351
  }
10339
10352
 
10340
- const defaults$1 = {
10353
+ const defaults$2 = {
10341
10354
  allowEmpty: true,
10342
10355
  alias: []
10343
10356
  };
10344
10357
  class H37 extends Rule {
10345
10358
  constructor(options) {
10346
- super({ ...defaults$1, ...options });
10359
+ super({ ...defaults$2, ...options });
10347
10360
  if (!Array.isArray(this.options.alias)) {
10348
10361
  this.options.alias = [this.options.alias];
10349
10362
  }
@@ -10405,9 +10418,62 @@ class H37 extends Rule {
10405
10418
  }
10406
10419
  }
10407
10420
 
10421
+ const defaults$1 = {
10422
+ strict: false
10423
+ };
10408
10424
  const { enum: validScopes } = html5.th.attributes?.scope;
10409
10425
  const joinedScopes = naturalJoin(validScopes);
10426
+ const td = 0;
10427
+ const th = 1;
10428
+ function getShape(cells) {
10429
+ const rows = cells.length;
10430
+ const cols = cells[0].length;
10431
+ return { rows, cols };
10432
+ }
10433
+ function isSimpleTable(table) {
10434
+ const haveHeadersAttr = table.querySelector("> tr > [headers], > tbody > tr > [headers]");
10435
+ if (haveHeadersAttr) {
10436
+ return false;
10437
+ }
10438
+ const rows = table.querySelectorAll("> tr, > thead > tr, > tbody > tr");
10439
+ if (rows.length === 0) {
10440
+ return false;
10441
+ }
10442
+ const cells = rows.map((tr) => tr.querySelectorAll("> *").map((el) => el.is("th") ? th : td));
10443
+ if (cells[0].length === 0) {
10444
+ return false;
10445
+ }
10446
+ const numColumns = cells[0].length;
10447
+ if (!cells.every((row) => row.length === numColumns)) {
10448
+ return false;
10449
+ }
10450
+ const shape = getShape(cells);
10451
+ const headersPerRow = cells.map((row) => row.reduce((sum, cell) => sum + cell, 0));
10452
+ const headersPerColumn = Array(shape.cols).fill(0).map((_, index) => {
10453
+ return cells.reduce((sum, it) => sum + it[index], 0);
10454
+ });
10455
+ const [firstRow, ...otherRows] = headersPerRow;
10456
+ if (firstRow === shape.cols && otherRows.every((row) => row === 0)) {
10457
+ return true;
10458
+ }
10459
+ const [firstCol, ...otherCols] = headersPerColumn;
10460
+ const haveThead = Boolean(table.querySelector("> thead"));
10461
+ if (firstCol === shape.rows && otherCols.every((col) => col === 0) && !haveThead) {
10462
+ return true;
10463
+ }
10464
+ return false;
10465
+ }
10410
10466
  class H63 extends Rule {
10467
+ constructor(options) {
10468
+ super({ ...defaults$1, ...options });
10469
+ }
10470
+ static schema() {
10471
+ return {
10472
+ strict: {
10473
+ type: "boolean"
10474
+ }
10475
+ };
10476
+ }
10411
10477
  documentation() {
10412
10478
  return {
10413
10479
  description: "H63: Using the scope attribute to associate header cells and data cells in data tables",
@@ -10415,23 +10481,31 @@ class H63 extends Rule {
10415
10481
  };
10416
10482
  }
10417
10483
  setup() {
10418
- this.on("tag:ready", (event) => {
10484
+ const { strict } = this.options;
10485
+ this.on("element:ready", (event) => {
10419
10486
  const node = event.target;
10420
- if (node.tagName !== "th") {
10487
+ if (!node.is("table")) {
10421
10488
  return;
10422
10489
  }
10423
- const scope = node.getAttribute("scope");
10490
+ if (strict || !isSimpleTable(node)) {
10491
+ this.validateTable(node);
10492
+ }
10493
+ });
10494
+ }
10495
+ validateTable(node) {
10496
+ for (const th2 of node.querySelectorAll("th")) {
10497
+ const scope = th2.getAttribute("scope");
10424
10498
  const value = scope?.value;
10425
10499
  if (value instanceof DynamicValue) {
10426
- return;
10500
+ continue;
10427
10501
  }
10428
10502
  if (value && validScopes.includes(value)) {
10429
- return;
10503
+ continue;
10430
10504
  }
10431
10505
  const message = `<th> element must have a valid scope attribute: ${joinedScopes}`;
10432
- const location = scope?.valueLocation ?? scope?.keyLocation ?? node.location;
10433
- this.report(node, message, location);
10434
- });
10506
+ const location = scope?.valueLocation ?? scope?.keyLocation ?? th2.location;
10507
+ this.report(th2, message, location);
10508
+ }
10435
10509
  }
10436
10510
  }
10437
10511
 
@@ -11723,7 +11797,7 @@ class EventHandler {
11723
11797
  }
11724
11798
 
11725
11799
  const name = "html-validate";
11726
- const version = "9.6.1";
11800
+ const version = "9.7.1";
11727
11801
  const bugs = "https://gitlab.com/html-validate/html-validate/issues/new";
11728
11802
 
11729
11803
  function freeze(src) {