html-validate 7.12.1 → 7.13.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/browser.d.ts +1 -1
- package/dist/cjs/browser.js +1 -0
- package/dist/cjs/browser.js.map +1 -1
- package/dist/cjs/cli.js +1 -1
- package/dist/cjs/cli.js.map +1 -1
- package/dist/cjs/core.d.ts +212 -19
- package/dist/cjs/core.js +336 -57
- package/dist/cjs/core.js.map +1 -1
- package/dist/cjs/index.d.ts +1 -1
- package/dist/cjs/index.js +1 -0
- package/dist/cjs/index.js.map +1 -1
- package/dist/es/browser.d.ts +1 -1
- package/dist/es/browser.js +1 -1
- package/dist/es/cli.js +1 -1
- package/dist/es/cli.js.map +1 -1
- package/dist/es/core.d.ts +212 -19
- package/dist/es/core.js +336 -58
- package/dist/es/core.js.map +1 -1
- package/dist/es/index.d.ts +1 -1
- package/dist/es/index.js +1 -1
- package/package.json +8 -8
package/dist/es/core.js
CHANGED
|
@@ -511,6 +511,7 @@ class DOMNode {
|
|
|
511
511
|
this.nodeName = nodeName !== null && nodeName !== void 0 ? nodeName : DOCUMENT_NODE_NAME;
|
|
512
512
|
this.location = location;
|
|
513
513
|
this.disabledRules = new Set();
|
|
514
|
+
this.blockedRules = new Map();
|
|
514
515
|
this.childNodes = [];
|
|
515
516
|
this.unique = counter++;
|
|
516
517
|
this.cache = null;
|
|
@@ -586,14 +587,42 @@ class DOMNode {
|
|
|
586
587
|
get lastChild() {
|
|
587
588
|
return this.childNodes[this.childNodes.length - 1] || null;
|
|
588
589
|
}
|
|
590
|
+
/**
|
|
591
|
+
* Block a rule for this node.
|
|
592
|
+
*
|
|
593
|
+
* @internal
|
|
594
|
+
*/
|
|
595
|
+
blockRule(ruleId, blocker) {
|
|
596
|
+
const current = this.blockedRules.get(ruleId);
|
|
597
|
+
if (current) {
|
|
598
|
+
current.push(blocker);
|
|
599
|
+
}
|
|
600
|
+
else {
|
|
601
|
+
this.blockedRules.set(ruleId, [blocker]);
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
/**
|
|
605
|
+
* Blocks multiple rules.
|
|
606
|
+
*
|
|
607
|
+
* @internal
|
|
608
|
+
*/
|
|
609
|
+
blockRules(rules, blocker) {
|
|
610
|
+
for (const rule of rules) {
|
|
611
|
+
this.blockRule(rule, blocker);
|
|
612
|
+
}
|
|
613
|
+
}
|
|
589
614
|
/**
|
|
590
615
|
* Disable a rule for this node.
|
|
616
|
+
*
|
|
617
|
+
* @internal
|
|
591
618
|
*/
|
|
592
619
|
disableRule(ruleId) {
|
|
593
620
|
this.disabledRules.add(ruleId);
|
|
594
621
|
}
|
|
595
622
|
/**
|
|
596
623
|
* Disables multiple rules.
|
|
624
|
+
*
|
|
625
|
+
* @internal
|
|
597
626
|
*/
|
|
598
627
|
disableRules(rules) {
|
|
599
628
|
for (const rule of rules) {
|
|
@@ -616,10 +645,21 @@ class DOMNode {
|
|
|
616
645
|
}
|
|
617
646
|
/**
|
|
618
647
|
* Test if a rule is enabled for this node.
|
|
648
|
+
*
|
|
649
|
+
* @internal
|
|
619
650
|
*/
|
|
620
651
|
ruleEnabled(ruleId) {
|
|
621
652
|
return !this.disabledRules.has(ruleId);
|
|
622
653
|
}
|
|
654
|
+
/**
|
|
655
|
+
* Test if a rule is blocked for this node.
|
|
656
|
+
*
|
|
657
|
+
* @internal
|
|
658
|
+
*/
|
|
659
|
+
ruleBlockers(ruleId) {
|
|
660
|
+
var _a;
|
|
661
|
+
return (_a = this.blockedRules.get(ruleId)) !== null && _a !== void 0 ? _a : [];
|
|
662
|
+
}
|
|
623
663
|
generateSelector() {
|
|
624
664
|
return null;
|
|
625
665
|
}
|
|
@@ -2675,6 +2715,8 @@ function matchAttributeFacade(node, match) {
|
|
|
2675
2715
|
const allowedKeys = ["exclude"];
|
|
2676
2716
|
/**
|
|
2677
2717
|
* Helper class to validate elements against metadata rules.
|
|
2718
|
+
*
|
|
2719
|
+
* @public
|
|
2678
2720
|
*/
|
|
2679
2721
|
class Validator {
|
|
2680
2722
|
/**
|
|
@@ -3430,6 +3472,7 @@ class Rule {
|
|
|
3430
3472
|
this.event = null;
|
|
3431
3473
|
this.options = options;
|
|
3432
3474
|
this.enabled = true;
|
|
3475
|
+
this.blockers = [];
|
|
3433
3476
|
this.severity = 0;
|
|
3434
3477
|
this.name = "";
|
|
3435
3478
|
}
|
|
@@ -3439,6 +3482,26 @@ class Rule {
|
|
|
3439
3482
|
setServerity(severity) {
|
|
3440
3483
|
this.severity = severity;
|
|
3441
3484
|
}
|
|
3485
|
+
/**
|
|
3486
|
+
* Block this rule from generating errors. Pass in an id generated by {@link
|
|
3487
|
+
* createBlocker}. Can be unblocked by {@link unblock}.
|
|
3488
|
+
*
|
|
3489
|
+
* A blocked rule is similar to disabling it but it will still receive parser
|
|
3490
|
+
* events. A list of all blockers is passed to the `rule:error` event.
|
|
3491
|
+
*
|
|
3492
|
+
* @internal
|
|
3493
|
+
*/
|
|
3494
|
+
block(id) {
|
|
3495
|
+
this.blockers.push(id);
|
|
3496
|
+
}
|
|
3497
|
+
/**
|
|
3498
|
+
* Unblock a rule previously blocked by {@link block}.
|
|
3499
|
+
*
|
|
3500
|
+
* @internal
|
|
3501
|
+
*/
|
|
3502
|
+
unblock(id) {
|
|
3503
|
+
this.blockers = this.blockers.filter((it) => it !== id);
|
|
3504
|
+
}
|
|
3442
3505
|
setEnabled(enabled) {
|
|
3443
3506
|
this.enabled = enabled;
|
|
3444
3507
|
}
|
|
@@ -3455,9 +3518,36 @@ class Rule {
|
|
|
3455
3518
|
*
|
|
3456
3519
|
* To be considered enabled the enabled flag must be true and the severity at
|
|
3457
3520
|
* least warning.
|
|
3521
|
+
*
|
|
3522
|
+
* @internal
|
|
3523
|
+
*/
|
|
3524
|
+
isEnabled(node) {
|
|
3525
|
+
return this.enabled && this.severity >= Severity.WARN && (!node || node.ruleEnabled(this.name));
|
|
3526
|
+
}
|
|
3527
|
+
/**
|
|
3528
|
+
* Test if rule is enabled.
|
|
3529
|
+
*
|
|
3530
|
+
* To be considered enabled the enabled flag must be true and the severity at
|
|
3531
|
+
* least warning.
|
|
3532
|
+
*
|
|
3533
|
+
* @internal
|
|
3458
3534
|
*/
|
|
3459
|
-
|
|
3460
|
-
|
|
3535
|
+
isBlocked(node) {
|
|
3536
|
+
if (this.blockers.length > 0) {
|
|
3537
|
+
return true;
|
|
3538
|
+
}
|
|
3539
|
+
if (node && node.ruleBlockers(this.name).length > 0) {
|
|
3540
|
+
return true;
|
|
3541
|
+
}
|
|
3542
|
+
return false;
|
|
3543
|
+
}
|
|
3544
|
+
/**
|
|
3545
|
+
* Get a list of all blockers currently active this rule.
|
|
3546
|
+
*
|
|
3547
|
+
* @internal
|
|
3548
|
+
*/
|
|
3549
|
+
getBlockers(node) {
|
|
3550
|
+
return [...this.blockers, ...(node ? node.ruleBlockers(this.name) : [])];
|
|
3461
3551
|
}
|
|
3462
3552
|
/**
|
|
3463
3553
|
* Check if keyword is being ignored by the current rule configuration.
|
|
@@ -3521,8 +3611,16 @@ class Rule {
|
|
|
3521
3611
|
}
|
|
3522
3612
|
report(...args) {
|
|
3523
3613
|
const { node, message, location, context } = unpackErrorDescriptor(args);
|
|
3524
|
-
|
|
3525
|
-
|
|
3614
|
+
const enabled = this.isEnabled(node);
|
|
3615
|
+
const blocked = this.isBlocked(node);
|
|
3616
|
+
const where = this.findLocation({ node, location, event: this.event });
|
|
3617
|
+
this.parser.trigger("rule:error", {
|
|
3618
|
+
location: where,
|
|
3619
|
+
ruleId: this.name,
|
|
3620
|
+
enabled,
|
|
3621
|
+
blockers: this.getBlockers(node),
|
|
3622
|
+
});
|
|
3623
|
+
if (enabled && !blocked) {
|
|
3526
3624
|
const interpolated = interpolate(message, context !== null && context !== void 0 ? context : {});
|
|
3527
3625
|
this.reporter.add(this, interpolated, this.severity, node, where, context);
|
|
3528
3626
|
}
|
|
@@ -3621,7 +3719,7 @@ class Rule {
|
|
|
3621
3719
|
}
|
|
3622
3720
|
}
|
|
3623
3721
|
|
|
3624
|
-
const defaults$
|
|
3722
|
+
const defaults$w = {
|
|
3625
3723
|
allowExternal: true,
|
|
3626
3724
|
allowRelative: true,
|
|
3627
3725
|
allowAbsolute: true,
|
|
@@ -3665,7 +3763,7 @@ function matchList(value, list) {
|
|
|
3665
3763
|
}
|
|
3666
3764
|
class AllowedLinks extends Rule {
|
|
3667
3765
|
constructor(options) {
|
|
3668
|
-
super({ ...defaults$
|
|
3766
|
+
super({ ...defaults$w, ...options });
|
|
3669
3767
|
this.allowExternal = parseAllow(this.options.allowExternal);
|
|
3670
3768
|
this.allowRelative = parseAllow(this.options.allowRelative);
|
|
3671
3769
|
this.allowAbsolute = parseAllow(this.options.allowAbsolute);
|
|
@@ -3813,7 +3911,7 @@ var RuleContext$1;
|
|
|
3813
3911
|
RuleContext["MISSING_ALT"] = "missing-alt";
|
|
3814
3912
|
RuleContext["MISSING_HREF"] = "missing-href";
|
|
3815
3913
|
})(RuleContext$1 || (RuleContext$1 = {}));
|
|
3816
|
-
const defaults$
|
|
3914
|
+
const defaults$v = {
|
|
3817
3915
|
accessible: true,
|
|
3818
3916
|
};
|
|
3819
3917
|
function findByTarget(target, siblings) {
|
|
@@ -3851,7 +3949,7 @@ function getDescription$1(context) {
|
|
|
3851
3949
|
}
|
|
3852
3950
|
class AreaAlt extends Rule {
|
|
3853
3951
|
constructor(options) {
|
|
3854
|
-
super({ ...defaults$
|
|
3952
|
+
super({ ...defaults$v, ...options });
|
|
3855
3953
|
}
|
|
3856
3954
|
static schema() {
|
|
3857
3955
|
return {
|
|
@@ -4020,13 +4118,13 @@ class ConfigError extends UserError {
|
|
|
4020
4118
|
}
|
|
4021
4119
|
}
|
|
4022
4120
|
|
|
4023
|
-
const defaults$
|
|
4121
|
+
const defaults$u = {
|
|
4024
4122
|
style: "lowercase",
|
|
4025
4123
|
ignoreForeign: true,
|
|
4026
4124
|
};
|
|
4027
4125
|
class AttrCase extends Rule {
|
|
4028
4126
|
constructor(options) {
|
|
4029
|
-
super({ ...defaults$
|
|
4127
|
+
super({ ...defaults$u, ...options });
|
|
4030
4128
|
this.style = new CaseStyle(this.options.style, "attr-case");
|
|
4031
4129
|
}
|
|
4032
4130
|
static schema() {
|
|
@@ -4136,7 +4234,7 @@ const MATCH_SCRIPT_DATA = /^[^]*?(?=<\/script)/;
|
|
|
4136
4234
|
const MATCH_SCRIPT_END = /^<(\/)(script)/;
|
|
4137
4235
|
const MATCH_STYLE_DATA = /^[^]*?(?=<\/style)/;
|
|
4138
4236
|
const MATCH_STYLE_END = /^<(\/)(style)/;
|
|
4139
|
-
const MATCH_DIRECTIVE =
|
|
4237
|
+
const MATCH_DIRECTIVE = /^(<!--\s*\[html-validate-)([a-z0-9-]+)(\s*)(.*?)(]?\s*-->)/;
|
|
4140
4238
|
const MATCH_COMMENT = /^<!--([^]*?)-->/;
|
|
4141
4239
|
const MATCH_CONDITIONAL = /^<!\[([^\]]*?)\]>/;
|
|
4142
4240
|
class InvalidTokenError extends Error {
|
|
@@ -4371,7 +4469,7 @@ class AttrDelimiter extends Rule {
|
|
|
4371
4469
|
}
|
|
4372
4470
|
|
|
4373
4471
|
const DEFAULT_PATTERN = "[a-z0-9-:]+";
|
|
4374
|
-
const defaults$
|
|
4472
|
+
const defaults$t = {
|
|
4375
4473
|
pattern: DEFAULT_PATTERN,
|
|
4376
4474
|
ignoreForeign: true,
|
|
4377
4475
|
};
|
|
@@ -4408,7 +4506,7 @@ function generateDescription(name, pattern) {
|
|
|
4408
4506
|
}
|
|
4409
4507
|
class AttrPattern extends Rule {
|
|
4410
4508
|
constructor(options) {
|
|
4411
|
-
super({ ...defaults$
|
|
4509
|
+
super({ ...defaults$t, ...options });
|
|
4412
4510
|
this.pattern = generateRegexp(this.options.pattern);
|
|
4413
4511
|
}
|
|
4414
4512
|
static schema() {
|
|
@@ -4469,7 +4567,7 @@ var QuoteStyle;
|
|
|
4469
4567
|
QuoteStyle["AUTO_QUOTE"] = "auto";
|
|
4470
4568
|
QuoteStyle["ANY_QUOTE"] = "any";
|
|
4471
4569
|
})(QuoteStyle || (QuoteStyle = {}));
|
|
4472
|
-
const defaults$
|
|
4570
|
+
const defaults$s = {
|
|
4473
4571
|
style: "auto",
|
|
4474
4572
|
unquoted: false,
|
|
4475
4573
|
};
|
|
@@ -4536,7 +4634,7 @@ class AttrQuotes extends Rule {
|
|
|
4536
4634
|
};
|
|
4537
4635
|
}
|
|
4538
4636
|
constructor(options) {
|
|
4539
|
-
super({ ...defaults$
|
|
4637
|
+
super({ ...defaults$s, ...options });
|
|
4540
4638
|
this.style = parseStyle$4(this.options.style);
|
|
4541
4639
|
}
|
|
4542
4640
|
setup() {
|
|
@@ -4706,12 +4804,12 @@ class AttributeAllowedValues extends Rule {
|
|
|
4706
4804
|
}
|
|
4707
4805
|
}
|
|
4708
4806
|
|
|
4709
|
-
const defaults$
|
|
4807
|
+
const defaults$r = {
|
|
4710
4808
|
style: "omit",
|
|
4711
4809
|
};
|
|
4712
4810
|
class AttributeBooleanStyle extends Rule {
|
|
4713
4811
|
constructor(options) {
|
|
4714
|
-
super({ ...defaults$
|
|
4812
|
+
super({ ...defaults$r, ...options });
|
|
4715
4813
|
this.hasInvalidStyle = parseStyle$3(this.options.style);
|
|
4716
4814
|
}
|
|
4717
4815
|
static schema() {
|
|
@@ -4787,12 +4885,12 @@ function reportMessage$1(attr, style) {
|
|
|
4787
4885
|
return "";
|
|
4788
4886
|
}
|
|
4789
4887
|
|
|
4790
|
-
const defaults$
|
|
4888
|
+
const defaults$q = {
|
|
4791
4889
|
style: "omit",
|
|
4792
4890
|
};
|
|
4793
4891
|
class AttributeEmptyStyle extends Rule {
|
|
4794
4892
|
constructor(options) {
|
|
4795
|
-
super({ ...defaults$
|
|
4893
|
+
super({ ...defaults$q, ...options });
|
|
4796
4894
|
this.hasInvalidStyle = parseStyle$2(this.options.style);
|
|
4797
4895
|
}
|
|
4798
4896
|
static schema() {
|
|
@@ -4948,12 +5046,12 @@ function describePattern(pattern) {
|
|
|
4948
5046
|
}
|
|
4949
5047
|
}
|
|
4950
5048
|
|
|
4951
|
-
const defaults$
|
|
5049
|
+
const defaults$p = {
|
|
4952
5050
|
pattern: "kebabcase",
|
|
4953
5051
|
};
|
|
4954
5052
|
class ClassPattern extends Rule {
|
|
4955
5053
|
constructor(options) {
|
|
4956
|
-
super({ ...defaults$
|
|
5054
|
+
super({ ...defaults$p, ...options });
|
|
4957
5055
|
this.pattern = parsePattern(this.options.pattern);
|
|
4958
5056
|
}
|
|
4959
5057
|
static schema() {
|
|
@@ -5062,13 +5160,13 @@ class CloseOrder extends Rule {
|
|
|
5062
5160
|
}
|
|
5063
5161
|
}
|
|
5064
5162
|
|
|
5065
|
-
const defaults$
|
|
5163
|
+
const defaults$o = {
|
|
5066
5164
|
include: null,
|
|
5067
5165
|
exclude: null,
|
|
5068
5166
|
};
|
|
5069
5167
|
class Deprecated extends Rule {
|
|
5070
5168
|
constructor(options) {
|
|
5071
|
-
super({ ...defaults$
|
|
5169
|
+
super({ ...defaults$o, ...options });
|
|
5072
5170
|
}
|
|
5073
5171
|
static schema() {
|
|
5074
5172
|
return {
|
|
@@ -5231,12 +5329,12 @@ let NoStyleTag$1 = class NoStyleTag extends Rule {
|
|
|
5231
5329
|
}
|
|
5232
5330
|
};
|
|
5233
5331
|
|
|
5234
|
-
const defaults$
|
|
5332
|
+
const defaults$n = {
|
|
5235
5333
|
style: "uppercase",
|
|
5236
5334
|
};
|
|
5237
5335
|
class DoctypeStyle extends Rule {
|
|
5238
5336
|
constructor(options) {
|
|
5239
|
-
super({ ...defaults$
|
|
5337
|
+
super({ ...defaults$n, ...options });
|
|
5240
5338
|
}
|
|
5241
5339
|
static schema() {
|
|
5242
5340
|
return {
|
|
@@ -5268,12 +5366,12 @@ class DoctypeStyle extends Rule {
|
|
|
5268
5366
|
}
|
|
5269
5367
|
}
|
|
5270
5368
|
|
|
5271
|
-
const defaults$
|
|
5369
|
+
const defaults$m = {
|
|
5272
5370
|
style: "lowercase",
|
|
5273
5371
|
};
|
|
5274
5372
|
class ElementCase extends Rule {
|
|
5275
5373
|
constructor(options) {
|
|
5276
|
-
super({ ...defaults$
|
|
5374
|
+
super({ ...defaults$m, ...options });
|
|
5277
5375
|
this.style = new CaseStyle(this.options.style, "element-case");
|
|
5278
5376
|
}
|
|
5279
5377
|
static schema() {
|
|
@@ -5339,14 +5437,14 @@ class ElementCase extends Rule {
|
|
|
5339
5437
|
}
|
|
5340
5438
|
}
|
|
5341
5439
|
|
|
5342
|
-
const defaults$
|
|
5440
|
+
const defaults$l = {
|
|
5343
5441
|
pattern: "^[a-z][a-z0-9\\-._]*-[a-z0-9\\-._]*$",
|
|
5344
5442
|
whitelist: [],
|
|
5345
5443
|
blacklist: [],
|
|
5346
5444
|
};
|
|
5347
5445
|
class ElementName extends Rule {
|
|
5348
5446
|
constructor(options) {
|
|
5349
|
-
super({ ...defaults$
|
|
5447
|
+
super({ ...defaults$l, ...options });
|
|
5350
5448
|
// eslint-disable-next-line security/detect-non-literal-regexp
|
|
5351
5449
|
this.pattern = new RegExp(this.options.pattern);
|
|
5352
5450
|
}
|
|
@@ -5387,7 +5485,7 @@ class ElementName extends Rule {
|
|
|
5387
5485
|
...context.blacklist.map((cur) => `- ${cur}`),
|
|
5388
5486
|
];
|
|
5389
5487
|
}
|
|
5390
|
-
if (context.pattern !== defaults$
|
|
5488
|
+
if (context.pattern !== defaults$l.pattern) {
|
|
5391
5489
|
return [
|
|
5392
5490
|
`<${context.tagName}> is not a valid element name. This project is configured to only allow names matching the following regular expression:`,
|
|
5393
5491
|
"",
|
|
@@ -5931,29 +6029,70 @@ class EmptyTitle extends Rule {
|
|
|
5931
6029
|
}
|
|
5932
6030
|
}
|
|
5933
6031
|
|
|
6032
|
+
const defaults$k = {
|
|
6033
|
+
allowArrayBrackets: true,
|
|
6034
|
+
shared: ["radio"],
|
|
6035
|
+
};
|
|
5934
6036
|
const UNIQUE_CACHE_KEY = Symbol("form-elements-unique");
|
|
5935
6037
|
const SHARED_CACHE_KEY = Symbol("form-elements-shared");
|
|
5936
6038
|
function haveName(name) {
|
|
5937
6039
|
return typeof name === "string" && name !== "";
|
|
5938
6040
|
}
|
|
5939
|
-
function allowSharedName(node) {
|
|
6041
|
+
function allowSharedName(node, shared) {
|
|
5940
6042
|
const type = node.getAttribute("type");
|
|
5941
|
-
return Boolean(type && type.valueMatches(
|
|
6043
|
+
return Boolean(type && type.valueMatches(shared, false));
|
|
6044
|
+
}
|
|
6045
|
+
function getDocumentation(context) {
|
|
6046
|
+
const trailer = "Each form control must have a unique name.";
|
|
6047
|
+
if (!context) {
|
|
6048
|
+
return trailer;
|
|
6049
|
+
}
|
|
6050
|
+
else {
|
|
6051
|
+
const { name } = context;
|
|
6052
|
+
switch (context.kind) {
|
|
6053
|
+
case "duplicate":
|
|
6054
|
+
return [`Duplicate form control name "${name}"`, trailer].join("\n");
|
|
6055
|
+
case "mix":
|
|
6056
|
+
return [
|
|
6057
|
+
`Form control name cannot mix regular name "{{ name }}" with array brackets "{{ name }}[]"`,
|
|
6058
|
+
trailer,
|
|
6059
|
+
].join("\n");
|
|
6060
|
+
}
|
|
6061
|
+
}
|
|
5942
6062
|
}
|
|
5943
6063
|
class FormDupName extends Rule {
|
|
5944
|
-
|
|
6064
|
+
constructor(options) {
|
|
6065
|
+
super({ ...defaults$k, ...options });
|
|
6066
|
+
}
|
|
6067
|
+
static schema() {
|
|
5945
6068
|
return {
|
|
5946
|
-
|
|
6069
|
+
allowArrayBrackets: {
|
|
6070
|
+
type: "boolean",
|
|
6071
|
+
},
|
|
6072
|
+
shared: {
|
|
6073
|
+
type: "array",
|
|
6074
|
+
items: {
|
|
6075
|
+
enum: ["radio", "checkbox", "submit"],
|
|
6076
|
+
},
|
|
6077
|
+
},
|
|
6078
|
+
};
|
|
6079
|
+
}
|
|
6080
|
+
documentation(context) {
|
|
6081
|
+
return {
|
|
6082
|
+
description: getDocumentation(context),
|
|
5947
6083
|
url: "https://html-validate.org/rules/form-dup-name.html",
|
|
5948
6084
|
};
|
|
5949
6085
|
}
|
|
5950
6086
|
setup() {
|
|
5951
6087
|
const selector = this.getSelector();
|
|
6088
|
+
const { shared } = this.options;
|
|
5952
6089
|
this.on("dom:ready", (event) => {
|
|
5953
6090
|
var _a, _b;
|
|
5954
6091
|
const { document } = event;
|
|
5955
6092
|
const controls = document.querySelectorAll(selector);
|
|
5956
|
-
const [sharedControls, uniqueControls] = partition(controls,
|
|
6093
|
+
const [sharedControls, uniqueControls] = partition(controls, (it) => {
|
|
6094
|
+
return allowSharedName(it, shared);
|
|
6095
|
+
});
|
|
5957
6096
|
/* validate all form controls which require unique elements first so each
|
|
5958
6097
|
* form has a populated list of unique names */
|
|
5959
6098
|
for (const control of uniqueControls) {
|
|
@@ -5980,9 +6119,35 @@ class FormDupName extends Rule {
|
|
|
5980
6119
|
}
|
|
5981
6120
|
validateUniqueName(control, form, attr, name) {
|
|
5982
6121
|
const elements = this.getUniqueElements(form);
|
|
6122
|
+
const { allowArrayBrackets } = this.options;
|
|
6123
|
+
if (allowArrayBrackets) {
|
|
6124
|
+
const isarray = name.endsWith("[]");
|
|
6125
|
+
const basename = isarray ? name.slice(0, -2) : name;
|
|
6126
|
+
const details = elements.get(basename);
|
|
6127
|
+
if (details && details.array !== isarray) {
|
|
6128
|
+
const context = {
|
|
6129
|
+
name: basename,
|
|
6130
|
+
kind: "mix",
|
|
6131
|
+
};
|
|
6132
|
+
this.report({
|
|
6133
|
+
node: control,
|
|
6134
|
+
location: attr.valueLocation,
|
|
6135
|
+
message: 'Cannot mix "{{ name }}[]" and "{{ name }}"',
|
|
6136
|
+
context,
|
|
6137
|
+
});
|
|
6138
|
+
return;
|
|
6139
|
+
}
|
|
6140
|
+
else if (!details && isarray) {
|
|
6141
|
+
elements.set(basename, {
|
|
6142
|
+
array: true,
|
|
6143
|
+
});
|
|
6144
|
+
return;
|
|
6145
|
+
}
|
|
6146
|
+
}
|
|
5983
6147
|
if (elements.has(name)) {
|
|
5984
6148
|
const context = {
|
|
5985
6149
|
name,
|
|
6150
|
+
kind: "duplicate",
|
|
5986
6151
|
};
|
|
5987
6152
|
this.report({
|
|
5988
6153
|
node: control,
|
|
@@ -5992,7 +6157,9 @@ class FormDupName extends Rule {
|
|
|
5992
6157
|
});
|
|
5993
6158
|
}
|
|
5994
6159
|
else {
|
|
5995
|
-
elements.
|
|
6160
|
+
elements.set(name, {
|
|
6161
|
+
array: false,
|
|
6162
|
+
});
|
|
5996
6163
|
}
|
|
5997
6164
|
}
|
|
5998
6165
|
validateSharedName(control, form, attr, name) {
|
|
@@ -6005,6 +6172,7 @@ class FormDupName extends Rule {
|
|
|
6005
6172
|
(sharedElements.has(name) && sharedElements.get(name) !== type)) {
|
|
6006
6173
|
const context = {
|
|
6007
6174
|
name,
|
|
6175
|
+
kind: "duplicate",
|
|
6008
6176
|
};
|
|
6009
6177
|
this.report({
|
|
6010
6178
|
node: control,
|
|
@@ -6036,7 +6204,7 @@ class FormDupName extends Rule {
|
|
|
6036
6204
|
return existing;
|
|
6037
6205
|
}
|
|
6038
6206
|
else {
|
|
6039
|
-
const elements = new
|
|
6207
|
+
const elements = new Map();
|
|
6040
6208
|
form.cacheSet(UNIQUE_CACHE_KEY, elements);
|
|
6041
6209
|
return elements;
|
|
6042
6210
|
}
|
|
@@ -7566,6 +7734,39 @@ class NoUnknownElements extends Rule {
|
|
|
7566
7734
|
}
|
|
7567
7735
|
}
|
|
7568
7736
|
|
|
7737
|
+
class NoUnusedDisable extends Rule {
|
|
7738
|
+
documentation(context) {
|
|
7739
|
+
return {
|
|
7740
|
+
description: context
|
|
7741
|
+
? `\`${context.ruleId}\` rule is disabled but no error was reported.`
|
|
7742
|
+
: "Rule is disabled but no error was reported.",
|
|
7743
|
+
url: "https://html-validate.org/rules/no-unused-disable.html",
|
|
7744
|
+
};
|
|
7745
|
+
}
|
|
7746
|
+
setup() {
|
|
7747
|
+
/* this is a special rule, the `Engine` class directly emits errors on this
|
|
7748
|
+
* rule, it exists only to be able to configure whenever the rule is enabled
|
|
7749
|
+
* or not and to get the regular documentation and contextual help. */
|
|
7750
|
+
}
|
|
7751
|
+
reportUnused(unused, options, location) {
|
|
7752
|
+
const tokens = new DOMTokenList(options.replace(",", " "), location);
|
|
7753
|
+
for (const ruleId of unused) {
|
|
7754
|
+
const index = tokens.indexOf(ruleId);
|
|
7755
|
+
/* istanbul ignore next: the token should be present or it wouldn't be
|
|
7756
|
+
* reported as unused, this is just a sanity check and fallback */
|
|
7757
|
+
const tokenLocation = index >= 0 ? tokens.location(index) : location;
|
|
7758
|
+
this.report({
|
|
7759
|
+
node: null,
|
|
7760
|
+
message: '"{{ ruleId }}" rule is disabled but no error was reported',
|
|
7761
|
+
location: tokenLocation,
|
|
7762
|
+
context: {
|
|
7763
|
+
ruleId: ruleId,
|
|
7764
|
+
},
|
|
7765
|
+
});
|
|
7766
|
+
}
|
|
7767
|
+
}
|
|
7768
|
+
}
|
|
7769
|
+
|
|
7569
7770
|
class NoUtf8Bom extends Rule {
|
|
7570
7771
|
documentation() {
|
|
7571
7772
|
return {
|
|
@@ -9189,6 +9390,7 @@ const bundledRules = {
|
|
|
9189
9390
|
"no-style-tag": NoStyleTag,
|
|
9190
9391
|
"no-trailing-whitespace": NoTrailingWhitespace,
|
|
9191
9392
|
"no-unknown-elements": NoUnknownElements,
|
|
9393
|
+
"no-unused-disable": NoUnusedDisable,
|
|
9192
9394
|
"no-utf8-bom": NoUtf8Bom,
|
|
9193
9395
|
"prefer-button": PreferButton,
|
|
9194
9396
|
"prefer-native-element": PreferNativeElement,
|
|
@@ -9299,6 +9501,7 @@ const config$1 = {
|
|
|
9299
9501
|
"no-self-closing": "error",
|
|
9300
9502
|
"no-trailing-whitespace": "error",
|
|
9301
9503
|
"no-utf8-bom": "error",
|
|
9504
|
+
"no-unused-disable": "error",
|
|
9302
9505
|
"prefer-button": "error",
|
|
9303
9506
|
"prefer-native-element": "error",
|
|
9304
9507
|
"prefer-tbody": "error",
|
|
@@ -9349,6 +9552,7 @@ const config = {
|
|
|
9349
9552
|
"no-dup-id": "error",
|
|
9350
9553
|
"no-multiple-main": "error",
|
|
9351
9554
|
"no-raw-characters": ["error", { relaxed: true }],
|
|
9555
|
+
"no-unused-disable": "error",
|
|
9352
9556
|
"script-element": "error",
|
|
9353
9557
|
"unrecognized-char-ref": "error",
|
|
9354
9558
|
"valid-id": ["error", { relaxed: true }],
|
|
@@ -10148,6 +10352,10 @@ class Parser {
|
|
|
10148
10352
|
offset: 0,
|
|
10149
10353
|
};
|
|
10150
10354
|
}
|
|
10355
|
+
/* trigger starting event */
|
|
10356
|
+
this.trigger("parse:begin", {
|
|
10357
|
+
location: null,
|
|
10358
|
+
});
|
|
10151
10359
|
/* reset DOM in case there are multiple calls in the same session */
|
|
10152
10360
|
this.dom = new DOMTree({
|
|
10153
10361
|
filename: (_a = source.filename) !== null && _a !== void 0 ? _a : "",
|
|
@@ -10182,6 +10390,10 @@ class Parser {
|
|
|
10182
10390
|
* instead */
|
|
10183
10391
|
location: null,
|
|
10184
10392
|
});
|
|
10393
|
+
/* trigger ending event */
|
|
10394
|
+
this.trigger("parse:end", {
|
|
10395
|
+
location: null,
|
|
10396
|
+
});
|
|
10185
10397
|
return this.dom.root;
|
|
10186
10398
|
}
|
|
10187
10399
|
/**
|
|
@@ -10492,11 +10704,11 @@ class Parser {
|
|
|
10492
10704
|
};
|
|
10493
10705
|
}
|
|
10494
10706
|
consumeDirective(token) {
|
|
10495
|
-
const [text, , action, directive,
|
|
10496
|
-
if (
|
|
10707
|
+
const [text, preamble, action, separator1, directive, postamble] = token.data;
|
|
10708
|
+
if (!postamble.startsWith("]")) {
|
|
10497
10709
|
throw new ParserError(token.location, `Missing end bracket "]" on directive "${text}"`);
|
|
10498
10710
|
}
|
|
10499
|
-
const match = directive.match(/^(.*?)(
|
|
10711
|
+
const match = directive.match(/^(.*?)(?:(\s*(?:--|:)\s*)(.*))?$/);
|
|
10500
10712
|
/* istanbul ignore next: should not be possible, would be emitted as comment token */
|
|
10501
10713
|
if (!match) {
|
|
10502
10714
|
throw new Error(`Failed to parse directive "${text}"`);
|
|
@@ -10504,12 +10716,32 @@ class Parser {
|
|
|
10504
10716
|
if (!isValidDirective(action)) {
|
|
10505
10717
|
throw new ParserError(token.location, `Unknown directive "${action}"`);
|
|
10506
10718
|
}
|
|
10507
|
-
const [, data, comment] = match;
|
|
10719
|
+
const [, data, separator2, comment] = match;
|
|
10720
|
+
const prefix = "html-validate-";
|
|
10721
|
+
/* <!-- [html-validate-action options -- comment] -->
|
|
10722
|
+
* ^ ^ ^--------------- comment offset
|
|
10723
|
+
* | \-------------------------- options offset
|
|
10724
|
+
* \--------------------------------- action offset
|
|
10725
|
+
*/
|
|
10726
|
+
const actionOffset = preamble.length;
|
|
10727
|
+
const optionsOffset = actionOffset + action.length + separator1.length;
|
|
10728
|
+
const commentOffset = optionsOffset + data.length + (separator2 || "").length;
|
|
10729
|
+
const location = sliceLocation(token.location, preamble.length - prefix.length - 1, -postamble.length + 1);
|
|
10730
|
+
const actionLocation = sliceLocation(token.location, actionOffset, actionOffset + action.length);
|
|
10731
|
+
const optionsLocation = data
|
|
10732
|
+
? sliceLocation(token.location, optionsOffset, optionsOffset + data.length)
|
|
10733
|
+
: undefined;
|
|
10734
|
+
const commentLocation = comment
|
|
10735
|
+
? sliceLocation(token.location, commentOffset, commentOffset + comment.length)
|
|
10736
|
+
: undefined;
|
|
10508
10737
|
this.trigger("directive", {
|
|
10509
10738
|
action,
|
|
10510
10739
|
data,
|
|
10511
10740
|
comment: comment || "",
|
|
10512
|
-
location
|
|
10741
|
+
location,
|
|
10742
|
+
actionLocation,
|
|
10743
|
+
optionsLocation,
|
|
10744
|
+
commentLocation,
|
|
10513
10745
|
});
|
|
10514
10746
|
}
|
|
10515
10747
|
/**
|
|
@@ -10795,6 +11027,18 @@ function messageSort(a, b) {
|
|
|
10795
11027
|
return 0;
|
|
10796
11028
|
}
|
|
10797
11029
|
|
|
11030
|
+
let blockerCounter = 1;
|
|
11031
|
+
/**
|
|
11032
|
+
* Creates a new rule blocker for using when blocking rules from generating
|
|
11033
|
+
* errors.
|
|
11034
|
+
*
|
|
11035
|
+
* @internal
|
|
11036
|
+
*/
|
|
11037
|
+
function createBlocker() {
|
|
11038
|
+
const id = blockerCounter++;
|
|
11039
|
+
return id;
|
|
11040
|
+
}
|
|
11041
|
+
|
|
10798
11042
|
/**
|
|
10799
11043
|
* @internal
|
|
10800
11044
|
*/
|
|
@@ -10822,6 +11066,15 @@ class Engine {
|
|
|
10822
11066
|
const parser = this.instantiateParser();
|
|
10823
11067
|
/* setup plugins and rules */
|
|
10824
11068
|
const { rules } = this.setupPlugins(source, this.config, parser);
|
|
11069
|
+
const noUnusedDisable = rules["no-unused-disable"];
|
|
11070
|
+
const directiveContext = {
|
|
11071
|
+
rules,
|
|
11072
|
+
reportUnused(unused, options, location) {
|
|
11073
|
+
if (noUnusedDisable) {
|
|
11074
|
+
noUnusedDisable.reportUnused(unused, options, location);
|
|
11075
|
+
}
|
|
11076
|
+
},
|
|
11077
|
+
};
|
|
10825
11078
|
/* create a faux location at the start of the stream for the next events */
|
|
10826
11079
|
const location = {
|
|
10827
11080
|
filename: source.filename,
|
|
@@ -10847,7 +11100,7 @@ class Engine {
|
|
|
10847
11100
|
parser.trigger("source:ready", sourceEvent);
|
|
10848
11101
|
/* setup directive handling */
|
|
10849
11102
|
parser.on("directive", (_, event) => {
|
|
10850
|
-
this.processDirective(event, parser,
|
|
11103
|
+
this.processDirective(event, parser, directiveContext);
|
|
10851
11104
|
});
|
|
10852
11105
|
/* parse token stream */
|
|
10853
11106
|
try {
|
|
@@ -10954,12 +11207,15 @@ class Engine {
|
|
|
10954
11207
|
instantiateParser() {
|
|
10955
11208
|
return new this.ParserClass(this.config);
|
|
10956
11209
|
}
|
|
10957
|
-
processDirective(event, parser,
|
|
11210
|
+
processDirective(event, parser, context) {
|
|
11211
|
+
var _a;
|
|
10958
11212
|
const rules = event.data
|
|
10959
11213
|
.split(",")
|
|
10960
11214
|
.map((name) => name.trim())
|
|
10961
|
-
.map((name) =>
|
|
11215
|
+
.map((name) => context.rules[name])
|
|
10962
11216
|
.filter((rule) => rule); /* filter out missing rules */
|
|
11217
|
+
/* istanbul ignore next: option must be present or there would be no rules to disable */
|
|
11218
|
+
const location = (_a = event.optionsLocation) !== null && _a !== void 0 ? _a : event.location;
|
|
10963
11219
|
switch (event.action) {
|
|
10964
11220
|
case "enable":
|
|
10965
11221
|
this.processEnableDirective(rules, parser);
|
|
@@ -10968,10 +11224,10 @@ class Engine {
|
|
|
10968
11224
|
this.processDisableDirective(rules, parser);
|
|
10969
11225
|
break;
|
|
10970
11226
|
case "disable-block":
|
|
10971
|
-
this.processDisableBlockDirective(rules, parser);
|
|
11227
|
+
this.processDisableBlockDirective(context, rules, parser, event.data, location);
|
|
10972
11228
|
break;
|
|
10973
11229
|
case "disable-next":
|
|
10974
|
-
this.processDisableNextDirective(rules, parser);
|
|
11230
|
+
this.processDisableNextDirective(context, rules, parser, event.data, location);
|
|
10975
11231
|
break;
|
|
10976
11232
|
}
|
|
10977
11233
|
}
|
|
@@ -10996,10 +11252,13 @@ class Engine {
|
|
|
10996
11252
|
data.target.disableRules(rules.map((rule) => rule.name));
|
|
10997
11253
|
});
|
|
10998
11254
|
}
|
|
10999
|
-
processDisableBlockDirective(rules, parser) {
|
|
11255
|
+
processDisableBlockDirective(context, rules, parser, options, location) {
|
|
11256
|
+
const ruleIds = rules.map((it) => it.name);
|
|
11257
|
+
const blocker = createBlocker();
|
|
11258
|
+
const unused = new Set(ruleIds);
|
|
11000
11259
|
let directiveBlock = null;
|
|
11001
11260
|
for (const rule of rules) {
|
|
11002
|
-
rule.
|
|
11261
|
+
rule.block(blocker);
|
|
11003
11262
|
}
|
|
11004
11263
|
const unregisterOpen = parser.on("tag:start", (event, data) => {
|
|
11005
11264
|
var _a, _b;
|
|
@@ -11011,7 +11270,7 @@ class Engine {
|
|
|
11011
11270
|
}
|
|
11012
11271
|
/* disable rules directly on the node so it will be recorded for later,
|
|
11013
11272
|
* more specifically when using the domtree to trigger errors */
|
|
11014
|
-
data.target.
|
|
11273
|
+
data.target.blockRules(ruleIds, blocker);
|
|
11015
11274
|
});
|
|
11016
11275
|
const unregisterClose = parser.on("tag:end", (event, data) => {
|
|
11017
11276
|
/* if the directive is the last thing in a block no id would be set */
|
|
@@ -11024,26 +11283,45 @@ class Engine {
|
|
|
11024
11283
|
unregisterClose();
|
|
11025
11284
|
unregisterOpen();
|
|
11026
11285
|
for (const rule of rules) {
|
|
11027
|
-
rule.
|
|
11286
|
+
rule.unblock(blocker);
|
|
11028
11287
|
}
|
|
11029
11288
|
}
|
|
11030
11289
|
});
|
|
11290
|
+
parser.on("rule:error", (event, data) => {
|
|
11291
|
+
if (data.blockers.includes(blocker)) {
|
|
11292
|
+
unused.delete(data.ruleId);
|
|
11293
|
+
}
|
|
11294
|
+
});
|
|
11295
|
+
parser.on("parse:end", () => {
|
|
11296
|
+
context.reportUnused(unused, options, location);
|
|
11297
|
+
});
|
|
11031
11298
|
}
|
|
11032
|
-
processDisableNextDirective(rules, parser) {
|
|
11299
|
+
processDisableNextDirective(context, rules, parser, options, location) {
|
|
11300
|
+
const ruleIds = rules.map((it) => it.name);
|
|
11301
|
+
const blocker = createBlocker();
|
|
11302
|
+
const unused = new Set(ruleIds);
|
|
11033
11303
|
for (const rule of rules) {
|
|
11034
|
-
rule.
|
|
11304
|
+
rule.block(blocker);
|
|
11035
11305
|
}
|
|
11036
|
-
/*
|
|
11306
|
+
/* block rules directly on the node so it will be recorded for later,
|
|
11037
11307
|
* more specifically when using the domtree to trigger errors */
|
|
11038
11308
|
const unregister = parser.on("tag:start", (event, data) => {
|
|
11039
|
-
data.target.
|
|
11309
|
+
data.target.blockRules(ruleIds, blocker);
|
|
11310
|
+
});
|
|
11311
|
+
parser.on("rule:error", (event, data) => {
|
|
11312
|
+
if (data.blockers.includes(blocker)) {
|
|
11313
|
+
unused.delete(data.ruleId);
|
|
11314
|
+
}
|
|
11315
|
+
});
|
|
11316
|
+
parser.on("parse:end", () => {
|
|
11317
|
+
context.reportUnused(unused, options, location);
|
|
11040
11318
|
});
|
|
11041
11319
|
/* disable directive after next event occurs */
|
|
11042
11320
|
parser.once("tag:ready, tag:end, attr", () => {
|
|
11043
11321
|
unregister();
|
|
11044
11322
|
parser.defer(() => {
|
|
11045
11323
|
for (const rule of rules) {
|
|
11046
|
-
rule.
|
|
11324
|
+
rule.unblock(blocker);
|
|
11047
11325
|
}
|
|
11048
11326
|
});
|
|
11049
11327
|
});
|
|
@@ -11435,7 +11713,7 @@ class HtmlValidate {
|
|
|
11435
11713
|
/** @public */
|
|
11436
11714
|
const name = "html-validate";
|
|
11437
11715
|
/** @public */
|
|
11438
|
-
const version = "7.
|
|
11716
|
+
const version = "7.13.0";
|
|
11439
11717
|
/** @public */
|
|
11440
11718
|
const bugs = "https://gitlab.com/html-validate/html-validate/issues/new";
|
|
11441
11719
|
|
|
@@ -11878,5 +12156,5 @@ function getFormatter(name) {
|
|
|
11878
12156
|
return (_a = availableFormatters[name]) !== null && _a !== void 0 ? _a : null;
|
|
11879
12157
|
}
|
|
11880
12158
|
|
|
11881
|
-
export { Attribute as A, Config as C, DynamicValue as D, EventHandler as E, FileSystemConfigLoader as F, HtmlValidate as H, MetaTable as M, NodeClosed as N, Parser as P, Rule as R, Severity as S, TextNode as T, UserError as U, WrappedError as W, ConfigError as a, ConfigLoader as b, StaticConfigLoader as c, HtmlElement as d, SchemaValidationError as e, NestedError as f, MetaCopyableProperty as g, Reporter as h, TemplateExtractor as i, definePlugin as j, getFormatter as k, legacyRequire as l, ensureError as m, configDataFromFile as n, compatibilityCheck as o, presets as p, codeframe as q, ruleExists as r, sliceLocation as s, isTextNode as t, isElementNode as u, version as v, generateIdSelector as w, name as x, bugs as y };
|
|
12159
|
+
export { Attribute as A, Config as C, DynamicValue as D, EventHandler as E, FileSystemConfigLoader as F, HtmlValidate as H, MetaTable as M, NodeClosed as N, Parser as P, Rule as R, Severity as S, TextNode as T, UserError as U, Validator as V, WrappedError as W, ConfigError as a, ConfigLoader as b, StaticConfigLoader as c, HtmlElement as d, SchemaValidationError as e, NestedError as f, MetaCopyableProperty as g, Reporter as h, TemplateExtractor as i, definePlugin as j, getFormatter as k, legacyRequire as l, ensureError as m, configDataFromFile as n, compatibilityCheck as o, presets as p, codeframe as q, ruleExists as r, sliceLocation as s, isTextNode as t, isElementNode as u, version as v, generateIdSelector as w, name as x, bugs as y };
|
|
11882
12160
|
//# sourceMappingURL=core.js.map
|