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/cjs/core.js CHANGED
@@ -295,7 +295,7 @@ class NestedError extends Error {
295
295
  super(message);
296
296
  Error.captureStackTrace(this, NestedError);
297
297
  this.name = NestedError.name;
298
- if (nested && nested.stack) {
298
+ if (nested === null || nested === void 0 ? void 0 : nested.stack) {
299
299
  this.stack += `\nCaused by: ${nested.stack}`;
300
300
  }
301
301
  }
@@ -1030,7 +1030,8 @@ function hasAttribute(node, attr) {
1030
1030
  * Matches attribute against value.
1031
1031
  */
1032
1032
  function matchAttribute(node, key, op, value) {
1033
- const nodeValue = (node.getAttributeValue(key) || "").toLowerCase();
1033
+ var _a;
1034
+ const nodeValue = ((_a = node.getAttributeValue(key)) !== null && _a !== void 0 ? _a : "").toLowerCase();
1034
1035
  switch (op) {
1035
1036
  case "!=":
1036
1037
  return nodeValue !== value;
@@ -1392,6 +1393,16 @@ class Attribute {
1392
1393
  get isDynamic() {
1393
1394
  return this.value instanceof DynamicValue;
1394
1395
  }
1396
+ /**
1397
+ * Test attribute value.
1398
+ *
1399
+ * @param pattern - Pattern to match value against. Can be a RegExp, literal
1400
+ * string or an array of strings (returns true if any value matches the
1401
+ * array).
1402
+ * @param dynamicMatches - If true `DynamicValue` will always match, if false
1403
+ * it never matches.
1404
+ * @returns `true` if attribute value matches pattern.
1405
+ */
1395
1406
  valueMatches(pattern, dynamicMatches = true) {
1396
1407
  if (this.value === null) {
1397
1408
  return false;
@@ -1777,7 +1788,7 @@ class DOMTokenList extends Array {
1777
1788
  this.value = value.expr;
1778
1789
  }
1779
1790
  else {
1780
- this.value = value || "";
1791
+ this.value = value !== null && value !== void 0 ? value : "";
1781
1792
  }
1782
1793
  }
1783
1794
  item(n) {
@@ -2002,7 +2013,7 @@ class IdMatcher extends Matcher {
2002
2013
  class AttrMatcher extends Matcher {
2003
2014
  constructor(attr) {
2004
2015
  super();
2005
- const [, key, op, value] = attr.match(/^(.+?)(?:([~^$*|]?=)"([^"]+?)")?$/);
2016
+ const [, key, op, value] = attr.match(/^(.+?)(?:([~^$*|]?=)"([^"]+?)")?$/); // eslint-disable-line @typescript-eslint/no-non-null-assertion -- will always match
2006
2017
  this.key = key;
2007
2018
  this.op = op;
2008
2019
  this.value = value;
@@ -2039,11 +2050,11 @@ class PseudoClassMatcher extends Matcher {
2039
2050
  }
2040
2051
  class Pattern {
2041
2052
  constructor(pattern) {
2042
- const match = pattern.match(/^([~+\->]?)((?:[*]|[^.#[:]+)?)(.*)$/);
2053
+ const match = pattern.match(/^([~+\->]?)((?:[*]|[^.#[:]+)?)(.*)$/); // eslint-disable-line @typescript-eslint/no-non-null-assertion -- will always match
2043
2054
  match.shift(); /* remove full matched string */
2044
2055
  this.selector = pattern;
2045
2056
  this.combinator = parseCombinator(match.shift(), pattern);
2046
- this.tagName = match.shift() || "*";
2057
+ this.tagName = match.shift() || "*"; // eslint-disable-line @typescript-eslint/prefer-nullish-coalescing -- empty string */
2047
2058
  this.pattern = Array.from(splitPattern(match[0]), (it) => this.createMatcher(it));
2048
2059
  }
2049
2060
  match(node, context) {
@@ -2217,6 +2228,21 @@ function isElementNode(node) {
2217
2228
  function isValidTagName(tagName) {
2218
2229
  return Boolean(tagName !== "" && tagName !== "*");
2219
2230
  }
2231
+ function createAdapter(node) {
2232
+ return {
2233
+ closest(selectors) {
2234
+ var _a;
2235
+ return (_a = node.closest(selectors)) === null || _a === void 0 ? void 0 : _a._adapter;
2236
+ },
2237
+ getAttribute(name) {
2238
+ var _a;
2239
+ return (_a = node.getAttribute(name)) === null || _a === void 0 ? void 0 : _a.value;
2240
+ },
2241
+ hasAttribute(name) {
2242
+ return node.hasAttribute(name);
2243
+ },
2244
+ };
2245
+ }
2220
2246
  /**
2221
2247
  * @public
2222
2248
  */
@@ -2225,9 +2251,9 @@ class HtmlElement extends DOMNode {
2225
2251
  const nodeType = tagName ? exports.NodeType.ELEMENT_NODE : exports.NodeType.DOCUMENT_NODE;
2226
2252
  super(nodeType, tagName, location);
2227
2253
  if (!isValidTagName(tagName)) {
2228
- throw new Error(`The tag name provided ('${tagName || ""}') is not a valid name`);
2254
+ throw new Error(`The tag name provided ('${tagName !== null && tagName !== void 0 ? tagName : ""}') is not a valid name`);
2229
2255
  }
2230
- this.tagName = tagName || "#document";
2256
+ this.tagName = tagName !== null && tagName !== void 0 ? tagName : "#document";
2231
2257
  this.parent = parent !== null && parent !== void 0 ? parent : null;
2232
2258
  this.attr = {};
2233
2259
  this.metaElement = meta !== null && meta !== void 0 ? meta : null;
@@ -2235,6 +2261,7 @@ class HtmlElement extends DOMNode {
2235
2261
  this.voidElement = meta ? Boolean(meta.void) : false;
2236
2262
  this.depth = 0;
2237
2263
  this.annotation = null;
2264
+ this._adapter = createAdapter(this);
2238
2265
  if (parent) {
2239
2266
  parent.childNodes.push(this);
2240
2267
  /* calculate depth in domtree */
@@ -2294,7 +2321,7 @@ class HtmlElement extends DOMNode {
2294
2321
  */
2295
2322
  get ariaLabelledby() {
2296
2323
  const attr = this.getAttribute("aria-labelledby");
2297
- if (!attr || !attr.value) {
2324
+ if (!(attr === null || attr === void 0 ? void 0 : attr.value)) {
2298
2325
  return null;
2299
2326
  }
2300
2327
  if (attr.value instanceof DynamicValue) {
@@ -2412,6 +2439,7 @@ class HtmlElement extends DOMNode {
2412
2439
  setMetaProperty(this.metaElement, key, value);
2413
2440
  }
2414
2441
  else {
2442
+ /* eslint-disable-next-line @typescript-eslint/no-dynamic-delete -- technical debt */
2415
2443
  delete this.metaElement[key];
2416
2444
  }
2417
2445
  }
@@ -2687,11 +2715,12 @@ class DOMTree {
2687
2715
  this.active = node;
2688
2716
  }
2689
2717
  popActive() {
2718
+ var _a;
2690
2719
  if (this.active.isRootElement()) {
2691
2720
  /* root element should never be popped, continue as if nothing happened */
2692
2721
  return;
2693
2722
  }
2694
- this.active = this.active.parent || this.root;
2723
+ this.active = (_a = this.active.parent) !== null && _a !== void 0 ? _a : this.root;
2695
2724
  }
2696
2725
  getActive() {
2697
2726
  return this.active;
@@ -2727,6 +2756,7 @@ const allowedKeys = ["exclude"];
2727
2756
  *
2728
2757
  * @public
2729
2758
  */
2759
+ /* eslint-disable-next-line @typescript-eslint/no-extraneous-class -- technical debt, should probably be plain functions maybe in an object */
2730
2760
  class Validator {
2731
2761
  /**
2732
2762
  * Test if element is used in a proper context.
@@ -2768,7 +2798,7 @@ class Validator {
2768
2798
  return false;
2769
2799
  }
2770
2800
  // Check if the rule has a quantifier
2771
- const [, category, quantifier] = rule.match(/^(@?.*?)([?*]?)$/);
2801
+ const [, category, quantifier] = rule.match(/^(@?.*?)([?*]?)$/); // eslint-disable-line @typescript-eslint/no-non-null-assertion -- will always match
2772
2802
  const limit = category && quantifier && parseQuantifier(quantifier);
2773
2803
  if (limit) {
2774
2804
  const siblings = children.filter((cur) => Validator.validatePermittedCategory(cur, rule, true));
@@ -2814,6 +2844,7 @@ class Validator {
2814
2844
  * In both of these cases no error should be reported. */
2815
2845
  const orderSpecified = rules.find((cur) => Validator.validatePermittedCategory(node, cur, true));
2816
2846
  if (orderSpecified) {
2847
+ /* eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- technical debt, should never happen */
2817
2848
  cb(node, prev);
2818
2849
  return false;
2819
2850
  }
@@ -2954,7 +2985,7 @@ class Validator {
2954
2985
  */
2955
2986
  /* eslint-disable-next-line complexity -- rule does not like switch */
2956
2987
  static validatePermittedCategory(node, category, defaultMatch) {
2957
- const [, rawCategory] = category.match(/^(@?.*?)([?*]?)$/);
2988
+ const [, rawCategory] = category.match(/^(@?.*?)([?*]?)$/); // eslint-disable-line @typescript-eslint/no-non-null-assertion -- will always match
2958
2989
  /* match tagName when an explicit name is given */
2959
2990
  if (!rawCategory.startsWith("@")) {
2960
2991
  return node.tagName === rawCategory;
@@ -2979,9 +3010,9 @@ class Validator {
2979
3010
  case "@interactive":
2980
3011
  return node.meta.interactive;
2981
3012
  case "@script":
2982
- return node.meta.scriptSupporting;
3013
+ return Boolean(node.meta.scriptSupporting);
2983
3014
  case "@form":
2984
- return node.meta.form;
3015
+ return Boolean(node.meta.form);
2985
3016
  default:
2986
3017
  throw new Error(`Invalid content category "${category}"`);
2987
3018
  }
@@ -3166,10 +3197,12 @@ var configurationSchema = {
3166
3197
  properties: properties
3167
3198
  };
3168
3199
 
3169
- var TRANSFORMER_API;
3170
- (function (TRANSFORMER_API) {
3171
- TRANSFORMER_API[TRANSFORMER_API["VERSION"] = 1] = "VERSION";
3172
- })(TRANSFORMER_API || (TRANSFORMER_API = {}));
3200
+ /**
3201
+ * @internal
3202
+ */
3203
+ const TRANSFORMER_API = {
3204
+ VERSION: 1,
3205
+ };
3173
3206
 
3174
3207
  /**
3175
3208
  * @public
@@ -3416,7 +3449,7 @@ function classifyNodeText(node, options = {}) {
3416
3449
  const { accessible = false, ignoreHiddenRoot = false } = options;
3417
3450
  const cacheKey = getCachekey(options);
3418
3451
  if (node.cacheExists(cacheKey)) {
3419
- return node.cacheGet(cacheKey);
3452
+ return node.cacheGet(cacheKey); // eslint-disable-line @typescript-eslint/no-non-null-assertion -- has/get combo
3420
3453
  }
3421
3454
  if (!ignoreHiddenRoot && isHTMLHidden(node)) {
3422
3455
  return node.cacheSet(cacheKey, exports.TextClassification.EMPTY_TEXT);
@@ -3464,8 +3497,12 @@ function findTextNodes(node, options) {
3464
3497
 
3465
3498
  function hasAltText(image) {
3466
3499
  const alt = image.getAttribute("alt");
3467
- /* missing or boolean */
3468
- if (alt === null || alt.value === null) {
3500
+ /* missing attribute */
3501
+ if (!alt) {
3502
+ return false;
3503
+ }
3504
+ /* (incorrectly) set as boolean value */
3505
+ if (alt.value === null) {
3469
3506
  return false;
3470
3507
  }
3471
3508
  return alt.isDynamic || alt.value.toString() !== "";
@@ -3473,8 +3510,12 @@ function hasAltText(image) {
3473
3510
 
3474
3511
  function hasAriaLabel(node) {
3475
3512
  const label = node.getAttribute("aria-label");
3476
- /* missing or boolean */
3477
- if (label === null || label.value === null) {
3513
+ /* missing attribute */
3514
+ if (!label) {
3515
+ return false;
3516
+ }
3517
+ /* (incorrectly) set as boolean value */
3518
+ if (label.value === null) {
3478
3519
  return false;
3479
3520
  }
3480
3521
  return label.isDynamic || label.value.toString() !== "";
@@ -3555,7 +3596,7 @@ class Rule {
3555
3596
  this.options = options;
3556
3597
  this.enabled = true;
3557
3598
  this.blockers = [];
3558
- this.severity = 0;
3599
+ this.severity = exports.Severity.DISABLED;
3559
3600
  this.name = "";
3560
3601
  }
3561
3602
  getSeverity() {
@@ -3708,13 +3749,14 @@ class Rule {
3708
3749
  }
3709
3750
  }
3710
3751
  findLocation(src) {
3752
+ var _a, _b;
3711
3753
  if (src.location) {
3712
3754
  return src.location;
3713
3755
  }
3714
- if (src.event && src.event.location) {
3756
+ if ((_a = src.event) === null || _a === void 0 ? void 0 : _a.location) {
3715
3757
  return src.event.location;
3716
3758
  }
3717
- if (src.node && src.node.location) {
3759
+ if ((_b = src.node) === null || _b === void 0 ? void 0 : _b.location) {
3718
3760
  return src.node.location;
3719
3761
  }
3720
3762
  return {};
@@ -3887,7 +3929,8 @@ class AllowedLinks extends Rule {
3887
3929
  };
3888
3930
  }
3889
3931
  documentation(context) {
3890
- const message = description[context] || "This link type is not allowed by current configuration";
3932
+ var _a;
3933
+ const message = (_a = description[context]) !== null && _a !== void 0 ? _a : "This link type is not allowed by current configuration";
3891
3934
  return {
3892
3935
  description: message,
3893
3936
  url: "https://html-validate.org/rules/allowed-links.html",
@@ -4151,7 +4194,7 @@ function isValidUsage(target, meta) {
4151
4194
  return true;
4152
4195
  }
4153
4196
  /* interactive and labelable elements are valid */
4154
- if (meta.interactive || meta.labelable) {
4197
+ if (Boolean(meta.interactive) || Boolean(meta.labelable)) {
4155
4198
  return true;
4156
4199
  }
4157
4200
  return false;
@@ -4186,7 +4229,7 @@ class AriaLabelMisuse extends Rule {
4186
4229
  }
4187
4230
  validateElement(target) {
4188
4231
  const attr = target.getAttribute("aria-label");
4189
- if (!attr || !attr.value || attr.valueMatches("", false)) {
4232
+ if (!(attr === null || attr === void 0 ? void 0 : attr.value) || attr.valueMatches("", false)) {
4190
4233
  return;
4191
4234
  }
4192
4235
  /* ignore elements without meta */
@@ -4917,7 +4960,7 @@ class AttributeAllowedValues extends Rule {
4917
4960
  const meta = node.meta;
4918
4961
  /* ignore rule if element has no meta or meta does not specify attribute
4919
4962
  * allowed values */
4920
- if (!meta || !meta.attributes)
4963
+ if (!(meta === null || meta === void 0 ? void 0 : meta.attributes))
4921
4964
  return;
4922
4965
  for (const attr of node.attributes) {
4923
4966
  if (Validator.validateAttribute(attr, meta.attributes)) {
@@ -4985,7 +5028,7 @@ class AttributeBooleanStyle extends Rule {
4985
5028
  const meta = node.meta;
4986
5029
  /* ignore rule if element has no meta or meta does not specify attribute
4987
5030
  * allowed values */
4988
- if (!meta || !meta.attributes)
5031
+ if (!(meta === null || meta === void 0 ? void 0 : meta.attributes))
4989
5032
  return;
4990
5033
  /* check all boolean attributes */
4991
5034
  for (const attr of node.attributes) {
@@ -5066,7 +5109,7 @@ class AttributeEmptyStyle extends Rule {
5066
5109
  const meta = node.meta;
5067
5110
  /* ignore rule if element has no meta or meta does not specify attribute
5068
5111
  * allowed values */
5069
- if (!meta || !meta.attributes)
5112
+ if (!(meta === null || meta === void 0 ? void 0 : meta.attributes))
5070
5113
  return;
5071
5114
  /* check all boolean attributes */
5072
5115
  for (const attr of node.attributes) {
@@ -5154,10 +5197,10 @@ class AttributeMisuse extends Rule {
5154
5197
  });
5155
5198
  }
5156
5199
  validateAttr(node, attr, meta) {
5157
- if (!meta || !meta.allowed) {
5200
+ if (!(meta === null || meta === void 0 ? void 0 : meta.allowed)) {
5158
5201
  return;
5159
5202
  }
5160
- const details = meta.allowed(node, attr);
5203
+ const details = meta.allowed(node._adapter, attr.value);
5161
5204
  if (details) {
5162
5205
  this.report({
5163
5206
  node,
@@ -5828,7 +5871,7 @@ class ElementPermittedOccurrences extends Rule {
5828
5871
  this.on("dom:ready", (event) => {
5829
5872
  const doc = event.document;
5830
5873
  doc.visitDepthFirst((node) => {
5831
- if (!node || !node.meta) {
5874
+ if (!(node === null || node === void 0 ? void 0 : node.meta)) {
5832
5875
  return;
5833
5876
  }
5834
5877
  const rules = node.meta.permittedContent;
@@ -6033,7 +6076,7 @@ class ElementRequiredAttributes extends Rule {
6033
6076
  const node = event.previous;
6034
6077
  const meta = node.meta;
6035
6078
  /* handle missing metadata and missing attributes */
6036
- if (!meta || !meta.attributes) {
6079
+ if (!(meta === null || meta === void 0 ? void 0 : meta.attributes)) {
6037
6080
  return;
6038
6081
  }
6039
6082
  for (const [key, attr] of Object.entries(meta.attributes)) {
@@ -6345,7 +6388,7 @@ class FormDupName extends Rule {
6345
6388
  const meta = this.getMetaFor(tagName);
6346
6389
  /* istanbul ignore if: the earlier check for getTagsWithProperty ensures
6347
6390
  * these will actually be set so this is just an untestable fallback */
6348
- if (!meta || !meta.formAssociated) {
6391
+ if (!(meta === null || meta === void 0 ? void 0 : meta.formAssociated)) {
6349
6392
  return false;
6350
6393
  }
6351
6394
  return meta.formAssociated.listed;
@@ -6593,7 +6636,7 @@ class IdPattern extends Rule {
6593
6636
  }
6594
6637
  setup() {
6595
6638
  this.on("attr", (event) => {
6596
- var _a;
6639
+ var _a, _b;
6597
6640
  if (event.key.toLowerCase() !== "id") {
6598
6641
  return;
6599
6642
  }
@@ -6601,8 +6644,8 @@ class IdPattern extends Rule {
6601
6644
  if (event.value instanceof DynamicValue) {
6602
6645
  return;
6603
6646
  }
6604
- if (!event.value || !event.value.match(this.pattern)) {
6605
- const value = (_a = event.value) !== null && _a !== void 0 ? _a : "";
6647
+ if (!((_a = event.value) === null || _a === void 0 ? void 0 : _a.match(this.pattern))) {
6648
+ const value = (_b = event.value) !== null && _b !== void 0 ? _b : "";
6606
6649
  const pattern = this.pattern.toString();
6607
6650
  const message = `ID "${value}" does not match required pattern "${pattern}"`;
6608
6651
  this.report(event.target, message, event.valueLocation);
@@ -7030,7 +7073,7 @@ class MetaRefresh extends Rule {
7030
7073
  }
7031
7074
  /* ensure content attribute is set */
7032
7075
  const content = target.getAttribute("content");
7033
- if (!content || !content.value || content.isDynamic) {
7076
+ if (!(content === null || content === void 0 ? void 0 : content.value) || content.isDynamic) {
7034
7077
  return;
7035
7078
  }
7036
7079
  /* ensure content attribute is valid */
@@ -7309,7 +7352,7 @@ class NoDeprecatedAttr extends Rule {
7309
7352
  if (meta === null) {
7310
7353
  return;
7311
7354
  }
7312
- const metaAttribute = meta.attributes && meta.attributes[attr];
7355
+ const metaAttribute = meta.attributes[attr];
7313
7356
  if (!metaAttribute) {
7314
7357
  return;
7315
7358
  }
@@ -7389,7 +7432,7 @@ class NoDupID extends Rule {
7389
7432
  for (const el of relevant) {
7390
7433
  const attr = el.getAttribute("id");
7391
7434
  /* istanbul ignore next: this has already been tested in isRelevant once but for type-safety it is checked again */
7392
- if (!attr || !attr.value) {
7435
+ if (!(attr === null || attr === void 0 ? void 0 : attr.value)) {
7393
7436
  continue;
7394
7437
  }
7395
7438
  const id = attr.value.toString();
@@ -7523,11 +7566,12 @@ class NoInlineStyle extends Rule {
7523
7566
  });
7524
7567
  }
7525
7568
  isRelevant(event) {
7569
+ var _a;
7526
7570
  if (event.key !== "style") {
7527
7571
  return false;
7528
7572
  }
7529
7573
  const { include, exclude } = this.options;
7530
- const key = event.originalAttribute || event.key;
7574
+ const key = (_a = event.originalAttribute) !== null && _a !== void 0 ? _a : event.key;
7531
7575
  /* ignore attributes not present in "include" */
7532
7576
  if (include && !include.includes(key)) {
7533
7577
  return false;
@@ -7721,7 +7765,8 @@ class NoRawCharacters extends Rule {
7721
7765
  if (event.quote) {
7722
7766
  return;
7723
7767
  }
7724
- this.findRawChars(event.target, event.value.toString(), event.valueLocation, unquotedAttrRegexp);
7768
+ 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
7769
+ unquotedAttrRegexp);
7725
7770
  });
7726
7771
  }
7727
7772
  /**
@@ -7935,7 +7980,7 @@ class NoSelfClosing extends Rule {
7935
7980
  function isRelevant(node, options) {
7936
7981
  /* tags in XML namespaces are relevant only if ignoreXml is false, in which
7937
7982
  * case assume all xml elements must not be self-closed */
7938
- if (node.tagName && node.tagName.match(xmlns)) {
7983
+ if (node.tagName.match(xmlns)) {
7939
7984
  return !options.ignoreXML;
7940
7985
  }
7941
7986
  /* nodes with missing metadata is assumed relevant */
@@ -8141,16 +8186,12 @@ class PreferButton extends Rule {
8141
8186
  };
8142
8187
  }
8143
8188
  documentation(context) {
8144
- const doc = {
8145
- description: `Prefer to use the generic \`<button>\` element instead of \`<input>\`.`,
8189
+ const src = `<input type="${context.type}">`;
8190
+ const dst = replacement[context.type] || `<button>`;
8191
+ return {
8192
+ description: `Prefer to use \`${dst}\` instead of \`"${src}\`.`,
8146
8193
  url: "https://html-validate.org/rules/prefer-button.html",
8147
8194
  };
8148
- if (context) {
8149
- const src = `<input type="${context.type}">`;
8150
- const dst = replacement[context.type] || `<button>`;
8151
- doc.description = `Prefer to use \`${dst}\` instead of \`"${src}\`.`;
8152
- }
8153
- return doc;
8154
8195
  }
8155
8196
  setup() {
8156
8197
  this.on("attr", (event) => {
@@ -8249,14 +8290,10 @@ class PreferNativeElement extends Rule {
8249
8290
  };
8250
8291
  }
8251
8292
  documentation(context) {
8252
- const doc = {
8253
- description: `Instead of using WAI-ARIA roles prefer to use the native HTML elements.`,
8293
+ return {
8294
+ description: `Instead of using the WAI-ARIA role "${context.role}" prefer to use the native <${context.replacement}> element.`,
8254
8295
  url: "https://html-validate.org/rules/prefer-native-element.html",
8255
8296
  };
8256
- if (context) {
8257
- doc.description = `Instead of using the WAI-ARIA role "${context.role}" prefer to use the native <${context.replacement}> element.`;
8258
- }
8259
- return doc;
8260
8297
  }
8261
8298
  setup() {
8262
8299
  const { mapping } = this.options;
@@ -8297,7 +8334,7 @@ class PreferNativeElement extends Rule {
8297
8334
  }
8298
8335
  getLocation(event) {
8299
8336
  const begin = event.location;
8300
- const end = event.valueLocation;
8337
+ 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
8301
8338
  const quote = event.quote ? 1 : 0;
8302
8339
  const size = end.offset + end.size - begin.offset + quote;
8303
8340
  return {
@@ -8371,7 +8408,7 @@ class RequireCSPNonce extends Rule {
8371
8408
  const { tags } = this.options;
8372
8409
  const node = event.previous;
8373
8410
  /* ignore other tags */
8374
- if (!node || !tags.includes(node.tagName)) {
8411
+ if (!tags.includes(node.tagName)) {
8375
8412
  return;
8376
8413
  }
8377
8414
  /* ignore if nonce is set to non-empty value (or dynamic) */
@@ -8463,7 +8500,10 @@ class RequireSri extends Rule {
8463
8500
  }
8464
8501
  needSri(node) {
8465
8502
  const attr = this.elementSourceAttr(node);
8466
- if (!attr || attr.value === null || attr.value === "" || attr.isDynamic) {
8503
+ if (!attr) {
8504
+ return false;
8505
+ }
8506
+ if (attr.value === null || attr.value === "" || attr.isDynamic) {
8467
8507
  return false;
8468
8508
  }
8469
8509
  const url = attr.value.toString();
@@ -8520,7 +8560,7 @@ class ScriptType extends Rule {
8520
8560
  setup() {
8521
8561
  this.on("tag:end", (event) => {
8522
8562
  const node = event.previous;
8523
- if (!node || node.tagName !== "script") {
8563
+ if (node.tagName !== "script") {
8524
8564
  return;
8525
8565
  }
8526
8566
  const attr = node.getAttribute("type");
@@ -8801,18 +8841,16 @@ class TextContent extends Rule {
8801
8841
  description: `The textual content for this element is not valid.`,
8802
8842
  url: "https://html-validate.org/rules/text-content.html",
8803
8843
  };
8804
- if (context === null || context === void 0 ? void 0 : context.textContent) {
8805
- switch (context.textContent) {
8806
- case exports.TextContent.NONE:
8807
- doc.description = `The \`<${context.tagName}>\` element must not have textual content.`;
8808
- break;
8809
- case exports.TextContent.REQUIRED:
8810
- doc.description = `The \`<${context.tagName}>\` element must have textual content.`;
8811
- break;
8812
- case exports.TextContent.ACCESSIBLE:
8813
- doc.description = `The \`<${context.tagName}>\` element must have accessible text.`;
8814
- break;
8815
- }
8844
+ switch (context.textContent) {
8845
+ case exports.TextContent.NONE:
8846
+ doc.description = `The \`<${context.tagName}>\` element must not have textual content.`;
8847
+ break;
8848
+ case exports.TextContent.REQUIRED:
8849
+ doc.description = `The \`<${context.tagName}>\` element must have textual content.`;
8850
+ break;
8851
+ case exports.TextContent.ACCESSIBLE:
8852
+ doc.description = `The \`<${context.tagName}>\` element must have accessible text.`;
8853
+ break;
8816
8854
  }
8817
8855
  return doc;
8818
8856
  }
@@ -9173,20 +9211,16 @@ class VoidStyle extends Rule {
9173
9211
  };
9174
9212
  }
9175
9213
  documentation(context) {
9176
- const doc = {
9177
- description: "The current configuration requires a specific style for ending void elements.",
9214
+ const [desc, end] = styleDescription(context.style);
9215
+ return {
9216
+ description: `The current configuration requires void elements to ${desc}, use <${context.tagName}${end}> instead.`,
9178
9217
  url: "https://html-validate.org/rules/void-style.html",
9179
9218
  };
9180
- if (context) {
9181
- const [desc, end] = styleDescription(context.style);
9182
- doc.description = `The current configuration requires void elements to ${desc}, use <${context.tagName}${end}> instead.`;
9183
- }
9184
- return doc;
9185
9219
  }
9186
9220
  setup() {
9187
9221
  this.on("tag:end", (event) => {
9188
9222
  const active = event.previous; // The current active element (that is, the current element on the stack)
9189
- if (active && active.meta) {
9223
+ if (active.meta) {
9190
9224
  this.validateActive(active);
9191
9225
  }
9192
9226
  });
@@ -9423,7 +9457,8 @@ class H37 extends Rule {
9423
9457
  return;
9424
9458
  }
9425
9459
  /* validate plain alt-attribute */
9426
- if (node.getAttributeValue("alt") || (node.hasAttribute("alt") && this.options.allowEmpty)) {
9460
+ if (Boolean(node.getAttributeValue("alt")) ||
9461
+ Boolean(node.hasAttribute("alt") && this.options.allowEmpty)) {
9427
9462
  return;
9428
9463
  }
9429
9464
  /* validate if any non-empty alias is present */
@@ -9856,7 +9891,7 @@ class ResolvedConfig {
9856
9891
  * @returns A list of transformed sources ready for validation.
9857
9892
  */
9858
9893
  transformSource(source, filename) {
9859
- const transformer = this.findTransformer(filename || source.filename);
9894
+ const transformer = this.findTransformer(filename !== null && filename !== void 0 ? filename : source.filename);
9860
9895
  const context = {
9861
9896
  hasChain: (filename) => {
9862
9897
  return !!this.findTransformer(filename);
@@ -9868,9 +9903,10 @@ class ResolvedConfig {
9868
9903
  if (transformer) {
9869
9904
  try {
9870
9905
  return Array.from(transformer.fn.call(context, source), (cur) => {
9906
+ var _a;
9871
9907
  /* keep track of which transformers that has been run on this source
9872
9908
  * by appending this entry to the transformedBy array */
9873
- cur.transformedBy = cur.transformedBy || [];
9909
+ (_a = cur.transformedBy) !== null && _a !== void 0 ? _a : (cur.transformedBy = []);
9874
9910
  cur.transformedBy.push(transformer.name);
9875
9911
  return cur;
9876
9912
  });
@@ -10042,7 +10078,7 @@ function mergeInternal(base, rhs) {
10042
10078
  }
10043
10079
  /* root property is merged with boolean "or" since it should always be truthy
10044
10080
  * if any config has it set. */
10045
- const root = base.root || rhs.root;
10081
+ const root = Boolean(base.root) || Boolean(rhs.root);
10046
10082
  if (root) {
10047
10083
  dst.root = root;
10048
10084
  }
@@ -10129,7 +10165,7 @@ class Config {
10129
10165
  * @internal
10130
10166
  */
10131
10167
  constructor(resolvers, options) {
10132
- var _a;
10168
+ var _a, _b;
10133
10169
  this.transformers = [];
10134
10170
  const initial = {
10135
10171
  extends: [],
@@ -10142,18 +10178,18 @@ class Config {
10142
10178
  this.initialized = false;
10143
10179
  this.resolvers = toArray(resolvers);
10144
10180
  /* load plugins */
10145
- this.plugins = this.loadPlugins(this.config.plugins || []);
10181
+ this.plugins = this.loadPlugins((_a = this.config.plugins) !== null && _a !== void 0 ? _a : []);
10146
10182
  this.configurations = this.loadConfigurations(this.plugins);
10147
10183
  this.extendMeta(this.plugins);
10148
10184
  /* process extended configs */
10149
- this.config = this.extendConfig((_a = this.config.extends) !== null && _a !== void 0 ? _a : []);
10185
+ this.config = this.extendConfig((_b = this.config.extends) !== null && _b !== void 0 ? _b : []);
10150
10186
  /* reset extends as we already processed them, this prevents the next config
10151
10187
  * from reapplying config from extended config as well as duplicate entries
10152
10188
  * when merging arrays */
10153
10189
  this.config.extends = [];
10154
10190
  /* rules explicitly set by passed options should have precedence over any
10155
10191
  * extended rules, not the other way around. */
10156
- if (options && options.rules) {
10192
+ if (options === null || options === void 0 ? void 0 : options.rules) {
10157
10193
  this.config = mergeInternal(this.config, { rules: options.rules });
10158
10194
  }
10159
10195
  }
@@ -10166,11 +10202,12 @@ class Config {
10166
10202
  * @public
10167
10203
  */
10168
10204
  init() {
10205
+ var _a;
10169
10206
  if (this.initialized) {
10170
10207
  return;
10171
10208
  }
10172
10209
  /* precompile transform patterns */
10173
- this.transformers = this.precompileTransformers(this.config.transform || {});
10210
+ this.transformers = this.precompileTransformers((_a = this.config.transform) !== null && _a !== void 0 ? _a : {});
10174
10211
  this.initialized = true;
10175
10212
  }
10176
10213
  /**
@@ -10197,7 +10234,7 @@ class Config {
10197
10234
  for (const entry of entries) {
10198
10235
  let extended;
10199
10236
  if (this.configurations.has(entry)) {
10200
- extended = this.configurations.get(entry);
10237
+ extended = this.configurations.get(entry); // eslint-disable-line @typescript-eslint/no-non-null-assertion -- map has/get combo
10201
10238
  }
10202
10239
  else {
10203
10240
  extended = Config.fromFile(this.resolvers, entry).config;
@@ -10212,12 +10249,13 @@ class Config {
10212
10249
  * @internal
10213
10250
  */
10214
10251
  getMetaTable() {
10252
+ var _a;
10215
10253
  /* use cached table if it exists */
10216
10254
  if (this.metaTable) {
10217
10255
  return this.metaTable;
10218
10256
  }
10219
10257
  const metaTable = new MetaTable();
10220
- const source = this.config.elements || ["html5"];
10258
+ const source = (_a = this.config.elements) !== null && _a !== void 0 ? _a : ["html5"];
10221
10259
  /* extend validation schema from plugins */
10222
10260
  for (const plugin of this.getPlugins()) {
10223
10261
  if (plugin.elementSchema) {
@@ -10315,6 +10353,7 @@ class Config {
10315
10353
  });
10316
10354
  }
10317
10355
  loadConfigurations(plugins) {
10356
+ var _a;
10318
10357
  const configs = new Map();
10319
10358
  /* builtin presets */
10320
10359
  for (const [name, config] of Object.entries(Presets)) {
@@ -10323,7 +10362,7 @@ class Config {
10323
10362
  }
10324
10363
  /* presets from plugins */
10325
10364
  for (const plugin of plugins) {
10326
- for (const [name, config] of Object.entries(plugin.configs || {})) {
10365
+ for (const [name, config] of Object.entries((_a = plugin.configs) !== null && _a !== void 0 ? _a : {})) {
10327
10366
  if (!config)
10328
10367
  continue;
10329
10368
  Config.validate(config, name);
@@ -10688,10 +10727,11 @@ class Parser {
10688
10727
  * stack when is allowed to omit.
10689
10728
  */
10690
10729
  closeOptional(token) {
10730
+ var _a;
10691
10731
  /* if the element doesn't have metadata it cannot have optional end
10692
10732
  * tags. Period. */
10693
10733
  const active = this.dom.getActive();
10694
- if (!(active.meta && active.meta.implicitClosed)) {
10734
+ if (!((_a = active.meta) === null || _a === void 0 ? void 0 : _a.implicitClosed)) {
10695
10735
  return false;
10696
10736
  }
10697
10737
  const tagName = token.data[2];
@@ -10843,9 +10883,10 @@ class Parser {
10843
10883
  }
10844
10884
  }
10845
10885
  processElement(node, source) {
10886
+ var _a;
10846
10887
  /* enable cache on node now that it is fully constructed */
10847
10888
  node.cacheEnable();
10848
- if (source.hooks && source.hooks.processElement) {
10889
+ if ((_a = source.hooks) === null || _a === void 0 ? void 0 : _a.processElement) {
10849
10890
  const processElement = source.hooks.processElement;
10850
10891
  const metaTable = this.metaTable;
10851
10892
  const context = {
@@ -10912,6 +10953,7 @@ class Parser {
10912
10953
  * @internal
10913
10954
  */
10914
10955
  consumeAttribute(source, node, token, next) {
10956
+ var _a;
10915
10957
  const keyLocation = this.getAttributeKeyLocation(token);
10916
10958
  const valueLocation = this.getAttributeValueLocation(next);
10917
10959
  const location = this.getAttributeLocation(token, next);
@@ -10930,7 +10972,7 @@ class Parser {
10930
10972
  * data right away but a transformer may override it to allow aliasing
10931
10973
  * attributes, e.g ng-attr-foo or v-bind:foo */
10932
10974
  let processAttribute = (attr) => [attr];
10933
- if (source.hooks && source.hooks.processAttribute) {
10975
+ if ((_a = source.hooks) === null || _a === void 0 ? void 0 : _a.processAttribute) {
10934
10976
  processAttribute = source.hooks.processAttribute;
10935
10977
  }
10936
10978
  /* handle deprecated callbacks */
@@ -11280,14 +11322,15 @@ class Reporter {
11280
11322
  const report = {
11281
11323
  valid: this.isValid(),
11282
11324
  results: Object.keys(this.result).map((filePath) => {
11325
+ var _a;
11283
11326
  const messages = Array.from(this.result[filePath], freeze).sort(messageSort);
11284
- const source = (sources || []).find((source) => { var _a; return filePath === ((_a = source.filename) !== null && _a !== void 0 ? _a : ""); });
11327
+ const source = (sources !== null && sources !== void 0 ? sources : []).find((source) => { var _a; return filePath === ((_a = source.filename) !== null && _a !== void 0 ? _a : ""); });
11285
11328
  return {
11286
11329
  filePath,
11287
11330
  messages,
11288
11331
  errorCount: countErrors(messages),
11289
11332
  warningCount: countWarnings(messages),
11290
- source: source ? source.originalData || source.data : null,
11333
+ source: source ? (_a = source.originalData) !== null && _a !== void 0 ? _a : source.data : null,
11291
11334
  };
11292
11335
  }),
11293
11336
  errorCount: 0,
@@ -11305,10 +11348,10 @@ class Reporter {
11305
11348
  }
11306
11349
  }
11307
11350
  function countErrors(messages) {
11308
- return messages.filter((m) => m.severity === exports.Severity.ERROR).length;
11351
+ return messages.filter((m) => m.severity === Number(exports.Severity.ERROR)).length;
11309
11352
  }
11310
11353
  function countWarnings(messages) {
11311
- return messages.filter((m) => m.severity === exports.Severity.WARN).length;
11354
+ return messages.filter((m) => m.severity === Number(exports.Severity.WARN)).length;
11312
11355
  }
11313
11356
  function sumErrors(results) {
11314
11357
  return results.reduce((sum, result) => {
@@ -11653,9 +11696,10 @@ class Engine {
11653
11696
  * between rule name and its constructor.
11654
11697
  */
11655
11698
  initRules(config) {
11699
+ var _a;
11656
11700
  const availableRules = {};
11657
11701
  for (const plugin of config.getPlugins()) {
11658
- for (const [name, rule] of Object.entries(plugin.rules || {})) {
11702
+ for (const [name, rule] of Object.entries((_a = plugin.rules) !== null && _a !== void 0 ? _a : {})) {
11659
11703
  if (!rule)
11660
11704
  continue;
11661
11705
  availableRules[name] = rule;
@@ -11758,7 +11802,7 @@ class StaticConfigLoader extends ConfigLoader {
11758
11802
  }
11759
11803
  }
11760
11804
  getConfigFor(_handle, configOverride) {
11761
- const override = this.loadFromObject(configOverride || {});
11805
+ const override = this.loadFromObject(configOverride !== null && configOverride !== void 0 ? configOverride : {});
11762
11806
  if (override.isRootFound()) {
11763
11807
  override.init();
11764
11808
  return override.resolve();
@@ -11802,6 +11846,7 @@ class HtmlValidate {
11802
11846
  const [loader, config] = arg instanceof ConfigLoader ? [arg, undefined] : [undefined, arg];
11803
11847
  this.configLoader = loader !== null && loader !== void 0 ? loader : new StaticConfigLoader(config);
11804
11848
  }
11849
+ /* eslint-enable @typescript-eslint/unified-signatures */
11805
11850
  validateString(str, arg1, arg2, arg3) {
11806
11851
  const filename = typeof arg1 === "string" ? arg1 : "inline";
11807
11852
  const options = isConfigData(arg1) ? arg1 : isConfigData(arg2) ? arg2 : undefined;
@@ -11816,6 +11861,7 @@ class HtmlValidate {
11816
11861
  };
11817
11862
  return this.validateSource(source, options);
11818
11863
  }
11864
+ /* eslint-enable @typescript-eslint/unified-signatures */
11819
11865
  validateStringSync(str, arg1, arg2, arg3) {
11820
11866
  const filename = typeof arg1 === "string" ? arg1 : "inline";
11821
11867
  const options = isConfigData(arg1) ? arg1 : isConfigData(arg2) ? arg2 : undefined;
@@ -12087,7 +12133,7 @@ class HtmlValidate {
12087
12133
  * contextual details and suggestions.
12088
12134
  */
12089
12135
  async getRuleDocumentation(ruleId, config = null, context = null) {
12090
- const c = config || this.getConfigFor("inline");
12136
+ const c = config !== null && config !== void 0 ? config : this.getConfigFor("inline");
12091
12137
  const engine = new Engine(await c, Parser);
12092
12138
  return engine.getRuleDocumentation({ ruleId, context });
12093
12139
  }
@@ -12116,7 +12162,7 @@ class HtmlValidate {
12116
12162
  * contextual details and suggestions.
12117
12163
  */
12118
12164
  getRuleDocumentationSync(ruleId, config = null, context = null) {
12119
- const c = config || this.getConfigForSync("inline");
12165
+ const c = config !== null && config !== void 0 ? config : this.getConfigForSync("inline");
12120
12166
  const engine = new Engine(c, Parser);
12121
12167
  return engine.getRuleDocumentation({ ruleId, context });
12122
12168
  }
@@ -12172,7 +12218,7 @@ class HtmlValidate {
12172
12218
  /** @public */
12173
12219
  const name = "html-validate";
12174
12220
  /** @public */
12175
- const version = "8.1.0";
12221
+ const version = "8.2.0";
12176
12222
  /** @public */
12177
12223
  const bugs = "https://gitlab.com/html-validate/html-validate/issues/new";
12178
12224