html-validate 9.6.0 → 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/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 = [
@@ -4467,7 +4473,7 @@ function isValidUsage(target, meta) {
4467
4473
  }
4468
4474
  class AriaLabelMisuse extends Rule {
4469
4475
  constructor(options) {
4470
- super({ ...defaults$v, ...options });
4476
+ super({ ...defaults$w, ...options });
4471
4477
  }
4472
4478
  documentation() {
4473
4479
  const valid = [
@@ -4578,14 +4584,14 @@ class CaseStyle {
4578
4584
  }
4579
4585
  }
4580
4586
 
4581
- const defaults$u = {
4587
+ const defaults$v = {
4582
4588
  style: "lowercase",
4583
4589
  ignoreForeign: true
4584
4590
  };
4585
4591
  class AttrCase extends Rule {
4586
4592
  style;
4587
4593
  constructor(options) {
4588
- super({ ...defaults$u, ...options });
4594
+ super({ ...defaults$v, ...options });
4589
4595
  this.style = new CaseStyle(this.options.style, "attr-case");
4590
4596
  }
4591
4597
  static schema() {
@@ -4943,7 +4949,7 @@ class AttrDelimiter extends Rule {
4943
4949
  }
4944
4950
 
4945
4951
  const DEFAULT_PATTERN = "[a-z0-9-:]+";
4946
- const defaults$t = {
4952
+ const defaults$u = {
4947
4953
  pattern: DEFAULT_PATTERN,
4948
4954
  ignoreForeign: true
4949
4955
  };
@@ -4976,7 +4982,7 @@ function generateDescription(name, pattern) {
4976
4982
  class AttrPattern extends Rule {
4977
4983
  pattern;
4978
4984
  constructor(options) {
4979
- super({ ...defaults$t, ...options });
4985
+ super({ ...defaults$u, ...options });
4980
4986
  this.pattern = generateRegexp(this.options.pattern);
4981
4987
  }
4982
4988
  static schema() {
@@ -5023,7 +5029,7 @@ class AttrPattern extends Rule {
5023
5029
  }
5024
5030
  }
5025
5031
 
5026
- const defaults$s = {
5032
+ const defaults$t = {
5027
5033
  style: "auto",
5028
5034
  unquoted: false
5029
5035
  };
@@ -5089,7 +5095,7 @@ class AttrQuotes extends Rule {
5089
5095
  };
5090
5096
  }
5091
5097
  constructor(options) {
5092
- super({ ...defaults$s, ...options });
5098
+ super({ ...defaults$t, ...options });
5093
5099
  this.style = parseStyle$3(this.options.style);
5094
5100
  }
5095
5101
  setup() {
@@ -5246,13 +5252,13 @@ class AttributeAllowedValues extends Rule {
5246
5252
  }
5247
5253
  }
5248
5254
 
5249
- const defaults$r = {
5255
+ const defaults$s = {
5250
5256
  style: "omit"
5251
5257
  };
5252
5258
  class AttributeBooleanStyle extends Rule {
5253
5259
  hasInvalidStyle;
5254
5260
  constructor(options) {
5255
- super({ ...defaults$r, ...options });
5261
+ super({ ...defaults$s, ...options });
5256
5262
  this.hasInvalidStyle = parseStyle$2(this.options.style);
5257
5263
  }
5258
5264
  static schema() {
@@ -5318,13 +5324,13 @@ function reportMessage$1(attr, style) {
5318
5324
  return "";
5319
5325
  }
5320
5326
 
5321
- const defaults$q = {
5327
+ const defaults$r = {
5322
5328
  style: "omit"
5323
5329
  };
5324
5330
  class AttributeEmptyStyle extends Rule {
5325
5331
  hasInvalidStyle;
5326
5332
  constructor(options) {
5327
- super({ ...defaults$q, ...options });
5333
+ super({ ...defaults$r, ...options });
5328
5334
  this.hasInvalidStyle = parseStyle$1(this.options.style);
5329
5335
  }
5330
5336
  static schema() {
@@ -5519,12 +5525,12 @@ class BasePatternRule extends Rule {
5519
5525
  }
5520
5526
  }
5521
5527
 
5522
- const defaults$p = {
5528
+ const defaults$q = {
5523
5529
  pattern: "kebabcase"
5524
5530
  };
5525
5531
  class ClassPattern extends BasePatternRule {
5526
5532
  constructor(options) {
5527
- super("class", { ...defaults$p, ...options });
5533
+ super("class", { ...defaults$q, ...options });
5528
5534
  }
5529
5535
  static schema() {
5530
5536
  return BasePatternRule.schema();
@@ -5671,13 +5677,13 @@ class CloseOrder extends Rule {
5671
5677
  }
5672
5678
  }
5673
5679
 
5674
- const defaults$o = {
5680
+ const defaults$p = {
5675
5681
  include: null,
5676
5682
  exclude: null
5677
5683
  };
5678
5684
  class Deprecated extends Rule {
5679
5685
  constructor(options) {
5680
- super({ ...defaults$o, ...options });
5686
+ super({ ...defaults$p, ...options });
5681
5687
  }
5682
5688
  static schema() {
5683
5689
  return {
@@ -5831,12 +5837,12 @@ let NoStyleTag$1 = class NoStyleTag extends Rule {
5831
5837
  }
5832
5838
  };
5833
5839
 
5834
- const defaults$n = {
5840
+ const defaults$o = {
5835
5841
  style: "uppercase"
5836
5842
  };
5837
5843
  class DoctypeStyle extends Rule {
5838
5844
  constructor(options) {
5839
- super({ ...defaults$n, ...options });
5845
+ super({ ...defaults$o, ...options });
5840
5846
  }
5841
5847
  static schema() {
5842
5848
  return {
@@ -5864,13 +5870,13 @@ class DoctypeStyle extends Rule {
5864
5870
  }
5865
5871
  }
5866
5872
 
5867
- const defaults$m = {
5873
+ const defaults$n = {
5868
5874
  style: "lowercase"
5869
5875
  };
5870
5876
  class ElementCase extends Rule {
5871
5877
  style;
5872
5878
  constructor(options) {
5873
- super({ ...defaults$m, ...options });
5879
+ super({ ...defaults$n, ...options });
5874
5880
  this.style = new CaseStyle(this.options.style, "element-case");
5875
5881
  }
5876
5882
  static schema() {
@@ -5930,7 +5936,7 @@ class ElementCase extends Rule {
5930
5936
  }
5931
5937
  }
5932
5938
 
5933
- const defaults$l = {
5939
+ const defaults$m = {
5934
5940
  pattern: "^[a-z][a-z0-9\\-._]*-[a-z0-9\\-._]*$",
5935
5941
  whitelist: [],
5936
5942
  blacklist: []
@@ -5938,7 +5944,7 @@ const defaults$l = {
5938
5944
  class ElementName extends Rule {
5939
5945
  pattern;
5940
5946
  constructor(options) {
5941
- super({ ...defaults$l, ...options });
5947
+ super({ ...defaults$m, ...options });
5942
5948
  this.pattern = new RegExp(this.options.pattern);
5943
5949
  }
5944
5950
  static schema() {
@@ -5975,7 +5981,7 @@ class ElementName extends Rule {
5975
5981
  ...context.blacklist.map((cur) => `- ${cur}`)
5976
5982
  ];
5977
5983
  }
5978
- if (context.pattern !== defaults$l.pattern) {
5984
+ if (context.pattern !== defaults$m.pattern) {
5979
5985
  return [
5980
5986
  `<${context.tagName}> is not a valid element name. This project is configured to only allow names matching the following regular expression:`,
5981
5987
  "",
@@ -6020,6 +6026,10 @@ class ElementName extends Rule {
6020
6026
  }
6021
6027
  }
6022
6028
 
6029
+ function isNativeTemplate(node) {
6030
+ const { tagName, meta } = node;
6031
+ return Boolean(tagName === "template" && meta?.templateRoot && meta?.scriptSupporting);
6032
+ }
6023
6033
  function getTransparentChildren(node, transparent) {
6024
6034
  if (typeof transparent === "boolean") {
6025
6035
  return node.childElements;
@@ -6093,7 +6103,7 @@ class ElementPermittedContent extends Rule {
6093
6103
  return false;
6094
6104
  }
6095
6105
  validatePermittedDescendant(node, parent) {
6096
- for (let cur = parent; cur && !cur.isRootElement() && cur.tagName !== "template"; cur = /* istanbul ignore next */
6106
+ for (let cur = parent; cur && !cur.isRootElement() && !isNativeTemplate(cur); cur = /* istanbul ignore next */
6097
6107
  cur.parent ?? null) {
6098
6108
  const meta = cur.meta;
6099
6109
  if (!meta) {
@@ -6461,7 +6471,7 @@ class EmptyTitle extends Rule {
6461
6471
  }
6462
6472
  }
6463
6473
 
6464
- const defaults$k = {
6474
+ const defaults$l = {
6465
6475
  allowArrayBrackets: true,
6466
6476
  allowCheckboxDefault: true,
6467
6477
  shared: ["radio", "button", "reset", "submit"]
@@ -6509,7 +6519,7 @@ function getDocumentation(context) {
6509
6519
  }
6510
6520
  class FormDupName extends Rule {
6511
6521
  constructor(options) {
6512
- super({ ...defaults$k, ...options });
6522
+ super({ ...defaults$l, ...options });
6513
6523
  }
6514
6524
  static schema() {
6515
6525
  return {
@@ -6668,7 +6678,7 @@ class FormDupName extends Rule {
6668
6678
  }
6669
6679
  }
6670
6680
 
6671
- const defaults$j = {
6681
+ const defaults$k = {
6672
6682
  allowMultipleH1: false,
6673
6683
  minInitialRank: "h1",
6674
6684
  sectioningRoots: ["dialog", '[role="dialog"]', '[role="alertdialog"]']
@@ -6700,7 +6710,7 @@ class HeadingLevel extends Rule {
6700
6710
  sectionRoots;
6701
6711
  stack = [];
6702
6712
  constructor(options) {
6703
- super({ ...defaults$j, ...options });
6713
+ super({ ...defaults$k, ...options });
6704
6714
  this.minInitialRank = parseMaxInitial(this.options.minInitialRank);
6705
6715
  this.sectionRoots = this.options.sectioningRoots.map((it) => new Compound(it));
6706
6716
  this.stack.push({
@@ -6937,12 +6947,12 @@ class HiddenFocusable extends Rule {
6937
6947
  }
6938
6948
  }
6939
6949
 
6940
- const defaults$i = {
6950
+ const defaults$j = {
6941
6951
  pattern: "kebabcase"
6942
6952
  };
6943
6953
  class IdPattern extends BasePatternRule {
6944
6954
  constructor(options) {
6945
- super("id", { ...defaults$i, ...options });
6955
+ super("id", { ...defaults$j, ...options });
6946
6956
  }
6947
6957
  static schema() {
6948
6958
  return BasePatternRule.schema();
@@ -7260,13 +7270,13 @@ function findLabelByParent(el) {
7260
7270
  return [];
7261
7271
  }
7262
7272
 
7263
- const defaults$h = {
7273
+ const defaults$i = {
7264
7274
  maxlength: 70
7265
7275
  };
7266
7276
  class LongTitle extends Rule {
7267
7277
  maxlength;
7268
7278
  constructor(options) {
7269
- super({ ...defaults$h, ...options });
7279
+ super({ ...defaults$i, ...options });
7270
7280
  this.maxlength = this.options.maxlength;
7271
7281
  }
7272
7282
  static schema() {
@@ -7294,12 +7304,12 @@ class LongTitle extends Rule {
7294
7304
  }
7295
7305
  }
7296
7306
 
7297
- const defaults$g = {
7307
+ const defaults$h = {
7298
7308
  allowLongDelay: false
7299
7309
  };
7300
7310
  class MetaRefresh extends Rule {
7301
7311
  constructor(options) {
7302
- super({ ...defaults$g, ...options });
7312
+ super({ ...defaults$h, ...options });
7303
7313
  }
7304
7314
  documentation() {
7305
7315
  return {
@@ -7484,12 +7494,12 @@ class MultipleLabeledControls extends Rule {
7484
7494
  }
7485
7495
  }
7486
7496
 
7487
- const defaults$f = {
7497
+ const defaults$g = {
7488
7498
  pattern: "camelcase"
7489
7499
  };
7490
7500
  class NamePattern extends BasePatternRule {
7491
7501
  constructor(options) {
7492
- super("name", { ...defaults$f, ...options });
7502
+ super("name", { ...defaults$g, ...options });
7493
7503
  }
7494
7504
  static schema() {
7495
7505
  return BasePatternRule.schema();
@@ -7578,13 +7588,13 @@ class NoAbstractRole extends Rule {
7578
7588
  }
7579
7589
  }
7580
7590
 
7581
- const defaults$e = {
7591
+ const defaults$f = {
7582
7592
  include: null,
7583
7593
  exclude: null
7584
7594
  };
7585
7595
  class NoAutoplay extends Rule {
7586
7596
  constructor(options) {
7587
- super({ ...defaults$e, ...options });
7597
+ super({ ...defaults$f, ...options });
7588
7598
  }
7589
7599
  documentation(context) {
7590
7600
  return {
@@ -7903,14 +7913,14 @@ Omitted end tags can be ambigious for humans to read and many editors have troub
7903
7913
  }
7904
7914
  }
7905
7915
 
7906
- const defaults$d = {
7916
+ const defaults$e = {
7907
7917
  include: null,
7908
7918
  exclude: null,
7909
7919
  allowedProperties: ["display"]
7910
7920
  };
7911
7921
  class NoInlineStyle extends Rule {
7912
7922
  constructor(options) {
7913
- super({ ...defaults$d, ...options });
7923
+ super({ ...defaults$e, ...options });
7914
7924
  }
7915
7925
  static schema() {
7916
7926
  return {
@@ -8096,7 +8106,7 @@ class NoMultipleMain extends Rule {
8096
8106
  }
8097
8107
  }
8098
8108
 
8099
- const defaults$c = {
8109
+ const defaults$d = {
8100
8110
  relaxed: false
8101
8111
  };
8102
8112
  const textRegexp = /([<>]|&(?![a-zA-Z0-9#]+;))/g;
@@ -8114,7 +8124,7 @@ const replacementTable = {
8114
8124
  class NoRawCharacters extends Rule {
8115
8125
  relaxed;
8116
8126
  constructor(options) {
8117
- super({ ...defaults$c, ...options });
8127
+ super({ ...defaults$d, ...options });
8118
8128
  this.relaxed = this.options.relaxed;
8119
8129
  }
8120
8130
  static schema() {
@@ -8287,13 +8297,13 @@ class NoRedundantRole extends Rule {
8287
8297
  }
8288
8298
 
8289
8299
  const xmlns = /^(.+):.+$/;
8290
- const defaults$b = {
8300
+ const defaults$c = {
8291
8301
  ignoreForeign: true,
8292
8302
  ignoreXML: true
8293
8303
  };
8294
8304
  class NoSelfClosing extends Rule {
8295
8305
  constructor(options) {
8296
- super({ ...defaults$b, ...options });
8306
+ super({ ...defaults$c, ...options });
8297
8307
  }
8298
8308
  static schema() {
8299
8309
  return {
@@ -8377,13 +8387,13 @@ class NoTrailingWhitespace extends Rule {
8377
8387
  }
8378
8388
  }
8379
8389
 
8380
- const defaults$a = {
8390
+ const defaults$b = {
8381
8391
  include: null,
8382
8392
  exclude: null
8383
8393
  };
8384
8394
  class NoUnknownElements extends Rule {
8385
8395
  constructor(options) {
8386
- super({ ...defaults$a, ...options });
8396
+ super({ ...defaults$b, ...options });
8387
8397
  }
8388
8398
  static schema() {
8389
8399
  return {
@@ -8489,13 +8499,13 @@ const replacement = {
8489
8499
  reset: '<button type="reset">',
8490
8500
  image: '<button type="button">'
8491
8501
  };
8492
- const defaults$9 = {
8502
+ const defaults$a = {
8493
8503
  include: null,
8494
8504
  exclude: null
8495
8505
  };
8496
8506
  class PreferButton extends Rule {
8497
8507
  constructor(options) {
8498
- super({ ...defaults$9, ...options });
8508
+ super({ ...defaults$a, ...options });
8499
8509
  }
8500
8510
  static schema() {
8501
8511
  return {
@@ -8561,7 +8571,7 @@ class PreferButton extends Rule {
8561
8571
  }
8562
8572
  }
8563
8573
 
8564
- const defaults$8 = {
8574
+ const defaults$9 = {
8565
8575
  mapping: {
8566
8576
  article: "article",
8567
8577
  banner: "header",
@@ -8591,7 +8601,7 @@ const defaults$8 = {
8591
8601
  };
8592
8602
  class PreferNativeElement extends Rule {
8593
8603
  constructor(options) {
8594
- super({ ...defaults$8, ...options });
8604
+ super({ ...defaults$9, ...options });
8595
8605
  }
8596
8606
  static schema() {
8597
8607
  return {
@@ -8705,12 +8715,12 @@ class PreferTbody extends Rule {
8705
8715
  }
8706
8716
  }
8707
8717
 
8708
- const defaults$7 = {
8718
+ const defaults$8 = {
8709
8719
  tags: ["script", "style"]
8710
8720
  };
8711
8721
  class RequireCSPNonce extends Rule {
8712
8722
  constructor(options) {
8713
- super({ ...defaults$7, ...options });
8723
+ super({ ...defaults$8, ...options });
8714
8724
  }
8715
8725
  static schema() {
8716
8726
  return {
@@ -8757,7 +8767,7 @@ class RequireCSPNonce extends Rule {
8757
8767
  }
8758
8768
  }
8759
8769
 
8760
- const defaults$6 = {
8770
+ const defaults$7 = {
8761
8771
  target: "all",
8762
8772
  include: null,
8763
8773
  exclude: null
@@ -8786,7 +8796,7 @@ function linkSupportsSri(node) {
8786
8796
  class RequireSri extends Rule {
8787
8797
  target;
8788
8798
  constructor(options) {
8789
- super({ ...defaults$6, ...options });
8799
+ super({ ...defaults$7, ...options });
8790
8800
  this.target = this.options.target;
8791
8801
  }
8792
8802
  static schema() {
@@ -8959,7 +8969,7 @@ class SvgFocusable extends Rule {
8959
8969
  }
8960
8970
  }
8961
8971
 
8962
- const defaults$5 = {
8972
+ const defaults$6 = {
8963
8973
  characters: [
8964
8974
  { pattern: " ", replacement: "&nbsp;", description: "non-breaking space" },
8965
8975
  { pattern: "-", replacement: "&#8209;", description: "non-breaking hyphen" }
@@ -8991,7 +9001,7 @@ function matchAll(text, regexp) {
8991
9001
  class TelNonBreaking extends Rule {
8992
9002
  regex;
8993
9003
  constructor(options) {
8994
- super({ ...defaults$5, ...options });
9004
+ super({ ...defaults$6, ...options });
8995
9005
  this.regex = constructRegex(this.options.characters);
8996
9006
  }
8997
9007
  static schema() {
@@ -9366,7 +9376,7 @@ class UniqueLandmark extends Rule {
9366
9376
  }
9367
9377
  }
9368
9378
 
9369
- const defaults$4 = {
9379
+ const defaults$5 = {
9370
9380
  ignoreCase: false,
9371
9381
  requireSemicolon: true
9372
9382
  };
@@ -9400,7 +9410,7 @@ function getDescription(context, options) {
9400
9410
  }
9401
9411
  class UnknownCharReference extends Rule {
9402
9412
  constructor(options) {
9403
- super({ ...defaults$4, ...options });
9413
+ super({ ...defaults$5, ...options });
9404
9414
  }
9405
9415
  static schema() {
9406
9416
  return {
@@ -10016,12 +10026,12 @@ class ValidAutocomplete extends Rule {
10016
10026
  }
10017
10027
  }
10018
10028
 
10019
- const defaults$3 = {
10029
+ const defaults$4 = {
10020
10030
  relaxed: false
10021
10031
  };
10022
10032
  class ValidID extends Rule {
10023
10033
  constructor(options) {
10024
- super({ ...defaults$3, ...options });
10034
+ super({ ...defaults$4, ...options });
10025
10035
  }
10026
10036
  static schema() {
10027
10037
  return {
@@ -10127,13 +10137,13 @@ class VoidContent extends Rule {
10127
10137
  }
10128
10138
  }
10129
10139
 
10130
- const defaults$2 = {
10140
+ const defaults$3 = {
10131
10141
  style: "omit"
10132
10142
  };
10133
10143
  class VoidStyle extends Rule {
10134
10144
  style;
10135
10145
  constructor(options) {
10136
- super({ ...defaults$2, ...options });
10146
+ super({ ...defaults$3, ...options });
10137
10147
  this.style = parseStyle(this.options.style);
10138
10148
  }
10139
10149
  static schema() {
@@ -10334,13 +10344,13 @@ class H36 extends Rule {
10334
10344
  }
10335
10345
  }
10336
10346
 
10337
- const defaults$1 = {
10347
+ const defaults$2 = {
10338
10348
  allowEmpty: true,
10339
10349
  alias: []
10340
10350
  };
10341
10351
  class H37 extends Rule {
10342
10352
  constructor(options) {
10343
- super({ ...defaults$1, ...options });
10353
+ super({ ...defaults$2, ...options });
10344
10354
  if (!Array.isArray(this.options.alias)) {
10345
10355
  this.options.alias = [this.options.alias];
10346
10356
  }
@@ -10402,9 +10412,62 @@ class H37 extends Rule {
10402
10412
  }
10403
10413
  }
10404
10414
 
10415
+ const defaults$1 = {
10416
+ strict: false
10417
+ };
10405
10418
  const { enum: validScopes } = html5.th.attributes?.scope;
10406
10419
  const joinedScopes = naturalJoin(validScopes);
10420
+ const td = 0;
10421
+ const th = 1;
10422
+ function getShape(cells) {
10423
+ const rows = cells.length;
10424
+ const cols = cells[0].length;
10425
+ return { rows, cols };
10426
+ }
10427
+ function isSimpleTable(table) {
10428
+ const haveHeadersAttr = table.querySelector("> tr > [headers], > tbody > tr > [headers]");
10429
+ if (haveHeadersAttr) {
10430
+ return false;
10431
+ }
10432
+ const rows = table.querySelectorAll("> tr, > thead > tr, > tbody > tr");
10433
+ if (rows.length === 0) {
10434
+ return false;
10435
+ }
10436
+ const cells = rows.map((tr) => tr.querySelectorAll("> *").map((el) => el.is("th") ? th : td));
10437
+ if (cells[0].length === 0) {
10438
+ return false;
10439
+ }
10440
+ const numColumns = cells[0].length;
10441
+ if (!cells.every((row) => row.length === numColumns)) {
10442
+ return false;
10443
+ }
10444
+ const shape = getShape(cells);
10445
+ const headersPerRow = cells.map((row) => row.reduce((sum, cell) => sum + cell, 0));
10446
+ const headersPerColumn = Array(shape.cols).fill(0).map((_, index) => {
10447
+ return cells.reduce((sum, it) => sum + it[index], 0);
10448
+ });
10449
+ const [firstRow, ...otherRows] = headersPerRow;
10450
+ if (firstRow === shape.cols && otherRows.every((row) => row === 0)) {
10451
+ return true;
10452
+ }
10453
+ const [firstCol, ...otherCols] = headersPerColumn;
10454
+ const haveThead = Boolean(table.querySelector("> thead"));
10455
+ if (firstCol === shape.rows && otherCols.every((col) => col === 0) && !haveThead) {
10456
+ return true;
10457
+ }
10458
+ return false;
10459
+ }
10407
10460
  class H63 extends Rule {
10461
+ constructor(options) {
10462
+ super({ ...defaults$1, ...options });
10463
+ }
10464
+ static schema() {
10465
+ return {
10466
+ strict: {
10467
+ type: "boolean"
10468
+ }
10469
+ };
10470
+ }
10408
10471
  documentation() {
10409
10472
  return {
10410
10473
  description: "H63: Using the scope attribute to associate header cells and data cells in data tables",
@@ -10412,23 +10475,31 @@ class H63 extends Rule {
10412
10475
  };
10413
10476
  }
10414
10477
  setup() {
10415
- this.on("tag:ready", (event) => {
10478
+ const { strict } = this.options;
10479
+ this.on("element:ready", (event) => {
10416
10480
  const node = event.target;
10417
- if (node.tagName !== "th") {
10481
+ if (!node.is("table")) {
10418
10482
  return;
10419
10483
  }
10420
- const scope = node.getAttribute("scope");
10484
+ if (strict || !isSimpleTable(node)) {
10485
+ this.validateTable(node);
10486
+ }
10487
+ });
10488
+ }
10489
+ validateTable(node) {
10490
+ for (const th2 of node.querySelectorAll("th")) {
10491
+ const scope = th2.getAttribute("scope");
10421
10492
  const value = scope?.value;
10422
10493
  if (value instanceof DynamicValue) {
10423
- return;
10494
+ continue;
10424
10495
  }
10425
10496
  if (value && validScopes.includes(value)) {
10426
- return;
10497
+ continue;
10427
10498
  }
10428
10499
  const message = `<th> element must have a valid scope attribute: ${joinedScopes}`;
10429
- const location = scope?.valueLocation ?? scope?.keyLocation ?? node.location;
10430
- this.report(node, message, location);
10431
- });
10500
+ const location = scope?.valueLocation ?? scope?.keyLocation ?? th2.location;
10501
+ this.report(th2, message, location);
10502
+ }
10432
10503
  }
10433
10504
  }
10434
10505
 
@@ -11720,7 +11791,7 @@ class EventHandler {
11720
11791
  }
11721
11792
 
11722
11793
  const name = "html-validate";
11723
- const version = "9.6.0";
11794
+ const version = "9.7.0";
11724
11795
  const bugs = "https://gitlab.com/html-validate/html-validate/issues/new";
11725
11796
 
11726
11797
  function freeze(src) {