html-validate 8.15.0 → 8.17.0
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 +271 -110
- package/dist/cjs/core.js.map +1 -1
- package/dist/cjs/elements.js +13 -0
- package/dist/cjs/elements.js.map +1 -1
- package/dist/es/core.js +271 -110
- package/dist/es/core.js.map +1 -1
- package/dist/es/elements.js +13 -0
- package/dist/es/elements.js.map +1 -1
- package/dist/schema/elements.json +4 -0
- package/dist/types/browser.d.ts +19 -0
- package/dist/types/index.d.ts +19 -0
- package/package.json +4 -4
package/dist/cjs/core.js
CHANGED
|
@@ -811,6 +811,10 @@ const definitions = {
|
|
|
811
811
|
type: "object",
|
|
812
812
|
additionalProperties: false,
|
|
813
813
|
properties: {
|
|
814
|
+
disablable: {
|
|
815
|
+
type: "boolean",
|
|
816
|
+
title: "Disablable elements can be disabled using the disabled attribute."
|
|
817
|
+
},
|
|
814
818
|
listed: {
|
|
815
819
|
type: "boolean",
|
|
816
820
|
title: "Listed elements have a name attribute and is listed in the form and fieldset elements property."
|
|
@@ -1136,6 +1140,7 @@ function migrateElement(src) {
|
|
|
1136
1140
|
}
|
|
1137
1141
|
if (src.formAssociated) {
|
|
1138
1142
|
result.formAssociated = {
|
|
1143
|
+
disablable: Boolean(src.formAssociated.disablable),
|
|
1139
1144
|
listed: Boolean(src.formAssociated.listed)
|
|
1140
1145
|
};
|
|
1141
1146
|
} else {
|
|
@@ -1395,10 +1400,14 @@ function expandRegexValue(value) {
|
|
|
1395
1400
|
if (value instanceof RegExp) {
|
|
1396
1401
|
return value;
|
|
1397
1402
|
}
|
|
1398
|
-
const match = value.match(
|
|
1403
|
+
const match = value.match(/^\/(.*(?=\/))\/(i?)$/);
|
|
1399
1404
|
if (match) {
|
|
1400
1405
|
const [, expr, flags] = match;
|
|
1401
|
-
|
|
1406
|
+
if (expr.startsWith("^") || expr.endsWith("$")) {
|
|
1407
|
+
return new RegExp(expr, flags);
|
|
1408
|
+
} else {
|
|
1409
|
+
return new RegExp(`^${expr}$`, flags);
|
|
1410
|
+
}
|
|
1402
1411
|
} else {
|
|
1403
1412
|
return value;
|
|
1404
1413
|
}
|
|
@@ -2300,6 +2309,7 @@ class TextNode extends DOMNode {
|
|
|
2300
2309
|
}
|
|
2301
2310
|
|
|
2302
2311
|
const ROLE = Symbol("role");
|
|
2312
|
+
const TABINDEX = Symbol("tabindex");
|
|
2303
2313
|
var NodeClosed = /* @__PURE__ */ ((NodeClosed2) => {
|
|
2304
2314
|
NodeClosed2[NodeClosed2["Open"] = 0] = "Open";
|
|
2305
2315
|
NodeClosed2[NodeClosed2["EndTag"] = 1] = "EndTag";
|
|
@@ -2580,6 +2590,43 @@ class HtmlElement extends DOMNode {
|
|
|
2580
2590
|
}
|
|
2581
2591
|
this.attr[key].push(new Attribute(key, value, keyLocation, valueLocation, originalAttribute));
|
|
2582
2592
|
}
|
|
2593
|
+
/**
|
|
2594
|
+
* Get parsed tabindex for this element.
|
|
2595
|
+
*
|
|
2596
|
+
* - If `tabindex` attribute is not present `null` is returned.
|
|
2597
|
+
* - If attribute value is omitted or the empty string `null` is returned.
|
|
2598
|
+
* - If attribute value cannot be parsed `null` is returned.
|
|
2599
|
+
* - If attribute value is dynamic `0` is returned.
|
|
2600
|
+
* - Otherwise the parsed value is returned.
|
|
2601
|
+
*
|
|
2602
|
+
* This property does *NOT* take into account if the element have a default
|
|
2603
|
+
* `tabindex` (such as `<input>` have). Instead use the `focusable` metadata
|
|
2604
|
+
* property to determine this.
|
|
2605
|
+
*
|
|
2606
|
+
* @public
|
|
2607
|
+
* @since 8.16.0
|
|
2608
|
+
*/
|
|
2609
|
+
get tabIndex() {
|
|
2610
|
+
const cached = this.cacheGet(TABINDEX);
|
|
2611
|
+
if (cached !== void 0) {
|
|
2612
|
+
return cached;
|
|
2613
|
+
}
|
|
2614
|
+
const tabindex = this.getAttribute("tabindex");
|
|
2615
|
+
if (!tabindex) {
|
|
2616
|
+
return this.cacheSet(TABINDEX, null);
|
|
2617
|
+
}
|
|
2618
|
+
if (tabindex.value === null) {
|
|
2619
|
+
return this.cacheSet(TABINDEX, null);
|
|
2620
|
+
}
|
|
2621
|
+
if (tabindex.value instanceof DynamicValue) {
|
|
2622
|
+
return this.cacheSet(TABINDEX, 0);
|
|
2623
|
+
}
|
|
2624
|
+
const parsed = parseInt(tabindex.value, 10);
|
|
2625
|
+
if (isNaN(parsed)) {
|
|
2626
|
+
return this.cacheSet(TABINDEX, null);
|
|
2627
|
+
}
|
|
2628
|
+
return this.cacheSet(TABINDEX, parsed);
|
|
2629
|
+
}
|
|
2583
2630
|
/**
|
|
2584
2631
|
* Get a list of all attributes on this node.
|
|
2585
2632
|
*/
|
|
@@ -3376,6 +3423,7 @@ function isKeywordIgnored(options, keyword, matcher = (list, it) => list.include
|
|
|
3376
3423
|
|
|
3377
3424
|
const ARIA_HIDDEN_CACHE = Symbol(isAriaHidden.name);
|
|
3378
3425
|
const HTML_HIDDEN_CACHE = Symbol(isHTMLHidden.name);
|
|
3426
|
+
const INERT_CACHE = Symbol(isInert.name);
|
|
3379
3427
|
const ROLE_PRESENTATION_CACHE = Symbol(isPresentation.name);
|
|
3380
3428
|
const STYLE_HIDDEN_CACHE = Symbol(isStyleHidden.name);
|
|
3381
3429
|
function inAccessibilityTree(node) {
|
|
@@ -3388,6 +3436,9 @@ function inAccessibilityTree(node) {
|
|
|
3388
3436
|
if (isHTMLHidden(node)) {
|
|
3389
3437
|
return false;
|
|
3390
3438
|
}
|
|
3439
|
+
if (isInert(node)) {
|
|
3440
|
+
return false;
|
|
3441
|
+
}
|
|
3391
3442
|
if (isStyleHidden(node)) {
|
|
3392
3443
|
return false;
|
|
3393
3444
|
}
|
|
@@ -3429,6 +3480,24 @@ function isHTMLHidden(node, details) {
|
|
|
3429
3480
|
const result = node.cacheSet(HTML_HIDDEN_CACHE, isHTMLHiddenImpl(node));
|
|
3430
3481
|
return details ? result : result.byParent || result.bySelf;
|
|
3431
3482
|
}
|
|
3483
|
+
function isInertImpl(node) {
|
|
3484
|
+
const isInert2 = (node2) => {
|
|
3485
|
+
const inert = node2.getAttribute("inert");
|
|
3486
|
+
return inert !== null && inert.isStatic;
|
|
3487
|
+
};
|
|
3488
|
+
return {
|
|
3489
|
+
byParent: node.parent ? isInert2(node.parent) : false,
|
|
3490
|
+
bySelf: isInert2(node)
|
|
3491
|
+
};
|
|
3492
|
+
}
|
|
3493
|
+
function isInert(node, details) {
|
|
3494
|
+
const cached = node.cacheGet(INERT_CACHE);
|
|
3495
|
+
if (cached) {
|
|
3496
|
+
return details ? cached : cached.byParent || cached.bySelf;
|
|
3497
|
+
}
|
|
3498
|
+
const result = node.cacheSet(INERT_CACHE, isInertImpl(node));
|
|
3499
|
+
return details ? result : result.byParent || result.bySelf;
|
|
3500
|
+
}
|
|
3432
3501
|
function isStyleHiddenImpl(node) {
|
|
3433
3502
|
const isHidden = (node2) => {
|
|
3434
3503
|
const style = node2.getAttribute("style");
|
|
@@ -3854,7 +3923,7 @@ class Rule {
|
|
|
3854
3923
|
}
|
|
3855
3924
|
}
|
|
3856
3925
|
|
|
3857
|
-
const defaults$
|
|
3926
|
+
const defaults$x = {
|
|
3858
3927
|
allowExternal: true,
|
|
3859
3928
|
allowRelative: true,
|
|
3860
3929
|
allowAbsolute: true,
|
|
@@ -3895,7 +3964,7 @@ function matchList(value, list) {
|
|
|
3895
3964
|
}
|
|
3896
3965
|
class AllowedLinks extends Rule {
|
|
3897
3966
|
constructor(options) {
|
|
3898
|
-
super({ ...defaults$
|
|
3967
|
+
super({ ...defaults$x, ...options });
|
|
3899
3968
|
this.allowExternal = parseAllow(this.options.allowExternal);
|
|
3900
3969
|
this.allowRelative = parseAllow(this.options.allowRelative);
|
|
3901
3970
|
this.allowAbsolute = parseAllow(this.options.allowAbsolute);
|
|
@@ -4059,7 +4128,7 @@ class AllowedLinks extends Rule {
|
|
|
4059
4128
|
}
|
|
4060
4129
|
}
|
|
4061
4130
|
|
|
4062
|
-
const defaults$
|
|
4131
|
+
const defaults$w = {
|
|
4063
4132
|
accessible: true
|
|
4064
4133
|
};
|
|
4065
4134
|
function findByTarget(target, siblings) {
|
|
@@ -4089,7 +4158,7 @@ function getDescription$1(context) {
|
|
|
4089
4158
|
}
|
|
4090
4159
|
class AreaAlt extends Rule {
|
|
4091
4160
|
constructor(options) {
|
|
4092
|
-
super({ ...defaults$
|
|
4161
|
+
super({ ...defaults$w, ...options });
|
|
4093
4162
|
}
|
|
4094
4163
|
static schema() {
|
|
4095
4164
|
return {
|
|
@@ -4168,7 +4237,7 @@ class AriaHiddenBody extends Rule {
|
|
|
4168
4237
|
}
|
|
4169
4238
|
}
|
|
4170
4239
|
|
|
4171
|
-
const defaults$
|
|
4240
|
+
const defaults$v = {
|
|
4172
4241
|
allowAnyNamable: false
|
|
4173
4242
|
};
|
|
4174
4243
|
const whitelisted = [
|
|
@@ -4210,7 +4279,7 @@ function isValidUsage(target, meta) {
|
|
|
4210
4279
|
}
|
|
4211
4280
|
class AriaLabelMisuse extends Rule {
|
|
4212
4281
|
constructor(options) {
|
|
4213
|
-
super({ ...defaults$
|
|
4282
|
+
super({ ...defaults$v, ...options });
|
|
4214
4283
|
}
|
|
4215
4284
|
documentation() {
|
|
4216
4285
|
const valid = [
|
|
@@ -4320,13 +4389,13 @@ class CaseStyle {
|
|
|
4320
4389
|
}
|
|
4321
4390
|
}
|
|
4322
4391
|
|
|
4323
|
-
const defaults$
|
|
4392
|
+
const defaults$u = {
|
|
4324
4393
|
style: "lowercase",
|
|
4325
4394
|
ignoreForeign: true
|
|
4326
4395
|
};
|
|
4327
4396
|
class AttrCase extends Rule {
|
|
4328
4397
|
constructor(options) {
|
|
4329
|
-
super({ ...defaults$
|
|
4398
|
+
super({ ...defaults$u, ...options });
|
|
4330
4399
|
this.style = new CaseStyle(this.options.style, "attr-case");
|
|
4331
4400
|
}
|
|
4332
4401
|
static schema() {
|
|
@@ -4682,7 +4751,7 @@ class AttrDelimiter extends Rule {
|
|
|
4682
4751
|
}
|
|
4683
4752
|
|
|
4684
4753
|
const DEFAULT_PATTERN = "[a-z0-9-:]+";
|
|
4685
|
-
const defaults$
|
|
4754
|
+
const defaults$t = {
|
|
4686
4755
|
pattern: DEFAULT_PATTERN,
|
|
4687
4756
|
ignoreForeign: true
|
|
4688
4757
|
};
|
|
@@ -4714,7 +4783,7 @@ function generateDescription(name, pattern) {
|
|
|
4714
4783
|
}
|
|
4715
4784
|
class AttrPattern extends Rule {
|
|
4716
4785
|
constructor(options) {
|
|
4717
|
-
super({ ...defaults$
|
|
4786
|
+
super({ ...defaults$t, ...options });
|
|
4718
4787
|
this.pattern = generateRegexp(this.options.pattern);
|
|
4719
4788
|
}
|
|
4720
4789
|
static schema() {
|
|
@@ -4761,7 +4830,7 @@ class AttrPattern extends Rule {
|
|
|
4761
4830
|
}
|
|
4762
4831
|
}
|
|
4763
4832
|
|
|
4764
|
-
const defaults$
|
|
4833
|
+
const defaults$s = {
|
|
4765
4834
|
style: "auto",
|
|
4766
4835
|
unquoted: false
|
|
4767
4836
|
};
|
|
@@ -4800,7 +4869,7 @@ function describeStyle(style, unquoted) {
|
|
|
4800
4869
|
}
|
|
4801
4870
|
class AttrQuotes extends Rule {
|
|
4802
4871
|
constructor(options) {
|
|
4803
|
-
super({ ...defaults$
|
|
4872
|
+
super({ ...defaults$s, ...options });
|
|
4804
4873
|
this.style = parseStyle$3(this.options.style);
|
|
4805
4874
|
}
|
|
4806
4875
|
static schema() {
|
|
@@ -4984,12 +5053,12 @@ class AttributeAllowedValues extends Rule {
|
|
|
4984
5053
|
}
|
|
4985
5054
|
}
|
|
4986
5055
|
|
|
4987
|
-
const defaults$
|
|
5056
|
+
const defaults$r = {
|
|
4988
5057
|
style: "omit"
|
|
4989
5058
|
};
|
|
4990
5059
|
class AttributeBooleanStyle extends Rule {
|
|
4991
5060
|
constructor(options) {
|
|
4992
|
-
super({ ...defaults$
|
|
5061
|
+
super({ ...defaults$r, ...options });
|
|
4993
5062
|
this.hasInvalidStyle = parseStyle$2(this.options.style);
|
|
4994
5063
|
}
|
|
4995
5064
|
static schema() {
|
|
@@ -5056,12 +5125,12 @@ function reportMessage$1(attr, style) {
|
|
|
5056
5125
|
return "";
|
|
5057
5126
|
}
|
|
5058
5127
|
|
|
5059
|
-
const defaults$
|
|
5128
|
+
const defaults$q = {
|
|
5060
5129
|
style: "omit"
|
|
5061
5130
|
};
|
|
5062
5131
|
class AttributeEmptyStyle extends Rule {
|
|
5063
5132
|
constructor(options) {
|
|
5064
|
-
super({ ...defaults$
|
|
5133
|
+
super({ ...defaults$q, ...options });
|
|
5065
5134
|
this.hasInvalidStyle = parseStyle$1(this.options.style);
|
|
5066
5135
|
}
|
|
5067
5136
|
static schema() {
|
|
@@ -5179,64 +5248,95 @@ class AttributeMisuse extends Rule {
|
|
|
5179
5248
|
function parsePattern(pattern) {
|
|
5180
5249
|
switch (pattern) {
|
|
5181
5250
|
case "kebabcase":
|
|
5182
|
-
return /^[a-z0-9-]
|
|
5251
|
+
return { regexp: /^[a-z0-9-]+$/, description: pattern };
|
|
5183
5252
|
case "camelcase":
|
|
5184
|
-
return /^[a-z][a-zA-Z0-9]
|
|
5253
|
+
return { regexp: /^[a-z][a-zA-Z0-9]+$/, description: pattern };
|
|
5185
5254
|
case "underscore":
|
|
5186
|
-
return /^[a-z0-9_]
|
|
5187
|
-
default:
|
|
5188
|
-
|
|
5255
|
+
return { regexp: /^[a-z0-9_]+$/, description: pattern };
|
|
5256
|
+
default: {
|
|
5257
|
+
const regexp = new RegExp(pattern);
|
|
5258
|
+
return { regexp, description: regexp.toString() };
|
|
5259
|
+
}
|
|
5189
5260
|
}
|
|
5190
5261
|
}
|
|
5191
|
-
|
|
5192
|
-
|
|
5193
|
-
|
|
5194
|
-
|
|
5195
|
-
|
|
5196
|
-
|
|
5197
|
-
|
|
5262
|
+
|
|
5263
|
+
function toArray$1(value) {
|
|
5264
|
+
return Array.isArray(value) ? value : [value];
|
|
5265
|
+
}
|
|
5266
|
+
class BasePatternRule extends Rule {
|
|
5267
|
+
/**
|
|
5268
|
+
* @param attr - Attribute holding the value.
|
|
5269
|
+
* @param options - Rule options with defaults expanded.
|
|
5270
|
+
*/
|
|
5271
|
+
constructor(attr, options) {
|
|
5272
|
+
super(options);
|
|
5273
|
+
const { pattern } = this.options;
|
|
5274
|
+
this.attr = attr;
|
|
5275
|
+
this.patterns = toArray$1(pattern).map((it) => parsePattern(it));
|
|
5276
|
+
}
|
|
5277
|
+
static schema() {
|
|
5278
|
+
return {
|
|
5279
|
+
pattern: {
|
|
5280
|
+
oneOf: [{ type: "array", items: { type: "string" }, minItems: 1 }, { type: "string" }]
|
|
5281
|
+
}
|
|
5282
|
+
};
|
|
5283
|
+
}
|
|
5284
|
+
description(context) {
|
|
5285
|
+
const { attr, patterns } = this;
|
|
5286
|
+
const { value } = context;
|
|
5287
|
+
const lead = patterns.length === 1 ? `The \`${attr}\` attribute value \`"${value}"\` does not match the configured pattern.` : `The \`${attr}\` attribute value \`"${value}"\` does not match either of the configured patterns.`;
|
|
5288
|
+
return [
|
|
5289
|
+
lead,
|
|
5290
|
+
"For consistency within the codebase the `${attr}` is required to match one or more of the following patterns:",
|
|
5291
|
+
"",
|
|
5292
|
+
...patterns.map((it) => `- \`${it.description}\``)
|
|
5293
|
+
].join("\n");
|
|
5294
|
+
}
|
|
5295
|
+
validateValue(node, value, location) {
|
|
5296
|
+
const { attr, patterns } = this;
|
|
5297
|
+
const matches = patterns.some((it) => it.regexp.test(value));
|
|
5298
|
+
if (matches) {
|
|
5299
|
+
return;
|
|
5198
5300
|
}
|
|
5199
|
-
|
|
5200
|
-
|
|
5301
|
+
const allowed = utils_naturalJoin.naturalJoin(patterns.map((it) => `"${it.description}"`));
|
|
5302
|
+
const message = patterns.length === 1 ? `${attr} "${value}" does not match the configured pattern ${allowed}` : `${attr} "${value}" does not match either of the configured patterns: ${allowed}`;
|
|
5303
|
+
this.report({
|
|
5304
|
+
node,
|
|
5305
|
+
message,
|
|
5306
|
+
location,
|
|
5307
|
+
context: {
|
|
5308
|
+
value
|
|
5309
|
+
}
|
|
5310
|
+
});
|
|
5201
5311
|
}
|
|
5202
5312
|
}
|
|
5203
5313
|
|
|
5204
|
-
const defaults$
|
|
5314
|
+
const defaults$p = {
|
|
5205
5315
|
pattern: "kebabcase"
|
|
5206
5316
|
};
|
|
5207
|
-
class ClassPattern extends
|
|
5317
|
+
class ClassPattern extends BasePatternRule {
|
|
5208
5318
|
constructor(options) {
|
|
5209
|
-
super({ ...defaults$
|
|
5210
|
-
this.pattern = parsePattern(this.options.pattern);
|
|
5319
|
+
super("class", { ...defaults$p, ...options });
|
|
5211
5320
|
}
|
|
5212
5321
|
static schema() {
|
|
5213
|
-
return
|
|
5214
|
-
pattern: {
|
|
5215
|
-
type: "string"
|
|
5216
|
-
}
|
|
5217
|
-
};
|
|
5322
|
+
return BasePatternRule.schema();
|
|
5218
5323
|
}
|
|
5219
|
-
documentation() {
|
|
5220
|
-
const pattern = describePattern(this.options.pattern);
|
|
5324
|
+
documentation(context) {
|
|
5221
5325
|
return {
|
|
5222
|
-
description:
|
|
5326
|
+
description: this.description(context),
|
|
5223
5327
|
url: "https://html-validate.org/rules/class-pattern.html"
|
|
5224
5328
|
};
|
|
5225
5329
|
}
|
|
5226
5330
|
setup() {
|
|
5227
5331
|
this.on("attr", (event) => {
|
|
5228
|
-
|
|
5332
|
+
const { target, key, value, valueLocation } = event;
|
|
5333
|
+
if (key.toLowerCase() !== "class") {
|
|
5229
5334
|
return;
|
|
5230
5335
|
}
|
|
5231
|
-
const classes = new DOMTokenList(
|
|
5232
|
-
classes.
|
|
5233
|
-
|
|
5234
|
-
|
|
5235
|
-
const pattern = this.pattern.toString();
|
|
5236
|
-
const message = `Class "${cur}" does not match required pattern "${pattern}"`;
|
|
5237
|
-
this.report(event.target, message, location);
|
|
5238
|
-
}
|
|
5239
|
-
});
|
|
5336
|
+
const classes = new DOMTokenList(value, valueLocation);
|
|
5337
|
+
for (const { item, location } of classes.iterator()) {
|
|
5338
|
+
this.validateValue(target, item, location);
|
|
5339
|
+
}
|
|
5240
5340
|
});
|
|
5241
5341
|
}
|
|
5242
5342
|
}
|
|
@@ -5312,13 +5412,13 @@ class CloseOrder extends Rule {
|
|
|
5312
5412
|
}
|
|
5313
5413
|
}
|
|
5314
5414
|
|
|
5315
|
-
const defaults$
|
|
5415
|
+
const defaults$o = {
|
|
5316
5416
|
include: null,
|
|
5317
5417
|
exclude: null
|
|
5318
5418
|
};
|
|
5319
5419
|
class Deprecated extends Rule {
|
|
5320
5420
|
constructor(options) {
|
|
5321
|
-
super({ ...defaults$
|
|
5421
|
+
super({ ...defaults$o, ...options });
|
|
5322
5422
|
}
|
|
5323
5423
|
static schema() {
|
|
5324
5424
|
return {
|
|
@@ -5472,12 +5572,12 @@ let NoStyleTag$1 = class NoStyleTag extends Rule {
|
|
|
5472
5572
|
}
|
|
5473
5573
|
};
|
|
5474
5574
|
|
|
5475
|
-
const defaults$
|
|
5575
|
+
const defaults$n = {
|
|
5476
5576
|
style: "uppercase"
|
|
5477
5577
|
};
|
|
5478
5578
|
class DoctypeStyle extends Rule {
|
|
5479
5579
|
constructor(options) {
|
|
5480
|
-
super({ ...defaults$
|
|
5580
|
+
super({ ...defaults$n, ...options });
|
|
5481
5581
|
}
|
|
5482
5582
|
static schema() {
|
|
5483
5583
|
return {
|
|
@@ -5505,12 +5605,12 @@ class DoctypeStyle extends Rule {
|
|
|
5505
5605
|
}
|
|
5506
5606
|
}
|
|
5507
5607
|
|
|
5508
|
-
const defaults$
|
|
5608
|
+
const defaults$m = {
|
|
5509
5609
|
style: "lowercase"
|
|
5510
5610
|
};
|
|
5511
5611
|
class ElementCase extends Rule {
|
|
5512
5612
|
constructor(options) {
|
|
5513
|
-
super({ ...defaults$
|
|
5613
|
+
super({ ...defaults$m, ...options });
|
|
5514
5614
|
this.style = new CaseStyle(this.options.style, "element-case");
|
|
5515
5615
|
}
|
|
5516
5616
|
static schema() {
|
|
@@ -5570,14 +5670,14 @@ class ElementCase extends Rule {
|
|
|
5570
5670
|
}
|
|
5571
5671
|
}
|
|
5572
5672
|
|
|
5573
|
-
const defaults$
|
|
5673
|
+
const defaults$l = {
|
|
5574
5674
|
pattern: "^[a-z][a-z0-9\\-._]*-[a-z0-9\\-._]*$",
|
|
5575
5675
|
whitelist: [],
|
|
5576
5676
|
blacklist: []
|
|
5577
5677
|
};
|
|
5578
5678
|
class ElementName extends Rule {
|
|
5579
5679
|
constructor(options) {
|
|
5580
|
-
super({ ...defaults$
|
|
5680
|
+
super({ ...defaults$l, ...options });
|
|
5581
5681
|
this.pattern = new RegExp(this.options.pattern);
|
|
5582
5682
|
}
|
|
5583
5683
|
static schema() {
|
|
@@ -5614,7 +5714,7 @@ class ElementName extends Rule {
|
|
|
5614
5714
|
...context.blacklist.map((cur) => `- ${cur}`)
|
|
5615
5715
|
];
|
|
5616
5716
|
}
|
|
5617
|
-
if (context.pattern !== defaults$
|
|
5717
|
+
if (context.pattern !== defaults$l.pattern) {
|
|
5618
5718
|
return [
|
|
5619
5719
|
`<${context.tagName}> is not a valid element name. This project is configured to only allow names matching the following regular expression:`,
|
|
5620
5720
|
"",
|
|
@@ -6103,7 +6203,7 @@ class EmptyTitle extends Rule {
|
|
|
6103
6203
|
}
|
|
6104
6204
|
}
|
|
6105
6205
|
|
|
6106
|
-
const defaults$
|
|
6206
|
+
const defaults$k = {
|
|
6107
6207
|
allowArrayBrackets: true,
|
|
6108
6208
|
shared: ["radio", "button", "reset", "submit"]
|
|
6109
6209
|
};
|
|
@@ -6131,7 +6231,7 @@ function getDocumentation(context) {
|
|
|
6131
6231
|
}
|
|
6132
6232
|
class FormDupName extends Rule {
|
|
6133
6233
|
constructor(options) {
|
|
6134
|
-
super({ ...defaults$
|
|
6234
|
+
super({ ...defaults$k, ...options });
|
|
6135
6235
|
}
|
|
6136
6236
|
static schema() {
|
|
6137
6237
|
return {
|
|
@@ -6280,7 +6380,7 @@ class FormDupName extends Rule {
|
|
|
6280
6380
|
}
|
|
6281
6381
|
}
|
|
6282
6382
|
|
|
6283
|
-
const defaults$
|
|
6383
|
+
const defaults$j = {
|
|
6284
6384
|
allowMultipleH1: false,
|
|
6285
6385
|
minInitialRank: "h1",
|
|
6286
6386
|
sectioningRoots: ["dialog", '[role="dialog"]', '[role="alertdialog"]']
|
|
@@ -6309,7 +6409,7 @@ function parseMaxInitial(value) {
|
|
|
6309
6409
|
}
|
|
6310
6410
|
class HeadingLevel extends Rule {
|
|
6311
6411
|
constructor(options) {
|
|
6312
|
-
super({ ...defaults$
|
|
6412
|
+
super({ ...defaults$j, ...options });
|
|
6313
6413
|
this.stack = [];
|
|
6314
6414
|
this.minInitialRank = parseMaxInitial(this.options.minInitialRank);
|
|
6315
6415
|
this.sectionRoots = this.options.sectioningRoots.map((it) => new Pattern(it));
|
|
@@ -6460,6 +6560,46 @@ class HeadingLevel extends Rule {
|
|
|
6460
6560
|
}
|
|
6461
6561
|
}
|
|
6462
6562
|
|
|
6563
|
+
const FOCUSABLE_CACHE = Symbol(isFocusable.name);
|
|
6564
|
+
function isDisabled(element, meta) {
|
|
6565
|
+
var _a;
|
|
6566
|
+
if (!((_a = meta.formAssociated) == null ? void 0 : _a.disablable)) {
|
|
6567
|
+
return false;
|
|
6568
|
+
}
|
|
6569
|
+
const disabled = element.matches("[disabled]");
|
|
6570
|
+
if (disabled) {
|
|
6571
|
+
return true;
|
|
6572
|
+
}
|
|
6573
|
+
const fieldset = element.closest("fieldset[disabled]");
|
|
6574
|
+
if (fieldset) {
|
|
6575
|
+
return true;
|
|
6576
|
+
}
|
|
6577
|
+
return false;
|
|
6578
|
+
}
|
|
6579
|
+
function isFocusableImpl(element) {
|
|
6580
|
+
if (isHTMLHidden(element) || isInert(element) || isStyleHidden(element)) {
|
|
6581
|
+
return false;
|
|
6582
|
+
}
|
|
6583
|
+
const { tabIndex, meta } = element;
|
|
6584
|
+
if (tabIndex !== null) {
|
|
6585
|
+
return tabIndex >= 0;
|
|
6586
|
+
}
|
|
6587
|
+
if (!meta) {
|
|
6588
|
+
return false;
|
|
6589
|
+
}
|
|
6590
|
+
if (isDisabled(element, meta)) {
|
|
6591
|
+
return false;
|
|
6592
|
+
}
|
|
6593
|
+
return Boolean(meta == null ? void 0 : meta.focusable);
|
|
6594
|
+
}
|
|
6595
|
+
function isFocusable(element) {
|
|
6596
|
+
const cached = element.cacheGet(FOCUSABLE_CACHE);
|
|
6597
|
+
if (cached) {
|
|
6598
|
+
return cached;
|
|
6599
|
+
}
|
|
6600
|
+
return element.cacheSet(FOCUSABLE_CACHE, isFocusableImpl(element));
|
|
6601
|
+
}
|
|
6602
|
+
|
|
6463
6603
|
class HiddenFocusable extends Rule {
|
|
6464
6604
|
documentation(context) {
|
|
6465
6605
|
const byParent = context === "parent" ? " In this case it is being hidden by an ancestor with `aria-hidden.`" : "";
|
|
@@ -6473,7 +6613,8 @@ class HiddenFocusable extends Rule {
|
|
|
6473
6613
|
"To fix this either:",
|
|
6474
6614
|
" - Remove `aria-hidden`.",
|
|
6475
6615
|
" - Remove the element from the DOM instead.",
|
|
6476
|
-
|
|
6616
|
+
' - Use `tabindex="-1"` to remove the element from tab order.',
|
|
6617
|
+
" - Use `hidden`, `inert` or similar means to hide or disable the element."
|
|
6477
6618
|
].join("\n"),
|
|
6478
6619
|
url: "https://html-validate.org/rules/hidden-focusable.html"
|
|
6479
6620
|
};
|
|
@@ -6484,21 +6625,13 @@ class HiddenFocusable extends Rule {
|
|
|
6484
6625
|
this.on("dom:ready", (event) => {
|
|
6485
6626
|
const { document } = event;
|
|
6486
6627
|
for (const element of document.querySelectorAll(selector)) {
|
|
6487
|
-
if (
|
|
6488
|
-
|
|
6489
|
-
}
|
|
6490
|
-
if (isAriaHidden(element)) {
|
|
6491
|
-
this.validateElement(element);
|
|
6628
|
+
if (isFocusable(element) && isAriaHidden(element)) {
|
|
6629
|
+
this.reportElement(element);
|
|
6492
6630
|
}
|
|
6493
6631
|
}
|
|
6494
6632
|
});
|
|
6495
6633
|
}
|
|
6496
|
-
|
|
6497
|
-
const { meta } = element;
|
|
6498
|
-
const tabindex = element.getAttribute("tabindex");
|
|
6499
|
-
if (meta && !meta.focusable && !tabindex) {
|
|
6500
|
-
return;
|
|
6501
|
-
}
|
|
6634
|
+
reportElement(element) {
|
|
6502
6635
|
const attribute = element.getAttribute("aria-hidden");
|
|
6503
6636
|
const message = attribute ? `aria-hidden cannot be used on focusable elements` : `aria-hidden cannot be used on focusable elements (hidden by ancestor element)`;
|
|
6504
6637
|
const location = attribute ? attribute.keyLocation : element.location;
|
|
@@ -6512,43 +6645,35 @@ class HiddenFocusable extends Rule {
|
|
|
6512
6645
|
}
|
|
6513
6646
|
}
|
|
6514
6647
|
|
|
6515
|
-
const defaults$
|
|
6648
|
+
const defaults$i = {
|
|
6516
6649
|
pattern: "kebabcase"
|
|
6517
6650
|
};
|
|
6518
|
-
class IdPattern extends
|
|
6651
|
+
class IdPattern extends BasePatternRule {
|
|
6519
6652
|
constructor(options) {
|
|
6520
|
-
super({ ...defaults$
|
|
6521
|
-
this.pattern = parsePattern(this.options.pattern);
|
|
6653
|
+
super("id", { ...defaults$i, ...options });
|
|
6522
6654
|
}
|
|
6523
6655
|
static schema() {
|
|
6524
|
-
return
|
|
6525
|
-
pattern: {
|
|
6526
|
-
type: "string"
|
|
6527
|
-
}
|
|
6528
|
-
};
|
|
6656
|
+
return BasePatternRule.schema();
|
|
6529
6657
|
}
|
|
6530
|
-
documentation() {
|
|
6531
|
-
const pattern = describePattern(this.options.pattern);
|
|
6658
|
+
documentation(context) {
|
|
6532
6659
|
return {
|
|
6533
|
-
description:
|
|
6660
|
+
description: this.description(context),
|
|
6534
6661
|
url: "https://html-validate.org/rules/id-pattern.html"
|
|
6535
6662
|
};
|
|
6536
6663
|
}
|
|
6537
6664
|
setup() {
|
|
6538
6665
|
this.on("attr", (event) => {
|
|
6539
|
-
|
|
6540
|
-
if (
|
|
6666
|
+
const { target, key, value, valueLocation } = event;
|
|
6667
|
+
if (key.toLowerCase() !== "id") {
|
|
6541
6668
|
return;
|
|
6542
6669
|
}
|
|
6543
|
-
if (
|
|
6670
|
+
if (value instanceof DynamicValue) {
|
|
6544
6671
|
return;
|
|
6545
6672
|
}
|
|
6546
|
-
if (
|
|
6547
|
-
|
|
6548
|
-
const pattern = this.pattern.toString();
|
|
6549
|
-
const message = `ID "${value}" does not match required pattern "${pattern}"`;
|
|
6550
|
-
this.report(event.target, message, event.valueLocation);
|
|
6673
|
+
if (value === null) {
|
|
6674
|
+
return;
|
|
6551
6675
|
}
|
|
6676
|
+
this.validateValue(target, value, valueLocation);
|
|
6552
6677
|
});
|
|
6553
6678
|
}
|
|
6554
6679
|
}
|
|
@@ -6787,7 +6912,7 @@ class InputMissingLabel extends Rule {
|
|
|
6787
6912
|
});
|
|
6788
6913
|
}
|
|
6789
6914
|
validateInput(root, elem) {
|
|
6790
|
-
if (
|
|
6915
|
+
if (!inAccessibilityTree(elem)) {
|
|
6791
6916
|
return;
|
|
6792
6917
|
}
|
|
6793
6918
|
if (isIgnored(elem)) {
|
|
@@ -6820,7 +6945,7 @@ class InputMissingLabel extends Rule {
|
|
|
6820
6945
|
* Reports error if none of the labels are accessible.
|
|
6821
6946
|
*/
|
|
6822
6947
|
validateLabel(root, elem, labels) {
|
|
6823
|
-
const visible = labels.filter(
|
|
6948
|
+
const visible = labels.filter(inAccessibilityTree);
|
|
6824
6949
|
if (visible.length === 0) {
|
|
6825
6950
|
this.report(elem, `<${elem.tagName}> element has <label> but <label> element is hidden`);
|
|
6826
6951
|
return;
|
|
@@ -6830,10 +6955,6 @@ class InputMissingLabel extends Rule {
|
|
|
6830
6955
|
}
|
|
6831
6956
|
}
|
|
6832
6957
|
}
|
|
6833
|
-
function isVisible(elem) {
|
|
6834
|
-
const hidden = isHTMLHidden(elem) || isAriaHidden(elem);
|
|
6835
|
-
return !hidden;
|
|
6836
|
-
}
|
|
6837
6958
|
function findLabelById(root, id) {
|
|
6838
6959
|
if (!id)
|
|
6839
6960
|
return [];
|
|
@@ -6850,12 +6971,12 @@ function findLabelByParent(el) {
|
|
|
6850
6971
|
return [];
|
|
6851
6972
|
}
|
|
6852
6973
|
|
|
6853
|
-
const defaults$
|
|
6974
|
+
const defaults$h = {
|
|
6854
6975
|
maxlength: 70
|
|
6855
6976
|
};
|
|
6856
6977
|
class LongTitle extends Rule {
|
|
6857
6978
|
constructor(options) {
|
|
6858
|
-
super({ ...defaults$
|
|
6979
|
+
super({ ...defaults$h, ...options });
|
|
6859
6980
|
this.maxlength = this.options.maxlength;
|
|
6860
6981
|
}
|
|
6861
6982
|
static schema() {
|
|
@@ -6884,12 +7005,12 @@ class LongTitle extends Rule {
|
|
|
6884
7005
|
}
|
|
6885
7006
|
}
|
|
6886
7007
|
|
|
6887
|
-
const defaults$
|
|
7008
|
+
const defaults$g = {
|
|
6888
7009
|
allowLongDelay: false
|
|
6889
7010
|
};
|
|
6890
7011
|
class MetaRefresh extends Rule {
|
|
6891
7012
|
constructor(options) {
|
|
6892
|
-
super({ ...defaults$
|
|
7013
|
+
super({ ...defaults$g, ...options });
|
|
6893
7014
|
}
|
|
6894
7015
|
documentation() {
|
|
6895
7016
|
return {
|
|
@@ -7077,6 +7198,45 @@ class MultipleLabeledControls extends Rule {
|
|
|
7077
7198
|
}
|
|
7078
7199
|
}
|
|
7079
7200
|
|
|
7201
|
+
const defaults$f = {
|
|
7202
|
+
pattern: "camelcase"
|
|
7203
|
+
};
|
|
7204
|
+
class NamePattern extends BasePatternRule {
|
|
7205
|
+
constructor(options) {
|
|
7206
|
+
super("name", { ...defaults$f, ...options });
|
|
7207
|
+
}
|
|
7208
|
+
static schema() {
|
|
7209
|
+
return BasePatternRule.schema();
|
|
7210
|
+
}
|
|
7211
|
+
documentation(context) {
|
|
7212
|
+
return {
|
|
7213
|
+
description: this.description(context),
|
|
7214
|
+
url: "https://html-validate.org/rules/name-pattern.html"
|
|
7215
|
+
};
|
|
7216
|
+
}
|
|
7217
|
+
setup() {
|
|
7218
|
+
this.on("attr", (event) => {
|
|
7219
|
+
var _a;
|
|
7220
|
+
const { target, key, value, valueLocation } = event;
|
|
7221
|
+
const { meta } = target;
|
|
7222
|
+
if (!((_a = meta == null ? void 0 : meta.formAssociated) == null ? void 0 : _a.listed)) {
|
|
7223
|
+
return;
|
|
7224
|
+
}
|
|
7225
|
+
if (key.toLowerCase() !== "name") {
|
|
7226
|
+
return;
|
|
7227
|
+
}
|
|
7228
|
+
if (value instanceof DynamicValue) {
|
|
7229
|
+
return;
|
|
7230
|
+
}
|
|
7231
|
+
if (value === null) {
|
|
7232
|
+
return;
|
|
7233
|
+
}
|
|
7234
|
+
const name = value.endsWith("[]") ? value.slice(0, -2) : value;
|
|
7235
|
+
this.validateValue(target, name, valueLocation);
|
|
7236
|
+
});
|
|
7237
|
+
}
|
|
7238
|
+
}
|
|
7239
|
+
|
|
7080
7240
|
const abstractRoles = [
|
|
7081
7241
|
"command",
|
|
7082
7242
|
"composite",
|
|
@@ -10058,6 +10218,7 @@ const bundledRules = {
|
|
|
10058
10218
|
"meta-refresh": MetaRefresh,
|
|
10059
10219
|
"missing-doctype": MissingDoctype,
|
|
10060
10220
|
"multiple-labeled-controls": MultipleLabeledControls,
|
|
10221
|
+
"name-pattern": NamePattern,
|
|
10061
10222
|
"no-abstract-role": NoAbstractRole,
|
|
10062
10223
|
"no-autoplay": NoAutoplay,
|
|
10063
10224
|
"no-conditional-comment": NoConditionalComment,
|
|
@@ -12498,7 +12659,7 @@ class HtmlValidate {
|
|
|
12498
12659
|
}
|
|
12499
12660
|
|
|
12500
12661
|
const name = "html-validate";
|
|
12501
|
-
const version = "8.
|
|
12662
|
+
const version = "8.17.0";
|
|
12502
12663
|
const bugs = "https://gitlab.com/html-validate/html-validate/issues/new";
|
|
12503
12664
|
|
|
12504
12665
|
function definePlugin(plugin) {
|