html-validate 8.1.0 → 8.2.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
@@ -284,7 +284,7 @@ class NestedError extends Error {
284
284
  super(message);
285
285
  Error.captureStackTrace(this, NestedError);
286
286
  this.name = NestedError.name;
287
- if (nested && nested.stack) {
287
+ if (nested === null || nested === void 0 ? void 0 : nested.stack) {
288
288
  this.stack += `\nCaused by: ${nested.stack}`;
289
289
  }
290
290
  }
@@ -1019,7 +1019,8 @@ function hasAttribute(node, attr) {
1019
1019
  * Matches attribute against value.
1020
1020
  */
1021
1021
  function matchAttribute(node, key, op, value) {
1022
- const nodeValue = (node.getAttributeValue(key) || "").toLowerCase();
1022
+ var _a;
1023
+ const nodeValue = ((_a = node.getAttributeValue(key)) !== null && _a !== void 0 ? _a : "").toLowerCase();
1023
1024
  switch (op) {
1024
1025
  case "!=":
1025
1026
  return nodeValue !== value;
@@ -1381,6 +1382,16 @@ class Attribute {
1381
1382
  get isDynamic() {
1382
1383
  return this.value instanceof DynamicValue;
1383
1384
  }
1385
+ /**
1386
+ * Test attribute value.
1387
+ *
1388
+ * @param pattern - Pattern to match value against. Can be a RegExp, literal
1389
+ * string or an array of strings (returns true if any value matches the
1390
+ * array).
1391
+ * @param dynamicMatches - If true `DynamicValue` will always match, if false
1392
+ * it never matches.
1393
+ * @returns `true` if attribute value matches pattern.
1394
+ */
1384
1395
  valueMatches(pattern, dynamicMatches = true) {
1385
1396
  if (this.value === null) {
1386
1397
  return false;
@@ -1766,7 +1777,7 @@ class DOMTokenList extends Array {
1766
1777
  this.value = value.expr;
1767
1778
  }
1768
1779
  else {
1769
- this.value = value || "";
1780
+ this.value = value !== null && value !== void 0 ? value : "";
1770
1781
  }
1771
1782
  }
1772
1783
  item(n) {
@@ -1991,7 +2002,7 @@ class IdMatcher extends Matcher {
1991
2002
  class AttrMatcher extends Matcher {
1992
2003
  constructor(attr) {
1993
2004
  super();
1994
- const [, key, op, value] = attr.match(/^(.+?)(?:([~^$*|]?=)"([^"]+?)")?$/);
2005
+ const [, key, op, value] = attr.match(/^(.+?)(?:([~^$*|]?=)"([^"]+?)")?$/); // eslint-disable-line @typescript-eslint/no-non-null-assertion -- will always match
1995
2006
  this.key = key;
1996
2007
  this.op = op;
1997
2008
  this.value = value;
@@ -2028,11 +2039,11 @@ class PseudoClassMatcher extends Matcher {
2028
2039
  }
2029
2040
  class Pattern {
2030
2041
  constructor(pattern) {
2031
- const match = pattern.match(/^([~+\->]?)((?:[*]|[^.#[:]+)?)(.*)$/);
2042
+ const match = pattern.match(/^([~+\->]?)((?:[*]|[^.#[:]+)?)(.*)$/); // eslint-disable-line @typescript-eslint/no-non-null-assertion -- will always match
2032
2043
  match.shift(); /* remove full matched string */
2033
2044
  this.selector = pattern;
2034
2045
  this.combinator = parseCombinator(match.shift(), pattern);
2035
- this.tagName = match.shift() || "*";
2046
+ this.tagName = match.shift() || "*"; // eslint-disable-line @typescript-eslint/prefer-nullish-coalescing -- empty string */
2036
2047
  this.pattern = Array.from(splitPattern(match[0]), (it) => this.createMatcher(it));
2037
2048
  }
2038
2049
  match(node, context) {
@@ -2206,6 +2217,21 @@ function isElementNode(node) {
2206
2217
  function isValidTagName(tagName) {
2207
2218
  return Boolean(tagName !== "" && tagName !== "*");
2208
2219
  }
2220
+ function createAdapter(node) {
2221
+ return {
2222
+ closest(selectors) {
2223
+ var _a;
2224
+ return (_a = node.closest(selectors)) === null || _a === void 0 ? void 0 : _a._adapter;
2225
+ },
2226
+ getAttribute(name) {
2227
+ var _a;
2228
+ return (_a = node.getAttribute(name)) === null || _a === void 0 ? void 0 : _a.value;
2229
+ },
2230
+ hasAttribute(name) {
2231
+ return node.hasAttribute(name);
2232
+ },
2233
+ };
2234
+ }
2209
2235
  /**
2210
2236
  * @public
2211
2237
  */
@@ -2214,9 +2240,9 @@ class HtmlElement extends DOMNode {
2214
2240
  const nodeType = tagName ? NodeType.ELEMENT_NODE : NodeType.DOCUMENT_NODE;
2215
2241
  super(nodeType, tagName, location);
2216
2242
  if (!isValidTagName(tagName)) {
2217
- throw new Error(`The tag name provided ('${tagName || ""}') is not a valid name`);
2243
+ throw new Error(`The tag name provided ('${tagName !== null && tagName !== void 0 ? tagName : ""}') is not a valid name`);
2218
2244
  }
2219
- this.tagName = tagName || "#document";
2245
+ this.tagName = tagName !== null && tagName !== void 0 ? tagName : "#document";
2220
2246
  this.parent = parent !== null && parent !== void 0 ? parent : null;
2221
2247
  this.attr = {};
2222
2248
  this.metaElement = meta !== null && meta !== void 0 ? meta : null;
@@ -2224,6 +2250,7 @@ class HtmlElement extends DOMNode {
2224
2250
  this.voidElement = meta ? Boolean(meta.void) : false;
2225
2251
  this.depth = 0;
2226
2252
  this.annotation = null;
2253
+ this._adapter = createAdapter(this);
2227
2254
  if (parent) {
2228
2255
  parent.childNodes.push(this);
2229
2256
  /* calculate depth in domtree */
@@ -2283,7 +2310,7 @@ class HtmlElement extends DOMNode {
2283
2310
  */
2284
2311
  get ariaLabelledby() {
2285
2312
  const attr = this.getAttribute("aria-labelledby");
2286
- if (!attr || !attr.value) {
2313
+ if (!(attr === null || attr === void 0 ? void 0 : attr.value)) {
2287
2314
  return null;
2288
2315
  }
2289
2316
  if (attr.value instanceof DynamicValue) {
@@ -2401,6 +2428,7 @@ class HtmlElement extends DOMNode {
2401
2428
  setMetaProperty(this.metaElement, key, value);
2402
2429
  }
2403
2430
  else {
2431
+ /* eslint-disable-next-line @typescript-eslint/no-dynamic-delete -- technical debt */
2404
2432
  delete this.metaElement[key];
2405
2433
  }
2406
2434
  }
@@ -2676,11 +2704,12 @@ class DOMTree {
2676
2704
  this.active = node;
2677
2705
  }
2678
2706
  popActive() {
2707
+ var _a;
2679
2708
  if (this.active.isRootElement()) {
2680
2709
  /* root element should never be popped, continue as if nothing happened */
2681
2710
  return;
2682
2711
  }
2683
- this.active = this.active.parent || this.root;
2712
+ this.active = (_a = this.active.parent) !== null && _a !== void 0 ? _a : this.root;
2684
2713
  }
2685
2714
  getActive() {
2686
2715
  return this.active;
@@ -2716,6 +2745,7 @@ const allowedKeys = ["exclude"];
2716
2745
  *
2717
2746
  * @public
2718
2747
  */
2748
+ /* eslint-disable-next-line @typescript-eslint/no-extraneous-class -- technical debt, should probably be plain functions maybe in an object */
2719
2749
  class Validator {
2720
2750
  /**
2721
2751
  * Test if element is used in a proper context.
@@ -2757,7 +2787,7 @@ class Validator {
2757
2787
  return false;
2758
2788
  }
2759
2789
  // Check if the rule has a quantifier
2760
- const [, category, quantifier] = rule.match(/^(@?.*?)([?*]?)$/);
2790
+ const [, category, quantifier] = rule.match(/^(@?.*?)([?*]?)$/); // eslint-disable-line @typescript-eslint/no-non-null-assertion -- will always match
2761
2791
  const limit = category && quantifier && parseQuantifier(quantifier);
2762
2792
  if (limit) {
2763
2793
  const siblings = children.filter((cur) => Validator.validatePermittedCategory(cur, rule, true));
@@ -2803,6 +2833,7 @@ class Validator {
2803
2833
  * In both of these cases no error should be reported. */
2804
2834
  const orderSpecified = rules.find((cur) => Validator.validatePermittedCategory(node, cur, true));
2805
2835
  if (orderSpecified) {
2836
+ /* eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- technical debt, should never happen */
2806
2837
  cb(node, prev);
2807
2838
  return false;
2808
2839
  }
@@ -2943,7 +2974,7 @@ class Validator {
2943
2974
  */
2944
2975
  /* eslint-disable-next-line complexity -- rule does not like switch */
2945
2976
  static validatePermittedCategory(node, category, defaultMatch) {
2946
- const [, rawCategory] = category.match(/^(@?.*?)([?*]?)$/);
2977
+ const [, rawCategory] = category.match(/^(@?.*?)([?*]?)$/); // eslint-disable-line @typescript-eslint/no-non-null-assertion -- will always match
2947
2978
  /* match tagName when an explicit name is given */
2948
2979
  if (!rawCategory.startsWith("@")) {
2949
2980
  return node.tagName === rawCategory;
@@ -2968,9 +2999,9 @@ class Validator {
2968
2999
  case "@interactive":
2969
3000
  return node.meta.interactive;
2970
3001
  case "@script":
2971
- return node.meta.scriptSupporting;
3002
+ return Boolean(node.meta.scriptSupporting);
2972
3003
  case "@form":
2973
- return node.meta.form;
3004
+ return Boolean(node.meta.form);
2974
3005
  default:
2975
3006
  throw new Error(`Invalid content category "${category}"`);
2976
3007
  }
@@ -3155,10 +3186,12 @@ var configurationSchema = {
3155
3186
  properties: properties
3156
3187
  };
3157
3188
 
3158
- var TRANSFORMER_API;
3159
- (function (TRANSFORMER_API) {
3160
- TRANSFORMER_API[TRANSFORMER_API["VERSION"] = 1] = "VERSION";
3161
- })(TRANSFORMER_API || (TRANSFORMER_API = {}));
3189
+ /**
3190
+ * @internal
3191
+ */
3192
+ const TRANSFORMER_API = {
3193
+ VERSION: 1,
3194
+ };
3162
3195
 
3163
3196
  /**
3164
3197
  * @public
@@ -3405,7 +3438,7 @@ function classifyNodeText(node, options = {}) {
3405
3438
  const { accessible = false, ignoreHiddenRoot = false } = options;
3406
3439
  const cacheKey = getCachekey(options);
3407
3440
  if (node.cacheExists(cacheKey)) {
3408
- return node.cacheGet(cacheKey);
3441
+ return node.cacheGet(cacheKey); // eslint-disable-line @typescript-eslint/no-non-null-assertion -- has/get combo
3409
3442
  }
3410
3443
  if (!ignoreHiddenRoot && isHTMLHidden(node)) {
3411
3444
  return node.cacheSet(cacheKey, TextClassification.EMPTY_TEXT);
@@ -3453,8 +3486,12 @@ function findTextNodes(node, options) {
3453
3486
 
3454
3487
  function hasAltText(image) {
3455
3488
  const alt = image.getAttribute("alt");
3456
- /* missing or boolean */
3457
- if (alt === null || alt.value === null) {
3489
+ /* missing attribute */
3490
+ if (!alt) {
3491
+ return false;
3492
+ }
3493
+ /* (incorrectly) set as boolean value */
3494
+ if (alt.value === null) {
3458
3495
  return false;
3459
3496
  }
3460
3497
  return alt.isDynamic || alt.value.toString() !== "";
@@ -3462,8 +3499,12 @@ function hasAltText(image) {
3462
3499
 
3463
3500
  function hasAriaLabel(node) {
3464
3501
  const label = node.getAttribute("aria-label");
3465
- /* missing or boolean */
3466
- if (label === null || label.value === null) {
3502
+ /* missing attribute */
3503
+ if (!label) {
3504
+ return false;
3505
+ }
3506
+ /* (incorrectly) set as boolean value */
3507
+ if (label.value === null) {
3467
3508
  return false;
3468
3509
  }
3469
3510
  return label.isDynamic || label.value.toString() !== "";
@@ -3544,7 +3585,7 @@ class Rule {
3544
3585
  this.options = options;
3545
3586
  this.enabled = true;
3546
3587
  this.blockers = [];
3547
- this.severity = 0;
3588
+ this.severity = Severity.DISABLED;
3548
3589
  this.name = "";
3549
3590
  }
3550
3591
  getSeverity() {
@@ -3697,13 +3738,14 @@ class Rule {
3697
3738
  }
3698
3739
  }
3699
3740
  findLocation(src) {
3741
+ var _a, _b;
3700
3742
  if (src.location) {
3701
3743
  return src.location;
3702
3744
  }
3703
- if (src.event && src.event.location) {
3745
+ if ((_a = src.event) === null || _a === void 0 ? void 0 : _a.location) {
3704
3746
  return src.event.location;
3705
3747
  }
3706
- if (src.node && src.node.location) {
3748
+ if ((_b = src.node) === null || _b === void 0 ? void 0 : _b.location) {
3707
3749
  return src.node.location;
3708
3750
  }
3709
3751
  return {};
@@ -3876,7 +3918,8 @@ class AllowedLinks extends Rule {
3876
3918
  };
3877
3919
  }
3878
3920
  documentation(context) {
3879
- const message = description[context] || "This link type is not allowed by current configuration";
3921
+ var _a;
3922
+ const message = (_a = description[context]) !== null && _a !== void 0 ? _a : "This link type is not allowed by current configuration";
3880
3923
  return {
3881
3924
  description: message,
3882
3925
  url: "https://html-validate.org/rules/allowed-links.html",
@@ -4140,7 +4183,7 @@ function isValidUsage(target, meta) {
4140
4183
  return true;
4141
4184
  }
4142
4185
  /* interactive and labelable elements are valid */
4143
- if (meta.interactive || meta.labelable) {
4186
+ if (Boolean(meta.interactive) || Boolean(meta.labelable)) {
4144
4187
  return true;
4145
4188
  }
4146
4189
  return false;
@@ -4175,7 +4218,7 @@ class AriaLabelMisuse extends Rule {
4175
4218
  }
4176
4219
  validateElement(target) {
4177
4220
  const attr = target.getAttribute("aria-label");
4178
- if (!attr || !attr.value || attr.valueMatches("", false)) {
4221
+ if (!(attr === null || attr === void 0 ? void 0 : attr.value) || attr.valueMatches("", false)) {
4179
4222
  return;
4180
4223
  }
4181
4224
  /* ignore elements without meta */
@@ -4906,7 +4949,7 @@ class AttributeAllowedValues extends Rule {
4906
4949
  const meta = node.meta;
4907
4950
  /* ignore rule if element has no meta or meta does not specify attribute
4908
4951
  * allowed values */
4909
- if (!meta || !meta.attributes)
4952
+ if (!(meta === null || meta === void 0 ? void 0 : meta.attributes))
4910
4953
  return;
4911
4954
  for (const attr of node.attributes) {
4912
4955
  if (Validator.validateAttribute(attr, meta.attributes)) {
@@ -4974,7 +5017,7 @@ class AttributeBooleanStyle extends Rule {
4974
5017
  const meta = node.meta;
4975
5018
  /* ignore rule if element has no meta or meta does not specify attribute
4976
5019
  * allowed values */
4977
- if (!meta || !meta.attributes)
5020
+ if (!(meta === null || meta === void 0 ? void 0 : meta.attributes))
4978
5021
  return;
4979
5022
  /* check all boolean attributes */
4980
5023
  for (const attr of node.attributes) {
@@ -5055,7 +5098,7 @@ class AttributeEmptyStyle extends Rule {
5055
5098
  const meta = node.meta;
5056
5099
  /* ignore rule if element has no meta or meta does not specify attribute
5057
5100
  * allowed values */
5058
- if (!meta || !meta.attributes)
5101
+ if (!(meta === null || meta === void 0 ? void 0 : meta.attributes))
5059
5102
  return;
5060
5103
  /* check all boolean attributes */
5061
5104
  for (const attr of node.attributes) {
@@ -5143,10 +5186,10 @@ class AttributeMisuse extends Rule {
5143
5186
  });
5144
5187
  }
5145
5188
  validateAttr(node, attr, meta) {
5146
- if (!meta || !meta.allowed) {
5189
+ if (!(meta === null || meta === void 0 ? void 0 : meta.allowed)) {
5147
5190
  return;
5148
5191
  }
5149
- const details = meta.allowed(node, attr);
5192
+ const details = meta.allowed(node._adapter, attr.value);
5150
5193
  if (details) {
5151
5194
  this.report({
5152
5195
  node,
@@ -5817,7 +5860,7 @@ class ElementPermittedOccurrences extends Rule {
5817
5860
  this.on("dom:ready", (event) => {
5818
5861
  const doc = event.document;
5819
5862
  doc.visitDepthFirst((node) => {
5820
- if (!node || !node.meta) {
5863
+ if (!(node === null || node === void 0 ? void 0 : node.meta)) {
5821
5864
  return;
5822
5865
  }
5823
5866
  const rules = node.meta.permittedContent;
@@ -6022,7 +6065,7 @@ class ElementRequiredAttributes extends Rule {
6022
6065
  const node = event.previous;
6023
6066
  const meta = node.meta;
6024
6067
  /* handle missing metadata and missing attributes */
6025
- if (!meta || !meta.attributes) {
6068
+ if (!(meta === null || meta === void 0 ? void 0 : meta.attributes)) {
6026
6069
  return;
6027
6070
  }
6028
6071
  for (const [key, attr] of Object.entries(meta.attributes)) {
@@ -6334,7 +6377,7 @@ class FormDupName extends Rule {
6334
6377
  const meta = this.getMetaFor(tagName);
6335
6378
  /* istanbul ignore if: the earlier check for getTagsWithProperty ensures
6336
6379
  * these will actually be set so this is just an untestable fallback */
6337
- if (!meta || !meta.formAssociated) {
6380
+ if (!(meta === null || meta === void 0 ? void 0 : meta.formAssociated)) {
6338
6381
  return false;
6339
6382
  }
6340
6383
  return meta.formAssociated.listed;
@@ -6582,7 +6625,7 @@ class IdPattern extends Rule {
6582
6625
  }
6583
6626
  setup() {
6584
6627
  this.on("attr", (event) => {
6585
- var _a;
6628
+ var _a, _b;
6586
6629
  if (event.key.toLowerCase() !== "id") {
6587
6630
  return;
6588
6631
  }
@@ -6590,8 +6633,8 @@ class IdPattern extends Rule {
6590
6633
  if (event.value instanceof DynamicValue) {
6591
6634
  return;
6592
6635
  }
6593
- if (!event.value || !event.value.match(this.pattern)) {
6594
- const value = (_a = event.value) !== null && _a !== void 0 ? _a : "";
6636
+ if (!((_a = event.value) === null || _a === void 0 ? void 0 : _a.match(this.pattern))) {
6637
+ const value = (_b = event.value) !== null && _b !== void 0 ? _b : "";
6595
6638
  const pattern = this.pattern.toString();
6596
6639
  const message = `ID "${value}" does not match required pattern "${pattern}"`;
6597
6640
  this.report(event.target, message, event.valueLocation);
@@ -7019,7 +7062,7 @@ class MetaRefresh extends Rule {
7019
7062
  }
7020
7063
  /* ensure content attribute is set */
7021
7064
  const content = target.getAttribute("content");
7022
- if (!content || !content.value || content.isDynamic) {
7065
+ if (!(content === null || content === void 0 ? void 0 : content.value) || content.isDynamic) {
7023
7066
  return;
7024
7067
  }
7025
7068
  /* ensure content attribute is valid */
@@ -7298,7 +7341,7 @@ class NoDeprecatedAttr extends Rule {
7298
7341
  if (meta === null) {
7299
7342
  return;
7300
7343
  }
7301
- const metaAttribute = meta.attributes && meta.attributes[attr];
7344
+ const metaAttribute = meta.attributes[attr];
7302
7345
  if (!metaAttribute) {
7303
7346
  return;
7304
7347
  }
@@ -7378,7 +7421,7 @@ class NoDupID extends Rule {
7378
7421
  for (const el of relevant) {
7379
7422
  const attr = el.getAttribute("id");
7380
7423
  /* istanbul ignore next: this has already been tested in isRelevant once but for type-safety it is checked again */
7381
- if (!attr || !attr.value) {
7424
+ if (!(attr === null || attr === void 0 ? void 0 : attr.value)) {
7382
7425
  continue;
7383
7426
  }
7384
7427
  const id = attr.value.toString();
@@ -7512,11 +7555,12 @@ class NoInlineStyle extends Rule {
7512
7555
  });
7513
7556
  }
7514
7557
  isRelevant(event) {
7558
+ var _a;
7515
7559
  if (event.key !== "style") {
7516
7560
  return false;
7517
7561
  }
7518
7562
  const { include, exclude } = this.options;
7519
- const key = event.originalAttribute || event.key;
7563
+ const key = (_a = event.originalAttribute) !== null && _a !== void 0 ? _a : event.key;
7520
7564
  /* ignore attributes not present in "include" */
7521
7565
  if (include && !include.includes(key)) {
7522
7566
  return false;
@@ -7710,7 +7754,8 @@ class NoRawCharacters extends Rule {
7710
7754
  if (event.quote) {
7711
7755
  return;
7712
7756
  }
7713
- this.findRawChars(event.target, event.value.toString(), event.valueLocation, unquotedAttrRegexp);
7757
+ this.findRawChars(event.target, event.value.toString(), event.valueLocation, // eslint-disable-line @typescript-eslint/no-non-null-assertion -- technical debt, valueLocation is always set if a value is provided
7758
+ unquotedAttrRegexp);
7714
7759
  });
7715
7760
  }
7716
7761
  /**
@@ -7924,7 +7969,7 @@ class NoSelfClosing extends Rule {
7924
7969
  function isRelevant(node, options) {
7925
7970
  /* tags in XML namespaces are relevant only if ignoreXml is false, in which
7926
7971
  * case assume all xml elements must not be self-closed */
7927
- if (node.tagName && node.tagName.match(xmlns)) {
7972
+ if (node.tagName.match(xmlns)) {
7928
7973
  return !options.ignoreXML;
7929
7974
  }
7930
7975
  /* nodes with missing metadata is assumed relevant */
@@ -8130,16 +8175,12 @@ class PreferButton extends Rule {
8130
8175
  };
8131
8176
  }
8132
8177
  documentation(context) {
8133
- const doc = {
8134
- description: `Prefer to use the generic \`<button>\` element instead of \`<input>\`.`,
8178
+ const src = `<input type="${context.type}">`;
8179
+ const dst = replacement[context.type] || `<button>`;
8180
+ return {
8181
+ description: `Prefer to use \`${dst}\` instead of \`"${src}\`.`,
8135
8182
  url: "https://html-validate.org/rules/prefer-button.html",
8136
8183
  };
8137
- if (context) {
8138
- const src = `<input type="${context.type}">`;
8139
- const dst = replacement[context.type] || `<button>`;
8140
- doc.description = `Prefer to use \`${dst}\` instead of \`"${src}\`.`;
8141
- }
8142
- return doc;
8143
8184
  }
8144
8185
  setup() {
8145
8186
  this.on("attr", (event) => {
@@ -8238,14 +8279,10 @@ class PreferNativeElement extends Rule {
8238
8279
  };
8239
8280
  }
8240
8281
  documentation(context) {
8241
- const doc = {
8242
- description: `Instead of using WAI-ARIA roles prefer to use the native HTML elements.`,
8282
+ return {
8283
+ description: `Instead of using the WAI-ARIA role "${context.role}" prefer to use the native <${context.replacement}> element.`,
8243
8284
  url: "https://html-validate.org/rules/prefer-native-element.html",
8244
8285
  };
8245
- if (context) {
8246
- doc.description = `Instead of using the WAI-ARIA role "${context.role}" prefer to use the native <${context.replacement}> element.`;
8247
- }
8248
- return doc;
8249
8286
  }
8250
8287
  setup() {
8251
8288
  const { mapping } = this.options;
@@ -8286,7 +8323,7 @@ class PreferNativeElement extends Rule {
8286
8323
  }
8287
8324
  getLocation(event) {
8288
8325
  const begin = event.location;
8289
- const end = event.valueLocation;
8326
+ const end = event.valueLocation; // eslint-disable-line @typescript-eslint/no-non-null-assertion -- technical debt, valueLocation will always be set when a value is provided
8290
8327
  const quote = event.quote ? 1 : 0;
8291
8328
  const size = end.offset + end.size - begin.offset + quote;
8292
8329
  return {
@@ -8360,7 +8397,7 @@ class RequireCSPNonce extends Rule {
8360
8397
  const { tags } = this.options;
8361
8398
  const node = event.previous;
8362
8399
  /* ignore other tags */
8363
- if (!node || !tags.includes(node.tagName)) {
8400
+ if (!tags.includes(node.tagName)) {
8364
8401
  return;
8365
8402
  }
8366
8403
  /* ignore if nonce is set to non-empty value (or dynamic) */
@@ -8452,7 +8489,10 @@ class RequireSri extends Rule {
8452
8489
  }
8453
8490
  needSri(node) {
8454
8491
  const attr = this.elementSourceAttr(node);
8455
- if (!attr || attr.value === null || attr.value === "" || attr.isDynamic) {
8492
+ if (!attr) {
8493
+ return false;
8494
+ }
8495
+ if (attr.value === null || attr.value === "" || attr.isDynamic) {
8456
8496
  return false;
8457
8497
  }
8458
8498
  const url = attr.value.toString();
@@ -8509,7 +8549,7 @@ class ScriptType extends Rule {
8509
8549
  setup() {
8510
8550
  this.on("tag:end", (event) => {
8511
8551
  const node = event.previous;
8512
- if (!node || node.tagName !== "script") {
8552
+ if (node.tagName !== "script") {
8513
8553
  return;
8514
8554
  }
8515
8555
  const attr = node.getAttribute("type");
@@ -8790,18 +8830,16 @@ class TextContent extends Rule {
8790
8830
  description: `The textual content for this element is not valid.`,
8791
8831
  url: "https://html-validate.org/rules/text-content.html",
8792
8832
  };
8793
- if (context === null || context === void 0 ? void 0 : context.textContent) {
8794
- switch (context.textContent) {
8795
- case TextContent$1.NONE:
8796
- doc.description = `The \`<${context.tagName}>\` element must not have textual content.`;
8797
- break;
8798
- case TextContent$1.REQUIRED:
8799
- doc.description = `The \`<${context.tagName}>\` element must have textual content.`;
8800
- break;
8801
- case TextContent$1.ACCESSIBLE:
8802
- doc.description = `The \`<${context.tagName}>\` element must have accessible text.`;
8803
- break;
8804
- }
8833
+ switch (context.textContent) {
8834
+ case TextContent$1.NONE:
8835
+ doc.description = `The \`<${context.tagName}>\` element must not have textual content.`;
8836
+ break;
8837
+ case TextContent$1.REQUIRED:
8838
+ doc.description = `The \`<${context.tagName}>\` element must have textual content.`;
8839
+ break;
8840
+ case TextContent$1.ACCESSIBLE:
8841
+ doc.description = `The \`<${context.tagName}>\` element must have accessible text.`;
8842
+ break;
8805
8843
  }
8806
8844
  return doc;
8807
8845
  }
@@ -9162,20 +9200,16 @@ class VoidStyle extends Rule {
9162
9200
  };
9163
9201
  }
9164
9202
  documentation(context) {
9165
- const doc = {
9166
- description: "The current configuration requires a specific style for ending void elements.",
9203
+ const [desc, end] = styleDescription(context.style);
9204
+ return {
9205
+ description: `The current configuration requires void elements to ${desc}, use <${context.tagName}${end}> instead.`,
9167
9206
  url: "https://html-validate.org/rules/void-style.html",
9168
9207
  };
9169
- if (context) {
9170
- const [desc, end] = styleDescription(context.style);
9171
- doc.description = `The current configuration requires void elements to ${desc}, use <${context.tagName}${end}> instead.`;
9172
- }
9173
- return doc;
9174
9208
  }
9175
9209
  setup() {
9176
9210
  this.on("tag:end", (event) => {
9177
9211
  const active = event.previous; // The current active element (that is, the current element on the stack)
9178
- if (active && active.meta) {
9212
+ if (active.meta) {
9179
9213
  this.validateActive(active);
9180
9214
  }
9181
9215
  });
@@ -9412,7 +9446,8 @@ class H37 extends Rule {
9412
9446
  return;
9413
9447
  }
9414
9448
  /* validate plain alt-attribute */
9415
- if (node.getAttributeValue("alt") || (node.hasAttribute("alt") && this.options.allowEmpty)) {
9449
+ if (Boolean(node.getAttributeValue("alt")) ||
9450
+ Boolean(node.hasAttribute("alt") && this.options.allowEmpty)) {
9416
9451
  return;
9417
9452
  }
9418
9453
  /* validate if any non-empty alias is present */
@@ -9845,7 +9880,7 @@ class ResolvedConfig {
9845
9880
  * @returns A list of transformed sources ready for validation.
9846
9881
  */
9847
9882
  transformSource(source, filename) {
9848
- const transformer = this.findTransformer(filename || source.filename);
9883
+ const transformer = this.findTransformer(filename !== null && filename !== void 0 ? filename : source.filename);
9849
9884
  const context = {
9850
9885
  hasChain: (filename) => {
9851
9886
  return !!this.findTransformer(filename);
@@ -9857,9 +9892,10 @@ class ResolvedConfig {
9857
9892
  if (transformer) {
9858
9893
  try {
9859
9894
  return Array.from(transformer.fn.call(context, source), (cur) => {
9895
+ var _a;
9860
9896
  /* keep track of which transformers that has been run on this source
9861
9897
  * by appending this entry to the transformedBy array */
9862
- cur.transformedBy = cur.transformedBy || [];
9898
+ (_a = cur.transformedBy) !== null && _a !== void 0 ? _a : (cur.transformedBy = []);
9863
9899
  cur.transformedBy.push(transformer.name);
9864
9900
  return cur;
9865
9901
  });
@@ -10031,7 +10067,7 @@ function mergeInternal(base, rhs) {
10031
10067
  }
10032
10068
  /* root property is merged with boolean "or" since it should always be truthy
10033
10069
  * if any config has it set. */
10034
- const root = base.root || rhs.root;
10070
+ const root = Boolean(base.root) || Boolean(rhs.root);
10035
10071
  if (root) {
10036
10072
  dst.root = root;
10037
10073
  }
@@ -10118,7 +10154,7 @@ class Config {
10118
10154
  * @internal
10119
10155
  */
10120
10156
  constructor(resolvers, options) {
10121
- var _a;
10157
+ var _a, _b;
10122
10158
  this.transformers = [];
10123
10159
  const initial = {
10124
10160
  extends: [],
@@ -10131,18 +10167,18 @@ class Config {
10131
10167
  this.initialized = false;
10132
10168
  this.resolvers = toArray(resolvers);
10133
10169
  /* load plugins */
10134
- this.plugins = this.loadPlugins(this.config.plugins || []);
10170
+ this.plugins = this.loadPlugins((_a = this.config.plugins) !== null && _a !== void 0 ? _a : []);
10135
10171
  this.configurations = this.loadConfigurations(this.plugins);
10136
10172
  this.extendMeta(this.plugins);
10137
10173
  /* process extended configs */
10138
- this.config = this.extendConfig((_a = this.config.extends) !== null && _a !== void 0 ? _a : []);
10174
+ this.config = this.extendConfig((_b = this.config.extends) !== null && _b !== void 0 ? _b : []);
10139
10175
  /* reset extends as we already processed them, this prevents the next config
10140
10176
  * from reapplying config from extended config as well as duplicate entries
10141
10177
  * when merging arrays */
10142
10178
  this.config.extends = [];
10143
10179
  /* rules explicitly set by passed options should have precedence over any
10144
10180
  * extended rules, not the other way around. */
10145
- if (options && options.rules) {
10181
+ if (options === null || options === void 0 ? void 0 : options.rules) {
10146
10182
  this.config = mergeInternal(this.config, { rules: options.rules });
10147
10183
  }
10148
10184
  }
@@ -10155,11 +10191,12 @@ class Config {
10155
10191
  * @public
10156
10192
  */
10157
10193
  init() {
10194
+ var _a;
10158
10195
  if (this.initialized) {
10159
10196
  return;
10160
10197
  }
10161
10198
  /* precompile transform patterns */
10162
- this.transformers = this.precompileTransformers(this.config.transform || {});
10199
+ this.transformers = this.precompileTransformers((_a = this.config.transform) !== null && _a !== void 0 ? _a : {});
10163
10200
  this.initialized = true;
10164
10201
  }
10165
10202
  /**
@@ -10186,7 +10223,7 @@ class Config {
10186
10223
  for (const entry of entries) {
10187
10224
  let extended;
10188
10225
  if (this.configurations.has(entry)) {
10189
- extended = this.configurations.get(entry);
10226
+ extended = this.configurations.get(entry); // eslint-disable-line @typescript-eslint/no-non-null-assertion -- map has/get combo
10190
10227
  }
10191
10228
  else {
10192
10229
  extended = Config.fromFile(this.resolvers, entry).config;
@@ -10201,12 +10238,13 @@ class Config {
10201
10238
  * @internal
10202
10239
  */
10203
10240
  getMetaTable() {
10241
+ var _a;
10204
10242
  /* use cached table if it exists */
10205
10243
  if (this.metaTable) {
10206
10244
  return this.metaTable;
10207
10245
  }
10208
10246
  const metaTable = new MetaTable();
10209
- const source = this.config.elements || ["html5"];
10247
+ const source = (_a = this.config.elements) !== null && _a !== void 0 ? _a : ["html5"];
10210
10248
  /* extend validation schema from plugins */
10211
10249
  for (const plugin of this.getPlugins()) {
10212
10250
  if (plugin.elementSchema) {
@@ -10304,6 +10342,7 @@ class Config {
10304
10342
  });
10305
10343
  }
10306
10344
  loadConfigurations(plugins) {
10345
+ var _a;
10307
10346
  const configs = new Map();
10308
10347
  /* builtin presets */
10309
10348
  for (const [name, config] of Object.entries(Presets)) {
@@ -10312,7 +10351,7 @@ class Config {
10312
10351
  }
10313
10352
  /* presets from plugins */
10314
10353
  for (const plugin of plugins) {
10315
- for (const [name, config] of Object.entries(plugin.configs || {})) {
10354
+ for (const [name, config] of Object.entries((_a = plugin.configs) !== null && _a !== void 0 ? _a : {})) {
10316
10355
  if (!config)
10317
10356
  continue;
10318
10357
  Config.validate(config, name);
@@ -10677,10 +10716,11 @@ class Parser {
10677
10716
  * stack when is allowed to omit.
10678
10717
  */
10679
10718
  closeOptional(token) {
10719
+ var _a;
10680
10720
  /* if the element doesn't have metadata it cannot have optional end
10681
10721
  * tags. Period. */
10682
10722
  const active = this.dom.getActive();
10683
- if (!(active.meta && active.meta.implicitClosed)) {
10723
+ if (!((_a = active.meta) === null || _a === void 0 ? void 0 : _a.implicitClosed)) {
10684
10724
  return false;
10685
10725
  }
10686
10726
  const tagName = token.data[2];
@@ -10832,9 +10872,10 @@ class Parser {
10832
10872
  }
10833
10873
  }
10834
10874
  processElement(node, source) {
10875
+ var _a;
10835
10876
  /* enable cache on node now that it is fully constructed */
10836
10877
  node.cacheEnable();
10837
- if (source.hooks && source.hooks.processElement) {
10878
+ if ((_a = source.hooks) === null || _a === void 0 ? void 0 : _a.processElement) {
10838
10879
  const processElement = source.hooks.processElement;
10839
10880
  const metaTable = this.metaTable;
10840
10881
  const context = {
@@ -10901,6 +10942,7 @@ class Parser {
10901
10942
  * @internal
10902
10943
  */
10903
10944
  consumeAttribute(source, node, token, next) {
10945
+ var _a;
10904
10946
  const keyLocation = this.getAttributeKeyLocation(token);
10905
10947
  const valueLocation = this.getAttributeValueLocation(next);
10906
10948
  const location = this.getAttributeLocation(token, next);
@@ -10919,7 +10961,7 @@ class Parser {
10919
10961
  * data right away but a transformer may override it to allow aliasing
10920
10962
  * attributes, e.g ng-attr-foo or v-bind:foo */
10921
10963
  let processAttribute = (attr) => [attr];
10922
- if (source.hooks && source.hooks.processAttribute) {
10964
+ if ((_a = source.hooks) === null || _a === void 0 ? void 0 : _a.processAttribute) {
10923
10965
  processAttribute = source.hooks.processAttribute;
10924
10966
  }
10925
10967
  /* handle deprecated callbacks */
@@ -11269,14 +11311,15 @@ class Reporter {
11269
11311
  const report = {
11270
11312
  valid: this.isValid(),
11271
11313
  results: Object.keys(this.result).map((filePath) => {
11314
+ var _a;
11272
11315
  const messages = Array.from(this.result[filePath], freeze).sort(messageSort);
11273
- const source = (sources || []).find((source) => { var _a; return filePath === ((_a = source.filename) !== null && _a !== void 0 ? _a : ""); });
11316
+ const source = (sources !== null && sources !== void 0 ? sources : []).find((source) => { var _a; return filePath === ((_a = source.filename) !== null && _a !== void 0 ? _a : ""); });
11274
11317
  return {
11275
11318
  filePath,
11276
11319
  messages,
11277
11320
  errorCount: countErrors(messages),
11278
11321
  warningCount: countWarnings(messages),
11279
- source: source ? source.originalData || source.data : null,
11322
+ source: source ? (_a = source.originalData) !== null && _a !== void 0 ? _a : source.data : null,
11280
11323
  };
11281
11324
  }),
11282
11325
  errorCount: 0,
@@ -11294,10 +11337,10 @@ class Reporter {
11294
11337
  }
11295
11338
  }
11296
11339
  function countErrors(messages) {
11297
- return messages.filter((m) => m.severity === Severity.ERROR).length;
11340
+ return messages.filter((m) => m.severity === Number(Severity.ERROR)).length;
11298
11341
  }
11299
11342
  function countWarnings(messages) {
11300
- return messages.filter((m) => m.severity === Severity.WARN).length;
11343
+ return messages.filter((m) => m.severity === Number(Severity.WARN)).length;
11301
11344
  }
11302
11345
  function sumErrors(results) {
11303
11346
  return results.reduce((sum, result) => {
@@ -11642,9 +11685,10 @@ class Engine {
11642
11685
  * between rule name and its constructor.
11643
11686
  */
11644
11687
  initRules(config) {
11688
+ var _a;
11645
11689
  const availableRules = {};
11646
11690
  for (const plugin of config.getPlugins()) {
11647
- for (const [name, rule] of Object.entries(plugin.rules || {})) {
11691
+ for (const [name, rule] of Object.entries((_a = plugin.rules) !== null && _a !== void 0 ? _a : {})) {
11648
11692
  if (!rule)
11649
11693
  continue;
11650
11694
  availableRules[name] = rule;
@@ -11747,7 +11791,7 @@ class StaticConfigLoader extends ConfigLoader {
11747
11791
  }
11748
11792
  }
11749
11793
  getConfigFor(_handle, configOverride) {
11750
- const override = this.loadFromObject(configOverride || {});
11794
+ const override = this.loadFromObject(configOverride !== null && configOverride !== void 0 ? configOverride : {});
11751
11795
  if (override.isRootFound()) {
11752
11796
  override.init();
11753
11797
  return override.resolve();
@@ -11791,6 +11835,7 @@ class HtmlValidate {
11791
11835
  const [loader, config] = arg instanceof ConfigLoader ? [arg, undefined] : [undefined, arg];
11792
11836
  this.configLoader = loader !== null && loader !== void 0 ? loader : new StaticConfigLoader(config);
11793
11837
  }
11838
+ /* eslint-enable @typescript-eslint/unified-signatures */
11794
11839
  validateString(str, arg1, arg2, arg3) {
11795
11840
  const filename = typeof arg1 === "string" ? arg1 : "inline";
11796
11841
  const options = isConfigData(arg1) ? arg1 : isConfigData(arg2) ? arg2 : undefined;
@@ -11805,6 +11850,7 @@ class HtmlValidate {
11805
11850
  };
11806
11851
  return this.validateSource(source, options);
11807
11852
  }
11853
+ /* eslint-enable @typescript-eslint/unified-signatures */
11808
11854
  validateStringSync(str, arg1, arg2, arg3) {
11809
11855
  const filename = typeof arg1 === "string" ? arg1 : "inline";
11810
11856
  const options = isConfigData(arg1) ? arg1 : isConfigData(arg2) ? arg2 : undefined;
@@ -12076,7 +12122,7 @@ class HtmlValidate {
12076
12122
  * contextual details and suggestions.
12077
12123
  */
12078
12124
  async getRuleDocumentation(ruleId, config = null, context = null) {
12079
- const c = config || this.getConfigFor("inline");
12125
+ const c = config !== null && config !== void 0 ? config : this.getConfigFor("inline");
12080
12126
  const engine = new Engine(await c, Parser);
12081
12127
  return engine.getRuleDocumentation({ ruleId, context });
12082
12128
  }
@@ -12105,7 +12151,7 @@ class HtmlValidate {
12105
12151
  * contextual details and suggestions.
12106
12152
  */
12107
12153
  getRuleDocumentationSync(ruleId, config = null, context = null) {
12108
- const c = config || this.getConfigForSync("inline");
12154
+ const c = config !== null && config !== void 0 ? config : this.getConfigForSync("inline");
12109
12155
  const engine = new Engine(c, Parser);
12110
12156
  return engine.getRuleDocumentation({ ruleId, context });
12111
12157
  }
@@ -12161,7 +12207,7 @@ class HtmlValidate {
12161
12207
  /** @public */
12162
12208
  const name = "html-validate";
12163
12209
  /** @public */
12164
- const version = "8.1.0";
12210
+ const version = "8.2.0";
12165
12211
  /** @public */
12166
12212
  const bugs = "https://gitlab.com/html-validate/html-validate/issues/new";
12167
12213
 
@@ -12459,5 +12505,5 @@ function compatibilityCheckImpl(name, declared, options) {
12459
12505
  return false;
12460
12506
  }
12461
12507
 
12462
- export { Attribute as A, codeframe as B, Config as C, DynamicValue as D, EventHandler as E, name as F, bugs as G, HtmlValidate as H, MetaTable as M, NodeClosed as N, Presets as P, ResolvedConfig as R, Severity as S, TextNode as T, UserError as U, Validator as V, WrappedError as W, ConfigError as a, ConfigLoader as b, StaticConfigLoader as c, DOMTokenList as d, ensureError as e, HtmlElement as f, getFormatter as g, DOMNode as h, DOMTree as i, NodeType as j, SchemaValidationError as k, NestedError as l, TextContent$1 as m, MetaCopyableProperty as n, Rule as o, TextClassification as p, classifyNodeText as q, keywordPatternMatcher as r, staticResolver as s, sliceLocation as t, Reporter as u, version as v, definePlugin as w, Parser as x, ruleExists as y, compatibilityCheckImpl as z };
12508
+ export { Attribute as A, codeframe as B, Config as C, DOMNode as D, EventHandler as E, name as F, bugs as G, HtmlValidate as H, MetaCopyableProperty as M, NodeClosed as N, Presets as P, ResolvedConfig as R, Severity as S, TextNode as T, UserError as U, Validator as V, WrappedError as W, ConfigError as a, ConfigLoader as b, StaticConfigLoader as c, DOMTokenList as d, ensureError as e, DOMTree as f, getFormatter as g, DynamicValue as h, HtmlElement as i, NodeType as j, NestedError as k, SchemaValidationError as l, MetaTable as m, TextContent$1 as n, Rule as o, TextClassification as p, classifyNodeText as q, keywordPatternMatcher as r, staticResolver as s, sliceLocation as t, Reporter as u, version as v, definePlugin as w, Parser as x, ruleExists as y, compatibilityCheckImpl as z };
12463
12509
  //# sourceMappingURL=core.js.map