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/cjs/core.js CHANGED
@@ -274,6 +274,42 @@ var ajvSchemaDraft = {
274
274
  }
275
275
  };
276
276
 
277
+ function stringify(value) {
278
+ if (typeof value === "string") {
279
+ return String(value);
280
+ }
281
+ else {
282
+ return JSON.stringify(value);
283
+ }
284
+ }
285
+ /**
286
+ * Represents an `Error` created from arbitrary values.
287
+ *
288
+ * @public
289
+ */
290
+ class WrappedError extends Error {
291
+ constructor(message) {
292
+ super(stringify(message));
293
+ }
294
+ }
295
+
296
+ /**
297
+ * Ensures the value is an Error.
298
+ *
299
+ * If the passed value is not an `Error` instance a [[WrappedError]] is
300
+ * constructed with the stringified value.
301
+ *
302
+ * @internal
303
+ */
304
+ function ensureError(value) {
305
+ if (value instanceof Error) {
306
+ return value;
307
+ }
308
+ else {
309
+ return new WrappedError(value);
310
+ }
311
+ }
312
+
277
313
  class NestedError extends Error {
278
314
  constructor(message, nested) {
279
315
  super(message);
@@ -327,6 +363,32 @@ class SchemaValidationError extends UserError {
327
363
  }
328
364
  }
329
365
 
366
+ /**
367
+ * Computes hash for given string.
368
+ *
369
+ * @internal
370
+ */
371
+ function cyrb53(str) {
372
+ const a = 2654435761;
373
+ const b = 1597334677;
374
+ const c = 2246822507;
375
+ const d = 3266489909;
376
+ const e = 4294967296;
377
+ const f = 2097151;
378
+ const seed = 0;
379
+ let h1 = 0xdeadbeef ^ seed;
380
+ let h2 = 0x41c6ce57 ^ seed;
381
+ for (let i = 0, ch; i < str.length; i++) {
382
+ ch = str.charCodeAt(i);
383
+ h1 = Math.imul(h1 ^ ch, a);
384
+ h2 = Math.imul(h2 ^ ch, b);
385
+ }
386
+ h1 = Math.imul(h1 ^ (h1 >>> 16), c) ^ Math.imul(h2 ^ (h2 >>> 13), d);
387
+ h2 = Math.imul(h2 ^ (h2 >>> 16), c) ^ Math.imul(h1 ^ (h1 >>> 13), d);
388
+ return e * (f & h2) + (h1 >>> 0);
389
+ }
390
+ const computeHash = cyrb53;
391
+
330
392
  const projectRoot = path__default["default"].resolve(__dirname, "../../");
331
393
  const legacyRequire = require;
332
394
  const distFolder = path__default["default"].resolve(projectRoot, "dist/cjs");
@@ -731,6 +793,35 @@ var schema = {
731
793
  definitions: definitions
732
794
  };
733
795
 
796
+ /**
797
+ * AJV keyword "regexp" to validate the type to be a regular expression.
798
+ * Injects errors with the "type" keyword to give the same output.
799
+ */
800
+ /* istanbul ignore next: manual testing */
801
+ const ajvRegexpValidate = function (data, dataCxt) {
802
+ const valid = data instanceof RegExp;
803
+ if (!valid) {
804
+ ajvRegexpValidate.errors = [
805
+ {
806
+ instancePath: dataCxt === null || dataCxt === void 0 ? void 0 : dataCxt.instancePath,
807
+ schemaPath: undefined,
808
+ keyword: "type",
809
+ message: "should be a regular expression",
810
+ params: {
811
+ keyword: "type",
812
+ },
813
+ },
814
+ ];
815
+ }
816
+ return valid;
817
+ };
818
+ const ajvRegexpKeyword = {
819
+ keyword: "regexp",
820
+ schema: false,
821
+ errors: true,
822
+ validate: ajvRegexpValidate,
823
+ };
824
+
734
825
  var TextContent$1;
735
826
  (function (TextContent) {
736
827
  /* forbid node to have text content, inter-element whitespace is ignored */
@@ -838,6 +929,43 @@ function migrateElement(src) {
838
929
  return result;
839
930
  }
840
931
 
932
+ /**
933
+ * Returns true if given element is a descendant of given tagname.
934
+ *
935
+ * @internal
936
+ */
937
+ function isDescendant(node, tagName) {
938
+ let cur = node.parent;
939
+ while (cur && !cur.isRootElement()) {
940
+ if (cur.is(tagName)) {
941
+ return true;
942
+ }
943
+ cur = cur.parent;
944
+ }
945
+ return false;
946
+ }
947
+
948
+ /**
949
+ * Returns true if given element has given attribute (no matter the value, null,
950
+ * dynamic, etc).
951
+ */
952
+ function hasAttribute(node, attr) {
953
+ return node.hasAttribute(attr);
954
+ }
955
+
956
+ /**
957
+ * Matches attribute against value.
958
+ */
959
+ function matchAttribute(node, key, op, value) {
960
+ const nodeValue = (node.getAttributeValue(key) || "").toLowerCase();
961
+ switch (op) {
962
+ case "!=":
963
+ return nodeValue !== value;
964
+ case "=":
965
+ return nodeValue === value;
966
+ }
967
+ }
968
+
841
969
  const dynamicKeys = [
842
970
  "metadata",
843
971
  "flow",
@@ -849,44 +977,17 @@ const dynamicKeys = [
849
977
  "labelable",
850
978
  ];
851
979
  const functionTable = {
852
- isDescendant,
853
- hasAttribute,
854
- matchAttribute,
980
+ isDescendant: isDescendantFacade,
981
+ hasAttribute: hasAttributeFacade,
982
+ matchAttribute: matchAttributeFacade,
855
983
  };
984
+ const schemaCache = new Map();
856
985
  function clone(src) {
857
986
  return JSON.parse(JSON.stringify(src));
858
987
  }
859
988
  function overwriteMerge$1(a, b) {
860
989
  return b;
861
990
  }
862
- /**
863
- * AJV keyword "regexp" to validate the type to be a regular expression.
864
- * Injects errors with the "type" keyword to give the same output.
865
- */
866
- /* istanbul ignore next: manual testing */
867
- const ajvRegexpValidate = function (data, dataCxt) {
868
- const valid = data instanceof RegExp;
869
- if (!valid) {
870
- ajvRegexpValidate.errors = [
871
- {
872
- instancePath: dataCxt === null || dataCxt === void 0 ? void 0 : dataCxt.instancePath,
873
- schemaPath: undefined,
874
- keyword: "type",
875
- message: "should be regexp",
876
- params: {
877
- keyword: "type",
878
- },
879
- },
880
- ];
881
- }
882
- return valid;
883
- };
884
- const ajvRegexpKeyword = {
885
- keyword: "regexp",
886
- schema: false,
887
- errors: true,
888
- validate: ajvRegexpValidate,
889
- };
890
991
  /**
891
992
  * @public
892
993
  */
@@ -962,7 +1063,7 @@ class MetaTable {
962
1063
  if (err instanceof SchemaValidationError) {
963
1064
  throw err;
964
1065
  }
965
- throw new UserError(`Failed to load element metadata from "${filename}"`, err);
1066
+ throw new UserError(`Failed to load element metadata from "${filename}"`, ensureError(err));
966
1067
  }
967
1068
  }
968
1069
  /**
@@ -1023,11 +1124,20 @@ class MetaTable {
1023
1124
  * Construct a new AJV schema validator.
1024
1125
  */
1025
1126
  getSchemaValidator() {
1026
- const ajv = new Ajv__default["default"]({ strict: true, strictTuples: true, strictTypes: true });
1027
- ajv.addMetaSchema(ajvSchemaDraft);
1028
- ajv.addKeyword(ajvRegexpKeyword);
1029
- ajv.addKeyword({ keyword: "copyable" });
1030
- return ajv.compile(this.schema);
1127
+ const hash = computeHash(JSON.stringify(this.schema));
1128
+ const cached = schemaCache.get(hash);
1129
+ if (cached) {
1130
+ return cached;
1131
+ }
1132
+ else {
1133
+ const ajv = new Ajv__default["default"]({ strict: true, strictTuples: true, strictTypes: true });
1134
+ ajv.addMetaSchema(ajvSchemaDraft);
1135
+ ajv.addKeyword(ajvRegexpKeyword);
1136
+ ajv.addKeyword({ keyword: "copyable" });
1137
+ const validate = ajv.compile(this.schema);
1138
+ schemaCache.set(hash, validate);
1139
+ return validate;
1140
+ }
1031
1141
  }
1032
1142
  /**
1033
1143
  * @public
@@ -1129,36 +1239,27 @@ function parseExpression(expr) {
1129
1239
  return [func, options];
1130
1240
  }
1131
1241
  }
1132
- function isDescendant(node, tagName) {
1242
+ function isDescendantFacade(node, tagName) {
1133
1243
  if (typeof tagName !== "string") {
1134
1244
  throw new Error(`Property expression "isDescendant" must take string argument when evaluating metadata for <${node.tagName}>`);
1135
1245
  }
1136
- let cur = node.parent;
1137
- while (cur && !cur.isRootElement()) {
1138
- if (cur.is(tagName)) {
1139
- return true;
1140
- }
1141
- cur = cur.parent;
1142
- }
1143
- return false;
1246
+ return isDescendant(node, tagName);
1144
1247
  }
1145
- function hasAttribute(node, attr) {
1248
+ function hasAttributeFacade(node, attr) {
1146
1249
  if (typeof attr !== "string") {
1147
1250
  throw new Error(`Property expression "hasAttribute" must take string argument when evaluating metadata for <${node.tagName}>`);
1148
1251
  }
1149
- return node.hasAttribute(attr);
1252
+ return hasAttribute(node, attr);
1150
1253
  }
1151
- function matchAttribute(node, match) {
1254
+ function matchAttributeFacade(node, match) {
1152
1255
  if (!Array.isArray(match) || match.length !== 3) {
1153
1256
  throw new Error(`Property expression "matchAttribute" must take [key, op, value] array as argument when evaluating metadata for <${node.tagName}>`);
1154
1257
  }
1155
1258
  const [key, op, value] = match.map((x) => x.toLowerCase());
1156
- const nodeValue = (node.getAttributeValue(key) || "").toLowerCase();
1157
1259
  switch (op) {
1158
1260
  case "!=":
1159
- return nodeValue !== value;
1160
1261
  case "=":
1161
- return nodeValue === value;
1262
+ return matchAttribute(node, key, op, value);
1162
1263
  default:
1163
1264
  throw new Error(`Property expression "matchAttribute" has invalid operator "${op}" when evaluating metadata for <${node.tagName}>`);
1164
1265
  }
@@ -2009,9 +2110,10 @@ class HtmlElement extends DOMNode {
2009
2110
  /* if a unique id is present, use it and short-circuit */
2010
2111
  if (cur.id) {
2011
2112
  const escaped = escapeSelectorComponent(cur.id);
2012
- const matches = root.querySelectorAll(`#${escaped}`);
2113
+ const selector = escaped.match(/^\d/) ? `[id="${escaped}"]` : `#${escaped}`;
2114
+ const matches = root.querySelectorAll(selector);
2013
2115
  if (matches.length === 1) {
2014
- parts.push(`#${escaped}`);
2116
+ parts.push(selector);
2015
2117
  break;
2016
2118
  }
2017
2119
  }
@@ -3011,7 +3113,7 @@ var TRANSFORMER_API;
3011
3113
  /** @public */
3012
3114
  const name = "html-validate";
3013
3115
  /** @public */
3014
- const version = "6.8.0";
3116
+ const version = "6.10.0";
3015
3117
  /** @public */
3016
3118
  const homepage = "https://html-validate.org";
3017
3119
  /** @public */
@@ -3332,7 +3434,7 @@ function ruleDocumentationUrl(filename) {
3332
3434
  return `${homepage}/rules/${normalized}.html`;
3333
3435
  }
3334
3436
 
3335
- const defaults$q = {
3437
+ const defaults$r = {
3336
3438
  allowExternal: true,
3337
3439
  allowRelative: true,
3338
3440
  allowAbsolute: true,
@@ -3376,7 +3478,7 @@ function matchList(value, list) {
3376
3478
  }
3377
3479
  class AllowedLinks extends Rule {
3378
3480
  constructor(options) {
3379
- super({ ...defaults$q, ...options });
3481
+ super({ ...defaults$r, ...options });
3380
3482
  this.allowExternal = parseAllow(this.options.allowExternal);
3381
3483
  this.allowRelative = parseAllow(this.options.allowRelative);
3382
3484
  this.allowAbsolute = parseAllow(this.options.allowAbsolute);
@@ -3677,13 +3779,13 @@ class CaseStyle {
3677
3779
  }
3678
3780
  }
3679
3781
 
3680
- const defaults$p = {
3782
+ const defaults$q = {
3681
3783
  style: "lowercase",
3682
3784
  ignoreForeign: true,
3683
3785
  };
3684
3786
  class AttrCase extends Rule {
3685
3787
  constructor(options) {
3686
- super({ ...defaults$p, ...options });
3788
+ super({ ...defaults$q, ...options });
3687
3789
  this.style = new CaseStyle(this.options.style, "attr-case");
3688
3790
  }
3689
3791
  static schema() {
@@ -3999,9 +4101,6 @@ class Lexer {
3999
4101
  }
4000
4102
 
4001
4103
  const whitespace = /(\s+)/;
4002
- function isRelevant$3(event) {
4003
- return event.type === TokenType.ATTR_VALUE;
4004
- }
4005
4104
  class AttrDelimiter extends Rule {
4006
4105
  documentation() {
4007
4106
  return {
@@ -4010,8 +4109,12 @@ class AttrDelimiter extends Rule {
4010
4109
  };
4011
4110
  }
4012
4111
  setup() {
4013
- this.on("token", isRelevant$3, (event) => {
4014
- const delimiter = event.data[1];
4112
+ this.on("token", (event) => {
4113
+ const { token } = event;
4114
+ if (token.type !== TokenType.ATTR_VALUE) {
4115
+ return;
4116
+ }
4117
+ const delimiter = token.data[1];
4015
4118
  const match = whitespace.exec(delimiter);
4016
4119
  if (match) {
4017
4120
  const location = sliceLocation(event.location, 0, delimiter.length);
@@ -4022,7 +4125,7 @@ class AttrDelimiter extends Rule {
4022
4125
  }
4023
4126
 
4024
4127
  const DEFAULT_PATTERN = "[a-z0-9-:]+";
4025
- const defaults$o = {
4128
+ const defaults$p = {
4026
4129
  pattern: DEFAULT_PATTERN,
4027
4130
  ignoreForeign: true,
4028
4131
  };
@@ -4059,7 +4162,7 @@ function generateDescription(name, pattern) {
4059
4162
  }
4060
4163
  class AttrPattern extends Rule {
4061
4164
  constructor(options) {
4062
- super({ ...defaults$o, ...options });
4165
+ super({ ...defaults$p, ...options });
4063
4166
  this.pattern = generateRegexp(this.options.pattern);
4064
4167
  }
4065
4168
  static schema() {
@@ -4119,13 +4222,13 @@ var QuoteStyle;
4119
4222
  QuoteStyle["DOUBLE_QUOTE"] = "\"";
4120
4223
  QuoteStyle["AUTO_QUOTE"] = "auto";
4121
4224
  })(QuoteStyle || (QuoteStyle = {}));
4122
- const defaults$n = {
4225
+ const defaults$o = {
4123
4226
  style: "auto",
4124
4227
  unquoted: false,
4125
4228
  };
4126
4229
  class AttrQuotes extends Rule {
4127
4230
  constructor(options) {
4128
- super({ ...defaults$n, ...options });
4231
+ super({ ...defaults$o, ...options });
4129
4232
  this.style = parseStyle$4(this.options.style);
4130
4233
  }
4131
4234
  static schema() {
@@ -4290,12 +4393,12 @@ class AttributeAllowedValues extends Rule {
4290
4393
  }
4291
4394
  }
4292
4395
 
4293
- const defaults$m = {
4396
+ const defaults$n = {
4294
4397
  style: "omit",
4295
4398
  };
4296
4399
  class AttributeBooleanStyle extends Rule {
4297
4400
  constructor(options) {
4298
- super({ ...defaults$m, ...options });
4401
+ super({ ...defaults$n, ...options });
4299
4402
  this.hasInvalidStyle = parseStyle$3(this.options.style);
4300
4403
  }
4301
4404
  static schema() {
@@ -4371,12 +4474,12 @@ function reportMessage$1(attr, style) {
4371
4474
  return "";
4372
4475
  }
4373
4476
 
4374
- const defaults$l = {
4477
+ const defaults$m = {
4375
4478
  style: "omit",
4376
4479
  };
4377
4480
  class AttributeEmptyStyle extends Rule {
4378
4481
  constructor(options) {
4379
- super({ ...defaults$l, ...options });
4482
+ super({ ...defaults$m, ...options });
4380
4483
  this.hasInvalidStyle = parseStyle$2(this.options.style);
4381
4484
  }
4382
4485
  static schema() {
@@ -4484,12 +4587,12 @@ function describePattern(pattern) {
4484
4587
  }
4485
4588
  }
4486
4589
 
4487
- const defaults$k = {
4590
+ const defaults$l = {
4488
4591
  pattern: "kebabcase",
4489
4592
  };
4490
4593
  class ClassPattern extends Rule {
4491
4594
  constructor(options) {
4492
- super({ ...defaults$k, ...options });
4595
+ super({ ...defaults$l, ...options });
4493
4596
  this.pattern = parsePattern(this.options.pattern);
4494
4597
  }
4495
4598
  static schema() {
@@ -4598,13 +4701,13 @@ class CloseOrder extends Rule {
4598
4701
  }
4599
4702
  }
4600
4703
 
4601
- const defaults$j = {
4704
+ const defaults$k = {
4602
4705
  include: null,
4603
4706
  exclude: null,
4604
4707
  };
4605
4708
  class Deprecated extends Rule {
4606
4709
  constructor(options) {
4607
- super({ ...defaults$j, ...options });
4710
+ super({ ...defaults$k, ...options });
4608
4711
  }
4609
4712
  static schema() {
4610
4713
  return {
@@ -4767,12 +4870,12 @@ class NoStyleTag$1 extends Rule {
4767
4870
  }
4768
4871
  }
4769
4872
 
4770
- const defaults$i = {
4873
+ const defaults$j = {
4771
4874
  style: "uppercase",
4772
4875
  };
4773
4876
  class DoctypeStyle extends Rule {
4774
4877
  constructor(options) {
4775
- super({ ...defaults$i, ...options });
4878
+ super({ ...defaults$j, ...options });
4776
4879
  }
4777
4880
  static schema() {
4778
4881
  return {
@@ -4804,12 +4907,12 @@ class DoctypeStyle extends Rule {
4804
4907
  }
4805
4908
  }
4806
4909
 
4807
- const defaults$h = {
4910
+ const defaults$i = {
4808
4911
  style: "lowercase",
4809
4912
  };
4810
4913
  class ElementCase extends Rule {
4811
4914
  constructor(options) {
4812
- super({ ...defaults$h, ...options });
4915
+ super({ ...defaults$i, ...options });
4813
4916
  this.style = new CaseStyle(this.options.style, "element-case");
4814
4917
  }
4815
4918
  static schema() {
@@ -4875,14 +4978,14 @@ class ElementCase extends Rule {
4875
4978
  }
4876
4979
  }
4877
4980
 
4878
- const defaults$g = {
4981
+ const defaults$h = {
4879
4982
  pattern: "^[a-z][a-z0-9\\-._]*-[a-z0-9\\-._]*$",
4880
4983
  whitelist: [],
4881
4984
  blacklist: [],
4882
4985
  };
4883
4986
  class ElementName extends Rule {
4884
4987
  constructor(options) {
4885
- super({ ...defaults$g, ...options });
4988
+ super({ ...defaults$h, ...options });
4886
4989
  // eslint-disable-next-line security/detect-non-literal-regexp
4887
4990
  this.pattern = new RegExp(this.options.pattern);
4888
4991
  }
@@ -4923,7 +5026,7 @@ class ElementName extends Rule {
4923
5026
  ...context.blacklist.map((cur) => `- ${cur}`),
4924
5027
  ];
4925
5028
  }
4926
- if (context.pattern !== defaults$g.pattern) {
5029
+ if (context.pattern !== defaults$h.pattern) {
4927
5030
  return [
4928
5031
  `<${context.tagName}> is not a valid element name. This project is configured to only allow names matching the following regular expression:`,
4929
5032
  "",
@@ -5325,7 +5428,7 @@ class EmptyTitle extends Rule {
5325
5428
  }
5326
5429
  }
5327
5430
 
5328
- const defaults$f = {
5431
+ const defaults$g = {
5329
5432
  allowMultipleH1: false,
5330
5433
  minInitialRank: "h1",
5331
5434
  sectioningRoots: ["dialog", '[role="dialog"]'],
@@ -5356,7 +5459,7 @@ function parseMaxInitial(value) {
5356
5459
  }
5357
5460
  class HeadingLevel extends Rule {
5358
5461
  constructor(options) {
5359
- super({ ...defaults$f, ...options });
5462
+ super({ ...defaults$g, ...options });
5360
5463
  this.stack = [];
5361
5464
  this.minInitialRank = parseMaxInitial(this.options.minInitialRank);
5362
5465
  this.sectionRoots = this.options.sectioningRoots.map((it) => new Pattern(it));
@@ -5514,12 +5617,12 @@ class HeadingLevel extends Rule {
5514
5617
  }
5515
5618
  }
5516
5619
 
5517
- const defaults$e = {
5620
+ const defaults$f = {
5518
5621
  pattern: "kebabcase",
5519
5622
  };
5520
5623
  class IdPattern extends Rule {
5521
5624
  constructor(options) {
5522
- super({ ...defaults$e, ...options });
5625
+ super({ ...defaults$f, ...options });
5523
5626
  this.pattern = parsePattern(this.options.pattern);
5524
5627
  }
5525
5628
  static schema() {
@@ -5870,12 +5973,12 @@ function findLabelByParent(el) {
5870
5973
  return [];
5871
5974
  }
5872
5975
 
5873
- const defaults$d = {
5976
+ const defaults$e = {
5874
5977
  maxlength: 70,
5875
5978
  };
5876
5979
  class LongTitle extends Rule {
5877
5980
  constructor(options) {
5878
- super({ ...defaults$d, ...options });
5981
+ super({ ...defaults$e, ...options });
5879
5982
  this.maxlength = this.options.maxlength;
5880
5983
  }
5881
5984
  static schema() {
@@ -6022,13 +6125,13 @@ class MultipleLabeledControls extends Rule {
6022
6125
  }
6023
6126
  }
6024
6127
 
6025
- const defaults$c = {
6128
+ const defaults$d = {
6026
6129
  include: null,
6027
6130
  exclude: null,
6028
6131
  };
6029
6132
  class NoAutoplay extends Rule {
6030
6133
  constructor(options) {
6031
- super({ ...defaults$c, ...options });
6134
+ super({ ...defaults$d, ...options });
6032
6135
  }
6033
6136
  documentation(context) {
6034
6137
  const tagName = context ? ` on <${context.tagName}>` : "";
@@ -6269,14 +6372,14 @@ Omitted end tags can be ambigious for humans to read and many editors have troub
6269
6372
  }
6270
6373
  }
6271
6374
 
6272
- const defaults$b = {
6375
+ const defaults$c = {
6273
6376
  include: null,
6274
6377
  exclude: null,
6275
6378
  allowedProperties: ["display"],
6276
6379
  };
6277
6380
  class NoInlineStyle extends Rule {
6278
6381
  constructor(options) {
6279
- super({ ...defaults$b, ...options });
6382
+ super({ ...defaults$c, ...options });
6280
6383
  }
6281
6384
  static schema() {
6282
6385
  return {
@@ -6478,7 +6581,7 @@ class NoMultipleMain extends Rule {
6478
6581
  }
6479
6582
  }
6480
6583
 
6481
- const defaults$a = {
6584
+ const defaults$b = {
6482
6585
  relaxed: false,
6483
6586
  };
6484
6587
  const textRegexp = /([<>]|&(?![a-zA-Z0-9#]+;))/g;
@@ -6495,7 +6598,7 @@ const replacementTable = {
6495
6598
  };
6496
6599
  class NoRawCharacters extends Rule {
6497
6600
  constructor(options) {
6498
- super({ ...defaults$a, ...options });
6601
+ super({ ...defaults$b, ...options });
6499
6602
  this.relaxed = this.options.relaxed;
6500
6603
  }
6501
6604
  static schema() {
@@ -6673,13 +6776,13 @@ class NoRedundantRole extends Rule {
6673
6776
  }
6674
6777
 
6675
6778
  const xmlns = /^(.+):.+$/;
6676
- const defaults$9 = {
6779
+ const defaults$a = {
6677
6780
  ignoreForeign: true,
6678
6781
  ignoreXML: true,
6679
6782
  };
6680
6783
  class NoSelfClosing extends Rule {
6681
6784
  constructor(options) {
6682
- super({ ...defaults$9, ...options });
6785
+ super({ ...defaults$a, ...options });
6683
6786
  }
6684
6787
  static schema() {
6685
6788
  return {
@@ -6812,13 +6915,13 @@ const replacement = {
6812
6915
  reset: '<button type="reset">',
6813
6916
  image: '<button type="button">',
6814
6917
  };
6815
- const defaults$8 = {
6918
+ const defaults$9 = {
6816
6919
  include: null,
6817
6920
  exclude: null,
6818
6921
  };
6819
6922
  class PreferButton extends Rule {
6820
6923
  constructor(options) {
6821
- super({ ...defaults$8, ...options });
6924
+ super({ ...defaults$9, ...options });
6822
6925
  }
6823
6926
  static schema() {
6824
6927
  return {
@@ -6893,7 +6996,7 @@ class PreferButton extends Rule {
6893
6996
  }
6894
6997
  }
6895
6998
 
6896
- const defaults$7 = {
6999
+ const defaults$8 = {
6897
7000
  mapping: {
6898
7001
  article: "article",
6899
7002
  banner: "header",
@@ -6923,7 +7026,7 @@ const defaults$7 = {
6923
7026
  };
6924
7027
  class PreferNativeElement extends Rule {
6925
7028
  constructor(options) {
6926
- super({ ...defaults$7, ...options });
7029
+ super({ ...defaults$8, ...options });
6927
7030
  }
6928
7031
  static schema() {
6929
7032
  return {
@@ -7043,7 +7146,7 @@ class PreferTbody extends Rule {
7043
7146
  }
7044
7147
  }
7045
7148
 
7046
- const defaults$6 = {
7149
+ const defaults$7 = {
7047
7150
  target: "all",
7048
7151
  };
7049
7152
  const crossorigin = new RegExp("^(\\w+://|//)"); /* e.g. https:// or // */
@@ -7053,7 +7156,7 @@ const supportSri = {
7053
7156
  };
7054
7157
  class RequireSri extends Rule {
7055
7158
  constructor(options) {
7056
- super({ ...defaults$6, ...options });
7159
+ super({ ...defaults$7, ...options });
7057
7160
  this.target = this.options.target;
7058
7161
  }
7059
7162
  static schema() {
@@ -7181,7 +7284,7 @@ class SvgFocusable extends Rule {
7181
7284
  }
7182
7285
  }
7183
7286
 
7184
- const defaults$5 = {
7287
+ const defaults$6 = {
7185
7288
  characters: [
7186
7289
  { pattern: " ", replacement: "&nbsp;", description: "non-breaking space" },
7187
7290
  { pattern: "-", replacement: "&#8209;", description: "non-breaking hyphen" },
@@ -7224,7 +7327,7 @@ function matchAll(text, regexp) {
7224
7327
  }
7225
7328
  class TelNonBreaking extends Rule {
7226
7329
  constructor(options) {
7227
- super({ ...defaults$5, ...options });
7330
+ super({ ...defaults$6, ...options });
7228
7331
  this.regex = constructRegex(this.options.characters);
7229
7332
  }
7230
7333
  static schema() {
@@ -9303,6 +9406,95 @@ class UnknownCharReference extends Rule {
9303
9406
  }
9304
9407
  }
9305
9408
 
9409
+ var RuleContext;
9410
+ (function (RuleContext) {
9411
+ RuleContext[RuleContext["EMPTY"] = 1] = "EMPTY";
9412
+ RuleContext[RuleContext["WHITESPACE"] = 2] = "WHITESPACE";
9413
+ RuleContext[RuleContext["LEADING_CHARACTER"] = 3] = "LEADING_CHARACTER";
9414
+ RuleContext[RuleContext["DISALLOWED_CHARACTER"] = 4] = "DISALLOWED_CHARACTER";
9415
+ })(RuleContext || (RuleContext = {}));
9416
+ const defaults$5 = {
9417
+ relaxed: false,
9418
+ };
9419
+ class ValidID extends Rule {
9420
+ constructor(options) {
9421
+ super({ ...defaults$5, ...options });
9422
+ }
9423
+ static schema() {
9424
+ return {
9425
+ relaxed: {
9426
+ type: "boolean",
9427
+ },
9428
+ };
9429
+ }
9430
+ documentation(context) {
9431
+ const { relaxed } = this.options;
9432
+ const message = context
9433
+ ? this.messages[context].replace("id", "ID").replace(/^(.)/, (m) => m.toUpperCase())
9434
+ : "Element ID is not valid";
9435
+ const relaxedDescription = relaxed
9436
+ ? []
9437
+ : [
9438
+ " - ID must begin with a letter",
9439
+ " - ID must only contain alphanumerical characters, `-` and `_`",
9440
+ ];
9441
+ return {
9442
+ description: [
9443
+ `${message}.`,
9444
+ "",
9445
+ "Under the current configuration the following rules are applied:",
9446
+ "",
9447
+ " - ID must not be empty",
9448
+ " - ID must not contain any whitespace characters",
9449
+ ...relaxedDescription,
9450
+ ].join("\n"),
9451
+ url: ruleDocumentationUrl("@/rules/valid-id.ts"),
9452
+ };
9453
+ }
9454
+ setup() {
9455
+ this.on("attr", this.isRelevant, (event) => {
9456
+ const { value } = event;
9457
+ if (value === null || value instanceof DynamicValue) {
9458
+ return;
9459
+ }
9460
+ if (value === "") {
9461
+ const context = RuleContext.EMPTY;
9462
+ this.report(event.target, this.messages[context], event.location, context);
9463
+ return;
9464
+ }
9465
+ if (value.match(/\s/)) {
9466
+ const context = RuleContext.WHITESPACE;
9467
+ this.report(event.target, this.messages[context], event.valueLocation, context);
9468
+ return;
9469
+ }
9470
+ const { relaxed } = this.options;
9471
+ if (relaxed) {
9472
+ return;
9473
+ }
9474
+ if (value.match(/^[^a-zA-Z]/)) {
9475
+ const context = RuleContext.LEADING_CHARACTER;
9476
+ this.report(event.target, this.messages[context], event.valueLocation, context);
9477
+ return;
9478
+ }
9479
+ if (value.match(/[^a-zA-Z0-9-_]/)) {
9480
+ const context = RuleContext.DISALLOWED_CHARACTER;
9481
+ this.report(event.target, this.messages[context], event.valueLocation, context);
9482
+ }
9483
+ });
9484
+ }
9485
+ get messages() {
9486
+ return {
9487
+ [RuleContext.EMPTY]: "element id must not be empty",
9488
+ [RuleContext.WHITESPACE]: "element id must not contain whitespace",
9489
+ [RuleContext.LEADING_CHARACTER]: "element id must begin with a letter",
9490
+ [RuleContext.DISALLOWED_CHARACTER]: "element id must only contain alphanumerical, dash and underscore characters",
9491
+ };
9492
+ }
9493
+ isRelevant(event) {
9494
+ return event.key === "id";
9495
+ }
9496
+ }
9497
+
9306
9498
  var Style$1;
9307
9499
  (function (Style) {
9308
9500
  Style[Style["Any"] = 0] = "Any";
@@ -9814,6 +10006,7 @@ const bundledRules = {
9814
10006
  "tel-non-breaking": TelNonBreaking,
9815
10007
  "text-content": TextContent,
9816
10008
  "unrecognized-char-ref": UnknownCharReference,
10009
+ "valid-id": ValidID,
9817
10010
  void: Void,
9818
10011
  "void-content": VoidContent,
9819
10012
  "void-style": VoidStyle,
@@ -9911,6 +10104,7 @@ const config$1 = {
9911
10104
  "tel-non-breaking": "error",
9912
10105
  "text-content": "error",
9913
10106
  "unrecognized-char-ref": "error",
10107
+ "valid-id": ["error", { relaxed: false }],
9914
10108
  void: "off",
9915
10109
  "void-content": "error",
9916
10110
  "void-style": "error",
@@ -9946,6 +10140,7 @@ const config = {
9946
10140
  "no-raw-characters": ["error", { relaxed: true }],
9947
10141
  "script-element": "error",
9948
10142
  "unrecognized-char-ref": "error",
10143
+ "valid-id": ["error", { relaxed: true }],
9949
10144
  "void-content": "error",
9950
10145
  },
9951
10146
  };
@@ -10015,7 +10210,7 @@ class ResolvedConfig {
10015
10210
  }
10016
10211
  catch (err) {
10017
10212
  const message = err instanceof Error ? err.message : String(err);
10018
- throw new NestedError(`When transforming "${source.filename}": ${message}`, err);
10213
+ throw new NestedError(`When transforming "${source.filename}": ${message}`, ensureError(err));
10019
10214
  }
10020
10215
  }
10021
10216
  else {
@@ -10087,7 +10282,7 @@ function loadFromFile(filename) {
10087
10282
  json = requireUncached(filename);
10088
10283
  }
10089
10284
  catch (err) {
10090
- throw new ConfigError(`Failed to read configuration from "${filename}"`, err);
10285
+ throw new ConfigError(`Failed to read configuration from "${filename}"`, ensureError(err));
10091
10286
  }
10092
10287
  /* expand any relative paths */
10093
10288
  for (const key of ["extends", "elements", "plugins"]) {
@@ -10292,7 +10487,7 @@ class Config {
10292
10487
  }
10293
10488
  catch (err) {
10294
10489
  const message = err instanceof Error ? err.message : String(err);
10295
- throw new ConfigError(`Failed to load elements from "${entry}": ${message}`, err);
10490
+ throw new ConfigError(`Failed to load elements from "${entry}": ${message}`, ensureError(err));
10296
10491
  }
10297
10492
  }
10298
10493
  metaTable.init();
@@ -10369,7 +10564,7 @@ class Config {
10369
10564
  }
10370
10565
  catch (err) {
10371
10566
  const message = err instanceof Error ? err.message : String(err);
10372
- throw new ConfigError(`Failed to load plugin "${moduleName}": ${message}`, err);
10567
+ throw new ConfigError(`Failed to load plugin "${moduleName}": ${message}`, ensureError(err));
10373
10568
  }
10374
10569
  });
10375
10570
  }
@@ -10462,7 +10657,7 @@ class Config {
10462
10657
  throw new ConfigError(`Failed to load transformer "${name}": ${err.message}`, err);
10463
10658
  }
10464
10659
  else {
10465
- throw new ConfigError(`Failed to load transformer "${name}"`, err);
10660
+ throw new ConfigError(`Failed to load transformer "${name}"`, ensureError(err));
10466
10661
  }
10467
10662
  }
10468
10663
  });
@@ -11186,6 +11381,7 @@ class Parser {
11186
11381
  location: token.location,
11187
11382
  type: token.type,
11188
11383
  data: Array.from(token.data),
11384
+ token,
11189
11385
  });
11190
11386
  }
11191
11387
  return it;
@@ -11504,8 +11700,9 @@ class Engine {
11504
11700
  getRuleDocumentation(ruleId, context // eslint-disable-line @typescript-eslint/explicit-module-boundary-types
11505
11701
  ) {
11506
11702
  const rules = this.config.getRules();
11507
- if (rules.has(ruleId)) {
11508
- const [, options] = rules.get(ruleId);
11703
+ const ruleData = rules.get(ruleId);
11704
+ if (ruleData) {
11705
+ const [, options] = ruleData;
11509
11706
  const rule = this.instantiateRule(ruleId, options);
11510
11707
  return rule.documentation(context);
11511
11708
  }
@@ -12446,6 +12643,7 @@ exports.HtmlElement = HtmlElement;
12446
12643
  exports.HtmlValidate = HtmlValidate;
12447
12644
  exports.MetaCopyableProperty = MetaCopyableProperty;
12448
12645
  exports.MetaTable = MetaTable;
12646
+ exports.NestedError = NestedError;
12449
12647
  exports.Parser = Parser;
12450
12648
  exports.Reporter = Reporter;
12451
12649
  exports.Rule = Rule;
@@ -12454,9 +12652,11 @@ exports.StaticConfigLoader = StaticConfigLoader;
12454
12652
  exports.TemplateExtractor = TemplateExtractor;
12455
12653
  exports.TextNode = TextNode;
12456
12654
  exports.UserError = UserError;
12655
+ exports.WrappedError = WrappedError;
12457
12656
  exports.bugs = bugs;
12458
12657
  exports.codeframe = codeframe;
12459
12658
  exports.compatibilityCheck = compatibilityCheck;
12659
+ exports.ensureError = ensureError;
12460
12660
  exports.getFormatter = getFormatter;
12461
12661
  exports.legacyRequire = legacyRequire;
12462
12662
  exports.name = name;