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/cjs/core.js CHANGED
@@ -666,6 +666,11 @@ const patternProperties = {
666
666
  }
667
667
  ]
668
668
  },
669
+ templateRoot: {
670
+ title: "Mark element as an element ignoring DOM ancestry, i.e. <template>.",
671
+ description: "The <template> element can contain any elements.",
672
+ type: "boolean"
673
+ },
669
674
  deprecatedAttributes: {
670
675
  title: "List of deprecated attributes",
671
676
  type: "array",
@@ -1107,6 +1112,7 @@ function migrateElement(src) {
1107
1112
  textContent: src.textContent,
1108
1113
  focusable: src.focusable ?? false,
1109
1114
  implicitRole,
1115
+ templateRoot: src.templateRoot === true,
1110
1116
  aria: {
1111
1117
  implicitRole,
1112
1118
  naming: normalizeAriaNaming(src.aria?.naming)
@@ -4113,7 +4119,7 @@ class Rule {
4113
4119
  }
4114
4120
  }
4115
4121
 
4116
- const defaults$x = {
4122
+ const defaults$y = {
4117
4123
  allowExternal: true,
4118
4124
  allowRelative: true,
4119
4125
  allowAbsolute: true,
@@ -4157,7 +4163,7 @@ class AllowedLinks extends Rule {
4157
4163
  allowRelative;
4158
4164
  allowAbsolute;
4159
4165
  constructor(options) {
4160
- super({ ...defaults$x, ...options });
4166
+ super({ ...defaults$y, ...options });
4161
4167
  this.allowExternal = parseAllow(this.options.allowExternal);
4162
4168
  this.allowRelative = parseAllow(this.options.allowRelative);
4163
4169
  this.allowAbsolute = parseAllow(this.options.allowAbsolute);
@@ -4325,7 +4331,7 @@ class AllowedLinks extends Rule {
4325
4331
  }
4326
4332
  }
4327
4333
 
4328
- const defaults$w = {
4334
+ const defaults$x = {
4329
4335
  accessible: true
4330
4336
  };
4331
4337
  function findByTarget(target, siblings) {
@@ -4355,7 +4361,7 @@ function getDescription$1(context) {
4355
4361
  }
4356
4362
  class AreaAlt extends Rule {
4357
4363
  constructor(options) {
4358
- super({ ...defaults$w, ...options });
4364
+ super({ ...defaults$x, ...options });
4359
4365
  }
4360
4366
  static schema() {
4361
4367
  return {
@@ -4434,7 +4440,7 @@ class AriaHiddenBody extends Rule {
4434
4440
  }
4435
4441
  }
4436
4442
 
4437
- const defaults$v = {
4443
+ const defaults$w = {
4438
4444
  allowAnyNamable: false
4439
4445
  };
4440
4446
  const whitelisted = [
@@ -4448,6 +4454,7 @@ const whitelisted = [
4448
4454
  "footer",
4449
4455
  "section",
4450
4456
  "article",
4457
+ "dialog",
4451
4458
  "form",
4452
4459
  "img",
4453
4460
  "area",
@@ -4476,7 +4483,7 @@ function isValidUsage(target, meta) {
4476
4483
  }
4477
4484
  class AriaLabelMisuse extends Rule {
4478
4485
  constructor(options) {
4479
- super({ ...defaults$v, ...options });
4486
+ super({ ...defaults$w, ...options });
4480
4487
  }
4481
4488
  documentation() {
4482
4489
  const valid = [
@@ -4485,6 +4492,7 @@ class AriaLabelMisuse extends Rule {
4485
4492
  "Landmark elements",
4486
4493
  "Elements with roles inheriting from widget",
4487
4494
  "`<area>`",
4495
+ "`<dialog>`",
4488
4496
  "`<form>` and `<fieldset>`",
4489
4497
  "`<iframe>`",
4490
4498
  "`<img>` and `<figure>`",
@@ -4587,14 +4595,14 @@ class CaseStyle {
4587
4595
  }
4588
4596
  }
4589
4597
 
4590
- const defaults$u = {
4598
+ const defaults$v = {
4591
4599
  style: "lowercase",
4592
4600
  ignoreForeign: true
4593
4601
  };
4594
4602
  class AttrCase extends Rule {
4595
4603
  style;
4596
4604
  constructor(options) {
4597
- super({ ...defaults$u, ...options });
4605
+ super({ ...defaults$v, ...options });
4598
4606
  this.style = new CaseStyle(this.options.style, "attr-case");
4599
4607
  }
4600
4608
  static schema() {
@@ -4952,7 +4960,7 @@ class AttrDelimiter extends Rule {
4952
4960
  }
4953
4961
 
4954
4962
  const DEFAULT_PATTERN = "[a-z0-9-:]+";
4955
- const defaults$t = {
4963
+ const defaults$u = {
4956
4964
  pattern: DEFAULT_PATTERN,
4957
4965
  ignoreForeign: true
4958
4966
  };
@@ -4985,7 +4993,7 @@ function generateDescription(name, pattern) {
4985
4993
  class AttrPattern extends Rule {
4986
4994
  pattern;
4987
4995
  constructor(options) {
4988
- super({ ...defaults$t, ...options });
4996
+ super({ ...defaults$u, ...options });
4989
4997
  this.pattern = generateRegexp(this.options.pattern);
4990
4998
  }
4991
4999
  static schema() {
@@ -5032,7 +5040,7 @@ class AttrPattern extends Rule {
5032
5040
  }
5033
5041
  }
5034
5042
 
5035
- const defaults$s = {
5043
+ const defaults$t = {
5036
5044
  style: "auto",
5037
5045
  unquoted: false
5038
5046
  };
@@ -5098,7 +5106,7 @@ class AttrQuotes extends Rule {
5098
5106
  };
5099
5107
  }
5100
5108
  constructor(options) {
5101
- super({ ...defaults$s, ...options });
5109
+ super({ ...defaults$t, ...options });
5102
5110
  this.style = parseStyle$3(this.options.style);
5103
5111
  }
5104
5112
  setup() {
@@ -5255,13 +5263,13 @@ class AttributeAllowedValues extends Rule {
5255
5263
  }
5256
5264
  }
5257
5265
 
5258
- const defaults$r = {
5266
+ const defaults$s = {
5259
5267
  style: "omit"
5260
5268
  };
5261
5269
  class AttributeBooleanStyle extends Rule {
5262
5270
  hasInvalidStyle;
5263
5271
  constructor(options) {
5264
- super({ ...defaults$r, ...options });
5272
+ super({ ...defaults$s, ...options });
5265
5273
  this.hasInvalidStyle = parseStyle$2(this.options.style);
5266
5274
  }
5267
5275
  static schema() {
@@ -5327,13 +5335,13 @@ function reportMessage$1(attr, style) {
5327
5335
  return "";
5328
5336
  }
5329
5337
 
5330
- const defaults$q = {
5338
+ const defaults$r = {
5331
5339
  style: "omit"
5332
5340
  };
5333
5341
  class AttributeEmptyStyle extends Rule {
5334
5342
  hasInvalidStyle;
5335
5343
  constructor(options) {
5336
- super({ ...defaults$q, ...options });
5344
+ super({ ...defaults$r, ...options });
5337
5345
  this.hasInvalidStyle = parseStyle$1(this.options.style);
5338
5346
  }
5339
5347
  static schema() {
@@ -5528,12 +5536,12 @@ class BasePatternRule extends Rule {
5528
5536
  }
5529
5537
  }
5530
5538
 
5531
- const defaults$p = {
5539
+ const defaults$q = {
5532
5540
  pattern: "kebabcase"
5533
5541
  };
5534
5542
  class ClassPattern extends BasePatternRule {
5535
5543
  constructor(options) {
5536
- super("class", { ...defaults$p, ...options });
5544
+ super("class", { ...defaults$q, ...options });
5537
5545
  }
5538
5546
  static schema() {
5539
5547
  return BasePatternRule.schema();
@@ -5680,13 +5688,13 @@ class CloseOrder extends Rule {
5680
5688
  }
5681
5689
  }
5682
5690
 
5683
- const defaults$o = {
5691
+ const defaults$p = {
5684
5692
  include: null,
5685
5693
  exclude: null
5686
5694
  };
5687
5695
  class Deprecated extends Rule {
5688
5696
  constructor(options) {
5689
- super({ ...defaults$o, ...options });
5697
+ super({ ...defaults$p, ...options });
5690
5698
  }
5691
5699
  static schema() {
5692
5700
  return {
@@ -5840,12 +5848,12 @@ let NoStyleTag$1 = class NoStyleTag extends Rule {
5840
5848
  }
5841
5849
  };
5842
5850
 
5843
- const defaults$n = {
5851
+ const defaults$o = {
5844
5852
  style: "uppercase"
5845
5853
  };
5846
5854
  class DoctypeStyle extends Rule {
5847
5855
  constructor(options) {
5848
- super({ ...defaults$n, ...options });
5856
+ super({ ...defaults$o, ...options });
5849
5857
  }
5850
5858
  static schema() {
5851
5859
  return {
@@ -5873,13 +5881,13 @@ class DoctypeStyle extends Rule {
5873
5881
  }
5874
5882
  }
5875
5883
 
5876
- const defaults$m = {
5884
+ const defaults$n = {
5877
5885
  style: "lowercase"
5878
5886
  };
5879
5887
  class ElementCase extends Rule {
5880
5888
  style;
5881
5889
  constructor(options) {
5882
- super({ ...defaults$m, ...options });
5890
+ super({ ...defaults$n, ...options });
5883
5891
  this.style = new CaseStyle(this.options.style, "element-case");
5884
5892
  }
5885
5893
  static schema() {
@@ -5939,7 +5947,7 @@ class ElementCase extends Rule {
5939
5947
  }
5940
5948
  }
5941
5949
 
5942
- const defaults$l = {
5950
+ const defaults$m = {
5943
5951
  pattern: "^[a-z][a-z0-9\\-._]*-[a-z0-9\\-._]*$",
5944
5952
  whitelist: [],
5945
5953
  blacklist: []
@@ -5947,7 +5955,7 @@ const defaults$l = {
5947
5955
  class ElementName extends Rule {
5948
5956
  pattern;
5949
5957
  constructor(options) {
5950
- super({ ...defaults$l, ...options });
5958
+ super({ ...defaults$m, ...options });
5951
5959
  this.pattern = new RegExp(this.options.pattern);
5952
5960
  }
5953
5961
  static schema() {
@@ -5984,7 +5992,7 @@ class ElementName extends Rule {
5984
5992
  ...context.blacklist.map((cur) => `- ${cur}`)
5985
5993
  ];
5986
5994
  }
5987
- if (context.pattern !== defaults$l.pattern) {
5995
+ if (context.pattern !== defaults$m.pattern) {
5988
5996
  return [
5989
5997
  `<${context.tagName}> is not a valid element name. This project is configured to only allow names matching the following regular expression:`,
5990
5998
  "",
@@ -6030,7 +6038,8 @@ class ElementName extends Rule {
6030
6038
  }
6031
6039
 
6032
6040
  function isNativeTemplate(node) {
6033
- return Boolean(node.tagName === "template" && node.meta?.scriptSupporting);
6041
+ const { tagName, meta } = node;
6042
+ return Boolean(tagName === "template" && meta?.templateRoot && meta?.scriptSupporting);
6034
6043
  }
6035
6044
  function getTransparentChildren(node, transparent) {
6036
6045
  if (typeof transparent === "boolean") {
@@ -6473,7 +6482,7 @@ class EmptyTitle extends Rule {
6473
6482
  }
6474
6483
  }
6475
6484
 
6476
- const defaults$k = {
6485
+ const defaults$l = {
6477
6486
  allowArrayBrackets: true,
6478
6487
  allowCheckboxDefault: true,
6479
6488
  shared: ["radio", "button", "reset", "submit"]
@@ -6521,7 +6530,7 @@ function getDocumentation(context) {
6521
6530
  }
6522
6531
  class FormDupName extends Rule {
6523
6532
  constructor(options) {
6524
- super({ ...defaults$k, ...options });
6533
+ super({ ...defaults$l, ...options });
6525
6534
  }
6526
6535
  static schema() {
6527
6536
  return {
@@ -6680,7 +6689,7 @@ class FormDupName extends Rule {
6680
6689
  }
6681
6690
  }
6682
6691
 
6683
- const defaults$j = {
6692
+ const defaults$k = {
6684
6693
  allowMultipleH1: false,
6685
6694
  minInitialRank: "h1",
6686
6695
  sectioningRoots: ["dialog", '[role="dialog"]', '[role="alertdialog"]']
@@ -6712,7 +6721,7 @@ class HeadingLevel extends Rule {
6712
6721
  sectionRoots;
6713
6722
  stack = [];
6714
6723
  constructor(options) {
6715
- super({ ...defaults$j, ...options });
6724
+ super({ ...defaults$k, ...options });
6716
6725
  this.minInitialRank = parseMaxInitial(this.options.minInitialRank);
6717
6726
  this.sectionRoots = this.options.sectioningRoots.map((it) => new Compound(it));
6718
6727
  this.stack.push({
@@ -6949,12 +6958,12 @@ class HiddenFocusable extends Rule {
6949
6958
  }
6950
6959
  }
6951
6960
 
6952
- const defaults$i = {
6961
+ const defaults$j = {
6953
6962
  pattern: "kebabcase"
6954
6963
  };
6955
6964
  class IdPattern extends BasePatternRule {
6956
6965
  constructor(options) {
6957
- super("id", { ...defaults$i, ...options });
6966
+ super("id", { ...defaults$j, ...options });
6958
6967
  }
6959
6968
  static schema() {
6960
6969
  return BasePatternRule.schema();
@@ -7272,13 +7281,13 @@ function findLabelByParent(el) {
7272
7281
  return [];
7273
7282
  }
7274
7283
 
7275
- const defaults$h = {
7284
+ const defaults$i = {
7276
7285
  maxlength: 70
7277
7286
  };
7278
7287
  class LongTitle extends Rule {
7279
7288
  maxlength;
7280
7289
  constructor(options) {
7281
- super({ ...defaults$h, ...options });
7290
+ super({ ...defaults$i, ...options });
7282
7291
  this.maxlength = this.options.maxlength;
7283
7292
  }
7284
7293
  static schema() {
@@ -7306,12 +7315,12 @@ class LongTitle extends Rule {
7306
7315
  }
7307
7316
  }
7308
7317
 
7309
- const defaults$g = {
7318
+ const defaults$h = {
7310
7319
  allowLongDelay: false
7311
7320
  };
7312
7321
  class MetaRefresh extends Rule {
7313
7322
  constructor(options) {
7314
- super({ ...defaults$g, ...options });
7323
+ super({ ...defaults$h, ...options });
7315
7324
  }
7316
7325
  documentation() {
7317
7326
  return {
@@ -7496,12 +7505,12 @@ class MultipleLabeledControls extends Rule {
7496
7505
  }
7497
7506
  }
7498
7507
 
7499
- const defaults$f = {
7508
+ const defaults$g = {
7500
7509
  pattern: "camelcase"
7501
7510
  };
7502
7511
  class NamePattern extends BasePatternRule {
7503
7512
  constructor(options) {
7504
- super("name", { ...defaults$f, ...options });
7513
+ super("name", { ...defaults$g, ...options });
7505
7514
  }
7506
7515
  static schema() {
7507
7516
  return BasePatternRule.schema();
@@ -7590,13 +7599,13 @@ class NoAbstractRole extends Rule {
7590
7599
  }
7591
7600
  }
7592
7601
 
7593
- const defaults$e = {
7602
+ const defaults$f = {
7594
7603
  include: null,
7595
7604
  exclude: null
7596
7605
  };
7597
7606
  class NoAutoplay extends Rule {
7598
7607
  constructor(options) {
7599
- super({ ...defaults$e, ...options });
7608
+ super({ ...defaults$f, ...options });
7600
7609
  }
7601
7610
  documentation(context) {
7602
7611
  return {
@@ -7915,14 +7924,14 @@ Omitted end tags can be ambigious for humans to read and many editors have troub
7915
7924
  }
7916
7925
  }
7917
7926
 
7918
- const defaults$d = {
7927
+ const defaults$e = {
7919
7928
  include: null,
7920
7929
  exclude: null,
7921
7930
  allowedProperties: ["display"]
7922
7931
  };
7923
7932
  class NoInlineStyle extends Rule {
7924
7933
  constructor(options) {
7925
- super({ ...defaults$d, ...options });
7934
+ super({ ...defaults$e, ...options });
7926
7935
  }
7927
7936
  static schema() {
7928
7937
  return {
@@ -8108,7 +8117,7 @@ class NoMultipleMain extends Rule {
8108
8117
  }
8109
8118
  }
8110
8119
 
8111
- const defaults$c = {
8120
+ const defaults$d = {
8112
8121
  relaxed: false
8113
8122
  };
8114
8123
  const textRegexp = /([<>]|&(?![a-zA-Z0-9#]+;))/g;
@@ -8126,7 +8135,7 @@ const replacementTable = {
8126
8135
  class NoRawCharacters extends Rule {
8127
8136
  relaxed;
8128
8137
  constructor(options) {
8129
- super({ ...defaults$c, ...options });
8138
+ super({ ...defaults$d, ...options });
8130
8139
  this.relaxed = this.options.relaxed;
8131
8140
  }
8132
8141
  static schema() {
@@ -8156,12 +8165,16 @@ class NoRawCharacters extends Rule {
8156
8165
  }
8157
8166
  });
8158
8167
  this.on("attr", (event) => {
8168
+ const { meta } = event;
8159
8169
  if (!event.value) {
8160
8170
  return;
8161
8171
  }
8162
8172
  if (event.quote) {
8163
8173
  return;
8164
8174
  }
8175
+ if (meta?.boolean) {
8176
+ return;
8177
+ }
8165
8178
  this.findRawChars(
8166
8179
  event.target,
8167
8180
  event.value.toString(),
@@ -8299,13 +8312,13 @@ class NoRedundantRole extends Rule {
8299
8312
  }
8300
8313
 
8301
8314
  const xmlns = /^(.+):.+$/;
8302
- const defaults$b = {
8315
+ const defaults$c = {
8303
8316
  ignoreForeign: true,
8304
8317
  ignoreXML: true
8305
8318
  };
8306
8319
  class NoSelfClosing extends Rule {
8307
8320
  constructor(options) {
8308
- super({ ...defaults$b, ...options });
8321
+ super({ ...defaults$c, ...options });
8309
8322
  }
8310
8323
  static schema() {
8311
8324
  return {
@@ -8389,13 +8402,13 @@ class NoTrailingWhitespace extends Rule {
8389
8402
  }
8390
8403
  }
8391
8404
 
8392
- const defaults$a = {
8405
+ const defaults$b = {
8393
8406
  include: null,
8394
8407
  exclude: null
8395
8408
  };
8396
8409
  class NoUnknownElements extends Rule {
8397
8410
  constructor(options) {
8398
- super({ ...defaults$a, ...options });
8411
+ super({ ...defaults$b, ...options });
8399
8412
  }
8400
8413
  static schema() {
8401
8414
  return {
@@ -8501,13 +8514,13 @@ const replacement = {
8501
8514
  reset: '<button type="reset">',
8502
8515
  image: '<button type="button">'
8503
8516
  };
8504
- const defaults$9 = {
8517
+ const defaults$a = {
8505
8518
  include: null,
8506
8519
  exclude: null
8507
8520
  };
8508
8521
  class PreferButton extends Rule {
8509
8522
  constructor(options) {
8510
- super({ ...defaults$9, ...options });
8523
+ super({ ...defaults$a, ...options });
8511
8524
  }
8512
8525
  static schema() {
8513
8526
  return {
@@ -8573,7 +8586,7 @@ class PreferButton extends Rule {
8573
8586
  }
8574
8587
  }
8575
8588
 
8576
- const defaults$8 = {
8589
+ const defaults$9 = {
8577
8590
  mapping: {
8578
8591
  article: "article",
8579
8592
  banner: "header",
@@ -8603,7 +8616,7 @@ const defaults$8 = {
8603
8616
  };
8604
8617
  class PreferNativeElement extends Rule {
8605
8618
  constructor(options) {
8606
- super({ ...defaults$8, ...options });
8619
+ super({ ...defaults$9, ...options });
8607
8620
  }
8608
8621
  static schema() {
8609
8622
  return {
@@ -8717,12 +8730,12 @@ class PreferTbody extends Rule {
8717
8730
  }
8718
8731
  }
8719
8732
 
8720
- const defaults$7 = {
8733
+ const defaults$8 = {
8721
8734
  tags: ["script", "style"]
8722
8735
  };
8723
8736
  class RequireCSPNonce extends Rule {
8724
8737
  constructor(options) {
8725
- super({ ...defaults$7, ...options });
8738
+ super({ ...defaults$8, ...options });
8726
8739
  }
8727
8740
  static schema() {
8728
8741
  return {
@@ -8769,7 +8782,7 @@ class RequireCSPNonce extends Rule {
8769
8782
  }
8770
8783
  }
8771
8784
 
8772
- const defaults$6 = {
8785
+ const defaults$7 = {
8773
8786
  target: "all",
8774
8787
  include: null,
8775
8788
  exclude: null
@@ -8798,7 +8811,7 @@ function linkSupportsSri(node) {
8798
8811
  class RequireSri extends Rule {
8799
8812
  target;
8800
8813
  constructor(options) {
8801
- super({ ...defaults$6, ...options });
8814
+ super({ ...defaults$7, ...options });
8802
8815
  this.target = this.options.target;
8803
8816
  }
8804
8817
  static schema() {
@@ -8971,7 +8984,7 @@ class SvgFocusable extends Rule {
8971
8984
  }
8972
8985
  }
8973
8986
 
8974
- const defaults$5 = {
8987
+ const defaults$6 = {
8975
8988
  characters: [
8976
8989
  { pattern: " ", replacement: "&nbsp;", description: "non-breaking space" },
8977
8990
  { pattern: "-", replacement: "&#8209;", description: "non-breaking hyphen" }
@@ -9003,7 +9016,7 @@ function matchAll(text, regexp) {
9003
9016
  class TelNonBreaking extends Rule {
9004
9017
  regex;
9005
9018
  constructor(options) {
9006
- super({ ...defaults$5, ...options });
9019
+ super({ ...defaults$6, ...options });
9007
9020
  this.regex = constructRegex(this.options.characters);
9008
9021
  }
9009
9022
  static schema() {
@@ -9378,7 +9391,7 @@ class UniqueLandmark extends Rule {
9378
9391
  }
9379
9392
  }
9380
9393
 
9381
- const defaults$4 = {
9394
+ const defaults$5 = {
9382
9395
  ignoreCase: false,
9383
9396
  requireSemicolon: true
9384
9397
  };
@@ -9412,7 +9425,7 @@ function getDescription(context, options) {
9412
9425
  }
9413
9426
  class UnknownCharReference extends Rule {
9414
9427
  constructor(options) {
9415
- super({ ...defaults$4, ...options });
9428
+ super({ ...defaults$5, ...options });
9416
9429
  }
9417
9430
  static schema() {
9418
9431
  return {
@@ -10028,12 +10041,12 @@ class ValidAutocomplete extends Rule {
10028
10041
  }
10029
10042
  }
10030
10043
 
10031
- const defaults$3 = {
10044
+ const defaults$4 = {
10032
10045
  relaxed: false
10033
10046
  };
10034
10047
  class ValidID extends Rule {
10035
10048
  constructor(options) {
10036
- super({ ...defaults$3, ...options });
10049
+ super({ ...defaults$4, ...options });
10037
10050
  }
10038
10051
  static schema() {
10039
10052
  return {
@@ -10139,13 +10152,13 @@ class VoidContent extends Rule {
10139
10152
  }
10140
10153
  }
10141
10154
 
10142
- const defaults$2 = {
10155
+ const defaults$3 = {
10143
10156
  style: "omit"
10144
10157
  };
10145
10158
  class VoidStyle extends Rule {
10146
10159
  style;
10147
10160
  constructor(options) {
10148
- super({ ...defaults$2, ...options });
10161
+ super({ ...defaults$3, ...options });
10149
10162
  this.style = parseStyle(this.options.style);
10150
10163
  }
10151
10164
  static schema() {
@@ -10346,13 +10359,13 @@ class H36 extends Rule {
10346
10359
  }
10347
10360
  }
10348
10361
 
10349
- const defaults$1 = {
10362
+ const defaults$2 = {
10350
10363
  allowEmpty: true,
10351
10364
  alias: []
10352
10365
  };
10353
10366
  class H37 extends Rule {
10354
10367
  constructor(options) {
10355
- super({ ...defaults$1, ...options });
10368
+ super({ ...defaults$2, ...options });
10356
10369
  if (!Array.isArray(this.options.alias)) {
10357
10370
  this.options.alias = [this.options.alias];
10358
10371
  }
@@ -10414,9 +10427,62 @@ class H37 extends Rule {
10414
10427
  }
10415
10428
  }
10416
10429
 
10430
+ const defaults$1 = {
10431
+ strict: false
10432
+ };
10417
10433
  const { enum: validScopes } = elements.html5.th.attributes?.scope;
10418
10434
  const joinedScopes = utils_naturalJoin.naturalJoin(validScopes);
10435
+ const td = 0;
10436
+ const th = 1;
10437
+ function getShape(cells) {
10438
+ const rows = cells.length;
10439
+ const cols = cells[0].length;
10440
+ return { rows, cols };
10441
+ }
10442
+ function isSimpleTable(table) {
10443
+ const haveHeadersAttr = table.querySelector("> tr > [headers], > tbody > tr > [headers]");
10444
+ if (haveHeadersAttr) {
10445
+ return false;
10446
+ }
10447
+ const rows = table.querySelectorAll("> tr, > thead > tr, > tbody > tr");
10448
+ if (rows.length === 0) {
10449
+ return false;
10450
+ }
10451
+ const cells = rows.map((tr) => tr.querySelectorAll("> *").map((el) => el.is("th") ? th : td));
10452
+ if (cells[0].length === 0) {
10453
+ return false;
10454
+ }
10455
+ const numColumns = cells[0].length;
10456
+ if (!cells.every((row) => row.length === numColumns)) {
10457
+ return false;
10458
+ }
10459
+ const shape = getShape(cells);
10460
+ const headersPerRow = cells.map((row) => row.reduce((sum, cell) => sum + cell, 0));
10461
+ const headersPerColumn = Array(shape.cols).fill(0).map((_, index) => {
10462
+ return cells.reduce((sum, it) => sum + it[index], 0);
10463
+ });
10464
+ const [firstRow, ...otherRows] = headersPerRow;
10465
+ if (firstRow === shape.cols && otherRows.every((row) => row === 0)) {
10466
+ return true;
10467
+ }
10468
+ const [firstCol, ...otherCols] = headersPerColumn;
10469
+ const haveThead = Boolean(table.querySelector("> thead"));
10470
+ if (firstCol === shape.rows && otherCols.every((col) => col === 0) && !haveThead) {
10471
+ return true;
10472
+ }
10473
+ return false;
10474
+ }
10419
10475
  class H63 extends Rule {
10476
+ constructor(options) {
10477
+ super({ ...defaults$1, ...options });
10478
+ }
10479
+ static schema() {
10480
+ return {
10481
+ strict: {
10482
+ type: "boolean"
10483
+ }
10484
+ };
10485
+ }
10420
10486
  documentation() {
10421
10487
  return {
10422
10488
  description: "H63: Using the scope attribute to associate header cells and data cells in data tables",
@@ -10424,23 +10490,31 @@ class H63 extends Rule {
10424
10490
  };
10425
10491
  }
10426
10492
  setup() {
10427
- this.on("tag:ready", (event) => {
10493
+ const { strict } = this.options;
10494
+ this.on("element:ready", (event) => {
10428
10495
  const node = event.target;
10429
- if (node.tagName !== "th") {
10496
+ if (!node.is("table")) {
10430
10497
  return;
10431
10498
  }
10432
- const scope = node.getAttribute("scope");
10499
+ if (strict || !isSimpleTable(node)) {
10500
+ this.validateTable(node);
10501
+ }
10502
+ });
10503
+ }
10504
+ validateTable(node) {
10505
+ for (const th2 of node.querySelectorAll("th")) {
10506
+ const scope = th2.getAttribute("scope");
10433
10507
  const value = scope?.value;
10434
10508
  if (value instanceof DynamicValue) {
10435
- return;
10509
+ continue;
10436
10510
  }
10437
10511
  if (value && validScopes.includes(value)) {
10438
- return;
10512
+ continue;
10439
10513
  }
10440
10514
  const message = `<th> element must have a valid scope attribute: ${joinedScopes}`;
10441
- const location = scope?.valueLocation ?? scope?.keyLocation ?? node.location;
10442
- this.report(node, message, location);
10443
- });
10515
+ const location = scope?.valueLocation ?? scope?.keyLocation ?? th2.location;
10516
+ this.report(th2, message, location);
10517
+ }
10444
10518
  }
10445
10519
  }
10446
10520
 
@@ -11732,7 +11806,7 @@ class EventHandler {
11732
11806
  }
11733
11807
 
11734
11808
  const name = "html-validate";
11735
- const version = "9.6.1";
11809
+ const version = "9.7.1";
11736
11810
  const bugs = "https://gitlab.com/html-validate/html-validate/issues/new";
11737
11811
 
11738
11812
  function freeze(src) {