html-validate 6.8.0 → 6.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/es/core.js CHANGED
@@ -243,6 +243,42 @@ var ajvSchemaDraft = {
243
243
  }
244
244
  };
245
245
 
246
+ function stringify(value) {
247
+ if (typeof value === "string") {
248
+ return String(value);
249
+ }
250
+ else {
251
+ return JSON.stringify(value);
252
+ }
253
+ }
254
+ /**
255
+ * Represents an `Error` created from arbitrary values.
256
+ *
257
+ * @public
258
+ */
259
+ class WrappedError extends Error {
260
+ constructor(message) {
261
+ super(stringify(message));
262
+ }
263
+ }
264
+
265
+ /**
266
+ * Ensures the value is an Error.
267
+ *
268
+ * If the passed value is not an `Error` instance a [[WrappedError]] is
269
+ * constructed with the stringified value.
270
+ *
271
+ * @internal
272
+ */
273
+ function ensureError(value) {
274
+ if (value instanceof Error) {
275
+ return value;
276
+ }
277
+ else {
278
+ return new WrappedError(value);
279
+ }
280
+ }
281
+
246
282
  class NestedError extends Error {
247
283
  constructor(message, nested) {
248
284
  super(message);
@@ -296,6 +332,32 @@ class SchemaValidationError extends UserError {
296
332
  }
297
333
  }
298
334
 
335
+ /**
336
+ * Computes hash for given string.
337
+ *
338
+ * @internal
339
+ */
340
+ function cyrb53(str) {
341
+ const a = 2654435761;
342
+ const b = 1597334677;
343
+ const c = 2246822507;
344
+ const d = 3266489909;
345
+ const e = 4294967296;
346
+ const f = 2097151;
347
+ const seed = 0;
348
+ let h1 = 0xdeadbeef ^ seed;
349
+ let h2 = 0x41c6ce57 ^ seed;
350
+ for (let i = 0, ch; i < str.length; i++) {
351
+ ch = str.charCodeAt(i);
352
+ h1 = Math.imul(h1 ^ ch, a);
353
+ h2 = Math.imul(h2 ^ ch, b);
354
+ }
355
+ h1 = Math.imul(h1 ^ (h1 >>> 16), c) ^ Math.imul(h2 ^ (h2 >>> 13), d);
356
+ h2 = Math.imul(h2 ^ (h2 >>> 16), c) ^ Math.imul(h1 ^ (h1 >>> 13), d);
357
+ return e * (f & h2) + (h1 >>> 0);
358
+ }
359
+ const computeHash = cyrb53;
360
+
299
361
  const projectRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../../");
300
362
  const legacyRequire = createRequire(import.meta.url);
301
363
  const distFolder = path.resolve(projectRoot, "dist/es");
@@ -700,6 +762,35 @@ var schema = {
700
762
  definitions: definitions
701
763
  };
702
764
 
765
+ /**
766
+ * AJV keyword "regexp" to validate the type to be a regular expression.
767
+ * Injects errors with the "type" keyword to give the same output.
768
+ */
769
+ /* istanbul ignore next: manual testing */
770
+ const ajvRegexpValidate = function (data, dataCxt) {
771
+ const valid = data instanceof RegExp;
772
+ if (!valid) {
773
+ ajvRegexpValidate.errors = [
774
+ {
775
+ instancePath: dataCxt === null || dataCxt === void 0 ? void 0 : dataCxt.instancePath,
776
+ schemaPath: undefined,
777
+ keyword: "type",
778
+ message: "should be a regular expression",
779
+ params: {
780
+ keyword: "type",
781
+ },
782
+ },
783
+ ];
784
+ }
785
+ return valid;
786
+ };
787
+ const ajvRegexpKeyword = {
788
+ keyword: "regexp",
789
+ schema: false,
790
+ errors: true,
791
+ validate: ajvRegexpValidate,
792
+ };
793
+
703
794
  var TextContent$1;
704
795
  (function (TextContent) {
705
796
  /* forbid node to have text content, inter-element whitespace is ignored */
@@ -807,6 +898,43 @@ function migrateElement(src) {
807
898
  return result;
808
899
  }
809
900
 
901
+ /**
902
+ * Returns true if given element is a descendant of given tagname.
903
+ *
904
+ * @internal
905
+ */
906
+ function isDescendant(node, tagName) {
907
+ let cur = node.parent;
908
+ while (cur && !cur.isRootElement()) {
909
+ if (cur.is(tagName)) {
910
+ return true;
911
+ }
912
+ cur = cur.parent;
913
+ }
914
+ return false;
915
+ }
916
+
917
+ /**
918
+ * Returns true if given element has given attribute (no matter the value, null,
919
+ * dynamic, etc).
920
+ */
921
+ function hasAttribute(node, attr) {
922
+ return node.hasAttribute(attr);
923
+ }
924
+
925
+ /**
926
+ * Matches attribute against value.
927
+ */
928
+ function matchAttribute(node, key, op, value) {
929
+ const nodeValue = (node.getAttributeValue(key) || "").toLowerCase();
930
+ switch (op) {
931
+ case "!=":
932
+ return nodeValue !== value;
933
+ case "=":
934
+ return nodeValue === value;
935
+ }
936
+ }
937
+
810
938
  const dynamicKeys = [
811
939
  "metadata",
812
940
  "flow",
@@ -818,44 +946,17 @@ const dynamicKeys = [
818
946
  "labelable",
819
947
  ];
820
948
  const functionTable = {
821
- isDescendant,
822
- hasAttribute,
823
- matchAttribute,
949
+ isDescendant: isDescendantFacade,
950
+ hasAttribute: hasAttributeFacade,
951
+ matchAttribute: matchAttributeFacade,
824
952
  };
953
+ const schemaCache = new Map();
825
954
  function clone(src) {
826
955
  return JSON.parse(JSON.stringify(src));
827
956
  }
828
957
  function overwriteMerge$1(a, b) {
829
958
  return b;
830
959
  }
831
- /**
832
- * AJV keyword "regexp" to validate the type to be a regular expression.
833
- * Injects errors with the "type" keyword to give the same output.
834
- */
835
- /* istanbul ignore next: manual testing */
836
- const ajvRegexpValidate = function (data, dataCxt) {
837
- const valid = data instanceof RegExp;
838
- if (!valid) {
839
- ajvRegexpValidate.errors = [
840
- {
841
- instancePath: dataCxt === null || dataCxt === void 0 ? void 0 : dataCxt.instancePath,
842
- schemaPath: undefined,
843
- keyword: "type",
844
- message: "should be regexp",
845
- params: {
846
- keyword: "type",
847
- },
848
- },
849
- ];
850
- }
851
- return valid;
852
- };
853
- const ajvRegexpKeyword = {
854
- keyword: "regexp",
855
- schema: false,
856
- errors: true,
857
- validate: ajvRegexpValidate,
858
- };
859
960
  /**
860
961
  * @public
861
962
  */
@@ -931,7 +1032,7 @@ class MetaTable {
931
1032
  if (err instanceof SchemaValidationError) {
932
1033
  throw err;
933
1034
  }
934
- throw new UserError(`Failed to load element metadata from "${filename}"`, err);
1035
+ throw new UserError(`Failed to load element metadata from "${filename}"`, ensureError(err));
935
1036
  }
936
1037
  }
937
1038
  /**
@@ -992,11 +1093,20 @@ class MetaTable {
992
1093
  * Construct a new AJV schema validator.
993
1094
  */
994
1095
  getSchemaValidator() {
995
- const ajv = new Ajv({ strict: true, strictTuples: true, strictTypes: true });
996
- ajv.addMetaSchema(ajvSchemaDraft);
997
- ajv.addKeyword(ajvRegexpKeyword);
998
- ajv.addKeyword({ keyword: "copyable" });
999
- return ajv.compile(this.schema);
1096
+ const hash = computeHash(JSON.stringify(this.schema));
1097
+ const cached = schemaCache.get(hash);
1098
+ if (cached) {
1099
+ return cached;
1100
+ }
1101
+ else {
1102
+ const ajv = new Ajv({ strict: true, strictTuples: true, strictTypes: true });
1103
+ ajv.addMetaSchema(ajvSchemaDraft);
1104
+ ajv.addKeyword(ajvRegexpKeyword);
1105
+ ajv.addKeyword({ keyword: "copyable" });
1106
+ const validate = ajv.compile(this.schema);
1107
+ schemaCache.set(hash, validate);
1108
+ return validate;
1109
+ }
1000
1110
  }
1001
1111
  /**
1002
1112
  * @public
@@ -1098,36 +1208,27 @@ function parseExpression(expr) {
1098
1208
  return [func, options];
1099
1209
  }
1100
1210
  }
1101
- function isDescendant(node, tagName) {
1211
+ function isDescendantFacade(node, tagName) {
1102
1212
  if (typeof tagName !== "string") {
1103
1213
  throw new Error(`Property expression "isDescendant" must take string argument when evaluating metadata for <${node.tagName}>`);
1104
1214
  }
1105
- let cur = node.parent;
1106
- while (cur && !cur.isRootElement()) {
1107
- if (cur.is(tagName)) {
1108
- return true;
1109
- }
1110
- cur = cur.parent;
1111
- }
1112
- return false;
1215
+ return isDescendant(node, tagName);
1113
1216
  }
1114
- function hasAttribute(node, attr) {
1217
+ function hasAttributeFacade(node, attr) {
1115
1218
  if (typeof attr !== "string") {
1116
1219
  throw new Error(`Property expression "hasAttribute" must take string argument when evaluating metadata for <${node.tagName}>`);
1117
1220
  }
1118
- return node.hasAttribute(attr);
1221
+ return hasAttribute(node, attr);
1119
1222
  }
1120
- function matchAttribute(node, match) {
1223
+ function matchAttributeFacade(node, match) {
1121
1224
  if (!Array.isArray(match) || match.length !== 3) {
1122
1225
  throw new Error(`Property expression "matchAttribute" must take [key, op, value] array as argument when evaluating metadata for <${node.tagName}>`);
1123
1226
  }
1124
1227
  const [key, op, value] = match.map((x) => x.toLowerCase());
1125
- const nodeValue = (node.getAttributeValue(key) || "").toLowerCase();
1126
1228
  switch (op) {
1127
1229
  case "!=":
1128
- return nodeValue !== value;
1129
1230
  case "=":
1130
- return nodeValue === value;
1231
+ return matchAttribute(node, key, op, value);
1131
1232
  default:
1132
1233
  throw new Error(`Property expression "matchAttribute" has invalid operator "${op}" when evaluating metadata for <${node.tagName}>`);
1133
1234
  }
@@ -1978,9 +2079,10 @@ class HtmlElement extends DOMNode {
1978
2079
  /* if a unique id is present, use it and short-circuit */
1979
2080
  if (cur.id) {
1980
2081
  const escaped = escapeSelectorComponent(cur.id);
1981
- const matches = root.querySelectorAll(`#${escaped}`);
2082
+ const selector = escaped.match(/^\d/) ? `[id="${escaped}"]` : `#${escaped}`;
2083
+ const matches = root.querySelectorAll(selector);
1982
2084
  if (matches.length === 1) {
1983
- parts.push(`#${escaped}`);
2085
+ parts.push(selector);
1984
2086
  break;
1985
2087
  }
1986
2088
  }
@@ -2980,7 +3082,7 @@ var TRANSFORMER_API;
2980
3082
  /** @public */
2981
3083
  const name = "html-validate";
2982
3084
  /** @public */
2983
- const version = "6.8.0";
3085
+ const version = "6.10.0";
2984
3086
  /** @public */
2985
3087
  const homepage = "https://html-validate.org";
2986
3088
  /** @public */
@@ -3301,7 +3403,7 @@ function ruleDocumentationUrl(filename) {
3301
3403
  return `${homepage}/rules/${normalized}.html`;
3302
3404
  }
3303
3405
 
3304
- const defaults$q = {
3406
+ const defaults$r = {
3305
3407
  allowExternal: true,
3306
3408
  allowRelative: true,
3307
3409
  allowAbsolute: true,
@@ -3345,7 +3447,7 @@ function matchList(value, list) {
3345
3447
  }
3346
3448
  class AllowedLinks extends Rule {
3347
3449
  constructor(options) {
3348
- super({ ...defaults$q, ...options });
3450
+ super({ ...defaults$r, ...options });
3349
3451
  this.allowExternal = parseAllow(this.options.allowExternal);
3350
3452
  this.allowRelative = parseAllow(this.options.allowRelative);
3351
3453
  this.allowAbsolute = parseAllow(this.options.allowAbsolute);
@@ -3646,13 +3748,13 @@ class CaseStyle {
3646
3748
  }
3647
3749
  }
3648
3750
 
3649
- const defaults$p = {
3751
+ const defaults$q = {
3650
3752
  style: "lowercase",
3651
3753
  ignoreForeign: true,
3652
3754
  };
3653
3755
  class AttrCase extends Rule {
3654
3756
  constructor(options) {
3655
- super({ ...defaults$p, ...options });
3757
+ super({ ...defaults$q, ...options });
3656
3758
  this.style = new CaseStyle(this.options.style, "attr-case");
3657
3759
  }
3658
3760
  static schema() {
@@ -3968,9 +4070,6 @@ class Lexer {
3968
4070
  }
3969
4071
 
3970
4072
  const whitespace = /(\s+)/;
3971
- function isRelevant$3(event) {
3972
- return event.type === TokenType.ATTR_VALUE;
3973
- }
3974
4073
  class AttrDelimiter extends Rule {
3975
4074
  documentation() {
3976
4075
  return {
@@ -3979,8 +4078,12 @@ class AttrDelimiter extends Rule {
3979
4078
  };
3980
4079
  }
3981
4080
  setup() {
3982
- this.on("token", isRelevant$3, (event) => {
3983
- const delimiter = event.data[1];
4081
+ this.on("token", (event) => {
4082
+ const { token } = event;
4083
+ if (token.type !== TokenType.ATTR_VALUE) {
4084
+ return;
4085
+ }
4086
+ const delimiter = token.data[1];
3984
4087
  const match = whitespace.exec(delimiter);
3985
4088
  if (match) {
3986
4089
  const location = sliceLocation(event.location, 0, delimiter.length);
@@ -3991,7 +4094,7 @@ class AttrDelimiter extends Rule {
3991
4094
  }
3992
4095
 
3993
4096
  const DEFAULT_PATTERN = "[a-z0-9-:]+";
3994
- const defaults$o = {
4097
+ const defaults$p = {
3995
4098
  pattern: DEFAULT_PATTERN,
3996
4099
  ignoreForeign: true,
3997
4100
  };
@@ -4028,7 +4131,7 @@ function generateDescription(name, pattern) {
4028
4131
  }
4029
4132
  class AttrPattern extends Rule {
4030
4133
  constructor(options) {
4031
- super({ ...defaults$o, ...options });
4134
+ super({ ...defaults$p, ...options });
4032
4135
  this.pattern = generateRegexp(this.options.pattern);
4033
4136
  }
4034
4137
  static schema() {
@@ -4088,13 +4191,13 @@ var QuoteStyle;
4088
4191
  QuoteStyle["DOUBLE_QUOTE"] = "\"";
4089
4192
  QuoteStyle["AUTO_QUOTE"] = "auto";
4090
4193
  })(QuoteStyle || (QuoteStyle = {}));
4091
- const defaults$n = {
4194
+ const defaults$o = {
4092
4195
  style: "auto",
4093
4196
  unquoted: false,
4094
4197
  };
4095
4198
  class AttrQuotes extends Rule {
4096
4199
  constructor(options) {
4097
- super({ ...defaults$n, ...options });
4200
+ super({ ...defaults$o, ...options });
4098
4201
  this.style = parseStyle$4(this.options.style);
4099
4202
  }
4100
4203
  static schema() {
@@ -4259,12 +4362,12 @@ class AttributeAllowedValues extends Rule {
4259
4362
  }
4260
4363
  }
4261
4364
 
4262
- const defaults$m = {
4365
+ const defaults$n = {
4263
4366
  style: "omit",
4264
4367
  };
4265
4368
  class AttributeBooleanStyle extends Rule {
4266
4369
  constructor(options) {
4267
- super({ ...defaults$m, ...options });
4370
+ super({ ...defaults$n, ...options });
4268
4371
  this.hasInvalidStyle = parseStyle$3(this.options.style);
4269
4372
  }
4270
4373
  static schema() {
@@ -4340,12 +4443,12 @@ function reportMessage$1(attr, style) {
4340
4443
  return "";
4341
4444
  }
4342
4445
 
4343
- const defaults$l = {
4446
+ const defaults$m = {
4344
4447
  style: "omit",
4345
4448
  };
4346
4449
  class AttributeEmptyStyle extends Rule {
4347
4450
  constructor(options) {
4348
- super({ ...defaults$l, ...options });
4451
+ super({ ...defaults$m, ...options });
4349
4452
  this.hasInvalidStyle = parseStyle$2(this.options.style);
4350
4453
  }
4351
4454
  static schema() {
@@ -4453,12 +4556,12 @@ function describePattern(pattern) {
4453
4556
  }
4454
4557
  }
4455
4558
 
4456
- const defaults$k = {
4559
+ const defaults$l = {
4457
4560
  pattern: "kebabcase",
4458
4561
  };
4459
4562
  class ClassPattern extends Rule {
4460
4563
  constructor(options) {
4461
- super({ ...defaults$k, ...options });
4564
+ super({ ...defaults$l, ...options });
4462
4565
  this.pattern = parsePattern(this.options.pattern);
4463
4566
  }
4464
4567
  static schema() {
@@ -4567,13 +4670,13 @@ class CloseOrder extends Rule {
4567
4670
  }
4568
4671
  }
4569
4672
 
4570
- const defaults$j = {
4673
+ const defaults$k = {
4571
4674
  include: null,
4572
4675
  exclude: null,
4573
4676
  };
4574
4677
  class Deprecated extends Rule {
4575
4678
  constructor(options) {
4576
- super({ ...defaults$j, ...options });
4679
+ super({ ...defaults$k, ...options });
4577
4680
  }
4578
4681
  static schema() {
4579
4682
  return {
@@ -4736,12 +4839,12 @@ class NoStyleTag$1 extends Rule {
4736
4839
  }
4737
4840
  }
4738
4841
 
4739
- const defaults$i = {
4842
+ const defaults$j = {
4740
4843
  style: "uppercase",
4741
4844
  };
4742
4845
  class DoctypeStyle extends Rule {
4743
4846
  constructor(options) {
4744
- super({ ...defaults$i, ...options });
4847
+ super({ ...defaults$j, ...options });
4745
4848
  }
4746
4849
  static schema() {
4747
4850
  return {
@@ -4773,12 +4876,12 @@ class DoctypeStyle extends Rule {
4773
4876
  }
4774
4877
  }
4775
4878
 
4776
- const defaults$h = {
4879
+ const defaults$i = {
4777
4880
  style: "lowercase",
4778
4881
  };
4779
4882
  class ElementCase extends Rule {
4780
4883
  constructor(options) {
4781
- super({ ...defaults$h, ...options });
4884
+ super({ ...defaults$i, ...options });
4782
4885
  this.style = new CaseStyle(this.options.style, "element-case");
4783
4886
  }
4784
4887
  static schema() {
@@ -4844,14 +4947,14 @@ class ElementCase extends Rule {
4844
4947
  }
4845
4948
  }
4846
4949
 
4847
- const defaults$g = {
4950
+ const defaults$h = {
4848
4951
  pattern: "^[a-z][a-z0-9\\-._]*-[a-z0-9\\-._]*$",
4849
4952
  whitelist: [],
4850
4953
  blacklist: [],
4851
4954
  };
4852
4955
  class ElementName extends Rule {
4853
4956
  constructor(options) {
4854
- super({ ...defaults$g, ...options });
4957
+ super({ ...defaults$h, ...options });
4855
4958
  // eslint-disable-next-line security/detect-non-literal-regexp
4856
4959
  this.pattern = new RegExp(this.options.pattern);
4857
4960
  }
@@ -4892,7 +4995,7 @@ class ElementName extends Rule {
4892
4995
  ...context.blacklist.map((cur) => `- ${cur}`),
4893
4996
  ];
4894
4997
  }
4895
- if (context.pattern !== defaults$g.pattern) {
4998
+ if (context.pattern !== defaults$h.pattern) {
4896
4999
  return [
4897
5000
  `<${context.tagName}> is not a valid element name. This project is configured to only allow names matching the following regular expression:`,
4898
5001
  "",
@@ -5294,7 +5397,7 @@ class EmptyTitle extends Rule {
5294
5397
  }
5295
5398
  }
5296
5399
 
5297
- const defaults$f = {
5400
+ const defaults$g = {
5298
5401
  allowMultipleH1: false,
5299
5402
  minInitialRank: "h1",
5300
5403
  sectioningRoots: ["dialog", '[role="dialog"]'],
@@ -5325,7 +5428,7 @@ function parseMaxInitial(value) {
5325
5428
  }
5326
5429
  class HeadingLevel extends Rule {
5327
5430
  constructor(options) {
5328
- super({ ...defaults$f, ...options });
5431
+ super({ ...defaults$g, ...options });
5329
5432
  this.stack = [];
5330
5433
  this.minInitialRank = parseMaxInitial(this.options.minInitialRank);
5331
5434
  this.sectionRoots = this.options.sectioningRoots.map((it) => new Pattern(it));
@@ -5483,12 +5586,12 @@ class HeadingLevel extends Rule {
5483
5586
  }
5484
5587
  }
5485
5588
 
5486
- const defaults$e = {
5589
+ const defaults$f = {
5487
5590
  pattern: "kebabcase",
5488
5591
  };
5489
5592
  class IdPattern extends Rule {
5490
5593
  constructor(options) {
5491
- super({ ...defaults$e, ...options });
5594
+ super({ ...defaults$f, ...options });
5492
5595
  this.pattern = parsePattern(this.options.pattern);
5493
5596
  }
5494
5597
  static schema() {
@@ -5839,12 +5942,12 @@ function findLabelByParent(el) {
5839
5942
  return [];
5840
5943
  }
5841
5944
 
5842
- const defaults$d = {
5945
+ const defaults$e = {
5843
5946
  maxlength: 70,
5844
5947
  };
5845
5948
  class LongTitle extends Rule {
5846
5949
  constructor(options) {
5847
- super({ ...defaults$d, ...options });
5950
+ super({ ...defaults$e, ...options });
5848
5951
  this.maxlength = this.options.maxlength;
5849
5952
  }
5850
5953
  static schema() {
@@ -5991,13 +6094,13 @@ class MultipleLabeledControls extends Rule {
5991
6094
  }
5992
6095
  }
5993
6096
 
5994
- const defaults$c = {
6097
+ const defaults$d = {
5995
6098
  include: null,
5996
6099
  exclude: null,
5997
6100
  };
5998
6101
  class NoAutoplay extends Rule {
5999
6102
  constructor(options) {
6000
- super({ ...defaults$c, ...options });
6103
+ super({ ...defaults$d, ...options });
6001
6104
  }
6002
6105
  documentation(context) {
6003
6106
  const tagName = context ? ` on <${context.tagName}>` : "";
@@ -6238,14 +6341,14 @@ Omitted end tags can be ambigious for humans to read and many editors have troub
6238
6341
  }
6239
6342
  }
6240
6343
 
6241
- const defaults$b = {
6344
+ const defaults$c = {
6242
6345
  include: null,
6243
6346
  exclude: null,
6244
6347
  allowedProperties: ["display"],
6245
6348
  };
6246
6349
  class NoInlineStyle extends Rule {
6247
6350
  constructor(options) {
6248
- super({ ...defaults$b, ...options });
6351
+ super({ ...defaults$c, ...options });
6249
6352
  }
6250
6353
  static schema() {
6251
6354
  return {
@@ -6447,7 +6550,7 @@ class NoMultipleMain extends Rule {
6447
6550
  }
6448
6551
  }
6449
6552
 
6450
- const defaults$a = {
6553
+ const defaults$b = {
6451
6554
  relaxed: false,
6452
6555
  };
6453
6556
  const textRegexp = /([<>]|&(?![a-zA-Z0-9#]+;))/g;
@@ -6464,7 +6567,7 @@ const replacementTable = {
6464
6567
  };
6465
6568
  class NoRawCharacters extends Rule {
6466
6569
  constructor(options) {
6467
- super({ ...defaults$a, ...options });
6570
+ super({ ...defaults$b, ...options });
6468
6571
  this.relaxed = this.options.relaxed;
6469
6572
  }
6470
6573
  static schema() {
@@ -6642,13 +6745,13 @@ class NoRedundantRole extends Rule {
6642
6745
  }
6643
6746
 
6644
6747
  const xmlns = /^(.+):.+$/;
6645
- const defaults$9 = {
6748
+ const defaults$a = {
6646
6749
  ignoreForeign: true,
6647
6750
  ignoreXML: true,
6648
6751
  };
6649
6752
  class NoSelfClosing extends Rule {
6650
6753
  constructor(options) {
6651
- super({ ...defaults$9, ...options });
6754
+ super({ ...defaults$a, ...options });
6652
6755
  }
6653
6756
  static schema() {
6654
6757
  return {
@@ -6781,13 +6884,13 @@ const replacement = {
6781
6884
  reset: '<button type="reset">',
6782
6885
  image: '<button type="button">',
6783
6886
  };
6784
- const defaults$8 = {
6887
+ const defaults$9 = {
6785
6888
  include: null,
6786
6889
  exclude: null,
6787
6890
  };
6788
6891
  class PreferButton extends Rule {
6789
6892
  constructor(options) {
6790
- super({ ...defaults$8, ...options });
6893
+ super({ ...defaults$9, ...options });
6791
6894
  }
6792
6895
  static schema() {
6793
6896
  return {
@@ -6862,7 +6965,7 @@ class PreferButton extends Rule {
6862
6965
  }
6863
6966
  }
6864
6967
 
6865
- const defaults$7 = {
6968
+ const defaults$8 = {
6866
6969
  mapping: {
6867
6970
  article: "article",
6868
6971
  banner: "header",
@@ -6892,7 +6995,7 @@ const defaults$7 = {
6892
6995
  };
6893
6996
  class PreferNativeElement extends Rule {
6894
6997
  constructor(options) {
6895
- super({ ...defaults$7, ...options });
6998
+ super({ ...defaults$8, ...options });
6896
6999
  }
6897
7000
  static schema() {
6898
7001
  return {
@@ -7012,7 +7115,7 @@ class PreferTbody extends Rule {
7012
7115
  }
7013
7116
  }
7014
7117
 
7015
- const defaults$6 = {
7118
+ const defaults$7 = {
7016
7119
  target: "all",
7017
7120
  };
7018
7121
  const crossorigin = new RegExp("^(\\w+://|//)"); /* e.g. https:// or // */
@@ -7022,7 +7125,7 @@ const supportSri = {
7022
7125
  };
7023
7126
  class RequireSri extends Rule {
7024
7127
  constructor(options) {
7025
- super({ ...defaults$6, ...options });
7128
+ super({ ...defaults$7, ...options });
7026
7129
  this.target = this.options.target;
7027
7130
  }
7028
7131
  static schema() {
@@ -7150,7 +7253,7 @@ class SvgFocusable extends Rule {
7150
7253
  }
7151
7254
  }
7152
7255
 
7153
- const defaults$5 = {
7256
+ const defaults$6 = {
7154
7257
  characters: [
7155
7258
  { pattern: " ", replacement: "&nbsp;", description: "non-breaking space" },
7156
7259
  { pattern: "-", replacement: "&#8209;", description: "non-breaking hyphen" },
@@ -7193,7 +7296,7 @@ function matchAll(text, regexp) {
7193
7296
  }
7194
7297
  class TelNonBreaking extends Rule {
7195
7298
  constructor(options) {
7196
- super({ ...defaults$5, ...options });
7299
+ super({ ...defaults$6, ...options });
7197
7300
  this.regex = constructRegex(this.options.characters);
7198
7301
  }
7199
7302
  static schema() {
@@ -9272,6 +9375,95 @@ class UnknownCharReference extends Rule {
9272
9375
  }
9273
9376
  }
9274
9377
 
9378
+ var RuleContext;
9379
+ (function (RuleContext) {
9380
+ RuleContext[RuleContext["EMPTY"] = 1] = "EMPTY";
9381
+ RuleContext[RuleContext["WHITESPACE"] = 2] = "WHITESPACE";
9382
+ RuleContext[RuleContext["LEADING_CHARACTER"] = 3] = "LEADING_CHARACTER";
9383
+ RuleContext[RuleContext["DISALLOWED_CHARACTER"] = 4] = "DISALLOWED_CHARACTER";
9384
+ })(RuleContext || (RuleContext = {}));
9385
+ const defaults$5 = {
9386
+ relaxed: false,
9387
+ };
9388
+ class ValidID extends Rule {
9389
+ constructor(options) {
9390
+ super({ ...defaults$5, ...options });
9391
+ }
9392
+ static schema() {
9393
+ return {
9394
+ relaxed: {
9395
+ type: "boolean",
9396
+ },
9397
+ };
9398
+ }
9399
+ documentation(context) {
9400
+ const { relaxed } = this.options;
9401
+ const message = context
9402
+ ? this.messages[context].replace("id", "ID").replace(/^(.)/, (m) => m.toUpperCase())
9403
+ : "Element ID is not valid";
9404
+ const relaxedDescription = relaxed
9405
+ ? []
9406
+ : [
9407
+ " - ID must begin with a letter",
9408
+ " - ID must only contain alphanumerical characters, `-` and `_`",
9409
+ ];
9410
+ return {
9411
+ description: [
9412
+ `${message}.`,
9413
+ "",
9414
+ "Under the current configuration the following rules are applied:",
9415
+ "",
9416
+ " - ID must not be empty",
9417
+ " - ID must not contain any whitespace characters",
9418
+ ...relaxedDescription,
9419
+ ].join("\n"),
9420
+ url: ruleDocumentationUrl("@/rules/valid-id.ts"),
9421
+ };
9422
+ }
9423
+ setup() {
9424
+ this.on("attr", this.isRelevant, (event) => {
9425
+ const { value } = event;
9426
+ if (value === null || value instanceof DynamicValue) {
9427
+ return;
9428
+ }
9429
+ if (value === "") {
9430
+ const context = RuleContext.EMPTY;
9431
+ this.report(event.target, this.messages[context], event.location, context);
9432
+ return;
9433
+ }
9434
+ if (value.match(/\s/)) {
9435
+ const context = RuleContext.WHITESPACE;
9436
+ this.report(event.target, this.messages[context], event.valueLocation, context);
9437
+ return;
9438
+ }
9439
+ const { relaxed } = this.options;
9440
+ if (relaxed) {
9441
+ return;
9442
+ }
9443
+ if (value.match(/^[^a-zA-Z]/)) {
9444
+ const context = RuleContext.LEADING_CHARACTER;
9445
+ this.report(event.target, this.messages[context], event.valueLocation, context);
9446
+ return;
9447
+ }
9448
+ if (value.match(/[^a-zA-Z0-9-_]/)) {
9449
+ const context = RuleContext.DISALLOWED_CHARACTER;
9450
+ this.report(event.target, this.messages[context], event.valueLocation, context);
9451
+ }
9452
+ });
9453
+ }
9454
+ get messages() {
9455
+ return {
9456
+ [RuleContext.EMPTY]: "element id must not be empty",
9457
+ [RuleContext.WHITESPACE]: "element id must not contain whitespace",
9458
+ [RuleContext.LEADING_CHARACTER]: "element id must begin with a letter",
9459
+ [RuleContext.DISALLOWED_CHARACTER]: "element id must only contain alphanumerical, dash and underscore characters",
9460
+ };
9461
+ }
9462
+ isRelevant(event) {
9463
+ return event.key === "id";
9464
+ }
9465
+ }
9466
+
9275
9467
  var Style$1;
9276
9468
  (function (Style) {
9277
9469
  Style[Style["Any"] = 0] = "Any";
@@ -9783,6 +9975,7 @@ const bundledRules = {
9783
9975
  "tel-non-breaking": TelNonBreaking,
9784
9976
  "text-content": TextContent,
9785
9977
  "unrecognized-char-ref": UnknownCharReference,
9978
+ "valid-id": ValidID,
9786
9979
  void: Void,
9787
9980
  "void-content": VoidContent,
9788
9981
  "void-style": VoidStyle,
@@ -9880,6 +10073,7 @@ const config$1 = {
9880
10073
  "tel-non-breaking": "error",
9881
10074
  "text-content": "error",
9882
10075
  "unrecognized-char-ref": "error",
10076
+ "valid-id": ["error", { relaxed: false }],
9883
10077
  void: "off",
9884
10078
  "void-content": "error",
9885
10079
  "void-style": "error",
@@ -9915,6 +10109,7 @@ const config = {
9915
10109
  "no-raw-characters": ["error", { relaxed: true }],
9916
10110
  "script-element": "error",
9917
10111
  "unrecognized-char-ref": "error",
10112
+ "valid-id": ["error", { relaxed: true }],
9918
10113
  "void-content": "error",
9919
10114
  },
9920
10115
  };
@@ -9984,7 +10179,7 @@ class ResolvedConfig {
9984
10179
  }
9985
10180
  catch (err) {
9986
10181
  const message = err instanceof Error ? err.message : String(err);
9987
- throw new NestedError(`When transforming "${source.filename}": ${message}`, err);
10182
+ throw new NestedError(`When transforming "${source.filename}": ${message}`, ensureError(err));
9988
10183
  }
9989
10184
  }
9990
10185
  else {
@@ -10056,7 +10251,7 @@ function loadFromFile(filename) {
10056
10251
  json = requireUncached(filename);
10057
10252
  }
10058
10253
  catch (err) {
10059
- throw new ConfigError(`Failed to read configuration from "${filename}"`, err);
10254
+ throw new ConfigError(`Failed to read configuration from "${filename}"`, ensureError(err));
10060
10255
  }
10061
10256
  /* expand any relative paths */
10062
10257
  for (const key of ["extends", "elements", "plugins"]) {
@@ -10261,7 +10456,7 @@ class Config {
10261
10456
  }
10262
10457
  catch (err) {
10263
10458
  const message = err instanceof Error ? err.message : String(err);
10264
- throw new ConfigError(`Failed to load elements from "${entry}": ${message}`, err);
10459
+ throw new ConfigError(`Failed to load elements from "${entry}": ${message}`, ensureError(err));
10265
10460
  }
10266
10461
  }
10267
10462
  metaTable.init();
@@ -10338,7 +10533,7 @@ class Config {
10338
10533
  }
10339
10534
  catch (err) {
10340
10535
  const message = err instanceof Error ? err.message : String(err);
10341
- throw new ConfigError(`Failed to load plugin "${moduleName}": ${message}`, err);
10536
+ throw new ConfigError(`Failed to load plugin "${moduleName}": ${message}`, ensureError(err));
10342
10537
  }
10343
10538
  });
10344
10539
  }
@@ -10431,7 +10626,7 @@ class Config {
10431
10626
  throw new ConfigError(`Failed to load transformer "${name}": ${err.message}`, err);
10432
10627
  }
10433
10628
  else {
10434
- throw new ConfigError(`Failed to load transformer "${name}"`, err);
10629
+ throw new ConfigError(`Failed to load transformer "${name}"`, ensureError(err));
10435
10630
  }
10436
10631
  }
10437
10632
  });
@@ -11155,6 +11350,7 @@ class Parser {
11155
11350
  location: token.location,
11156
11351
  type: token.type,
11157
11352
  data: Array.from(token.data),
11353
+ token,
11158
11354
  });
11159
11355
  }
11160
11356
  return it;
@@ -11473,8 +11669,9 @@ class Engine {
11473
11669
  getRuleDocumentation(ruleId, context // eslint-disable-line @typescript-eslint/explicit-module-boundary-types
11474
11670
  ) {
11475
11671
  const rules = this.config.getRules();
11476
- if (rules.has(ruleId)) {
11477
- const [, options] = rules.get(ruleId);
11672
+ const ruleData = rules.get(ruleId);
11673
+ if (ruleData) {
11674
+ const [, options] = ruleData;
11478
11675
  const rule = this.instantiateRule(ruleId, options);
11479
11676
  return rule.documentation(context);
11480
11677
  }
@@ -12405,5 +12602,5 @@ function getFormatter(name) {
12405
12602
  return (_a = availableFormatters[name]) !== null && _a !== void 0 ? _a : null;
12406
12603
  }
12407
12604
 
12408
- 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, codeframe as k, legacyRequire as l, bugs as m, name as n, presets as p, ruleExists as r, version as v };
12605
+ 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, WrappedError as W, ConfigError as a, ConfigLoader as b, StaticConfigLoader as c, HtmlElement as d, SchemaValidationError as e, NestedError as f, MetaCopyableProperty as g, Reporter as h, TemplateExtractor as i, getFormatter as j, ensureError as k, legacyRequire as l, compatibilityCheck as m, codeframe as n, name as o, presets as p, bugs as q, ruleExists as r, version as v };
12409
12606
  //# sourceMappingURL=core.js.map