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/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
|
-
|
|
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
|
-
}
|
|
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
|
|
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]
|
|
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
|
-
|
|
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
|
|
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 (
|
|
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
|
-
|
|
4368
|
-
|
|
4369
|
-
|
|
4370
|
-
|
|
4371
|
-
|
|
4372
|
-
|
|
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
|
-
|
|
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
|
-
|
|
4411
|
+
const allowsNaming = ariaNaming(target) === "allowed";
|
|
4412
|
+
if (allowsNaming && this.options.allowAnyNamable) {
|
|
4396
4413
|
return;
|
|
4397
4414
|
}
|
|
4398
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
6227
|
-
description:
|
|
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(
|
|
8237
|
-
tagName = tagName || "element";
|
|
8264
|
+
documentation(context) {
|
|
8238
8265
|
return {
|
|
8239
|
-
description: `Self-closing elements are disallowed. Use regular end tag <${
|
|
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 (
|
|
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(
|
|
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 (
|
|
10036
|
-
doc.description = `<${
|
|
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
|
|
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 (
|
|
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
|
|
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
|
-
|
|
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
|
|
12991
|
-
return plugin.transformer
|
|
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$
|
|
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$
|
|
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$
|
|
13481
|
+
checkstyle: formatter$2,
|
|
13455
13482
|
codeframe,
|
|
13456
|
-
json: formatter$
|
|
13457
|
-
stylish
|
|
13483
|
+
json: formatter$1,
|
|
13484
|
+
stylish,
|
|
13458
13485
|
text: formatter
|
|
13459
13486
|
};
|
|
13460
13487
|
function getFormatter(name) {
|