html-validate 5.5.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.
package/dist/cjs/core.js CHANGED
@@ -268,7 +268,7 @@ class UserError extends NestedError {
268
268
  }
269
269
 
270
270
  function getSummary(schema, obj, errors) {
271
- const output = betterAjvErrors__default['default'](schema, obj, errors, {
271
+ const output = betterAjvErrors__default["default"](schema, obj, errors, {
272
272
  format: "js",
273
273
  });
274
274
  // istanbul ignore next: for safety only
@@ -285,15 +285,15 @@ class SchemaValidationError extends UserError {
285
285
  }
286
286
  prettyError() {
287
287
  const json = this.getRawJSON();
288
- return betterAjvErrors__default['default'](this.schema, this.obj, this.errors, {
288
+ return betterAjvErrors__default["default"](this.schema, this.obj, this.errors, {
289
289
  format: "cli",
290
290
  indent: 2,
291
291
  json,
292
292
  });
293
293
  }
294
294
  getRawJSON() {
295
- if (this.filename && fs__default['default'].existsSync(this.filename)) {
296
- return fs__default['default'].readFileSync(this.filename, "utf-8");
295
+ if (this.filename && fs__default["default"].existsSync(this.filename)) {
296
+ return fs__default["default"].readFileSync(this.filename, "utf-8");
297
297
  }
298
298
  else {
299
299
  return null;
@@ -301,9 +301,9 @@ class SchemaValidationError extends UserError {
301
301
  }
302
302
  }
303
303
 
304
- const projectRoot = path__default['default'].resolve(__dirname, "../../");
304
+ const projectRoot = path__default["default"].resolve(__dirname, "../../");
305
305
  const legacyRequire = require;
306
- const distFolder = path__default['default'].resolve(projectRoot, "dist/cjs");
306
+ const distFolder = path__default["default"].resolve(projectRoot, "dist/cjs");
307
307
 
308
308
  /**
309
309
  * Similar to `require(..)` but removes the cached copy first.
@@ -586,18 +586,63 @@ const definitions = {
586
586
  type: "object",
587
587
  patternProperties: {
588
588
  "^.*$": {
589
- type: "array",
590
- uniqueItems: true,
591
- items: {
592
- anyOf: [
593
- {
589
+ anyOf: [
590
+ {
591
+ type: "object",
592
+ additionalProperties: false,
593
+ properties: {
594
+ boolean: {
595
+ type: "boolean",
596
+ title: "Set to true if this is a boolean attribute"
597
+ },
598
+ deprecated: {
599
+ title: "Set to true or string if this attribute is deprecated",
600
+ oneOf: [
601
+ {
602
+ type: "boolean"
603
+ },
604
+ {
605
+ type: "string"
606
+ }
607
+ ]
608
+ },
609
+ list: {
610
+ type: "boolean",
611
+ title: "Set to true if this attribute is a list of space-separated tokens, each which must be valid by itself"
612
+ },
613
+ "enum": {
614
+ type: "array",
615
+ title: "Exhaustive list of values (string or regex) this attribute accepts",
616
+ uniqueItems: true,
617
+ items: {
618
+ anyOf: [
619
+ {
620
+ type: "string"
621
+ },
622
+ {
623
+ regexp: true
624
+ }
625
+ ]
626
+ }
627
+ },
628
+ omit: {
629
+ type: "boolean",
630
+ title: "Set to true if this attribute can optionally omit its value"
631
+ },
632
+ required: {
633
+ type: "boolean",
634
+ title: "Set to true if this attribute is required"
635
+ }
636
+ }
637
+ },
638
+ {
639
+ type: "array",
640
+ uniqueItems: true,
641
+ items: {
594
642
  type: "string"
595
- },
596
- {
597
- regexp: true
598
643
  }
599
- ]
600
- }
644
+ }
645
+ ]
601
646
  }
602
647
  }
603
648
  },
@@ -648,6 +693,111 @@ var schema = {
648
693
  definitions: definitions
649
694
  };
650
695
 
696
+ var TextContent$1;
697
+ (function (TextContent) {
698
+ /* forbid node to have text content, inter-element whitespace is ignored */
699
+ TextContent["NONE"] = "none";
700
+ /* node can have text but not required too */
701
+ TextContent["DEFAULT"] = "default";
702
+ /* node requires text-nodes to be present (direct or by descendant) */
703
+ TextContent["REQUIRED"] = "required";
704
+ /* node requires accessible text (hidden text is ignored, tries to get text from accessibility tree) */
705
+ TextContent["ACCESSIBLE"] = "accessible";
706
+ })(TextContent$1 || (TextContent$1 = {}));
707
+ /**
708
+ * Properties listed here can be copied (loaded) onto another element using
709
+ * [[HtmlElement.loadMeta]].
710
+ */
711
+ const MetaCopyableProperty = [
712
+ "metadata",
713
+ "flow",
714
+ "sectioning",
715
+ "heading",
716
+ "phrasing",
717
+ "embedded",
718
+ "interactive",
719
+ "transparent",
720
+ "form",
721
+ "labelable",
722
+ "attributes",
723
+ "permittedContent",
724
+ "permittedDescendants",
725
+ "permittedOrder",
726
+ "requiredAncestors",
727
+ "requiredContent",
728
+ ];
729
+ /**
730
+ * @internal
731
+ */
732
+ function setMetaProperty(dst, key, value) {
733
+ dst[key] = value;
734
+ }
735
+
736
+ function isSet(value) {
737
+ return typeof value !== "undefined";
738
+ }
739
+ function flag(value) {
740
+ return value ? true : undefined;
741
+ }
742
+ function stripUndefined(src) {
743
+ const entries = Object.entries(src).filter(([, value]) => isSet(value));
744
+ return Object.fromEntries(entries);
745
+ }
746
+ function migrateSingleAttribute(src, key) {
747
+ var _a, _b;
748
+ const result = {};
749
+ result.deprecated = flag((_a = src.deprecatedAttributes) === null || _a === void 0 ? void 0 : _a.includes(key));
750
+ result.required = flag((_b = src.requiredAttributes) === null || _b === void 0 ? void 0 : _b.includes(key));
751
+ result.omit = undefined;
752
+ const attr = src.attributes ? src.attributes[key] : undefined;
753
+ if (typeof attr === "undefined") {
754
+ return stripUndefined(result);
755
+ }
756
+ /* when the attribute is set to null we use a special property "delete" to
757
+ * flag it, if it is still set during merge (inheritance, overwriting, etc) the attribute will be removed */
758
+ if (attr === null) {
759
+ result.delete = true;
760
+ return stripUndefined(result);
761
+ }
762
+ if (Array.isArray(attr)) {
763
+ if (attr.length === 0) {
764
+ result.boolean = true;
765
+ }
766
+ else {
767
+ result.enum = attr.filter((it) => it !== "");
768
+ if (attr.includes("")) {
769
+ result.omit = true;
770
+ }
771
+ }
772
+ return stripUndefined(result);
773
+ }
774
+ else {
775
+ return stripUndefined({ ...result, ...attr });
776
+ }
777
+ }
778
+ function migrateAttributes(src) {
779
+ var _a, _b, _c;
780
+ const keys = [
781
+ ...Object.keys((_a = src.attributes) !== null && _a !== void 0 ? _a : {}),
782
+ ...((_b = src.requiredAttributes) !== null && _b !== void 0 ? _b : []),
783
+ ...((_c = src.deprecatedAttributes) !== null && _c !== void 0 ? _c : []),
784
+ ].sort();
785
+ const entries = keys.map((key) => {
786
+ return [key, migrateSingleAttribute(src, key)];
787
+ });
788
+ return Object.fromEntries(entries);
789
+ }
790
+ function migrateElement(src) {
791
+ const result = {
792
+ ...src,
793
+ attributes: migrateAttributes(src),
794
+ };
795
+ /* removed properties */
796
+ delete result.deprecatedAttributes;
797
+ delete result.requiredAttributes;
798
+ return result;
799
+ }
800
+
651
801
  const dynamicKeys = [
652
802
  "metadata",
653
803
  "flow",
@@ -710,7 +860,7 @@ class MetaTable {
710
860
  */
711
861
  extendValidationSchema(patch) {
712
862
  if (patch.properties) {
713
- this.schema = jsonMergePatch__default['default'].apply(this.schema, {
863
+ this.schema = jsonMergePatch__default["default"].apply(this.schema, {
714
864
  patternProperties: {
715
865
  "^[^$].*$": {
716
866
  properties: patch.properties,
@@ -719,7 +869,7 @@ class MetaTable {
719
869
  });
720
870
  }
721
871
  if (patch.definitions) {
722
- this.schema = jsonMergePatch__default['default'].apply(this.schema, {
872
+ this.schema = jsonMergePatch__default["default"].apply(this.schema, {
723
873
  definitions: patch.definitions,
724
874
  });
725
875
  }
@@ -741,7 +891,7 @@ class MetaTable {
741
891
  for (const [key, value] of Object.entries(obj)) {
742
892
  if (key === "$schema")
743
893
  continue;
744
- this.addEntry(key, value);
894
+ this.addEntry(key, migrateElement(value));
745
895
  }
746
896
  }
747
897
  /**
@@ -769,7 +919,7 @@ class MetaTable {
769
919
  */
770
920
  getMetaFor(tagName) {
771
921
  tagName = tagName.toLowerCase();
772
- return this.elements[tagName] ? Object.assign({}, this.elements[tagName]) : null;
922
+ return this.elements[tagName] ? { ...this.elements[tagName] } : null;
773
923
  }
774
924
  /**
775
925
  * Find all tags which has enabled given property.
@@ -798,7 +948,7 @@ class MetaTable {
798
948
  }
799
949
  }
800
950
  /* merge all sources together */
801
- const expanded = deepmerge__default['default'](parent, Object.assign(Object.assign({}, entry), { tagName }), { arrayMerge: overwriteMerge$1 });
951
+ const expanded = this.mergeElement(parent, { ...entry, tagName });
802
952
  expandRegex(expanded);
803
953
  this.elements[tagName] = expanded;
804
954
  }
@@ -806,7 +956,7 @@ class MetaTable {
806
956
  * Construct a new AJV schema validator.
807
957
  */
808
958
  getSchemaValidator() {
809
- const ajv = new Ajv__default['default']({ strict: true, strictTuples: true, strictTypes: true });
959
+ const ajv = new Ajv__default["default"]({ strict: true, strictTuples: true, strictTypes: true });
810
960
  ajv.addMetaSchema(ajvSchemaDraft);
811
961
  ajv.addKeyword(ajvRegexpKeyword);
812
962
  ajv.addKeyword({ keyword: "copyable" });
@@ -836,7 +986,16 @@ class MetaTable {
836
986
  }
837
987
  }
838
988
  mergeElement(a, b) {
839
- return deepmerge__default['default'](a, b, { arrayMerge: overwriteMerge$1 });
989
+ const merged = deepmerge__default["default"](a, b, { arrayMerge: overwriteMerge$1 });
990
+ /* special handling when removing attributes by setting them to null
991
+ * resulting in the deletion flag being set */
992
+ const filteredAttrs = Object.entries(merged.attributes).filter(([, attr]) => {
993
+ const val = !attr.delete;
994
+ delete attr.delete;
995
+ return val;
996
+ });
997
+ merged.attributes = Object.fromEntries(filteredAttrs);
998
+ return merged;
840
999
  }
841
1000
  resolve(node) {
842
1001
  if (node.meta) {
@@ -848,7 +1007,7 @@ function expandProperties(node, entry) {
848
1007
  for (const key of dynamicKeys) {
849
1008
  const property = entry[key];
850
1009
  if (property && typeof property !== "boolean") {
851
- entry[key] = evaluateProperty(node, property);
1010
+ setMetaProperty(entry, key, evaluateProperty(node, property));
852
1011
  }
853
1012
  }
854
1013
  }
@@ -874,14 +1033,9 @@ function expandRegexValue(value) {
874
1033
  * Expand all regular expressions in strings ("/../"). This mutates the object.
875
1034
  */
876
1035
  function expandRegex(entry) {
877
- if (!entry.attributes)
878
- return;
879
1036
  for (const [name, values] of Object.entries(entry.attributes)) {
880
- if (values) {
881
- entry.attributes[name] = values.map(expandRegexValue);
882
- }
883
- else {
884
- delete entry.attributes[name];
1037
+ if (values.enum) {
1038
+ entry.attributes[name].enum = values.enum.map(expandRegexValue);
885
1039
  }
886
1040
  }
887
1041
  }
@@ -937,41 +1091,6 @@ function matchAttribute(node, match) {
937
1091
  }
938
1092
  }
939
1093
 
940
- var TextContent$1;
941
- (function (TextContent) {
942
- /* forbid node to have text content, inter-element whitespace is ignored */
943
- TextContent["NONE"] = "none";
944
- /* node can have text but not required too */
945
- TextContent["DEFAULT"] = "default";
946
- /* node requires text-nodes to be present (direct or by descendant) */
947
- TextContent["REQUIRED"] = "required";
948
- /* node requires accessible text (hidden text is ignored, tries to get text from accessibility tree) */
949
- TextContent["ACCESSIBLE"] = "accessible";
950
- })(TextContent$1 || (TextContent$1 = {}));
951
- /**
952
- * Properties listed here can be copied (loaded) onto another element using
953
- * [[HtmlElement.loadMeta]].
954
- */
955
- const MetaCopyableProperty = [
956
- "metadata",
957
- "flow",
958
- "sectioning",
959
- "heading",
960
- "phrasing",
961
- "embedded",
962
- "interactive",
963
- "transparent",
964
- "form",
965
- "labelable",
966
- "requiredAttributes",
967
- "attributes",
968
- "permittedContent",
969
- "permittedDescendants",
970
- "permittedOrder",
971
- "requiredAncestors",
972
- "requiredContent",
973
- ];
974
-
975
1094
  class DynamicValue {
976
1095
  constructor(expr) {
977
1096
  this.expr = expr;
@@ -1823,8 +1942,9 @@ class HtmlElement extends DOMNode {
1823
1942
  this.metaElement = {};
1824
1943
  }
1825
1944
  for (const key of MetaCopyableProperty) {
1826
- if (typeof meta[key] !== "undefined") {
1827
- this.metaElement[key] = meta[key];
1945
+ const value = meta[key];
1946
+ if (typeof value !== "undefined") {
1947
+ setMetaProperty(this.metaElement, key, value);
1828
1948
  }
1829
1949
  else {
1830
1950
  delete this.metaElement[key];
@@ -2240,6 +2360,7 @@ class Validator {
2240
2360
  * @param rules - Element attribute metadta.
2241
2361
  * @returns `true` if attribute passes all tests.
2242
2362
  */
2363
+ /* eslint-disable-next-line complexity */
2243
2364
  static validateAttribute(attr, rules) {
2244
2365
  const rule = rules[attr.key];
2245
2366
  if (!rule) {
@@ -2253,19 +2374,34 @@ class Validator {
2253
2374
  return true;
2254
2375
  }
2255
2376
  const empty = value === null || value === "";
2256
- /* consider an empty array as being a boolean attribute */
2257
- if (rule.length === 0) {
2377
+ /* if boolean is set the value can be either null, empty string or the
2378
+ * attribute key (attribute-boolean-style regulates style) */
2379
+ if (rule.boolean) {
2258
2380
  return empty || value === attr.key;
2259
2381
  }
2260
- /* if the empty string is present allow both "" and null
2261
- * (boolean-attribute-style will regulate which is allowed) */
2262
- if (rule.includes("") && empty) {
2382
+ /* if omit is set the value can be either null or empty string
2383
+ * (attribute-empty style regulates style) */
2384
+ if (rule.omit && empty) {
2385
+ return true;
2386
+ }
2387
+ /* validate each token when using list, all tokens must be valid */
2388
+ if (rule.list) {
2389
+ const tokens = new DOMTokenList(value, attr.valueLocation);
2390
+ return tokens.every((token) => {
2391
+ return this.validateAttributeValue(token, rule);
2392
+ });
2393
+ }
2394
+ return this.validateAttributeValue(value, rule);
2395
+ }
2396
+ static validateAttributeValue(value, rule) {
2397
+ /* skip attribute if it not have enumerated list */
2398
+ if (!rule.enum) {
2263
2399
  return true;
2264
2400
  }
2265
2401
  if (value === null || value === undefined) {
2266
2402
  return false;
2267
2403
  }
2268
- return rule.some((entry) => {
2404
+ return rule.enum.some((entry) => {
2269
2405
  if (entry instanceof RegExp) {
2270
2406
  return !!value.match(entry);
2271
2407
  }
@@ -2531,12 +2667,12 @@ var configurationSchema = {
2531
2667
  const espree = legacyRequire("espree");
2532
2668
  const walk = legacyRequire("acorn-walk");
2533
2669
  function joinTemplateLiteral(nodes) {
2534
- let offset = nodes[0].start;
2670
+ let offset = nodes[0].start + 1;
2535
2671
  let output = "";
2536
2672
  for (const node of nodes) {
2537
- output += " ".repeat(node.start - offset);
2673
+ output += " ".repeat(node.start + 1 - offset);
2538
2674
  output += node.value.raw;
2539
- offset = node.end;
2675
+ offset = node.end - 2;
2540
2676
  }
2541
2677
  return output;
2542
2678
  }
@@ -2641,7 +2777,7 @@ class TemplateExtractor {
2641
2777
  this.data = data;
2642
2778
  }
2643
2779
  static fromFilename(filename) {
2644
- const source = fs__default['default'].readFileSync(filename, "utf-8");
2780
+ const source = fs__default["default"].readFileSync(filename, "utf-8");
2645
2781
  const ast = espree.parse(source, {
2646
2782
  ecmaVersion: 2017,
2647
2783
  sourceType: "module",
@@ -2677,7 +2813,7 @@ class TemplateExtractor {
2677
2813
  * functions.
2678
2814
  */
2679
2815
  static createSource(filename) {
2680
- const data = fs__default['default'].readFileSync(filename, "utf-8");
2816
+ const data = fs__default["default"].readFileSync(filename, "utf-8");
2681
2817
  return [
2682
2818
  {
2683
2819
  column: 1,
@@ -2728,7 +2864,7 @@ var TRANSFORMER_API;
2728
2864
  })(TRANSFORMER_API || (TRANSFORMER_API = {}));
2729
2865
 
2730
2866
  const name = "html-validate";
2731
- const version = "5.5.0";
2867
+ const version = "6.0.0";
2732
2868
  const homepage = "https://html-validate.org";
2733
2869
  const bugs = {
2734
2870
  url: "https://gitlab.com/html-validate/html-validate/issues/new"
@@ -2765,7 +2901,7 @@ const remapEvents = {
2765
2901
  "tag:open": "tag:start",
2766
2902
  "tag:close": "tag:end",
2767
2903
  };
2768
- const ajv$1 = new Ajv__default['default']({ strict: true, strictTuples: true, strictTypes: true });
2904
+ const ajv$1 = new Ajv__default["default"]({ strict: true, strictTuples: true, strictTypes: true });
2769
2905
  ajv$1.addMetaSchema(ajvSchemaDraft);
2770
2906
  /**
2771
2907
  * Get (cached) schema validator for rule options.
@@ -2993,9 +3129,9 @@ function ruleDocumentationUrl(filename) {
2993
3129
  * folder and with the @/ prefix, by replacing the @ with the dist folder we
2994
3130
  * can resolve the path properly */
2995
3131
  filename = filename.replace("@", distFolder);
2996
- const p = path__default['default'].parse(filename);
2997
- const root = path__default['default'].join(distFolder, "rules");
2998
- const rel = path__default['default'].relative(root, path__default['default'].join(p.dir, p.name));
3132
+ const p = path__default["default"].parse(filename);
3133
+ const root = path__default["default"].join(distFolder, "rules");
3134
+ const rel = path__default["default"].relative(root, path__default["default"].join(p.dir, p.name));
2999
3135
  return `${homepage}/rules/${rel}.html`;
3000
3136
  }
3001
3137
 
@@ -3020,7 +3156,7 @@ const description = {
3020
3156
  };
3021
3157
  class AllowedLinks extends Rule {
3022
3158
  constructor(options) {
3023
- super(Object.assign(Object.assign({}, defaults$p), options));
3159
+ super({ ...defaults$p, ...options });
3024
3160
  }
3025
3161
  static schema() {
3026
3162
  return {
@@ -3268,7 +3404,7 @@ const defaults$o = {
3268
3404
  };
3269
3405
  class AttrCase extends Rule {
3270
3406
  constructor(options) {
3271
- super(Object.assign(Object.assign({}, defaults$o), options));
3407
+ super({ ...defaults$o, ...options });
3272
3408
  this.style = new CaseStyle(this.options.style, "attr-case");
3273
3409
  }
3274
3410
  static schema() {
@@ -3621,7 +3757,7 @@ function generateDescription(name, pattern) {
3621
3757
  }
3622
3758
  class AttrPattern extends Rule {
3623
3759
  constructor(options) {
3624
- super(Object.assign(Object.assign({}, defaults$n), options));
3760
+ super({ ...defaults$n, ...options });
3625
3761
  this.pattern = generateRegexp(this.options.pattern);
3626
3762
  }
3627
3763
  static schema() {
@@ -3687,7 +3823,7 @@ const defaults$m = {
3687
3823
  };
3688
3824
  class AttrQuotes extends Rule {
3689
3825
  constructor(options) {
3690
- super(Object.assign(Object.assign({}, defaults$m), options));
3826
+ super({ ...defaults$m, ...options });
3691
3827
  this.style = parseStyle$4(this.options.style);
3692
3828
  }
3693
3829
  static schema() {
@@ -3783,11 +3919,11 @@ class AttributeAllowedValues extends Rule {
3783
3919
  if (!context) {
3784
3920
  return docs;
3785
3921
  }
3786
- if (context.allowed.length > 0) {
3787
- const allowed = context.allowed.map((val) => `- \`${val}\``);
3922
+ if (context.allowed.enum) {
3923
+ const allowed = context.allowed.enum.map((val) => `- \`${val}\``);
3788
3924
  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")}`;
3789
3925
  }
3790
- else {
3926
+ else if (context.allowed.boolean) {
3791
3927
  docs.description = `Element <${context.element}> attribute \`${context.attribute}\` must be a boolean attribute, e.g. \`<${context.element} ${context.attribute}>\``;
3792
3928
  }
3793
3929
  return docs;
@@ -3843,7 +3979,7 @@ const defaults$l = {
3843
3979
  };
3844
3980
  class AttributeBooleanStyle extends Rule {
3845
3981
  constructor(options) {
3846
- super(Object.assign(Object.assign({}, defaults$l), options));
3982
+ super({ ...defaults$l, ...options });
3847
3983
  this.hasInvalidStyle = parseStyle$3(this.options.style);
3848
3984
  }
3849
3985
  static schema() {
@@ -3888,7 +4024,8 @@ class AttributeBooleanStyle extends Rule {
3888
4024
  });
3889
4025
  }
3890
4026
  isBoolean(attr, rules) {
3891
- return rules[attr.key] && rules[attr.key].length === 0;
4027
+ var _a;
4028
+ return Boolean((_a = rules[attr.key]) === null || _a === void 0 ? void 0 : _a.boolean);
3892
4029
  }
3893
4030
  }
3894
4031
  function parseStyle$3(style) {
@@ -3923,7 +4060,7 @@ const defaults$k = {
3923
4060
  };
3924
4061
  class AttributeEmptyStyle extends Rule {
3925
4062
  constructor(options) {
3926
- super(Object.assign(Object.assign({}, defaults$k), options));
4063
+ super({ ...defaults$k, ...options });
3927
4064
  this.hasInvalidStyle = parseStyle$2(this.options.style);
3928
4065
  }
3929
4066
  static schema() {
@@ -3972,7 +4109,8 @@ class AttributeEmptyStyle extends Rule {
3972
4109
  }
3973
4110
  }
3974
4111
  function allowsEmpty(attr, rules) {
3975
- return rules[attr.key] && rules[attr.key].includes("");
4112
+ var _a;
4113
+ return Boolean((_a = rules[attr.key]) === null || _a === void 0 ? void 0 : _a.omit);
3976
4114
  }
3977
4115
  function isEmptyValue(attr) {
3978
4116
  /* dynamic values are ignored, assumed to contain a value */
@@ -4035,7 +4173,7 @@ const defaults$j = {
4035
4173
  };
4036
4174
  class ClassPattern extends Rule {
4037
4175
  constructor(options) {
4038
- super(Object.assign(Object.assign({}, defaults$j), options));
4176
+ super({ ...defaults$j, ...options });
4039
4177
  this.pattern = parsePattern(this.options.pattern);
4040
4178
  }
4041
4179
  static schema() {
@@ -4148,7 +4286,7 @@ const defaults$i = {
4148
4286
  };
4149
4287
  class Deprecated extends Rule {
4150
4288
  constructor(options) {
4151
- super(Object.assign(Object.assign({}, defaults$i), options));
4289
+ super({ ...defaults$i, ...options });
4152
4290
  }
4153
4291
  static schema() {
4154
4292
  return {
@@ -4242,7 +4380,7 @@ class Deprecated extends Rule {
4242
4380
  this.report(node, message, location, context);
4243
4381
  }
4244
4382
  reportObject(deprecated, node, location) {
4245
- const context = Object.assign(Object.assign({}, deprecated), { tagName: node.tagName });
4383
+ const context = { ...deprecated, tagName: node.tagName };
4246
4384
  const notice = deprecated.message ? `: ${deprecated.message}` : "";
4247
4385
  const message = `<${node.tagName}> is deprecated${notice}`;
4248
4386
  this.report(node, message, location, context);
@@ -4316,7 +4454,7 @@ const defaults$h = {
4316
4454
  };
4317
4455
  class DoctypeStyle extends Rule {
4318
4456
  constructor(options) {
4319
- super(Object.assign(Object.assign({}, defaults$h), options));
4457
+ super({ ...defaults$h, ...options });
4320
4458
  }
4321
4459
  static schema() {
4322
4460
  return {
@@ -4353,7 +4491,7 @@ const defaults$g = {
4353
4491
  };
4354
4492
  class ElementCase extends Rule {
4355
4493
  constructor(options) {
4356
- super(Object.assign(Object.assign({}, defaults$g), options));
4494
+ super({ ...defaults$g, ...options });
4357
4495
  this.style = new CaseStyle(this.options.style, "element-case");
4358
4496
  }
4359
4497
  static schema() {
@@ -4423,7 +4561,7 @@ const defaults$f = {
4423
4561
  };
4424
4562
  class ElementName extends Rule {
4425
4563
  constructor(options) {
4426
- super(Object.assign(Object.assign({}, defaults$f), options));
4564
+ super({ ...defaults$f, ...options });
4427
4565
  // eslint-disable-next-line security/detect-non-literal-regexp
4428
4566
  this.pattern = new RegExp(this.options.pattern);
4429
4567
  }
@@ -4695,9 +4833,14 @@ class ElementRequiredAttributes extends Rule {
4695
4833
  this.on("tag:end", (event) => {
4696
4834
  const node = event.previous;
4697
4835
  const meta = node.meta;
4698
- if (!meta || !meta.requiredAttributes)
4836
+ /* handle missing metadata and missing attributes */
4837
+ if (!meta || !meta.attributes) {
4699
4838
  return;
4700
- for (const key of meta.requiredAttributes) {
4839
+ }
4840
+ for (const [key, attr] of Object.entries(meta.attributes)) {
4841
+ if (!attr.required) {
4842
+ continue;
4843
+ }
4701
4844
  if (node.hasAttribute(key))
4702
4845
  continue;
4703
4846
  const context = {
@@ -4880,7 +5023,7 @@ function parseMaxInitial(value) {
4880
5023
  }
4881
5024
  class HeadingLevel extends Rule {
4882
5025
  constructor(options) {
4883
- super(Object.assign(Object.assign({}, defaults$e), options));
5026
+ super({ ...defaults$e, ...options });
4884
5027
  this.stack = [];
4885
5028
  this.minInitialRank = parseMaxInitial(this.options.minInitialRank);
4886
5029
  this.sectionRoots = this.options.sectioningRoots.map((it) => new Pattern(it));
@@ -5043,7 +5186,7 @@ const defaults$d = {
5043
5186
  };
5044
5187
  class IdPattern extends Rule {
5045
5188
  constructor(options) {
5046
- super(Object.assign(Object.assign({}, defaults$d), options));
5189
+ super({ ...defaults$d, ...options });
5047
5190
  this.pattern = parsePattern(this.options.pattern);
5048
5191
  }
5049
5192
  static schema() {
@@ -5395,7 +5538,7 @@ const defaults$c = {
5395
5538
  };
5396
5539
  class LongTitle extends Rule {
5397
5540
  constructor(options) {
5398
- super(Object.assign(Object.assign({}, defaults$c), options));
5541
+ super({ ...defaults$c, ...options });
5399
5542
  this.maxlength = this.options.maxlength;
5400
5543
  }
5401
5544
  static schema() {
@@ -5548,7 +5691,7 @@ const defaults$b = {
5548
5691
  };
5549
5692
  class NoAutoplay extends Rule {
5550
5693
  constructor(options) {
5551
- super(Object.assign(Object.assign({}, defaults$b), options));
5694
+ super({ ...defaults$b, ...options });
5552
5695
  }
5553
5696
  documentation(context) {
5554
5697
  const tagName = context ? ` on <${context.tagName}>` : "";
@@ -5644,8 +5787,12 @@ class NoDeprecatedAttr extends Rule {
5644
5787
  if (meta === null) {
5645
5788
  return;
5646
5789
  }
5647
- const deprecated = meta.deprecatedAttributes || [];
5648
- if (deprecated.includes(attr)) {
5790
+ const metaAttribute = meta.attributes && meta.attributes[attr];
5791
+ if (!metaAttribute) {
5792
+ return;
5793
+ }
5794
+ const deprecated = metaAttribute.deprecated;
5795
+ if (deprecated) {
5649
5796
  this.report(node, `Attribute "${event.key}" is deprecated on <${node.tagName}> element`, event.keyLocation);
5650
5797
  }
5651
5798
  });
@@ -5802,7 +5949,7 @@ function getCSSDeclarations(value) {
5802
5949
  }
5803
5950
  class NoInlineStyle extends Rule {
5804
5951
  constructor(options) {
5805
- super(Object.assign(Object.assign({}, defaults$a), options));
5952
+ super({ ...defaults$a, ...options });
5806
5953
  }
5807
5954
  static schema() {
5808
5955
  return {
@@ -6024,7 +6171,7 @@ const replacementTable = new Map([
6024
6171
  ]);
6025
6172
  class NoRawCharacters extends Rule {
6026
6173
  constructor(options) {
6027
- super(Object.assign(Object.assign({}, defaults$9), options));
6174
+ super({ ...defaults$9, ...options });
6028
6175
  this.relaxed = this.options.relaxed;
6029
6176
  }
6030
6177
  static schema() {
@@ -6209,7 +6356,7 @@ const defaults$8 = {
6209
6356
  };
6210
6357
  class NoSelfClosing extends Rule {
6211
6358
  constructor(options) {
6212
- super(Object.assign(Object.assign({}, defaults$8), options));
6359
+ super({ ...defaults$8, ...options });
6213
6360
  }
6214
6361
  static schema() {
6215
6362
  return {
@@ -6348,7 +6495,7 @@ const defaults$7 = {
6348
6495
  };
6349
6496
  class PreferButton extends Rule {
6350
6497
  constructor(options) {
6351
- super(Object.assign(Object.assign({}, defaults$7), options));
6498
+ super({ ...defaults$7, ...options });
6352
6499
  }
6353
6500
  static schema() {
6354
6501
  return {
@@ -6453,7 +6600,7 @@ const defaults$6 = {
6453
6600
  };
6454
6601
  class PreferNativeElement extends Rule {
6455
6602
  constructor(options) {
6456
- super(Object.assign(Object.assign({}, defaults$6), options));
6603
+ super({ ...defaults$6, ...options });
6457
6604
  }
6458
6605
  static schema() {
6459
6606
  return {
@@ -6583,7 +6730,7 @@ const supportSri = {
6583
6730
  };
6584
6731
  class RequireSri extends Rule {
6585
6732
  constructor(options) {
6586
- super(Object.assign(Object.assign({}, defaults$5), options));
6733
+ super({ ...defaults$5, ...options });
6587
6734
  this.target = this.options.target;
6588
6735
  }
6589
6736
  static schema() {
@@ -8678,7 +8825,7 @@ const defaults$4 = {
8678
8825
  };
8679
8826
  class Void extends Rule {
8680
8827
  constructor(options) {
8681
- super(Object.assign(Object.assign({}, defaults$4), options));
8828
+ super({ ...defaults$4, ...options });
8682
8829
  this.style = parseStyle$1(this.options.style);
8683
8830
  }
8684
8831
  get deprecated() {
@@ -8787,7 +8934,7 @@ const defaults$3 = {
8787
8934
  };
8788
8935
  class VoidStyle extends Rule {
8789
8936
  constructor(options) {
8790
- super(Object.assign(Object.assign({}, defaults$3), options));
8937
+ super({ ...defaults$3, ...options });
8791
8938
  this.style = parseStyle(this.options.style);
8792
8939
  }
8793
8940
  static schema() {
@@ -8990,7 +9137,7 @@ const defaults$2 = {
8990
9137
  };
8991
9138
  class H37 extends Rule {
8992
9139
  constructor(options) {
8993
- super(Object.assign(Object.assign({}, defaults$2), options));
9140
+ super({ ...defaults$2, ...options });
8994
9141
  /* ensure alias is array */
8995
9142
  if (!Array.isArray(this.options.alias)) {
8996
9143
  this.options.alias = [this.options.alias];
@@ -9117,7 +9264,73 @@ const bundledRules$1 = {
9117
9264
  "wcag/h71": H71,
9118
9265
  };
9119
9266
 
9120
- 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);
9267
+ const bundledRules = {
9268
+ "allowed-links": AllowedLinks,
9269
+ "aria-label-misuse": AriaLabelMisuse,
9270
+ "attr-case": AttrCase,
9271
+ "attr-delimiter": AttrDelimiter,
9272
+ "attr-pattern": AttrPattern,
9273
+ "attr-quotes": AttrQuotes,
9274
+ "attr-spacing": AttrSpacing,
9275
+ "attribute-allowed-values": AttributeAllowedValues,
9276
+ "attribute-boolean-style": AttributeBooleanStyle,
9277
+ "attribute-empty-style": AttributeEmptyStyle,
9278
+ "class-pattern": ClassPattern,
9279
+ "close-attr": CloseAttr,
9280
+ "close-order": CloseOrder,
9281
+ deprecated: Deprecated,
9282
+ "deprecated-rule": DeprecatedRule,
9283
+ "doctype-html": NoStyleTag$1,
9284
+ "doctype-style": DoctypeStyle,
9285
+ "element-case": ElementCase,
9286
+ "element-name": ElementName,
9287
+ "element-permitted-content": ElementPermittedContent,
9288
+ "element-permitted-occurrences": ElementPermittedOccurrences,
9289
+ "element-permitted-order": ElementPermittedOrder,
9290
+ "element-required-attributes": ElementRequiredAttributes,
9291
+ "element-required-content": ElementRequiredContent,
9292
+ "empty-heading": EmptyHeading,
9293
+ "empty-title": EmptyTitle,
9294
+ "heading-level": HeadingLevel,
9295
+ "id-pattern": IdPattern,
9296
+ "input-attributes": InputAttributes,
9297
+ "input-missing-label": InputMissingLabel,
9298
+ "long-title": LongTitle,
9299
+ "meta-refresh": MetaRefresh,
9300
+ "missing-doctype": MissingDoctype,
9301
+ "multiple-labeled-controls": MultipleLabeledControls,
9302
+ "no-autoplay": NoAutoplay,
9303
+ "no-conditional-comment": NoConditionalComment,
9304
+ "no-deprecated-attr": NoDeprecatedAttr,
9305
+ "no-dup-attr": NoDupAttr,
9306
+ "no-dup-class": NoDupClass,
9307
+ "no-dup-id": NoDupID,
9308
+ "no-implicit-close": NoImplicitClose,
9309
+ "no-inline-style": NoInlineStyle,
9310
+ "no-missing-references": NoMissingReferences,
9311
+ "no-multiple-main": NoMultipleMain,
9312
+ "no-raw-characters": NoRawCharacters,
9313
+ "no-redundant-for": NoRedundantFor,
9314
+ "no-redundant-role": NoRedundantRole,
9315
+ "no-self-closing": NoSelfClosing,
9316
+ "no-style-tag": NoStyleTag,
9317
+ "no-trailing-whitespace": NoTrailingWhitespace,
9318
+ "no-unknown-elements": NoUnknownElements,
9319
+ "no-utf8-bom": NoUtf8Bom,
9320
+ "prefer-button": PreferButton,
9321
+ "prefer-native-element": PreferNativeElement,
9322
+ "prefer-tbody": PreferTbody,
9323
+ "require-sri": RequireSri,
9324
+ "script-element": ScriptElement,
9325
+ "script-type": ScriptType,
9326
+ "svg-focusable": SvgFocusable,
9327
+ "text-content": TextContent,
9328
+ "unrecognized-char-ref": UnknownCharReference,
9329
+ void: Void,
9330
+ "void-content": VoidContent,
9331
+ "void-style": VoidStyle,
9332
+ ...bundledRules$1,
9333
+ };
9121
9334
 
9122
9335
  var defaultConfig = {};
9123
9336
 
@@ -9323,7 +9536,7 @@ class ResolvedConfig {
9323
9536
  * @returns A list of transformed sources ready for validation.
9324
9537
  */
9325
9538
  transformFilename(filename) {
9326
- const data = fs__default['default'].readFileSync(filename, { encoding: "utf8" });
9539
+ const data = fs__default["default"].readFileSync(filename, { encoding: "utf8" });
9327
9540
  const source = {
9328
9541
  data,
9329
9542
  filename,
@@ -9348,19 +9561,19 @@ class ResolvedConfig {
9348
9561
  }
9349
9562
 
9350
9563
  let rootDirCache = null;
9351
- const ajv = new Ajv__default['default']({ strict: true, strictTuples: true, strictTypes: true });
9564
+ const ajv = new Ajv__default["default"]({ strict: true, strictTuples: true, strictTypes: true });
9352
9565
  ajv.addMetaSchema(ajvSchemaDraft);
9353
9566
  const validator = ajv.compile(configurationSchema);
9354
9567
  function overwriteMerge(a, b) {
9355
9568
  return b;
9356
9569
  }
9357
9570
  function mergeInternal(base, rhs) {
9358
- const dst = deepmerge__default['default'](base, Object.assign(Object.assign({}, rhs), { rules: {} }));
9571
+ const dst = deepmerge__default["default"](base, { ...rhs, rules: {} });
9359
9572
  /* rules need some special care, should overwrite arrays instead of
9360
9573
  * concaternation, i.e. ["error", {...options}] should not be merged by
9361
9574
  * appending to old value */
9362
9575
  if (rhs.rules) {
9363
- dst.rules = deepmerge__default['default'](dst.rules, rhs.rules, { arrayMerge: overwriteMerge });
9576
+ dst.rules = deepmerge__default["default"](dst.rules, rhs.rules, { arrayMerge: overwriteMerge });
9364
9577
  }
9365
9578
  /* root property is merged with boolean "or" since it should always be truthy
9366
9579
  * if any config has it set. */
@@ -9384,7 +9597,7 @@ function loadFromFile(filename) {
9384
9597
  if (!json[key])
9385
9598
  continue;
9386
9599
  json[key] = json[key].map((ref) => {
9387
- return Config.expandRelative(ref, path__default['default'].dirname(filename));
9600
+ return Config.expandRelative(ref, path__default["default"].dirname(filename));
9388
9601
  });
9389
9602
  }
9390
9603
  return json;
@@ -9543,14 +9756,14 @@ class Config {
9543
9756
  }
9544
9757
  let filename;
9545
9758
  /* try searching builtin metadata */
9546
- filename = path__default['default'].join(projectRoot, "elements", `${entry}.json`);
9547
- if (fs__default['default'].existsSync(filename)) {
9759
+ filename = path__default["default"].join(projectRoot, "elements", `${entry}.json`);
9760
+ if (fs__default["default"].existsSync(filename)) {
9548
9761
  metaTable.loadFromFile(filename);
9549
9762
  continue;
9550
9763
  }
9551
9764
  /* try as regular file */
9552
9765
  filename = entry.replace("<rootDir>", this.rootDir);
9553
- if (fs__default['default'].existsSync(filename)) {
9766
+ if (fs__default["default"].existsSync(filename)) {
9554
9767
  metaTable.loadFromFile(filename);
9555
9768
  continue;
9556
9769
  }
@@ -9570,7 +9783,7 @@ class Config {
9570
9783
  */
9571
9784
  static expandRelative(src, currentPath) {
9572
9785
  if (src[0] === ".") {
9573
- return path__default['default'].normalize(`${currentPath}/${src}`);
9786
+ return path__default["default"].normalize(`${currentPath}/${src}`);
9574
9787
  }
9575
9788
  return src;
9576
9789
  }
@@ -9580,7 +9793,7 @@ class Config {
9580
9793
  * @internal primary purpose is unittests
9581
9794
  */
9582
9795
  get() {
9583
- const config = Object.assign({}, this.config);
9796
+ const config = { ...this.config };
9584
9797
  if (config.elements) {
9585
9798
  config.elements = config.elements.map((cur) => {
9586
9799
  if (typeof cur === "string") {
@@ -9666,7 +9879,11 @@ class Config {
9666
9879
  if (!properties) {
9667
9880
  continue;
9668
9881
  }
9669
- for (const [key, schema] of Object.entries(properties)) {
9882
+ for (const [raw, schema] of Object.entries(properties)) {
9883
+ /* at compile time this is a fixed list but the point of this method is
9884
+ * to augment the runtime with additional keys so it is a bit of lying
9885
+ * to typescript */
9886
+ const key = raw;
9670
9887
  if (schema.copyable && !MetaCopyableProperty.includes(key)) {
9671
9888
  MetaCopyableProperty.push(key);
9672
9889
  }
@@ -9820,13 +10037,13 @@ class Config {
9820
10037
  let current = process.cwd();
9821
10038
  // eslint-disable-next-line no-constant-condition
9822
10039
  while (true) {
9823
- const search = path__default['default'].join(current, "package.json");
9824
- if (fs__default['default'].existsSync(search)) {
10040
+ const search = path__default["default"].join(current, "package.json");
10041
+ if (fs__default["default"].existsSync(search)) {
9825
10042
  return (this.rootDirCache = current);
9826
10043
  }
9827
10044
  /* get the parent directory */
9828
10045
  const child = current;
9829
- current = path__default['default'].dirname(current);
10046
+ current = path__default["default"].dirname(current);
9830
10047
  /* stop if this is the root directory */
9831
10048
  if (current === child) {
9832
10049
  break;
@@ -9862,31 +10079,6 @@ class ConfigLoader {
9862
10079
  }
9863
10080
  }
9864
10081
 
9865
- /*! *****************************************************************************
9866
- Copyright (c) Microsoft Corporation. All rights reserved.
9867
- Licensed under the Apache License, Version 2.0 (the "License"); you may not use
9868
- this file except in compliance with the License. You may obtain a copy of the
9869
- License at http://www.apache.org/licenses/LICENSE-2.0
9870
-
9871
- THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
9872
- KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
9873
- WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
9874
- MERCHANTABLITY OR NON-INFRINGEMENT.
9875
-
9876
- See the Apache Version 2.0 License for specific language governing permissions
9877
- and limitations under the License.
9878
- ***************************************************************************** */
9879
-
9880
- function __rest(s, e) {
9881
- var t = {};
9882
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
9883
- t[p] = s[p];
9884
- if (s != null && typeof Object.getOwnPropertySymbols === "function")
9885
- for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) if (e.indexOf(p[i]) < 0)
9886
- t[p[i]] = s[p[i]];
9887
- return t;
9888
- }
9889
-
9890
10082
  class EventHandler {
9891
10083
  constructor() {
9892
10084
  this.listeners = {};
@@ -10460,7 +10652,7 @@ class Reporter {
10460
10652
  merged[key].messages = [...merged[key].messages, ...result.messages];
10461
10653
  }
10462
10654
  else {
10463
- merged[key] = Object.assign({}, result);
10655
+ merged[key] = { ...result };
10464
10656
  }
10465
10657
  });
10466
10658
  });
@@ -10562,14 +10754,16 @@ function messageSort(a, b) {
10562
10754
  }
10563
10755
 
10564
10756
  class Engine {
10565
- constructor(config, configData, ParserClass) {
10757
+ constructor(config, ParserClass) {
10566
10758
  this.report = new Reporter();
10567
- this.configData = configData;
10568
10759
  this.config = config;
10569
10760
  this.ParserClass = ParserClass;
10570
10761
  /* initialize plugins and rules */
10571
10762
  const result = this.initPlugins(this.config);
10572
- this.availableRules = Object.assign(Object.assign({}, bundledRules), result.availableRules);
10763
+ this.availableRules = {
10764
+ ...bundledRules,
10765
+ ...result.availableRules,
10766
+ };
10573
10767
  }
10574
10768
  /**
10575
10769
  * Lint sources and return report
@@ -10594,13 +10788,13 @@ class Engine {
10594
10788
  /* trigger configuration ready event */
10595
10789
  const configEvent = {
10596
10790
  location,
10597
- config: this.configData,
10791
+ config: this.config,
10598
10792
  rules,
10599
10793
  };
10600
10794
  parser.trigger("config:ready", configEvent);
10601
10795
  /* trigger source ready event */
10602
10796
  /* eslint-disable-next-line @typescript-eslint/no-unused-vars -- object destructured on purpose to remove property */
10603
- const sourceData = __rest(source, ["hooks"]);
10797
+ const { hooks: _, ...sourceData } = source;
10604
10798
  const sourceEvent = {
10605
10799
  location,
10606
10800
  source: sourceData,
@@ -10907,133 +11101,31 @@ class Engine {
10907
11101
  }
10908
11102
 
10909
11103
  /**
10910
- * @internal
10911
- */
10912
- function findConfigurationFiles(directory) {
10913
- return ["json", "cjs", "js"]
10914
- .map((extension) => path__default['default'].join(directory, `.htmlvalidate.${extension}`))
10915
- .filter((filePath) => fs__default['default'].existsSync(filePath));
10916
- }
10917
- /**
10918
- * Loads configuration by traversing filesystem.
10919
- *
10920
- * Configuration is read from three sources and in the following order:
10921
- *
10922
- * 1. Global configuration passed to constructor.
10923
- * 2. Configuration files found when traversing the directory structure.
10924
- * 3. Override passed to this function.
10925
- *
10926
- * The following configuration filenames are searched:
10927
- *
10928
- * - `.htmlvalidate.json`
10929
- * - `.htmlvalidate.js`
10930
- * - `.htmlvalidate.cjs`
10931
- *
10932
- * Global configuration is used when no configuration file is found. The
10933
- * result is always merged with override if present.
10934
- *
10935
- * The `root` property set to `true` affects the configuration as following:
11104
+ * The static configuration loader does not do any per-handle lookup. Only the
11105
+ * global or per-call configuration is used.
10936
11106
  *
10937
- * 1. If set in override the override is returned as-is.
10938
- * 2. If set in the global config the override is merged into global and
10939
- * returned. No configuration files are searched.
10940
- * 3. Setting `root` in configuration file only stops directory traversal.
11107
+ * In practice this means no configuration is fetch by traversing the
11108
+ * filesystem.
10941
11109
  */
10942
- class FileSystemConfigLoader extends ConfigLoader {
10943
- /**
10944
- * @param config - Global configuration
10945
- * @param configFactory - Optional configuration factory
10946
- */
10947
- constructor(config, configFactory = Config) {
10948
- super(config, configFactory);
10949
- this.cache = new Map();
10950
- }
10951
- /**
10952
- * Get configuration for given filename.
10953
- *
10954
- * @param filename - Filename to get configuration for.
10955
- * @param configOverride - Configuration to merge final result with.
10956
- */
10957
- getConfigFor(filename, configOverride) {
10958
- /* special case when the overridden configuration is marked as root, should
10959
- * not try to load any more configuration files */
11110
+ class StaticConfigLoader extends ConfigLoader {
11111
+ getConfigFor(handle, configOverride) {
10960
11112
  const override = this.loadFromObject(configOverride || {});
10961
11113
  if (override.isRootFound()) {
10962
11114
  override.init();
10963
11115
  return override;
10964
11116
  }
10965
- /* special case when the global configuration is marked as root, should not
10966
- * try to load and more configuration files */
10967
- if (this.globalConfig.isRootFound()) {
10968
- const merged = this.globalConfig.merge(override);
10969
- merged.init();
10970
- return merged;
10971
- }
10972
- const config = this.fromFilename(filename);
10973
- const merged = config ? config.merge(override) : this.globalConfig.merge(override);
11117
+ const merged = this.globalConfig.merge(override);
10974
11118
  merged.init();
10975
11119
  return merged;
10976
11120
  }
10977
- /**
10978
- * Flush configuration cache.
10979
- *
10980
- * @param filename - If given only the cache for that file is flushed.
10981
- */
10982
- flushCache(filename) {
10983
- if (filename) {
10984
- this.cache.delete(filename);
10985
- }
10986
- else {
10987
- this.cache.clear();
10988
- }
10989
- }
10990
- /**
10991
- * Load raw configuration from directory traversal.
10992
- *
10993
- * This configuration is not merged with global configuration and may return
10994
- * `null` if no configuration files are found.
10995
- */
10996
- fromFilename(filename) {
10997
- var _a;
10998
- if (filename === "inline") {
10999
- return null;
11000
- }
11001
- if (this.cache.has(filename)) {
11002
- return (_a = this.cache.get(filename)) !== null && _a !== void 0 ? _a : null;
11003
- }
11004
- let found = false;
11005
- let current = path__default['default'].resolve(path__default['default'].dirname(filename));
11006
- let config = this.empty();
11007
- // eslint-disable-next-line no-constant-condition
11008
- while (true) {
11009
- /* search configuration files in current directory */
11010
- for (const configFile of findConfigurationFiles(current)) {
11011
- const local = this.loadFromFile(configFile);
11012
- found = true;
11013
- config = local.merge(config);
11014
- }
11015
- /* stop if a configuration with "root" is set to true */
11016
- if (config.isRootFound()) {
11017
- break;
11018
- }
11019
- /* get the parent directory */
11020
- const child = current;
11021
- current = path__default['default'].dirname(current);
11022
- /* stop if this is the root directory */
11023
- if (current === child) {
11024
- break;
11025
- }
11026
- }
11027
- /* no config was found by loader, return null and let caller decide what to do */
11028
- if (!found) {
11029
- this.cache.set(filename, null);
11030
- return null;
11031
- }
11032
- this.cache.set(filename, config);
11033
- return config;
11121
+ flushCache() {
11122
+ /* do nothing */
11034
11123
  }
11035
11124
  defaultConfig() {
11036
- return this.configFactory.defaultConfig();
11125
+ return this.loadFromObject({
11126
+ extends: ["html-validate:recommended"],
11127
+ elements: ["html5"],
11128
+ });
11037
11129
  }
11038
11130
  }
11039
11131
 
@@ -11057,7 +11149,7 @@ function isConfigData(value) {
11057
11149
  class HtmlValidate {
11058
11150
  constructor(arg) {
11059
11151
  const [loader, config] = arg instanceof ConfigLoader ? [arg, undefined] : [undefined, arg];
11060
- this.configLoader = loader !== null && loader !== void 0 ? loader : new FileSystemConfigLoader(config);
11152
+ this.configLoader = loader !== null && loader !== void 0 ? loader : new StaticConfigLoader(config);
11061
11153
  }
11062
11154
  validateString(str, arg1, arg2, arg3) {
11063
11155
  const filename = typeof arg1 === "string" ? arg1 : "inline";
@@ -11084,7 +11176,7 @@ class HtmlValidate {
11084
11176
  const config = this.getConfigFor(input.filename, configOverride);
11085
11177
  const resolved = config.resolve();
11086
11178
  const source = resolved.transformSource(input);
11087
- const engine = new Engine(resolved, config.get(), Parser);
11179
+ const engine = new Engine(resolved, Parser);
11088
11180
  return engine.lint(source);
11089
11181
  }
11090
11182
  /**
@@ -11098,7 +11190,7 @@ class HtmlValidate {
11098
11190
  const config = this.getConfigFor(filename);
11099
11191
  const resolved = config.resolve();
11100
11192
  const source = resolved.transformFilename(filename);
11101
- const engine = new Engine(resolved, config.get(), Parser);
11193
+ const engine = new Engine(resolved, Parser);
11102
11194
  return engine.lint(source);
11103
11195
  }
11104
11196
  /**
@@ -11122,7 +11214,7 @@ class HtmlValidate {
11122
11214
  */
11123
11215
  canValidate(filename) {
11124
11216
  /* .html is always supported */
11125
- const extension = path__default['default'].extname(filename).toLowerCase();
11217
+ const extension = path__default["default"].extname(filename).toLowerCase();
11126
11218
  if (extension === ".html") {
11127
11219
  return true;
11128
11220
  }
@@ -11143,7 +11235,7 @@ class HtmlValidate {
11143
11235
  const config = this.getConfigFor(filename);
11144
11236
  const resolved = config.resolve();
11145
11237
  const source = resolved.transformFilename(filename);
11146
- const engine = new Engine(resolved, config.get(), Parser);
11238
+ const engine = new Engine(resolved, Parser);
11147
11239
  return engine.dumpTokens(source);
11148
11240
  }
11149
11241
  /**
@@ -11158,7 +11250,7 @@ class HtmlValidate {
11158
11250
  const config = this.getConfigFor(filename);
11159
11251
  const resolved = config.resolve();
11160
11252
  const source = resolved.transformFilename(filename);
11161
- const engine = new Engine(resolved, config.get(), Parser);
11253
+ const engine = new Engine(resolved, Parser);
11162
11254
  return engine.dumpEvents(source);
11163
11255
  }
11164
11256
  /**
@@ -11173,7 +11265,7 @@ class HtmlValidate {
11173
11265
  const config = this.getConfigFor(filename);
11174
11266
  const resolved = config.resolve();
11175
11267
  const source = resolved.transformFilename(filename);
11176
- const engine = new Engine(resolved, config.get(), Parser);
11268
+ const engine = new Engine(resolved, Parser);
11177
11269
  return engine.dumpTree(source);
11178
11270
  }
11179
11271
  /**
@@ -11251,7 +11343,7 @@ class HtmlValidate {
11251
11343
  */
11252
11344
  getRuleDocumentation(ruleId, config = null, context = null) {
11253
11345
  const c = config || this.getConfigFor("inline");
11254
- const engine = new Engine(c.resolve(), c.get(), Parser);
11346
+ const engine = new Engine(c.resolve(), Parser);
11255
11347
  return engine.getRuleDocumentation(ruleId, context);
11256
11348
  }
11257
11349
  /**
@@ -11288,41 +11380,12 @@ class HtmlValidate {
11288
11380
  }
11289
11381
  }
11290
11382
 
11291
- /**
11292
- * The static configuration loader does not do any per-handle lookup. Only the
11293
- * global or per-call configuration is used.
11294
- *
11295
- * In practice this means no configuration is fetch by traversing the
11296
- * filesystem.
11297
- */
11298
- class StaticConfigLoader extends ConfigLoader {
11299
- getConfigFor(handle, configOverride) {
11300
- const override = this.loadFromObject(configOverride || {});
11301
- if (override.isRootFound()) {
11302
- override.init();
11303
- return override;
11304
- }
11305
- const merged = this.globalConfig.merge(override);
11306
- merged.init();
11307
- return merged;
11308
- }
11309
- flushCache() {
11310
- /* do nothing */
11311
- }
11312
- defaultConfig() {
11313
- return this.loadFromObject({
11314
- extends: ["html-validate:recommended"],
11315
- elements: ["html5"],
11316
- });
11317
- }
11318
- }
11319
-
11320
11383
  const defaults$1 = {
11321
11384
  silent: false,
11322
11385
  version,
11323
11386
  logger(text) {
11324
11387
  /* eslint-disable-next-line no-console */
11325
- console.error(kleur__default['default'].red(text));
11388
+ console.error(kleur__default["default"].red(text));
11326
11389
  },
11327
11390
  };
11328
11391
  /**
@@ -11334,8 +11397,8 @@ const defaults$1 = {
11334
11397
  * @returns - `true` if version is compatible
11335
11398
  */
11336
11399
  function compatibilityCheck(name, declared, options) {
11337
- const { silent, version: current, logger } = Object.assign(Object.assign({}, defaults$1), options);
11338
- const valid = semver__default['default'].satisfies(current, declared);
11400
+ const { silent, version: current, logger } = { ...defaults$1, ...options };
11401
+ const valid = semver__default["default"].satisfies(current, declared);
11339
11402
  if (valid || silent) {
11340
11403
  return valid;
11341
11404
  }
@@ -11364,6 +11427,137 @@ function ruleExists(ruleId) {
11364
11427
  return ruleIds.has(ruleId);
11365
11428
  }
11366
11429
 
11430
+ /**
11431
+ * @internal
11432
+ */
11433
+ function findConfigurationFiles(directory) {
11434
+ return ["json", "cjs", "js"]
11435
+ .map((extension) => path__default["default"].join(directory, `.htmlvalidate.${extension}`))
11436
+ .filter((filePath) => fs__default["default"].existsSync(filePath));
11437
+ }
11438
+ /**
11439
+ * Loads configuration by traversing filesystem.
11440
+ *
11441
+ * Configuration is read from three sources and in the following order:
11442
+ *
11443
+ * 1. Global configuration passed to constructor.
11444
+ * 2. Configuration files found when traversing the directory structure.
11445
+ * 3. Override passed to this function.
11446
+ *
11447
+ * The following configuration filenames are searched:
11448
+ *
11449
+ * - `.htmlvalidate.json`
11450
+ * - `.htmlvalidate.js`
11451
+ * - `.htmlvalidate.cjs`
11452
+ *
11453
+ * Global configuration is used when no configuration file is found. The
11454
+ * result is always merged with override if present.
11455
+ *
11456
+ * The `root` property set to `true` affects the configuration as following:
11457
+ *
11458
+ * 1. If set in override the override is returned as-is.
11459
+ * 2. If set in the global config the override is merged into global and
11460
+ * returned. No configuration files are searched.
11461
+ * 3. Setting `root` in configuration file only stops directory traversal.
11462
+ */
11463
+ class FileSystemConfigLoader extends ConfigLoader {
11464
+ /**
11465
+ * @param config - Global configuration
11466
+ * @param configFactory - Optional configuration factory
11467
+ */
11468
+ constructor(config, configFactory = Config) {
11469
+ super(config, configFactory);
11470
+ this.cache = new Map();
11471
+ }
11472
+ /**
11473
+ * Get configuration for given filename.
11474
+ *
11475
+ * @param filename - Filename to get configuration for.
11476
+ * @param configOverride - Configuration to merge final result with.
11477
+ */
11478
+ getConfigFor(filename, configOverride) {
11479
+ /* special case when the overridden configuration is marked as root, should
11480
+ * not try to load any more configuration files */
11481
+ const override = this.loadFromObject(configOverride || {});
11482
+ if (override.isRootFound()) {
11483
+ override.init();
11484
+ return override;
11485
+ }
11486
+ /* special case when the global configuration is marked as root, should not
11487
+ * try to load and more configuration files */
11488
+ if (this.globalConfig.isRootFound()) {
11489
+ const merged = this.globalConfig.merge(override);
11490
+ merged.init();
11491
+ return merged;
11492
+ }
11493
+ const config = this.fromFilename(filename);
11494
+ const merged = config ? config.merge(override) : this.globalConfig.merge(override);
11495
+ merged.init();
11496
+ return merged;
11497
+ }
11498
+ /**
11499
+ * Flush configuration cache.
11500
+ *
11501
+ * @param filename - If given only the cache for that file is flushed.
11502
+ */
11503
+ flushCache(filename) {
11504
+ if (filename) {
11505
+ this.cache.delete(filename);
11506
+ }
11507
+ else {
11508
+ this.cache.clear();
11509
+ }
11510
+ }
11511
+ /**
11512
+ * Load raw configuration from directory traversal.
11513
+ *
11514
+ * This configuration is not merged with global configuration and may return
11515
+ * `null` if no configuration files are found.
11516
+ */
11517
+ fromFilename(filename) {
11518
+ var _a;
11519
+ if (filename === "inline") {
11520
+ return null;
11521
+ }
11522
+ if (this.cache.has(filename)) {
11523
+ return (_a = this.cache.get(filename)) !== null && _a !== void 0 ? _a : null;
11524
+ }
11525
+ let found = false;
11526
+ let current = path__default["default"].resolve(path__default["default"].dirname(filename));
11527
+ let config = this.empty();
11528
+ // eslint-disable-next-line no-constant-condition
11529
+ while (true) {
11530
+ /* search configuration files in current directory */
11531
+ for (const configFile of findConfigurationFiles(current)) {
11532
+ const local = this.loadFromFile(configFile);
11533
+ found = true;
11534
+ config = local.merge(config);
11535
+ }
11536
+ /* stop if a configuration with "root" is set to true */
11537
+ if (config.isRootFound()) {
11538
+ break;
11539
+ }
11540
+ /* get the parent directory */
11541
+ const child = current;
11542
+ current = path__default["default"].dirname(current);
11543
+ /* stop if this is the root directory */
11544
+ if (current === child) {
11545
+ break;
11546
+ }
11547
+ }
11548
+ /* no config was found by loader, return null and let caller decide what to do */
11549
+ if (!found) {
11550
+ this.cache.set(filename, null);
11551
+ return null;
11552
+ }
11553
+ this.cache.set(filename, config);
11554
+ return config;
11555
+ }
11556
+ defaultConfig() {
11557
+ return this.configFactory.defaultConfig();
11558
+ }
11559
+ }
11560
+
11367
11561
  const entities = {
11368
11562
  ">": "&gt;",
11369
11563
  "<": "&lt;",
@@ -11435,12 +11629,12 @@ function pluralize(word, count) {
11435
11629
  * @returns The formatted file path.
11436
11630
  */
11437
11631
  function formatFilePath(filePath, line, column) {
11438
- let relPath = path__default['default'].relative(process.cwd(), filePath);
11632
+ let relPath = path__default["default"].relative(process.cwd(), filePath);
11439
11633
  /* istanbul ignore next: safety check from original implementation */
11440
11634
  if (line && column) {
11441
11635
  relPath += `:${line}:${column}`;
11442
11636
  }
11443
- return kleur__default['default'].green(relPath);
11637
+ return kleur__default["default"].green(relPath);
11444
11638
  }
11445
11639
  function getStartLocation(message) {
11446
11640
  return {
@@ -11469,9 +11663,9 @@ function getEndLocation(message, source) {
11469
11663
  * @returns The formatted output.
11470
11664
  */
11471
11665
  function formatMessage(message, parentResult, options) {
11472
- const type = message.severity === 2 ? kleur__default['default'].red("error") : kleur__default['default'].yellow("warning");
11473
- const msg = `${kleur__default['default'].bold(message.message.replace(/([^ ])\.$/, "$1"))}`;
11474
- const ruleId = kleur__default['default'].dim(`(${message.ruleId})`);
11666
+ const type = message.severity === 2 ? kleur__default["default"].red("error") : kleur__default["default"].yellow("warning");
11667
+ const msg = `${kleur__default["default"].bold(message.message.replace(/([^ ])\.$/, "$1"))}`;
11668
+ const ruleId = kleur__default["default"].dim(`(${message.ruleId})`);
11475
11669
  const filePath = formatFilePath(parentResult.filePath, message.line, message.column);
11476
11670
  const sourceCode = parentResult.source;
11477
11671
  /* istanbul ignore next: safety check from original implementation */
@@ -11492,7 +11686,7 @@ function formatMessage(message, parentResult, options) {
11492
11686
  }, { highlightCode: false }));
11493
11687
  }
11494
11688
  if (options.showLink && message.ruleUrl) {
11495
- result.push(`${kleur__default['default'].bold("Details:")} ${message.ruleUrl}`);
11689
+ result.push(`${kleur__default["default"].bold("Details:")} ${message.ruleUrl}`);
11496
11690
  }
11497
11691
  return result.join("\n");
11498
11692
  }
@@ -11511,10 +11705,10 @@ function formatSummary(errors, warnings) {
11511
11705
  if (warnings > 0) {
11512
11706
  summary.push(`${warnings} ${pluralize("warning", warnings)}`);
11513
11707
  }
11514
- return kleur__default['default'][summaryColor]().bold(`${summary.join(" and ")} found.`);
11708
+ return kleur__default["default"][summaryColor]().bold(`${summary.join(" and ")} found.`);
11515
11709
  }
11516
11710
  function codeframe(results, options) {
11517
- const merged = Object.assign(Object.assign({}, defaults), options);
11711
+ const merged = { ...defaults, ...options };
11518
11712
  let errors = 0;
11519
11713
  let warnings = 0;
11520
11714
  const resultsWithMessages = results.filter((result) => result.messages.length > 0);
@@ -11552,10 +11746,14 @@ function linkSummary(results) {
11552
11746
  return "";
11553
11747
  }
11554
11748
  const lines = unique.map((url) => ` ${url}\n`);
11555
- return `\n${kleur__default['default'].bold("More information")}:\n${lines.join("")}\n`;
11749
+ return `\n${kleur__default["default"].bold("More information")}:\n${lines.join("")}\n`;
11556
11750
  }
11557
11751
  function stylish(results) {
11558
- const errors = stylishImpl__default['default'](results.map((it) => (Object.assign(Object.assign({}, it), { fixableErrorCount: 0, fixableWarningCount: 0 }))));
11752
+ const errors = stylishImpl__default["default"](results.map((it) => ({
11753
+ ...it,
11754
+ fixableErrorCount: 0,
11755
+ fixableWarningCount: 0,
11756
+ })));
11559
11757
  const links = linkSummary(results);
11560
11758
  return `${errors}${links}`;
11561
11759
  }