html-validate 7.1.2 → 7.2.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/cli.js +126 -7
- package/dist/cjs/cli.js.map +1 -1
- package/dist/cjs/core.d.ts +11 -2
- package/dist/cjs/core.js +242 -108
- package/dist/cjs/core.js.map +1 -1
- package/dist/cjs/html-validate.js +49 -135
- package/dist/cjs/html-validate.js.map +1 -1
- package/dist/cjs/jest-lib.js +1 -0
- package/dist/cjs/jest-lib.js.map +1 -1
- package/dist/es/cli.js +122 -8
- package/dist/es/cli.js.map +1 -1
- package/dist/es/core.d.ts +11 -2
- package/dist/es/core.js +242 -108
- package/dist/es/core.js.map +1 -1
- package/dist/es/html-validate.js +34 -120
- package/dist/es/html-validate.js.map +1 -1
- package/dist/es/jest-lib.js +1 -0
- package/dist/es/jest-lib.js.map +1 -1
- package/elements/html5.js +12 -0
- package/package.json +19 -43
package/dist/cjs/core.js
CHANGED
|
@@ -3117,7 +3117,7 @@ var TRANSFORMER_API;
|
|
|
3117
3117
|
/** @public */
|
|
3118
3118
|
const name = "html-validate";
|
|
3119
3119
|
/** @public */
|
|
3120
|
-
const version = "7.
|
|
3120
|
+
const version = "7.2.0";
|
|
3121
3121
|
/** @public */
|
|
3122
3122
|
const homepage = "https://html-validate.org";
|
|
3123
3123
|
/** @public */
|
|
@@ -3218,6 +3218,18 @@ function getSchemaValidator(ruleId, properties) {
|
|
|
3218
3218
|
};
|
|
3219
3219
|
return ajv$1.compile(schema);
|
|
3220
3220
|
}
|
|
3221
|
+
function isErrorDescriptor(value) {
|
|
3222
|
+
return Boolean(value[0] && value[0].message);
|
|
3223
|
+
}
|
|
3224
|
+
function unpackErrorDescriptor(value) {
|
|
3225
|
+
if (isErrorDescriptor(value)) {
|
|
3226
|
+
return value[0];
|
|
3227
|
+
}
|
|
3228
|
+
else {
|
|
3229
|
+
const [node, message, location, context] = value;
|
|
3230
|
+
return { node, message, location, context };
|
|
3231
|
+
}
|
|
3232
|
+
}
|
|
3221
3233
|
/**
|
|
3222
3234
|
* @public
|
|
3223
3235
|
*/
|
|
@@ -3318,13 +3330,8 @@ class Rule {
|
|
|
3318
3330
|
static schema() {
|
|
3319
3331
|
return null;
|
|
3320
3332
|
}
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
*
|
|
3324
|
-
* Rule must be enabled both globally and on the specific node for this to
|
|
3325
|
-
* have any effect.
|
|
3326
|
-
*/
|
|
3327
|
-
report(node, message, location, context) {
|
|
3333
|
+
report(...args) {
|
|
3334
|
+
const { node, message, location, context } = unpackErrorDescriptor(args);
|
|
3328
3335
|
if (this.isEnabled() && (!node || node.ruleEnabled(this.name))) {
|
|
3329
3336
|
const where = this.findLocation({ node, location, event: this.event });
|
|
3330
3337
|
const interpolated = interpolate(message, context !== null && context !== void 0 ? context : {});
|
|
@@ -3837,9 +3844,14 @@ class AttrCase extends Rule {
|
|
|
3837
3844
|
return;
|
|
3838
3845
|
}
|
|
3839
3846
|
const letters = event.key.replace(/[^a-z]+/gi, "");
|
|
3840
|
-
if (
|
|
3841
|
-
|
|
3847
|
+
if (this.style.match(letters)) {
|
|
3848
|
+
return;
|
|
3842
3849
|
}
|
|
3850
|
+
this.report({
|
|
3851
|
+
node: event.target,
|
|
3852
|
+
message: `Attribute "${event.key}" should be ${this.style.name}`,
|
|
3853
|
+
location: event.keyLocation,
|
|
3854
|
+
});
|
|
3843
3855
|
});
|
|
3844
3856
|
}
|
|
3845
3857
|
isIgnored(node) {
|
|
@@ -5139,6 +5151,11 @@ class ElementName extends Rule {
|
|
|
5139
5151
|
}
|
|
5140
5152
|
}
|
|
5141
5153
|
|
|
5154
|
+
var ErrorKind;
|
|
5155
|
+
(function (ErrorKind) {
|
|
5156
|
+
ErrorKind["CONTENT"] = "content";
|
|
5157
|
+
ErrorKind["DESCENDANT"] = "descendant";
|
|
5158
|
+
})(ErrorKind || (ErrorKind = {}));
|
|
5142
5159
|
function getTransparentChildren(node, transparent) {
|
|
5143
5160
|
if (typeof transparent === "boolean") {
|
|
5144
5161
|
return node.childElements;
|
|
@@ -5152,10 +5169,28 @@ function getTransparentChildren(node, transparent) {
|
|
|
5152
5169
|
});
|
|
5153
5170
|
}
|
|
5154
5171
|
}
|
|
5172
|
+
function getRuleDescription$1(context) {
|
|
5173
|
+
if (!context) {
|
|
5174
|
+
return [
|
|
5175
|
+
"Some elements has restrictions on what content is allowed.",
|
|
5176
|
+
"This can include both direct children or descendant elements.",
|
|
5177
|
+
];
|
|
5178
|
+
}
|
|
5179
|
+
switch (context.kind) {
|
|
5180
|
+
case ErrorKind.CONTENT:
|
|
5181
|
+
return [
|
|
5182
|
+
`The \`${context.child}\` element is not permitted as content under the parent \`${context.parent}\` element.`,
|
|
5183
|
+
];
|
|
5184
|
+
case ErrorKind.DESCENDANT:
|
|
5185
|
+
return [
|
|
5186
|
+
`The \`${context.child}\` element is not permitted as a descendant of the \`${context.ancestor}\` element.`,
|
|
5187
|
+
];
|
|
5188
|
+
}
|
|
5189
|
+
}
|
|
5155
5190
|
class ElementPermittedContent extends Rule {
|
|
5156
|
-
documentation() {
|
|
5191
|
+
documentation(context) {
|
|
5157
5192
|
return {
|
|
5158
|
-
description:
|
|
5193
|
+
description: getRuleDescription$1(context).join("\n"),
|
|
5159
5194
|
url: ruleDocumentationUrl("@/rules/element-permitted-content.ts"),
|
|
5160
5195
|
};
|
|
5161
5196
|
}
|
|
@@ -5164,8 +5199,9 @@ class ElementPermittedContent extends Rule {
|
|
|
5164
5199
|
const doc = event.document;
|
|
5165
5200
|
doc.visitDepthFirst((node) => {
|
|
5166
5201
|
const parent = node.parent;
|
|
5167
|
-
/*
|
|
5168
|
-
|
|
5202
|
+
/* istanbul ignore next: satisfy typescript but will visitDepthFirst()
|
|
5203
|
+
* will not yield nodes without a parent */
|
|
5204
|
+
if (!parent) {
|
|
5169
5205
|
return;
|
|
5170
5206
|
}
|
|
5171
5207
|
/* Run each validation step, stop as soon as any errors are
|
|
@@ -5175,7 +5211,6 @@ class ElementPermittedContent extends Rule {
|
|
|
5175
5211
|
[
|
|
5176
5212
|
() => this.validatePermittedContent(node, parent),
|
|
5177
5213
|
() => this.validatePermittedDescendant(node, parent),
|
|
5178
|
-
() => this.validatePermittedAncestors(node),
|
|
5179
5214
|
].some((fn) => fn());
|
|
5180
5215
|
});
|
|
5181
5216
|
});
|
|
@@ -5192,7 +5227,14 @@ class ElementPermittedContent extends Rule {
|
|
|
5192
5227
|
}
|
|
5193
5228
|
validatePermittedContentImpl(cur, parent, rules) {
|
|
5194
5229
|
if (!Validator.validatePermitted(cur, rules)) {
|
|
5195
|
-
|
|
5230
|
+
const child = `<${cur.tagName}>`;
|
|
5231
|
+
const message = `${child} element is not permitted as content under ${parent.annotatedName}`;
|
|
5232
|
+
const context = {
|
|
5233
|
+
kind: ErrorKind.CONTENT,
|
|
5234
|
+
parent: parent.annotatedName,
|
|
5235
|
+
child,
|
|
5236
|
+
};
|
|
5237
|
+
this.report(cur, message, null, context);
|
|
5196
5238
|
return true;
|
|
5197
5239
|
}
|
|
5198
5240
|
/* for transparent elements all/listed children must be validated against
|
|
@@ -5223,21 +5265,15 @@ class ElementPermittedContent extends Rule {
|
|
|
5223
5265
|
if (Validator.validatePermitted(node, rules)) {
|
|
5224
5266
|
continue;
|
|
5225
5267
|
}
|
|
5226
|
-
|
|
5227
|
-
|
|
5228
|
-
|
|
5229
|
-
|
|
5230
|
-
|
|
5231
|
-
|
|
5232
|
-
|
|
5233
|
-
|
|
5234
|
-
|
|
5235
|
-
const rules = node.meta.requiredAncestors;
|
|
5236
|
-
if (!rules) {
|
|
5237
|
-
return false;
|
|
5238
|
-
}
|
|
5239
|
-
if (!Validator.validateAncestors(node, rules)) {
|
|
5240
|
-
this.report(node, `Element <${node.tagName}> requires an "${rules[0]}" ancestor`);
|
|
5268
|
+
const child = `<${node.tagName}>`;
|
|
5269
|
+
const ancestor = cur.annotatedName;
|
|
5270
|
+
const message = `${child} element is not permitted as a descendant of ${ancestor}`;
|
|
5271
|
+
const context = {
|
|
5272
|
+
kind: ErrorKind.DESCENDANT,
|
|
5273
|
+
ancestor,
|
|
5274
|
+
child,
|
|
5275
|
+
};
|
|
5276
|
+
this.report(node, message, null, context);
|
|
5241
5277
|
return true;
|
|
5242
5278
|
}
|
|
5243
5279
|
return false;
|
|
@@ -5304,6 +5340,152 @@ class ElementPermittedOrder extends Rule {
|
|
|
5304
5340
|
}
|
|
5305
5341
|
}
|
|
5306
5342
|
|
|
5343
|
+
const CACHE_KEY = Symbol(classifyNodeText.name);
|
|
5344
|
+
var TextClassification;
|
|
5345
|
+
(function (TextClassification) {
|
|
5346
|
+
TextClassification[TextClassification["EMPTY_TEXT"] = 0] = "EMPTY_TEXT";
|
|
5347
|
+
TextClassification[TextClassification["DYNAMIC_TEXT"] = 1] = "DYNAMIC_TEXT";
|
|
5348
|
+
TextClassification[TextClassification["STATIC_TEXT"] = 2] = "STATIC_TEXT";
|
|
5349
|
+
})(TextClassification || (TextClassification = {}));
|
|
5350
|
+
/**
|
|
5351
|
+
* Checks text content of an element.
|
|
5352
|
+
*
|
|
5353
|
+
* Any text is considered including text from descendant elements. Whitespace is
|
|
5354
|
+
* ignored.
|
|
5355
|
+
*
|
|
5356
|
+
* If any text is dynamic `TextClassification.DYNAMIC_TEXT` is returned.
|
|
5357
|
+
*/
|
|
5358
|
+
function classifyNodeText(node) {
|
|
5359
|
+
if (node.cacheExists(CACHE_KEY)) {
|
|
5360
|
+
return node.cacheGet(CACHE_KEY);
|
|
5361
|
+
}
|
|
5362
|
+
const text = findTextNodes(node);
|
|
5363
|
+
/* if any text is dynamic classify as dynamic */
|
|
5364
|
+
if (text.some((cur) => cur.isDynamic)) {
|
|
5365
|
+
return node.cacheSet(CACHE_KEY, TextClassification.DYNAMIC_TEXT);
|
|
5366
|
+
}
|
|
5367
|
+
/* if any text has non-whitespace character classify as static */
|
|
5368
|
+
if (text.some((cur) => cur.textContent.match(/\S/) !== null)) {
|
|
5369
|
+
return node.cacheSet(CACHE_KEY, TextClassification.STATIC_TEXT);
|
|
5370
|
+
}
|
|
5371
|
+
/* default to empty */
|
|
5372
|
+
return node.cacheSet(CACHE_KEY, TextClassification.EMPTY_TEXT);
|
|
5373
|
+
}
|
|
5374
|
+
function findTextNodes(node) {
|
|
5375
|
+
let text = [];
|
|
5376
|
+
for (const child of node.childNodes) {
|
|
5377
|
+
switch (child.nodeType) {
|
|
5378
|
+
case NodeType.TEXT_NODE:
|
|
5379
|
+
text.push(child);
|
|
5380
|
+
break;
|
|
5381
|
+
case NodeType.ELEMENT_NODE:
|
|
5382
|
+
text = text.concat(findTextNodes(child));
|
|
5383
|
+
break;
|
|
5384
|
+
}
|
|
5385
|
+
}
|
|
5386
|
+
return text;
|
|
5387
|
+
}
|
|
5388
|
+
|
|
5389
|
+
function hasAltText(image) {
|
|
5390
|
+
const alt = image.getAttribute("alt");
|
|
5391
|
+
/* missing or boolean */
|
|
5392
|
+
if (alt === null || alt.value === null) {
|
|
5393
|
+
return false;
|
|
5394
|
+
}
|
|
5395
|
+
return alt.isDynamic || alt.value.toString() !== "";
|
|
5396
|
+
}
|
|
5397
|
+
|
|
5398
|
+
function hasAriaLabel(node) {
|
|
5399
|
+
const label = node.getAttribute("aria-label");
|
|
5400
|
+
/* missing or boolean */
|
|
5401
|
+
if (label === null || label.value === null) {
|
|
5402
|
+
return false;
|
|
5403
|
+
}
|
|
5404
|
+
return label.isDynamic || label.value.toString() !== "";
|
|
5405
|
+
}
|
|
5406
|
+
|
|
5407
|
+
/**
|
|
5408
|
+
* Joins a list of words into natural language.
|
|
5409
|
+
*
|
|
5410
|
+
* - `["foo"]` becomes `"foo"`
|
|
5411
|
+
* - `["foo", "bar"]` becomes `"foo or bar"`
|
|
5412
|
+
* - `["foo", "bar", "baz"]` becomes `"foo, bar or baz"`
|
|
5413
|
+
* - and so on...
|
|
5414
|
+
*
|
|
5415
|
+
* @internal
|
|
5416
|
+
* @param values - List of words to join
|
|
5417
|
+
* @param conjunction - Conjunction for the last element.
|
|
5418
|
+
* @returns String with the words naturally joined with a conjunction.
|
|
5419
|
+
*/
|
|
5420
|
+
function naturalJoin(values, conjunction = "or") {
|
|
5421
|
+
switch (values.length) {
|
|
5422
|
+
case 0:
|
|
5423
|
+
return "";
|
|
5424
|
+
case 1:
|
|
5425
|
+
return values[0];
|
|
5426
|
+
case 2:
|
|
5427
|
+
return `${values[0]} ${conjunction} ${values[1]}`;
|
|
5428
|
+
default:
|
|
5429
|
+
return `${values.slice(0, -1).join(", ")} ${conjunction} ${values.slice(-1)[0]}`;
|
|
5430
|
+
}
|
|
5431
|
+
}
|
|
5432
|
+
|
|
5433
|
+
function isTagnameOnly(value) {
|
|
5434
|
+
return Boolean(value.match(/^[a-zA-Z0-9-]+$/));
|
|
5435
|
+
}
|
|
5436
|
+
function getRuleDescription(context) {
|
|
5437
|
+
if (!context) {
|
|
5438
|
+
return [
|
|
5439
|
+
"Some elements has restrictions on what content is allowed.",
|
|
5440
|
+
"This can include both direct children or descendant elements.",
|
|
5441
|
+
];
|
|
5442
|
+
}
|
|
5443
|
+
const escaped = context.ancestor.map((it) => `\`${it}\``);
|
|
5444
|
+
return [`The \`${context.child}\` element requires a ${naturalJoin(escaped)} ancestor.`];
|
|
5445
|
+
}
|
|
5446
|
+
class ElementRequiredAncestor extends Rule {
|
|
5447
|
+
documentation(context) {
|
|
5448
|
+
return {
|
|
5449
|
+
description: getRuleDescription(context).join("\n"),
|
|
5450
|
+
url: ruleDocumentationUrl("@/rules/element-required-ancestor.ts"),
|
|
5451
|
+
};
|
|
5452
|
+
}
|
|
5453
|
+
setup() {
|
|
5454
|
+
this.on("dom:ready", (event) => {
|
|
5455
|
+
const doc = event.document;
|
|
5456
|
+
doc.visitDepthFirst((node) => {
|
|
5457
|
+
const parent = node.parent;
|
|
5458
|
+
/* istanbul ignore next: satisfy typescript but will visitDepthFirst()
|
|
5459
|
+
* will not yield nodes without a parent */
|
|
5460
|
+
if (!parent) {
|
|
5461
|
+
return;
|
|
5462
|
+
}
|
|
5463
|
+
this.validateRequiredAncestors(node);
|
|
5464
|
+
});
|
|
5465
|
+
});
|
|
5466
|
+
}
|
|
5467
|
+
validateRequiredAncestors(node) {
|
|
5468
|
+
if (!node.meta) {
|
|
5469
|
+
return;
|
|
5470
|
+
}
|
|
5471
|
+
const rules = node.meta.requiredAncestors;
|
|
5472
|
+
if (!rules) {
|
|
5473
|
+
return;
|
|
5474
|
+
}
|
|
5475
|
+
if (Validator.validateAncestors(node, rules)) {
|
|
5476
|
+
return;
|
|
5477
|
+
}
|
|
5478
|
+
const ancestor = rules.map((it) => (isTagnameOnly(it) ? `<${it}>` : `"${it}"`));
|
|
5479
|
+
const child = `<${node.tagName}>`;
|
|
5480
|
+
const message = `<${node.tagName}> element requires a ${naturalJoin(ancestor)} ancestor`;
|
|
5481
|
+
const context = {
|
|
5482
|
+
ancestor,
|
|
5483
|
+
child,
|
|
5484
|
+
};
|
|
5485
|
+
this.report(node, message, null, context);
|
|
5486
|
+
}
|
|
5487
|
+
}
|
|
5488
|
+
|
|
5307
5489
|
class ElementRequiredAttributes extends Rule {
|
|
5308
5490
|
documentation(context) {
|
|
5309
5491
|
const docs = {
|
|
@@ -5381,52 +5563,6 @@ class ElementRequiredContent extends Rule {
|
|
|
5381
5563
|
}
|
|
5382
5564
|
}
|
|
5383
5565
|
|
|
5384
|
-
const CACHE_KEY = Symbol(classifyNodeText.name);
|
|
5385
|
-
var TextClassification;
|
|
5386
|
-
(function (TextClassification) {
|
|
5387
|
-
TextClassification[TextClassification["EMPTY_TEXT"] = 0] = "EMPTY_TEXT";
|
|
5388
|
-
TextClassification[TextClassification["DYNAMIC_TEXT"] = 1] = "DYNAMIC_TEXT";
|
|
5389
|
-
TextClassification[TextClassification["STATIC_TEXT"] = 2] = "STATIC_TEXT";
|
|
5390
|
-
})(TextClassification || (TextClassification = {}));
|
|
5391
|
-
/**
|
|
5392
|
-
* Checks text content of an element.
|
|
5393
|
-
*
|
|
5394
|
-
* Any text is considered including text from descendant elements. Whitespace is
|
|
5395
|
-
* ignored.
|
|
5396
|
-
*
|
|
5397
|
-
* If any text is dynamic `TextClassification.DYNAMIC_TEXT` is returned.
|
|
5398
|
-
*/
|
|
5399
|
-
function classifyNodeText(node) {
|
|
5400
|
-
if (node.cacheExists(CACHE_KEY)) {
|
|
5401
|
-
return node.cacheGet(CACHE_KEY);
|
|
5402
|
-
}
|
|
5403
|
-
const text = findTextNodes(node);
|
|
5404
|
-
/* if any text is dynamic classify as dynamic */
|
|
5405
|
-
if (text.some((cur) => cur.isDynamic)) {
|
|
5406
|
-
return node.cacheSet(CACHE_KEY, TextClassification.DYNAMIC_TEXT);
|
|
5407
|
-
}
|
|
5408
|
-
/* if any text has non-whitespace character classify as static */
|
|
5409
|
-
if (text.some((cur) => cur.textContent.match(/\S/) !== null)) {
|
|
5410
|
-
return node.cacheSet(CACHE_KEY, TextClassification.STATIC_TEXT);
|
|
5411
|
-
}
|
|
5412
|
-
/* default to empty */
|
|
5413
|
-
return node.cacheSet(CACHE_KEY, TextClassification.EMPTY_TEXT);
|
|
5414
|
-
}
|
|
5415
|
-
function findTextNodes(node) {
|
|
5416
|
-
let text = [];
|
|
5417
|
-
for (const child of node.childNodes) {
|
|
5418
|
-
switch (child.nodeType) {
|
|
5419
|
-
case NodeType.TEXT_NODE:
|
|
5420
|
-
text.push(child);
|
|
5421
|
-
break;
|
|
5422
|
-
case NodeType.ELEMENT_NODE:
|
|
5423
|
-
text = text.concat(findTextNodes(child));
|
|
5424
|
-
break;
|
|
5425
|
-
}
|
|
5426
|
-
}
|
|
5427
|
-
return text;
|
|
5428
|
-
}
|
|
5429
|
-
|
|
5430
5566
|
const selector = ["h1", "h2", "h3", "h4", "h5", "h6"].join(",");
|
|
5431
5567
|
class EmptyHeading extends Rule {
|
|
5432
5568
|
documentation() {
|
|
@@ -7607,24 +7743,6 @@ class TelNonBreaking extends Rule {
|
|
|
7607
7743
|
}
|
|
7608
7744
|
}
|
|
7609
7745
|
|
|
7610
|
-
function hasAltText(image) {
|
|
7611
|
-
const alt = image.getAttribute("alt");
|
|
7612
|
-
/* missing or boolean */
|
|
7613
|
-
if (alt === null || alt.value === null) {
|
|
7614
|
-
return false;
|
|
7615
|
-
}
|
|
7616
|
-
return alt.isDynamic || alt.value.toString() !== "";
|
|
7617
|
-
}
|
|
7618
|
-
|
|
7619
|
-
function hasAriaLabel(node) {
|
|
7620
|
-
const label = node.getAttribute("aria-label");
|
|
7621
|
-
/* missing or boolean */
|
|
7622
|
-
if (label === null || label.value === null) {
|
|
7623
|
-
return false;
|
|
7624
|
-
}
|
|
7625
|
-
return label.isDynamic || label.value.toString() !== "";
|
|
7626
|
-
}
|
|
7627
|
-
|
|
7628
7746
|
/**
|
|
7629
7747
|
* Check if attribute is present and non-empty or dynamic.
|
|
7630
7748
|
*/
|
|
@@ -9802,13 +9920,13 @@ class VoidStyle extends Rule {
|
|
|
9802
9920
|
return;
|
|
9803
9921
|
}
|
|
9804
9922
|
if (this.shouldBeOmitted(node)) {
|
|
9805
|
-
this.
|
|
9923
|
+
this.reportError(node, `Expected omitted end tag <${node.tagName}> instead of self-closing element <${node.tagName}/>`);
|
|
9806
9924
|
}
|
|
9807
9925
|
if (this.shouldBeSelfClosed(node)) {
|
|
9808
|
-
this.
|
|
9926
|
+
this.reportError(node, `Expected self-closing element <${node.tagName}/> instead of omitted end-tag <${node.tagName}>`);
|
|
9809
9927
|
}
|
|
9810
9928
|
}
|
|
9811
|
-
|
|
9929
|
+
reportError(node, message) {
|
|
9812
9930
|
const context = {
|
|
9813
9931
|
style: this.style,
|
|
9814
9932
|
tagName: node.tagName,
|
|
@@ -10120,6 +10238,7 @@ const bundledRules = {
|
|
|
10120
10238
|
"element-permitted-content": ElementPermittedContent,
|
|
10121
10239
|
"element-permitted-occurrences": ElementPermittedOccurrences,
|
|
10122
10240
|
"element-permitted-order": ElementPermittedOrder,
|
|
10241
|
+
"element-required-ancestor": ElementRequiredAncestor,
|
|
10123
10242
|
"element-required-attributes": ElementRequiredAttributes,
|
|
10124
10243
|
"element-required-content": ElementRequiredContent,
|
|
10125
10244
|
"empty-heading": EmptyHeading,
|
|
@@ -10227,6 +10346,7 @@ const config$1 = {
|
|
|
10227
10346
|
"element-permitted-content": "error",
|
|
10228
10347
|
"element-permitted-occurrences": "error",
|
|
10229
10348
|
"element-permitted-order": "error",
|
|
10349
|
+
"element-required-ancestor": "error",
|
|
10230
10350
|
"element-required-attributes": "error",
|
|
10231
10351
|
"element-required-content": "error",
|
|
10232
10352
|
"empty-heading": "error",
|
|
@@ -10285,6 +10405,7 @@ const config = {
|
|
|
10285
10405
|
"element-permitted-content": "error",
|
|
10286
10406
|
"element-permitted-occurrences": "error",
|
|
10287
10407
|
"element-permitted-order": "error",
|
|
10408
|
+
"element-required-ancestor": "error",
|
|
10288
10409
|
"element-required-attributes": "error",
|
|
10289
10410
|
"element-required-content": "error",
|
|
10290
10411
|
"multiple-labeled-controls": "error",
|
|
@@ -10364,6 +10485,7 @@ class ResolvedConfig {
|
|
|
10364
10485
|
});
|
|
10365
10486
|
}
|
|
10366
10487
|
catch (err) {
|
|
10488
|
+
/* istanbul ignore next: only used as a fallback */
|
|
10367
10489
|
const message = err instanceof Error ? err.message : String(err);
|
|
10368
10490
|
throw new NestedError(`When transforming "${source.filename}": ${message}`, ensureError(err));
|
|
10369
10491
|
}
|
|
@@ -10376,7 +10498,7 @@ class ResolvedConfig {
|
|
|
10376
10498
|
* Wrapper around [[transformSource]] which reads a file before passing it
|
|
10377
10499
|
* as-is to transformSource.
|
|
10378
10500
|
*
|
|
10379
|
-
* @param
|
|
10501
|
+
* @param filename - Filename to transform (according to configured
|
|
10380
10502
|
* transformations)
|
|
10381
10503
|
* @returns A list of transformed sources ready for validation.
|
|
10382
10504
|
*/
|
|
@@ -10532,7 +10654,9 @@ class Config {
|
|
|
10532
10654
|
var _a;
|
|
10533
10655
|
const valid = validator(configData);
|
|
10534
10656
|
if (!valid) {
|
|
10535
|
-
throw new SchemaValidationError(filename, `Invalid configuration`, configData, configurationSchema,
|
|
10657
|
+
throw new SchemaValidationError(filename, `Invalid configuration`, configData, configurationSchema,
|
|
10658
|
+
/* istanbul ignore next: will be set when a validation error has occurred */
|
|
10659
|
+
(_a = validator.errors) !== null && _a !== void 0 ? _a : []);
|
|
10536
10660
|
}
|
|
10537
10661
|
if (configData.rules) {
|
|
10538
10662
|
const normalizedRules = Config.getRulesObject(configData.rules);
|
|
@@ -10641,6 +10765,7 @@ class Config {
|
|
|
10641
10765
|
metaTable.loadFromObject(legacyRequire(entry));
|
|
10642
10766
|
}
|
|
10643
10767
|
catch (err) {
|
|
10768
|
+
/* istanbul ignore next: only used as a fallback */
|
|
10644
10769
|
const message = err instanceof Error ? err.message : String(err);
|
|
10645
10770
|
throw new ConfigError(`Failed to load elements from "${entry}": ${message}`, ensureError(err));
|
|
10646
10771
|
}
|
|
@@ -10662,6 +10787,7 @@ class Config {
|
|
|
10662
10787
|
*
|
|
10663
10788
|
* @internal primary purpose is unittests
|
|
10664
10789
|
*/
|
|
10790
|
+
/* istanbul ignore next: used for testing only */
|
|
10665
10791
|
get() {
|
|
10666
10792
|
const config = { ...this.config };
|
|
10667
10793
|
if (config.elements) {
|
|
@@ -10684,6 +10810,7 @@ class Config {
|
|
|
10684
10810
|
*/
|
|
10685
10811
|
getRules() {
|
|
10686
10812
|
var _a;
|
|
10813
|
+
/* istanbul ignore next: only used as a fallback */
|
|
10687
10814
|
return Config.getRulesObject((_a = this.config.rules) !== null && _a !== void 0 ? _a : {});
|
|
10688
10815
|
}
|
|
10689
10816
|
static getRulesObject(src) {
|
|
@@ -10718,6 +10845,7 @@ class Config {
|
|
|
10718
10845
|
return plugin;
|
|
10719
10846
|
}
|
|
10720
10847
|
catch (err) {
|
|
10848
|
+
/* istanbul ignore next: only used as a fallback */
|
|
10721
10849
|
const message = err instanceof Error ? err.message : String(err);
|
|
10722
10850
|
throw new ConfigError(`Failed to load plugin "${moduleName}": ${message}`, ensureError(err));
|
|
10723
10851
|
}
|
|
@@ -11633,9 +11761,9 @@ class Reporter {
|
|
|
11633
11761
|
if (!(location.filename in this.result)) {
|
|
11634
11762
|
this.result[location.filename] = [];
|
|
11635
11763
|
}
|
|
11636
|
-
|
|
11764
|
+
const ruleUrl = (_a = rule.documentation(context)) === null || _a === void 0 ? void 0 : _a.url;
|
|
11765
|
+
const entry = {
|
|
11637
11766
|
ruleId: rule.name,
|
|
11638
|
-
ruleUrl: (_a = rule.documentation(context)) === null || _a === void 0 ? void 0 : _a.url,
|
|
11639
11767
|
severity,
|
|
11640
11768
|
message,
|
|
11641
11769
|
offset: location.offset,
|
|
@@ -11645,8 +11773,14 @@ class Reporter {
|
|
|
11645
11773
|
selector() {
|
|
11646
11774
|
return node ? node.generateSelector() : null;
|
|
11647
11775
|
},
|
|
11648
|
-
|
|
11649
|
-
|
|
11776
|
+
};
|
|
11777
|
+
if (ruleUrl) {
|
|
11778
|
+
entry.ruleUrl = ruleUrl;
|
|
11779
|
+
}
|
|
11780
|
+
if (context) {
|
|
11781
|
+
entry.context = context;
|
|
11782
|
+
}
|
|
11783
|
+
this.result[location.filename].push(entry);
|
|
11650
11784
|
}
|
|
11651
11785
|
addManual(filename, message) {
|
|
11652
11786
|
if (!(filename in this.result)) {
|
|
@@ -12060,7 +12194,7 @@ class Engine {
|
|
|
12060
12194
|
offset: location.offset,
|
|
12061
12195
|
line: location.line,
|
|
12062
12196
|
column: location.column,
|
|
12063
|
-
size: location.size
|
|
12197
|
+
size: location.size,
|
|
12064
12198
|
selector: () => null,
|
|
12065
12199
|
});
|
|
12066
12200
|
}
|
|
@@ -12490,12 +12624,12 @@ class FileSystemConfigLoader extends ConfigLoader {
|
|
|
12490
12624
|
* `null` if no configuration files are found.
|
|
12491
12625
|
*/
|
|
12492
12626
|
fromFilename(filename) {
|
|
12493
|
-
var _a;
|
|
12494
12627
|
if (filename === "inline") {
|
|
12495
12628
|
return null;
|
|
12496
12629
|
}
|
|
12497
|
-
|
|
12498
|
-
|
|
12630
|
+
const cache = this.cache.get(filename);
|
|
12631
|
+
if (cache) {
|
|
12632
|
+
return cache;
|
|
12499
12633
|
}
|
|
12500
12634
|
let found = false;
|
|
12501
12635
|
let current = path__default["default"].resolve(path__default["default"].dirname(filename));
|