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/esm/core.js CHANGED
@@ -1486,7 +1486,7 @@ function sliceLocation(location, begin, end, wrap) {
1486
1486
  if (wrap) {
1487
1487
  let index = -1;
1488
1488
  const col = sliced.column;
1489
- do {
1489
+ for (; ; ) {
1490
1490
  index = wrap.indexOf("\n", index + 1);
1491
1491
  if (index >= 0 && index < begin) {
1492
1492
  sliced.column = col - (index + 1);
@@ -1494,7 +1494,7 @@ function sliceLocation(location, begin, end, wrap) {
1494
1494
  } else {
1495
1495
  break;
1496
1496
  }
1497
- } while (true);
1497
+ }
1498
1498
  }
1499
1499
  return sliced;
1500
1500
  }
@@ -3471,7 +3471,7 @@ function inAccessibilityTree(node) {
3471
3471
  function isAriaHiddenImpl(node) {
3472
3472
  const getAriaHiddenAttr = (node2) => {
3473
3473
  const ariaHidden = node2.getAttribute("aria-hidden");
3474
- return Boolean(ariaHidden && ariaHidden.value === "true");
3474
+ return ariaHidden?.value === "true";
3475
3475
  };
3476
3476
  return {
3477
3477
  byParent: node.parent ? isAriaHidden(node.parent) : false,
@@ -3706,7 +3706,7 @@ function getSchemaValidator(ruleId, properties) {
3706
3706
  return ajv$1.compile(schema);
3707
3707
  }
3708
3708
  function isErrorDescriptor(value) {
3709
- return Boolean(value[0] && value[0].message);
3709
+ return Boolean(value[0] && value[0]["message"]);
3710
3710
  }
3711
3711
  function unpackErrorDescriptor(value) {
3712
3712
  if (isErrorDescriptor(value)) {
@@ -3977,8 +3977,7 @@ class Rule {
3977
3977
  * @returns Rule documentation and url with additional details or `null` if no
3978
3978
  * additional documentation is available.
3979
3979
  */
3980
- /* eslint-disable-next-line @typescript-eslint/no-unused-vars -- technical debt, prototype should be moved to interface */
3981
- documentation(context) {
3980
+ documentation(_context) {
3982
3981
  return null;
3983
3982
  }
3984
3983
  }
@@ -4307,7 +4306,7 @@ class AriaHiddenBody extends Rule {
4307
4306
  const defaults$w = {
4308
4307
  allowAnyNamable: false
4309
4308
  };
4310
- const whitelisted = [
4309
+ const allowlist = /* @__PURE__ */ new Set([
4311
4310
  "main",
4312
4311
  "nav",
4313
4312
  "table",
@@ -4320,18 +4319,19 @@ const whitelisted = [
4320
4319
  "article",
4321
4320
  "dialog",
4322
4321
  "form",
4322
+ "iframe",
4323
4323
  "img",
4324
4324
  "area",
4325
4325
  "fieldset",
4326
4326
  "summary",
4327
4327
  "figure"
4328
- ];
4328
+ ]);
4329
4329
  function isValidUsage(target, meta) {
4330
4330
  const explicit = meta.attributes["aria-label"];
4331
4331
  if (explicit) {
4332
4332
  return true;
4333
4333
  }
4334
- if (whitelisted.includes(target.tagName)) {
4334
+ if (allowlist.has(target.tagName)) {
4335
4335
  return true;
4336
4336
  }
4337
4337
  if (target.hasAttribute("role")) {
@@ -4349,7 +4349,7 @@ class AriaLabelMisuse extends Rule {
4349
4349
  constructor(options) {
4350
4350
  super({ ...defaults$w, ...options });
4351
4351
  }
4352
- documentation() {
4352
+ documentation(context) {
4353
4353
  const valid = [
4354
4354
  "Interactive elements",
4355
4355
  "Labelable elements",
@@ -4363,25 +4363,41 @@ class AriaLabelMisuse extends Rule {
4363
4363
  "`<summary>`",
4364
4364
  "`<table>`, `<td>` and `<th>`"
4365
4365
  ];
4366
- const lines = valid.map((it) => `- ${it}
4367
- `).join("");
4368
- return {
4369
- description: `\`aria-label\` can only be used on:
4370
-
4371
- ${lines}`,
4372
- url: "https://html-validate.org/rules/aria-label-misuse.html"
4373
- };
4366
+ const lines = valid.map((it) => `- ${it}`);
4367
+ const url = "https://html-validate.org/rules/aria-label-misuse.html";
4368
+ if (context.allowsNaming) {
4369
+ return {
4370
+ description: [
4371
+ `\`${context.attr}\` is strictly allowed but is not recommended to be used on this element.`,
4372
+ `\`${context.attr}\` can only be used on:`,
4373
+ "",
4374
+ ...lines
4375
+ ].join("\n"),
4376
+ url
4377
+ };
4378
+ } else {
4379
+ return {
4380
+ description: [`\`${context.attr}\` can only be used on:`, "", ...lines].join("\n"),
4381
+ url
4382
+ };
4383
+ }
4374
4384
  }
4375
4385
  setup() {
4376
4386
  this.on("dom:ready", (event) => {
4377
4387
  const { document } = event;
4378
- for (const target of document.querySelectorAll("[aria-label]")) {
4379
- this.validateElement(target);
4388
+ for (const target of document.querySelectorAll("[aria-label], [aria-labelledby]")) {
4389
+ const ariaLabel = target.getAttribute("aria-label");
4390
+ if (ariaLabel) {
4391
+ this.validateElement(target, ariaLabel, "aria-label");
4392
+ }
4393
+ const ariaLabelledby = target.getAttribute("aria-labelledby");
4394
+ if (ariaLabelledby) {
4395
+ this.validateElement(target, ariaLabelledby, "aria-labelledby");
4396
+ }
4380
4397
  }
4381
4398
  });
4382
4399
  }
4383
- validateElement(target) {
4384
- const attr = target.getAttribute("aria-label");
4400
+ validateElement(target, attr, key) {
4385
4401
  if (!attr.value || attr.valueMatches("", false)) {
4386
4402
  return;
4387
4403
  }
@@ -4392,10 +4408,26 @@ ${lines}`,
4392
4408
  if (isValidUsage(target, meta)) {
4393
4409
  return;
4394
4410
  }
4395
- if (this.options.allowAnyNamable && ariaNaming(target) === "allowed") {
4411
+ const allowsNaming = ariaNaming(target) === "allowed";
4412
+ if (allowsNaming && this.options.allowAnyNamable) {
4396
4413
  return;
4397
4414
  }
4398
- this.report(target, `"aria-label" cannot be used on this element`, attr.keyLocation);
4415
+ const context = { attr: key, allowsNaming };
4416
+ if (allowsNaming) {
4417
+ this.report({
4418
+ node: target,
4419
+ location: attr.keyLocation,
4420
+ context,
4421
+ message: `"{{ attr }}" is strictly allowed but is not recommended to be used on this element`
4422
+ });
4423
+ } else {
4424
+ this.report({
4425
+ node: target,
4426
+ location: attr.keyLocation,
4427
+ context,
4428
+ message: `"{{ attr }}" cannot be used on this element`
4429
+ });
4430
+ }
4399
4431
  }
4400
4432
  }
4401
4433
 
@@ -5090,9 +5122,6 @@ class AttributeAllowedValues extends Rule {
5090
5122
  description: "Attribute has invalid value.",
5091
5123
  url: "https://html-validate.org/rules/attribute-allowed-values.html"
5092
5124
  };
5093
- if (!context) {
5094
- return docs;
5095
- }
5096
5125
  const { allowed, attribute, element, value } = context;
5097
5126
  if (allowed.enum) {
5098
5127
  const allowedList = allowed.enum.map((value2) => {
@@ -5927,7 +5956,10 @@ class ElementName extends Rule {
5927
5956
 
5928
5957
  function isNativeTemplate(node) {
5929
5958
  const { tagName, meta } = node;
5930
- return Boolean(tagName === "template" && meta?.templateRoot && meta?.scriptSupporting);
5959
+ if (!meta) {
5960
+ return false;
5961
+ }
5962
+ return Boolean(tagName === "template" && meta.templateRoot && meta.scriptSupporting);
5931
5963
  }
5932
5964
  function getTransparentChildren(node, transparent) {
5933
5965
  if (typeof transparent === "boolean") {
@@ -6156,7 +6188,7 @@ class ElementPermittedParent extends Rule {
6156
6188
  }
6157
6189
  const rules = node.meta?.permittedParent;
6158
6190
  if (!rules) {
6159
- return false;
6191
+ return;
6160
6192
  }
6161
6193
  if (Validator.validatePermitted(parent, rules)) {
6162
6194
  return;
@@ -6223,14 +6255,10 @@ class ElementRequiredAncestor extends Rule {
6223
6255
 
6224
6256
  class ElementRequiredAttributes extends Rule {
6225
6257
  documentation(context) {
6226
- const docs = {
6227
- description: "Element is missing a required attribute",
6258
+ return {
6259
+ description: `The \`<${context.element}>\` element is required to have a \`${context.attribute}\` attribute.`,
6228
6260
  url: "https://html-validate.org/rules/element-required-attributes.html"
6229
6261
  };
6230
- if (context) {
6231
- docs.description = `The <${context.element}> element is required to have a "${context.attribute}" attribute.`;
6232
- }
6233
- return docs;
6234
6262
  }
6235
6263
  setup() {
6236
6264
  this.on("tag:end", (event) => {
@@ -8233,10 +8261,9 @@ class NoSelfClosing extends Rule {
8233
8261
  }
8234
8262
  };
8235
8263
  }
8236
- documentation(tagName) {
8237
- tagName = tagName || "element";
8264
+ documentation(context) {
8238
8265
  return {
8239
- description: `Self-closing elements are disallowed. Use regular end tag <${tagName}></${tagName}> instead of self-closing <${tagName}/>.`,
8266
+ description: `Self-closing elements are disallowed. Use regular end tag <${context}></${context}> instead of self-closing <${context}/>.`,
8240
8267
  url: "https://html-validate.org/rules/no-self-closing.html"
8241
8268
  };
8242
8269
  }
@@ -8814,7 +8841,7 @@ class ScriptElement extends Rule {
8814
8841
  setup() {
8815
8842
  this.on("tag:end", (event) => {
8816
8843
  const node = event.target;
8817
- if (!node || node.tagName !== "script") {
8844
+ if (node?.tagName !== "script") {
8818
8845
  return;
8819
8846
  }
8820
8847
  if (node.closed !== NodeClosed.EndTag) {
@@ -10027,13 +10054,13 @@ class ValidID extends Rule {
10027
10054
  }
10028
10055
 
10029
10056
  class VoidContent extends Rule {
10030
- documentation(tagName) {
10057
+ documentation(context) {
10031
10058
  const doc = {
10032
10059
  description: "HTML void elements cannot have any content and must not have content or end tag.",
10033
10060
  url: "https://html-validate.org/rules/void-content.html"
10034
10061
  };
10035
- if (tagName) {
10036
- doc.description = `<${tagName}> is a void element and must not have content or end tag.`;
10062
+ if (context) {
10063
+ doc.description = `<${context}> is a void element and must not have content or end tag.`;
10037
10064
  }
10038
10065
  return doc;
10039
10066
  }
@@ -10336,7 +10363,7 @@ class H37 extends Rule {
10336
10363
  const defaults$1 = {
10337
10364
  strict: false
10338
10365
  };
10339
- const { enum: validScopes } = html5.th.attributes?.scope;
10366
+ const { enum: validScopes } = html5.th.attributes.scope;
10340
10367
  const joinedScopes = naturalJoin(validScopes);
10341
10368
  const td = 0;
10342
10369
  const th = 1;
@@ -10434,7 +10461,7 @@ class H67 extends Rule {
10434
10461
  setup() {
10435
10462
  this.on("tag:end", (event) => {
10436
10463
  const node = event.target;
10437
- if (!node || node.tagName !== "img") {
10464
+ if (node?.tagName !== "img") {
10438
10465
  return;
10439
10466
  }
10440
10467
  const title = node.getAttribute("title");
@@ -11878,7 +11905,7 @@ class EventHandler {
11878
11905
  }
11879
11906
 
11880
11907
  const name = "html-validate";
11881
- const version = "10.1.2";
11908
+ const version = "10.2.1";
11882
11909
  const bugs = "https://gitlab.com/html-validate/html-validate/issues/new";
11883
11910
 
11884
11911
  function freeze(src) {
@@ -12611,9 +12638,9 @@ class Parser {
12611
12638
  * Trigger close events for any still open elements.
12612
12639
  */
12613
12640
  closeTree(source, location) {
12614
- let active;
12615
12641
  const documentElement = this.dom.root;
12616
- while ((active = this.dom.getActive()) && !active.isRootElement()) {
12642
+ let active = this.dom.getActive();
12643
+ while (!active.isRootElement()) {
12617
12644
  if (active.meta?.implicitClosed) {
12618
12645
  active.closed = NodeClosed.ImplicitClosed;
12619
12646
  this.closeElement(source, documentElement, active, location);
@@ -12621,6 +12648,7 @@ class Parser {
12621
12648
  this.closeElement(source, null, active, location);
12622
12649
  }
12623
12650
  this.dom.popActive();
12651
+ active = this.dom.getActive();
12624
12652
  }
12625
12653
  }
12626
12654
  }
@@ -12987,8 +13015,8 @@ function getUnnamedTransformerFromPlugin(name, plugin) {
12987
13015
  throw new ConfigError(`Plugin does not expose any transformers`);
12988
13016
  }
12989
13017
  if (typeof plugin.transformer !== "function") {
12990
- if (plugin.transformer.default) {
12991
- return plugin.transformer.default;
13018
+ if (plugin.transformer["default"]) {
13019
+ return plugin.transformer["default"];
12992
13020
  }
12993
13021
  throw new ConfigError(
12994
13022
  `Transformer "${name}" refers to unnamed transformer but plugin exposes only named.`
@@ -13217,7 +13245,7 @@ function checkstyleFormatter(results) {
13217
13245
  output += "</checkstyle>\n";
13218
13246
  return output;
13219
13247
  }
13220
- const formatter$3 = checkstyleFormatter;
13248
+ const formatter$2 = checkstyleFormatter;
13221
13249
 
13222
13250
  const defaults = {
13223
13251
  showLink: true,
@@ -13392,7 +13420,7 @@ function codeframe(results, options) {
13392
13420
  function jsonFormatter(results) {
13393
13421
  return JSON.stringify(results);
13394
13422
  }
13395
- const formatter$2 = jsonFormatter;
13423
+ const formatter$1 = jsonFormatter;
13396
13424
 
13397
13425
  function linkSummary(results) {
13398
13426
  const urls = results.reduce((result, it) => {
@@ -13421,7 +13449,6 @@ function stylish(results) {
13421
13449
  const links = linkSummary(results);
13422
13450
  return `${errors}${links}`;
13423
13451
  }
13424
- const formatter$1 = stylish;
13425
13452
 
13426
13453
  function textFormatter(results) {
13427
13454
  let output = "";
@@ -13451,10 +13478,10 @@ function textFormatter(results) {
13451
13478
  const formatter = textFormatter;
13452
13479
 
13453
13480
  const availableFormatters = {
13454
- checkstyle: formatter$3,
13481
+ checkstyle: formatter$2,
13455
13482
  codeframe,
13456
- json: formatter$2,
13457
- stylish: formatter$1,
13483
+ json: formatter$1,
13484
+ stylish,
13458
13485
  text: formatter
13459
13486
  };
13460
13487
  function getFormatter(name) {