html-validate 5.3.0 → 6.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/CHANGELOG.md +71 -0
  2. package/dist/cjs/browser.js +2 -6
  3. package/dist/cjs/browser.js.map +1 -1
  4. package/dist/cjs/cli.js +53 -28
  5. package/dist/cjs/cli.js.map +1 -1
  6. package/dist/cjs/core.d.ts +21 -7
  7. package/dist/cjs/core.js +618 -351
  8. package/dist/cjs/core.js.map +1 -1
  9. package/dist/cjs/html-validate.js +9 -9
  10. package/dist/cjs/html-validate.js.map +1 -1
  11. package/dist/cjs/index.d.ts +15 -2
  12. package/dist/cjs/index.js +2 -6
  13. package/dist/cjs/index.js.map +1 -1
  14. package/dist/cjs/{matchers.d.ts → jest-lib.d.ts} +2 -4
  15. package/dist/cjs/{matchers.js → jest-lib.js} +125 -87
  16. package/dist/cjs/jest-lib.js.map +1 -0
  17. package/dist/cjs/jest.d.ts +4 -0
  18. package/dist/cjs/jest.js +17 -0
  19. package/dist/cjs/jest.js.map +1 -0
  20. package/dist/cjs/test-utils.js +1 -1
  21. package/dist/es/cli.js +34 -9
  22. package/dist/es/cli.js.map +1 -1
  23. package/dist/es/core.d.ts +21 -7
  24. package/dist/es/core.js +582 -315
  25. package/dist/es/core.js.map +1 -1
  26. package/dist/es/html-validate.js +1 -1
  27. package/dist/es/html-validate.js.map +1 -1
  28. package/dist/es/index.d.ts +15 -2
  29. package/dist/es/{matchers.d.ts → jest-lib.d.ts} +2 -4
  30. package/dist/es/{matchers.js → jest-lib.js} +120 -82
  31. package/dist/es/jest-lib.js.map +1 -0
  32. package/dist/es/jest.d.ts +4 -0
  33. package/dist/es/jest.js +16 -0
  34. package/dist/es/jest.js.map +1 -0
  35. package/dist/schema/elements.json +43 -5
  36. package/elements/html5.json +940 -290
  37. package/jest.d.ts +1 -1
  38. package/jest.js +1 -1
  39. package/package.json +30 -28
  40. package/dist/cjs/matchers.js.map +0 -1
  41. package/dist/es/matchers.js.map +0 -1
package/dist/es/core.js CHANGED
@@ -574,18 +574,63 @@ const definitions = {
574
574
  type: "object",
575
575
  patternProperties: {
576
576
  "^.*$": {
577
- type: "array",
578
- uniqueItems: true,
579
- items: {
580
- anyOf: [
581
- {
577
+ anyOf: [
578
+ {
579
+ type: "object",
580
+ additionalProperties: false,
581
+ properties: {
582
+ boolean: {
583
+ type: "boolean",
584
+ title: "Set to true if this is a boolean attribute"
585
+ },
586
+ deprecated: {
587
+ title: "Set to true or string if this attribute is deprecated",
588
+ oneOf: [
589
+ {
590
+ type: "boolean"
591
+ },
592
+ {
593
+ type: "string"
594
+ }
595
+ ]
596
+ },
597
+ list: {
598
+ type: "boolean",
599
+ title: "Set to true if this attribute is a list of space-separated tokens, each which must be valid by itself"
600
+ },
601
+ "enum": {
602
+ type: "array",
603
+ title: "Exhaustive list of values (string or regex) this attribute accepts",
604
+ uniqueItems: true,
605
+ items: {
606
+ anyOf: [
607
+ {
608
+ type: "string"
609
+ },
610
+ {
611
+ regexp: true
612
+ }
613
+ ]
614
+ }
615
+ },
616
+ omit: {
617
+ type: "boolean",
618
+ title: "Set to true if this attribute can optionally omit its value"
619
+ },
620
+ required: {
621
+ type: "boolean",
622
+ title: "Set to true if this attribute is required"
623
+ }
624
+ }
625
+ },
626
+ {
627
+ type: "array",
628
+ uniqueItems: true,
629
+ items: {
582
630
  type: "string"
583
- },
584
- {
585
- regexp: true
586
631
  }
587
- ]
588
- }
632
+ }
633
+ ]
589
634
  }
590
635
  }
591
636
  },
@@ -636,6 +681,111 @@ var schema = {
636
681
  definitions: definitions
637
682
  };
638
683
 
684
+ var TextContent$1;
685
+ (function (TextContent) {
686
+ /* forbid node to have text content, inter-element whitespace is ignored */
687
+ TextContent["NONE"] = "none";
688
+ /* node can have text but not required too */
689
+ TextContent["DEFAULT"] = "default";
690
+ /* node requires text-nodes to be present (direct or by descendant) */
691
+ TextContent["REQUIRED"] = "required";
692
+ /* node requires accessible text (hidden text is ignored, tries to get text from accessibility tree) */
693
+ TextContent["ACCESSIBLE"] = "accessible";
694
+ })(TextContent$1 || (TextContent$1 = {}));
695
+ /**
696
+ * Properties listed here can be copied (loaded) onto another element using
697
+ * [[HtmlElement.loadMeta]].
698
+ */
699
+ const MetaCopyableProperty = [
700
+ "metadata",
701
+ "flow",
702
+ "sectioning",
703
+ "heading",
704
+ "phrasing",
705
+ "embedded",
706
+ "interactive",
707
+ "transparent",
708
+ "form",
709
+ "labelable",
710
+ "attributes",
711
+ "permittedContent",
712
+ "permittedDescendants",
713
+ "permittedOrder",
714
+ "requiredAncestors",
715
+ "requiredContent",
716
+ ];
717
+ /**
718
+ * @internal
719
+ */
720
+ function setMetaProperty(dst, key, value) {
721
+ dst[key] = value;
722
+ }
723
+
724
+ function isSet(value) {
725
+ return typeof value !== "undefined";
726
+ }
727
+ function flag(value) {
728
+ return value ? true : undefined;
729
+ }
730
+ function stripUndefined(src) {
731
+ const entries = Object.entries(src).filter(([, value]) => isSet(value));
732
+ return Object.fromEntries(entries);
733
+ }
734
+ function migrateSingleAttribute(src, key) {
735
+ var _a, _b;
736
+ const result = {};
737
+ result.deprecated = flag((_a = src.deprecatedAttributes) === null || _a === void 0 ? void 0 : _a.includes(key));
738
+ result.required = flag((_b = src.requiredAttributes) === null || _b === void 0 ? void 0 : _b.includes(key));
739
+ result.omit = undefined;
740
+ const attr = src.attributes ? src.attributes[key] : undefined;
741
+ if (typeof attr === "undefined") {
742
+ return stripUndefined(result);
743
+ }
744
+ /* when the attribute is set to null we use a special property "delete" to
745
+ * flag it, if it is still set during merge (inheritance, overwriting, etc) the attribute will be removed */
746
+ if (attr === null) {
747
+ result.delete = true;
748
+ return stripUndefined(result);
749
+ }
750
+ if (Array.isArray(attr)) {
751
+ if (attr.length === 0) {
752
+ result.boolean = true;
753
+ }
754
+ else {
755
+ result.enum = attr.filter((it) => it !== "");
756
+ if (attr.includes("")) {
757
+ result.omit = true;
758
+ }
759
+ }
760
+ return stripUndefined(result);
761
+ }
762
+ else {
763
+ return stripUndefined({ ...result, ...attr });
764
+ }
765
+ }
766
+ function migrateAttributes(src) {
767
+ var _a, _b, _c;
768
+ const keys = [
769
+ ...Object.keys((_a = src.attributes) !== null && _a !== void 0 ? _a : {}),
770
+ ...((_b = src.requiredAttributes) !== null && _b !== void 0 ? _b : []),
771
+ ...((_c = src.deprecatedAttributes) !== null && _c !== void 0 ? _c : []),
772
+ ].sort();
773
+ const entries = keys.map((key) => {
774
+ return [key, migrateSingleAttribute(src, key)];
775
+ });
776
+ return Object.fromEntries(entries);
777
+ }
778
+ function migrateElement(src) {
779
+ const result = {
780
+ ...src,
781
+ attributes: migrateAttributes(src),
782
+ };
783
+ /* removed properties */
784
+ delete result.deprecatedAttributes;
785
+ delete result.requiredAttributes;
786
+ return result;
787
+ }
788
+
639
789
  const dynamicKeys = [
640
790
  "metadata",
641
791
  "flow",
@@ -729,7 +879,7 @@ class MetaTable {
729
879
  for (const [key, value] of Object.entries(obj)) {
730
880
  if (key === "$schema")
731
881
  continue;
732
- this.addEntry(key, value);
882
+ this.addEntry(key, migrateElement(value));
733
883
  }
734
884
  }
735
885
  /**
@@ -757,7 +907,7 @@ class MetaTable {
757
907
  */
758
908
  getMetaFor(tagName) {
759
909
  tagName = tagName.toLowerCase();
760
- return this.elements[tagName] ? Object.assign({}, this.elements[tagName]) : null;
910
+ return this.elements[tagName] ? { ...this.elements[tagName] } : null;
761
911
  }
762
912
  /**
763
913
  * Find all tags which has enabled given property.
@@ -786,7 +936,7 @@ class MetaTable {
786
936
  }
787
937
  }
788
938
  /* merge all sources together */
789
- const expanded = deepmerge(parent, Object.assign(Object.assign({}, entry), { tagName }), { arrayMerge: overwriteMerge$1 });
939
+ const expanded = this.mergeElement(parent, { ...entry, tagName });
790
940
  expandRegex(expanded);
791
941
  this.elements[tagName] = expanded;
792
942
  }
@@ -820,11 +970,20 @@ class MetaTable {
820
970
  delete global.void;
821
971
  /* merge elements */
822
972
  for (const [tagName, entry] of Object.entries(this.elements)) {
823
- this.elements[tagName] = this.mergeElement(entry, global);
973
+ this.elements[tagName] = this.mergeElement(global, entry);
824
974
  }
825
975
  }
826
976
  mergeElement(a, b) {
827
- return deepmerge(a, b);
977
+ const merged = deepmerge(a, b, { arrayMerge: overwriteMerge$1 });
978
+ /* special handling when removing attributes by setting them to null
979
+ * resulting in the deletion flag being set */
980
+ const filteredAttrs = Object.entries(merged.attributes).filter(([, attr]) => {
981
+ const val = !attr.delete;
982
+ delete attr.delete;
983
+ return val;
984
+ });
985
+ merged.attributes = Object.fromEntries(filteredAttrs);
986
+ return merged;
828
987
  }
829
988
  resolve(node) {
830
989
  if (node.meta) {
@@ -836,7 +995,7 @@ function expandProperties(node, entry) {
836
995
  for (const key of dynamicKeys) {
837
996
  const property = entry[key];
838
997
  if (property && typeof property !== "boolean") {
839
- entry[key] = evaluateProperty(node, property);
998
+ setMetaProperty(entry, key, evaluateProperty(node, property));
840
999
  }
841
1000
  }
842
1001
  }
@@ -862,14 +1021,9 @@ function expandRegexValue(value) {
862
1021
  * Expand all regular expressions in strings ("/../"). This mutates the object.
863
1022
  */
864
1023
  function expandRegex(entry) {
865
- if (!entry.attributes)
866
- return;
867
1024
  for (const [name, values] of Object.entries(entry.attributes)) {
868
- if (values) {
869
- entry.attributes[name] = values.map(expandRegexValue);
870
- }
871
- else {
872
- delete entry.attributes[name];
1025
+ if (values.enum) {
1026
+ entry.attributes[name].enum = values.enum.map(expandRegexValue);
873
1027
  }
874
1028
  }
875
1029
  }
@@ -925,41 +1079,6 @@ function matchAttribute(node, match) {
925
1079
  }
926
1080
  }
927
1081
 
928
- var TextContent$1;
929
- (function (TextContent) {
930
- /* forbid node to have text content, inter-element whitespace is ignored */
931
- TextContent["NONE"] = "none";
932
- /* node can have text but not required too */
933
- TextContent["DEFAULT"] = "default";
934
- /* node requires text-nodes to be present (direct or by descendant) */
935
- TextContent["REQUIRED"] = "required";
936
- /* node requires accessible text (hidden text is ignored, tries to get text from accessibility tree) */
937
- TextContent["ACCESSIBLE"] = "accessible";
938
- })(TextContent$1 || (TextContent$1 = {}));
939
- /**
940
- * Properties listed here can be copied (loaded) onto another element using
941
- * [[HtmlElement.loadMeta]].
942
- */
943
- const MetaCopyableProperty = [
944
- "metadata",
945
- "flow",
946
- "sectioning",
947
- "heading",
948
- "phrasing",
949
- "embedded",
950
- "interactive",
951
- "transparent",
952
- "form",
953
- "labelable",
954
- "requiredAttributes",
955
- "attributes",
956
- "permittedContent",
957
- "permittedDescendants",
958
- "permittedOrder",
959
- "requiredAncestors",
960
- "requiredContent",
961
- ];
962
-
963
1082
  class DynamicValue {
964
1083
  constructor(expr) {
965
1084
  this.expr = expr;
@@ -1330,6 +1449,15 @@ class DOMTokenList extends Array {
1330
1449
  contains(token) {
1331
1450
  return this.includes(token);
1332
1451
  }
1452
+ *iterator() {
1453
+ for (let index = 0; index < this.length; index++) {
1454
+ /* eslint-disable @typescript-eslint/no-non-null-assertion */
1455
+ const item = this.item(index);
1456
+ const location = this.location(index);
1457
+ /* eslint-enable @typescript-eslint/no-non-null-assertion */
1458
+ yield { index, item, location };
1459
+ }
1460
+ }
1333
1461
  }
1334
1462
 
1335
1463
  var Combinator;
@@ -1802,8 +1930,9 @@ class HtmlElement extends DOMNode {
1802
1930
  this.metaElement = {};
1803
1931
  }
1804
1932
  for (const key of MetaCopyableProperty) {
1805
- if (typeof meta[key] !== "undefined") {
1806
- this.metaElement[key] = meta[key];
1933
+ const value = meta[key];
1934
+ if (typeof value !== "undefined") {
1935
+ setMetaProperty(this.metaElement, key, value);
1807
1936
  }
1808
1937
  else {
1809
1938
  delete this.metaElement[key];
@@ -2219,6 +2348,7 @@ class Validator {
2219
2348
  * @param rules - Element attribute metadta.
2220
2349
  * @returns `true` if attribute passes all tests.
2221
2350
  */
2351
+ /* eslint-disable-next-line complexity */
2222
2352
  static validateAttribute(attr, rules) {
2223
2353
  const rule = rules[attr.key];
2224
2354
  if (!rule) {
@@ -2232,19 +2362,34 @@ class Validator {
2232
2362
  return true;
2233
2363
  }
2234
2364
  const empty = value === null || value === "";
2235
- /* consider an empty array as being a boolean attribute */
2236
- if (rule.length === 0) {
2365
+ /* if boolean is set the value can be either null, empty string or the
2366
+ * attribute key (attribute-boolean-style regulates style) */
2367
+ if (rule.boolean) {
2237
2368
  return empty || value === attr.key;
2238
2369
  }
2239
- /* if the empty string is present allow both "" and null
2240
- * (boolean-attribute-style will regulate which is allowed) */
2241
- if (rule.includes("") && empty) {
2370
+ /* if omit is set the value can be either null or empty string
2371
+ * (attribute-empty style regulates style) */
2372
+ if (rule.omit && empty) {
2373
+ return true;
2374
+ }
2375
+ /* validate each token when using list, all tokens must be valid */
2376
+ if (rule.list) {
2377
+ const tokens = new DOMTokenList(value, attr.valueLocation);
2378
+ return tokens.every((token) => {
2379
+ return this.validateAttributeValue(token, rule);
2380
+ });
2381
+ }
2382
+ return this.validateAttributeValue(value, rule);
2383
+ }
2384
+ static validateAttributeValue(value, rule) {
2385
+ /* skip attribute if it not have enumerated list */
2386
+ if (!rule.enum) {
2242
2387
  return true;
2243
2388
  }
2244
2389
  if (value === null || value === undefined) {
2245
2390
  return false;
2246
2391
  }
2247
- return rule.some((entry) => {
2392
+ return rule.enum.some((entry) => {
2248
2393
  if (entry instanceof RegExp) {
2249
2394
  return !!value.match(entry);
2250
2395
  }
@@ -2510,12 +2655,12 @@ var configurationSchema = {
2510
2655
  const espree = legacyRequire("espree");
2511
2656
  const walk = legacyRequire("acorn-walk");
2512
2657
  function joinTemplateLiteral(nodes) {
2513
- let offset = nodes[0].start;
2658
+ let offset = nodes[0].start + 1;
2514
2659
  let output = "";
2515
2660
  for (const node of nodes) {
2516
- output += " ".repeat(node.start - offset);
2661
+ output += " ".repeat(node.start + 1 - offset);
2517
2662
  output += node.value.raw;
2518
- offset = node.end;
2663
+ offset = node.end - 2;
2519
2664
  }
2520
2665
  return output;
2521
2666
  }
@@ -2707,7 +2852,7 @@ var TRANSFORMER_API;
2707
2852
  })(TRANSFORMER_API || (TRANSFORMER_API = {}));
2708
2853
 
2709
2854
  const name = "html-validate";
2710
- const version = "5.3.0";
2855
+ const version = "6.0.0";
2711
2856
  const homepage = "https://html-validate.org";
2712
2857
  const bugs = {
2713
2858
  url: "https://gitlab.com/html-validate/html-validate/issues/new"
@@ -2999,7 +3144,7 @@ const description = {
2999
3144
  };
3000
3145
  class AllowedLinks extends Rule {
3001
3146
  constructor(options) {
3002
- super(Object.assign(Object.assign({}, defaults$p), options));
3147
+ super({ ...defaults$p, ...options });
3003
3148
  }
3004
3149
  static schema() {
3005
3150
  return {
@@ -3247,7 +3392,7 @@ const defaults$o = {
3247
3392
  };
3248
3393
  class AttrCase extends Rule {
3249
3394
  constructor(options) {
3250
- super(Object.assign(Object.assign({}, defaults$o), options));
3395
+ super({ ...defaults$o, ...options });
3251
3396
  this.style = new CaseStyle(this.options.style, "attr-case");
3252
3397
  }
3253
3398
  static schema() {
@@ -3600,7 +3745,7 @@ function generateDescription(name, pattern) {
3600
3745
  }
3601
3746
  class AttrPattern extends Rule {
3602
3747
  constructor(options) {
3603
- super(Object.assign(Object.assign({}, defaults$n), options));
3748
+ super({ ...defaults$n, ...options });
3604
3749
  this.pattern = generateRegexp(this.options.pattern);
3605
3750
  }
3606
3751
  static schema() {
@@ -3666,7 +3811,7 @@ const defaults$m = {
3666
3811
  };
3667
3812
  class AttrQuotes extends Rule {
3668
3813
  constructor(options) {
3669
- super(Object.assign(Object.assign({}, defaults$m), options));
3814
+ super({ ...defaults$m, ...options });
3670
3815
  this.style = parseStyle$4(this.options.style);
3671
3816
  }
3672
3817
  static schema() {
@@ -3762,11 +3907,11 @@ class AttributeAllowedValues extends Rule {
3762
3907
  if (!context) {
3763
3908
  return docs;
3764
3909
  }
3765
- if (context.allowed.length > 0) {
3766
- const allowed = context.allowed.map((val) => `- \`${val}\``);
3910
+ if (context.allowed.enum) {
3911
+ const allowed = context.allowed.enum.map((val) => `- \`${val}\``);
3767
3912
  docs.description = `Element <${context.element}> does not allow attribute \`${context.attribute}\` to have the value \`"${context.value}"\`, it must match one of the following:\n\n${allowed.join("\n")}`;
3768
3913
  }
3769
- else {
3914
+ else if (context.allowed.boolean) {
3770
3915
  docs.description = `Element <${context.element}> attribute \`${context.attribute}\` must be a boolean attribute, e.g. \`<${context.element} ${context.attribute}>\``;
3771
3916
  }
3772
3917
  return docs;
@@ -3822,7 +3967,7 @@ const defaults$l = {
3822
3967
  };
3823
3968
  class AttributeBooleanStyle extends Rule {
3824
3969
  constructor(options) {
3825
- super(Object.assign(Object.assign({}, defaults$l), options));
3970
+ super({ ...defaults$l, ...options });
3826
3971
  this.hasInvalidStyle = parseStyle$3(this.options.style);
3827
3972
  }
3828
3973
  static schema() {
@@ -3867,7 +4012,8 @@ class AttributeBooleanStyle extends Rule {
3867
4012
  });
3868
4013
  }
3869
4014
  isBoolean(attr, rules) {
3870
- return rules[attr.key] && rules[attr.key].length === 0;
4015
+ var _a;
4016
+ return Boolean((_a = rules[attr.key]) === null || _a === void 0 ? void 0 : _a.boolean);
3871
4017
  }
3872
4018
  }
3873
4019
  function parseStyle$3(style) {
@@ -3902,7 +4048,7 @@ const defaults$k = {
3902
4048
  };
3903
4049
  class AttributeEmptyStyle extends Rule {
3904
4050
  constructor(options) {
3905
- super(Object.assign(Object.assign({}, defaults$k), options));
4051
+ super({ ...defaults$k, ...options });
3906
4052
  this.hasInvalidStyle = parseStyle$2(this.options.style);
3907
4053
  }
3908
4054
  static schema() {
@@ -3951,7 +4097,8 @@ class AttributeEmptyStyle extends Rule {
3951
4097
  }
3952
4098
  }
3953
4099
  function allowsEmpty(attr, rules) {
3954
- return rules[attr.key] && rules[attr.key].includes("");
4100
+ var _a;
4101
+ return Boolean((_a = rules[attr.key]) === null || _a === void 0 ? void 0 : _a.omit);
3955
4102
  }
3956
4103
  function isEmptyValue(attr) {
3957
4104
  /* dynamic values are ignored, assumed to contain a value */
@@ -4014,7 +4161,7 @@ const defaults$j = {
4014
4161
  };
4015
4162
  class ClassPattern extends Rule {
4016
4163
  constructor(options) {
4017
- super(Object.assign(Object.assign({}, defaults$j), options));
4164
+ super({ ...defaults$j, ...options });
4018
4165
  this.pattern = parsePattern(this.options.pattern);
4019
4166
  }
4020
4167
  static schema() {
@@ -4127,7 +4274,7 @@ const defaults$i = {
4127
4274
  };
4128
4275
  class Deprecated extends Rule {
4129
4276
  constructor(options) {
4130
- super(Object.assign(Object.assign({}, defaults$i), options));
4277
+ super({ ...defaults$i, ...options });
4131
4278
  }
4132
4279
  static schema() {
4133
4280
  return {
@@ -4221,7 +4368,7 @@ class Deprecated extends Rule {
4221
4368
  this.report(node, message, location, context);
4222
4369
  }
4223
4370
  reportObject(deprecated, node, location) {
4224
- const context = Object.assign(Object.assign({}, deprecated), { tagName: node.tagName });
4371
+ const context = { ...deprecated, tagName: node.tagName };
4225
4372
  const notice = deprecated.message ? `: ${deprecated.message}` : "";
4226
4373
  const message = `<${node.tagName}> is deprecated${notice}`;
4227
4374
  this.report(node, message, location, context);
@@ -4295,7 +4442,7 @@ const defaults$h = {
4295
4442
  };
4296
4443
  class DoctypeStyle extends Rule {
4297
4444
  constructor(options) {
4298
- super(Object.assign(Object.assign({}, defaults$h), options));
4445
+ super({ ...defaults$h, ...options });
4299
4446
  }
4300
4447
  static schema() {
4301
4448
  return {
@@ -4332,7 +4479,7 @@ const defaults$g = {
4332
4479
  };
4333
4480
  class ElementCase extends Rule {
4334
4481
  constructor(options) {
4335
- super(Object.assign(Object.assign({}, defaults$g), options));
4482
+ super({ ...defaults$g, ...options });
4336
4483
  this.style = new CaseStyle(this.options.style, "element-case");
4337
4484
  }
4338
4485
  static schema() {
@@ -4402,7 +4549,7 @@ const defaults$f = {
4402
4549
  };
4403
4550
  class ElementName extends Rule {
4404
4551
  constructor(options) {
4405
- super(Object.assign(Object.assign({}, defaults$f), options));
4552
+ super({ ...defaults$f, ...options });
4406
4553
  // eslint-disable-next-line security/detect-non-literal-regexp
4407
4554
  this.pattern = new RegExp(this.options.pattern);
4408
4555
  }
@@ -4674,9 +4821,14 @@ class ElementRequiredAttributes extends Rule {
4674
4821
  this.on("tag:end", (event) => {
4675
4822
  const node = event.previous;
4676
4823
  const meta = node.meta;
4677
- if (!meta || !meta.requiredAttributes)
4824
+ /* handle missing metadata and missing attributes */
4825
+ if (!meta || !meta.attributes) {
4678
4826
  return;
4679
- for (const key of meta.requiredAttributes) {
4827
+ }
4828
+ for (const [key, attr] of Object.entries(meta.attributes)) {
4829
+ if (!attr.required) {
4830
+ continue;
4831
+ }
4680
4832
  if (node.hasAttribute(key))
4681
4833
  continue;
4682
4834
  const context = {
@@ -4830,6 +4982,7 @@ class EmptyTitle extends Rule {
4830
4982
 
4831
4983
  const defaults$e = {
4832
4984
  allowMultipleH1: false,
4985
+ minInitialRank: "h1",
4833
4986
  sectioningRoots: ["dialog", '[role="dialog"]'],
4834
4987
  };
4835
4988
  function isRelevant$2(event) {
@@ -4845,10 +4998,22 @@ function extractLevel(node) {
4845
4998
  return null;
4846
4999
  }
4847
5000
  }
5001
+ function parseMaxInitial(value) {
5002
+ if (value === false || value === "any") {
5003
+ return 6;
5004
+ }
5005
+ const match = value.match(/^h(\d)$/);
5006
+ /* istanbul ignore next: should never happen, schema validation should catch invalid values */
5007
+ if (!match) {
5008
+ return 1;
5009
+ }
5010
+ return parseInt(match[1], 10);
5011
+ }
4848
5012
  class HeadingLevel extends Rule {
4849
5013
  constructor(options) {
4850
- super(Object.assign(Object.assign({}, defaults$e), options));
5014
+ super({ ...defaults$e, ...options });
4851
5015
  this.stack = [];
5016
+ this.minInitialRank = parseMaxInitial(this.options.minInitialRank);
4852
5017
  this.sectionRoots = this.options.sectioningRoots.map((it) => new Pattern(it));
4853
5018
  /* add a global sectioning root used by default */
4854
5019
  this.stack.push({
@@ -4862,6 +5027,9 @@ class HeadingLevel extends Rule {
4862
5027
  allowMultipleH1: {
4863
5028
  type: "boolean",
4864
5029
  },
5030
+ minInitialRank: {
5031
+ enum: ["h1", "h2", "h3", "h4", "h5", "h6", "any", false],
5032
+ },
4865
5033
  sectioningRoots: {
4866
5034
  items: {
4867
5035
  type: "string",
@@ -4872,7 +5040,8 @@ class HeadingLevel extends Rule {
4872
5040
  }
4873
5041
  documentation() {
4874
5042
  const text = [];
4875
- text.push("Headings must start at <h1> and can only increase one level at a time.");
5043
+ const modality = this.minInitialRank > 1 ? "should" : "must";
5044
+ text.push(`Headings ${modality} start at <h1> and can only increase one level at a time.`);
4876
5045
  text.push("The headings should form a table of contents and make sense on its own.");
4877
5046
  if (!this.options.allowMultipleH1) {
4878
5047
  text.push("");
@@ -4917,20 +5086,32 @@ class HeadingLevel extends Rule {
4917
5086
  */
4918
5087
  checkLevelIncrementation(root, event, level) {
4919
5088
  const expected = root.current + 1;
4920
- if (level !== expected) {
4921
- const location = sliceLocation(event.location, 1);
4922
- if (root.current > 0) {
4923
- const msg = `Heading level can only increase by one, expected <h${expected}> but got <h${level}>`;
4924
- this.report(event.target, msg, location);
4925
- }
4926
- else {
4927
- this.checkInitialLevel(event, location, level, expected);
4928
- }
5089
+ /* check if the new level is the expected one (headings with higher ranks
5090
+ * are skipped already) */
5091
+ if (level === expected) {
5092
+ return;
5093
+ }
5094
+ /* if this is the initial heading of the document it is compared to the
5095
+ * minimal allowed (default h1) */
5096
+ const isInitial = this.stack.length === 1 && expected === 1;
5097
+ if (isInitial && level <= this.minInitialRank) {
5098
+ return;
5099
+ }
5100
+ /* if we reach this far the heading level is not accepted */
5101
+ const location = sliceLocation(event.location, 1);
5102
+ if (root.current > 0) {
5103
+ const msg = `Heading level can only increase by one, expected <h${expected}> but got <h${level}>`;
5104
+ this.report(event.target, msg, location);
5105
+ }
5106
+ else {
5107
+ this.checkInitialLevel(event, location, level, expected);
4929
5108
  }
4930
5109
  }
4931
5110
  checkInitialLevel(event, location, level, expected) {
4932
5111
  if (this.stack.length === 1) {
4933
- const msg = `Initial heading level must be <h${expected}> but got <h${level}>`;
5112
+ const msg = this.minInitialRank > 1
5113
+ ? `Initial heading level must be <h${this.minInitialRank}> or higher rank but got <h${level}>`
5114
+ : `Initial heading level must be <h${expected}> but got <h${level}>`;
4934
5115
  this.report(event.target, msg, location);
4935
5116
  }
4936
5117
  else {
@@ -4993,7 +5174,7 @@ const defaults$d = {
4993
5174
  };
4994
5175
  class IdPattern extends Rule {
4995
5176
  constructor(options) {
4996
- super(Object.assign(Object.assign({}, defaults$d), options));
5177
+ super({ ...defaults$d, ...options });
4997
5178
  this.pattern = parsePattern(this.options.pattern);
4998
5179
  }
4999
5180
  static schema() {
@@ -5345,7 +5526,7 @@ const defaults$c = {
5345
5526
  };
5346
5527
  class LongTitle extends Rule {
5347
5528
  constructor(options) {
5348
- super(Object.assign(Object.assign({}, defaults$c), options));
5529
+ super({ ...defaults$c, ...options });
5349
5530
  this.maxlength = this.options.maxlength;
5350
5531
  }
5351
5532
  static schema() {
@@ -5498,7 +5679,7 @@ const defaults$b = {
5498
5679
  };
5499
5680
  class NoAutoplay extends Rule {
5500
5681
  constructor(options) {
5501
- super(Object.assign(Object.assign({}, defaults$b), options));
5682
+ super({ ...defaults$b, ...options });
5502
5683
  }
5503
5684
  documentation(context) {
5504
5685
  const tagName = context ? ` on <${context.tagName}>` : "";
@@ -5594,8 +5775,12 @@ class NoDeprecatedAttr extends Rule {
5594
5775
  if (meta === null) {
5595
5776
  return;
5596
5777
  }
5597
- const deprecated = meta.deprecatedAttributes || [];
5598
- if (deprecated.includes(attr)) {
5778
+ const metaAttribute = meta.attributes && meta.attributes[attr];
5779
+ if (!metaAttribute) {
5780
+ return;
5781
+ }
5782
+ const deprecated = metaAttribute.deprecated;
5783
+ if (deprecated) {
5599
5784
  this.report(node, `Attribute "${event.key}" is deprecated on <${node.tagName}> element`, event.keyLocation);
5600
5785
  }
5601
5786
  });
@@ -5752,7 +5937,7 @@ function getCSSDeclarations(value) {
5752
5937
  }
5753
5938
  class NoInlineStyle extends Rule {
5754
5939
  constructor(options) {
5755
- super(Object.assign(Object.assign({}, defaults$a), options));
5940
+ super({ ...defaults$a, ...options });
5756
5941
  }
5757
5942
  static schema() {
5758
5943
  return {
@@ -5846,7 +6031,20 @@ class NoInlineStyle extends Rule {
5846
6031
  }
5847
6032
  }
5848
6033
 
5849
- const ARIA = ["aria-controls", "aria-describedby", "aria-labelledby"];
6034
+ const ARIA = [
6035
+ { property: "aria-activedescendant", isList: false },
6036
+ { property: "aria-controls", isList: true },
6037
+ { property: "aria-describedby", isList: true },
6038
+ { property: "aria-details", isList: false },
6039
+ { property: "aria-errormessage", isList: false },
6040
+ { property: "aria-flowto", isList: true },
6041
+ { property: "aria-labelledby", isList: true },
6042
+ { property: "aria-owns", isList: true },
6043
+ ];
6044
+ function idMissing(document, id) {
6045
+ const nodes = document.querySelectorAll(`[id="${id}"]`);
6046
+ return nodes.length === 0;
6047
+ }
5850
6048
  class NoMissingReferences extends Rule {
5851
6049
  documentation(context) {
5852
6050
  if (context) {
@@ -5868,38 +6066,56 @@ class NoMissingReferences extends Rule {
5868
6066
  /* verify <label for=".."> */
5869
6067
  for (const node of document.querySelectorAll("label[for]")) {
5870
6068
  const attr = node.getAttribute("for");
5871
- this.validateReference(document, node, attr);
6069
+ this.validateReference(document, node, attr, false);
5872
6070
  }
5873
6071
  /* verify <input list=".."> */
5874
6072
  for (const node of document.querySelectorAll("input[list]")) {
5875
6073
  const attr = node.getAttribute("list");
5876
- this.validateReference(document, node, attr);
6074
+ this.validateReference(document, node, attr, false);
5877
6075
  }
5878
6076
  /* verify WAI-ARIA properties */
5879
- for (const property of ARIA) {
6077
+ for (const { property, isList } of ARIA) {
5880
6078
  for (const node of document.querySelectorAll(`[${property}]`)) {
5881
6079
  const attr = node.getAttribute(property);
5882
- this.validateReference(document, node, attr);
6080
+ this.validateReference(document, node, attr, isList);
5883
6081
  }
5884
6082
  }
5885
6083
  });
5886
6084
  }
5887
- validateReference(document, node, attr) {
6085
+ validateReference(document, node, attr, isList) {
5888
6086
  /* sanity check: querySelector should never return elements without the attribute */
5889
6087
  /* istanbul ignore next */
5890
6088
  if (!attr) {
5891
6089
  return;
5892
6090
  }
5893
- const id = attr.value;
5894
- if (id instanceof DynamicValue || id === null || id === "") {
6091
+ /* skip dynamic and empty values */
6092
+ const value = attr.value;
6093
+ if (value instanceof DynamicValue || value === null || value === "") {
5895
6094
  return;
5896
6095
  }
5897
- const nodes = document.querySelectorAll(`[id="${id}"]`);
5898
- if (nodes.length === 0) {
6096
+ if (isList) {
6097
+ this.validateList(document, node, attr, value);
6098
+ }
6099
+ else {
6100
+ this.validateSingle(document, node, attr, value);
6101
+ }
6102
+ }
6103
+ validateSingle(document, node, attr, id) {
6104
+ if (idMissing(document, id)) {
5899
6105
  const context = { key: attr.key, value: id };
5900
6106
  this.report(node, `Element references missing id "${id}"`, attr.valueLocation, context);
5901
6107
  }
5902
6108
  }
6109
+ validateList(document, node, attr, values) {
6110
+ const parsed = new DOMTokenList(values, attr.valueLocation);
6111
+ for (const entry of parsed.iterator()) {
6112
+ const id = entry.item;
6113
+ if (idMissing(document, id)) {
6114
+ const context = { key: attr.key, value: id };
6115
+ this.report(node, `Element references missing id "${id}"`, entry.location, context);
6116
+ }
6117
+ }
6118
+ }
5903
6119
  }
5904
6120
 
5905
6121
  class NoMultipleMain extends Rule {
@@ -5943,7 +6159,7 @@ const replacementTable = new Map([
5943
6159
  ]);
5944
6160
  class NoRawCharacters extends Rule {
5945
6161
  constructor(options) {
5946
- super(Object.assign(Object.assign({}, defaults$9), options));
6162
+ super({ ...defaults$9, ...options });
5947
6163
  this.relaxed = this.options.relaxed;
5948
6164
  }
5949
6165
  static schema() {
@@ -6128,7 +6344,7 @@ const defaults$8 = {
6128
6344
  };
6129
6345
  class NoSelfClosing extends Rule {
6130
6346
  constructor(options) {
6131
- super(Object.assign(Object.assign({}, defaults$8), options));
6347
+ super({ ...defaults$8, ...options });
6132
6348
  }
6133
6349
  static schema() {
6134
6350
  return {
@@ -6267,7 +6483,7 @@ const defaults$7 = {
6267
6483
  };
6268
6484
  class PreferButton extends Rule {
6269
6485
  constructor(options) {
6270
- super(Object.assign(Object.assign({}, defaults$7), options));
6486
+ super({ ...defaults$7, ...options });
6271
6487
  }
6272
6488
  static schema() {
6273
6489
  return {
@@ -6372,7 +6588,7 @@ const defaults$6 = {
6372
6588
  };
6373
6589
  class PreferNativeElement extends Rule {
6374
6590
  constructor(options) {
6375
- super(Object.assign(Object.assign({}, defaults$6), options));
6591
+ super({ ...defaults$6, ...options });
6376
6592
  }
6377
6593
  static schema() {
6378
6594
  return {
@@ -6502,7 +6718,7 @@ const supportSri = {
6502
6718
  };
6503
6719
  class RequireSri extends Rule {
6504
6720
  constructor(options) {
6505
- super(Object.assign(Object.assign({}, defaults$5), options));
6721
+ super({ ...defaults$5, ...options });
6506
6722
  this.target = this.options.target;
6507
6723
  }
6508
6724
  static schema() {
@@ -8597,7 +8813,7 @@ const defaults$4 = {
8597
8813
  };
8598
8814
  class Void extends Rule {
8599
8815
  constructor(options) {
8600
- super(Object.assign(Object.assign({}, defaults$4), options));
8816
+ super({ ...defaults$4, ...options });
8601
8817
  this.style = parseStyle$1(this.options.style);
8602
8818
  }
8603
8819
  get deprecated() {
@@ -8706,7 +8922,7 @@ const defaults$3 = {
8706
8922
  };
8707
8923
  class VoidStyle extends Rule {
8708
8924
  constructor(options) {
8709
- super(Object.assign(Object.assign({}, defaults$3), options));
8925
+ super({ ...defaults$3, ...options });
8710
8926
  this.style = parseStyle(this.options.style);
8711
8927
  }
8712
8928
  static schema() {
@@ -8909,7 +9125,7 @@ const defaults$2 = {
8909
9125
  };
8910
9126
  class H37 extends Rule {
8911
9127
  constructor(options) {
8912
- super(Object.assign(Object.assign({}, defaults$2), options));
9128
+ super({ ...defaults$2, ...options });
8913
9129
  /* ensure alias is array */
8914
9130
  if (!Array.isArray(this.options.alias)) {
8915
9131
  this.options.alias = [this.options.alias];
@@ -9036,7 +9252,73 @@ const bundledRules$1 = {
9036
9252
  "wcag/h71": H71,
9037
9253
  };
9038
9254
 
9039
- const bundledRules = Object.assign({ "allowed-links": AllowedLinks, "aria-label-misuse": AriaLabelMisuse, "attr-case": AttrCase, "attr-delimiter": AttrDelimiter, "attr-pattern": AttrPattern, "attr-quotes": AttrQuotes, "attr-spacing": AttrSpacing, "attribute-allowed-values": AttributeAllowedValues, "attribute-boolean-style": AttributeBooleanStyle, "attribute-empty-style": AttributeEmptyStyle, "class-pattern": ClassPattern, "close-attr": CloseAttr, "close-order": CloseOrder, deprecated: Deprecated, "deprecated-rule": DeprecatedRule, "doctype-html": NoStyleTag$1, "doctype-style": DoctypeStyle, "element-case": ElementCase, "element-name": ElementName, "element-permitted-content": ElementPermittedContent, "element-permitted-occurrences": ElementPermittedOccurrences, "element-permitted-order": ElementPermittedOrder, "element-required-attributes": ElementRequiredAttributes, "element-required-content": ElementRequiredContent, "empty-heading": EmptyHeading, "empty-title": EmptyTitle, "heading-level": HeadingLevel, "id-pattern": IdPattern, "input-attributes": InputAttributes, "input-missing-label": InputMissingLabel, "long-title": LongTitle, "meta-refresh": MetaRefresh, "missing-doctype": MissingDoctype, "multiple-labeled-controls": MultipleLabeledControls, "no-autoplay": NoAutoplay, "no-conditional-comment": NoConditionalComment, "no-deprecated-attr": NoDeprecatedAttr, "no-dup-attr": NoDupAttr, "no-dup-class": NoDupClass, "no-dup-id": NoDupID, "no-implicit-close": NoImplicitClose, "no-inline-style": NoInlineStyle, "no-missing-references": NoMissingReferences, "no-multiple-main": NoMultipleMain, "no-raw-characters": NoRawCharacters, "no-redundant-for": NoRedundantFor, "no-redundant-role": NoRedundantRole, "no-self-closing": NoSelfClosing, "no-style-tag": NoStyleTag, "no-trailing-whitespace": NoTrailingWhitespace, "no-unknown-elements": NoUnknownElements, "no-utf8-bom": NoUtf8Bom, "prefer-button": PreferButton, "prefer-native-element": PreferNativeElement, "prefer-tbody": PreferTbody, "require-sri": RequireSri, "script-element": ScriptElement, "script-type": ScriptType, "svg-focusable": SvgFocusable, "text-content": TextContent, "unrecognized-char-ref": UnknownCharReference, void: Void, "void-content": VoidContent, "void-style": VoidStyle }, bundledRules$1);
9255
+ const bundledRules = {
9256
+ "allowed-links": AllowedLinks,
9257
+ "aria-label-misuse": AriaLabelMisuse,
9258
+ "attr-case": AttrCase,
9259
+ "attr-delimiter": AttrDelimiter,
9260
+ "attr-pattern": AttrPattern,
9261
+ "attr-quotes": AttrQuotes,
9262
+ "attr-spacing": AttrSpacing,
9263
+ "attribute-allowed-values": AttributeAllowedValues,
9264
+ "attribute-boolean-style": AttributeBooleanStyle,
9265
+ "attribute-empty-style": AttributeEmptyStyle,
9266
+ "class-pattern": ClassPattern,
9267
+ "close-attr": CloseAttr,
9268
+ "close-order": CloseOrder,
9269
+ deprecated: Deprecated,
9270
+ "deprecated-rule": DeprecatedRule,
9271
+ "doctype-html": NoStyleTag$1,
9272
+ "doctype-style": DoctypeStyle,
9273
+ "element-case": ElementCase,
9274
+ "element-name": ElementName,
9275
+ "element-permitted-content": ElementPermittedContent,
9276
+ "element-permitted-occurrences": ElementPermittedOccurrences,
9277
+ "element-permitted-order": ElementPermittedOrder,
9278
+ "element-required-attributes": ElementRequiredAttributes,
9279
+ "element-required-content": ElementRequiredContent,
9280
+ "empty-heading": EmptyHeading,
9281
+ "empty-title": EmptyTitle,
9282
+ "heading-level": HeadingLevel,
9283
+ "id-pattern": IdPattern,
9284
+ "input-attributes": InputAttributes,
9285
+ "input-missing-label": InputMissingLabel,
9286
+ "long-title": LongTitle,
9287
+ "meta-refresh": MetaRefresh,
9288
+ "missing-doctype": MissingDoctype,
9289
+ "multiple-labeled-controls": MultipleLabeledControls,
9290
+ "no-autoplay": NoAutoplay,
9291
+ "no-conditional-comment": NoConditionalComment,
9292
+ "no-deprecated-attr": NoDeprecatedAttr,
9293
+ "no-dup-attr": NoDupAttr,
9294
+ "no-dup-class": NoDupClass,
9295
+ "no-dup-id": NoDupID,
9296
+ "no-implicit-close": NoImplicitClose,
9297
+ "no-inline-style": NoInlineStyle,
9298
+ "no-missing-references": NoMissingReferences,
9299
+ "no-multiple-main": NoMultipleMain,
9300
+ "no-raw-characters": NoRawCharacters,
9301
+ "no-redundant-for": NoRedundantFor,
9302
+ "no-redundant-role": NoRedundantRole,
9303
+ "no-self-closing": NoSelfClosing,
9304
+ "no-style-tag": NoStyleTag,
9305
+ "no-trailing-whitespace": NoTrailingWhitespace,
9306
+ "no-unknown-elements": NoUnknownElements,
9307
+ "no-utf8-bom": NoUtf8Bom,
9308
+ "prefer-button": PreferButton,
9309
+ "prefer-native-element": PreferNativeElement,
9310
+ "prefer-tbody": PreferTbody,
9311
+ "require-sri": RequireSri,
9312
+ "script-element": ScriptElement,
9313
+ "script-type": ScriptType,
9314
+ "svg-focusable": SvgFocusable,
9315
+ "text-content": TextContent,
9316
+ "unrecognized-char-ref": UnknownCharReference,
9317
+ void: Void,
9318
+ "void-content": VoidContent,
9319
+ "void-style": VoidStyle,
9320
+ ...bundledRules$1,
9321
+ };
9040
9322
 
9041
9323
  var defaultConfig = {};
9042
9324
 
@@ -9274,7 +9556,7 @@ function overwriteMerge(a, b) {
9274
9556
  return b;
9275
9557
  }
9276
9558
  function mergeInternal(base, rhs) {
9277
- const dst = deepmerge(base, Object.assign(Object.assign({}, rhs), { rules: {} }));
9559
+ const dst = deepmerge(base, { ...rhs, rules: {} });
9278
9560
  /* rules need some special care, should overwrite arrays instead of
9279
9561
  * concaternation, i.e. ["error", {...options}] should not be merged by
9280
9562
  * appending to old value */
@@ -9499,7 +9781,7 @@ class Config {
9499
9781
  * @internal primary purpose is unittests
9500
9782
  */
9501
9783
  get() {
9502
- const config = Object.assign({}, this.config);
9784
+ const config = { ...this.config };
9503
9785
  if (config.elements) {
9504
9786
  config.elements = config.elements.map((cur) => {
9505
9787
  if (typeof cur === "string") {
@@ -9585,7 +9867,11 @@ class Config {
9585
9867
  if (!properties) {
9586
9868
  continue;
9587
9869
  }
9588
- for (const [key, schema] of Object.entries(properties)) {
9870
+ for (const [raw, schema] of Object.entries(properties)) {
9871
+ /* at compile time this is a fixed list but the point of this method is
9872
+ * to augment the runtime with additional keys so it is a bit of lying
9873
+ * to typescript */
9874
+ const key = raw;
9589
9875
  if (schema.copyable && !MetaCopyableProperty.includes(key)) {
9590
9876
  MetaCopyableProperty.push(key);
9591
9877
  }
@@ -9781,31 +10067,6 @@ class ConfigLoader {
9781
10067
  }
9782
10068
  }
9783
10069
 
9784
- /*! *****************************************************************************
9785
- Copyright (c) Microsoft Corporation. All rights reserved.
9786
- Licensed under the Apache License, Version 2.0 (the "License"); you may not use
9787
- this file except in compliance with the License. You may obtain a copy of the
9788
- License at http://www.apache.org/licenses/LICENSE-2.0
9789
-
9790
- THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
9791
- KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
9792
- WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
9793
- MERCHANTABLITY OR NON-INFRINGEMENT.
9794
-
9795
- See the Apache Version 2.0 License for specific language governing permissions
9796
- and limitations under the License.
9797
- ***************************************************************************** */
9798
-
9799
- function __rest(s, e) {
9800
- var t = {};
9801
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
9802
- t[p] = s[p];
9803
- if (s != null && typeof Object.getOwnPropertySymbols === "function")
9804
- for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) if (e.indexOf(p[i]) < 0)
9805
- t[p[i]] = s[p[i]];
9806
- return t;
9807
- }
9808
-
9809
10070
  class EventHandler {
9810
10071
  constructor() {
9811
10072
  this.listeners = {};
@@ -10379,7 +10640,7 @@ class Reporter {
10379
10640
  merged[key].messages = [...merged[key].messages, ...result.messages];
10380
10641
  }
10381
10642
  else {
10382
- merged[key] = Object.assign({}, result);
10643
+ merged[key] = { ...result };
10383
10644
  }
10384
10645
  });
10385
10646
  });
@@ -10481,14 +10742,16 @@ function messageSort(a, b) {
10481
10742
  }
10482
10743
 
10483
10744
  class Engine {
10484
- constructor(config, configData, ParserClass) {
10745
+ constructor(config, ParserClass) {
10485
10746
  this.report = new Reporter();
10486
- this.configData = configData;
10487
10747
  this.config = config;
10488
10748
  this.ParserClass = ParserClass;
10489
10749
  /* initialize plugins and rules */
10490
10750
  const result = this.initPlugins(this.config);
10491
- this.availableRules = Object.assign(Object.assign({}, bundledRules), result.availableRules);
10751
+ this.availableRules = {
10752
+ ...bundledRules,
10753
+ ...result.availableRules,
10754
+ };
10492
10755
  }
10493
10756
  /**
10494
10757
  * Lint sources and return report
@@ -10513,13 +10776,13 @@ class Engine {
10513
10776
  /* trigger configuration ready event */
10514
10777
  const configEvent = {
10515
10778
  location,
10516
- config: this.configData,
10779
+ config: this.config,
10517
10780
  rules,
10518
10781
  };
10519
10782
  parser.trigger("config:ready", configEvent);
10520
10783
  /* trigger source ready event */
10521
10784
  /* eslint-disable-next-line @typescript-eslint/no-unused-vars -- object destructured on purpose to remove property */
10522
- const sourceData = __rest(source, ["hooks"]);
10785
+ const { hooks: _, ...sourceData } = source;
10523
10786
  const sourceEvent = {
10524
10787
  location,
10525
10788
  source: sourceData,
@@ -10826,133 +11089,31 @@ class Engine {
10826
11089
  }
10827
11090
 
10828
11091
  /**
10829
- * @internal
10830
- */
10831
- function findConfigurationFiles(directory) {
10832
- return ["json", "cjs", "js"]
10833
- .map((extension) => path.join(directory, `.htmlvalidate.${extension}`))
10834
- .filter((filePath) => fs.existsSync(filePath));
10835
- }
10836
- /**
10837
- * Loads configuration by traversing filesystem.
10838
- *
10839
- * Configuration is read from three sources and in the following order:
10840
- *
10841
- * 1. Global configuration passed to constructor.
10842
- * 2. Configuration files found when traversing the directory structure.
10843
- * 3. Override passed to this function.
10844
- *
10845
- * The following configuration filenames are searched:
10846
- *
10847
- * - `.htmlvalidate.json`
10848
- * - `.htmlvalidate.js`
10849
- * - `.htmlvalidate.cjs`
10850
- *
10851
- * Global configuration is used when no configuration file is found. The
10852
- * result is always merged with override if present.
10853
- *
10854
- * The `root` property set to `true` affects the configuration as following:
11092
+ * The static configuration loader does not do any per-handle lookup. Only the
11093
+ * global or per-call configuration is used.
10855
11094
  *
10856
- * 1. If set in override the override is returned as-is.
10857
- * 2. If set in the global config the override is merged into global and
10858
- * returned. No configuration files are searched.
10859
- * 3. Setting `root` in configuration file only stops directory traversal.
11095
+ * In practice this means no configuration is fetch by traversing the
11096
+ * filesystem.
10860
11097
  */
10861
- class FileSystemConfigLoader extends ConfigLoader {
10862
- /**
10863
- * @param config - Global configuration
10864
- * @param configFactory - Optional configuration factory
10865
- */
10866
- constructor(config, configFactory = Config) {
10867
- super(config, configFactory);
10868
- this.cache = new Map();
10869
- }
10870
- /**
10871
- * Get configuration for given filename.
10872
- *
10873
- * @param filename - Filename to get configuration for.
10874
- * @param configOverride - Configuration to merge final result with.
10875
- */
10876
- getConfigFor(filename, configOverride) {
10877
- /* special case when the overridden configuration is marked as root, should
10878
- * not try to load any more configuration files */
11098
+ class StaticConfigLoader extends ConfigLoader {
11099
+ getConfigFor(handle, configOverride) {
10879
11100
  const override = this.loadFromObject(configOverride || {});
10880
11101
  if (override.isRootFound()) {
10881
11102
  override.init();
10882
11103
  return override;
10883
11104
  }
10884
- /* special case when the global configuration is marked as root, should not
10885
- * try to load and more configuration files */
10886
- if (this.globalConfig.isRootFound()) {
10887
- const merged = this.globalConfig.merge(override);
10888
- merged.init();
10889
- return merged;
10890
- }
10891
- const config = this.fromFilename(filename);
10892
- const merged = config ? config.merge(override) : this.globalConfig.merge(override);
11105
+ const merged = this.globalConfig.merge(override);
10893
11106
  merged.init();
10894
11107
  return merged;
10895
11108
  }
10896
- /**
10897
- * Flush configuration cache.
10898
- *
10899
- * @param filename - If given only the cache for that file is flushed.
10900
- */
10901
- flushCache(filename) {
10902
- if (filename) {
10903
- this.cache.delete(filename);
10904
- }
10905
- else {
10906
- this.cache.clear();
10907
- }
10908
- }
10909
- /**
10910
- * Load raw configuration from directory traversal.
10911
- *
10912
- * This configuration is not merged with global configuration and may return
10913
- * `null` if no configuration files are found.
10914
- */
10915
- fromFilename(filename) {
10916
- var _a;
10917
- if (filename === "inline") {
10918
- return null;
10919
- }
10920
- if (this.cache.has(filename)) {
10921
- return (_a = this.cache.get(filename)) !== null && _a !== void 0 ? _a : null;
10922
- }
10923
- let found = false;
10924
- let current = path.resolve(path.dirname(filename));
10925
- let config = this.empty();
10926
- // eslint-disable-next-line no-constant-condition
10927
- while (true) {
10928
- /* search configuration files in current directory */
10929
- for (const configFile of findConfigurationFiles(current)) {
10930
- const local = this.loadFromFile(configFile);
10931
- found = true;
10932
- config = local.merge(config);
10933
- }
10934
- /* stop if a configuration with "root" is set to true */
10935
- if (config.isRootFound()) {
10936
- break;
10937
- }
10938
- /* get the parent directory */
10939
- const child = current;
10940
- current = path.dirname(current);
10941
- /* stop if this is the root directory */
10942
- if (current === child) {
10943
- break;
10944
- }
10945
- }
10946
- /* no config was found by loader, return null and let caller decide what to do */
10947
- if (!found) {
10948
- this.cache.set(filename, null);
10949
- return null;
10950
- }
10951
- this.cache.set(filename, config);
10952
- return config;
11109
+ flushCache() {
11110
+ /* do nothing */
10953
11111
  }
10954
11112
  defaultConfig() {
10955
- return this.configFactory.defaultConfig();
11113
+ return this.loadFromObject({
11114
+ extends: ["html-validate:recommended"],
11115
+ elements: ["html5"],
11116
+ });
10956
11117
  }
10957
11118
  }
10958
11119
 
@@ -10976,7 +11137,7 @@ function isConfigData(value) {
10976
11137
  class HtmlValidate {
10977
11138
  constructor(arg) {
10978
11139
  const [loader, config] = arg instanceof ConfigLoader ? [arg, undefined] : [undefined, arg];
10979
- this.configLoader = loader !== null && loader !== void 0 ? loader : new FileSystemConfigLoader(config);
11140
+ this.configLoader = loader !== null && loader !== void 0 ? loader : new StaticConfigLoader(config);
10980
11141
  }
10981
11142
  validateString(str, arg1, arg2, arg3) {
10982
11143
  const filename = typeof arg1 === "string" ? arg1 : "inline";
@@ -11003,7 +11164,7 @@ class HtmlValidate {
11003
11164
  const config = this.getConfigFor(input.filename, configOverride);
11004
11165
  const resolved = config.resolve();
11005
11166
  const source = resolved.transformSource(input);
11006
- const engine = new Engine(resolved, config.get(), Parser);
11167
+ const engine = new Engine(resolved, Parser);
11007
11168
  return engine.lint(source);
11008
11169
  }
11009
11170
  /**
@@ -11017,7 +11178,7 @@ class HtmlValidate {
11017
11178
  const config = this.getConfigFor(filename);
11018
11179
  const resolved = config.resolve();
11019
11180
  const source = resolved.transformFilename(filename);
11020
- const engine = new Engine(resolved, config.get(), Parser);
11181
+ const engine = new Engine(resolved, Parser);
11021
11182
  return engine.lint(source);
11022
11183
  }
11023
11184
  /**
@@ -11062,7 +11223,7 @@ class HtmlValidate {
11062
11223
  const config = this.getConfigFor(filename);
11063
11224
  const resolved = config.resolve();
11064
11225
  const source = resolved.transformFilename(filename);
11065
- const engine = new Engine(resolved, config.get(), Parser);
11226
+ const engine = new Engine(resolved, Parser);
11066
11227
  return engine.dumpTokens(source);
11067
11228
  }
11068
11229
  /**
@@ -11077,7 +11238,7 @@ class HtmlValidate {
11077
11238
  const config = this.getConfigFor(filename);
11078
11239
  const resolved = config.resolve();
11079
11240
  const source = resolved.transformFilename(filename);
11080
- const engine = new Engine(resolved, config.get(), Parser);
11241
+ const engine = new Engine(resolved, Parser);
11081
11242
  return engine.dumpEvents(source);
11082
11243
  }
11083
11244
  /**
@@ -11092,7 +11253,7 @@ class HtmlValidate {
11092
11253
  const config = this.getConfigFor(filename);
11093
11254
  const resolved = config.resolve();
11094
11255
  const source = resolved.transformFilename(filename);
11095
- const engine = new Engine(resolved, config.get(), Parser);
11256
+ const engine = new Engine(resolved, Parser);
11096
11257
  return engine.dumpTree(source);
11097
11258
  }
11098
11259
  /**
@@ -11170,7 +11331,7 @@ class HtmlValidate {
11170
11331
  */
11171
11332
  getRuleDocumentation(ruleId, config = null, context = null) {
11172
11333
  const c = config || this.getConfigFor("inline");
11173
- const engine = new Engine(c.resolve(), c.get(), Parser);
11334
+ const engine = new Engine(c.resolve(), Parser);
11174
11335
  return engine.getRuleDocumentation(ruleId, context);
11175
11336
  }
11176
11337
  /**
@@ -11207,35 +11368,6 @@ class HtmlValidate {
11207
11368
  }
11208
11369
  }
11209
11370
 
11210
- /**
11211
- * The static configuration loader does not do any per-handle lookup. Only the
11212
- * global or per-call configuration is used.
11213
- *
11214
- * In practice this means no configuration is fetch by traversing the
11215
- * filesystem.
11216
- */
11217
- class StaticConfigLoader extends ConfigLoader {
11218
- getConfigFor(handle, configOverride) {
11219
- const override = this.loadFromObject(configOverride || {});
11220
- if (override.isRootFound()) {
11221
- override.init();
11222
- return override;
11223
- }
11224
- const merged = this.globalConfig.merge(override);
11225
- merged.init();
11226
- return merged;
11227
- }
11228
- flushCache() {
11229
- /* do nothing */
11230
- }
11231
- defaultConfig() {
11232
- return this.loadFromObject({
11233
- extends: ["html-validate:recommended"],
11234
- elements: ["html5"],
11235
- });
11236
- }
11237
- }
11238
-
11239
11371
  const defaults$1 = {
11240
11372
  silent: false,
11241
11373
  version,
@@ -11253,7 +11385,7 @@ const defaults$1 = {
11253
11385
  * @returns - `true` if version is compatible
11254
11386
  */
11255
11387
  function compatibilityCheck(name, declared, options) {
11256
- const { silent, version: current, logger } = Object.assign(Object.assign({}, defaults$1), options);
11388
+ const { silent, version: current, logger } = { ...defaults$1, ...options };
11257
11389
  const valid = semver.satisfies(current, declared);
11258
11390
  if (valid || silent) {
11259
11391
  return valid;
@@ -11283,6 +11415,137 @@ function ruleExists(ruleId) {
11283
11415
  return ruleIds.has(ruleId);
11284
11416
  }
11285
11417
 
11418
+ /**
11419
+ * @internal
11420
+ */
11421
+ function findConfigurationFiles(directory) {
11422
+ return ["json", "cjs", "js"]
11423
+ .map((extension) => path.join(directory, `.htmlvalidate.${extension}`))
11424
+ .filter((filePath) => fs.existsSync(filePath));
11425
+ }
11426
+ /**
11427
+ * Loads configuration by traversing filesystem.
11428
+ *
11429
+ * Configuration is read from three sources and in the following order:
11430
+ *
11431
+ * 1. Global configuration passed to constructor.
11432
+ * 2. Configuration files found when traversing the directory structure.
11433
+ * 3. Override passed to this function.
11434
+ *
11435
+ * The following configuration filenames are searched:
11436
+ *
11437
+ * - `.htmlvalidate.json`
11438
+ * - `.htmlvalidate.js`
11439
+ * - `.htmlvalidate.cjs`
11440
+ *
11441
+ * Global configuration is used when no configuration file is found. The
11442
+ * result is always merged with override if present.
11443
+ *
11444
+ * The `root` property set to `true` affects the configuration as following:
11445
+ *
11446
+ * 1. If set in override the override is returned as-is.
11447
+ * 2. If set in the global config the override is merged into global and
11448
+ * returned. No configuration files are searched.
11449
+ * 3. Setting `root` in configuration file only stops directory traversal.
11450
+ */
11451
+ class FileSystemConfigLoader extends ConfigLoader {
11452
+ /**
11453
+ * @param config - Global configuration
11454
+ * @param configFactory - Optional configuration factory
11455
+ */
11456
+ constructor(config, configFactory = Config) {
11457
+ super(config, configFactory);
11458
+ this.cache = new Map();
11459
+ }
11460
+ /**
11461
+ * Get configuration for given filename.
11462
+ *
11463
+ * @param filename - Filename to get configuration for.
11464
+ * @param configOverride - Configuration to merge final result with.
11465
+ */
11466
+ getConfigFor(filename, configOverride) {
11467
+ /* special case when the overridden configuration is marked as root, should
11468
+ * not try to load any more configuration files */
11469
+ const override = this.loadFromObject(configOverride || {});
11470
+ if (override.isRootFound()) {
11471
+ override.init();
11472
+ return override;
11473
+ }
11474
+ /* special case when the global configuration is marked as root, should not
11475
+ * try to load and more configuration files */
11476
+ if (this.globalConfig.isRootFound()) {
11477
+ const merged = this.globalConfig.merge(override);
11478
+ merged.init();
11479
+ return merged;
11480
+ }
11481
+ const config = this.fromFilename(filename);
11482
+ const merged = config ? config.merge(override) : this.globalConfig.merge(override);
11483
+ merged.init();
11484
+ return merged;
11485
+ }
11486
+ /**
11487
+ * Flush configuration cache.
11488
+ *
11489
+ * @param filename - If given only the cache for that file is flushed.
11490
+ */
11491
+ flushCache(filename) {
11492
+ if (filename) {
11493
+ this.cache.delete(filename);
11494
+ }
11495
+ else {
11496
+ this.cache.clear();
11497
+ }
11498
+ }
11499
+ /**
11500
+ * Load raw configuration from directory traversal.
11501
+ *
11502
+ * This configuration is not merged with global configuration and may return
11503
+ * `null` if no configuration files are found.
11504
+ */
11505
+ fromFilename(filename) {
11506
+ var _a;
11507
+ if (filename === "inline") {
11508
+ return null;
11509
+ }
11510
+ if (this.cache.has(filename)) {
11511
+ return (_a = this.cache.get(filename)) !== null && _a !== void 0 ? _a : null;
11512
+ }
11513
+ let found = false;
11514
+ let current = path.resolve(path.dirname(filename));
11515
+ let config = this.empty();
11516
+ // eslint-disable-next-line no-constant-condition
11517
+ while (true) {
11518
+ /* search configuration files in current directory */
11519
+ for (const configFile of findConfigurationFiles(current)) {
11520
+ const local = this.loadFromFile(configFile);
11521
+ found = true;
11522
+ config = local.merge(config);
11523
+ }
11524
+ /* stop if a configuration with "root" is set to true */
11525
+ if (config.isRootFound()) {
11526
+ break;
11527
+ }
11528
+ /* get the parent directory */
11529
+ const child = current;
11530
+ current = path.dirname(current);
11531
+ /* stop if this is the root directory */
11532
+ if (current === child) {
11533
+ break;
11534
+ }
11535
+ }
11536
+ /* no config was found by loader, return null and let caller decide what to do */
11537
+ if (!found) {
11538
+ this.cache.set(filename, null);
11539
+ return null;
11540
+ }
11541
+ this.cache.set(filename, config);
11542
+ return config;
11543
+ }
11544
+ defaultConfig() {
11545
+ return this.configFactory.defaultConfig();
11546
+ }
11547
+ }
11548
+
11286
11549
  const entities = {
11287
11550
  ">": "&gt;",
11288
11551
  "<": "&lt;",
@@ -11433,7 +11696,7 @@ function formatSummary(errors, warnings) {
11433
11696
  return kleur[summaryColor]().bold(`${summary.join(" and ")} found.`);
11434
11697
  }
11435
11698
  function codeframe(results, options) {
11436
- const merged = Object.assign(Object.assign({}, defaults), options);
11699
+ const merged = { ...defaults, ...options };
11437
11700
  let errors = 0;
11438
11701
  let warnings = 0;
11439
11702
  const resultsWithMessages = results.filter((result) => result.messages.length > 0);
@@ -11474,7 +11737,11 @@ function linkSummary(results) {
11474
11737
  return `\n${kleur.bold("More information")}:\n${lines.join("")}\n`;
11475
11738
  }
11476
11739
  function stylish(results) {
11477
- const errors = stylishImpl(results.map((it) => (Object.assign(Object.assign({}, it), { fixableErrorCount: 0, fixableWarningCount: 0 }))));
11740
+ const errors = stylishImpl(results.map((it) => ({
11741
+ ...it,
11742
+ fixableErrorCount: 0,
11743
+ fixableWarningCount: 0,
11744
+ })));
11478
11745
  const links = linkSummary(results);
11479
11746
  return `${errors}${links}`;
11480
11747
  }
@@ -11525,5 +11792,5 @@ function getFormatter(name) {
11525
11792
  return (_a = availableFormatters[name]) !== null && _a !== void 0 ? _a : null;
11526
11793
  }
11527
11794
 
11528
- export { Config as C, DynamicValue as D, EventHandler as E, FileSystemConfigLoader as F, HtmlValidate as H, MetaTable as M, NodeClosed as N, Parser as P, Rule as R, Severity as S, TextNode as T, UserError as U, ConfigError as a, ConfigLoader as b, StaticConfigLoader as c, HtmlElement as d, SchemaValidationError as e, MetaCopyableProperty as f, Reporter as g, TemplateExtractor as h, getFormatter as i, compatibilityCheck as j, bugs as k, legacyRequire as l, TokenType as m, name as n, presets as p, ruleExists as r, version as v };
11795
+ export { Config as C, DynamicValue as D, EventHandler as E, FileSystemConfigLoader as F, HtmlValidate as H, MetaTable as M, NodeClosed as N, Parser as P, Rule as R, Severity as S, TextNode as T, UserError as U, ConfigError as a, ConfigLoader as b, StaticConfigLoader as c, HtmlElement as d, SchemaValidationError as e, MetaCopyableProperty as f, Reporter as g, TemplateExtractor as h, getFormatter as i, compatibilityCheck as j, TokenType as k, legacyRequire as l, bugs as m, name as n, presets as p, ruleExists as r, version as v };
11529
11796
  //# sourceMappingURL=core.js.map