html-validate 9.6.1 → 9.7.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.
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 = [
@@ -4476,7 +4482,7 @@ function isValidUsage(target, meta) {
4476
4482
  }
4477
4483
  class AriaLabelMisuse extends Rule {
4478
4484
  constructor(options) {
4479
- super({ ...defaults$v, ...options });
4485
+ super({ ...defaults$w, ...options });
4480
4486
  }
4481
4487
  documentation() {
4482
4488
  const valid = [
@@ -4587,14 +4593,14 @@ class CaseStyle {
4587
4593
  }
4588
4594
  }
4589
4595
 
4590
- const defaults$u = {
4596
+ const defaults$v = {
4591
4597
  style: "lowercase",
4592
4598
  ignoreForeign: true
4593
4599
  };
4594
4600
  class AttrCase extends Rule {
4595
4601
  style;
4596
4602
  constructor(options) {
4597
- super({ ...defaults$u, ...options });
4603
+ super({ ...defaults$v, ...options });
4598
4604
  this.style = new CaseStyle(this.options.style, "attr-case");
4599
4605
  }
4600
4606
  static schema() {
@@ -4952,7 +4958,7 @@ class AttrDelimiter extends Rule {
4952
4958
  }
4953
4959
 
4954
4960
  const DEFAULT_PATTERN = "[a-z0-9-:]+";
4955
- const defaults$t = {
4961
+ const defaults$u = {
4956
4962
  pattern: DEFAULT_PATTERN,
4957
4963
  ignoreForeign: true
4958
4964
  };
@@ -4985,7 +4991,7 @@ function generateDescription(name, pattern) {
4985
4991
  class AttrPattern extends Rule {
4986
4992
  pattern;
4987
4993
  constructor(options) {
4988
- super({ ...defaults$t, ...options });
4994
+ super({ ...defaults$u, ...options });
4989
4995
  this.pattern = generateRegexp(this.options.pattern);
4990
4996
  }
4991
4997
  static schema() {
@@ -5032,7 +5038,7 @@ class AttrPattern extends Rule {
5032
5038
  }
5033
5039
  }
5034
5040
 
5035
- const defaults$s = {
5041
+ const defaults$t = {
5036
5042
  style: "auto",
5037
5043
  unquoted: false
5038
5044
  };
@@ -5098,7 +5104,7 @@ class AttrQuotes extends Rule {
5098
5104
  };
5099
5105
  }
5100
5106
  constructor(options) {
5101
- super({ ...defaults$s, ...options });
5107
+ super({ ...defaults$t, ...options });
5102
5108
  this.style = parseStyle$3(this.options.style);
5103
5109
  }
5104
5110
  setup() {
@@ -5255,13 +5261,13 @@ class AttributeAllowedValues extends Rule {
5255
5261
  }
5256
5262
  }
5257
5263
 
5258
- const defaults$r = {
5264
+ const defaults$s = {
5259
5265
  style: "omit"
5260
5266
  };
5261
5267
  class AttributeBooleanStyle extends Rule {
5262
5268
  hasInvalidStyle;
5263
5269
  constructor(options) {
5264
- super({ ...defaults$r, ...options });
5270
+ super({ ...defaults$s, ...options });
5265
5271
  this.hasInvalidStyle = parseStyle$2(this.options.style);
5266
5272
  }
5267
5273
  static schema() {
@@ -5327,13 +5333,13 @@ function reportMessage$1(attr, style) {
5327
5333
  return "";
5328
5334
  }
5329
5335
 
5330
- const defaults$q = {
5336
+ const defaults$r = {
5331
5337
  style: "omit"
5332
5338
  };
5333
5339
  class AttributeEmptyStyle extends Rule {
5334
5340
  hasInvalidStyle;
5335
5341
  constructor(options) {
5336
- super({ ...defaults$q, ...options });
5342
+ super({ ...defaults$r, ...options });
5337
5343
  this.hasInvalidStyle = parseStyle$1(this.options.style);
5338
5344
  }
5339
5345
  static schema() {
@@ -5528,12 +5534,12 @@ class BasePatternRule extends Rule {
5528
5534
  }
5529
5535
  }
5530
5536
 
5531
- const defaults$p = {
5537
+ const defaults$q = {
5532
5538
  pattern: "kebabcase"
5533
5539
  };
5534
5540
  class ClassPattern extends BasePatternRule {
5535
5541
  constructor(options) {
5536
- super("class", { ...defaults$p, ...options });
5542
+ super("class", { ...defaults$q, ...options });
5537
5543
  }
5538
5544
  static schema() {
5539
5545
  return BasePatternRule.schema();
@@ -5680,13 +5686,13 @@ class CloseOrder extends Rule {
5680
5686
  }
5681
5687
  }
5682
5688
 
5683
- const defaults$o = {
5689
+ const defaults$p = {
5684
5690
  include: null,
5685
5691
  exclude: null
5686
5692
  };
5687
5693
  class Deprecated extends Rule {
5688
5694
  constructor(options) {
5689
- super({ ...defaults$o, ...options });
5695
+ super({ ...defaults$p, ...options });
5690
5696
  }
5691
5697
  static schema() {
5692
5698
  return {
@@ -5840,12 +5846,12 @@ let NoStyleTag$1 = class NoStyleTag extends Rule {
5840
5846
  }
5841
5847
  };
5842
5848
 
5843
- const defaults$n = {
5849
+ const defaults$o = {
5844
5850
  style: "uppercase"
5845
5851
  };
5846
5852
  class DoctypeStyle extends Rule {
5847
5853
  constructor(options) {
5848
- super({ ...defaults$n, ...options });
5854
+ super({ ...defaults$o, ...options });
5849
5855
  }
5850
5856
  static schema() {
5851
5857
  return {
@@ -5873,13 +5879,13 @@ class DoctypeStyle extends Rule {
5873
5879
  }
5874
5880
  }
5875
5881
 
5876
- const defaults$m = {
5882
+ const defaults$n = {
5877
5883
  style: "lowercase"
5878
5884
  };
5879
5885
  class ElementCase extends Rule {
5880
5886
  style;
5881
5887
  constructor(options) {
5882
- super({ ...defaults$m, ...options });
5888
+ super({ ...defaults$n, ...options });
5883
5889
  this.style = new CaseStyle(this.options.style, "element-case");
5884
5890
  }
5885
5891
  static schema() {
@@ -5939,7 +5945,7 @@ class ElementCase extends Rule {
5939
5945
  }
5940
5946
  }
5941
5947
 
5942
- const defaults$l = {
5948
+ const defaults$m = {
5943
5949
  pattern: "^[a-z][a-z0-9\\-._]*-[a-z0-9\\-._]*$",
5944
5950
  whitelist: [],
5945
5951
  blacklist: []
@@ -5947,7 +5953,7 @@ const defaults$l = {
5947
5953
  class ElementName extends Rule {
5948
5954
  pattern;
5949
5955
  constructor(options) {
5950
- super({ ...defaults$l, ...options });
5956
+ super({ ...defaults$m, ...options });
5951
5957
  this.pattern = new RegExp(this.options.pattern);
5952
5958
  }
5953
5959
  static schema() {
@@ -5984,7 +5990,7 @@ class ElementName extends Rule {
5984
5990
  ...context.blacklist.map((cur) => `- ${cur}`)
5985
5991
  ];
5986
5992
  }
5987
- if (context.pattern !== defaults$l.pattern) {
5993
+ if (context.pattern !== defaults$m.pattern) {
5988
5994
  return [
5989
5995
  `<${context.tagName}> is not a valid element name. This project is configured to only allow names matching the following regular expression:`,
5990
5996
  "",
@@ -6030,7 +6036,8 @@ class ElementName extends Rule {
6030
6036
  }
6031
6037
 
6032
6038
  function isNativeTemplate(node) {
6033
- return Boolean(node.tagName === "template" && node.meta?.scriptSupporting);
6039
+ const { tagName, meta } = node;
6040
+ return Boolean(tagName === "template" && meta?.templateRoot && meta?.scriptSupporting);
6034
6041
  }
6035
6042
  function getTransparentChildren(node, transparent) {
6036
6043
  if (typeof transparent === "boolean") {
@@ -6473,7 +6480,7 @@ class EmptyTitle extends Rule {
6473
6480
  }
6474
6481
  }
6475
6482
 
6476
- const defaults$k = {
6483
+ const defaults$l = {
6477
6484
  allowArrayBrackets: true,
6478
6485
  allowCheckboxDefault: true,
6479
6486
  shared: ["radio", "button", "reset", "submit"]
@@ -6521,7 +6528,7 @@ function getDocumentation(context) {
6521
6528
  }
6522
6529
  class FormDupName extends Rule {
6523
6530
  constructor(options) {
6524
- super({ ...defaults$k, ...options });
6531
+ super({ ...defaults$l, ...options });
6525
6532
  }
6526
6533
  static schema() {
6527
6534
  return {
@@ -6680,7 +6687,7 @@ class FormDupName extends Rule {
6680
6687
  }
6681
6688
  }
6682
6689
 
6683
- const defaults$j = {
6690
+ const defaults$k = {
6684
6691
  allowMultipleH1: false,
6685
6692
  minInitialRank: "h1",
6686
6693
  sectioningRoots: ["dialog", '[role="dialog"]', '[role="alertdialog"]']
@@ -6712,7 +6719,7 @@ class HeadingLevel extends Rule {
6712
6719
  sectionRoots;
6713
6720
  stack = [];
6714
6721
  constructor(options) {
6715
- super({ ...defaults$j, ...options });
6722
+ super({ ...defaults$k, ...options });
6716
6723
  this.minInitialRank = parseMaxInitial(this.options.minInitialRank);
6717
6724
  this.sectionRoots = this.options.sectioningRoots.map((it) => new Compound(it));
6718
6725
  this.stack.push({
@@ -6949,12 +6956,12 @@ class HiddenFocusable extends Rule {
6949
6956
  }
6950
6957
  }
6951
6958
 
6952
- const defaults$i = {
6959
+ const defaults$j = {
6953
6960
  pattern: "kebabcase"
6954
6961
  };
6955
6962
  class IdPattern extends BasePatternRule {
6956
6963
  constructor(options) {
6957
- super("id", { ...defaults$i, ...options });
6964
+ super("id", { ...defaults$j, ...options });
6958
6965
  }
6959
6966
  static schema() {
6960
6967
  return BasePatternRule.schema();
@@ -7272,13 +7279,13 @@ function findLabelByParent(el) {
7272
7279
  return [];
7273
7280
  }
7274
7281
 
7275
- const defaults$h = {
7282
+ const defaults$i = {
7276
7283
  maxlength: 70
7277
7284
  };
7278
7285
  class LongTitle extends Rule {
7279
7286
  maxlength;
7280
7287
  constructor(options) {
7281
- super({ ...defaults$h, ...options });
7288
+ super({ ...defaults$i, ...options });
7282
7289
  this.maxlength = this.options.maxlength;
7283
7290
  }
7284
7291
  static schema() {
@@ -7306,12 +7313,12 @@ class LongTitle extends Rule {
7306
7313
  }
7307
7314
  }
7308
7315
 
7309
- const defaults$g = {
7316
+ const defaults$h = {
7310
7317
  allowLongDelay: false
7311
7318
  };
7312
7319
  class MetaRefresh extends Rule {
7313
7320
  constructor(options) {
7314
- super({ ...defaults$g, ...options });
7321
+ super({ ...defaults$h, ...options });
7315
7322
  }
7316
7323
  documentation() {
7317
7324
  return {
@@ -7496,12 +7503,12 @@ class MultipleLabeledControls extends Rule {
7496
7503
  }
7497
7504
  }
7498
7505
 
7499
- const defaults$f = {
7506
+ const defaults$g = {
7500
7507
  pattern: "camelcase"
7501
7508
  };
7502
7509
  class NamePattern extends BasePatternRule {
7503
7510
  constructor(options) {
7504
- super("name", { ...defaults$f, ...options });
7511
+ super("name", { ...defaults$g, ...options });
7505
7512
  }
7506
7513
  static schema() {
7507
7514
  return BasePatternRule.schema();
@@ -7590,13 +7597,13 @@ class NoAbstractRole extends Rule {
7590
7597
  }
7591
7598
  }
7592
7599
 
7593
- const defaults$e = {
7600
+ const defaults$f = {
7594
7601
  include: null,
7595
7602
  exclude: null
7596
7603
  };
7597
7604
  class NoAutoplay extends Rule {
7598
7605
  constructor(options) {
7599
- super({ ...defaults$e, ...options });
7606
+ super({ ...defaults$f, ...options });
7600
7607
  }
7601
7608
  documentation(context) {
7602
7609
  return {
@@ -7915,14 +7922,14 @@ Omitted end tags can be ambigious for humans to read and many editors have troub
7915
7922
  }
7916
7923
  }
7917
7924
 
7918
- const defaults$d = {
7925
+ const defaults$e = {
7919
7926
  include: null,
7920
7927
  exclude: null,
7921
7928
  allowedProperties: ["display"]
7922
7929
  };
7923
7930
  class NoInlineStyle extends Rule {
7924
7931
  constructor(options) {
7925
- super({ ...defaults$d, ...options });
7932
+ super({ ...defaults$e, ...options });
7926
7933
  }
7927
7934
  static schema() {
7928
7935
  return {
@@ -8108,7 +8115,7 @@ class NoMultipleMain extends Rule {
8108
8115
  }
8109
8116
  }
8110
8117
 
8111
- const defaults$c = {
8118
+ const defaults$d = {
8112
8119
  relaxed: false
8113
8120
  };
8114
8121
  const textRegexp = /([<>]|&(?![a-zA-Z0-9#]+;))/g;
@@ -8126,7 +8133,7 @@ const replacementTable = {
8126
8133
  class NoRawCharacters extends Rule {
8127
8134
  relaxed;
8128
8135
  constructor(options) {
8129
- super({ ...defaults$c, ...options });
8136
+ super({ ...defaults$d, ...options });
8130
8137
  this.relaxed = this.options.relaxed;
8131
8138
  }
8132
8139
  static schema() {
@@ -8299,13 +8306,13 @@ class NoRedundantRole extends Rule {
8299
8306
  }
8300
8307
 
8301
8308
  const xmlns = /^(.+):.+$/;
8302
- const defaults$b = {
8309
+ const defaults$c = {
8303
8310
  ignoreForeign: true,
8304
8311
  ignoreXML: true
8305
8312
  };
8306
8313
  class NoSelfClosing extends Rule {
8307
8314
  constructor(options) {
8308
- super({ ...defaults$b, ...options });
8315
+ super({ ...defaults$c, ...options });
8309
8316
  }
8310
8317
  static schema() {
8311
8318
  return {
@@ -8389,13 +8396,13 @@ class NoTrailingWhitespace extends Rule {
8389
8396
  }
8390
8397
  }
8391
8398
 
8392
- const defaults$a = {
8399
+ const defaults$b = {
8393
8400
  include: null,
8394
8401
  exclude: null
8395
8402
  };
8396
8403
  class NoUnknownElements extends Rule {
8397
8404
  constructor(options) {
8398
- super({ ...defaults$a, ...options });
8405
+ super({ ...defaults$b, ...options });
8399
8406
  }
8400
8407
  static schema() {
8401
8408
  return {
@@ -8501,13 +8508,13 @@ const replacement = {
8501
8508
  reset: '<button type="reset">',
8502
8509
  image: '<button type="button">'
8503
8510
  };
8504
- const defaults$9 = {
8511
+ const defaults$a = {
8505
8512
  include: null,
8506
8513
  exclude: null
8507
8514
  };
8508
8515
  class PreferButton extends Rule {
8509
8516
  constructor(options) {
8510
- super({ ...defaults$9, ...options });
8517
+ super({ ...defaults$a, ...options });
8511
8518
  }
8512
8519
  static schema() {
8513
8520
  return {
@@ -8573,7 +8580,7 @@ class PreferButton extends Rule {
8573
8580
  }
8574
8581
  }
8575
8582
 
8576
- const defaults$8 = {
8583
+ const defaults$9 = {
8577
8584
  mapping: {
8578
8585
  article: "article",
8579
8586
  banner: "header",
@@ -8603,7 +8610,7 @@ const defaults$8 = {
8603
8610
  };
8604
8611
  class PreferNativeElement extends Rule {
8605
8612
  constructor(options) {
8606
- super({ ...defaults$8, ...options });
8613
+ super({ ...defaults$9, ...options });
8607
8614
  }
8608
8615
  static schema() {
8609
8616
  return {
@@ -8717,12 +8724,12 @@ class PreferTbody extends Rule {
8717
8724
  }
8718
8725
  }
8719
8726
 
8720
- const defaults$7 = {
8727
+ const defaults$8 = {
8721
8728
  tags: ["script", "style"]
8722
8729
  };
8723
8730
  class RequireCSPNonce extends Rule {
8724
8731
  constructor(options) {
8725
- super({ ...defaults$7, ...options });
8732
+ super({ ...defaults$8, ...options });
8726
8733
  }
8727
8734
  static schema() {
8728
8735
  return {
@@ -8769,7 +8776,7 @@ class RequireCSPNonce extends Rule {
8769
8776
  }
8770
8777
  }
8771
8778
 
8772
- const defaults$6 = {
8779
+ const defaults$7 = {
8773
8780
  target: "all",
8774
8781
  include: null,
8775
8782
  exclude: null
@@ -8798,7 +8805,7 @@ function linkSupportsSri(node) {
8798
8805
  class RequireSri extends Rule {
8799
8806
  target;
8800
8807
  constructor(options) {
8801
- super({ ...defaults$6, ...options });
8808
+ super({ ...defaults$7, ...options });
8802
8809
  this.target = this.options.target;
8803
8810
  }
8804
8811
  static schema() {
@@ -8971,7 +8978,7 @@ class SvgFocusable extends Rule {
8971
8978
  }
8972
8979
  }
8973
8980
 
8974
- const defaults$5 = {
8981
+ const defaults$6 = {
8975
8982
  characters: [
8976
8983
  { pattern: " ", replacement: "&nbsp;", description: "non-breaking space" },
8977
8984
  { pattern: "-", replacement: "&#8209;", description: "non-breaking hyphen" }
@@ -9003,7 +9010,7 @@ function matchAll(text, regexp) {
9003
9010
  class TelNonBreaking extends Rule {
9004
9011
  regex;
9005
9012
  constructor(options) {
9006
- super({ ...defaults$5, ...options });
9013
+ super({ ...defaults$6, ...options });
9007
9014
  this.regex = constructRegex(this.options.characters);
9008
9015
  }
9009
9016
  static schema() {
@@ -9378,7 +9385,7 @@ class UniqueLandmark extends Rule {
9378
9385
  }
9379
9386
  }
9380
9387
 
9381
- const defaults$4 = {
9388
+ const defaults$5 = {
9382
9389
  ignoreCase: false,
9383
9390
  requireSemicolon: true
9384
9391
  };
@@ -9412,7 +9419,7 @@ function getDescription(context, options) {
9412
9419
  }
9413
9420
  class UnknownCharReference extends Rule {
9414
9421
  constructor(options) {
9415
- super({ ...defaults$4, ...options });
9422
+ super({ ...defaults$5, ...options });
9416
9423
  }
9417
9424
  static schema() {
9418
9425
  return {
@@ -10028,12 +10035,12 @@ class ValidAutocomplete extends Rule {
10028
10035
  }
10029
10036
  }
10030
10037
 
10031
- const defaults$3 = {
10038
+ const defaults$4 = {
10032
10039
  relaxed: false
10033
10040
  };
10034
10041
  class ValidID extends Rule {
10035
10042
  constructor(options) {
10036
- super({ ...defaults$3, ...options });
10043
+ super({ ...defaults$4, ...options });
10037
10044
  }
10038
10045
  static schema() {
10039
10046
  return {
@@ -10139,13 +10146,13 @@ class VoidContent extends Rule {
10139
10146
  }
10140
10147
  }
10141
10148
 
10142
- const defaults$2 = {
10149
+ const defaults$3 = {
10143
10150
  style: "omit"
10144
10151
  };
10145
10152
  class VoidStyle extends Rule {
10146
10153
  style;
10147
10154
  constructor(options) {
10148
- super({ ...defaults$2, ...options });
10155
+ super({ ...defaults$3, ...options });
10149
10156
  this.style = parseStyle(this.options.style);
10150
10157
  }
10151
10158
  static schema() {
@@ -10346,13 +10353,13 @@ class H36 extends Rule {
10346
10353
  }
10347
10354
  }
10348
10355
 
10349
- const defaults$1 = {
10356
+ const defaults$2 = {
10350
10357
  allowEmpty: true,
10351
10358
  alias: []
10352
10359
  };
10353
10360
  class H37 extends Rule {
10354
10361
  constructor(options) {
10355
- super({ ...defaults$1, ...options });
10362
+ super({ ...defaults$2, ...options });
10356
10363
  if (!Array.isArray(this.options.alias)) {
10357
10364
  this.options.alias = [this.options.alias];
10358
10365
  }
@@ -10414,9 +10421,62 @@ class H37 extends Rule {
10414
10421
  }
10415
10422
  }
10416
10423
 
10424
+ const defaults$1 = {
10425
+ strict: false
10426
+ };
10417
10427
  const { enum: validScopes } = elements.html5.th.attributes?.scope;
10418
10428
  const joinedScopes = utils_naturalJoin.naturalJoin(validScopes);
10429
+ const td = 0;
10430
+ const th = 1;
10431
+ function getShape(cells) {
10432
+ const rows = cells.length;
10433
+ const cols = cells[0].length;
10434
+ return { rows, cols };
10435
+ }
10436
+ function isSimpleTable(table) {
10437
+ const haveHeadersAttr = table.querySelector("> tr > [headers], > tbody > tr > [headers]");
10438
+ if (haveHeadersAttr) {
10439
+ return false;
10440
+ }
10441
+ const rows = table.querySelectorAll("> tr, > thead > tr, > tbody > tr");
10442
+ if (rows.length === 0) {
10443
+ return false;
10444
+ }
10445
+ const cells = rows.map((tr) => tr.querySelectorAll("> *").map((el) => el.is("th") ? th : td));
10446
+ if (cells[0].length === 0) {
10447
+ return false;
10448
+ }
10449
+ const numColumns = cells[0].length;
10450
+ if (!cells.every((row) => row.length === numColumns)) {
10451
+ return false;
10452
+ }
10453
+ const shape = getShape(cells);
10454
+ const headersPerRow = cells.map((row) => row.reduce((sum, cell) => sum + cell, 0));
10455
+ const headersPerColumn = Array(shape.cols).fill(0).map((_, index) => {
10456
+ return cells.reduce((sum, it) => sum + it[index], 0);
10457
+ });
10458
+ const [firstRow, ...otherRows] = headersPerRow;
10459
+ if (firstRow === shape.cols && otherRows.every((row) => row === 0)) {
10460
+ return true;
10461
+ }
10462
+ const [firstCol, ...otherCols] = headersPerColumn;
10463
+ const haveThead = Boolean(table.querySelector("> thead"));
10464
+ if (firstCol === shape.rows && otherCols.every((col) => col === 0) && !haveThead) {
10465
+ return true;
10466
+ }
10467
+ return false;
10468
+ }
10419
10469
  class H63 extends Rule {
10470
+ constructor(options) {
10471
+ super({ ...defaults$1, ...options });
10472
+ }
10473
+ static schema() {
10474
+ return {
10475
+ strict: {
10476
+ type: "boolean"
10477
+ }
10478
+ };
10479
+ }
10420
10480
  documentation() {
10421
10481
  return {
10422
10482
  description: "H63: Using the scope attribute to associate header cells and data cells in data tables",
@@ -10424,23 +10484,31 @@ class H63 extends Rule {
10424
10484
  };
10425
10485
  }
10426
10486
  setup() {
10427
- this.on("tag:ready", (event) => {
10487
+ const { strict } = this.options;
10488
+ this.on("element:ready", (event) => {
10428
10489
  const node = event.target;
10429
- if (node.tagName !== "th") {
10490
+ if (!node.is("table")) {
10430
10491
  return;
10431
10492
  }
10432
- const scope = node.getAttribute("scope");
10493
+ if (strict || !isSimpleTable(node)) {
10494
+ this.validateTable(node);
10495
+ }
10496
+ });
10497
+ }
10498
+ validateTable(node) {
10499
+ for (const th2 of node.querySelectorAll("th")) {
10500
+ const scope = th2.getAttribute("scope");
10433
10501
  const value = scope?.value;
10434
10502
  if (value instanceof DynamicValue) {
10435
- return;
10503
+ continue;
10436
10504
  }
10437
10505
  if (value && validScopes.includes(value)) {
10438
- return;
10506
+ continue;
10439
10507
  }
10440
10508
  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
- });
10509
+ const location = scope?.valueLocation ?? scope?.keyLocation ?? th2.location;
10510
+ this.report(th2, message, location);
10511
+ }
10444
10512
  }
10445
10513
  }
10446
10514
 
@@ -11732,7 +11800,7 @@ class EventHandler {
11732
11800
  }
11733
11801
 
11734
11802
  const name = "html-validate";
11735
- const version = "9.6.1";
11803
+ const version = "9.7.0";
11736
11804
  const bugs = "https://gitlab.com/html-validate/html-validate/issues/new";
11737
11805
 
11738
11806
  function freeze(src) {