html-validate 10.5.0 → 10.7.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 +3 -1
- package/dist/cjs/cli.js.map +1 -1
- package/dist/cjs/core.js +308 -92
- package/dist/cjs/core.js.map +1 -1
- package/dist/cjs/matchers.js.map +1 -1
- package/dist/esm/browser.js +1 -1
- package/dist/esm/cli.js +4 -2
- package/dist/esm/cli.js.map +1 -1
- package/dist/esm/core-browser.js +1 -1
- package/dist/esm/core-nodejs.js +1 -1
- package/dist/esm/core.js +309 -93
- package/dist/esm/core.js.map +1 -1
- package/dist/esm/html-validate.js +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/matchers.js +1 -1
- package/dist/esm/matchers.js.map +1 -1
- package/dist/types/browser.d.ts +14 -0
- package/dist/types/index.d.ts +14 -0
- package/package.json +1 -1
package/dist/cjs/core.js
CHANGED
|
@@ -1216,7 +1216,9 @@ class MetaTable {
|
|
|
1216
1216
|
);
|
|
1217
1217
|
}
|
|
1218
1218
|
for (const [key, value] of Object.entries(obj)) {
|
|
1219
|
-
if (key === "$schema")
|
|
1219
|
+
if (key === "$schema") {
|
|
1220
|
+
continue;
|
|
1221
|
+
}
|
|
1220
1222
|
this.addEntry(key, migrateElement(value));
|
|
1221
1223
|
}
|
|
1222
1224
|
} catch (err) {
|
|
@@ -1316,7 +1318,9 @@ class MetaTable {
|
|
|
1316
1318
|
* global, e.g. to assign global attributes.
|
|
1317
1319
|
*/
|
|
1318
1320
|
resolveGlobal() {
|
|
1319
|
-
if (!this.elements["*"])
|
|
1321
|
+
if (!this.elements["*"]) {
|
|
1322
|
+
return;
|
|
1323
|
+
}
|
|
1320
1324
|
const global = this.elements["*"];
|
|
1321
1325
|
delete this.elements["*"];
|
|
1322
1326
|
delete global.tagName;
|
|
@@ -1490,7 +1494,9 @@ function sliceSize(size, begin, end) {
|
|
|
1490
1494
|
return Math.min(size, end - begin);
|
|
1491
1495
|
}
|
|
1492
1496
|
function sliceLocation(location, begin, end, wrap) {
|
|
1493
|
-
if (!location)
|
|
1497
|
+
if (!location) {
|
|
1498
|
+
return null;
|
|
1499
|
+
}
|
|
1494
1500
|
const size = sliceSize(location.size, begin, end);
|
|
1495
1501
|
const sliced = {
|
|
1496
1502
|
filename: location.filename,
|
|
@@ -2369,7 +2375,7 @@ class Selector {
|
|
|
2369
2375
|
|
|
2370
2376
|
const TEXT_NODE_NAME = "#text";
|
|
2371
2377
|
function isTextNode(node) {
|
|
2372
|
-
return
|
|
2378
|
+
return node?.nodeType === NodeType.TEXT_NODE;
|
|
2373
2379
|
}
|
|
2374
2380
|
class TextNode extends DOMNode {
|
|
2375
2381
|
text;
|
|
@@ -2413,7 +2419,7 @@ var NodeClosed = /* @__PURE__ */ ((NodeClosed2) => {
|
|
|
2413
2419
|
return NodeClosed2;
|
|
2414
2420
|
})(NodeClosed || {});
|
|
2415
2421
|
function isElementNode(node) {
|
|
2416
|
-
return
|
|
2422
|
+
return node?.nodeType === NodeType.ELEMENT_NODE;
|
|
2417
2423
|
}
|
|
2418
2424
|
function isInvalidTagName(tagName) {
|
|
2419
2425
|
return tagName === "" || tagName === "*";
|
|
@@ -3709,6 +3715,7 @@ function interpolate(text, data) {
|
|
|
3709
3715
|
|
|
3710
3716
|
const ajv$1 = new Ajv__default.default({ strict: true, strictTuples: true, strictTypes: true });
|
|
3711
3717
|
ajv$1.addMetaSchema(ajvSchemaDraft);
|
|
3718
|
+
ajv$1.addKeyword(ajvRegexpKeyword);
|
|
3712
3719
|
function getSchemaValidator(ruleId, properties) {
|
|
3713
3720
|
const $id = `rule/${ruleId}`;
|
|
3714
3721
|
const cached = ajv$1.getSchema($id);
|
|
@@ -3965,6 +3972,7 @@ class Rule {
|
|
|
3965
3972
|
*
|
|
3966
3973
|
* @internal
|
|
3967
3974
|
*/
|
|
3975
|
+
/* eslint-disable-next-line @typescript-eslint/max-params -- technical debt */
|
|
3968
3976
|
static validateOptions(cls, ruleId, jsonPath, options, filename, config) {
|
|
3969
3977
|
if (!cls) {
|
|
3970
3978
|
return;
|
|
@@ -4000,7 +4008,7 @@ class Rule {
|
|
|
4000
4008
|
}
|
|
4001
4009
|
}
|
|
4002
4010
|
|
|
4003
|
-
const defaults$
|
|
4011
|
+
const defaults$z = {
|
|
4004
4012
|
allowExternal: true,
|
|
4005
4013
|
allowRelative: true,
|
|
4006
4014
|
allowAbsolute: true,
|
|
@@ -4044,7 +4052,7 @@ class AllowedLinks extends Rule {
|
|
|
4044
4052
|
allowRelative;
|
|
4045
4053
|
allowAbsolute;
|
|
4046
4054
|
constructor(options) {
|
|
4047
|
-
super({ ...defaults$
|
|
4055
|
+
super({ ...defaults$z, ...options });
|
|
4048
4056
|
this.allowExternal = parseAllow(this.options.allowExternal);
|
|
4049
4057
|
this.allowRelative = parseAllow(this.options.allowRelative);
|
|
4050
4058
|
this.allowAbsolute = parseAllow(this.options.allowAbsolute);
|
|
@@ -4212,7 +4220,7 @@ class AllowedLinks extends Rule {
|
|
|
4212
4220
|
}
|
|
4213
4221
|
}
|
|
4214
4222
|
|
|
4215
|
-
const defaults$
|
|
4223
|
+
const defaults$y = {
|
|
4216
4224
|
accessible: true
|
|
4217
4225
|
};
|
|
4218
4226
|
function findByTarget(target, siblings) {
|
|
@@ -4242,7 +4250,7 @@ function getDescription$1(context) {
|
|
|
4242
4250
|
}
|
|
4243
4251
|
class AreaAlt extends Rule {
|
|
4244
4252
|
constructor(options) {
|
|
4245
|
-
super({ ...defaults$
|
|
4253
|
+
super({ ...defaults$y, ...options });
|
|
4246
4254
|
}
|
|
4247
4255
|
static schema() {
|
|
4248
4256
|
return {
|
|
@@ -4321,8 +4329,12 @@ class AriaHiddenBody extends Rule {
|
|
|
4321
4329
|
}
|
|
4322
4330
|
}
|
|
4323
4331
|
|
|
4324
|
-
const defaults$
|
|
4325
|
-
allowAnyNamable: false
|
|
4332
|
+
const defaults$x = {
|
|
4333
|
+
allowAnyNamable: false,
|
|
4334
|
+
elements: {
|
|
4335
|
+
include: null,
|
|
4336
|
+
exclude: null
|
|
4337
|
+
}
|
|
4326
4338
|
};
|
|
4327
4339
|
const allowlist = /* @__PURE__ */ new Set([
|
|
4328
4340
|
"main",
|
|
@@ -4365,7 +4377,26 @@ function isValidUsage(target, meta) {
|
|
|
4365
4377
|
}
|
|
4366
4378
|
class AriaLabelMisuse extends Rule {
|
|
4367
4379
|
constructor(options) {
|
|
4368
|
-
super({ ...defaults$
|
|
4380
|
+
super({ ...defaults$x, ...options });
|
|
4381
|
+
}
|
|
4382
|
+
static schema() {
|
|
4383
|
+
return {
|
|
4384
|
+
allowAnyNamable: {
|
|
4385
|
+
type: "boolean"
|
|
4386
|
+
},
|
|
4387
|
+
elements: {
|
|
4388
|
+
type: "object",
|
|
4389
|
+
properties: {
|
|
4390
|
+
include: {
|
|
4391
|
+
anyOf: [{ type: "array", items: { type: "string" } }, { type: "null" }]
|
|
4392
|
+
},
|
|
4393
|
+
exclude: {
|
|
4394
|
+
anyOf: [{ type: "array", items: { type: "string" } }, { type: "null" }]
|
|
4395
|
+
}
|
|
4396
|
+
},
|
|
4397
|
+
additionalProperties: false
|
|
4398
|
+
}
|
|
4399
|
+
};
|
|
4369
4400
|
}
|
|
4370
4401
|
documentation(context) {
|
|
4371
4402
|
const valid = [
|
|
@@ -4423,6 +4454,9 @@ class AriaLabelMisuse extends Rule {
|
|
|
4423
4454
|
if (!meta) {
|
|
4424
4455
|
return;
|
|
4425
4456
|
}
|
|
4457
|
+
if (this.shouldIgnoreElement(target)) {
|
|
4458
|
+
return;
|
|
4459
|
+
}
|
|
4426
4460
|
if (isValidUsage(target, meta)) {
|
|
4427
4461
|
return;
|
|
4428
4462
|
}
|
|
@@ -4447,6 +4481,9 @@ class AriaLabelMisuse extends Rule {
|
|
|
4447
4481
|
});
|
|
4448
4482
|
}
|
|
4449
4483
|
}
|
|
4484
|
+
shouldIgnoreElement(target) {
|
|
4485
|
+
return isKeywordIgnored(this.options.elements, target.tagName, keywordPatternMatcher);
|
|
4486
|
+
}
|
|
4450
4487
|
}
|
|
4451
4488
|
|
|
4452
4489
|
class ConfigError extends UserError {
|
|
@@ -4509,14 +4546,14 @@ class CaseStyle {
|
|
|
4509
4546
|
}
|
|
4510
4547
|
}
|
|
4511
4548
|
|
|
4512
|
-
const defaults$
|
|
4549
|
+
const defaults$w = {
|
|
4513
4550
|
style: "lowercase",
|
|
4514
4551
|
ignoreForeign: true
|
|
4515
4552
|
};
|
|
4516
4553
|
class AttrCase extends Rule {
|
|
4517
4554
|
style;
|
|
4518
4555
|
constructor(options) {
|
|
4519
|
-
super({ ...defaults$
|
|
4556
|
+
super({ ...defaults$w, ...options });
|
|
4520
4557
|
this.style = new CaseStyle(this.options.style, "attr-case");
|
|
4521
4558
|
}
|
|
4522
4559
|
static schema() {
|
|
@@ -4623,7 +4660,7 @@ const MATCH_TEXTAREA_DATA = /^[^]*?(?=<\/textarea)/;
|
|
|
4623
4660
|
const MATCH_TEXTAREA_END = /^<(\/)(textarea)/;
|
|
4624
4661
|
const MATCH_TITLE_DATA = /^[^]*?(?=<\/title)/;
|
|
4625
4662
|
const MATCH_TITLE_END = /^<(\/)(title)/;
|
|
4626
|
-
const MATCH_DIRECTIVE = /^(<!--\s*\[html-validate-)([a-z0-9-]+)(\s*)(.*?)(]?\s*-->)/;
|
|
4663
|
+
const MATCH_DIRECTIVE = /^(<!--\s*\[?)(html-validate-)([a-z0-9-]+)(\s*)(.*?)(]?\s*-->)/;
|
|
4627
4664
|
const MATCH_COMMENT = /^<!--([^]*?)-->/;
|
|
4628
4665
|
const MATCH_CONDITIONAL = /^<!\[([^\]]*?)\]>/;
|
|
4629
4666
|
class InvalidTokenError extends Error {
|
|
@@ -4921,7 +4958,7 @@ class AttrDelimiter extends Rule {
|
|
|
4921
4958
|
}
|
|
4922
4959
|
|
|
4923
4960
|
const DEFAULT_PATTERN = "[a-z0-9-:]+";
|
|
4924
|
-
const defaults$
|
|
4961
|
+
const defaults$v = {
|
|
4925
4962
|
pattern: DEFAULT_PATTERN,
|
|
4926
4963
|
ignoreForeign: true
|
|
4927
4964
|
};
|
|
@@ -4954,7 +4991,7 @@ function generateDescription(name, pattern) {
|
|
|
4954
4991
|
class AttrPattern extends Rule {
|
|
4955
4992
|
pattern;
|
|
4956
4993
|
constructor(options) {
|
|
4957
|
-
super({ ...defaults$
|
|
4994
|
+
super({ ...defaults$v, ...options });
|
|
4958
4995
|
this.pattern = generateRegexp(this.options.pattern);
|
|
4959
4996
|
}
|
|
4960
4997
|
static schema() {
|
|
@@ -5001,7 +5038,7 @@ class AttrPattern extends Rule {
|
|
|
5001
5038
|
}
|
|
5002
5039
|
}
|
|
5003
5040
|
|
|
5004
|
-
const defaults$
|
|
5041
|
+
const defaults$u = {
|
|
5005
5042
|
style: "auto",
|
|
5006
5043
|
unquoted: false
|
|
5007
5044
|
};
|
|
@@ -5067,7 +5104,7 @@ class AttrQuotes extends Rule {
|
|
|
5067
5104
|
};
|
|
5068
5105
|
}
|
|
5069
5106
|
constructor(options) {
|
|
5070
|
-
super({ ...defaults$
|
|
5107
|
+
super({ ...defaults$u, ...options });
|
|
5071
5108
|
this.style = parseStyle$3(this.options.style);
|
|
5072
5109
|
}
|
|
5073
5110
|
setup() {
|
|
@@ -5189,7 +5226,9 @@ class AttributeAllowedValues extends Rule {
|
|
|
5189
5226
|
const doc = event.document;
|
|
5190
5227
|
walk.depthFirst(doc, (node) => {
|
|
5191
5228
|
const meta = node.meta;
|
|
5192
|
-
if (!meta?.attributes)
|
|
5229
|
+
if (!meta?.attributes) {
|
|
5230
|
+
return;
|
|
5231
|
+
}
|
|
5193
5232
|
for (const attr of node.attributes) {
|
|
5194
5233
|
if (Validator.validateAttribute(attr, meta.attributes)) {
|
|
5195
5234
|
continue;
|
|
@@ -5221,13 +5260,13 @@ class AttributeAllowedValues extends Rule {
|
|
|
5221
5260
|
}
|
|
5222
5261
|
}
|
|
5223
5262
|
|
|
5224
|
-
const defaults$
|
|
5263
|
+
const defaults$t = {
|
|
5225
5264
|
style: "omit"
|
|
5226
5265
|
};
|
|
5227
5266
|
class AttributeBooleanStyle extends Rule {
|
|
5228
5267
|
hasInvalidStyle;
|
|
5229
5268
|
constructor(options) {
|
|
5230
|
-
super({ ...defaults$
|
|
5269
|
+
super({ ...defaults$t, ...options });
|
|
5231
5270
|
this.hasInvalidStyle = parseStyle$2(this.options.style);
|
|
5232
5271
|
}
|
|
5233
5272
|
static schema() {
|
|
@@ -5249,9 +5288,13 @@ class AttributeBooleanStyle extends Rule {
|
|
|
5249
5288
|
const doc = event.document;
|
|
5250
5289
|
walk.depthFirst(doc, (node) => {
|
|
5251
5290
|
const meta = node.meta;
|
|
5252
|
-
if (!meta?.attributes)
|
|
5291
|
+
if (!meta?.attributes) {
|
|
5292
|
+
return;
|
|
5293
|
+
}
|
|
5253
5294
|
for (const attr of node.attributes) {
|
|
5254
|
-
if (!this.isBoolean(attr, meta.attributes))
|
|
5295
|
+
if (!this.isBoolean(attr, meta.attributes)) {
|
|
5296
|
+
continue;
|
|
5297
|
+
}
|
|
5255
5298
|
if (attr.originalAttribute) {
|
|
5256
5299
|
continue;
|
|
5257
5300
|
}
|
|
@@ -5293,13 +5336,13 @@ function reportMessage$1(attr, style) {
|
|
|
5293
5336
|
return "";
|
|
5294
5337
|
}
|
|
5295
5338
|
|
|
5296
|
-
const defaults$
|
|
5339
|
+
const defaults$s = {
|
|
5297
5340
|
style: "omit"
|
|
5298
5341
|
};
|
|
5299
5342
|
class AttributeEmptyStyle extends Rule {
|
|
5300
5343
|
hasInvalidStyle;
|
|
5301
5344
|
constructor(options) {
|
|
5302
|
-
super({ ...defaults$
|
|
5345
|
+
super({ ...defaults$s, ...options });
|
|
5303
5346
|
this.hasInvalidStyle = parseStyle$1(this.options.style);
|
|
5304
5347
|
}
|
|
5305
5348
|
static schema() {
|
|
@@ -5321,7 +5364,9 @@ class AttributeEmptyStyle extends Rule {
|
|
|
5321
5364
|
const doc = event.document;
|
|
5322
5365
|
walk.depthFirst(doc, (node) => {
|
|
5323
5366
|
const meta = node.meta;
|
|
5324
|
-
if (!meta?.attributes)
|
|
5367
|
+
if (!meta?.attributes) {
|
|
5368
|
+
return;
|
|
5369
|
+
}
|
|
5325
5370
|
for (const attr of node.attributes) {
|
|
5326
5371
|
if (!allowsEmpty(attr, meta.attributes)) {
|
|
5327
5372
|
continue;
|
|
@@ -5414,7 +5459,22 @@ class AttributeMisuse extends Rule {
|
|
|
5414
5459
|
}
|
|
5415
5460
|
}
|
|
5416
5461
|
|
|
5462
|
+
const patternNamesValues = [
|
|
5463
|
+
"kebabcase",
|
|
5464
|
+
"camelcase",
|
|
5465
|
+
"underscore",
|
|
5466
|
+
"snakecase",
|
|
5467
|
+
"bem",
|
|
5468
|
+
"tailwind"
|
|
5469
|
+
];
|
|
5470
|
+
const patternNames = new Set(patternNamesValues);
|
|
5471
|
+
function isNamedPattern(value) {
|
|
5472
|
+
return typeof value === "string" && patternNames.has(value);
|
|
5473
|
+
}
|
|
5417
5474
|
function parsePattern(pattern) {
|
|
5475
|
+
if (pattern instanceof RegExp) {
|
|
5476
|
+
return { regexp: pattern, description: pattern.toString() };
|
|
5477
|
+
}
|
|
5418
5478
|
switch (pattern) {
|
|
5419
5479
|
case "kebabcase":
|
|
5420
5480
|
return { regexp: /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/, description: pattern };
|
|
@@ -5432,7 +5492,21 @@ function parsePattern(pattern) {
|
|
|
5432
5492
|
description: pattern
|
|
5433
5493
|
};
|
|
5434
5494
|
}
|
|
5495
|
+
case "tailwind": {
|
|
5496
|
+
return {
|
|
5497
|
+
regexp: /^!?(?:[-a-z[]|\d+xl:)[\w\-:./\\[\]()#'&>,!=%]*$/,
|
|
5498
|
+
description: "tailwind"
|
|
5499
|
+
};
|
|
5500
|
+
}
|
|
5435
5501
|
default: {
|
|
5502
|
+
if (pattern.startsWith("/") && pattern.endsWith("/")) {
|
|
5503
|
+
const regexpSource = pattern.slice(1, -1);
|
|
5504
|
+
const regexp2 = new RegExp(regexpSource);
|
|
5505
|
+
return { regexp: regexp2, description: regexp2.toString() };
|
|
5506
|
+
}
|
|
5507
|
+
console.warn(
|
|
5508
|
+
`Custom pattern "${pattern}" should be wrapped in forward slashes, e.g., "/${pattern}/". Support for unwrapped patterns is deprecated and will be removed in a future version.`
|
|
5509
|
+
);
|
|
5436
5510
|
const regexp = new RegExp(pattern);
|
|
5437
5511
|
return { regexp, description: regexp.toString() };
|
|
5438
5512
|
}
|
|
@@ -5440,27 +5514,49 @@ function parsePattern(pattern) {
|
|
|
5440
5514
|
}
|
|
5441
5515
|
|
|
5442
5516
|
function toArray$2(value) {
|
|
5443
|
-
|
|
5517
|
+
if (Array.isArray(value)) {
|
|
5518
|
+
return value;
|
|
5519
|
+
} else {
|
|
5520
|
+
return [value];
|
|
5521
|
+
}
|
|
5522
|
+
}
|
|
5523
|
+
function validateAllowedPatterns(patterns, allowedPatterns, ruleId) {
|
|
5524
|
+
const extraneous = patterns.filter(isNamedPattern).filter((p) => !allowedPatterns.has(p));
|
|
5525
|
+
if (extraneous.length > 0) {
|
|
5526
|
+
const quote = (it) => `"${it}"`;
|
|
5527
|
+
const disallowed = utils_naturalJoin.naturalJoin(extraneous.map(quote), "and");
|
|
5528
|
+
const allowed = utils_naturalJoin.naturalJoin(Array.from(allowedPatterns, quote), "and");
|
|
5529
|
+
throw new Error(
|
|
5530
|
+
`Pattern ${disallowed} cannot be used with "${ruleId}". Allowed patterns: ${allowed}`
|
|
5531
|
+
);
|
|
5532
|
+
}
|
|
5444
5533
|
}
|
|
5445
5534
|
class BasePatternRule extends Rule {
|
|
5446
5535
|
/** Attribute being tested */
|
|
5447
5536
|
attr;
|
|
5448
5537
|
/** Parsed configured patterns */
|
|
5449
5538
|
patterns;
|
|
5450
|
-
|
|
5451
|
-
|
|
5452
|
-
|
|
5453
|
-
|
|
5454
|
-
|
|
5539
|
+
constructor({
|
|
5540
|
+
ruleId,
|
|
5541
|
+
attr,
|
|
5542
|
+
options,
|
|
5543
|
+
allowedPatterns
|
|
5544
|
+
}) {
|
|
5455
5545
|
super(options);
|
|
5456
5546
|
const { pattern } = this.options;
|
|
5457
5547
|
this.attr = attr;
|
|
5458
|
-
|
|
5548
|
+
const patterns = toArray$2(pattern);
|
|
5549
|
+
validateAllowedPatterns(patterns, allowedPatterns, ruleId);
|
|
5550
|
+
this.patterns = patterns.map((it) => parsePattern(it));
|
|
5459
5551
|
}
|
|
5460
5552
|
static schema() {
|
|
5461
5553
|
return {
|
|
5462
5554
|
pattern: {
|
|
5463
|
-
|
|
5555
|
+
anyOf: [
|
|
5556
|
+
{ type: "array", items: { anyOf: [{ type: "string" }, { regexp: true }] }, minItems: 1 },
|
|
5557
|
+
{ type: "string" },
|
|
5558
|
+
{ regexp: true }
|
|
5559
|
+
]
|
|
5464
5560
|
}
|
|
5465
5561
|
};
|
|
5466
5562
|
}
|
|
@@ -5494,12 +5590,18 @@ class BasePatternRule extends Rule {
|
|
|
5494
5590
|
}
|
|
5495
5591
|
}
|
|
5496
5592
|
|
|
5497
|
-
const defaults$
|
|
5593
|
+
const defaults$r = {
|
|
5498
5594
|
pattern: "kebabcase"
|
|
5499
5595
|
};
|
|
5500
5596
|
class ClassPattern extends BasePatternRule {
|
|
5501
5597
|
constructor(options) {
|
|
5502
|
-
super(
|
|
5598
|
+
super({
|
|
5599
|
+
ruleId: "class-pattern",
|
|
5600
|
+
attr: "class",
|
|
5601
|
+
options: { ...defaults$r, ...options },
|
|
5602
|
+
allowedPatterns: patternNames
|
|
5603
|
+
// allow all patterns
|
|
5604
|
+
});
|
|
5503
5605
|
}
|
|
5504
5606
|
static schema() {
|
|
5505
5607
|
return BasePatternRule.schema();
|
|
@@ -5646,13 +5748,13 @@ class CloseOrder extends Rule {
|
|
|
5646
5748
|
}
|
|
5647
5749
|
}
|
|
5648
5750
|
|
|
5649
|
-
const defaults$
|
|
5751
|
+
const defaults$q = {
|
|
5650
5752
|
include: null,
|
|
5651
5753
|
exclude: null
|
|
5652
5754
|
};
|
|
5653
5755
|
class Deprecated extends Rule {
|
|
5654
5756
|
constructor(options) {
|
|
5655
|
-
super({ ...defaults$
|
|
5757
|
+
super({ ...defaults$q, ...options });
|
|
5656
5758
|
}
|
|
5657
5759
|
static schema() {
|
|
5658
5760
|
return {
|
|
@@ -5806,12 +5908,12 @@ let NoStyleTag$1 = class NoStyleTag extends Rule {
|
|
|
5806
5908
|
}
|
|
5807
5909
|
};
|
|
5808
5910
|
|
|
5809
|
-
const defaults$
|
|
5911
|
+
const defaults$p = {
|
|
5810
5912
|
style: "uppercase"
|
|
5811
5913
|
};
|
|
5812
5914
|
class DoctypeStyle extends Rule {
|
|
5813
5915
|
constructor(options) {
|
|
5814
|
-
super({ ...defaults$
|
|
5916
|
+
super({ ...defaults$p, ...options });
|
|
5815
5917
|
}
|
|
5816
5918
|
static schema() {
|
|
5817
5919
|
return {
|
|
@@ -5839,13 +5941,13 @@ class DoctypeStyle extends Rule {
|
|
|
5839
5941
|
}
|
|
5840
5942
|
}
|
|
5841
5943
|
|
|
5842
|
-
const defaults$
|
|
5944
|
+
const defaults$o = {
|
|
5843
5945
|
style: "lowercase"
|
|
5844
5946
|
};
|
|
5845
5947
|
class ElementCase extends Rule {
|
|
5846
5948
|
style;
|
|
5847
5949
|
constructor(options) {
|
|
5848
|
-
super({ ...defaults$
|
|
5950
|
+
super({ ...defaults$o, ...options });
|
|
5849
5951
|
this.style = new CaseStyle(this.options.style, "element-case");
|
|
5850
5952
|
}
|
|
5851
5953
|
static schema() {
|
|
@@ -5905,7 +6007,7 @@ class ElementCase extends Rule {
|
|
|
5905
6007
|
}
|
|
5906
6008
|
}
|
|
5907
6009
|
|
|
5908
|
-
const defaults$
|
|
6010
|
+
const defaults$n = {
|
|
5909
6011
|
pattern: "^[a-z][a-z0-9\\-._]*-[a-z0-9\\-._]*$",
|
|
5910
6012
|
whitelist: [],
|
|
5911
6013
|
blacklist: []
|
|
@@ -5913,7 +6015,7 @@ const defaults$m = {
|
|
|
5913
6015
|
class ElementName extends Rule {
|
|
5914
6016
|
pattern;
|
|
5915
6017
|
constructor(options) {
|
|
5916
|
-
super({ ...defaults$
|
|
6018
|
+
super({ ...defaults$n, ...options });
|
|
5917
6019
|
this.pattern = new RegExp(this.options.pattern);
|
|
5918
6020
|
}
|
|
5919
6021
|
static schema() {
|
|
@@ -5950,7 +6052,7 @@ class ElementName extends Rule {
|
|
|
5950
6052
|
...context.blacklist.map((cur) => `- ${cur}`)
|
|
5951
6053
|
];
|
|
5952
6054
|
}
|
|
5953
|
-
if (context.pattern !== defaults$
|
|
6055
|
+
if (context.pattern !== defaults$n.pattern) {
|
|
5954
6056
|
return [
|
|
5955
6057
|
`<${context.tagName}> is not a valid element name. This project is configured to only allow names matching the following regular expression:`,
|
|
5956
6058
|
"",
|
|
@@ -6449,7 +6551,9 @@ class EmptyTitle extends Rule {
|
|
|
6449
6551
|
setup() {
|
|
6450
6552
|
this.on("tag:end", (event) => {
|
|
6451
6553
|
const node = event.previous;
|
|
6452
|
-
if (node.tagName !== "title")
|
|
6554
|
+
if (node.tagName !== "title") {
|
|
6555
|
+
return;
|
|
6556
|
+
}
|
|
6453
6557
|
switch (classifyNodeText(node)) {
|
|
6454
6558
|
case TextClassification.DYNAMIC_TEXT:
|
|
6455
6559
|
case TextClassification.STATIC_TEXT:
|
|
@@ -6465,7 +6569,7 @@ class EmptyTitle extends Rule {
|
|
|
6465
6569
|
}
|
|
6466
6570
|
}
|
|
6467
6571
|
|
|
6468
|
-
const defaults$
|
|
6572
|
+
const defaults$m = {
|
|
6469
6573
|
allowArrayBrackets: true,
|
|
6470
6574
|
allowCheckboxDefault: true,
|
|
6471
6575
|
shared: ["radio", "button", "reset", "submit"]
|
|
@@ -6525,7 +6629,7 @@ function getDocumentation(context) {
|
|
|
6525
6629
|
}
|
|
6526
6630
|
class FormDupName extends Rule {
|
|
6527
6631
|
constructor(options) {
|
|
6528
|
-
super({ ...defaults$
|
|
6632
|
+
super({ ...defaults$m, ...options });
|
|
6529
6633
|
}
|
|
6530
6634
|
static schema() {
|
|
6531
6635
|
return {
|
|
@@ -6684,7 +6788,7 @@ class FormDupName extends Rule {
|
|
|
6684
6788
|
}
|
|
6685
6789
|
}
|
|
6686
6790
|
|
|
6687
|
-
const defaults$
|
|
6791
|
+
const defaults$l = {
|
|
6688
6792
|
allowMultipleH1: false,
|
|
6689
6793
|
minInitialRank: "h1",
|
|
6690
6794
|
sectioningRoots: ["dialog", '[role="dialog"]', '[role="alertdialog"]']
|
|
@@ -6716,7 +6820,7 @@ class HeadingLevel extends Rule {
|
|
|
6716
6820
|
sectionRoots;
|
|
6717
6821
|
stack = [];
|
|
6718
6822
|
constructor(options) {
|
|
6719
|
-
super({ ...defaults$
|
|
6823
|
+
super({ ...defaults$l, ...options });
|
|
6720
6824
|
this.minInitialRank = parseMaxInitial(this.options.minInitialRank);
|
|
6721
6825
|
this.sectionRoots = this.options.sectioningRoots.map((it) => new Compound(it));
|
|
6722
6826
|
this.stack.push({
|
|
@@ -6770,7 +6874,9 @@ class HeadingLevel extends Rule {
|
|
|
6770
6874
|
}
|
|
6771
6875
|
onTagStart(event) {
|
|
6772
6876
|
const level = extractLevel(event.target);
|
|
6773
|
-
if (!level)
|
|
6877
|
+
if (!level) {
|
|
6878
|
+
return;
|
|
6879
|
+
}
|
|
6774
6880
|
const root = this.getCurrentRoot();
|
|
6775
6881
|
if (!this.options.allowMultipleH1 && level === 1) {
|
|
6776
6882
|
if (root.h1Count >= 1) {
|
|
@@ -6953,12 +7059,25 @@ class HiddenFocusable extends Rule {
|
|
|
6953
7059
|
}
|
|
6954
7060
|
}
|
|
6955
7061
|
|
|
6956
|
-
const defaults$
|
|
7062
|
+
const defaults$k = {
|
|
6957
7063
|
pattern: "kebabcase"
|
|
6958
7064
|
};
|
|
7065
|
+
function exclude$1(set, ...values) {
|
|
7066
|
+
const result = new Set(set);
|
|
7067
|
+
for (const value of values) {
|
|
7068
|
+
result.delete(value);
|
|
7069
|
+
}
|
|
7070
|
+
return result;
|
|
7071
|
+
}
|
|
6959
7072
|
class IdPattern extends BasePatternRule {
|
|
6960
7073
|
constructor(options) {
|
|
6961
|
-
|
|
7074
|
+
const allowedPatterns = exclude$1(patternNames, "tailwind");
|
|
7075
|
+
super({
|
|
7076
|
+
ruleId: "id-pattern",
|
|
7077
|
+
attr: "id",
|
|
7078
|
+
options: { ...defaults$k, ...options },
|
|
7079
|
+
allowedPatterns
|
|
7080
|
+
});
|
|
6962
7081
|
}
|
|
6963
7082
|
static schema() {
|
|
6964
7083
|
return BasePatternRule.schema();
|
|
@@ -7262,7 +7381,9 @@ class InputMissingLabel extends Rule {
|
|
|
7262
7381
|
}
|
|
7263
7382
|
}
|
|
7264
7383
|
function findLabelById(root, id) {
|
|
7265
|
-
if (!id)
|
|
7384
|
+
if (!id) {
|
|
7385
|
+
return [];
|
|
7386
|
+
}
|
|
7266
7387
|
return root.querySelectorAll(`label[for="${id}"]`);
|
|
7267
7388
|
}
|
|
7268
7389
|
function findLabelByParent(el) {
|
|
@@ -7276,13 +7397,13 @@ function findLabelByParent(el) {
|
|
|
7276
7397
|
return [];
|
|
7277
7398
|
}
|
|
7278
7399
|
|
|
7279
|
-
const defaults$
|
|
7400
|
+
const defaults$j = {
|
|
7280
7401
|
maxlength: 70
|
|
7281
7402
|
};
|
|
7282
7403
|
class LongTitle extends Rule {
|
|
7283
7404
|
maxlength;
|
|
7284
7405
|
constructor(options) {
|
|
7285
|
-
super({ ...defaults$
|
|
7406
|
+
super({ ...defaults$j, ...options });
|
|
7286
7407
|
this.maxlength = this.options.maxlength;
|
|
7287
7408
|
}
|
|
7288
7409
|
static schema() {
|
|
@@ -7301,7 +7422,9 @@ class LongTitle extends Rule {
|
|
|
7301
7422
|
setup() {
|
|
7302
7423
|
this.on("tag:end", (event) => {
|
|
7303
7424
|
const node = event.previous;
|
|
7304
|
-
if (node.tagName !== "title")
|
|
7425
|
+
if (node.tagName !== "title") {
|
|
7426
|
+
return;
|
|
7427
|
+
}
|
|
7305
7428
|
const text = node.textContent;
|
|
7306
7429
|
if (text.length > this.maxlength) {
|
|
7307
7430
|
this.report(node, `title text cannot be longer than ${String(this.maxlength)} characters`);
|
|
@@ -7384,12 +7507,12 @@ class MapIdName extends Rule {
|
|
|
7384
7507
|
}
|
|
7385
7508
|
}
|
|
7386
7509
|
|
|
7387
|
-
const defaults$
|
|
7510
|
+
const defaults$i = {
|
|
7388
7511
|
allowLongDelay: false
|
|
7389
7512
|
};
|
|
7390
7513
|
class MetaRefresh extends Rule {
|
|
7391
7514
|
constructor(options) {
|
|
7392
|
-
super({ ...defaults$
|
|
7515
|
+
super({ ...defaults$i, ...options });
|
|
7393
7516
|
}
|
|
7394
7517
|
documentation() {
|
|
7395
7518
|
return {
|
|
@@ -7500,12 +7623,25 @@ class MultipleLabeledControls extends Rule {
|
|
|
7500
7623
|
}
|
|
7501
7624
|
}
|
|
7502
7625
|
|
|
7503
|
-
const defaults$
|
|
7626
|
+
const defaults$h = {
|
|
7504
7627
|
pattern: "camelcase"
|
|
7505
7628
|
};
|
|
7629
|
+
function exclude(set, ...values) {
|
|
7630
|
+
const result = new Set(set);
|
|
7631
|
+
for (const value of values) {
|
|
7632
|
+
result.delete(value);
|
|
7633
|
+
}
|
|
7634
|
+
return result;
|
|
7635
|
+
}
|
|
7506
7636
|
class NamePattern extends BasePatternRule {
|
|
7507
7637
|
constructor(options) {
|
|
7508
|
-
|
|
7638
|
+
const allowedPatterns = exclude(patternNames, "tailwind");
|
|
7639
|
+
super({
|
|
7640
|
+
ruleId: "name-pattern",
|
|
7641
|
+
attr: "name",
|
|
7642
|
+
options: { ...defaults$h, ...options },
|
|
7643
|
+
allowedPatterns
|
|
7644
|
+
});
|
|
7509
7645
|
}
|
|
7510
7646
|
static schema() {
|
|
7511
7647
|
return BasePatternRule.schema();
|
|
@@ -7594,13 +7730,13 @@ class NoAbstractRole extends Rule {
|
|
|
7594
7730
|
}
|
|
7595
7731
|
}
|
|
7596
7732
|
|
|
7597
|
-
const defaults$
|
|
7733
|
+
const defaults$g = {
|
|
7598
7734
|
include: null,
|
|
7599
7735
|
exclude: null
|
|
7600
7736
|
};
|
|
7601
7737
|
class NoAutoplay extends Rule {
|
|
7602
7738
|
constructor(options) {
|
|
7603
|
-
super({ ...defaults$
|
|
7739
|
+
super({ ...defaults$g, ...options });
|
|
7604
7740
|
}
|
|
7605
7741
|
documentation(context) {
|
|
7606
7742
|
return {
|
|
@@ -7866,7 +8002,7 @@ Omitted end tags can be ambigious for humans to read and many editors have troub
|
|
|
7866
8002
|
return;
|
|
7867
8003
|
}
|
|
7868
8004
|
const parent = closed.parent;
|
|
7869
|
-
const closedByParent = parent
|
|
8005
|
+
const closedByParent = parent?.tagName === by.tagName;
|
|
7870
8006
|
const closedByDocument = closedByParent && parent.isRootElement();
|
|
7871
8007
|
const sameTag = closed.tagName === by.tagName;
|
|
7872
8008
|
if (closedByDocument) {
|
|
@@ -7922,14 +8058,14 @@ class NoImplicitInputType extends Rule {
|
|
|
7922
8058
|
}
|
|
7923
8059
|
}
|
|
7924
8060
|
|
|
7925
|
-
const defaults$
|
|
8061
|
+
const defaults$f = {
|
|
7926
8062
|
include: null,
|
|
7927
8063
|
exclude: null,
|
|
7928
8064
|
allowedProperties: ["display"]
|
|
7929
8065
|
};
|
|
7930
8066
|
class NoInlineStyle extends Rule {
|
|
7931
8067
|
constructor(options) {
|
|
7932
|
-
super({ ...defaults$
|
|
8068
|
+
super({ ...defaults$f, ...options });
|
|
7933
8069
|
}
|
|
7934
8070
|
static schema() {
|
|
7935
8071
|
return {
|
|
@@ -8115,7 +8251,7 @@ class NoMultipleMain extends Rule {
|
|
|
8115
8251
|
}
|
|
8116
8252
|
}
|
|
8117
8253
|
|
|
8118
|
-
const defaults$
|
|
8254
|
+
const defaults$e = {
|
|
8119
8255
|
relaxed: false
|
|
8120
8256
|
};
|
|
8121
8257
|
const textRegexp = /([<>]|&(?![a-zA-Z0-9#]+;))/g;
|
|
@@ -8133,7 +8269,7 @@ const replacementTable = {
|
|
|
8133
8269
|
class NoRawCharacters extends Rule {
|
|
8134
8270
|
relaxed;
|
|
8135
8271
|
constructor(options) {
|
|
8136
|
-
super({ ...defaults$
|
|
8272
|
+
super({ ...defaults$e, ...options });
|
|
8137
8273
|
this.relaxed = this.options.relaxed;
|
|
8138
8274
|
}
|
|
8139
8275
|
static schema() {
|
|
@@ -8228,7 +8364,7 @@ class NoRedundantAriaLabel extends Rule {
|
|
|
8228
8364
|
continue;
|
|
8229
8365
|
}
|
|
8230
8366
|
const label = document.querySelector(`label[for="${id}"]`);
|
|
8231
|
-
if (!ariaLabel ||
|
|
8367
|
+
if (!ariaLabel || label?.textContent.trim() !== ariaLabel.value) {
|
|
8232
8368
|
continue;
|
|
8233
8369
|
}
|
|
8234
8370
|
const message = "aria-label is redundant when label containing same text exists";
|
|
@@ -8313,13 +8449,13 @@ class NoRedundantRole extends Rule {
|
|
|
8313
8449
|
}
|
|
8314
8450
|
|
|
8315
8451
|
const xmlns = /^(.+):.+$/;
|
|
8316
|
-
const defaults$
|
|
8452
|
+
const defaults$d = {
|
|
8317
8453
|
ignoreForeign: true,
|
|
8318
8454
|
ignoreXML: true
|
|
8319
8455
|
};
|
|
8320
8456
|
class NoSelfClosing extends Rule {
|
|
8321
8457
|
constructor(options) {
|
|
8322
|
-
super({ ...defaults$
|
|
8458
|
+
super({ ...defaults$d, ...options });
|
|
8323
8459
|
}
|
|
8324
8460
|
static schema() {
|
|
8325
8461
|
return {
|
|
@@ -8369,7 +8505,20 @@ function isRelevant(node, options) {
|
|
|
8369
8505
|
return true;
|
|
8370
8506
|
}
|
|
8371
8507
|
|
|
8508
|
+
const defaults$c = {
|
|
8509
|
+
allowTemplate: true
|
|
8510
|
+
};
|
|
8372
8511
|
class NoStyleTag extends Rule {
|
|
8512
|
+
constructor(options) {
|
|
8513
|
+
super({ ...defaults$c, ...options });
|
|
8514
|
+
}
|
|
8515
|
+
static schema() {
|
|
8516
|
+
return {
|
|
8517
|
+
allowTemplate: {
|
|
8518
|
+
type: "boolean"
|
|
8519
|
+
}
|
|
8520
|
+
};
|
|
8521
|
+
}
|
|
8373
8522
|
documentation() {
|
|
8374
8523
|
return {
|
|
8375
8524
|
description: "Prefer to use external stylesheets with the `<link>` tag instead of inlining the styling.",
|
|
@@ -8377,9 +8526,13 @@ class NoStyleTag extends Rule {
|
|
|
8377
8526
|
};
|
|
8378
8527
|
}
|
|
8379
8528
|
setup() {
|
|
8529
|
+
const { allowTemplate } = this.options;
|
|
8380
8530
|
this.on("tag:start", (event) => {
|
|
8381
8531
|
const node = event.target;
|
|
8382
8532
|
if (node.tagName === "style") {
|
|
8533
|
+
if (allowTemplate && node.parent?.is("template")) {
|
|
8534
|
+
return;
|
|
8535
|
+
}
|
|
8383
8536
|
this.report(node, "Use external stylesheet with <link> instead of <style> tag");
|
|
8384
8537
|
}
|
|
8385
8538
|
});
|
|
@@ -9286,7 +9439,7 @@ function getTextFromReference(document, id) {
|
|
|
9286
9439
|
if (!id || id instanceof DynamicValue) {
|
|
9287
9440
|
return id;
|
|
9288
9441
|
}
|
|
9289
|
-
const selector =
|
|
9442
|
+
const selector = generateIdSelector(id);
|
|
9290
9443
|
const ref = document.querySelector(selector);
|
|
9291
9444
|
if (ref) {
|
|
9292
9445
|
return ref.textContent;
|
|
@@ -10047,6 +10200,46 @@ class ValidAutocomplete extends Rule {
|
|
|
10047
10200
|
}
|
|
10048
10201
|
}
|
|
10049
10202
|
|
|
10203
|
+
function isLabelable(target) {
|
|
10204
|
+
const { meta } = target;
|
|
10205
|
+
if (!meta) {
|
|
10206
|
+
return true;
|
|
10207
|
+
}
|
|
10208
|
+
return Boolean(meta.labelable);
|
|
10209
|
+
}
|
|
10210
|
+
class ValidFor extends Rule {
|
|
10211
|
+
documentation() {
|
|
10212
|
+
return {
|
|
10213
|
+
description: `The \`<label>\` \`for\` attribute must reference a labelable form control.`,
|
|
10214
|
+
url: "https://html-validate.org/rules/valid-for.html"
|
|
10215
|
+
};
|
|
10216
|
+
}
|
|
10217
|
+
setup() {
|
|
10218
|
+
this.on("dom:ready", (event) => {
|
|
10219
|
+
const { document } = event;
|
|
10220
|
+
for (const node of document.querySelectorAll("label[for]")) {
|
|
10221
|
+
const attr = node.getAttribute("for");
|
|
10222
|
+
if (!isStaticAttribute(attr) || !attr.value) {
|
|
10223
|
+
continue;
|
|
10224
|
+
}
|
|
10225
|
+
const selector = generateIdSelector(attr.value);
|
|
10226
|
+
const target = document.querySelector(selector);
|
|
10227
|
+
if (!target) {
|
|
10228
|
+
continue;
|
|
10229
|
+
}
|
|
10230
|
+
if (isLabelable(target)) {
|
|
10231
|
+
continue;
|
|
10232
|
+
}
|
|
10233
|
+
this.report({
|
|
10234
|
+
node,
|
|
10235
|
+
message: '<label> "for" attribute must reference a labelable form control',
|
|
10236
|
+
location: attr.valueLocation
|
|
10237
|
+
});
|
|
10238
|
+
}
|
|
10239
|
+
});
|
|
10240
|
+
}
|
|
10241
|
+
}
|
|
10242
|
+
|
|
10050
10243
|
const defaults$4 = {
|
|
10051
10244
|
relaxed: false
|
|
10052
10245
|
};
|
|
@@ -10345,7 +10538,9 @@ class H36 extends Rule {
|
|
|
10345
10538
|
setup() {
|
|
10346
10539
|
this.on("tag:end", (event) => {
|
|
10347
10540
|
const node = event.previous;
|
|
10348
|
-
if (node.tagName !== "input")
|
|
10541
|
+
if (node.tagName !== "input") {
|
|
10542
|
+
return;
|
|
10543
|
+
}
|
|
10349
10544
|
if (node.getAttributeValue("type") !== "image") {
|
|
10350
10545
|
return;
|
|
10351
10546
|
}
|
|
@@ -10671,6 +10866,7 @@ const bundledRules = {
|
|
|
10671
10866
|
"unique-landmark": UniqueLandmark,
|
|
10672
10867
|
"unrecognized-char-ref": UnknownCharReference,
|
|
10673
10868
|
"valid-autocomplete": ValidAutocomplete,
|
|
10869
|
+
"valid-for": ValidFor,
|
|
10674
10870
|
"valid-id": ValidID,
|
|
10675
10871
|
"void-content": VoidContent,
|
|
10676
10872
|
"void-style": VoidStyle,
|
|
@@ -11024,6 +11220,7 @@ const config$1 = {
|
|
|
11024
11220
|
"unique-landmark": "error",
|
|
11025
11221
|
"unrecognized-char-ref": "error",
|
|
11026
11222
|
"valid-autocomplete": "error",
|
|
11223
|
+
"valid-for": "error",
|
|
11027
11224
|
"valid-id": ["error", { relaxed: false }],
|
|
11028
11225
|
void: "off",
|
|
11029
11226
|
"void-content": "error",
|
|
@@ -11071,6 +11268,7 @@ const config = {
|
|
|
11071
11268
|
"script-element": "error",
|
|
11072
11269
|
"unrecognized-char-ref": "error",
|
|
11073
11270
|
"valid-autocomplete": "error",
|
|
11271
|
+
"valid-for": "error",
|
|
11074
11272
|
"valid-id": ["error", { relaxed: true }],
|
|
11075
11273
|
"void-content": "error"
|
|
11076
11274
|
}
|
|
@@ -11677,7 +11875,9 @@ class Config {
|
|
|
11677
11875
|
}
|
|
11678
11876
|
for (const plugin of plugins) {
|
|
11679
11877
|
for (const [name, config] of Object.entries(plugin.configs ?? {})) {
|
|
11680
|
-
if (!config)
|
|
11878
|
+
if (!config) {
|
|
11879
|
+
continue;
|
|
11880
|
+
}
|
|
11681
11881
|
Config.validate(config, name);
|
|
11682
11882
|
configs.set(`${plugin.name}:${name}`, config);
|
|
11683
11883
|
if (plugin.name !== plugin.originalName) {
|
|
@@ -11978,7 +12178,7 @@ class EventHandler {
|
|
|
11978
12178
|
}
|
|
11979
12179
|
|
|
11980
12180
|
const name = "html-validate";
|
|
11981
|
-
const version = "10.
|
|
12181
|
+
const version = "10.7.0";
|
|
11982
12182
|
const bugs = "https://gitlab.com/html-validate/html-validate/issues/new";
|
|
11983
12183
|
|
|
11984
12184
|
function freeze(src) {
|
|
@@ -12029,6 +12229,7 @@ class Reporter {
|
|
|
12029
12229
|
warningCount: sumWarnings(results)
|
|
12030
12230
|
};
|
|
12031
12231
|
}
|
|
12232
|
+
/* eslint-disable-next-line @typescript-eslint/max-params -- technical debt */
|
|
12032
12233
|
add(rule, message, severity, node, location, context) {
|
|
12033
12234
|
if (!(location.filename in this.result)) {
|
|
12034
12235
|
this.result[location.filename] = [];
|
|
@@ -12148,7 +12349,7 @@ class ParserError extends Error {
|
|
|
12148
12349
|
}
|
|
12149
12350
|
|
|
12150
12351
|
function isAttrValueToken(token) {
|
|
12151
|
-
return
|
|
12352
|
+
return token?.type === TokenType.ATTR_VALUE;
|
|
12152
12353
|
}
|
|
12153
12354
|
function svgShouldRetainTag(foreignTagName, tagName) {
|
|
12154
12355
|
return foreignTagName === "svg" && ["title", "desc"].includes(tagName);
|
|
@@ -12494,7 +12695,7 @@ class Parser {
|
|
|
12494
12695
|
* ^^^ ^^^ ^^^ (null) (null)
|
|
12495
12696
|
*/
|
|
12496
12697
|
getAttributeValueLocation(token) {
|
|
12497
|
-
if (
|
|
12698
|
+
if (token?.type !== TokenType.ATTR_VALUE || token.data[2] === "") {
|
|
12498
12699
|
return null;
|
|
12499
12700
|
}
|
|
12500
12701
|
const quote = token.data[3];
|
|
@@ -12510,7 +12711,7 @@ class Parser {
|
|
|
12510
12711
|
*/
|
|
12511
12712
|
getAttributeLocation(key, value) {
|
|
12512
12713
|
const begin = key.location;
|
|
12513
|
-
const end = value
|
|
12714
|
+
const end = value?.type === TokenType.ATTR_VALUE ? value.location : void 0;
|
|
12514
12715
|
return {
|
|
12515
12716
|
filename: begin.filename,
|
|
12516
12717
|
line: begin.line,
|
|
@@ -12523,27 +12724,34 @@ class Parser {
|
|
|
12523
12724
|
* @internal
|
|
12524
12725
|
*/
|
|
12525
12726
|
consumeDirective(token) {
|
|
12526
|
-
const [text, preamble, action, separator1, directive, postamble] = token.data;
|
|
12527
|
-
|
|
12528
|
-
|
|
12727
|
+
const [text, preamble, prefix, action, separator1, directive, postamble] = token.data;
|
|
12728
|
+
const hasStartBracket = preamble.includes("[");
|
|
12729
|
+
const hasEndBracket = postamble.startsWith("]");
|
|
12730
|
+
if (hasStartBracket && !hasEndBracket) {
|
|
12731
|
+
this.trigger("parse:error", {
|
|
12732
|
+
location: sliceLocation(token.location, preamble.length - 1, -postamble.length),
|
|
12733
|
+
message: `Missing end bracket "]" on directive "${text}"`
|
|
12734
|
+
});
|
|
12735
|
+
return;
|
|
12529
12736
|
}
|
|
12530
12737
|
const match = /^(.*?)(?:(\s*(?:--|:)\s*)(.*))?$/.exec(directive);
|
|
12531
12738
|
if (!match) {
|
|
12532
12739
|
throw new Error(`Failed to parse directive "${text}"`);
|
|
12533
12740
|
}
|
|
12534
12741
|
if (!isValidDirective(action)) {
|
|
12535
|
-
|
|
12742
|
+
const begin = preamble.length;
|
|
12743
|
+
const end = preamble.length + prefix.length + action.length;
|
|
12744
|
+
this.trigger("parse:error", {
|
|
12745
|
+
location: sliceLocation(token.location, begin, -text.length + end),
|
|
12746
|
+
message: `Unknown directive "${action}"`
|
|
12747
|
+
});
|
|
12748
|
+
return;
|
|
12536
12749
|
}
|
|
12537
12750
|
const [, data, separator2, comment] = match;
|
|
12538
|
-
const
|
|
12539
|
-
const actionOffset = preamble.length;
|
|
12751
|
+
const actionOffset = preamble.length + prefix.length;
|
|
12540
12752
|
const optionsOffset = actionOffset + action.length + separator1.length;
|
|
12541
12753
|
const commentOffset = optionsOffset + data.length + (separator2 || "").length;
|
|
12542
|
-
const location = sliceLocation(
|
|
12543
|
-
token.location,
|
|
12544
|
-
preamble.length - prefix.length - 1,
|
|
12545
|
-
-postamble.length + 1
|
|
12546
|
-
);
|
|
12754
|
+
const location = sliceLocation(token.location, preamble.length - 1, -postamble.length + 1);
|
|
12547
12755
|
const actionLocation = sliceLocation(
|
|
12548
12756
|
token.location,
|
|
12549
12757
|
actionOffset,
|
|
@@ -12625,7 +12833,9 @@ class Parser {
|
|
|
12625
12833
|
while (!it.done) {
|
|
12626
12834
|
const token = it.value;
|
|
12627
12835
|
yield token;
|
|
12628
|
-
if (token.type === search)
|
|
12836
|
+
if (token.type === search) {
|
|
12837
|
+
return;
|
|
12838
|
+
}
|
|
12629
12839
|
it = this.next(tokenStream);
|
|
12630
12840
|
}
|
|
12631
12841
|
throw new ParserError(
|
|
@@ -12788,6 +12998,9 @@ class Engine {
|
|
|
12788
12998
|
parser.on("directive", (_2, event) => {
|
|
12789
12999
|
this.processDirective(event, parser, directiveContext);
|
|
12790
13000
|
});
|
|
13001
|
+
parser.on("parse:error", (_2, event) => {
|
|
13002
|
+
this.reportError("parser-error", event.message, event.location);
|
|
13003
|
+
});
|
|
12791
13004
|
try {
|
|
12792
13005
|
parser.parseHtml(source);
|
|
12793
13006
|
} catch (e) {
|
|
@@ -12985,7 +13198,9 @@ class Engine {
|
|
|
12985
13198
|
const availableRules = {};
|
|
12986
13199
|
for (const plugin of config.getPlugins()) {
|
|
12987
13200
|
for (const [name, rule] of Object.entries(plugin.rules ?? {})) {
|
|
12988
|
-
if (!rule)
|
|
13201
|
+
if (!rule) {
|
|
13202
|
+
continue;
|
|
13203
|
+
}
|
|
12989
13204
|
availableRules[name] = rule;
|
|
12990
13205
|
}
|
|
12991
13206
|
}
|
|
@@ -13018,6 +13233,7 @@ class Engine {
|
|
|
13018
13233
|
/**
|
|
13019
13234
|
* Load and setup a rule using current config.
|
|
13020
13235
|
*/
|
|
13236
|
+
/* eslint-disable-next-line @typescript-eslint/max-params -- technical debt */
|
|
13021
13237
|
loadRule(ruleId, config, severity, options, parser, report) {
|
|
13022
13238
|
const meta = config.getMetaTable();
|
|
13023
13239
|
const rule = this.instantiateRule(ruleId, options);
|