html-validate 10.1.2 → 10.2.1

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
@@ -1495,7 +1495,7 @@ function sliceLocation(location, begin, end, wrap) {
1495
1495
  if (wrap) {
1496
1496
  let index = -1;
1497
1497
  const col = sliced.column;
1498
- do {
1498
+ for (; ; ) {
1499
1499
  index = wrap.indexOf("\n", index + 1);
1500
1500
  if (index >= 0 && index < begin) {
1501
1501
  sliced.column = col - (index + 1);
@@ -1503,7 +1503,7 @@ function sliceLocation(location, begin, end, wrap) {
1503
1503
  } else {
1504
1504
  break;
1505
1505
  }
1506
- } while (true);
1506
+ }
1507
1507
  }
1508
1508
  return sliced;
1509
1509
  }
@@ -3480,7 +3480,7 @@ function inAccessibilityTree(node) {
3480
3480
  function isAriaHiddenImpl(node) {
3481
3481
  const getAriaHiddenAttr = (node2) => {
3482
3482
  const ariaHidden = node2.getAttribute("aria-hidden");
3483
- return Boolean(ariaHidden && ariaHidden.value === "true");
3483
+ return ariaHidden?.value === "true";
3484
3484
  };
3485
3485
  return {
3486
3486
  byParent: node.parent ? isAriaHidden(node.parent) : false,
@@ -3715,7 +3715,7 @@ function getSchemaValidator(ruleId, properties) {
3715
3715
  return ajv$1.compile(schema);
3716
3716
  }
3717
3717
  function isErrorDescriptor(value) {
3718
- return Boolean(value[0] && value[0].message);
3718
+ return Boolean(value[0] && value[0]["message"]);
3719
3719
  }
3720
3720
  function unpackErrorDescriptor(value) {
3721
3721
  if (isErrorDescriptor(value)) {
@@ -3986,8 +3986,7 @@ class Rule {
3986
3986
  * @returns Rule documentation and url with additional details or `null` if no
3987
3987
  * additional documentation is available.
3988
3988
  */
3989
- /* eslint-disable-next-line @typescript-eslint/no-unused-vars -- technical debt, prototype should be moved to interface */
3990
- documentation(context) {
3989
+ documentation(_context) {
3991
3990
  return null;
3992
3991
  }
3993
3992
  }
@@ -4316,7 +4315,7 @@ class AriaHiddenBody extends Rule {
4316
4315
  const defaults$w = {
4317
4316
  allowAnyNamable: false
4318
4317
  };
4319
- const whitelisted = [
4318
+ const allowlist = /* @__PURE__ */ new Set([
4320
4319
  "main",
4321
4320
  "nav",
4322
4321
  "table",
@@ -4329,18 +4328,19 @@ const whitelisted = [
4329
4328
  "article",
4330
4329
  "dialog",
4331
4330
  "form",
4331
+ "iframe",
4332
4332
  "img",
4333
4333
  "area",
4334
4334
  "fieldset",
4335
4335
  "summary",
4336
4336
  "figure"
4337
- ];
4337
+ ]);
4338
4338
  function isValidUsage(target, meta) {
4339
4339
  const explicit = meta.attributes["aria-label"];
4340
4340
  if (explicit) {
4341
4341
  return true;
4342
4342
  }
4343
- if (whitelisted.includes(target.tagName)) {
4343
+ if (allowlist.has(target.tagName)) {
4344
4344
  return true;
4345
4345
  }
4346
4346
  if (target.hasAttribute("role")) {
@@ -4358,7 +4358,7 @@ class AriaLabelMisuse extends Rule {
4358
4358
  constructor(options) {
4359
4359
  super({ ...defaults$w, ...options });
4360
4360
  }
4361
- documentation() {
4361
+ documentation(context) {
4362
4362
  const valid = [
4363
4363
  "Interactive elements",
4364
4364
  "Labelable elements",
@@ -4372,25 +4372,41 @@ class AriaLabelMisuse extends Rule {
4372
4372
  "`<summary>`",
4373
4373
  "`<table>`, `<td>` and `<th>`"
4374
4374
  ];
4375
- const lines = valid.map((it) => `- ${it}
4376
- `).join("");
4377
- return {
4378
- description: `\`aria-label\` can only be used on:
4379
-
4380
- ${lines}`,
4381
- url: "https://html-validate.org/rules/aria-label-misuse.html"
4382
- };
4375
+ const lines = valid.map((it) => `- ${it}`);
4376
+ const url = "https://html-validate.org/rules/aria-label-misuse.html";
4377
+ if (context.allowsNaming) {
4378
+ return {
4379
+ description: [
4380
+ `\`${context.attr}\` is strictly allowed but is not recommended to be used on this element.`,
4381
+ `\`${context.attr}\` can only be used on:`,
4382
+ "",
4383
+ ...lines
4384
+ ].join("\n"),
4385
+ url
4386
+ };
4387
+ } else {
4388
+ return {
4389
+ description: [`\`${context.attr}\` can only be used on:`, "", ...lines].join("\n"),
4390
+ url
4391
+ };
4392
+ }
4383
4393
  }
4384
4394
  setup() {
4385
4395
  this.on("dom:ready", (event) => {
4386
4396
  const { document } = event;
4387
- for (const target of document.querySelectorAll("[aria-label]")) {
4388
- this.validateElement(target);
4397
+ for (const target of document.querySelectorAll("[aria-label], [aria-labelledby]")) {
4398
+ const ariaLabel = target.getAttribute("aria-label");
4399
+ if (ariaLabel) {
4400
+ this.validateElement(target, ariaLabel, "aria-label");
4401
+ }
4402
+ const ariaLabelledby = target.getAttribute("aria-labelledby");
4403
+ if (ariaLabelledby) {
4404
+ this.validateElement(target, ariaLabelledby, "aria-labelledby");
4405
+ }
4389
4406
  }
4390
4407
  });
4391
4408
  }
4392
- validateElement(target) {
4393
- const attr = target.getAttribute("aria-label");
4409
+ validateElement(target, attr, key) {
4394
4410
  if (!attr.value || attr.valueMatches("", false)) {
4395
4411
  return;
4396
4412
  }
@@ -4401,10 +4417,26 @@ ${lines}`,
4401
4417
  if (isValidUsage(target, meta)) {
4402
4418
  return;
4403
4419
  }
4404
- if (this.options.allowAnyNamable && ariaNaming(target) === "allowed") {
4420
+ const allowsNaming = ariaNaming(target) === "allowed";
4421
+ if (allowsNaming && this.options.allowAnyNamable) {
4405
4422
  return;
4406
4423
  }
4407
- this.report(target, `"aria-label" cannot be used on this element`, attr.keyLocation);
4424
+ const context = { attr: key, allowsNaming };
4425
+ if (allowsNaming) {
4426
+ this.report({
4427
+ node: target,
4428
+ location: attr.keyLocation,
4429
+ context,
4430
+ message: `"{{ attr }}" is strictly allowed but is not recommended to be used on this element`
4431
+ });
4432
+ } else {
4433
+ this.report({
4434
+ node: target,
4435
+ location: attr.keyLocation,
4436
+ context,
4437
+ message: `"{{ attr }}" cannot be used on this element`
4438
+ });
4439
+ }
4408
4440
  }
4409
4441
  }
4410
4442
 
@@ -5099,9 +5131,6 @@ class AttributeAllowedValues extends Rule {
5099
5131
  description: "Attribute has invalid value.",
5100
5132
  url: "https://html-validate.org/rules/attribute-allowed-values.html"
5101
5133
  };
5102
- if (!context) {
5103
- return docs;
5104
- }
5105
5134
  const { allowed, attribute, element, value } = context;
5106
5135
  if (allowed.enum) {
5107
5136
  const allowedList = allowed.enum.map((value2) => {
@@ -5936,7 +5965,10 @@ class ElementName extends Rule {
5936
5965
 
5937
5966
  function isNativeTemplate(node) {
5938
5967
  const { tagName, meta } = node;
5939
- return Boolean(tagName === "template" && meta?.templateRoot && meta?.scriptSupporting);
5968
+ if (!meta) {
5969
+ return false;
5970
+ }
5971
+ return Boolean(tagName === "template" && meta.templateRoot && meta.scriptSupporting);
5940
5972
  }
5941
5973
  function getTransparentChildren(node, transparent) {
5942
5974
  if (typeof transparent === "boolean") {
@@ -6165,7 +6197,7 @@ class ElementPermittedParent extends Rule {
6165
6197
  }
6166
6198
  const rules = node.meta?.permittedParent;
6167
6199
  if (!rules) {
6168
- return false;
6200
+ return;
6169
6201
  }
6170
6202
  if (Validator.validatePermitted(parent, rules)) {
6171
6203
  return;
@@ -6232,14 +6264,10 @@ class ElementRequiredAncestor extends Rule {
6232
6264
 
6233
6265
  class ElementRequiredAttributes extends Rule {
6234
6266
  documentation(context) {
6235
- const docs = {
6236
- description: "Element is missing a required attribute",
6267
+ return {
6268
+ description: `The \`<${context.element}>\` element is required to have a \`${context.attribute}\` attribute.`,
6237
6269
  url: "https://html-validate.org/rules/element-required-attributes.html"
6238
6270
  };
6239
- if (context) {
6240
- docs.description = `The <${context.element}> element is required to have a "${context.attribute}" attribute.`;
6241
- }
6242
- return docs;
6243
6271
  }
6244
6272
  setup() {
6245
6273
  this.on("tag:end", (event) => {
@@ -8242,10 +8270,9 @@ class NoSelfClosing extends Rule {
8242
8270
  }
8243
8271
  };
8244
8272
  }
8245
- documentation(tagName) {
8246
- tagName = tagName || "element";
8273
+ documentation(context) {
8247
8274
  return {
8248
- description: `Self-closing elements are disallowed. Use regular end tag <${tagName}></${tagName}> instead of self-closing <${tagName}/>.`,
8275
+ description: `Self-closing elements are disallowed. Use regular end tag <${context}></${context}> instead of self-closing <${context}/>.`,
8249
8276
  url: "https://html-validate.org/rules/no-self-closing.html"
8250
8277
  };
8251
8278
  }
@@ -8823,7 +8850,7 @@ class ScriptElement extends Rule {
8823
8850
  setup() {
8824
8851
  this.on("tag:end", (event) => {
8825
8852
  const node = event.target;
8826
- if (!node || node.tagName !== "script") {
8853
+ if (node?.tagName !== "script") {
8827
8854
  return;
8828
8855
  }
8829
8856
  if (node.closed !== NodeClosed.EndTag) {
@@ -10036,13 +10063,13 @@ class ValidID extends Rule {
10036
10063
  }
10037
10064
 
10038
10065
  class VoidContent extends Rule {
10039
- documentation(tagName) {
10066
+ documentation(context) {
10040
10067
  const doc = {
10041
10068
  description: "HTML void elements cannot have any content and must not have content or end tag.",
10042
10069
  url: "https://html-validate.org/rules/void-content.html"
10043
10070
  };
10044
- if (tagName) {
10045
- doc.description = `<${tagName}> is a void element and must not have content or end tag.`;
10071
+ if (context) {
10072
+ doc.description = `<${context}> is a void element and must not have content or end tag.`;
10046
10073
  }
10047
10074
  return doc;
10048
10075
  }
@@ -10345,7 +10372,7 @@ class H37 extends Rule {
10345
10372
  const defaults$1 = {
10346
10373
  strict: false
10347
10374
  };
10348
- const { enum: validScopes } = elements.html5.th.attributes?.scope;
10375
+ const { enum: validScopes } = elements.html5.th.attributes.scope;
10349
10376
  const joinedScopes = utils_naturalJoin.naturalJoin(validScopes);
10350
10377
  const td = 0;
10351
10378
  const th = 1;
@@ -10443,7 +10470,7 @@ class H67 extends Rule {
10443
10470
  setup() {
10444
10471
  this.on("tag:end", (event) => {
10445
10472
  const node = event.target;
10446
- if (!node || node.tagName !== "img") {
10473
+ if (node?.tagName !== "img") {
10447
10474
  return;
10448
10475
  }
10449
10476
  const title = node.getAttribute("title");
@@ -11887,7 +11914,7 @@ class EventHandler {
11887
11914
  }
11888
11915
 
11889
11916
  const name = "html-validate";
11890
- const version = "10.1.2";
11917
+ const version = "10.2.1";
11891
11918
  const bugs = "https://gitlab.com/html-validate/html-validate/issues/new";
11892
11919
 
11893
11920
  function freeze(src) {
@@ -12620,9 +12647,9 @@ class Parser {
12620
12647
  * Trigger close events for any still open elements.
12621
12648
  */
12622
12649
  closeTree(source, location) {
12623
- let active;
12624
12650
  const documentElement = this.dom.root;
12625
- while ((active = this.dom.getActive()) && !active.isRootElement()) {
12651
+ let active = this.dom.getActive();
12652
+ while (!active.isRootElement()) {
12626
12653
  if (active.meta?.implicitClosed) {
12627
12654
  active.closed = NodeClosed.ImplicitClosed;
12628
12655
  this.closeElement(source, documentElement, active, location);
@@ -12630,6 +12657,7 @@ class Parser {
12630
12657
  this.closeElement(source, null, active, location);
12631
12658
  }
12632
12659
  this.dom.popActive();
12660
+ active = this.dom.getActive();
12633
12661
  }
12634
12662
  }
12635
12663
  }
@@ -12996,8 +13024,8 @@ function getUnnamedTransformerFromPlugin(name, plugin) {
12996
13024
  throw new ConfigError(`Plugin does not expose any transformers`);
12997
13025
  }
12998
13026
  if (typeof plugin.transformer !== "function") {
12999
- if (plugin.transformer.default) {
13000
- return plugin.transformer.default;
13027
+ if (plugin.transformer["default"]) {
13028
+ return plugin.transformer["default"];
13001
13029
  }
13002
13030
  throw new ConfigError(
13003
13031
  `Transformer "${name}" refers to unnamed transformer but plugin exposes only named.`
@@ -13226,7 +13254,7 @@ function checkstyleFormatter(results) {
13226
13254
  output += "</checkstyle>\n";
13227
13255
  return output;
13228
13256
  }
13229
- const formatter$3 = checkstyleFormatter;
13257
+ const formatter$2 = checkstyleFormatter;
13230
13258
 
13231
13259
  const defaults = {
13232
13260
  showLink: true,
@@ -13401,7 +13429,7 @@ function codeframe(results, options) {
13401
13429
  function jsonFormatter(results) {
13402
13430
  return JSON.stringify(results);
13403
13431
  }
13404
- const formatter$2 = jsonFormatter;
13432
+ const formatter$1 = jsonFormatter;
13405
13433
 
13406
13434
  function linkSummary(results) {
13407
13435
  const urls = results.reduce((result, it) => {
@@ -13430,7 +13458,6 @@ function stylish(results) {
13430
13458
  const links = linkSummary(results);
13431
13459
  return `${errors}${links}`;
13432
13460
  }
13433
- const formatter$1 = stylish;
13434
13461
 
13435
13462
  function textFormatter(results) {
13436
13463
  let output = "";
@@ -13460,10 +13487,10 @@ function textFormatter(results) {
13460
13487
  const formatter = textFormatter;
13461
13488
 
13462
13489
  const availableFormatters = {
13463
- checkstyle: formatter$3,
13490
+ checkstyle: formatter$2,
13464
13491
  codeframe,
13465
- json: formatter$2,
13466
- stylish: formatter$1,
13492
+ json: formatter$1,
13493
+ stylish,
13467
13494
  text: formatter
13468
13495
  };
13469
13496
  function getFormatter(name) {