html-validate 8.18.2 → 8.19.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 +88 -65
- package/dist/cjs/core.js.map +1 -1
- package/dist/cjs/tsdoc-metadata.json +1 -1
- package/dist/es/core.js +88 -65
- package/dist/es/core.js.map +1 -1
- package/dist/tsdoc-metadata.json +1 -1
- package/package.json +15 -15
package/dist/es/core.js
CHANGED
|
@@ -5,7 +5,7 @@ import { n as naturalJoin } from './utils/natural-join.js';
|
|
|
5
5
|
import fs from 'fs';
|
|
6
6
|
import { codeFrameColumns } from '@babel/code-frame';
|
|
7
7
|
import kleur from 'kleur';
|
|
8
|
-
import { stylish as stylish$
|
|
8
|
+
import { stylish as stylish$1 } from '@html-validate/stylish';
|
|
9
9
|
import semver from 'semver';
|
|
10
10
|
|
|
11
11
|
const $schema$2 = "http://json-schema.org/draft-06/schema#";
|
|
@@ -6214,6 +6214,7 @@ class EmptyTitle extends Rule {
|
|
|
6214
6214
|
|
|
6215
6215
|
const defaults$k = {
|
|
6216
6216
|
allowArrayBrackets: true,
|
|
6217
|
+
allowCheckboxDefault: true,
|
|
6217
6218
|
shared: ["radio", "button", "reset", "submit"]
|
|
6218
6219
|
};
|
|
6219
6220
|
const UNIQUE_CACHE_KEY = Symbol("form-elements-unique");
|
|
@@ -6225,6 +6226,25 @@ function allowSharedName(node, shared) {
|
|
|
6225
6226
|
const type = node.getAttribute("type");
|
|
6226
6227
|
return Boolean(type == null ? void 0 : type.valueMatches(shared, false));
|
|
6227
6228
|
}
|
|
6229
|
+
function isInputHidden(element) {
|
|
6230
|
+
return element.is("input") && element.getAttributeValue("type") === "hidden";
|
|
6231
|
+
}
|
|
6232
|
+
function isInputCheckbox(element) {
|
|
6233
|
+
return element.is("input") && element.getAttributeValue("type") === "checkbox";
|
|
6234
|
+
}
|
|
6235
|
+
function isCheckboxWithDefault(control, previous, options) {
|
|
6236
|
+
const { allowCheckboxDefault } = options;
|
|
6237
|
+
if (!allowCheckboxDefault) {
|
|
6238
|
+
return false;
|
|
6239
|
+
}
|
|
6240
|
+
if (!previous.potentialHiddenDefault) {
|
|
6241
|
+
return false;
|
|
6242
|
+
}
|
|
6243
|
+
if (!isInputCheckbox(control)) {
|
|
6244
|
+
return false;
|
|
6245
|
+
}
|
|
6246
|
+
return true;
|
|
6247
|
+
}
|
|
6228
6248
|
function getDocumentation(context) {
|
|
6229
6249
|
const trailer = "Each form control must have a unique name.";
|
|
6230
6250
|
const { name } = context;
|
|
@@ -6247,6 +6267,9 @@ class FormDupName extends Rule {
|
|
|
6247
6267
|
allowArrayBrackets: {
|
|
6248
6268
|
type: "boolean"
|
|
6249
6269
|
},
|
|
6270
|
+
allowCheckboxDefault: {
|
|
6271
|
+
type: "boolean"
|
|
6272
|
+
},
|
|
6250
6273
|
shared: {
|
|
6251
6274
|
type: "array",
|
|
6252
6275
|
items: {
|
|
@@ -6312,14 +6335,20 @@ class FormDupName extends Rule {
|
|
|
6312
6335
|
}
|
|
6313
6336
|
if (!details && isarray) {
|
|
6314
6337
|
elements.set(basename, {
|
|
6315
|
-
array: true
|
|
6338
|
+
array: true,
|
|
6339
|
+
potentialHiddenDefault: false
|
|
6316
6340
|
});
|
|
6317
6341
|
}
|
|
6318
6342
|
if (isarray) {
|
|
6319
6343
|
return;
|
|
6320
6344
|
}
|
|
6321
6345
|
}
|
|
6322
|
-
|
|
6346
|
+
const previous = elements.get(name);
|
|
6347
|
+
if (previous) {
|
|
6348
|
+
if (isCheckboxWithDefault(control, previous, this.options)) {
|
|
6349
|
+
previous.potentialHiddenDefault = false;
|
|
6350
|
+
return;
|
|
6351
|
+
}
|
|
6323
6352
|
const context = {
|
|
6324
6353
|
name,
|
|
6325
6354
|
kind: "duplicate"
|
|
@@ -6332,7 +6361,8 @@ class FormDupName extends Rule {
|
|
|
6332
6361
|
});
|
|
6333
6362
|
} else {
|
|
6334
6363
|
elements.set(name, {
|
|
6335
|
-
array: false
|
|
6364
|
+
array: false,
|
|
6365
|
+
potentialHiddenDefault: isInputHidden(control)
|
|
6336
6366
|
});
|
|
6337
6367
|
}
|
|
6338
6368
|
}
|
|
@@ -7186,20 +7216,23 @@ class MultipleLabeledControls extends Rule {
|
|
|
7186
7216
|
}
|
|
7187
7217
|
setup() {
|
|
7188
7218
|
this.labelable = this.getTagsWithProperty("labelable").join(",");
|
|
7189
|
-
this.on("
|
|
7190
|
-
const {
|
|
7191
|
-
|
|
7192
|
-
|
|
7193
|
-
|
|
7194
|
-
|
|
7195
|
-
|
|
7196
|
-
|
|
7219
|
+
this.on("dom:ready", (event) => {
|
|
7220
|
+
const { document } = event;
|
|
7221
|
+
const labels = document.querySelectorAll("label");
|
|
7222
|
+
for (const label of labels) {
|
|
7223
|
+
const numControls = this.getNumLabledControls(label);
|
|
7224
|
+
if (numControls <= 1) {
|
|
7225
|
+
continue;
|
|
7226
|
+
}
|
|
7227
|
+
this.report(label, "<label> is associated with multiple controls", label.location);
|
|
7197
7228
|
}
|
|
7198
|
-
this.report(target, "<label> is associated with multiple controls", target.location);
|
|
7199
7229
|
});
|
|
7200
7230
|
}
|
|
7201
7231
|
getNumLabledControls(src) {
|
|
7202
|
-
const controls = src.querySelectorAll(this.labelable).
|
|
7232
|
+
const controls = src.querySelectorAll(this.labelable).filter((node) => {
|
|
7233
|
+
var _a;
|
|
7234
|
+
return (_a = node.meta) == null ? void 0 : _a.labelable;
|
|
7235
|
+
}).map((node) => node.id);
|
|
7203
7236
|
const attr = src.getAttribute("for");
|
|
7204
7237
|
if (!attr || attr.isDynamic || !attr.value) {
|
|
7205
7238
|
return controls.length;
|
|
@@ -9743,7 +9776,7 @@ class ValidID extends Rule {
|
|
|
9743
9776
|
const message = this.messages[context].replace("id", "ID").replace(/^(.)/, (m) => m.toUpperCase());
|
|
9744
9777
|
const relaxedDescription = relaxed ? [] : [
|
|
9745
9778
|
" - ID must begin with a letter",
|
|
9746
|
-
" - ID must only contain
|
|
9779
|
+
" - ID must only contain letters, digits, `-` and `_`"
|
|
9747
9780
|
];
|
|
9748
9781
|
return {
|
|
9749
9782
|
description: [
|
|
@@ -9778,12 +9811,12 @@ class ValidID extends Rule {
|
|
|
9778
9811
|
if (relaxed) {
|
|
9779
9812
|
return;
|
|
9780
9813
|
}
|
|
9781
|
-
if (value.match(/^[
|
|
9814
|
+
if (value.match(/^[^\p{L}]/u)) {
|
|
9782
9815
|
const context = 3 /* LEADING_CHARACTER */;
|
|
9783
9816
|
this.report(event.target, this.messages[context], event.valueLocation, context);
|
|
9784
9817
|
return;
|
|
9785
9818
|
}
|
|
9786
|
-
if (value.match(/[
|
|
9819
|
+
if (value.match(/[^\p{L}\p{N}_-]/u)) {
|
|
9787
9820
|
const context = 4 /* DISALLOWED_CHARACTER */;
|
|
9788
9821
|
this.report(event.target, this.messages[context], event.valueLocation, context);
|
|
9789
9822
|
}
|
|
@@ -9794,7 +9827,7 @@ class ValidID extends Rule {
|
|
|
9794
9827
|
[1 /* EMPTY */]: "element id must not be empty",
|
|
9795
9828
|
[2 /* WHITESPACE */]: "element id must not contain whitespace",
|
|
9796
9829
|
[3 /* LEADING_CHARACTER */]: "element id must begin with a letter",
|
|
9797
|
-
[4 /* DISALLOWED_CHARACTER */]: "element id must only contain
|
|
9830
|
+
[4 /* DISALLOWED_CHARACTER */]: "element id must only contain letters, digits, dash and underscore characters"
|
|
9798
9831
|
};
|
|
9799
9832
|
}
|
|
9800
9833
|
isRelevant(event) {
|
|
@@ -9921,7 +9954,7 @@ function styleDescription(style) {
|
|
|
9921
9954
|
class H30 extends Rule {
|
|
9922
9955
|
documentation() {
|
|
9923
9956
|
return {
|
|
9924
|
-
description: "WCAG 2.1 requires each `<a>` anchor link to have a text describing the purpose of the link using either plain text or an `<img>` with the `alt` attribute set.",
|
|
9957
|
+
description: "WCAG 2.1 requires each `<a href>` anchor link to have a text describing the purpose of the link using either plain text or an `<img>` with the `alt` attribute set.",
|
|
9925
9958
|
url: "https://html-validate.org/rules/wcag/h30.html"
|
|
9926
9959
|
};
|
|
9927
9960
|
}
|
|
@@ -9929,6 +9962,9 @@ class H30 extends Rule {
|
|
|
9929
9962
|
this.on("dom:ready", (event) => {
|
|
9930
9963
|
const links = event.document.getElementsByTagName("a");
|
|
9931
9964
|
for (const link of links) {
|
|
9965
|
+
if (!link.hasAttribute("href")) {
|
|
9966
|
+
continue;
|
|
9967
|
+
}
|
|
9932
9968
|
if (!inAccessibilityTree(link)) {
|
|
9933
9969
|
continue;
|
|
9934
9970
|
}
|
|
@@ -9999,7 +10035,10 @@ function hasAssociatedSubmit(document, form) {
|
|
|
9999
10035
|
class H36 extends Rule {
|
|
10000
10036
|
documentation() {
|
|
10001
10037
|
return {
|
|
10002
|
-
description:
|
|
10038
|
+
description: [
|
|
10039
|
+
"WCAG 2.1 requires all images used as submit buttons to have a non-empty textual description using the `alt` attribute.",
|
|
10040
|
+
'The alt text cannot be empty (`alt=""`).'
|
|
10041
|
+
].join("\n"),
|
|
10003
10042
|
url: "https://html-validate.org/rules/wcag/h36.html"
|
|
10004
10043
|
};
|
|
10005
10044
|
}
|
|
@@ -10011,8 +10050,17 @@ class H36 extends Rule {
|
|
|
10011
10050
|
if (node.getAttributeValue("type") !== "image") {
|
|
10012
10051
|
return;
|
|
10013
10052
|
}
|
|
10053
|
+
if (!inAccessibilityTree(node)) {
|
|
10054
|
+
return;
|
|
10055
|
+
}
|
|
10014
10056
|
if (!hasAltText(node)) {
|
|
10015
|
-
|
|
10057
|
+
const message = "image used as submit button must have non-empty alt text";
|
|
10058
|
+
const alt = node.getAttribute("alt");
|
|
10059
|
+
this.report({
|
|
10060
|
+
node,
|
|
10061
|
+
message,
|
|
10062
|
+
location: alt ? alt.keyLocation : node.location
|
|
10063
|
+
});
|
|
10016
10064
|
}
|
|
10017
10065
|
});
|
|
10018
10066
|
}
|
|
@@ -10022,19 +10070,6 @@ const defaults$1 = {
|
|
|
10022
10070
|
allowEmpty: true,
|
|
10023
10071
|
alias: []
|
|
10024
10072
|
};
|
|
10025
|
-
function needsAlt(node) {
|
|
10026
|
-
if (node.is("img")) {
|
|
10027
|
-
return true;
|
|
10028
|
-
}
|
|
10029
|
-
if (node.is("input") && node.getAttributeValue("type") === "image") {
|
|
10030
|
-
return true;
|
|
10031
|
-
}
|
|
10032
|
-
return false;
|
|
10033
|
-
}
|
|
10034
|
-
function getTag(node) {
|
|
10035
|
-
return node.is("input") ? `<input type="${/* istanbul ignore next */
|
|
10036
|
-
node.getAttributeValue("type") ?? ""}">` : `<${node.tagName}>`;
|
|
10037
|
-
}
|
|
10038
10073
|
class H37 extends Rule {
|
|
10039
10074
|
constructor(options) {
|
|
10040
10075
|
super({ ...defaults$1, ...options });
|
|
@@ -10071,16 +10106,13 @@ class H37 extends Rule {
|
|
|
10071
10106
|
setup() {
|
|
10072
10107
|
this.on("dom:ready", (event) => {
|
|
10073
10108
|
const { document } = event;
|
|
10074
|
-
const nodes = document.querySelectorAll("img
|
|
10109
|
+
const nodes = document.querySelectorAll("img");
|
|
10075
10110
|
for (const node of nodes) {
|
|
10076
10111
|
this.validateNode(node);
|
|
10077
10112
|
}
|
|
10078
10113
|
});
|
|
10079
10114
|
}
|
|
10080
10115
|
validateNode(node) {
|
|
10081
|
-
if (!needsAlt(node)) {
|
|
10082
|
-
return;
|
|
10083
|
-
}
|
|
10084
10116
|
if (!inAccessibilityTree(node)) {
|
|
10085
10117
|
return;
|
|
10086
10118
|
}
|
|
@@ -10092,11 +10124,12 @@ class H37 extends Rule {
|
|
|
10092
10124
|
return;
|
|
10093
10125
|
}
|
|
10094
10126
|
}
|
|
10127
|
+
const tag = node.annotatedName;
|
|
10095
10128
|
if (node.hasAttribute("alt")) {
|
|
10096
10129
|
const attr = node.getAttribute("alt");
|
|
10097
|
-
this.report(node, `${
|
|
10130
|
+
this.report(node, `${tag} cannot have empty "alt" attribute`, attr.keyLocation);
|
|
10098
10131
|
} else {
|
|
10099
|
-
this.report(node, `${
|
|
10132
|
+
this.report(node, `${tag} is missing required "alt" attribute`, node.location);
|
|
10100
10133
|
}
|
|
10101
10134
|
}
|
|
10102
10135
|
}
|
|
@@ -10197,7 +10230,6 @@ const bundledRules$1 = {
|
|
|
10197
10230
|
"wcag/h67": H67,
|
|
10198
10231
|
"wcag/h71": H71
|
|
10199
10232
|
};
|
|
10200
|
-
var WCAG = bundledRules$1;
|
|
10201
10233
|
|
|
10202
10234
|
const bundledRules = {
|
|
10203
10235
|
"allowed-links": AllowedLinks,
|
|
@@ -10283,7 +10315,7 @@ const bundledRules = {
|
|
|
10283
10315
|
"valid-id": ValidID,
|
|
10284
10316
|
"void-content": VoidContent,
|
|
10285
10317
|
"void-style": VoidStyle,
|
|
10286
|
-
...
|
|
10318
|
+
...bundledRules$1
|
|
10287
10319
|
};
|
|
10288
10320
|
|
|
10289
10321
|
var defaultConfig = {};
|
|
@@ -10320,7 +10352,6 @@ const config$4 = {
|
|
|
10320
10352
|
"wcag/h71": "error"
|
|
10321
10353
|
}
|
|
10322
10354
|
};
|
|
10323
|
-
var a11y = config$4;
|
|
10324
10355
|
|
|
10325
10356
|
const config$3 = {
|
|
10326
10357
|
rules: {
|
|
@@ -10331,7 +10362,6 @@ const config$3 = {
|
|
|
10331
10362
|
"require-sri": "error"
|
|
10332
10363
|
}
|
|
10333
10364
|
};
|
|
10334
|
-
var document = config$3;
|
|
10335
10365
|
|
|
10336
10366
|
const config$2 = {
|
|
10337
10367
|
rules: {
|
|
@@ -10340,7 +10370,6 @@ const config$2 = {
|
|
|
10340
10370
|
"void-style": "off"
|
|
10341
10371
|
}
|
|
10342
10372
|
};
|
|
10343
|
-
var prettier = config$2;
|
|
10344
10373
|
|
|
10345
10374
|
const config$1 = {
|
|
10346
10375
|
rules: {
|
|
@@ -10424,7 +10453,6 @@ const config$1 = {
|
|
|
10424
10453
|
"wcag/h71": "error"
|
|
10425
10454
|
}
|
|
10426
10455
|
};
|
|
10427
|
-
var recommended = config$1;
|
|
10428
10456
|
|
|
10429
10457
|
const config = {
|
|
10430
10458
|
rules: {
|
|
@@ -10463,18 +10491,17 @@ const config = {
|
|
|
10463
10491
|
"void-content": "error"
|
|
10464
10492
|
}
|
|
10465
10493
|
};
|
|
10466
|
-
var standard = config;
|
|
10467
10494
|
|
|
10468
10495
|
const presets = {
|
|
10469
|
-
"html-validate:a11y":
|
|
10470
|
-
"html-validate:document":
|
|
10471
|
-
"html-validate:prettier":
|
|
10472
|
-
"html-validate:recommended":
|
|
10473
|
-
"html-validate:standard":
|
|
10496
|
+
"html-validate:a11y": config$4,
|
|
10497
|
+
"html-validate:document": config$3,
|
|
10498
|
+
"html-validate:prettier": config$2,
|
|
10499
|
+
"html-validate:recommended": config$1,
|
|
10500
|
+
"html-validate:standard": config,
|
|
10474
10501
|
/* @deprecated aliases */
|
|
10475
|
-
"htmlvalidate:recommended":
|
|
10476
|
-
"htmlvalidate:document":
|
|
10477
|
-
"html-validate:a17y":
|
|
10502
|
+
"htmlvalidate:recommended": config$1,
|
|
10503
|
+
"htmlvalidate:document": config$3,
|
|
10504
|
+
"html-validate:a17y": config$4
|
|
10478
10505
|
};
|
|
10479
10506
|
var Presets = presets;
|
|
10480
10507
|
|
|
@@ -12689,7 +12716,7 @@ class HtmlValidate {
|
|
|
12689
12716
|
}
|
|
12690
12717
|
|
|
12691
12718
|
const name = "html-validate";
|
|
12692
|
-
const version = "8.
|
|
12719
|
+
const version = "8.19.1";
|
|
12693
12720
|
const bugs = "https://gitlab.com/html-validate/html-validate/issues/new";
|
|
12694
12721
|
|
|
12695
12722
|
function definePlugin(plugin) {
|
|
@@ -13375,7 +13402,6 @@ function checkstyleFormatter(results) {
|
|
|
13375
13402
|
return output;
|
|
13376
13403
|
}
|
|
13377
13404
|
const formatter$3 = checkstyleFormatter;
|
|
13378
|
-
var checkstyle = formatter$3;
|
|
13379
13405
|
|
|
13380
13406
|
const defaults = {
|
|
13381
13407
|
showLink: true,
|
|
@@ -13481,7 +13507,6 @@ function jsonFormatter(results) {
|
|
|
13481
13507
|
return JSON.stringify(results);
|
|
13482
13508
|
}
|
|
13483
13509
|
const formatter$2 = jsonFormatter;
|
|
13484
|
-
var json = formatter$2;
|
|
13485
13510
|
|
|
13486
13511
|
function linkSummary(results) {
|
|
13487
13512
|
const urls = results.reduce((result, it) => {
|
|
@@ -13500,7 +13525,7 @@ ${lines.join("")}
|
|
|
13500
13525
|
`;
|
|
13501
13526
|
}
|
|
13502
13527
|
function stylish(results) {
|
|
13503
|
-
const errors = stylish$
|
|
13528
|
+
const errors = stylish$1(
|
|
13504
13529
|
results.map((it) => ({
|
|
13505
13530
|
...it,
|
|
13506
13531
|
fixableErrorCount: 0,
|
|
@@ -13511,7 +13536,6 @@ function stylish(results) {
|
|
|
13511
13536
|
return `${errors}${links}`;
|
|
13512
13537
|
}
|
|
13513
13538
|
const formatter$1 = stylish;
|
|
13514
|
-
var stylish$1 = formatter$1;
|
|
13515
13539
|
|
|
13516
13540
|
function textFormatter(results) {
|
|
13517
13541
|
let output = "";
|
|
@@ -13539,14 +13563,13 @@ function textFormatter(results) {
|
|
|
13539
13563
|
return total > 0 ? output : "";
|
|
13540
13564
|
}
|
|
13541
13565
|
const formatter = textFormatter;
|
|
13542
|
-
var text = formatter;
|
|
13543
13566
|
|
|
13544
13567
|
const availableFormatters = {
|
|
13545
|
-
checkstyle,
|
|
13568
|
+
checkstyle: formatter$3,
|
|
13546
13569
|
codeframe,
|
|
13547
|
-
json,
|
|
13548
|
-
stylish:
|
|
13549
|
-
text
|
|
13570
|
+
json: formatter$2,
|
|
13571
|
+
stylish: formatter$1,
|
|
13572
|
+
text: formatter
|
|
13550
13573
|
};
|
|
13551
13574
|
function getFormatter(name) {
|
|
13552
13575
|
return availableFormatters[name] ?? null;
|