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/es/core.js
CHANGED
|
@@ -3086,7 +3086,7 @@ var TRANSFORMER_API;
|
|
|
3086
3086
|
/** @public */
|
|
3087
3087
|
const name = "html-validate";
|
|
3088
3088
|
/** @public */
|
|
3089
|
-
const version = "7.
|
|
3089
|
+
const version = "7.2.0";
|
|
3090
3090
|
/** @public */
|
|
3091
3091
|
const homepage = "https://html-validate.org";
|
|
3092
3092
|
/** @public */
|
|
@@ -3187,6 +3187,18 @@ function getSchemaValidator(ruleId, properties) {
|
|
|
3187
3187
|
};
|
|
3188
3188
|
return ajv$1.compile(schema);
|
|
3189
3189
|
}
|
|
3190
|
+
function isErrorDescriptor(value) {
|
|
3191
|
+
return Boolean(value[0] && value[0].message);
|
|
3192
|
+
}
|
|
3193
|
+
function unpackErrorDescriptor(value) {
|
|
3194
|
+
if (isErrorDescriptor(value)) {
|
|
3195
|
+
return value[0];
|
|
3196
|
+
}
|
|
3197
|
+
else {
|
|
3198
|
+
const [node, message, location, context] = value;
|
|
3199
|
+
return { node, message, location, context };
|
|
3200
|
+
}
|
|
3201
|
+
}
|
|
3190
3202
|
/**
|
|
3191
3203
|
* @public
|
|
3192
3204
|
*/
|
|
@@ -3287,13 +3299,8 @@ class Rule {
|
|
|
3287
3299
|
static schema() {
|
|
3288
3300
|
return null;
|
|
3289
3301
|
}
|
|
3290
|
-
|
|
3291
|
-
|
|
3292
|
-
*
|
|
3293
|
-
* Rule must be enabled both globally and on the specific node for this to
|
|
3294
|
-
* have any effect.
|
|
3295
|
-
*/
|
|
3296
|
-
report(node, message, location, context) {
|
|
3302
|
+
report(...args) {
|
|
3303
|
+
const { node, message, location, context } = unpackErrorDescriptor(args);
|
|
3297
3304
|
if (this.isEnabled() && (!node || node.ruleEnabled(this.name))) {
|
|
3298
3305
|
const where = this.findLocation({ node, location, event: this.event });
|
|
3299
3306
|
const interpolated = interpolate(message, context !== null && context !== void 0 ? context : {});
|
|
@@ -3806,9 +3813,14 @@ class AttrCase extends Rule {
|
|
|
3806
3813
|
return;
|
|
3807
3814
|
}
|
|
3808
3815
|
const letters = event.key.replace(/[^a-z]+/gi, "");
|
|
3809
|
-
if (
|
|
3810
|
-
|
|
3816
|
+
if (this.style.match(letters)) {
|
|
3817
|
+
return;
|
|
3811
3818
|
}
|
|
3819
|
+
this.report({
|
|
3820
|
+
node: event.target,
|
|
3821
|
+
message: `Attribute "${event.key}" should be ${this.style.name}`,
|
|
3822
|
+
location: event.keyLocation,
|
|
3823
|
+
});
|
|
3812
3824
|
});
|
|
3813
3825
|
}
|
|
3814
3826
|
isIgnored(node) {
|
|
@@ -5108,6 +5120,11 @@ class ElementName extends Rule {
|
|
|
5108
5120
|
}
|
|
5109
5121
|
}
|
|
5110
5122
|
|
|
5123
|
+
var ErrorKind;
|
|
5124
|
+
(function (ErrorKind) {
|
|
5125
|
+
ErrorKind["CONTENT"] = "content";
|
|
5126
|
+
ErrorKind["DESCENDANT"] = "descendant";
|
|
5127
|
+
})(ErrorKind || (ErrorKind = {}));
|
|
5111
5128
|
function getTransparentChildren(node, transparent) {
|
|
5112
5129
|
if (typeof transparent === "boolean") {
|
|
5113
5130
|
return node.childElements;
|
|
@@ -5121,10 +5138,28 @@ function getTransparentChildren(node, transparent) {
|
|
|
5121
5138
|
});
|
|
5122
5139
|
}
|
|
5123
5140
|
}
|
|
5141
|
+
function getRuleDescription$1(context) {
|
|
5142
|
+
if (!context) {
|
|
5143
|
+
return [
|
|
5144
|
+
"Some elements has restrictions on what content is allowed.",
|
|
5145
|
+
"This can include both direct children or descendant elements.",
|
|
5146
|
+
];
|
|
5147
|
+
}
|
|
5148
|
+
switch (context.kind) {
|
|
5149
|
+
case ErrorKind.CONTENT:
|
|
5150
|
+
return [
|
|
5151
|
+
`The \`${context.child}\` element is not permitted as content under the parent \`${context.parent}\` element.`,
|
|
5152
|
+
];
|
|
5153
|
+
case ErrorKind.DESCENDANT:
|
|
5154
|
+
return [
|
|
5155
|
+
`The \`${context.child}\` element is not permitted as a descendant of the \`${context.ancestor}\` element.`,
|
|
5156
|
+
];
|
|
5157
|
+
}
|
|
5158
|
+
}
|
|
5124
5159
|
class ElementPermittedContent extends Rule {
|
|
5125
|
-
documentation() {
|
|
5160
|
+
documentation(context) {
|
|
5126
5161
|
return {
|
|
5127
|
-
description:
|
|
5162
|
+
description: getRuleDescription$1(context).join("\n"),
|
|
5128
5163
|
url: ruleDocumentationUrl("@/rules/element-permitted-content.ts"),
|
|
5129
5164
|
};
|
|
5130
5165
|
}
|
|
@@ -5133,8 +5168,9 @@ class ElementPermittedContent extends Rule {
|
|
|
5133
5168
|
const doc = event.document;
|
|
5134
5169
|
doc.visitDepthFirst((node) => {
|
|
5135
5170
|
const parent = node.parent;
|
|
5136
|
-
/*
|
|
5137
|
-
|
|
5171
|
+
/* istanbul ignore next: satisfy typescript but will visitDepthFirst()
|
|
5172
|
+
* will not yield nodes without a parent */
|
|
5173
|
+
if (!parent) {
|
|
5138
5174
|
return;
|
|
5139
5175
|
}
|
|
5140
5176
|
/* Run each validation step, stop as soon as any errors are
|
|
@@ -5144,7 +5180,6 @@ class ElementPermittedContent extends Rule {
|
|
|
5144
5180
|
[
|
|
5145
5181
|
() => this.validatePermittedContent(node, parent),
|
|
5146
5182
|
() => this.validatePermittedDescendant(node, parent),
|
|
5147
|
-
() => this.validatePermittedAncestors(node),
|
|
5148
5183
|
].some((fn) => fn());
|
|
5149
5184
|
});
|
|
5150
5185
|
});
|
|
@@ -5161,7 +5196,14 @@ class ElementPermittedContent extends Rule {
|
|
|
5161
5196
|
}
|
|
5162
5197
|
validatePermittedContentImpl(cur, parent, rules) {
|
|
5163
5198
|
if (!Validator.validatePermitted(cur, rules)) {
|
|
5164
|
-
|
|
5199
|
+
const child = `<${cur.tagName}>`;
|
|
5200
|
+
const message = `${child} element is not permitted as content under ${parent.annotatedName}`;
|
|
5201
|
+
const context = {
|
|
5202
|
+
kind: ErrorKind.CONTENT,
|
|
5203
|
+
parent: parent.annotatedName,
|
|
5204
|
+
child,
|
|
5205
|
+
};
|
|
5206
|
+
this.report(cur, message, null, context);
|
|
5165
5207
|
return true;
|
|
5166
5208
|
}
|
|
5167
5209
|
/* for transparent elements all/listed children must be validated against
|
|
@@ -5192,21 +5234,15 @@ class ElementPermittedContent extends Rule {
|
|
|
5192
5234
|
if (Validator.validatePermitted(node, rules)) {
|
|
5193
5235
|
continue;
|
|
5194
5236
|
}
|
|
5195
|
-
|
|
5196
|
-
|
|
5197
|
-
|
|
5198
|
-
|
|
5199
|
-
|
|
5200
|
-
|
|
5201
|
-
|
|
5202
|
-
|
|
5203
|
-
|
|
5204
|
-
const rules = node.meta.requiredAncestors;
|
|
5205
|
-
if (!rules) {
|
|
5206
|
-
return false;
|
|
5207
|
-
}
|
|
5208
|
-
if (!Validator.validateAncestors(node, rules)) {
|
|
5209
|
-
this.report(node, `Element <${node.tagName}> requires an "${rules[0]}" ancestor`);
|
|
5237
|
+
const child = `<${node.tagName}>`;
|
|
5238
|
+
const ancestor = cur.annotatedName;
|
|
5239
|
+
const message = `${child} element is not permitted as a descendant of ${ancestor}`;
|
|
5240
|
+
const context = {
|
|
5241
|
+
kind: ErrorKind.DESCENDANT,
|
|
5242
|
+
ancestor,
|
|
5243
|
+
child,
|
|
5244
|
+
};
|
|
5245
|
+
this.report(node, message, null, context);
|
|
5210
5246
|
return true;
|
|
5211
5247
|
}
|
|
5212
5248
|
return false;
|
|
@@ -5273,6 +5309,152 @@ class ElementPermittedOrder extends Rule {
|
|
|
5273
5309
|
}
|
|
5274
5310
|
}
|
|
5275
5311
|
|
|
5312
|
+
const CACHE_KEY = Symbol(classifyNodeText.name);
|
|
5313
|
+
var TextClassification;
|
|
5314
|
+
(function (TextClassification) {
|
|
5315
|
+
TextClassification[TextClassification["EMPTY_TEXT"] = 0] = "EMPTY_TEXT";
|
|
5316
|
+
TextClassification[TextClassification["DYNAMIC_TEXT"] = 1] = "DYNAMIC_TEXT";
|
|
5317
|
+
TextClassification[TextClassification["STATIC_TEXT"] = 2] = "STATIC_TEXT";
|
|
5318
|
+
})(TextClassification || (TextClassification = {}));
|
|
5319
|
+
/**
|
|
5320
|
+
* Checks text content of an element.
|
|
5321
|
+
*
|
|
5322
|
+
* Any text is considered including text from descendant elements. Whitespace is
|
|
5323
|
+
* ignored.
|
|
5324
|
+
*
|
|
5325
|
+
* If any text is dynamic `TextClassification.DYNAMIC_TEXT` is returned.
|
|
5326
|
+
*/
|
|
5327
|
+
function classifyNodeText(node) {
|
|
5328
|
+
if (node.cacheExists(CACHE_KEY)) {
|
|
5329
|
+
return node.cacheGet(CACHE_KEY);
|
|
5330
|
+
}
|
|
5331
|
+
const text = findTextNodes(node);
|
|
5332
|
+
/* if any text is dynamic classify as dynamic */
|
|
5333
|
+
if (text.some((cur) => cur.isDynamic)) {
|
|
5334
|
+
return node.cacheSet(CACHE_KEY, TextClassification.DYNAMIC_TEXT);
|
|
5335
|
+
}
|
|
5336
|
+
/* if any text has non-whitespace character classify as static */
|
|
5337
|
+
if (text.some((cur) => cur.textContent.match(/\S/) !== null)) {
|
|
5338
|
+
return node.cacheSet(CACHE_KEY, TextClassification.STATIC_TEXT);
|
|
5339
|
+
}
|
|
5340
|
+
/* default to empty */
|
|
5341
|
+
return node.cacheSet(CACHE_KEY, TextClassification.EMPTY_TEXT);
|
|
5342
|
+
}
|
|
5343
|
+
function findTextNodes(node) {
|
|
5344
|
+
let text = [];
|
|
5345
|
+
for (const child of node.childNodes) {
|
|
5346
|
+
switch (child.nodeType) {
|
|
5347
|
+
case NodeType.TEXT_NODE:
|
|
5348
|
+
text.push(child);
|
|
5349
|
+
break;
|
|
5350
|
+
case NodeType.ELEMENT_NODE:
|
|
5351
|
+
text = text.concat(findTextNodes(child));
|
|
5352
|
+
break;
|
|
5353
|
+
}
|
|
5354
|
+
}
|
|
5355
|
+
return text;
|
|
5356
|
+
}
|
|
5357
|
+
|
|
5358
|
+
function hasAltText(image) {
|
|
5359
|
+
const alt = image.getAttribute("alt");
|
|
5360
|
+
/* missing or boolean */
|
|
5361
|
+
if (alt === null || alt.value === null) {
|
|
5362
|
+
return false;
|
|
5363
|
+
}
|
|
5364
|
+
return alt.isDynamic || alt.value.toString() !== "";
|
|
5365
|
+
}
|
|
5366
|
+
|
|
5367
|
+
function hasAriaLabel(node) {
|
|
5368
|
+
const label = node.getAttribute("aria-label");
|
|
5369
|
+
/* missing or boolean */
|
|
5370
|
+
if (label === null || label.value === null) {
|
|
5371
|
+
return false;
|
|
5372
|
+
}
|
|
5373
|
+
return label.isDynamic || label.value.toString() !== "";
|
|
5374
|
+
}
|
|
5375
|
+
|
|
5376
|
+
/**
|
|
5377
|
+
* Joins a list of words into natural language.
|
|
5378
|
+
*
|
|
5379
|
+
* - `["foo"]` becomes `"foo"`
|
|
5380
|
+
* - `["foo", "bar"]` becomes `"foo or bar"`
|
|
5381
|
+
* - `["foo", "bar", "baz"]` becomes `"foo, bar or baz"`
|
|
5382
|
+
* - and so on...
|
|
5383
|
+
*
|
|
5384
|
+
* @internal
|
|
5385
|
+
* @param values - List of words to join
|
|
5386
|
+
* @param conjunction - Conjunction for the last element.
|
|
5387
|
+
* @returns String with the words naturally joined with a conjunction.
|
|
5388
|
+
*/
|
|
5389
|
+
function naturalJoin(values, conjunction = "or") {
|
|
5390
|
+
switch (values.length) {
|
|
5391
|
+
case 0:
|
|
5392
|
+
return "";
|
|
5393
|
+
case 1:
|
|
5394
|
+
return values[0];
|
|
5395
|
+
case 2:
|
|
5396
|
+
return `${values[0]} ${conjunction} ${values[1]}`;
|
|
5397
|
+
default:
|
|
5398
|
+
return `${values.slice(0, -1).join(", ")} ${conjunction} ${values.slice(-1)[0]}`;
|
|
5399
|
+
}
|
|
5400
|
+
}
|
|
5401
|
+
|
|
5402
|
+
function isTagnameOnly(value) {
|
|
5403
|
+
return Boolean(value.match(/^[a-zA-Z0-9-]+$/));
|
|
5404
|
+
}
|
|
5405
|
+
function getRuleDescription(context) {
|
|
5406
|
+
if (!context) {
|
|
5407
|
+
return [
|
|
5408
|
+
"Some elements has restrictions on what content is allowed.",
|
|
5409
|
+
"This can include both direct children or descendant elements.",
|
|
5410
|
+
];
|
|
5411
|
+
}
|
|
5412
|
+
const escaped = context.ancestor.map((it) => `\`${it}\``);
|
|
5413
|
+
return [`The \`${context.child}\` element requires a ${naturalJoin(escaped)} ancestor.`];
|
|
5414
|
+
}
|
|
5415
|
+
class ElementRequiredAncestor extends Rule {
|
|
5416
|
+
documentation(context) {
|
|
5417
|
+
return {
|
|
5418
|
+
description: getRuleDescription(context).join("\n"),
|
|
5419
|
+
url: ruleDocumentationUrl("@/rules/element-required-ancestor.ts"),
|
|
5420
|
+
};
|
|
5421
|
+
}
|
|
5422
|
+
setup() {
|
|
5423
|
+
this.on("dom:ready", (event) => {
|
|
5424
|
+
const doc = event.document;
|
|
5425
|
+
doc.visitDepthFirst((node) => {
|
|
5426
|
+
const parent = node.parent;
|
|
5427
|
+
/* istanbul ignore next: satisfy typescript but will visitDepthFirst()
|
|
5428
|
+
* will not yield nodes without a parent */
|
|
5429
|
+
if (!parent) {
|
|
5430
|
+
return;
|
|
5431
|
+
}
|
|
5432
|
+
this.validateRequiredAncestors(node);
|
|
5433
|
+
});
|
|
5434
|
+
});
|
|
5435
|
+
}
|
|
5436
|
+
validateRequiredAncestors(node) {
|
|
5437
|
+
if (!node.meta) {
|
|
5438
|
+
return;
|
|
5439
|
+
}
|
|
5440
|
+
const rules = node.meta.requiredAncestors;
|
|
5441
|
+
if (!rules) {
|
|
5442
|
+
return;
|
|
5443
|
+
}
|
|
5444
|
+
if (Validator.validateAncestors(node, rules)) {
|
|
5445
|
+
return;
|
|
5446
|
+
}
|
|
5447
|
+
const ancestor = rules.map((it) => (isTagnameOnly(it) ? `<${it}>` : `"${it}"`));
|
|
5448
|
+
const child = `<${node.tagName}>`;
|
|
5449
|
+
const message = `<${node.tagName}> element requires a ${naturalJoin(ancestor)} ancestor`;
|
|
5450
|
+
const context = {
|
|
5451
|
+
ancestor,
|
|
5452
|
+
child,
|
|
5453
|
+
};
|
|
5454
|
+
this.report(node, message, null, context);
|
|
5455
|
+
}
|
|
5456
|
+
}
|
|
5457
|
+
|
|
5276
5458
|
class ElementRequiredAttributes extends Rule {
|
|
5277
5459
|
documentation(context) {
|
|
5278
5460
|
const docs = {
|
|
@@ -5350,52 +5532,6 @@ class ElementRequiredContent extends Rule {
|
|
|
5350
5532
|
}
|
|
5351
5533
|
}
|
|
5352
5534
|
|
|
5353
|
-
const CACHE_KEY = Symbol(classifyNodeText.name);
|
|
5354
|
-
var TextClassification;
|
|
5355
|
-
(function (TextClassification) {
|
|
5356
|
-
TextClassification[TextClassification["EMPTY_TEXT"] = 0] = "EMPTY_TEXT";
|
|
5357
|
-
TextClassification[TextClassification["DYNAMIC_TEXT"] = 1] = "DYNAMIC_TEXT";
|
|
5358
|
-
TextClassification[TextClassification["STATIC_TEXT"] = 2] = "STATIC_TEXT";
|
|
5359
|
-
})(TextClassification || (TextClassification = {}));
|
|
5360
|
-
/**
|
|
5361
|
-
* Checks text content of an element.
|
|
5362
|
-
*
|
|
5363
|
-
* Any text is considered including text from descendant elements. Whitespace is
|
|
5364
|
-
* ignored.
|
|
5365
|
-
*
|
|
5366
|
-
* If any text is dynamic `TextClassification.DYNAMIC_TEXT` is returned.
|
|
5367
|
-
*/
|
|
5368
|
-
function classifyNodeText(node) {
|
|
5369
|
-
if (node.cacheExists(CACHE_KEY)) {
|
|
5370
|
-
return node.cacheGet(CACHE_KEY);
|
|
5371
|
-
}
|
|
5372
|
-
const text = findTextNodes(node);
|
|
5373
|
-
/* if any text is dynamic classify as dynamic */
|
|
5374
|
-
if (text.some((cur) => cur.isDynamic)) {
|
|
5375
|
-
return node.cacheSet(CACHE_KEY, TextClassification.DYNAMIC_TEXT);
|
|
5376
|
-
}
|
|
5377
|
-
/* if any text has non-whitespace character classify as static */
|
|
5378
|
-
if (text.some((cur) => cur.textContent.match(/\S/) !== null)) {
|
|
5379
|
-
return node.cacheSet(CACHE_KEY, TextClassification.STATIC_TEXT);
|
|
5380
|
-
}
|
|
5381
|
-
/* default to empty */
|
|
5382
|
-
return node.cacheSet(CACHE_KEY, TextClassification.EMPTY_TEXT);
|
|
5383
|
-
}
|
|
5384
|
-
function findTextNodes(node) {
|
|
5385
|
-
let text = [];
|
|
5386
|
-
for (const child of node.childNodes) {
|
|
5387
|
-
switch (child.nodeType) {
|
|
5388
|
-
case NodeType.TEXT_NODE:
|
|
5389
|
-
text.push(child);
|
|
5390
|
-
break;
|
|
5391
|
-
case NodeType.ELEMENT_NODE:
|
|
5392
|
-
text = text.concat(findTextNodes(child));
|
|
5393
|
-
break;
|
|
5394
|
-
}
|
|
5395
|
-
}
|
|
5396
|
-
return text;
|
|
5397
|
-
}
|
|
5398
|
-
|
|
5399
5535
|
const selector = ["h1", "h2", "h3", "h4", "h5", "h6"].join(",");
|
|
5400
5536
|
class EmptyHeading extends Rule {
|
|
5401
5537
|
documentation() {
|
|
@@ -7576,24 +7712,6 @@ class TelNonBreaking extends Rule {
|
|
|
7576
7712
|
}
|
|
7577
7713
|
}
|
|
7578
7714
|
|
|
7579
|
-
function hasAltText(image) {
|
|
7580
|
-
const alt = image.getAttribute("alt");
|
|
7581
|
-
/* missing or boolean */
|
|
7582
|
-
if (alt === null || alt.value === null) {
|
|
7583
|
-
return false;
|
|
7584
|
-
}
|
|
7585
|
-
return alt.isDynamic || alt.value.toString() !== "";
|
|
7586
|
-
}
|
|
7587
|
-
|
|
7588
|
-
function hasAriaLabel(node) {
|
|
7589
|
-
const label = node.getAttribute("aria-label");
|
|
7590
|
-
/* missing or boolean */
|
|
7591
|
-
if (label === null || label.value === null) {
|
|
7592
|
-
return false;
|
|
7593
|
-
}
|
|
7594
|
-
return label.isDynamic || label.value.toString() !== "";
|
|
7595
|
-
}
|
|
7596
|
-
|
|
7597
7715
|
/**
|
|
7598
7716
|
* Check if attribute is present and non-empty or dynamic.
|
|
7599
7717
|
*/
|
|
@@ -9771,13 +9889,13 @@ class VoidStyle extends Rule {
|
|
|
9771
9889
|
return;
|
|
9772
9890
|
}
|
|
9773
9891
|
if (this.shouldBeOmitted(node)) {
|
|
9774
|
-
this.
|
|
9892
|
+
this.reportError(node, `Expected omitted end tag <${node.tagName}> instead of self-closing element <${node.tagName}/>`);
|
|
9775
9893
|
}
|
|
9776
9894
|
if (this.shouldBeSelfClosed(node)) {
|
|
9777
|
-
this.
|
|
9895
|
+
this.reportError(node, `Expected self-closing element <${node.tagName}/> instead of omitted end-tag <${node.tagName}>`);
|
|
9778
9896
|
}
|
|
9779
9897
|
}
|
|
9780
|
-
|
|
9898
|
+
reportError(node, message) {
|
|
9781
9899
|
const context = {
|
|
9782
9900
|
style: this.style,
|
|
9783
9901
|
tagName: node.tagName,
|
|
@@ -10089,6 +10207,7 @@ const bundledRules = {
|
|
|
10089
10207
|
"element-permitted-content": ElementPermittedContent,
|
|
10090
10208
|
"element-permitted-occurrences": ElementPermittedOccurrences,
|
|
10091
10209
|
"element-permitted-order": ElementPermittedOrder,
|
|
10210
|
+
"element-required-ancestor": ElementRequiredAncestor,
|
|
10092
10211
|
"element-required-attributes": ElementRequiredAttributes,
|
|
10093
10212
|
"element-required-content": ElementRequiredContent,
|
|
10094
10213
|
"empty-heading": EmptyHeading,
|
|
@@ -10196,6 +10315,7 @@ const config$1 = {
|
|
|
10196
10315
|
"element-permitted-content": "error",
|
|
10197
10316
|
"element-permitted-occurrences": "error",
|
|
10198
10317
|
"element-permitted-order": "error",
|
|
10318
|
+
"element-required-ancestor": "error",
|
|
10199
10319
|
"element-required-attributes": "error",
|
|
10200
10320
|
"element-required-content": "error",
|
|
10201
10321
|
"empty-heading": "error",
|
|
@@ -10254,6 +10374,7 @@ const config = {
|
|
|
10254
10374
|
"element-permitted-content": "error",
|
|
10255
10375
|
"element-permitted-occurrences": "error",
|
|
10256
10376
|
"element-permitted-order": "error",
|
|
10377
|
+
"element-required-ancestor": "error",
|
|
10257
10378
|
"element-required-attributes": "error",
|
|
10258
10379
|
"element-required-content": "error",
|
|
10259
10380
|
"multiple-labeled-controls": "error",
|
|
@@ -10333,6 +10454,7 @@ class ResolvedConfig {
|
|
|
10333
10454
|
});
|
|
10334
10455
|
}
|
|
10335
10456
|
catch (err) {
|
|
10457
|
+
/* istanbul ignore next: only used as a fallback */
|
|
10336
10458
|
const message = err instanceof Error ? err.message : String(err);
|
|
10337
10459
|
throw new NestedError(`When transforming "${source.filename}": ${message}`, ensureError(err));
|
|
10338
10460
|
}
|
|
@@ -10345,7 +10467,7 @@ class ResolvedConfig {
|
|
|
10345
10467
|
* Wrapper around [[transformSource]] which reads a file before passing it
|
|
10346
10468
|
* as-is to transformSource.
|
|
10347
10469
|
*
|
|
10348
|
-
* @param
|
|
10470
|
+
* @param filename - Filename to transform (according to configured
|
|
10349
10471
|
* transformations)
|
|
10350
10472
|
* @returns A list of transformed sources ready for validation.
|
|
10351
10473
|
*/
|
|
@@ -10501,7 +10623,9 @@ class Config {
|
|
|
10501
10623
|
var _a;
|
|
10502
10624
|
const valid = validator(configData);
|
|
10503
10625
|
if (!valid) {
|
|
10504
|
-
throw new SchemaValidationError(filename, `Invalid configuration`, configData, configurationSchema,
|
|
10626
|
+
throw new SchemaValidationError(filename, `Invalid configuration`, configData, configurationSchema,
|
|
10627
|
+
/* istanbul ignore next: will be set when a validation error has occurred */
|
|
10628
|
+
(_a = validator.errors) !== null && _a !== void 0 ? _a : []);
|
|
10505
10629
|
}
|
|
10506
10630
|
if (configData.rules) {
|
|
10507
10631
|
const normalizedRules = Config.getRulesObject(configData.rules);
|
|
@@ -10610,6 +10734,7 @@ class Config {
|
|
|
10610
10734
|
metaTable.loadFromObject(legacyRequire(entry));
|
|
10611
10735
|
}
|
|
10612
10736
|
catch (err) {
|
|
10737
|
+
/* istanbul ignore next: only used as a fallback */
|
|
10613
10738
|
const message = err instanceof Error ? err.message : String(err);
|
|
10614
10739
|
throw new ConfigError(`Failed to load elements from "${entry}": ${message}`, ensureError(err));
|
|
10615
10740
|
}
|
|
@@ -10631,6 +10756,7 @@ class Config {
|
|
|
10631
10756
|
*
|
|
10632
10757
|
* @internal primary purpose is unittests
|
|
10633
10758
|
*/
|
|
10759
|
+
/* istanbul ignore next: used for testing only */
|
|
10634
10760
|
get() {
|
|
10635
10761
|
const config = { ...this.config };
|
|
10636
10762
|
if (config.elements) {
|
|
@@ -10653,6 +10779,7 @@ class Config {
|
|
|
10653
10779
|
*/
|
|
10654
10780
|
getRules() {
|
|
10655
10781
|
var _a;
|
|
10782
|
+
/* istanbul ignore next: only used as a fallback */
|
|
10656
10783
|
return Config.getRulesObject((_a = this.config.rules) !== null && _a !== void 0 ? _a : {});
|
|
10657
10784
|
}
|
|
10658
10785
|
static getRulesObject(src) {
|
|
@@ -10687,6 +10814,7 @@ class Config {
|
|
|
10687
10814
|
return plugin;
|
|
10688
10815
|
}
|
|
10689
10816
|
catch (err) {
|
|
10817
|
+
/* istanbul ignore next: only used as a fallback */
|
|
10690
10818
|
const message = err instanceof Error ? err.message : String(err);
|
|
10691
10819
|
throw new ConfigError(`Failed to load plugin "${moduleName}": ${message}`, ensureError(err));
|
|
10692
10820
|
}
|
|
@@ -11602,9 +11730,9 @@ class Reporter {
|
|
|
11602
11730
|
if (!(location.filename in this.result)) {
|
|
11603
11731
|
this.result[location.filename] = [];
|
|
11604
11732
|
}
|
|
11605
|
-
|
|
11733
|
+
const ruleUrl = (_a = rule.documentation(context)) === null || _a === void 0 ? void 0 : _a.url;
|
|
11734
|
+
const entry = {
|
|
11606
11735
|
ruleId: rule.name,
|
|
11607
|
-
ruleUrl: (_a = rule.documentation(context)) === null || _a === void 0 ? void 0 : _a.url,
|
|
11608
11736
|
severity,
|
|
11609
11737
|
message,
|
|
11610
11738
|
offset: location.offset,
|
|
@@ -11614,8 +11742,14 @@ class Reporter {
|
|
|
11614
11742
|
selector() {
|
|
11615
11743
|
return node ? node.generateSelector() : null;
|
|
11616
11744
|
},
|
|
11617
|
-
|
|
11618
|
-
|
|
11745
|
+
};
|
|
11746
|
+
if (ruleUrl) {
|
|
11747
|
+
entry.ruleUrl = ruleUrl;
|
|
11748
|
+
}
|
|
11749
|
+
if (context) {
|
|
11750
|
+
entry.context = context;
|
|
11751
|
+
}
|
|
11752
|
+
this.result[location.filename].push(entry);
|
|
11619
11753
|
}
|
|
11620
11754
|
addManual(filename, message) {
|
|
11621
11755
|
if (!(filename in this.result)) {
|
|
@@ -12029,7 +12163,7 @@ class Engine {
|
|
|
12029
12163
|
offset: location.offset,
|
|
12030
12164
|
line: location.line,
|
|
12031
12165
|
column: location.column,
|
|
12032
|
-
size: location.size
|
|
12166
|
+
size: location.size,
|
|
12033
12167
|
selector: () => null,
|
|
12034
12168
|
});
|
|
12035
12169
|
}
|
|
@@ -12459,12 +12593,12 @@ class FileSystemConfigLoader extends ConfigLoader {
|
|
|
12459
12593
|
* `null` if no configuration files are found.
|
|
12460
12594
|
*/
|
|
12461
12595
|
fromFilename(filename) {
|
|
12462
|
-
var _a;
|
|
12463
12596
|
if (filename === "inline") {
|
|
12464
12597
|
return null;
|
|
12465
12598
|
}
|
|
12466
|
-
|
|
12467
|
-
|
|
12599
|
+
const cache = this.cache.get(filename);
|
|
12600
|
+
if (cache) {
|
|
12601
|
+
return cache;
|
|
12468
12602
|
}
|
|
12469
12603
|
let found = false;
|
|
12470
12604
|
let current = path.resolve(path.dirname(filename));
|