html-validate 5.5.0 → 6.0.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/CHANGELOG.md +42 -0
- package/dist/cjs/browser.js +2 -6
- package/dist/cjs/browser.js.map +1 -1
- package/dist/cjs/cli.js +41 -27
- package/dist/cjs/cli.js.map +1 -1
- package/dist/cjs/core.d.ts +16 -7
- package/dist/cjs/core.js +527 -329
- package/dist/cjs/core.js.map +1 -1
- package/dist/cjs/html-validate.js +9 -9
- package/dist/cjs/html-validate.js.map +1 -1
- package/dist/cjs/index.d.ts +11 -1
- package/dist/cjs/index.js +2 -6
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/jest-lib.js +5 -7
- package/dist/cjs/jest-lib.js.map +1 -1
- package/dist/cjs/test-utils.js +1 -1
- package/dist/es/cli.js +19 -5
- package/dist/es/cli.js.map +1 -1
- package/dist/es/core.d.ts +16 -7
- package/dist/es/core.js +490 -292
- package/dist/es/core.js.map +1 -1
- package/dist/es/html-validate.js.map +1 -1
- package/dist/es/index.d.ts +11 -1
- package/dist/schema/elements.json +43 -5
- package/elements/html5.json +940 -307
- package/package.json +20 -21
package/dist/es/core.js
CHANGED
|
@@ -574,18 +574,63 @@ const definitions = {
|
|
|
574
574
|
type: "object",
|
|
575
575
|
patternProperties: {
|
|
576
576
|
"^.*$": {
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
{
|
|
577
|
+
anyOf: [
|
|
578
|
+
{
|
|
579
|
+
type: "object",
|
|
580
|
+
additionalProperties: false,
|
|
581
|
+
properties: {
|
|
582
|
+
boolean: {
|
|
583
|
+
type: "boolean",
|
|
584
|
+
title: "Set to true if this is a boolean attribute"
|
|
585
|
+
},
|
|
586
|
+
deprecated: {
|
|
587
|
+
title: "Set to true or string if this attribute is deprecated",
|
|
588
|
+
oneOf: [
|
|
589
|
+
{
|
|
590
|
+
type: "boolean"
|
|
591
|
+
},
|
|
592
|
+
{
|
|
593
|
+
type: "string"
|
|
594
|
+
}
|
|
595
|
+
]
|
|
596
|
+
},
|
|
597
|
+
list: {
|
|
598
|
+
type: "boolean",
|
|
599
|
+
title: "Set to true if this attribute is a list of space-separated tokens, each which must be valid by itself"
|
|
600
|
+
},
|
|
601
|
+
"enum": {
|
|
602
|
+
type: "array",
|
|
603
|
+
title: "Exhaustive list of values (string or regex) this attribute accepts",
|
|
604
|
+
uniqueItems: true,
|
|
605
|
+
items: {
|
|
606
|
+
anyOf: [
|
|
607
|
+
{
|
|
608
|
+
type: "string"
|
|
609
|
+
},
|
|
610
|
+
{
|
|
611
|
+
regexp: true
|
|
612
|
+
}
|
|
613
|
+
]
|
|
614
|
+
}
|
|
615
|
+
},
|
|
616
|
+
omit: {
|
|
617
|
+
type: "boolean",
|
|
618
|
+
title: "Set to true if this attribute can optionally omit its value"
|
|
619
|
+
},
|
|
620
|
+
required: {
|
|
621
|
+
type: "boolean",
|
|
622
|
+
title: "Set to true if this attribute is required"
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
},
|
|
626
|
+
{
|
|
627
|
+
type: "array",
|
|
628
|
+
uniqueItems: true,
|
|
629
|
+
items: {
|
|
582
630
|
type: "string"
|
|
583
|
-
},
|
|
584
|
-
{
|
|
585
|
-
regexp: true
|
|
586
631
|
}
|
|
587
|
-
|
|
588
|
-
|
|
632
|
+
}
|
|
633
|
+
]
|
|
589
634
|
}
|
|
590
635
|
}
|
|
591
636
|
},
|
|
@@ -636,6 +681,111 @@ var schema = {
|
|
|
636
681
|
definitions: definitions
|
|
637
682
|
};
|
|
638
683
|
|
|
684
|
+
var TextContent$1;
|
|
685
|
+
(function (TextContent) {
|
|
686
|
+
/* forbid node to have text content, inter-element whitespace is ignored */
|
|
687
|
+
TextContent["NONE"] = "none";
|
|
688
|
+
/* node can have text but not required too */
|
|
689
|
+
TextContent["DEFAULT"] = "default";
|
|
690
|
+
/* node requires text-nodes to be present (direct or by descendant) */
|
|
691
|
+
TextContent["REQUIRED"] = "required";
|
|
692
|
+
/* node requires accessible text (hidden text is ignored, tries to get text from accessibility tree) */
|
|
693
|
+
TextContent["ACCESSIBLE"] = "accessible";
|
|
694
|
+
})(TextContent$1 || (TextContent$1 = {}));
|
|
695
|
+
/**
|
|
696
|
+
* Properties listed here can be copied (loaded) onto another element using
|
|
697
|
+
* [[HtmlElement.loadMeta]].
|
|
698
|
+
*/
|
|
699
|
+
const MetaCopyableProperty = [
|
|
700
|
+
"metadata",
|
|
701
|
+
"flow",
|
|
702
|
+
"sectioning",
|
|
703
|
+
"heading",
|
|
704
|
+
"phrasing",
|
|
705
|
+
"embedded",
|
|
706
|
+
"interactive",
|
|
707
|
+
"transparent",
|
|
708
|
+
"form",
|
|
709
|
+
"labelable",
|
|
710
|
+
"attributes",
|
|
711
|
+
"permittedContent",
|
|
712
|
+
"permittedDescendants",
|
|
713
|
+
"permittedOrder",
|
|
714
|
+
"requiredAncestors",
|
|
715
|
+
"requiredContent",
|
|
716
|
+
];
|
|
717
|
+
/**
|
|
718
|
+
* @internal
|
|
719
|
+
*/
|
|
720
|
+
function setMetaProperty(dst, key, value) {
|
|
721
|
+
dst[key] = value;
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
function isSet(value) {
|
|
725
|
+
return typeof value !== "undefined";
|
|
726
|
+
}
|
|
727
|
+
function flag(value) {
|
|
728
|
+
return value ? true : undefined;
|
|
729
|
+
}
|
|
730
|
+
function stripUndefined(src) {
|
|
731
|
+
const entries = Object.entries(src).filter(([, value]) => isSet(value));
|
|
732
|
+
return Object.fromEntries(entries);
|
|
733
|
+
}
|
|
734
|
+
function migrateSingleAttribute(src, key) {
|
|
735
|
+
var _a, _b;
|
|
736
|
+
const result = {};
|
|
737
|
+
result.deprecated = flag((_a = src.deprecatedAttributes) === null || _a === void 0 ? void 0 : _a.includes(key));
|
|
738
|
+
result.required = flag((_b = src.requiredAttributes) === null || _b === void 0 ? void 0 : _b.includes(key));
|
|
739
|
+
result.omit = undefined;
|
|
740
|
+
const attr = src.attributes ? src.attributes[key] : undefined;
|
|
741
|
+
if (typeof attr === "undefined") {
|
|
742
|
+
return stripUndefined(result);
|
|
743
|
+
}
|
|
744
|
+
/* when the attribute is set to null we use a special property "delete" to
|
|
745
|
+
* flag it, if it is still set during merge (inheritance, overwriting, etc) the attribute will be removed */
|
|
746
|
+
if (attr === null) {
|
|
747
|
+
result.delete = true;
|
|
748
|
+
return stripUndefined(result);
|
|
749
|
+
}
|
|
750
|
+
if (Array.isArray(attr)) {
|
|
751
|
+
if (attr.length === 0) {
|
|
752
|
+
result.boolean = true;
|
|
753
|
+
}
|
|
754
|
+
else {
|
|
755
|
+
result.enum = attr.filter((it) => it !== "");
|
|
756
|
+
if (attr.includes("")) {
|
|
757
|
+
result.omit = true;
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
return stripUndefined(result);
|
|
761
|
+
}
|
|
762
|
+
else {
|
|
763
|
+
return stripUndefined({ ...result, ...attr });
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
function migrateAttributes(src) {
|
|
767
|
+
var _a, _b, _c;
|
|
768
|
+
const keys = [
|
|
769
|
+
...Object.keys((_a = src.attributes) !== null && _a !== void 0 ? _a : {}),
|
|
770
|
+
...((_b = src.requiredAttributes) !== null && _b !== void 0 ? _b : []),
|
|
771
|
+
...((_c = src.deprecatedAttributes) !== null && _c !== void 0 ? _c : []),
|
|
772
|
+
].sort();
|
|
773
|
+
const entries = keys.map((key) => {
|
|
774
|
+
return [key, migrateSingleAttribute(src, key)];
|
|
775
|
+
});
|
|
776
|
+
return Object.fromEntries(entries);
|
|
777
|
+
}
|
|
778
|
+
function migrateElement(src) {
|
|
779
|
+
const result = {
|
|
780
|
+
...src,
|
|
781
|
+
attributes: migrateAttributes(src),
|
|
782
|
+
};
|
|
783
|
+
/* removed properties */
|
|
784
|
+
delete result.deprecatedAttributes;
|
|
785
|
+
delete result.requiredAttributes;
|
|
786
|
+
return result;
|
|
787
|
+
}
|
|
788
|
+
|
|
639
789
|
const dynamicKeys = [
|
|
640
790
|
"metadata",
|
|
641
791
|
"flow",
|
|
@@ -729,7 +879,7 @@ class MetaTable {
|
|
|
729
879
|
for (const [key, value] of Object.entries(obj)) {
|
|
730
880
|
if (key === "$schema")
|
|
731
881
|
continue;
|
|
732
|
-
this.addEntry(key, value);
|
|
882
|
+
this.addEntry(key, migrateElement(value));
|
|
733
883
|
}
|
|
734
884
|
}
|
|
735
885
|
/**
|
|
@@ -757,7 +907,7 @@ class MetaTable {
|
|
|
757
907
|
*/
|
|
758
908
|
getMetaFor(tagName) {
|
|
759
909
|
tagName = tagName.toLowerCase();
|
|
760
|
-
return this.elements[tagName] ?
|
|
910
|
+
return this.elements[tagName] ? { ...this.elements[tagName] } : null;
|
|
761
911
|
}
|
|
762
912
|
/**
|
|
763
913
|
* Find all tags which has enabled given property.
|
|
@@ -786,7 +936,7 @@ class MetaTable {
|
|
|
786
936
|
}
|
|
787
937
|
}
|
|
788
938
|
/* merge all sources together */
|
|
789
|
-
const expanded =
|
|
939
|
+
const expanded = this.mergeElement(parent, { ...entry, tagName });
|
|
790
940
|
expandRegex(expanded);
|
|
791
941
|
this.elements[tagName] = expanded;
|
|
792
942
|
}
|
|
@@ -824,7 +974,16 @@ class MetaTable {
|
|
|
824
974
|
}
|
|
825
975
|
}
|
|
826
976
|
mergeElement(a, b) {
|
|
827
|
-
|
|
977
|
+
const merged = deepmerge(a, b, { arrayMerge: overwriteMerge$1 });
|
|
978
|
+
/* special handling when removing attributes by setting them to null
|
|
979
|
+
* resulting in the deletion flag being set */
|
|
980
|
+
const filteredAttrs = Object.entries(merged.attributes).filter(([, attr]) => {
|
|
981
|
+
const val = !attr.delete;
|
|
982
|
+
delete attr.delete;
|
|
983
|
+
return val;
|
|
984
|
+
});
|
|
985
|
+
merged.attributes = Object.fromEntries(filteredAttrs);
|
|
986
|
+
return merged;
|
|
828
987
|
}
|
|
829
988
|
resolve(node) {
|
|
830
989
|
if (node.meta) {
|
|
@@ -836,7 +995,7 @@ function expandProperties(node, entry) {
|
|
|
836
995
|
for (const key of dynamicKeys) {
|
|
837
996
|
const property = entry[key];
|
|
838
997
|
if (property && typeof property !== "boolean") {
|
|
839
|
-
entry
|
|
998
|
+
setMetaProperty(entry, key, evaluateProperty(node, property));
|
|
840
999
|
}
|
|
841
1000
|
}
|
|
842
1001
|
}
|
|
@@ -862,14 +1021,9 @@ function expandRegexValue(value) {
|
|
|
862
1021
|
* Expand all regular expressions in strings ("/../"). This mutates the object.
|
|
863
1022
|
*/
|
|
864
1023
|
function expandRegex(entry) {
|
|
865
|
-
if (!entry.attributes)
|
|
866
|
-
return;
|
|
867
1024
|
for (const [name, values] of Object.entries(entry.attributes)) {
|
|
868
|
-
if (values) {
|
|
869
|
-
entry.attributes[name] = values.map(expandRegexValue);
|
|
870
|
-
}
|
|
871
|
-
else {
|
|
872
|
-
delete entry.attributes[name];
|
|
1025
|
+
if (values.enum) {
|
|
1026
|
+
entry.attributes[name].enum = values.enum.map(expandRegexValue);
|
|
873
1027
|
}
|
|
874
1028
|
}
|
|
875
1029
|
}
|
|
@@ -925,41 +1079,6 @@ function matchAttribute(node, match) {
|
|
|
925
1079
|
}
|
|
926
1080
|
}
|
|
927
1081
|
|
|
928
|
-
var TextContent$1;
|
|
929
|
-
(function (TextContent) {
|
|
930
|
-
/* forbid node to have text content, inter-element whitespace is ignored */
|
|
931
|
-
TextContent["NONE"] = "none";
|
|
932
|
-
/* node can have text but not required too */
|
|
933
|
-
TextContent["DEFAULT"] = "default";
|
|
934
|
-
/* node requires text-nodes to be present (direct or by descendant) */
|
|
935
|
-
TextContent["REQUIRED"] = "required";
|
|
936
|
-
/* node requires accessible text (hidden text is ignored, tries to get text from accessibility tree) */
|
|
937
|
-
TextContent["ACCESSIBLE"] = "accessible";
|
|
938
|
-
})(TextContent$1 || (TextContent$1 = {}));
|
|
939
|
-
/**
|
|
940
|
-
* Properties listed here can be copied (loaded) onto another element using
|
|
941
|
-
* [[HtmlElement.loadMeta]].
|
|
942
|
-
*/
|
|
943
|
-
const MetaCopyableProperty = [
|
|
944
|
-
"metadata",
|
|
945
|
-
"flow",
|
|
946
|
-
"sectioning",
|
|
947
|
-
"heading",
|
|
948
|
-
"phrasing",
|
|
949
|
-
"embedded",
|
|
950
|
-
"interactive",
|
|
951
|
-
"transparent",
|
|
952
|
-
"form",
|
|
953
|
-
"labelable",
|
|
954
|
-
"requiredAttributes",
|
|
955
|
-
"attributes",
|
|
956
|
-
"permittedContent",
|
|
957
|
-
"permittedDescendants",
|
|
958
|
-
"permittedOrder",
|
|
959
|
-
"requiredAncestors",
|
|
960
|
-
"requiredContent",
|
|
961
|
-
];
|
|
962
|
-
|
|
963
1082
|
class DynamicValue {
|
|
964
1083
|
constructor(expr) {
|
|
965
1084
|
this.expr = expr;
|
|
@@ -1811,8 +1930,9 @@ class HtmlElement extends DOMNode {
|
|
|
1811
1930
|
this.metaElement = {};
|
|
1812
1931
|
}
|
|
1813
1932
|
for (const key of MetaCopyableProperty) {
|
|
1814
|
-
|
|
1815
|
-
|
|
1933
|
+
const value = meta[key];
|
|
1934
|
+
if (typeof value !== "undefined") {
|
|
1935
|
+
setMetaProperty(this.metaElement, key, value);
|
|
1816
1936
|
}
|
|
1817
1937
|
else {
|
|
1818
1938
|
delete this.metaElement[key];
|
|
@@ -2228,6 +2348,7 @@ class Validator {
|
|
|
2228
2348
|
* @param rules - Element attribute metadta.
|
|
2229
2349
|
* @returns `true` if attribute passes all tests.
|
|
2230
2350
|
*/
|
|
2351
|
+
/* eslint-disable-next-line complexity */
|
|
2231
2352
|
static validateAttribute(attr, rules) {
|
|
2232
2353
|
const rule = rules[attr.key];
|
|
2233
2354
|
if (!rule) {
|
|
@@ -2241,19 +2362,34 @@ class Validator {
|
|
|
2241
2362
|
return true;
|
|
2242
2363
|
}
|
|
2243
2364
|
const empty = value === null || value === "";
|
|
2244
|
-
/*
|
|
2245
|
-
|
|
2365
|
+
/* if boolean is set the value can be either null, empty string or the
|
|
2366
|
+
* attribute key (attribute-boolean-style regulates style) */
|
|
2367
|
+
if (rule.boolean) {
|
|
2246
2368
|
return empty || value === attr.key;
|
|
2247
2369
|
}
|
|
2248
|
-
/* if the
|
|
2249
|
-
* (
|
|
2250
|
-
if (rule.
|
|
2370
|
+
/* if omit is set the value can be either null or empty string
|
|
2371
|
+
* (attribute-empty style regulates style) */
|
|
2372
|
+
if (rule.omit && empty) {
|
|
2373
|
+
return true;
|
|
2374
|
+
}
|
|
2375
|
+
/* validate each token when using list, all tokens must be valid */
|
|
2376
|
+
if (rule.list) {
|
|
2377
|
+
const tokens = new DOMTokenList(value, attr.valueLocation);
|
|
2378
|
+
return tokens.every((token) => {
|
|
2379
|
+
return this.validateAttributeValue(token, rule);
|
|
2380
|
+
});
|
|
2381
|
+
}
|
|
2382
|
+
return this.validateAttributeValue(value, rule);
|
|
2383
|
+
}
|
|
2384
|
+
static validateAttributeValue(value, rule) {
|
|
2385
|
+
/* skip attribute if it not have enumerated list */
|
|
2386
|
+
if (!rule.enum) {
|
|
2251
2387
|
return true;
|
|
2252
2388
|
}
|
|
2253
2389
|
if (value === null || value === undefined) {
|
|
2254
2390
|
return false;
|
|
2255
2391
|
}
|
|
2256
|
-
return rule.some((entry) => {
|
|
2392
|
+
return rule.enum.some((entry) => {
|
|
2257
2393
|
if (entry instanceof RegExp) {
|
|
2258
2394
|
return !!value.match(entry);
|
|
2259
2395
|
}
|
|
@@ -2519,12 +2655,12 @@ var configurationSchema = {
|
|
|
2519
2655
|
const espree = legacyRequire("espree");
|
|
2520
2656
|
const walk = legacyRequire("acorn-walk");
|
|
2521
2657
|
function joinTemplateLiteral(nodes) {
|
|
2522
|
-
let offset = nodes[0].start;
|
|
2658
|
+
let offset = nodes[0].start + 1;
|
|
2523
2659
|
let output = "";
|
|
2524
2660
|
for (const node of nodes) {
|
|
2525
|
-
output += " ".repeat(node.start - offset);
|
|
2661
|
+
output += " ".repeat(node.start + 1 - offset);
|
|
2526
2662
|
output += node.value.raw;
|
|
2527
|
-
offset = node.end;
|
|
2663
|
+
offset = node.end - 2;
|
|
2528
2664
|
}
|
|
2529
2665
|
return output;
|
|
2530
2666
|
}
|
|
@@ -2716,7 +2852,7 @@ var TRANSFORMER_API;
|
|
|
2716
2852
|
})(TRANSFORMER_API || (TRANSFORMER_API = {}));
|
|
2717
2853
|
|
|
2718
2854
|
const name = "html-validate";
|
|
2719
|
-
const version = "
|
|
2855
|
+
const version = "6.0.0";
|
|
2720
2856
|
const homepage = "https://html-validate.org";
|
|
2721
2857
|
const bugs = {
|
|
2722
2858
|
url: "https://gitlab.com/html-validate/html-validate/issues/new"
|
|
@@ -3008,7 +3144,7 @@ const description = {
|
|
|
3008
3144
|
};
|
|
3009
3145
|
class AllowedLinks extends Rule {
|
|
3010
3146
|
constructor(options) {
|
|
3011
|
-
super(
|
|
3147
|
+
super({ ...defaults$p, ...options });
|
|
3012
3148
|
}
|
|
3013
3149
|
static schema() {
|
|
3014
3150
|
return {
|
|
@@ -3256,7 +3392,7 @@ const defaults$o = {
|
|
|
3256
3392
|
};
|
|
3257
3393
|
class AttrCase extends Rule {
|
|
3258
3394
|
constructor(options) {
|
|
3259
|
-
super(
|
|
3395
|
+
super({ ...defaults$o, ...options });
|
|
3260
3396
|
this.style = new CaseStyle(this.options.style, "attr-case");
|
|
3261
3397
|
}
|
|
3262
3398
|
static schema() {
|
|
@@ -3609,7 +3745,7 @@ function generateDescription(name, pattern) {
|
|
|
3609
3745
|
}
|
|
3610
3746
|
class AttrPattern extends Rule {
|
|
3611
3747
|
constructor(options) {
|
|
3612
|
-
super(
|
|
3748
|
+
super({ ...defaults$n, ...options });
|
|
3613
3749
|
this.pattern = generateRegexp(this.options.pattern);
|
|
3614
3750
|
}
|
|
3615
3751
|
static schema() {
|
|
@@ -3675,7 +3811,7 @@ const defaults$m = {
|
|
|
3675
3811
|
};
|
|
3676
3812
|
class AttrQuotes extends Rule {
|
|
3677
3813
|
constructor(options) {
|
|
3678
|
-
super(
|
|
3814
|
+
super({ ...defaults$m, ...options });
|
|
3679
3815
|
this.style = parseStyle$4(this.options.style);
|
|
3680
3816
|
}
|
|
3681
3817
|
static schema() {
|
|
@@ -3771,11 +3907,11 @@ class AttributeAllowedValues extends Rule {
|
|
|
3771
3907
|
if (!context) {
|
|
3772
3908
|
return docs;
|
|
3773
3909
|
}
|
|
3774
|
-
if (context.allowed.
|
|
3775
|
-
const allowed = context.allowed.map((val) => `- \`${val}\``);
|
|
3910
|
+
if (context.allowed.enum) {
|
|
3911
|
+
const allowed = context.allowed.enum.map((val) => `- \`${val}\``);
|
|
3776
3912
|
docs.description = `Element <${context.element}> does not allow attribute \`${context.attribute}\` to have the value \`"${context.value}"\`, it must match one of the following:\n\n${allowed.join("\n")}`;
|
|
3777
3913
|
}
|
|
3778
|
-
else {
|
|
3914
|
+
else if (context.allowed.boolean) {
|
|
3779
3915
|
docs.description = `Element <${context.element}> attribute \`${context.attribute}\` must be a boolean attribute, e.g. \`<${context.element} ${context.attribute}>\``;
|
|
3780
3916
|
}
|
|
3781
3917
|
return docs;
|
|
@@ -3831,7 +3967,7 @@ const defaults$l = {
|
|
|
3831
3967
|
};
|
|
3832
3968
|
class AttributeBooleanStyle extends Rule {
|
|
3833
3969
|
constructor(options) {
|
|
3834
|
-
super(
|
|
3970
|
+
super({ ...defaults$l, ...options });
|
|
3835
3971
|
this.hasInvalidStyle = parseStyle$3(this.options.style);
|
|
3836
3972
|
}
|
|
3837
3973
|
static schema() {
|
|
@@ -3876,7 +4012,8 @@ class AttributeBooleanStyle extends Rule {
|
|
|
3876
4012
|
});
|
|
3877
4013
|
}
|
|
3878
4014
|
isBoolean(attr, rules) {
|
|
3879
|
-
|
|
4015
|
+
var _a;
|
|
4016
|
+
return Boolean((_a = rules[attr.key]) === null || _a === void 0 ? void 0 : _a.boolean);
|
|
3880
4017
|
}
|
|
3881
4018
|
}
|
|
3882
4019
|
function parseStyle$3(style) {
|
|
@@ -3911,7 +4048,7 @@ const defaults$k = {
|
|
|
3911
4048
|
};
|
|
3912
4049
|
class AttributeEmptyStyle extends Rule {
|
|
3913
4050
|
constructor(options) {
|
|
3914
|
-
super(
|
|
4051
|
+
super({ ...defaults$k, ...options });
|
|
3915
4052
|
this.hasInvalidStyle = parseStyle$2(this.options.style);
|
|
3916
4053
|
}
|
|
3917
4054
|
static schema() {
|
|
@@ -3960,7 +4097,8 @@ class AttributeEmptyStyle extends Rule {
|
|
|
3960
4097
|
}
|
|
3961
4098
|
}
|
|
3962
4099
|
function allowsEmpty(attr, rules) {
|
|
3963
|
-
|
|
4100
|
+
var _a;
|
|
4101
|
+
return Boolean((_a = rules[attr.key]) === null || _a === void 0 ? void 0 : _a.omit);
|
|
3964
4102
|
}
|
|
3965
4103
|
function isEmptyValue(attr) {
|
|
3966
4104
|
/* dynamic values are ignored, assumed to contain a value */
|
|
@@ -4023,7 +4161,7 @@ const defaults$j = {
|
|
|
4023
4161
|
};
|
|
4024
4162
|
class ClassPattern extends Rule {
|
|
4025
4163
|
constructor(options) {
|
|
4026
|
-
super(
|
|
4164
|
+
super({ ...defaults$j, ...options });
|
|
4027
4165
|
this.pattern = parsePattern(this.options.pattern);
|
|
4028
4166
|
}
|
|
4029
4167
|
static schema() {
|
|
@@ -4136,7 +4274,7 @@ const defaults$i = {
|
|
|
4136
4274
|
};
|
|
4137
4275
|
class Deprecated extends Rule {
|
|
4138
4276
|
constructor(options) {
|
|
4139
|
-
super(
|
|
4277
|
+
super({ ...defaults$i, ...options });
|
|
4140
4278
|
}
|
|
4141
4279
|
static schema() {
|
|
4142
4280
|
return {
|
|
@@ -4230,7 +4368,7 @@ class Deprecated extends Rule {
|
|
|
4230
4368
|
this.report(node, message, location, context);
|
|
4231
4369
|
}
|
|
4232
4370
|
reportObject(deprecated, node, location) {
|
|
4233
|
-
const context =
|
|
4371
|
+
const context = { ...deprecated, tagName: node.tagName };
|
|
4234
4372
|
const notice = deprecated.message ? `: ${deprecated.message}` : "";
|
|
4235
4373
|
const message = `<${node.tagName}> is deprecated${notice}`;
|
|
4236
4374
|
this.report(node, message, location, context);
|
|
@@ -4304,7 +4442,7 @@ const defaults$h = {
|
|
|
4304
4442
|
};
|
|
4305
4443
|
class DoctypeStyle extends Rule {
|
|
4306
4444
|
constructor(options) {
|
|
4307
|
-
super(
|
|
4445
|
+
super({ ...defaults$h, ...options });
|
|
4308
4446
|
}
|
|
4309
4447
|
static schema() {
|
|
4310
4448
|
return {
|
|
@@ -4341,7 +4479,7 @@ const defaults$g = {
|
|
|
4341
4479
|
};
|
|
4342
4480
|
class ElementCase extends Rule {
|
|
4343
4481
|
constructor(options) {
|
|
4344
|
-
super(
|
|
4482
|
+
super({ ...defaults$g, ...options });
|
|
4345
4483
|
this.style = new CaseStyle(this.options.style, "element-case");
|
|
4346
4484
|
}
|
|
4347
4485
|
static schema() {
|
|
@@ -4411,7 +4549,7 @@ const defaults$f = {
|
|
|
4411
4549
|
};
|
|
4412
4550
|
class ElementName extends Rule {
|
|
4413
4551
|
constructor(options) {
|
|
4414
|
-
super(
|
|
4552
|
+
super({ ...defaults$f, ...options });
|
|
4415
4553
|
// eslint-disable-next-line security/detect-non-literal-regexp
|
|
4416
4554
|
this.pattern = new RegExp(this.options.pattern);
|
|
4417
4555
|
}
|
|
@@ -4683,9 +4821,14 @@ class ElementRequiredAttributes extends Rule {
|
|
|
4683
4821
|
this.on("tag:end", (event) => {
|
|
4684
4822
|
const node = event.previous;
|
|
4685
4823
|
const meta = node.meta;
|
|
4686
|
-
|
|
4824
|
+
/* handle missing metadata and missing attributes */
|
|
4825
|
+
if (!meta || !meta.attributes) {
|
|
4687
4826
|
return;
|
|
4688
|
-
|
|
4827
|
+
}
|
|
4828
|
+
for (const [key, attr] of Object.entries(meta.attributes)) {
|
|
4829
|
+
if (!attr.required) {
|
|
4830
|
+
continue;
|
|
4831
|
+
}
|
|
4689
4832
|
if (node.hasAttribute(key))
|
|
4690
4833
|
continue;
|
|
4691
4834
|
const context = {
|
|
@@ -4868,7 +5011,7 @@ function parseMaxInitial(value) {
|
|
|
4868
5011
|
}
|
|
4869
5012
|
class HeadingLevel extends Rule {
|
|
4870
5013
|
constructor(options) {
|
|
4871
|
-
super(
|
|
5014
|
+
super({ ...defaults$e, ...options });
|
|
4872
5015
|
this.stack = [];
|
|
4873
5016
|
this.minInitialRank = parseMaxInitial(this.options.minInitialRank);
|
|
4874
5017
|
this.sectionRoots = this.options.sectioningRoots.map((it) => new Pattern(it));
|
|
@@ -5031,7 +5174,7 @@ const defaults$d = {
|
|
|
5031
5174
|
};
|
|
5032
5175
|
class IdPattern extends Rule {
|
|
5033
5176
|
constructor(options) {
|
|
5034
|
-
super(
|
|
5177
|
+
super({ ...defaults$d, ...options });
|
|
5035
5178
|
this.pattern = parsePattern(this.options.pattern);
|
|
5036
5179
|
}
|
|
5037
5180
|
static schema() {
|
|
@@ -5383,7 +5526,7 @@ const defaults$c = {
|
|
|
5383
5526
|
};
|
|
5384
5527
|
class LongTitle extends Rule {
|
|
5385
5528
|
constructor(options) {
|
|
5386
|
-
super(
|
|
5529
|
+
super({ ...defaults$c, ...options });
|
|
5387
5530
|
this.maxlength = this.options.maxlength;
|
|
5388
5531
|
}
|
|
5389
5532
|
static schema() {
|
|
@@ -5536,7 +5679,7 @@ const defaults$b = {
|
|
|
5536
5679
|
};
|
|
5537
5680
|
class NoAutoplay extends Rule {
|
|
5538
5681
|
constructor(options) {
|
|
5539
|
-
super(
|
|
5682
|
+
super({ ...defaults$b, ...options });
|
|
5540
5683
|
}
|
|
5541
5684
|
documentation(context) {
|
|
5542
5685
|
const tagName = context ? ` on <${context.tagName}>` : "";
|
|
@@ -5632,8 +5775,12 @@ class NoDeprecatedAttr extends Rule {
|
|
|
5632
5775
|
if (meta === null) {
|
|
5633
5776
|
return;
|
|
5634
5777
|
}
|
|
5635
|
-
const
|
|
5636
|
-
if (
|
|
5778
|
+
const metaAttribute = meta.attributes && meta.attributes[attr];
|
|
5779
|
+
if (!metaAttribute) {
|
|
5780
|
+
return;
|
|
5781
|
+
}
|
|
5782
|
+
const deprecated = metaAttribute.deprecated;
|
|
5783
|
+
if (deprecated) {
|
|
5637
5784
|
this.report(node, `Attribute "${event.key}" is deprecated on <${node.tagName}> element`, event.keyLocation);
|
|
5638
5785
|
}
|
|
5639
5786
|
});
|
|
@@ -5790,7 +5937,7 @@ function getCSSDeclarations(value) {
|
|
|
5790
5937
|
}
|
|
5791
5938
|
class NoInlineStyle extends Rule {
|
|
5792
5939
|
constructor(options) {
|
|
5793
|
-
super(
|
|
5940
|
+
super({ ...defaults$a, ...options });
|
|
5794
5941
|
}
|
|
5795
5942
|
static schema() {
|
|
5796
5943
|
return {
|
|
@@ -6012,7 +6159,7 @@ const replacementTable = new Map([
|
|
|
6012
6159
|
]);
|
|
6013
6160
|
class NoRawCharacters extends Rule {
|
|
6014
6161
|
constructor(options) {
|
|
6015
|
-
super(
|
|
6162
|
+
super({ ...defaults$9, ...options });
|
|
6016
6163
|
this.relaxed = this.options.relaxed;
|
|
6017
6164
|
}
|
|
6018
6165
|
static schema() {
|
|
@@ -6197,7 +6344,7 @@ const defaults$8 = {
|
|
|
6197
6344
|
};
|
|
6198
6345
|
class NoSelfClosing extends Rule {
|
|
6199
6346
|
constructor(options) {
|
|
6200
|
-
super(
|
|
6347
|
+
super({ ...defaults$8, ...options });
|
|
6201
6348
|
}
|
|
6202
6349
|
static schema() {
|
|
6203
6350
|
return {
|
|
@@ -6336,7 +6483,7 @@ const defaults$7 = {
|
|
|
6336
6483
|
};
|
|
6337
6484
|
class PreferButton extends Rule {
|
|
6338
6485
|
constructor(options) {
|
|
6339
|
-
super(
|
|
6486
|
+
super({ ...defaults$7, ...options });
|
|
6340
6487
|
}
|
|
6341
6488
|
static schema() {
|
|
6342
6489
|
return {
|
|
@@ -6441,7 +6588,7 @@ const defaults$6 = {
|
|
|
6441
6588
|
};
|
|
6442
6589
|
class PreferNativeElement extends Rule {
|
|
6443
6590
|
constructor(options) {
|
|
6444
|
-
super(
|
|
6591
|
+
super({ ...defaults$6, ...options });
|
|
6445
6592
|
}
|
|
6446
6593
|
static schema() {
|
|
6447
6594
|
return {
|
|
@@ -6571,7 +6718,7 @@ const supportSri = {
|
|
|
6571
6718
|
};
|
|
6572
6719
|
class RequireSri extends Rule {
|
|
6573
6720
|
constructor(options) {
|
|
6574
|
-
super(
|
|
6721
|
+
super({ ...defaults$5, ...options });
|
|
6575
6722
|
this.target = this.options.target;
|
|
6576
6723
|
}
|
|
6577
6724
|
static schema() {
|
|
@@ -8666,7 +8813,7 @@ const defaults$4 = {
|
|
|
8666
8813
|
};
|
|
8667
8814
|
class Void extends Rule {
|
|
8668
8815
|
constructor(options) {
|
|
8669
|
-
super(
|
|
8816
|
+
super({ ...defaults$4, ...options });
|
|
8670
8817
|
this.style = parseStyle$1(this.options.style);
|
|
8671
8818
|
}
|
|
8672
8819
|
get deprecated() {
|
|
@@ -8775,7 +8922,7 @@ const defaults$3 = {
|
|
|
8775
8922
|
};
|
|
8776
8923
|
class VoidStyle extends Rule {
|
|
8777
8924
|
constructor(options) {
|
|
8778
|
-
super(
|
|
8925
|
+
super({ ...defaults$3, ...options });
|
|
8779
8926
|
this.style = parseStyle(this.options.style);
|
|
8780
8927
|
}
|
|
8781
8928
|
static schema() {
|
|
@@ -8978,7 +9125,7 @@ const defaults$2 = {
|
|
|
8978
9125
|
};
|
|
8979
9126
|
class H37 extends Rule {
|
|
8980
9127
|
constructor(options) {
|
|
8981
|
-
super(
|
|
9128
|
+
super({ ...defaults$2, ...options });
|
|
8982
9129
|
/* ensure alias is array */
|
|
8983
9130
|
if (!Array.isArray(this.options.alias)) {
|
|
8984
9131
|
this.options.alias = [this.options.alias];
|
|
@@ -9105,7 +9252,73 @@ const bundledRules$1 = {
|
|
|
9105
9252
|
"wcag/h71": H71,
|
|
9106
9253
|
};
|
|
9107
9254
|
|
|
9108
|
-
const bundledRules =
|
|
9255
|
+
const bundledRules = {
|
|
9256
|
+
"allowed-links": AllowedLinks,
|
|
9257
|
+
"aria-label-misuse": AriaLabelMisuse,
|
|
9258
|
+
"attr-case": AttrCase,
|
|
9259
|
+
"attr-delimiter": AttrDelimiter,
|
|
9260
|
+
"attr-pattern": AttrPattern,
|
|
9261
|
+
"attr-quotes": AttrQuotes,
|
|
9262
|
+
"attr-spacing": AttrSpacing,
|
|
9263
|
+
"attribute-allowed-values": AttributeAllowedValues,
|
|
9264
|
+
"attribute-boolean-style": AttributeBooleanStyle,
|
|
9265
|
+
"attribute-empty-style": AttributeEmptyStyle,
|
|
9266
|
+
"class-pattern": ClassPattern,
|
|
9267
|
+
"close-attr": CloseAttr,
|
|
9268
|
+
"close-order": CloseOrder,
|
|
9269
|
+
deprecated: Deprecated,
|
|
9270
|
+
"deprecated-rule": DeprecatedRule,
|
|
9271
|
+
"doctype-html": NoStyleTag$1,
|
|
9272
|
+
"doctype-style": DoctypeStyle,
|
|
9273
|
+
"element-case": ElementCase,
|
|
9274
|
+
"element-name": ElementName,
|
|
9275
|
+
"element-permitted-content": ElementPermittedContent,
|
|
9276
|
+
"element-permitted-occurrences": ElementPermittedOccurrences,
|
|
9277
|
+
"element-permitted-order": ElementPermittedOrder,
|
|
9278
|
+
"element-required-attributes": ElementRequiredAttributes,
|
|
9279
|
+
"element-required-content": ElementRequiredContent,
|
|
9280
|
+
"empty-heading": EmptyHeading,
|
|
9281
|
+
"empty-title": EmptyTitle,
|
|
9282
|
+
"heading-level": HeadingLevel,
|
|
9283
|
+
"id-pattern": IdPattern,
|
|
9284
|
+
"input-attributes": InputAttributes,
|
|
9285
|
+
"input-missing-label": InputMissingLabel,
|
|
9286
|
+
"long-title": LongTitle,
|
|
9287
|
+
"meta-refresh": MetaRefresh,
|
|
9288
|
+
"missing-doctype": MissingDoctype,
|
|
9289
|
+
"multiple-labeled-controls": MultipleLabeledControls,
|
|
9290
|
+
"no-autoplay": NoAutoplay,
|
|
9291
|
+
"no-conditional-comment": NoConditionalComment,
|
|
9292
|
+
"no-deprecated-attr": NoDeprecatedAttr,
|
|
9293
|
+
"no-dup-attr": NoDupAttr,
|
|
9294
|
+
"no-dup-class": NoDupClass,
|
|
9295
|
+
"no-dup-id": NoDupID,
|
|
9296
|
+
"no-implicit-close": NoImplicitClose,
|
|
9297
|
+
"no-inline-style": NoInlineStyle,
|
|
9298
|
+
"no-missing-references": NoMissingReferences,
|
|
9299
|
+
"no-multiple-main": NoMultipleMain,
|
|
9300
|
+
"no-raw-characters": NoRawCharacters,
|
|
9301
|
+
"no-redundant-for": NoRedundantFor,
|
|
9302
|
+
"no-redundant-role": NoRedundantRole,
|
|
9303
|
+
"no-self-closing": NoSelfClosing,
|
|
9304
|
+
"no-style-tag": NoStyleTag,
|
|
9305
|
+
"no-trailing-whitespace": NoTrailingWhitespace,
|
|
9306
|
+
"no-unknown-elements": NoUnknownElements,
|
|
9307
|
+
"no-utf8-bom": NoUtf8Bom,
|
|
9308
|
+
"prefer-button": PreferButton,
|
|
9309
|
+
"prefer-native-element": PreferNativeElement,
|
|
9310
|
+
"prefer-tbody": PreferTbody,
|
|
9311
|
+
"require-sri": RequireSri,
|
|
9312
|
+
"script-element": ScriptElement,
|
|
9313
|
+
"script-type": ScriptType,
|
|
9314
|
+
"svg-focusable": SvgFocusable,
|
|
9315
|
+
"text-content": TextContent,
|
|
9316
|
+
"unrecognized-char-ref": UnknownCharReference,
|
|
9317
|
+
void: Void,
|
|
9318
|
+
"void-content": VoidContent,
|
|
9319
|
+
"void-style": VoidStyle,
|
|
9320
|
+
...bundledRules$1,
|
|
9321
|
+
};
|
|
9109
9322
|
|
|
9110
9323
|
var defaultConfig = {};
|
|
9111
9324
|
|
|
@@ -9343,7 +9556,7 @@ function overwriteMerge(a, b) {
|
|
|
9343
9556
|
return b;
|
|
9344
9557
|
}
|
|
9345
9558
|
function mergeInternal(base, rhs) {
|
|
9346
|
-
const dst = deepmerge(base,
|
|
9559
|
+
const dst = deepmerge(base, { ...rhs, rules: {} });
|
|
9347
9560
|
/* rules need some special care, should overwrite arrays instead of
|
|
9348
9561
|
* concaternation, i.e. ["error", {...options}] should not be merged by
|
|
9349
9562
|
* appending to old value */
|
|
@@ -9568,7 +9781,7 @@ class Config {
|
|
|
9568
9781
|
* @internal primary purpose is unittests
|
|
9569
9782
|
*/
|
|
9570
9783
|
get() {
|
|
9571
|
-
const config =
|
|
9784
|
+
const config = { ...this.config };
|
|
9572
9785
|
if (config.elements) {
|
|
9573
9786
|
config.elements = config.elements.map((cur) => {
|
|
9574
9787
|
if (typeof cur === "string") {
|
|
@@ -9654,7 +9867,11 @@ class Config {
|
|
|
9654
9867
|
if (!properties) {
|
|
9655
9868
|
continue;
|
|
9656
9869
|
}
|
|
9657
|
-
for (const [
|
|
9870
|
+
for (const [raw, schema] of Object.entries(properties)) {
|
|
9871
|
+
/* at compile time this is a fixed list but the point of this method is
|
|
9872
|
+
* to augment the runtime with additional keys so it is a bit of lying
|
|
9873
|
+
* to typescript */
|
|
9874
|
+
const key = raw;
|
|
9658
9875
|
if (schema.copyable && !MetaCopyableProperty.includes(key)) {
|
|
9659
9876
|
MetaCopyableProperty.push(key);
|
|
9660
9877
|
}
|
|
@@ -9850,31 +10067,6 @@ class ConfigLoader {
|
|
|
9850
10067
|
}
|
|
9851
10068
|
}
|
|
9852
10069
|
|
|
9853
|
-
/*! *****************************************************************************
|
|
9854
|
-
Copyright (c) Microsoft Corporation. All rights reserved.
|
|
9855
|
-
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
|
9856
|
-
this file except in compliance with the License. You may obtain a copy of the
|
|
9857
|
-
License at http://www.apache.org/licenses/LICENSE-2.0
|
|
9858
|
-
|
|
9859
|
-
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
9860
|
-
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
|
|
9861
|
-
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
|
|
9862
|
-
MERCHANTABLITY OR NON-INFRINGEMENT.
|
|
9863
|
-
|
|
9864
|
-
See the Apache Version 2.0 License for specific language governing permissions
|
|
9865
|
-
and limitations under the License.
|
|
9866
|
-
***************************************************************************** */
|
|
9867
|
-
|
|
9868
|
-
function __rest(s, e) {
|
|
9869
|
-
var t = {};
|
|
9870
|
-
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
9871
|
-
t[p] = s[p];
|
|
9872
|
-
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
9873
|
-
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) if (e.indexOf(p[i]) < 0)
|
|
9874
|
-
t[p[i]] = s[p[i]];
|
|
9875
|
-
return t;
|
|
9876
|
-
}
|
|
9877
|
-
|
|
9878
10070
|
class EventHandler {
|
|
9879
10071
|
constructor() {
|
|
9880
10072
|
this.listeners = {};
|
|
@@ -10448,7 +10640,7 @@ class Reporter {
|
|
|
10448
10640
|
merged[key].messages = [...merged[key].messages, ...result.messages];
|
|
10449
10641
|
}
|
|
10450
10642
|
else {
|
|
10451
|
-
merged[key] =
|
|
10643
|
+
merged[key] = { ...result };
|
|
10452
10644
|
}
|
|
10453
10645
|
});
|
|
10454
10646
|
});
|
|
@@ -10550,14 +10742,16 @@ function messageSort(a, b) {
|
|
|
10550
10742
|
}
|
|
10551
10743
|
|
|
10552
10744
|
class Engine {
|
|
10553
|
-
constructor(config,
|
|
10745
|
+
constructor(config, ParserClass) {
|
|
10554
10746
|
this.report = new Reporter();
|
|
10555
|
-
this.configData = configData;
|
|
10556
10747
|
this.config = config;
|
|
10557
10748
|
this.ParserClass = ParserClass;
|
|
10558
10749
|
/* initialize plugins and rules */
|
|
10559
10750
|
const result = this.initPlugins(this.config);
|
|
10560
|
-
this.availableRules =
|
|
10751
|
+
this.availableRules = {
|
|
10752
|
+
...bundledRules,
|
|
10753
|
+
...result.availableRules,
|
|
10754
|
+
};
|
|
10561
10755
|
}
|
|
10562
10756
|
/**
|
|
10563
10757
|
* Lint sources and return report
|
|
@@ -10582,13 +10776,13 @@ class Engine {
|
|
|
10582
10776
|
/* trigger configuration ready event */
|
|
10583
10777
|
const configEvent = {
|
|
10584
10778
|
location,
|
|
10585
|
-
config: this.
|
|
10779
|
+
config: this.config,
|
|
10586
10780
|
rules,
|
|
10587
10781
|
};
|
|
10588
10782
|
parser.trigger("config:ready", configEvent);
|
|
10589
10783
|
/* trigger source ready event */
|
|
10590
10784
|
/* eslint-disable-next-line @typescript-eslint/no-unused-vars -- object destructured on purpose to remove property */
|
|
10591
|
-
const sourceData =
|
|
10785
|
+
const { hooks: _, ...sourceData } = source;
|
|
10592
10786
|
const sourceEvent = {
|
|
10593
10787
|
location,
|
|
10594
10788
|
source: sourceData,
|
|
@@ -10895,133 +11089,31 @@ class Engine {
|
|
|
10895
11089
|
}
|
|
10896
11090
|
|
|
10897
11091
|
/**
|
|
10898
|
-
*
|
|
10899
|
-
|
|
10900
|
-
function findConfigurationFiles(directory) {
|
|
10901
|
-
return ["json", "cjs", "js"]
|
|
10902
|
-
.map((extension) => path.join(directory, `.htmlvalidate.${extension}`))
|
|
10903
|
-
.filter((filePath) => fs.existsSync(filePath));
|
|
10904
|
-
}
|
|
10905
|
-
/**
|
|
10906
|
-
* Loads configuration by traversing filesystem.
|
|
10907
|
-
*
|
|
10908
|
-
* Configuration is read from three sources and in the following order:
|
|
10909
|
-
*
|
|
10910
|
-
* 1. Global configuration passed to constructor.
|
|
10911
|
-
* 2. Configuration files found when traversing the directory structure.
|
|
10912
|
-
* 3. Override passed to this function.
|
|
10913
|
-
*
|
|
10914
|
-
* The following configuration filenames are searched:
|
|
10915
|
-
*
|
|
10916
|
-
* - `.htmlvalidate.json`
|
|
10917
|
-
* - `.htmlvalidate.js`
|
|
10918
|
-
* - `.htmlvalidate.cjs`
|
|
10919
|
-
*
|
|
10920
|
-
* Global configuration is used when no configuration file is found. The
|
|
10921
|
-
* result is always merged with override if present.
|
|
10922
|
-
*
|
|
10923
|
-
* The `root` property set to `true` affects the configuration as following:
|
|
11092
|
+
* The static configuration loader does not do any per-handle lookup. Only the
|
|
11093
|
+
* global or per-call configuration is used.
|
|
10924
11094
|
*
|
|
10925
|
-
*
|
|
10926
|
-
*
|
|
10927
|
-
* returned. No configuration files are searched.
|
|
10928
|
-
* 3. Setting `root` in configuration file only stops directory traversal.
|
|
11095
|
+
* In practice this means no configuration is fetch by traversing the
|
|
11096
|
+
* filesystem.
|
|
10929
11097
|
*/
|
|
10930
|
-
class
|
|
10931
|
-
|
|
10932
|
-
* @param config - Global configuration
|
|
10933
|
-
* @param configFactory - Optional configuration factory
|
|
10934
|
-
*/
|
|
10935
|
-
constructor(config, configFactory = Config) {
|
|
10936
|
-
super(config, configFactory);
|
|
10937
|
-
this.cache = new Map();
|
|
10938
|
-
}
|
|
10939
|
-
/**
|
|
10940
|
-
* Get configuration for given filename.
|
|
10941
|
-
*
|
|
10942
|
-
* @param filename - Filename to get configuration for.
|
|
10943
|
-
* @param configOverride - Configuration to merge final result with.
|
|
10944
|
-
*/
|
|
10945
|
-
getConfigFor(filename, configOverride) {
|
|
10946
|
-
/* special case when the overridden configuration is marked as root, should
|
|
10947
|
-
* not try to load any more configuration files */
|
|
11098
|
+
class StaticConfigLoader extends ConfigLoader {
|
|
11099
|
+
getConfigFor(handle, configOverride) {
|
|
10948
11100
|
const override = this.loadFromObject(configOverride || {});
|
|
10949
11101
|
if (override.isRootFound()) {
|
|
10950
11102
|
override.init();
|
|
10951
11103
|
return override;
|
|
10952
11104
|
}
|
|
10953
|
-
|
|
10954
|
-
* try to load and more configuration files */
|
|
10955
|
-
if (this.globalConfig.isRootFound()) {
|
|
10956
|
-
const merged = this.globalConfig.merge(override);
|
|
10957
|
-
merged.init();
|
|
10958
|
-
return merged;
|
|
10959
|
-
}
|
|
10960
|
-
const config = this.fromFilename(filename);
|
|
10961
|
-
const merged = config ? config.merge(override) : this.globalConfig.merge(override);
|
|
11105
|
+
const merged = this.globalConfig.merge(override);
|
|
10962
11106
|
merged.init();
|
|
10963
11107
|
return merged;
|
|
10964
11108
|
}
|
|
10965
|
-
|
|
10966
|
-
|
|
10967
|
-
*
|
|
10968
|
-
* @param filename - If given only the cache for that file is flushed.
|
|
10969
|
-
*/
|
|
10970
|
-
flushCache(filename) {
|
|
10971
|
-
if (filename) {
|
|
10972
|
-
this.cache.delete(filename);
|
|
10973
|
-
}
|
|
10974
|
-
else {
|
|
10975
|
-
this.cache.clear();
|
|
10976
|
-
}
|
|
10977
|
-
}
|
|
10978
|
-
/**
|
|
10979
|
-
* Load raw configuration from directory traversal.
|
|
10980
|
-
*
|
|
10981
|
-
* This configuration is not merged with global configuration and may return
|
|
10982
|
-
* `null` if no configuration files are found.
|
|
10983
|
-
*/
|
|
10984
|
-
fromFilename(filename) {
|
|
10985
|
-
var _a;
|
|
10986
|
-
if (filename === "inline") {
|
|
10987
|
-
return null;
|
|
10988
|
-
}
|
|
10989
|
-
if (this.cache.has(filename)) {
|
|
10990
|
-
return (_a = this.cache.get(filename)) !== null && _a !== void 0 ? _a : null;
|
|
10991
|
-
}
|
|
10992
|
-
let found = false;
|
|
10993
|
-
let current = path.resolve(path.dirname(filename));
|
|
10994
|
-
let config = this.empty();
|
|
10995
|
-
// eslint-disable-next-line no-constant-condition
|
|
10996
|
-
while (true) {
|
|
10997
|
-
/* search configuration files in current directory */
|
|
10998
|
-
for (const configFile of findConfigurationFiles(current)) {
|
|
10999
|
-
const local = this.loadFromFile(configFile);
|
|
11000
|
-
found = true;
|
|
11001
|
-
config = local.merge(config);
|
|
11002
|
-
}
|
|
11003
|
-
/* stop if a configuration with "root" is set to true */
|
|
11004
|
-
if (config.isRootFound()) {
|
|
11005
|
-
break;
|
|
11006
|
-
}
|
|
11007
|
-
/* get the parent directory */
|
|
11008
|
-
const child = current;
|
|
11009
|
-
current = path.dirname(current);
|
|
11010
|
-
/* stop if this is the root directory */
|
|
11011
|
-
if (current === child) {
|
|
11012
|
-
break;
|
|
11013
|
-
}
|
|
11014
|
-
}
|
|
11015
|
-
/* no config was found by loader, return null and let caller decide what to do */
|
|
11016
|
-
if (!found) {
|
|
11017
|
-
this.cache.set(filename, null);
|
|
11018
|
-
return null;
|
|
11019
|
-
}
|
|
11020
|
-
this.cache.set(filename, config);
|
|
11021
|
-
return config;
|
|
11109
|
+
flushCache() {
|
|
11110
|
+
/* do nothing */
|
|
11022
11111
|
}
|
|
11023
11112
|
defaultConfig() {
|
|
11024
|
-
return this.
|
|
11113
|
+
return this.loadFromObject({
|
|
11114
|
+
extends: ["html-validate:recommended"],
|
|
11115
|
+
elements: ["html5"],
|
|
11116
|
+
});
|
|
11025
11117
|
}
|
|
11026
11118
|
}
|
|
11027
11119
|
|
|
@@ -11045,7 +11137,7 @@ function isConfigData(value) {
|
|
|
11045
11137
|
class HtmlValidate {
|
|
11046
11138
|
constructor(arg) {
|
|
11047
11139
|
const [loader, config] = arg instanceof ConfigLoader ? [arg, undefined] : [undefined, arg];
|
|
11048
|
-
this.configLoader = loader !== null && loader !== void 0 ? loader : new
|
|
11140
|
+
this.configLoader = loader !== null && loader !== void 0 ? loader : new StaticConfigLoader(config);
|
|
11049
11141
|
}
|
|
11050
11142
|
validateString(str, arg1, arg2, arg3) {
|
|
11051
11143
|
const filename = typeof arg1 === "string" ? arg1 : "inline";
|
|
@@ -11072,7 +11164,7 @@ class HtmlValidate {
|
|
|
11072
11164
|
const config = this.getConfigFor(input.filename, configOverride);
|
|
11073
11165
|
const resolved = config.resolve();
|
|
11074
11166
|
const source = resolved.transformSource(input);
|
|
11075
|
-
const engine = new Engine(resolved,
|
|
11167
|
+
const engine = new Engine(resolved, Parser);
|
|
11076
11168
|
return engine.lint(source);
|
|
11077
11169
|
}
|
|
11078
11170
|
/**
|
|
@@ -11086,7 +11178,7 @@ class HtmlValidate {
|
|
|
11086
11178
|
const config = this.getConfigFor(filename);
|
|
11087
11179
|
const resolved = config.resolve();
|
|
11088
11180
|
const source = resolved.transformFilename(filename);
|
|
11089
|
-
const engine = new Engine(resolved,
|
|
11181
|
+
const engine = new Engine(resolved, Parser);
|
|
11090
11182
|
return engine.lint(source);
|
|
11091
11183
|
}
|
|
11092
11184
|
/**
|
|
@@ -11131,7 +11223,7 @@ class HtmlValidate {
|
|
|
11131
11223
|
const config = this.getConfigFor(filename);
|
|
11132
11224
|
const resolved = config.resolve();
|
|
11133
11225
|
const source = resolved.transformFilename(filename);
|
|
11134
|
-
const engine = new Engine(resolved,
|
|
11226
|
+
const engine = new Engine(resolved, Parser);
|
|
11135
11227
|
return engine.dumpTokens(source);
|
|
11136
11228
|
}
|
|
11137
11229
|
/**
|
|
@@ -11146,7 +11238,7 @@ class HtmlValidate {
|
|
|
11146
11238
|
const config = this.getConfigFor(filename);
|
|
11147
11239
|
const resolved = config.resolve();
|
|
11148
11240
|
const source = resolved.transformFilename(filename);
|
|
11149
|
-
const engine = new Engine(resolved,
|
|
11241
|
+
const engine = new Engine(resolved, Parser);
|
|
11150
11242
|
return engine.dumpEvents(source);
|
|
11151
11243
|
}
|
|
11152
11244
|
/**
|
|
@@ -11161,7 +11253,7 @@ class HtmlValidate {
|
|
|
11161
11253
|
const config = this.getConfigFor(filename);
|
|
11162
11254
|
const resolved = config.resolve();
|
|
11163
11255
|
const source = resolved.transformFilename(filename);
|
|
11164
|
-
const engine = new Engine(resolved,
|
|
11256
|
+
const engine = new Engine(resolved, Parser);
|
|
11165
11257
|
return engine.dumpTree(source);
|
|
11166
11258
|
}
|
|
11167
11259
|
/**
|
|
@@ -11239,7 +11331,7 @@ class HtmlValidate {
|
|
|
11239
11331
|
*/
|
|
11240
11332
|
getRuleDocumentation(ruleId, config = null, context = null) {
|
|
11241
11333
|
const c = config || this.getConfigFor("inline");
|
|
11242
|
-
const engine = new Engine(c.resolve(),
|
|
11334
|
+
const engine = new Engine(c.resolve(), Parser);
|
|
11243
11335
|
return engine.getRuleDocumentation(ruleId, context);
|
|
11244
11336
|
}
|
|
11245
11337
|
/**
|
|
@@ -11276,35 +11368,6 @@ class HtmlValidate {
|
|
|
11276
11368
|
}
|
|
11277
11369
|
}
|
|
11278
11370
|
|
|
11279
|
-
/**
|
|
11280
|
-
* The static configuration loader does not do any per-handle lookup. Only the
|
|
11281
|
-
* global or per-call configuration is used.
|
|
11282
|
-
*
|
|
11283
|
-
* In practice this means no configuration is fetch by traversing the
|
|
11284
|
-
* filesystem.
|
|
11285
|
-
*/
|
|
11286
|
-
class StaticConfigLoader extends ConfigLoader {
|
|
11287
|
-
getConfigFor(handle, configOverride) {
|
|
11288
|
-
const override = this.loadFromObject(configOverride || {});
|
|
11289
|
-
if (override.isRootFound()) {
|
|
11290
|
-
override.init();
|
|
11291
|
-
return override;
|
|
11292
|
-
}
|
|
11293
|
-
const merged = this.globalConfig.merge(override);
|
|
11294
|
-
merged.init();
|
|
11295
|
-
return merged;
|
|
11296
|
-
}
|
|
11297
|
-
flushCache() {
|
|
11298
|
-
/* do nothing */
|
|
11299
|
-
}
|
|
11300
|
-
defaultConfig() {
|
|
11301
|
-
return this.loadFromObject({
|
|
11302
|
-
extends: ["html-validate:recommended"],
|
|
11303
|
-
elements: ["html5"],
|
|
11304
|
-
});
|
|
11305
|
-
}
|
|
11306
|
-
}
|
|
11307
|
-
|
|
11308
11371
|
const defaults$1 = {
|
|
11309
11372
|
silent: false,
|
|
11310
11373
|
version,
|
|
@@ -11322,7 +11385,7 @@ const defaults$1 = {
|
|
|
11322
11385
|
* @returns - `true` if version is compatible
|
|
11323
11386
|
*/
|
|
11324
11387
|
function compatibilityCheck(name, declared, options) {
|
|
11325
|
-
const { silent, version: current, logger } =
|
|
11388
|
+
const { silent, version: current, logger } = { ...defaults$1, ...options };
|
|
11326
11389
|
const valid = semver.satisfies(current, declared);
|
|
11327
11390
|
if (valid || silent) {
|
|
11328
11391
|
return valid;
|
|
@@ -11352,6 +11415,137 @@ function ruleExists(ruleId) {
|
|
|
11352
11415
|
return ruleIds.has(ruleId);
|
|
11353
11416
|
}
|
|
11354
11417
|
|
|
11418
|
+
/**
|
|
11419
|
+
* @internal
|
|
11420
|
+
*/
|
|
11421
|
+
function findConfigurationFiles(directory) {
|
|
11422
|
+
return ["json", "cjs", "js"]
|
|
11423
|
+
.map((extension) => path.join(directory, `.htmlvalidate.${extension}`))
|
|
11424
|
+
.filter((filePath) => fs.existsSync(filePath));
|
|
11425
|
+
}
|
|
11426
|
+
/**
|
|
11427
|
+
* Loads configuration by traversing filesystem.
|
|
11428
|
+
*
|
|
11429
|
+
* Configuration is read from three sources and in the following order:
|
|
11430
|
+
*
|
|
11431
|
+
* 1. Global configuration passed to constructor.
|
|
11432
|
+
* 2. Configuration files found when traversing the directory structure.
|
|
11433
|
+
* 3. Override passed to this function.
|
|
11434
|
+
*
|
|
11435
|
+
* The following configuration filenames are searched:
|
|
11436
|
+
*
|
|
11437
|
+
* - `.htmlvalidate.json`
|
|
11438
|
+
* - `.htmlvalidate.js`
|
|
11439
|
+
* - `.htmlvalidate.cjs`
|
|
11440
|
+
*
|
|
11441
|
+
* Global configuration is used when no configuration file is found. The
|
|
11442
|
+
* result is always merged with override if present.
|
|
11443
|
+
*
|
|
11444
|
+
* The `root` property set to `true` affects the configuration as following:
|
|
11445
|
+
*
|
|
11446
|
+
* 1. If set in override the override is returned as-is.
|
|
11447
|
+
* 2. If set in the global config the override is merged into global and
|
|
11448
|
+
* returned. No configuration files are searched.
|
|
11449
|
+
* 3. Setting `root` in configuration file only stops directory traversal.
|
|
11450
|
+
*/
|
|
11451
|
+
class FileSystemConfigLoader extends ConfigLoader {
|
|
11452
|
+
/**
|
|
11453
|
+
* @param config - Global configuration
|
|
11454
|
+
* @param configFactory - Optional configuration factory
|
|
11455
|
+
*/
|
|
11456
|
+
constructor(config, configFactory = Config) {
|
|
11457
|
+
super(config, configFactory);
|
|
11458
|
+
this.cache = new Map();
|
|
11459
|
+
}
|
|
11460
|
+
/**
|
|
11461
|
+
* Get configuration for given filename.
|
|
11462
|
+
*
|
|
11463
|
+
* @param filename - Filename to get configuration for.
|
|
11464
|
+
* @param configOverride - Configuration to merge final result with.
|
|
11465
|
+
*/
|
|
11466
|
+
getConfigFor(filename, configOverride) {
|
|
11467
|
+
/* special case when the overridden configuration is marked as root, should
|
|
11468
|
+
* not try to load any more configuration files */
|
|
11469
|
+
const override = this.loadFromObject(configOverride || {});
|
|
11470
|
+
if (override.isRootFound()) {
|
|
11471
|
+
override.init();
|
|
11472
|
+
return override;
|
|
11473
|
+
}
|
|
11474
|
+
/* special case when the global configuration is marked as root, should not
|
|
11475
|
+
* try to load and more configuration files */
|
|
11476
|
+
if (this.globalConfig.isRootFound()) {
|
|
11477
|
+
const merged = this.globalConfig.merge(override);
|
|
11478
|
+
merged.init();
|
|
11479
|
+
return merged;
|
|
11480
|
+
}
|
|
11481
|
+
const config = this.fromFilename(filename);
|
|
11482
|
+
const merged = config ? config.merge(override) : this.globalConfig.merge(override);
|
|
11483
|
+
merged.init();
|
|
11484
|
+
return merged;
|
|
11485
|
+
}
|
|
11486
|
+
/**
|
|
11487
|
+
* Flush configuration cache.
|
|
11488
|
+
*
|
|
11489
|
+
* @param filename - If given only the cache for that file is flushed.
|
|
11490
|
+
*/
|
|
11491
|
+
flushCache(filename) {
|
|
11492
|
+
if (filename) {
|
|
11493
|
+
this.cache.delete(filename);
|
|
11494
|
+
}
|
|
11495
|
+
else {
|
|
11496
|
+
this.cache.clear();
|
|
11497
|
+
}
|
|
11498
|
+
}
|
|
11499
|
+
/**
|
|
11500
|
+
* Load raw configuration from directory traversal.
|
|
11501
|
+
*
|
|
11502
|
+
* This configuration is not merged with global configuration and may return
|
|
11503
|
+
* `null` if no configuration files are found.
|
|
11504
|
+
*/
|
|
11505
|
+
fromFilename(filename) {
|
|
11506
|
+
var _a;
|
|
11507
|
+
if (filename === "inline") {
|
|
11508
|
+
return null;
|
|
11509
|
+
}
|
|
11510
|
+
if (this.cache.has(filename)) {
|
|
11511
|
+
return (_a = this.cache.get(filename)) !== null && _a !== void 0 ? _a : null;
|
|
11512
|
+
}
|
|
11513
|
+
let found = false;
|
|
11514
|
+
let current = path.resolve(path.dirname(filename));
|
|
11515
|
+
let config = this.empty();
|
|
11516
|
+
// eslint-disable-next-line no-constant-condition
|
|
11517
|
+
while (true) {
|
|
11518
|
+
/* search configuration files in current directory */
|
|
11519
|
+
for (const configFile of findConfigurationFiles(current)) {
|
|
11520
|
+
const local = this.loadFromFile(configFile);
|
|
11521
|
+
found = true;
|
|
11522
|
+
config = local.merge(config);
|
|
11523
|
+
}
|
|
11524
|
+
/* stop if a configuration with "root" is set to true */
|
|
11525
|
+
if (config.isRootFound()) {
|
|
11526
|
+
break;
|
|
11527
|
+
}
|
|
11528
|
+
/* get the parent directory */
|
|
11529
|
+
const child = current;
|
|
11530
|
+
current = path.dirname(current);
|
|
11531
|
+
/* stop if this is the root directory */
|
|
11532
|
+
if (current === child) {
|
|
11533
|
+
break;
|
|
11534
|
+
}
|
|
11535
|
+
}
|
|
11536
|
+
/* no config was found by loader, return null and let caller decide what to do */
|
|
11537
|
+
if (!found) {
|
|
11538
|
+
this.cache.set(filename, null);
|
|
11539
|
+
return null;
|
|
11540
|
+
}
|
|
11541
|
+
this.cache.set(filename, config);
|
|
11542
|
+
return config;
|
|
11543
|
+
}
|
|
11544
|
+
defaultConfig() {
|
|
11545
|
+
return this.configFactory.defaultConfig();
|
|
11546
|
+
}
|
|
11547
|
+
}
|
|
11548
|
+
|
|
11355
11549
|
const entities = {
|
|
11356
11550
|
">": ">",
|
|
11357
11551
|
"<": "<",
|
|
@@ -11502,7 +11696,7 @@ function formatSummary(errors, warnings) {
|
|
|
11502
11696
|
return kleur[summaryColor]().bold(`${summary.join(" and ")} found.`);
|
|
11503
11697
|
}
|
|
11504
11698
|
function codeframe(results, options) {
|
|
11505
|
-
const merged =
|
|
11699
|
+
const merged = { ...defaults, ...options };
|
|
11506
11700
|
let errors = 0;
|
|
11507
11701
|
let warnings = 0;
|
|
11508
11702
|
const resultsWithMessages = results.filter((result) => result.messages.length > 0);
|
|
@@ -11543,7 +11737,11 @@ function linkSummary(results) {
|
|
|
11543
11737
|
return `\n${kleur.bold("More information")}:\n${lines.join("")}\n`;
|
|
11544
11738
|
}
|
|
11545
11739
|
function stylish(results) {
|
|
11546
|
-
const errors = stylishImpl(results.map((it) => (
|
|
11740
|
+
const errors = stylishImpl(results.map((it) => ({
|
|
11741
|
+
...it,
|
|
11742
|
+
fixableErrorCount: 0,
|
|
11743
|
+
fixableWarningCount: 0,
|
|
11744
|
+
})));
|
|
11547
11745
|
const links = linkSummary(results);
|
|
11548
11746
|
return `${errors}${links}`;
|
|
11549
11747
|
}
|