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/cli.js +2 -2
- package/dist/cjs/cli.js.map +1 -1
- package/dist/cjs/core-nodejs.js +3 -3
- package/dist/cjs/core-nodejs.js.map +1 -1
- package/dist/cjs/core.js +82 -55
- package/dist/cjs/core.js.map +1 -1
- package/dist/cjs/elements.js +2 -2
- package/dist/cjs/elements.js.map +1 -1
- package/dist/cjs/html-validate.js +1 -1
- package/dist/cjs/html-validate.js.map +1 -1
- package/dist/cjs/jest-diff.js +2 -0
- package/dist/cjs/jest-diff.js.map +1 -1
- package/dist/cjs/jest-worker.js.map +1 -1
- package/dist/cjs/meta-helper.js +4 -4
- package/dist/cjs/meta-helper.js.map +1 -1
- package/dist/cjs/tsdoc-metadata.json +1 -1
- package/dist/esm/cli.js +2 -2
- package/dist/esm/cli.js.map +1 -1
- package/dist/esm/core-nodejs.js +3 -3
- package/dist/esm/core-nodejs.js.map +1 -1
- package/dist/esm/core.js +82 -55
- package/dist/esm/core.js.map +1 -1
- package/dist/esm/elements.js +3 -3
- package/dist/esm/elements.js.map +1 -1
- package/dist/esm/html-validate.js +1 -1
- package/dist/esm/html-validate.js.map +1 -1
- package/dist/esm/jest-diff.js +2 -0
- package/dist/esm/jest-diff.js.map +1 -1
- package/dist/esm/jest-worker.js.map +1 -1
- package/dist/esm/meta-helper.js +4 -4
- package/dist/esm/meta-helper.js.map +1 -1
- package/dist/tsdoc-metadata.json +1 -1
- package/dist/types/browser.d.ts +1 -1
- package/dist/types/index.d.ts +1 -1
- package/package.json +2 -2
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
|
-
|
|
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
|
-
}
|
|
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
|
|
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]
|
|
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
|
-
|
|
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
|
|
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 (
|
|
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
|
-
|
|
4377
|
-
|
|
4378
|
-
|
|
4379
|
-
|
|
4380
|
-
|
|
4381
|
-
|
|
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
|
-
|
|
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
|
-
|
|
4420
|
+
const allowsNaming = ariaNaming(target) === "allowed";
|
|
4421
|
+
if (allowsNaming && this.options.allowAnyNamable) {
|
|
4405
4422
|
return;
|
|
4406
4423
|
}
|
|
4407
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
6236
|
-
description:
|
|
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(
|
|
8246
|
-
tagName = tagName || "element";
|
|
8273
|
+
documentation(context) {
|
|
8247
8274
|
return {
|
|
8248
|
-
description: `Self-closing elements are disallowed. Use regular end tag <${
|
|
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 (
|
|
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(
|
|
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 (
|
|
10045
|
-
doc.description = `<${
|
|
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
|
|
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 (
|
|
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
|
|
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
|
-
|
|
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
|
|
13000
|
-
return plugin.transformer
|
|
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$
|
|
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$
|
|
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$
|
|
13490
|
+
checkstyle: formatter$2,
|
|
13464
13491
|
codeframe,
|
|
13465
|
-
json: formatter$
|
|
13466
|
-
stylish
|
|
13492
|
+
json: formatter$1,
|
|
13493
|
+
stylish,
|
|
13467
13494
|
text: formatter
|
|
13468
13495
|
};
|
|
13469
13496
|
function getFormatter(name) {
|