html-validate 5.3.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 +71 -0
- package/dist/cjs/browser.js +2 -6
- package/dist/cjs/browser.js.map +1 -1
- package/dist/cjs/cli.js +53 -28
- package/dist/cjs/cli.js.map +1 -1
- package/dist/cjs/core.d.ts +21 -7
- package/dist/cjs/core.js +618 -351
- 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 +15 -2
- package/dist/cjs/index.js +2 -6
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/{matchers.d.ts → jest-lib.d.ts} +2 -4
- package/dist/cjs/{matchers.js → jest-lib.js} +125 -87
- package/dist/cjs/jest-lib.js.map +1 -0
- package/dist/cjs/jest.d.ts +4 -0
- package/dist/cjs/jest.js +17 -0
- package/dist/cjs/jest.js.map +1 -0
- package/dist/cjs/test-utils.js +1 -1
- package/dist/es/cli.js +34 -9
- package/dist/es/cli.js.map +1 -1
- package/dist/es/core.d.ts +21 -7
- package/dist/es/core.js +582 -315
- package/dist/es/core.js.map +1 -1
- package/dist/es/html-validate.js +1 -1
- package/dist/es/html-validate.js.map +1 -1
- package/dist/es/index.d.ts +15 -2
- package/dist/es/{matchers.d.ts → jest-lib.d.ts} +2 -4
- package/dist/es/{matchers.js → jest-lib.js} +120 -82
- package/dist/es/jest-lib.js.map +1 -0
- package/dist/es/jest.d.ts +4 -0
- package/dist/es/jest.js +16 -0
- package/dist/es/jest.js.map +1 -0
- package/dist/schema/elements.json +43 -5
- package/elements/html5.json +940 -290
- package/jest.d.ts +1 -1
- package/jest.js +1 -1
- package/package.json +30 -28
- package/dist/cjs/matchers.js.map +0 -1
- package/dist/es/matchers.js.map +0 -1
package/dist/cjs/core.js
CHANGED
|
@@ -268,7 +268,7 @@ class UserError extends NestedError {
|
|
|
268
268
|
}
|
|
269
269
|
|
|
270
270
|
function getSummary(schema, obj, errors) {
|
|
271
|
-
const output = betterAjvErrors__default[
|
|
271
|
+
const output = betterAjvErrors__default["default"](schema, obj, errors, {
|
|
272
272
|
format: "js",
|
|
273
273
|
});
|
|
274
274
|
// istanbul ignore next: for safety only
|
|
@@ -285,15 +285,15 @@ class SchemaValidationError extends UserError {
|
|
|
285
285
|
}
|
|
286
286
|
prettyError() {
|
|
287
287
|
const json = this.getRawJSON();
|
|
288
|
-
return betterAjvErrors__default[
|
|
288
|
+
return betterAjvErrors__default["default"](this.schema, this.obj, this.errors, {
|
|
289
289
|
format: "cli",
|
|
290
290
|
indent: 2,
|
|
291
291
|
json,
|
|
292
292
|
});
|
|
293
293
|
}
|
|
294
294
|
getRawJSON() {
|
|
295
|
-
if (this.filename && fs__default[
|
|
296
|
-
return fs__default[
|
|
295
|
+
if (this.filename && fs__default["default"].existsSync(this.filename)) {
|
|
296
|
+
return fs__default["default"].readFileSync(this.filename, "utf-8");
|
|
297
297
|
}
|
|
298
298
|
else {
|
|
299
299
|
return null;
|
|
@@ -301,9 +301,9 @@ class SchemaValidationError extends UserError {
|
|
|
301
301
|
}
|
|
302
302
|
}
|
|
303
303
|
|
|
304
|
-
const projectRoot = path__default[
|
|
304
|
+
const projectRoot = path__default["default"].resolve(__dirname, "../../");
|
|
305
305
|
const legacyRequire = require;
|
|
306
|
-
const distFolder = path__default[
|
|
306
|
+
const distFolder = path__default["default"].resolve(projectRoot, "dist/cjs");
|
|
307
307
|
|
|
308
308
|
/**
|
|
309
309
|
* Similar to `require(..)` but removes the cached copy first.
|
|
@@ -586,18 +586,63 @@ const definitions = {
|
|
|
586
586
|
type: "object",
|
|
587
587
|
patternProperties: {
|
|
588
588
|
"^.*$": {
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
{
|
|
589
|
+
anyOf: [
|
|
590
|
+
{
|
|
591
|
+
type: "object",
|
|
592
|
+
additionalProperties: false,
|
|
593
|
+
properties: {
|
|
594
|
+
boolean: {
|
|
595
|
+
type: "boolean",
|
|
596
|
+
title: "Set to true if this is a boolean attribute"
|
|
597
|
+
},
|
|
598
|
+
deprecated: {
|
|
599
|
+
title: "Set to true or string if this attribute is deprecated",
|
|
600
|
+
oneOf: [
|
|
601
|
+
{
|
|
602
|
+
type: "boolean"
|
|
603
|
+
},
|
|
604
|
+
{
|
|
605
|
+
type: "string"
|
|
606
|
+
}
|
|
607
|
+
]
|
|
608
|
+
},
|
|
609
|
+
list: {
|
|
610
|
+
type: "boolean",
|
|
611
|
+
title: "Set to true if this attribute is a list of space-separated tokens, each which must be valid by itself"
|
|
612
|
+
},
|
|
613
|
+
"enum": {
|
|
614
|
+
type: "array",
|
|
615
|
+
title: "Exhaustive list of values (string or regex) this attribute accepts",
|
|
616
|
+
uniqueItems: true,
|
|
617
|
+
items: {
|
|
618
|
+
anyOf: [
|
|
619
|
+
{
|
|
620
|
+
type: "string"
|
|
621
|
+
},
|
|
622
|
+
{
|
|
623
|
+
regexp: true
|
|
624
|
+
}
|
|
625
|
+
]
|
|
626
|
+
}
|
|
627
|
+
},
|
|
628
|
+
omit: {
|
|
629
|
+
type: "boolean",
|
|
630
|
+
title: "Set to true if this attribute can optionally omit its value"
|
|
631
|
+
},
|
|
632
|
+
required: {
|
|
633
|
+
type: "boolean",
|
|
634
|
+
title: "Set to true if this attribute is required"
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
},
|
|
638
|
+
{
|
|
639
|
+
type: "array",
|
|
640
|
+
uniqueItems: true,
|
|
641
|
+
items: {
|
|
594
642
|
type: "string"
|
|
595
|
-
},
|
|
596
|
-
{
|
|
597
|
-
regexp: true
|
|
598
643
|
}
|
|
599
|
-
|
|
600
|
-
|
|
644
|
+
}
|
|
645
|
+
]
|
|
601
646
|
}
|
|
602
647
|
}
|
|
603
648
|
},
|
|
@@ -648,6 +693,111 @@ var schema = {
|
|
|
648
693
|
definitions: definitions
|
|
649
694
|
};
|
|
650
695
|
|
|
696
|
+
var TextContent$1;
|
|
697
|
+
(function (TextContent) {
|
|
698
|
+
/* forbid node to have text content, inter-element whitespace is ignored */
|
|
699
|
+
TextContent["NONE"] = "none";
|
|
700
|
+
/* node can have text but not required too */
|
|
701
|
+
TextContent["DEFAULT"] = "default";
|
|
702
|
+
/* node requires text-nodes to be present (direct or by descendant) */
|
|
703
|
+
TextContent["REQUIRED"] = "required";
|
|
704
|
+
/* node requires accessible text (hidden text is ignored, tries to get text from accessibility tree) */
|
|
705
|
+
TextContent["ACCESSIBLE"] = "accessible";
|
|
706
|
+
})(TextContent$1 || (TextContent$1 = {}));
|
|
707
|
+
/**
|
|
708
|
+
* Properties listed here can be copied (loaded) onto another element using
|
|
709
|
+
* [[HtmlElement.loadMeta]].
|
|
710
|
+
*/
|
|
711
|
+
const MetaCopyableProperty = [
|
|
712
|
+
"metadata",
|
|
713
|
+
"flow",
|
|
714
|
+
"sectioning",
|
|
715
|
+
"heading",
|
|
716
|
+
"phrasing",
|
|
717
|
+
"embedded",
|
|
718
|
+
"interactive",
|
|
719
|
+
"transparent",
|
|
720
|
+
"form",
|
|
721
|
+
"labelable",
|
|
722
|
+
"attributes",
|
|
723
|
+
"permittedContent",
|
|
724
|
+
"permittedDescendants",
|
|
725
|
+
"permittedOrder",
|
|
726
|
+
"requiredAncestors",
|
|
727
|
+
"requiredContent",
|
|
728
|
+
];
|
|
729
|
+
/**
|
|
730
|
+
* @internal
|
|
731
|
+
*/
|
|
732
|
+
function setMetaProperty(dst, key, value) {
|
|
733
|
+
dst[key] = value;
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
function isSet(value) {
|
|
737
|
+
return typeof value !== "undefined";
|
|
738
|
+
}
|
|
739
|
+
function flag(value) {
|
|
740
|
+
return value ? true : undefined;
|
|
741
|
+
}
|
|
742
|
+
function stripUndefined(src) {
|
|
743
|
+
const entries = Object.entries(src).filter(([, value]) => isSet(value));
|
|
744
|
+
return Object.fromEntries(entries);
|
|
745
|
+
}
|
|
746
|
+
function migrateSingleAttribute(src, key) {
|
|
747
|
+
var _a, _b;
|
|
748
|
+
const result = {};
|
|
749
|
+
result.deprecated = flag((_a = src.deprecatedAttributes) === null || _a === void 0 ? void 0 : _a.includes(key));
|
|
750
|
+
result.required = flag((_b = src.requiredAttributes) === null || _b === void 0 ? void 0 : _b.includes(key));
|
|
751
|
+
result.omit = undefined;
|
|
752
|
+
const attr = src.attributes ? src.attributes[key] : undefined;
|
|
753
|
+
if (typeof attr === "undefined") {
|
|
754
|
+
return stripUndefined(result);
|
|
755
|
+
}
|
|
756
|
+
/* when the attribute is set to null we use a special property "delete" to
|
|
757
|
+
* flag it, if it is still set during merge (inheritance, overwriting, etc) the attribute will be removed */
|
|
758
|
+
if (attr === null) {
|
|
759
|
+
result.delete = true;
|
|
760
|
+
return stripUndefined(result);
|
|
761
|
+
}
|
|
762
|
+
if (Array.isArray(attr)) {
|
|
763
|
+
if (attr.length === 0) {
|
|
764
|
+
result.boolean = true;
|
|
765
|
+
}
|
|
766
|
+
else {
|
|
767
|
+
result.enum = attr.filter((it) => it !== "");
|
|
768
|
+
if (attr.includes("")) {
|
|
769
|
+
result.omit = true;
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
return stripUndefined(result);
|
|
773
|
+
}
|
|
774
|
+
else {
|
|
775
|
+
return stripUndefined({ ...result, ...attr });
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
function migrateAttributes(src) {
|
|
779
|
+
var _a, _b, _c;
|
|
780
|
+
const keys = [
|
|
781
|
+
...Object.keys((_a = src.attributes) !== null && _a !== void 0 ? _a : {}),
|
|
782
|
+
...((_b = src.requiredAttributes) !== null && _b !== void 0 ? _b : []),
|
|
783
|
+
...((_c = src.deprecatedAttributes) !== null && _c !== void 0 ? _c : []),
|
|
784
|
+
].sort();
|
|
785
|
+
const entries = keys.map((key) => {
|
|
786
|
+
return [key, migrateSingleAttribute(src, key)];
|
|
787
|
+
});
|
|
788
|
+
return Object.fromEntries(entries);
|
|
789
|
+
}
|
|
790
|
+
function migrateElement(src) {
|
|
791
|
+
const result = {
|
|
792
|
+
...src,
|
|
793
|
+
attributes: migrateAttributes(src),
|
|
794
|
+
};
|
|
795
|
+
/* removed properties */
|
|
796
|
+
delete result.deprecatedAttributes;
|
|
797
|
+
delete result.requiredAttributes;
|
|
798
|
+
return result;
|
|
799
|
+
}
|
|
800
|
+
|
|
651
801
|
const dynamicKeys = [
|
|
652
802
|
"metadata",
|
|
653
803
|
"flow",
|
|
@@ -710,7 +860,7 @@ class MetaTable {
|
|
|
710
860
|
*/
|
|
711
861
|
extendValidationSchema(patch) {
|
|
712
862
|
if (patch.properties) {
|
|
713
|
-
this.schema = jsonMergePatch__default[
|
|
863
|
+
this.schema = jsonMergePatch__default["default"].apply(this.schema, {
|
|
714
864
|
patternProperties: {
|
|
715
865
|
"^[^$].*$": {
|
|
716
866
|
properties: patch.properties,
|
|
@@ -719,7 +869,7 @@ class MetaTable {
|
|
|
719
869
|
});
|
|
720
870
|
}
|
|
721
871
|
if (patch.definitions) {
|
|
722
|
-
this.schema = jsonMergePatch__default[
|
|
872
|
+
this.schema = jsonMergePatch__default["default"].apply(this.schema, {
|
|
723
873
|
definitions: patch.definitions,
|
|
724
874
|
});
|
|
725
875
|
}
|
|
@@ -741,7 +891,7 @@ class MetaTable {
|
|
|
741
891
|
for (const [key, value] of Object.entries(obj)) {
|
|
742
892
|
if (key === "$schema")
|
|
743
893
|
continue;
|
|
744
|
-
this.addEntry(key, value);
|
|
894
|
+
this.addEntry(key, migrateElement(value));
|
|
745
895
|
}
|
|
746
896
|
}
|
|
747
897
|
/**
|
|
@@ -769,7 +919,7 @@ class MetaTable {
|
|
|
769
919
|
*/
|
|
770
920
|
getMetaFor(tagName) {
|
|
771
921
|
tagName = tagName.toLowerCase();
|
|
772
|
-
return this.elements[tagName] ?
|
|
922
|
+
return this.elements[tagName] ? { ...this.elements[tagName] } : null;
|
|
773
923
|
}
|
|
774
924
|
/**
|
|
775
925
|
* Find all tags which has enabled given property.
|
|
@@ -798,7 +948,7 @@ class MetaTable {
|
|
|
798
948
|
}
|
|
799
949
|
}
|
|
800
950
|
/* merge all sources together */
|
|
801
|
-
const expanded =
|
|
951
|
+
const expanded = this.mergeElement(parent, { ...entry, tagName });
|
|
802
952
|
expandRegex(expanded);
|
|
803
953
|
this.elements[tagName] = expanded;
|
|
804
954
|
}
|
|
@@ -806,7 +956,7 @@ class MetaTable {
|
|
|
806
956
|
* Construct a new AJV schema validator.
|
|
807
957
|
*/
|
|
808
958
|
getSchemaValidator() {
|
|
809
|
-
const ajv = new Ajv__default[
|
|
959
|
+
const ajv = new Ajv__default["default"]({ strict: true, strictTuples: true, strictTypes: true });
|
|
810
960
|
ajv.addMetaSchema(ajvSchemaDraft);
|
|
811
961
|
ajv.addKeyword(ajvRegexpKeyword);
|
|
812
962
|
ajv.addKeyword({ keyword: "copyable" });
|
|
@@ -832,11 +982,20 @@ class MetaTable {
|
|
|
832
982
|
delete global.void;
|
|
833
983
|
/* merge elements */
|
|
834
984
|
for (const [tagName, entry] of Object.entries(this.elements)) {
|
|
835
|
-
this.elements[tagName] = this.mergeElement(
|
|
985
|
+
this.elements[tagName] = this.mergeElement(global, entry);
|
|
836
986
|
}
|
|
837
987
|
}
|
|
838
988
|
mergeElement(a, b) {
|
|
839
|
-
|
|
989
|
+
const merged = deepmerge__default["default"](a, b, { arrayMerge: overwriteMerge$1 });
|
|
990
|
+
/* special handling when removing attributes by setting them to null
|
|
991
|
+
* resulting in the deletion flag being set */
|
|
992
|
+
const filteredAttrs = Object.entries(merged.attributes).filter(([, attr]) => {
|
|
993
|
+
const val = !attr.delete;
|
|
994
|
+
delete attr.delete;
|
|
995
|
+
return val;
|
|
996
|
+
});
|
|
997
|
+
merged.attributes = Object.fromEntries(filteredAttrs);
|
|
998
|
+
return merged;
|
|
840
999
|
}
|
|
841
1000
|
resolve(node) {
|
|
842
1001
|
if (node.meta) {
|
|
@@ -848,7 +1007,7 @@ function expandProperties(node, entry) {
|
|
|
848
1007
|
for (const key of dynamicKeys) {
|
|
849
1008
|
const property = entry[key];
|
|
850
1009
|
if (property && typeof property !== "boolean") {
|
|
851
|
-
entry
|
|
1010
|
+
setMetaProperty(entry, key, evaluateProperty(node, property));
|
|
852
1011
|
}
|
|
853
1012
|
}
|
|
854
1013
|
}
|
|
@@ -874,14 +1033,9 @@ function expandRegexValue(value) {
|
|
|
874
1033
|
* Expand all regular expressions in strings ("/../"). This mutates the object.
|
|
875
1034
|
*/
|
|
876
1035
|
function expandRegex(entry) {
|
|
877
|
-
if (!entry.attributes)
|
|
878
|
-
return;
|
|
879
1036
|
for (const [name, values] of Object.entries(entry.attributes)) {
|
|
880
|
-
if (values) {
|
|
881
|
-
entry.attributes[name] = values.map(expandRegexValue);
|
|
882
|
-
}
|
|
883
|
-
else {
|
|
884
|
-
delete entry.attributes[name];
|
|
1037
|
+
if (values.enum) {
|
|
1038
|
+
entry.attributes[name].enum = values.enum.map(expandRegexValue);
|
|
885
1039
|
}
|
|
886
1040
|
}
|
|
887
1041
|
}
|
|
@@ -937,41 +1091,6 @@ function matchAttribute(node, match) {
|
|
|
937
1091
|
}
|
|
938
1092
|
}
|
|
939
1093
|
|
|
940
|
-
var TextContent$1;
|
|
941
|
-
(function (TextContent) {
|
|
942
|
-
/* forbid node to have text content, inter-element whitespace is ignored */
|
|
943
|
-
TextContent["NONE"] = "none";
|
|
944
|
-
/* node can have text but not required too */
|
|
945
|
-
TextContent["DEFAULT"] = "default";
|
|
946
|
-
/* node requires text-nodes to be present (direct or by descendant) */
|
|
947
|
-
TextContent["REQUIRED"] = "required";
|
|
948
|
-
/* node requires accessible text (hidden text is ignored, tries to get text from accessibility tree) */
|
|
949
|
-
TextContent["ACCESSIBLE"] = "accessible";
|
|
950
|
-
})(TextContent$1 || (TextContent$1 = {}));
|
|
951
|
-
/**
|
|
952
|
-
* Properties listed here can be copied (loaded) onto another element using
|
|
953
|
-
* [[HtmlElement.loadMeta]].
|
|
954
|
-
*/
|
|
955
|
-
const MetaCopyableProperty = [
|
|
956
|
-
"metadata",
|
|
957
|
-
"flow",
|
|
958
|
-
"sectioning",
|
|
959
|
-
"heading",
|
|
960
|
-
"phrasing",
|
|
961
|
-
"embedded",
|
|
962
|
-
"interactive",
|
|
963
|
-
"transparent",
|
|
964
|
-
"form",
|
|
965
|
-
"labelable",
|
|
966
|
-
"requiredAttributes",
|
|
967
|
-
"attributes",
|
|
968
|
-
"permittedContent",
|
|
969
|
-
"permittedDescendants",
|
|
970
|
-
"permittedOrder",
|
|
971
|
-
"requiredAncestors",
|
|
972
|
-
"requiredContent",
|
|
973
|
-
];
|
|
974
|
-
|
|
975
1094
|
class DynamicValue {
|
|
976
1095
|
constructor(expr) {
|
|
977
1096
|
this.expr = expr;
|
|
@@ -1342,6 +1461,15 @@ class DOMTokenList extends Array {
|
|
|
1342
1461
|
contains(token) {
|
|
1343
1462
|
return this.includes(token);
|
|
1344
1463
|
}
|
|
1464
|
+
*iterator() {
|
|
1465
|
+
for (let index = 0; index < this.length; index++) {
|
|
1466
|
+
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
|
1467
|
+
const item = this.item(index);
|
|
1468
|
+
const location = this.location(index);
|
|
1469
|
+
/* eslint-enable @typescript-eslint/no-non-null-assertion */
|
|
1470
|
+
yield { index, item, location };
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1345
1473
|
}
|
|
1346
1474
|
|
|
1347
1475
|
var Combinator;
|
|
@@ -1814,8 +1942,9 @@ class HtmlElement extends DOMNode {
|
|
|
1814
1942
|
this.metaElement = {};
|
|
1815
1943
|
}
|
|
1816
1944
|
for (const key of MetaCopyableProperty) {
|
|
1817
|
-
|
|
1818
|
-
|
|
1945
|
+
const value = meta[key];
|
|
1946
|
+
if (typeof value !== "undefined") {
|
|
1947
|
+
setMetaProperty(this.metaElement, key, value);
|
|
1819
1948
|
}
|
|
1820
1949
|
else {
|
|
1821
1950
|
delete this.metaElement[key];
|
|
@@ -2231,6 +2360,7 @@ class Validator {
|
|
|
2231
2360
|
* @param rules - Element attribute metadta.
|
|
2232
2361
|
* @returns `true` if attribute passes all tests.
|
|
2233
2362
|
*/
|
|
2363
|
+
/* eslint-disable-next-line complexity */
|
|
2234
2364
|
static validateAttribute(attr, rules) {
|
|
2235
2365
|
const rule = rules[attr.key];
|
|
2236
2366
|
if (!rule) {
|
|
@@ -2244,19 +2374,34 @@ class Validator {
|
|
|
2244
2374
|
return true;
|
|
2245
2375
|
}
|
|
2246
2376
|
const empty = value === null || value === "";
|
|
2247
|
-
/*
|
|
2248
|
-
|
|
2377
|
+
/* if boolean is set the value can be either null, empty string or the
|
|
2378
|
+
* attribute key (attribute-boolean-style regulates style) */
|
|
2379
|
+
if (rule.boolean) {
|
|
2249
2380
|
return empty || value === attr.key;
|
|
2250
2381
|
}
|
|
2251
|
-
/* if the
|
|
2252
|
-
* (
|
|
2253
|
-
if (rule.
|
|
2382
|
+
/* if omit is set the value can be either null or empty string
|
|
2383
|
+
* (attribute-empty style regulates style) */
|
|
2384
|
+
if (rule.omit && empty) {
|
|
2385
|
+
return true;
|
|
2386
|
+
}
|
|
2387
|
+
/* validate each token when using list, all tokens must be valid */
|
|
2388
|
+
if (rule.list) {
|
|
2389
|
+
const tokens = new DOMTokenList(value, attr.valueLocation);
|
|
2390
|
+
return tokens.every((token) => {
|
|
2391
|
+
return this.validateAttributeValue(token, rule);
|
|
2392
|
+
});
|
|
2393
|
+
}
|
|
2394
|
+
return this.validateAttributeValue(value, rule);
|
|
2395
|
+
}
|
|
2396
|
+
static validateAttributeValue(value, rule) {
|
|
2397
|
+
/* skip attribute if it not have enumerated list */
|
|
2398
|
+
if (!rule.enum) {
|
|
2254
2399
|
return true;
|
|
2255
2400
|
}
|
|
2256
2401
|
if (value === null || value === undefined) {
|
|
2257
2402
|
return false;
|
|
2258
2403
|
}
|
|
2259
|
-
return rule.some((entry) => {
|
|
2404
|
+
return rule.enum.some((entry) => {
|
|
2260
2405
|
if (entry instanceof RegExp) {
|
|
2261
2406
|
return !!value.match(entry);
|
|
2262
2407
|
}
|
|
@@ -2522,12 +2667,12 @@ var configurationSchema = {
|
|
|
2522
2667
|
const espree = legacyRequire("espree");
|
|
2523
2668
|
const walk = legacyRequire("acorn-walk");
|
|
2524
2669
|
function joinTemplateLiteral(nodes) {
|
|
2525
|
-
let offset = nodes[0].start;
|
|
2670
|
+
let offset = nodes[0].start + 1;
|
|
2526
2671
|
let output = "";
|
|
2527
2672
|
for (const node of nodes) {
|
|
2528
|
-
output += " ".repeat(node.start - offset);
|
|
2673
|
+
output += " ".repeat(node.start + 1 - offset);
|
|
2529
2674
|
output += node.value.raw;
|
|
2530
|
-
offset = node.end;
|
|
2675
|
+
offset = node.end - 2;
|
|
2531
2676
|
}
|
|
2532
2677
|
return output;
|
|
2533
2678
|
}
|
|
@@ -2632,7 +2777,7 @@ class TemplateExtractor {
|
|
|
2632
2777
|
this.data = data;
|
|
2633
2778
|
}
|
|
2634
2779
|
static fromFilename(filename) {
|
|
2635
|
-
const source = fs__default[
|
|
2780
|
+
const source = fs__default["default"].readFileSync(filename, "utf-8");
|
|
2636
2781
|
const ast = espree.parse(source, {
|
|
2637
2782
|
ecmaVersion: 2017,
|
|
2638
2783
|
sourceType: "module",
|
|
@@ -2668,7 +2813,7 @@ class TemplateExtractor {
|
|
|
2668
2813
|
* functions.
|
|
2669
2814
|
*/
|
|
2670
2815
|
static createSource(filename) {
|
|
2671
|
-
const data = fs__default[
|
|
2816
|
+
const data = fs__default["default"].readFileSync(filename, "utf-8");
|
|
2672
2817
|
return [
|
|
2673
2818
|
{
|
|
2674
2819
|
column: 1,
|
|
@@ -2719,7 +2864,7 @@ var TRANSFORMER_API;
|
|
|
2719
2864
|
})(TRANSFORMER_API || (TRANSFORMER_API = {}));
|
|
2720
2865
|
|
|
2721
2866
|
const name = "html-validate";
|
|
2722
|
-
const version = "
|
|
2867
|
+
const version = "6.0.0";
|
|
2723
2868
|
const homepage = "https://html-validate.org";
|
|
2724
2869
|
const bugs = {
|
|
2725
2870
|
url: "https://gitlab.com/html-validate/html-validate/issues/new"
|
|
@@ -2756,7 +2901,7 @@ const remapEvents = {
|
|
|
2756
2901
|
"tag:open": "tag:start",
|
|
2757
2902
|
"tag:close": "tag:end",
|
|
2758
2903
|
};
|
|
2759
|
-
const ajv$1 = new Ajv__default[
|
|
2904
|
+
const ajv$1 = new Ajv__default["default"]({ strict: true, strictTuples: true, strictTypes: true });
|
|
2760
2905
|
ajv$1.addMetaSchema(ajvSchemaDraft);
|
|
2761
2906
|
/**
|
|
2762
2907
|
* Get (cached) schema validator for rule options.
|
|
@@ -2984,9 +3129,9 @@ function ruleDocumentationUrl(filename) {
|
|
|
2984
3129
|
* folder and with the @/ prefix, by replacing the @ with the dist folder we
|
|
2985
3130
|
* can resolve the path properly */
|
|
2986
3131
|
filename = filename.replace("@", distFolder);
|
|
2987
|
-
const p = path__default[
|
|
2988
|
-
const root = path__default[
|
|
2989
|
-
const rel = path__default[
|
|
3132
|
+
const p = path__default["default"].parse(filename);
|
|
3133
|
+
const root = path__default["default"].join(distFolder, "rules");
|
|
3134
|
+
const rel = path__default["default"].relative(root, path__default["default"].join(p.dir, p.name));
|
|
2990
3135
|
return `${homepage}/rules/${rel}.html`;
|
|
2991
3136
|
}
|
|
2992
3137
|
|
|
@@ -3011,7 +3156,7 @@ const description = {
|
|
|
3011
3156
|
};
|
|
3012
3157
|
class AllowedLinks extends Rule {
|
|
3013
3158
|
constructor(options) {
|
|
3014
|
-
super(
|
|
3159
|
+
super({ ...defaults$p, ...options });
|
|
3015
3160
|
}
|
|
3016
3161
|
static schema() {
|
|
3017
3162
|
return {
|
|
@@ -3259,7 +3404,7 @@ const defaults$o = {
|
|
|
3259
3404
|
};
|
|
3260
3405
|
class AttrCase extends Rule {
|
|
3261
3406
|
constructor(options) {
|
|
3262
|
-
super(
|
|
3407
|
+
super({ ...defaults$o, ...options });
|
|
3263
3408
|
this.style = new CaseStyle(this.options.style, "attr-case");
|
|
3264
3409
|
}
|
|
3265
3410
|
static schema() {
|
|
@@ -3612,7 +3757,7 @@ function generateDescription(name, pattern) {
|
|
|
3612
3757
|
}
|
|
3613
3758
|
class AttrPattern extends Rule {
|
|
3614
3759
|
constructor(options) {
|
|
3615
|
-
super(
|
|
3760
|
+
super({ ...defaults$n, ...options });
|
|
3616
3761
|
this.pattern = generateRegexp(this.options.pattern);
|
|
3617
3762
|
}
|
|
3618
3763
|
static schema() {
|
|
@@ -3678,7 +3823,7 @@ const defaults$m = {
|
|
|
3678
3823
|
};
|
|
3679
3824
|
class AttrQuotes extends Rule {
|
|
3680
3825
|
constructor(options) {
|
|
3681
|
-
super(
|
|
3826
|
+
super({ ...defaults$m, ...options });
|
|
3682
3827
|
this.style = parseStyle$4(this.options.style);
|
|
3683
3828
|
}
|
|
3684
3829
|
static schema() {
|
|
@@ -3774,11 +3919,11 @@ class AttributeAllowedValues extends Rule {
|
|
|
3774
3919
|
if (!context) {
|
|
3775
3920
|
return docs;
|
|
3776
3921
|
}
|
|
3777
|
-
if (context.allowed.
|
|
3778
|
-
const allowed = context.allowed.map((val) => `- \`${val}\``);
|
|
3922
|
+
if (context.allowed.enum) {
|
|
3923
|
+
const allowed = context.allowed.enum.map((val) => `- \`${val}\``);
|
|
3779
3924
|
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")}`;
|
|
3780
3925
|
}
|
|
3781
|
-
else {
|
|
3926
|
+
else if (context.allowed.boolean) {
|
|
3782
3927
|
docs.description = `Element <${context.element}> attribute \`${context.attribute}\` must be a boolean attribute, e.g. \`<${context.element} ${context.attribute}>\``;
|
|
3783
3928
|
}
|
|
3784
3929
|
return docs;
|
|
@@ -3834,7 +3979,7 @@ const defaults$l = {
|
|
|
3834
3979
|
};
|
|
3835
3980
|
class AttributeBooleanStyle extends Rule {
|
|
3836
3981
|
constructor(options) {
|
|
3837
|
-
super(
|
|
3982
|
+
super({ ...defaults$l, ...options });
|
|
3838
3983
|
this.hasInvalidStyle = parseStyle$3(this.options.style);
|
|
3839
3984
|
}
|
|
3840
3985
|
static schema() {
|
|
@@ -3879,7 +4024,8 @@ class AttributeBooleanStyle extends Rule {
|
|
|
3879
4024
|
});
|
|
3880
4025
|
}
|
|
3881
4026
|
isBoolean(attr, rules) {
|
|
3882
|
-
|
|
4027
|
+
var _a;
|
|
4028
|
+
return Boolean((_a = rules[attr.key]) === null || _a === void 0 ? void 0 : _a.boolean);
|
|
3883
4029
|
}
|
|
3884
4030
|
}
|
|
3885
4031
|
function parseStyle$3(style) {
|
|
@@ -3914,7 +4060,7 @@ const defaults$k = {
|
|
|
3914
4060
|
};
|
|
3915
4061
|
class AttributeEmptyStyle extends Rule {
|
|
3916
4062
|
constructor(options) {
|
|
3917
|
-
super(
|
|
4063
|
+
super({ ...defaults$k, ...options });
|
|
3918
4064
|
this.hasInvalidStyle = parseStyle$2(this.options.style);
|
|
3919
4065
|
}
|
|
3920
4066
|
static schema() {
|
|
@@ -3963,7 +4109,8 @@ class AttributeEmptyStyle extends Rule {
|
|
|
3963
4109
|
}
|
|
3964
4110
|
}
|
|
3965
4111
|
function allowsEmpty(attr, rules) {
|
|
3966
|
-
|
|
4112
|
+
var _a;
|
|
4113
|
+
return Boolean((_a = rules[attr.key]) === null || _a === void 0 ? void 0 : _a.omit);
|
|
3967
4114
|
}
|
|
3968
4115
|
function isEmptyValue(attr) {
|
|
3969
4116
|
/* dynamic values are ignored, assumed to contain a value */
|
|
@@ -4026,7 +4173,7 @@ const defaults$j = {
|
|
|
4026
4173
|
};
|
|
4027
4174
|
class ClassPattern extends Rule {
|
|
4028
4175
|
constructor(options) {
|
|
4029
|
-
super(
|
|
4176
|
+
super({ ...defaults$j, ...options });
|
|
4030
4177
|
this.pattern = parsePattern(this.options.pattern);
|
|
4031
4178
|
}
|
|
4032
4179
|
static schema() {
|
|
@@ -4139,7 +4286,7 @@ const defaults$i = {
|
|
|
4139
4286
|
};
|
|
4140
4287
|
class Deprecated extends Rule {
|
|
4141
4288
|
constructor(options) {
|
|
4142
|
-
super(
|
|
4289
|
+
super({ ...defaults$i, ...options });
|
|
4143
4290
|
}
|
|
4144
4291
|
static schema() {
|
|
4145
4292
|
return {
|
|
@@ -4233,7 +4380,7 @@ class Deprecated extends Rule {
|
|
|
4233
4380
|
this.report(node, message, location, context);
|
|
4234
4381
|
}
|
|
4235
4382
|
reportObject(deprecated, node, location) {
|
|
4236
|
-
const context =
|
|
4383
|
+
const context = { ...deprecated, tagName: node.tagName };
|
|
4237
4384
|
const notice = deprecated.message ? `: ${deprecated.message}` : "";
|
|
4238
4385
|
const message = `<${node.tagName}> is deprecated${notice}`;
|
|
4239
4386
|
this.report(node, message, location, context);
|
|
@@ -4307,7 +4454,7 @@ const defaults$h = {
|
|
|
4307
4454
|
};
|
|
4308
4455
|
class DoctypeStyle extends Rule {
|
|
4309
4456
|
constructor(options) {
|
|
4310
|
-
super(
|
|
4457
|
+
super({ ...defaults$h, ...options });
|
|
4311
4458
|
}
|
|
4312
4459
|
static schema() {
|
|
4313
4460
|
return {
|
|
@@ -4344,7 +4491,7 @@ const defaults$g = {
|
|
|
4344
4491
|
};
|
|
4345
4492
|
class ElementCase extends Rule {
|
|
4346
4493
|
constructor(options) {
|
|
4347
|
-
super(
|
|
4494
|
+
super({ ...defaults$g, ...options });
|
|
4348
4495
|
this.style = new CaseStyle(this.options.style, "element-case");
|
|
4349
4496
|
}
|
|
4350
4497
|
static schema() {
|
|
@@ -4414,7 +4561,7 @@ const defaults$f = {
|
|
|
4414
4561
|
};
|
|
4415
4562
|
class ElementName extends Rule {
|
|
4416
4563
|
constructor(options) {
|
|
4417
|
-
super(
|
|
4564
|
+
super({ ...defaults$f, ...options });
|
|
4418
4565
|
// eslint-disable-next-line security/detect-non-literal-regexp
|
|
4419
4566
|
this.pattern = new RegExp(this.options.pattern);
|
|
4420
4567
|
}
|
|
@@ -4686,9 +4833,14 @@ class ElementRequiredAttributes extends Rule {
|
|
|
4686
4833
|
this.on("tag:end", (event) => {
|
|
4687
4834
|
const node = event.previous;
|
|
4688
4835
|
const meta = node.meta;
|
|
4689
|
-
|
|
4836
|
+
/* handle missing metadata and missing attributes */
|
|
4837
|
+
if (!meta || !meta.attributes) {
|
|
4690
4838
|
return;
|
|
4691
|
-
|
|
4839
|
+
}
|
|
4840
|
+
for (const [key, attr] of Object.entries(meta.attributes)) {
|
|
4841
|
+
if (!attr.required) {
|
|
4842
|
+
continue;
|
|
4843
|
+
}
|
|
4692
4844
|
if (node.hasAttribute(key))
|
|
4693
4845
|
continue;
|
|
4694
4846
|
const context = {
|
|
@@ -4842,6 +4994,7 @@ class EmptyTitle extends Rule {
|
|
|
4842
4994
|
|
|
4843
4995
|
const defaults$e = {
|
|
4844
4996
|
allowMultipleH1: false,
|
|
4997
|
+
minInitialRank: "h1",
|
|
4845
4998
|
sectioningRoots: ["dialog", '[role="dialog"]'],
|
|
4846
4999
|
};
|
|
4847
5000
|
function isRelevant$2(event) {
|
|
@@ -4857,10 +5010,22 @@ function extractLevel(node) {
|
|
|
4857
5010
|
return null;
|
|
4858
5011
|
}
|
|
4859
5012
|
}
|
|
5013
|
+
function parseMaxInitial(value) {
|
|
5014
|
+
if (value === false || value === "any") {
|
|
5015
|
+
return 6;
|
|
5016
|
+
}
|
|
5017
|
+
const match = value.match(/^h(\d)$/);
|
|
5018
|
+
/* istanbul ignore next: should never happen, schema validation should catch invalid values */
|
|
5019
|
+
if (!match) {
|
|
5020
|
+
return 1;
|
|
5021
|
+
}
|
|
5022
|
+
return parseInt(match[1], 10);
|
|
5023
|
+
}
|
|
4860
5024
|
class HeadingLevel extends Rule {
|
|
4861
5025
|
constructor(options) {
|
|
4862
|
-
super(
|
|
5026
|
+
super({ ...defaults$e, ...options });
|
|
4863
5027
|
this.stack = [];
|
|
5028
|
+
this.minInitialRank = parseMaxInitial(this.options.minInitialRank);
|
|
4864
5029
|
this.sectionRoots = this.options.sectioningRoots.map((it) => new Pattern(it));
|
|
4865
5030
|
/* add a global sectioning root used by default */
|
|
4866
5031
|
this.stack.push({
|
|
@@ -4874,6 +5039,9 @@ class HeadingLevel extends Rule {
|
|
|
4874
5039
|
allowMultipleH1: {
|
|
4875
5040
|
type: "boolean",
|
|
4876
5041
|
},
|
|
5042
|
+
minInitialRank: {
|
|
5043
|
+
enum: ["h1", "h2", "h3", "h4", "h5", "h6", "any", false],
|
|
5044
|
+
},
|
|
4877
5045
|
sectioningRoots: {
|
|
4878
5046
|
items: {
|
|
4879
5047
|
type: "string",
|
|
@@ -4884,7 +5052,8 @@ class HeadingLevel extends Rule {
|
|
|
4884
5052
|
}
|
|
4885
5053
|
documentation() {
|
|
4886
5054
|
const text = [];
|
|
4887
|
-
|
|
5055
|
+
const modality = this.minInitialRank > 1 ? "should" : "must";
|
|
5056
|
+
text.push(`Headings ${modality} start at <h1> and can only increase one level at a time.`);
|
|
4888
5057
|
text.push("The headings should form a table of contents and make sense on its own.");
|
|
4889
5058
|
if (!this.options.allowMultipleH1) {
|
|
4890
5059
|
text.push("");
|
|
@@ -4929,20 +5098,32 @@ class HeadingLevel extends Rule {
|
|
|
4929
5098
|
*/
|
|
4930
5099
|
checkLevelIncrementation(root, event, level) {
|
|
4931
5100
|
const expected = root.current + 1;
|
|
4932
|
-
if
|
|
4933
|
-
|
|
4934
|
-
|
|
4935
|
-
|
|
4936
|
-
|
|
4937
|
-
|
|
4938
|
-
|
|
4939
|
-
|
|
4940
|
-
|
|
5101
|
+
/* check if the new level is the expected one (headings with higher ranks
|
|
5102
|
+
* are skipped already) */
|
|
5103
|
+
if (level === expected) {
|
|
5104
|
+
return;
|
|
5105
|
+
}
|
|
5106
|
+
/* if this is the initial heading of the document it is compared to the
|
|
5107
|
+
* minimal allowed (default h1) */
|
|
5108
|
+
const isInitial = this.stack.length === 1 && expected === 1;
|
|
5109
|
+
if (isInitial && level <= this.minInitialRank) {
|
|
5110
|
+
return;
|
|
5111
|
+
}
|
|
5112
|
+
/* if we reach this far the heading level is not accepted */
|
|
5113
|
+
const location = sliceLocation(event.location, 1);
|
|
5114
|
+
if (root.current > 0) {
|
|
5115
|
+
const msg = `Heading level can only increase by one, expected <h${expected}> but got <h${level}>`;
|
|
5116
|
+
this.report(event.target, msg, location);
|
|
5117
|
+
}
|
|
5118
|
+
else {
|
|
5119
|
+
this.checkInitialLevel(event, location, level, expected);
|
|
4941
5120
|
}
|
|
4942
5121
|
}
|
|
4943
5122
|
checkInitialLevel(event, location, level, expected) {
|
|
4944
5123
|
if (this.stack.length === 1) {
|
|
4945
|
-
const msg =
|
|
5124
|
+
const msg = this.minInitialRank > 1
|
|
5125
|
+
? `Initial heading level must be <h${this.minInitialRank}> or higher rank but got <h${level}>`
|
|
5126
|
+
: `Initial heading level must be <h${expected}> but got <h${level}>`;
|
|
4946
5127
|
this.report(event.target, msg, location);
|
|
4947
5128
|
}
|
|
4948
5129
|
else {
|
|
@@ -5005,7 +5186,7 @@ const defaults$d = {
|
|
|
5005
5186
|
};
|
|
5006
5187
|
class IdPattern extends Rule {
|
|
5007
5188
|
constructor(options) {
|
|
5008
|
-
super(
|
|
5189
|
+
super({ ...defaults$d, ...options });
|
|
5009
5190
|
this.pattern = parsePattern(this.options.pattern);
|
|
5010
5191
|
}
|
|
5011
5192
|
static schema() {
|
|
@@ -5357,7 +5538,7 @@ const defaults$c = {
|
|
|
5357
5538
|
};
|
|
5358
5539
|
class LongTitle extends Rule {
|
|
5359
5540
|
constructor(options) {
|
|
5360
|
-
super(
|
|
5541
|
+
super({ ...defaults$c, ...options });
|
|
5361
5542
|
this.maxlength = this.options.maxlength;
|
|
5362
5543
|
}
|
|
5363
5544
|
static schema() {
|
|
@@ -5510,7 +5691,7 @@ const defaults$b = {
|
|
|
5510
5691
|
};
|
|
5511
5692
|
class NoAutoplay extends Rule {
|
|
5512
5693
|
constructor(options) {
|
|
5513
|
-
super(
|
|
5694
|
+
super({ ...defaults$b, ...options });
|
|
5514
5695
|
}
|
|
5515
5696
|
documentation(context) {
|
|
5516
5697
|
const tagName = context ? ` on <${context.tagName}>` : "";
|
|
@@ -5606,8 +5787,12 @@ class NoDeprecatedAttr extends Rule {
|
|
|
5606
5787
|
if (meta === null) {
|
|
5607
5788
|
return;
|
|
5608
5789
|
}
|
|
5609
|
-
const
|
|
5610
|
-
if (
|
|
5790
|
+
const metaAttribute = meta.attributes && meta.attributes[attr];
|
|
5791
|
+
if (!metaAttribute) {
|
|
5792
|
+
return;
|
|
5793
|
+
}
|
|
5794
|
+
const deprecated = metaAttribute.deprecated;
|
|
5795
|
+
if (deprecated) {
|
|
5611
5796
|
this.report(node, `Attribute "${event.key}" is deprecated on <${node.tagName}> element`, event.keyLocation);
|
|
5612
5797
|
}
|
|
5613
5798
|
});
|
|
@@ -5764,7 +5949,7 @@ function getCSSDeclarations(value) {
|
|
|
5764
5949
|
}
|
|
5765
5950
|
class NoInlineStyle extends Rule {
|
|
5766
5951
|
constructor(options) {
|
|
5767
|
-
super(
|
|
5952
|
+
super({ ...defaults$a, ...options });
|
|
5768
5953
|
}
|
|
5769
5954
|
static schema() {
|
|
5770
5955
|
return {
|
|
@@ -5858,7 +6043,20 @@ class NoInlineStyle extends Rule {
|
|
|
5858
6043
|
}
|
|
5859
6044
|
}
|
|
5860
6045
|
|
|
5861
|
-
const ARIA = [
|
|
6046
|
+
const ARIA = [
|
|
6047
|
+
{ property: "aria-activedescendant", isList: false },
|
|
6048
|
+
{ property: "aria-controls", isList: true },
|
|
6049
|
+
{ property: "aria-describedby", isList: true },
|
|
6050
|
+
{ property: "aria-details", isList: false },
|
|
6051
|
+
{ property: "aria-errormessage", isList: false },
|
|
6052
|
+
{ property: "aria-flowto", isList: true },
|
|
6053
|
+
{ property: "aria-labelledby", isList: true },
|
|
6054
|
+
{ property: "aria-owns", isList: true },
|
|
6055
|
+
];
|
|
6056
|
+
function idMissing(document, id) {
|
|
6057
|
+
const nodes = document.querySelectorAll(`[id="${id}"]`);
|
|
6058
|
+
return nodes.length === 0;
|
|
6059
|
+
}
|
|
5862
6060
|
class NoMissingReferences extends Rule {
|
|
5863
6061
|
documentation(context) {
|
|
5864
6062
|
if (context) {
|
|
@@ -5880,38 +6078,56 @@ class NoMissingReferences extends Rule {
|
|
|
5880
6078
|
/* verify <label for=".."> */
|
|
5881
6079
|
for (const node of document.querySelectorAll("label[for]")) {
|
|
5882
6080
|
const attr = node.getAttribute("for");
|
|
5883
|
-
this.validateReference(document, node, attr);
|
|
6081
|
+
this.validateReference(document, node, attr, false);
|
|
5884
6082
|
}
|
|
5885
6083
|
/* verify <input list=".."> */
|
|
5886
6084
|
for (const node of document.querySelectorAll("input[list]")) {
|
|
5887
6085
|
const attr = node.getAttribute("list");
|
|
5888
|
-
this.validateReference(document, node, attr);
|
|
6086
|
+
this.validateReference(document, node, attr, false);
|
|
5889
6087
|
}
|
|
5890
6088
|
/* verify WAI-ARIA properties */
|
|
5891
|
-
for (const property of ARIA) {
|
|
6089
|
+
for (const { property, isList } of ARIA) {
|
|
5892
6090
|
for (const node of document.querySelectorAll(`[${property}]`)) {
|
|
5893
6091
|
const attr = node.getAttribute(property);
|
|
5894
|
-
this.validateReference(document, node, attr);
|
|
6092
|
+
this.validateReference(document, node, attr, isList);
|
|
5895
6093
|
}
|
|
5896
6094
|
}
|
|
5897
6095
|
});
|
|
5898
6096
|
}
|
|
5899
|
-
validateReference(document, node, attr) {
|
|
6097
|
+
validateReference(document, node, attr, isList) {
|
|
5900
6098
|
/* sanity check: querySelector should never return elements without the attribute */
|
|
5901
6099
|
/* istanbul ignore next */
|
|
5902
6100
|
if (!attr) {
|
|
5903
6101
|
return;
|
|
5904
6102
|
}
|
|
5905
|
-
|
|
5906
|
-
|
|
6103
|
+
/* skip dynamic and empty values */
|
|
6104
|
+
const value = attr.value;
|
|
6105
|
+
if (value instanceof DynamicValue || value === null || value === "") {
|
|
5907
6106
|
return;
|
|
5908
6107
|
}
|
|
5909
|
-
|
|
5910
|
-
|
|
6108
|
+
if (isList) {
|
|
6109
|
+
this.validateList(document, node, attr, value);
|
|
6110
|
+
}
|
|
6111
|
+
else {
|
|
6112
|
+
this.validateSingle(document, node, attr, value);
|
|
6113
|
+
}
|
|
6114
|
+
}
|
|
6115
|
+
validateSingle(document, node, attr, id) {
|
|
6116
|
+
if (idMissing(document, id)) {
|
|
5911
6117
|
const context = { key: attr.key, value: id };
|
|
5912
6118
|
this.report(node, `Element references missing id "${id}"`, attr.valueLocation, context);
|
|
5913
6119
|
}
|
|
5914
6120
|
}
|
|
6121
|
+
validateList(document, node, attr, values) {
|
|
6122
|
+
const parsed = new DOMTokenList(values, attr.valueLocation);
|
|
6123
|
+
for (const entry of parsed.iterator()) {
|
|
6124
|
+
const id = entry.item;
|
|
6125
|
+
if (idMissing(document, id)) {
|
|
6126
|
+
const context = { key: attr.key, value: id };
|
|
6127
|
+
this.report(node, `Element references missing id "${id}"`, entry.location, context);
|
|
6128
|
+
}
|
|
6129
|
+
}
|
|
6130
|
+
}
|
|
5915
6131
|
}
|
|
5916
6132
|
|
|
5917
6133
|
class NoMultipleMain extends Rule {
|
|
@@ -5955,7 +6171,7 @@ const replacementTable = new Map([
|
|
|
5955
6171
|
]);
|
|
5956
6172
|
class NoRawCharacters extends Rule {
|
|
5957
6173
|
constructor(options) {
|
|
5958
|
-
super(
|
|
6174
|
+
super({ ...defaults$9, ...options });
|
|
5959
6175
|
this.relaxed = this.options.relaxed;
|
|
5960
6176
|
}
|
|
5961
6177
|
static schema() {
|
|
@@ -6140,7 +6356,7 @@ const defaults$8 = {
|
|
|
6140
6356
|
};
|
|
6141
6357
|
class NoSelfClosing extends Rule {
|
|
6142
6358
|
constructor(options) {
|
|
6143
|
-
super(
|
|
6359
|
+
super({ ...defaults$8, ...options });
|
|
6144
6360
|
}
|
|
6145
6361
|
static schema() {
|
|
6146
6362
|
return {
|
|
@@ -6279,7 +6495,7 @@ const defaults$7 = {
|
|
|
6279
6495
|
};
|
|
6280
6496
|
class PreferButton extends Rule {
|
|
6281
6497
|
constructor(options) {
|
|
6282
|
-
super(
|
|
6498
|
+
super({ ...defaults$7, ...options });
|
|
6283
6499
|
}
|
|
6284
6500
|
static schema() {
|
|
6285
6501
|
return {
|
|
@@ -6384,7 +6600,7 @@ const defaults$6 = {
|
|
|
6384
6600
|
};
|
|
6385
6601
|
class PreferNativeElement extends Rule {
|
|
6386
6602
|
constructor(options) {
|
|
6387
|
-
super(
|
|
6603
|
+
super({ ...defaults$6, ...options });
|
|
6388
6604
|
}
|
|
6389
6605
|
static schema() {
|
|
6390
6606
|
return {
|
|
@@ -6514,7 +6730,7 @@ const supportSri = {
|
|
|
6514
6730
|
};
|
|
6515
6731
|
class RequireSri extends Rule {
|
|
6516
6732
|
constructor(options) {
|
|
6517
|
-
super(
|
|
6733
|
+
super({ ...defaults$5, ...options });
|
|
6518
6734
|
this.target = this.options.target;
|
|
6519
6735
|
}
|
|
6520
6736
|
static schema() {
|
|
@@ -8609,7 +8825,7 @@ const defaults$4 = {
|
|
|
8609
8825
|
};
|
|
8610
8826
|
class Void extends Rule {
|
|
8611
8827
|
constructor(options) {
|
|
8612
|
-
super(
|
|
8828
|
+
super({ ...defaults$4, ...options });
|
|
8613
8829
|
this.style = parseStyle$1(this.options.style);
|
|
8614
8830
|
}
|
|
8615
8831
|
get deprecated() {
|
|
@@ -8718,7 +8934,7 @@ const defaults$3 = {
|
|
|
8718
8934
|
};
|
|
8719
8935
|
class VoidStyle extends Rule {
|
|
8720
8936
|
constructor(options) {
|
|
8721
|
-
super(
|
|
8937
|
+
super({ ...defaults$3, ...options });
|
|
8722
8938
|
this.style = parseStyle(this.options.style);
|
|
8723
8939
|
}
|
|
8724
8940
|
static schema() {
|
|
@@ -8921,7 +9137,7 @@ const defaults$2 = {
|
|
|
8921
9137
|
};
|
|
8922
9138
|
class H37 extends Rule {
|
|
8923
9139
|
constructor(options) {
|
|
8924
|
-
super(
|
|
9140
|
+
super({ ...defaults$2, ...options });
|
|
8925
9141
|
/* ensure alias is array */
|
|
8926
9142
|
if (!Array.isArray(this.options.alias)) {
|
|
8927
9143
|
this.options.alias = [this.options.alias];
|
|
@@ -9048,7 +9264,73 @@ const bundledRules$1 = {
|
|
|
9048
9264
|
"wcag/h71": H71,
|
|
9049
9265
|
};
|
|
9050
9266
|
|
|
9051
|
-
const bundledRules =
|
|
9267
|
+
const bundledRules = {
|
|
9268
|
+
"allowed-links": AllowedLinks,
|
|
9269
|
+
"aria-label-misuse": AriaLabelMisuse,
|
|
9270
|
+
"attr-case": AttrCase,
|
|
9271
|
+
"attr-delimiter": AttrDelimiter,
|
|
9272
|
+
"attr-pattern": AttrPattern,
|
|
9273
|
+
"attr-quotes": AttrQuotes,
|
|
9274
|
+
"attr-spacing": AttrSpacing,
|
|
9275
|
+
"attribute-allowed-values": AttributeAllowedValues,
|
|
9276
|
+
"attribute-boolean-style": AttributeBooleanStyle,
|
|
9277
|
+
"attribute-empty-style": AttributeEmptyStyle,
|
|
9278
|
+
"class-pattern": ClassPattern,
|
|
9279
|
+
"close-attr": CloseAttr,
|
|
9280
|
+
"close-order": CloseOrder,
|
|
9281
|
+
deprecated: Deprecated,
|
|
9282
|
+
"deprecated-rule": DeprecatedRule,
|
|
9283
|
+
"doctype-html": NoStyleTag$1,
|
|
9284
|
+
"doctype-style": DoctypeStyle,
|
|
9285
|
+
"element-case": ElementCase,
|
|
9286
|
+
"element-name": ElementName,
|
|
9287
|
+
"element-permitted-content": ElementPermittedContent,
|
|
9288
|
+
"element-permitted-occurrences": ElementPermittedOccurrences,
|
|
9289
|
+
"element-permitted-order": ElementPermittedOrder,
|
|
9290
|
+
"element-required-attributes": ElementRequiredAttributes,
|
|
9291
|
+
"element-required-content": ElementRequiredContent,
|
|
9292
|
+
"empty-heading": EmptyHeading,
|
|
9293
|
+
"empty-title": EmptyTitle,
|
|
9294
|
+
"heading-level": HeadingLevel,
|
|
9295
|
+
"id-pattern": IdPattern,
|
|
9296
|
+
"input-attributes": InputAttributes,
|
|
9297
|
+
"input-missing-label": InputMissingLabel,
|
|
9298
|
+
"long-title": LongTitle,
|
|
9299
|
+
"meta-refresh": MetaRefresh,
|
|
9300
|
+
"missing-doctype": MissingDoctype,
|
|
9301
|
+
"multiple-labeled-controls": MultipleLabeledControls,
|
|
9302
|
+
"no-autoplay": NoAutoplay,
|
|
9303
|
+
"no-conditional-comment": NoConditionalComment,
|
|
9304
|
+
"no-deprecated-attr": NoDeprecatedAttr,
|
|
9305
|
+
"no-dup-attr": NoDupAttr,
|
|
9306
|
+
"no-dup-class": NoDupClass,
|
|
9307
|
+
"no-dup-id": NoDupID,
|
|
9308
|
+
"no-implicit-close": NoImplicitClose,
|
|
9309
|
+
"no-inline-style": NoInlineStyle,
|
|
9310
|
+
"no-missing-references": NoMissingReferences,
|
|
9311
|
+
"no-multiple-main": NoMultipleMain,
|
|
9312
|
+
"no-raw-characters": NoRawCharacters,
|
|
9313
|
+
"no-redundant-for": NoRedundantFor,
|
|
9314
|
+
"no-redundant-role": NoRedundantRole,
|
|
9315
|
+
"no-self-closing": NoSelfClosing,
|
|
9316
|
+
"no-style-tag": NoStyleTag,
|
|
9317
|
+
"no-trailing-whitespace": NoTrailingWhitespace,
|
|
9318
|
+
"no-unknown-elements": NoUnknownElements,
|
|
9319
|
+
"no-utf8-bom": NoUtf8Bom,
|
|
9320
|
+
"prefer-button": PreferButton,
|
|
9321
|
+
"prefer-native-element": PreferNativeElement,
|
|
9322
|
+
"prefer-tbody": PreferTbody,
|
|
9323
|
+
"require-sri": RequireSri,
|
|
9324
|
+
"script-element": ScriptElement,
|
|
9325
|
+
"script-type": ScriptType,
|
|
9326
|
+
"svg-focusable": SvgFocusable,
|
|
9327
|
+
"text-content": TextContent,
|
|
9328
|
+
"unrecognized-char-ref": UnknownCharReference,
|
|
9329
|
+
void: Void,
|
|
9330
|
+
"void-content": VoidContent,
|
|
9331
|
+
"void-style": VoidStyle,
|
|
9332
|
+
...bundledRules$1,
|
|
9333
|
+
};
|
|
9052
9334
|
|
|
9053
9335
|
var defaultConfig = {};
|
|
9054
9336
|
|
|
@@ -9254,7 +9536,7 @@ class ResolvedConfig {
|
|
|
9254
9536
|
* @returns A list of transformed sources ready for validation.
|
|
9255
9537
|
*/
|
|
9256
9538
|
transformFilename(filename) {
|
|
9257
|
-
const data = fs__default[
|
|
9539
|
+
const data = fs__default["default"].readFileSync(filename, { encoding: "utf8" });
|
|
9258
9540
|
const source = {
|
|
9259
9541
|
data,
|
|
9260
9542
|
filename,
|
|
@@ -9279,19 +9561,19 @@ class ResolvedConfig {
|
|
|
9279
9561
|
}
|
|
9280
9562
|
|
|
9281
9563
|
let rootDirCache = null;
|
|
9282
|
-
const ajv = new Ajv__default[
|
|
9564
|
+
const ajv = new Ajv__default["default"]({ strict: true, strictTuples: true, strictTypes: true });
|
|
9283
9565
|
ajv.addMetaSchema(ajvSchemaDraft);
|
|
9284
9566
|
const validator = ajv.compile(configurationSchema);
|
|
9285
9567
|
function overwriteMerge(a, b) {
|
|
9286
9568
|
return b;
|
|
9287
9569
|
}
|
|
9288
9570
|
function mergeInternal(base, rhs) {
|
|
9289
|
-
const dst = deepmerge__default[
|
|
9571
|
+
const dst = deepmerge__default["default"](base, { ...rhs, rules: {} });
|
|
9290
9572
|
/* rules need some special care, should overwrite arrays instead of
|
|
9291
9573
|
* concaternation, i.e. ["error", {...options}] should not be merged by
|
|
9292
9574
|
* appending to old value */
|
|
9293
9575
|
if (rhs.rules) {
|
|
9294
|
-
dst.rules = deepmerge__default[
|
|
9576
|
+
dst.rules = deepmerge__default["default"](dst.rules, rhs.rules, { arrayMerge: overwriteMerge });
|
|
9295
9577
|
}
|
|
9296
9578
|
/* root property is merged with boolean "or" since it should always be truthy
|
|
9297
9579
|
* if any config has it set. */
|
|
@@ -9315,7 +9597,7 @@ function loadFromFile(filename) {
|
|
|
9315
9597
|
if (!json[key])
|
|
9316
9598
|
continue;
|
|
9317
9599
|
json[key] = json[key].map((ref) => {
|
|
9318
|
-
return Config.expandRelative(ref, path__default[
|
|
9600
|
+
return Config.expandRelative(ref, path__default["default"].dirname(filename));
|
|
9319
9601
|
});
|
|
9320
9602
|
}
|
|
9321
9603
|
return json;
|
|
@@ -9474,14 +9756,14 @@ class Config {
|
|
|
9474
9756
|
}
|
|
9475
9757
|
let filename;
|
|
9476
9758
|
/* try searching builtin metadata */
|
|
9477
|
-
filename = path__default[
|
|
9478
|
-
if (fs__default[
|
|
9759
|
+
filename = path__default["default"].join(projectRoot, "elements", `${entry}.json`);
|
|
9760
|
+
if (fs__default["default"].existsSync(filename)) {
|
|
9479
9761
|
metaTable.loadFromFile(filename);
|
|
9480
9762
|
continue;
|
|
9481
9763
|
}
|
|
9482
9764
|
/* try as regular file */
|
|
9483
9765
|
filename = entry.replace("<rootDir>", this.rootDir);
|
|
9484
|
-
if (fs__default[
|
|
9766
|
+
if (fs__default["default"].existsSync(filename)) {
|
|
9485
9767
|
metaTable.loadFromFile(filename);
|
|
9486
9768
|
continue;
|
|
9487
9769
|
}
|
|
@@ -9501,7 +9783,7 @@ class Config {
|
|
|
9501
9783
|
*/
|
|
9502
9784
|
static expandRelative(src, currentPath) {
|
|
9503
9785
|
if (src[0] === ".") {
|
|
9504
|
-
return path__default[
|
|
9786
|
+
return path__default["default"].normalize(`${currentPath}/${src}`);
|
|
9505
9787
|
}
|
|
9506
9788
|
return src;
|
|
9507
9789
|
}
|
|
@@ -9511,7 +9793,7 @@ class Config {
|
|
|
9511
9793
|
* @internal primary purpose is unittests
|
|
9512
9794
|
*/
|
|
9513
9795
|
get() {
|
|
9514
|
-
const config =
|
|
9796
|
+
const config = { ...this.config };
|
|
9515
9797
|
if (config.elements) {
|
|
9516
9798
|
config.elements = config.elements.map((cur) => {
|
|
9517
9799
|
if (typeof cur === "string") {
|
|
@@ -9597,7 +9879,11 @@ class Config {
|
|
|
9597
9879
|
if (!properties) {
|
|
9598
9880
|
continue;
|
|
9599
9881
|
}
|
|
9600
|
-
for (const [
|
|
9882
|
+
for (const [raw, schema] of Object.entries(properties)) {
|
|
9883
|
+
/* at compile time this is a fixed list but the point of this method is
|
|
9884
|
+
* to augment the runtime with additional keys so it is a bit of lying
|
|
9885
|
+
* to typescript */
|
|
9886
|
+
const key = raw;
|
|
9601
9887
|
if (schema.copyable && !MetaCopyableProperty.includes(key)) {
|
|
9602
9888
|
MetaCopyableProperty.push(key);
|
|
9603
9889
|
}
|
|
@@ -9751,13 +10037,13 @@ class Config {
|
|
|
9751
10037
|
let current = process.cwd();
|
|
9752
10038
|
// eslint-disable-next-line no-constant-condition
|
|
9753
10039
|
while (true) {
|
|
9754
|
-
const search = path__default[
|
|
9755
|
-
if (fs__default[
|
|
10040
|
+
const search = path__default["default"].join(current, "package.json");
|
|
10041
|
+
if (fs__default["default"].existsSync(search)) {
|
|
9756
10042
|
return (this.rootDirCache = current);
|
|
9757
10043
|
}
|
|
9758
10044
|
/* get the parent directory */
|
|
9759
10045
|
const child = current;
|
|
9760
|
-
current = path__default[
|
|
10046
|
+
current = path__default["default"].dirname(current);
|
|
9761
10047
|
/* stop if this is the root directory */
|
|
9762
10048
|
if (current === child) {
|
|
9763
10049
|
break;
|
|
@@ -9793,31 +10079,6 @@ class ConfigLoader {
|
|
|
9793
10079
|
}
|
|
9794
10080
|
}
|
|
9795
10081
|
|
|
9796
|
-
/*! *****************************************************************************
|
|
9797
|
-
Copyright (c) Microsoft Corporation. All rights reserved.
|
|
9798
|
-
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
|
9799
|
-
this file except in compliance with the License. You may obtain a copy of the
|
|
9800
|
-
License at http://www.apache.org/licenses/LICENSE-2.0
|
|
9801
|
-
|
|
9802
|
-
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
9803
|
-
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
|
|
9804
|
-
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
|
|
9805
|
-
MERCHANTABLITY OR NON-INFRINGEMENT.
|
|
9806
|
-
|
|
9807
|
-
See the Apache Version 2.0 License for specific language governing permissions
|
|
9808
|
-
and limitations under the License.
|
|
9809
|
-
***************************************************************************** */
|
|
9810
|
-
|
|
9811
|
-
function __rest(s, e) {
|
|
9812
|
-
var t = {};
|
|
9813
|
-
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
9814
|
-
t[p] = s[p];
|
|
9815
|
-
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
9816
|
-
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) if (e.indexOf(p[i]) < 0)
|
|
9817
|
-
t[p[i]] = s[p[i]];
|
|
9818
|
-
return t;
|
|
9819
|
-
}
|
|
9820
|
-
|
|
9821
10082
|
class EventHandler {
|
|
9822
10083
|
constructor() {
|
|
9823
10084
|
this.listeners = {};
|
|
@@ -10391,7 +10652,7 @@ class Reporter {
|
|
|
10391
10652
|
merged[key].messages = [...merged[key].messages, ...result.messages];
|
|
10392
10653
|
}
|
|
10393
10654
|
else {
|
|
10394
|
-
merged[key] =
|
|
10655
|
+
merged[key] = { ...result };
|
|
10395
10656
|
}
|
|
10396
10657
|
});
|
|
10397
10658
|
});
|
|
@@ -10493,14 +10754,16 @@ function messageSort(a, b) {
|
|
|
10493
10754
|
}
|
|
10494
10755
|
|
|
10495
10756
|
class Engine {
|
|
10496
|
-
constructor(config,
|
|
10757
|
+
constructor(config, ParserClass) {
|
|
10497
10758
|
this.report = new Reporter();
|
|
10498
|
-
this.configData = configData;
|
|
10499
10759
|
this.config = config;
|
|
10500
10760
|
this.ParserClass = ParserClass;
|
|
10501
10761
|
/* initialize plugins and rules */
|
|
10502
10762
|
const result = this.initPlugins(this.config);
|
|
10503
|
-
this.availableRules =
|
|
10763
|
+
this.availableRules = {
|
|
10764
|
+
...bundledRules,
|
|
10765
|
+
...result.availableRules,
|
|
10766
|
+
};
|
|
10504
10767
|
}
|
|
10505
10768
|
/**
|
|
10506
10769
|
* Lint sources and return report
|
|
@@ -10525,13 +10788,13 @@ class Engine {
|
|
|
10525
10788
|
/* trigger configuration ready event */
|
|
10526
10789
|
const configEvent = {
|
|
10527
10790
|
location,
|
|
10528
|
-
config: this.
|
|
10791
|
+
config: this.config,
|
|
10529
10792
|
rules,
|
|
10530
10793
|
};
|
|
10531
10794
|
parser.trigger("config:ready", configEvent);
|
|
10532
10795
|
/* trigger source ready event */
|
|
10533
10796
|
/* eslint-disable-next-line @typescript-eslint/no-unused-vars -- object destructured on purpose to remove property */
|
|
10534
|
-
const sourceData =
|
|
10797
|
+
const { hooks: _, ...sourceData } = source;
|
|
10535
10798
|
const sourceEvent = {
|
|
10536
10799
|
location,
|
|
10537
10800
|
source: sourceData,
|
|
@@ -10838,133 +11101,31 @@ class Engine {
|
|
|
10838
11101
|
}
|
|
10839
11102
|
|
|
10840
11103
|
/**
|
|
10841
|
-
*
|
|
10842
|
-
|
|
10843
|
-
function findConfigurationFiles(directory) {
|
|
10844
|
-
return ["json", "cjs", "js"]
|
|
10845
|
-
.map((extension) => path__default['default'].join(directory, `.htmlvalidate.${extension}`))
|
|
10846
|
-
.filter((filePath) => fs__default['default'].existsSync(filePath));
|
|
10847
|
-
}
|
|
10848
|
-
/**
|
|
10849
|
-
* Loads configuration by traversing filesystem.
|
|
10850
|
-
*
|
|
10851
|
-
* Configuration is read from three sources and in the following order:
|
|
10852
|
-
*
|
|
10853
|
-
* 1. Global configuration passed to constructor.
|
|
10854
|
-
* 2. Configuration files found when traversing the directory structure.
|
|
10855
|
-
* 3. Override passed to this function.
|
|
10856
|
-
*
|
|
10857
|
-
* The following configuration filenames are searched:
|
|
10858
|
-
*
|
|
10859
|
-
* - `.htmlvalidate.json`
|
|
10860
|
-
* - `.htmlvalidate.js`
|
|
10861
|
-
* - `.htmlvalidate.cjs`
|
|
10862
|
-
*
|
|
10863
|
-
* Global configuration is used when no configuration file is found. The
|
|
10864
|
-
* result is always merged with override if present.
|
|
10865
|
-
*
|
|
10866
|
-
* The `root` property set to `true` affects the configuration as following:
|
|
11104
|
+
* The static configuration loader does not do any per-handle lookup. Only the
|
|
11105
|
+
* global or per-call configuration is used.
|
|
10867
11106
|
*
|
|
10868
|
-
*
|
|
10869
|
-
*
|
|
10870
|
-
* returned. No configuration files are searched.
|
|
10871
|
-
* 3. Setting `root` in configuration file only stops directory traversal.
|
|
11107
|
+
* In practice this means no configuration is fetch by traversing the
|
|
11108
|
+
* filesystem.
|
|
10872
11109
|
*/
|
|
10873
|
-
class
|
|
10874
|
-
|
|
10875
|
-
* @param config - Global configuration
|
|
10876
|
-
* @param configFactory - Optional configuration factory
|
|
10877
|
-
*/
|
|
10878
|
-
constructor(config, configFactory = Config) {
|
|
10879
|
-
super(config, configFactory);
|
|
10880
|
-
this.cache = new Map();
|
|
10881
|
-
}
|
|
10882
|
-
/**
|
|
10883
|
-
* Get configuration for given filename.
|
|
10884
|
-
*
|
|
10885
|
-
* @param filename - Filename to get configuration for.
|
|
10886
|
-
* @param configOverride - Configuration to merge final result with.
|
|
10887
|
-
*/
|
|
10888
|
-
getConfigFor(filename, configOverride) {
|
|
10889
|
-
/* special case when the overridden configuration is marked as root, should
|
|
10890
|
-
* not try to load any more configuration files */
|
|
11110
|
+
class StaticConfigLoader extends ConfigLoader {
|
|
11111
|
+
getConfigFor(handle, configOverride) {
|
|
10891
11112
|
const override = this.loadFromObject(configOverride || {});
|
|
10892
11113
|
if (override.isRootFound()) {
|
|
10893
11114
|
override.init();
|
|
10894
11115
|
return override;
|
|
10895
11116
|
}
|
|
10896
|
-
|
|
10897
|
-
* try to load and more configuration files */
|
|
10898
|
-
if (this.globalConfig.isRootFound()) {
|
|
10899
|
-
const merged = this.globalConfig.merge(override);
|
|
10900
|
-
merged.init();
|
|
10901
|
-
return merged;
|
|
10902
|
-
}
|
|
10903
|
-
const config = this.fromFilename(filename);
|
|
10904
|
-
const merged = config ? config.merge(override) : this.globalConfig.merge(override);
|
|
11117
|
+
const merged = this.globalConfig.merge(override);
|
|
10905
11118
|
merged.init();
|
|
10906
11119
|
return merged;
|
|
10907
11120
|
}
|
|
10908
|
-
|
|
10909
|
-
|
|
10910
|
-
*
|
|
10911
|
-
* @param filename - If given only the cache for that file is flushed.
|
|
10912
|
-
*/
|
|
10913
|
-
flushCache(filename) {
|
|
10914
|
-
if (filename) {
|
|
10915
|
-
this.cache.delete(filename);
|
|
10916
|
-
}
|
|
10917
|
-
else {
|
|
10918
|
-
this.cache.clear();
|
|
10919
|
-
}
|
|
10920
|
-
}
|
|
10921
|
-
/**
|
|
10922
|
-
* Load raw configuration from directory traversal.
|
|
10923
|
-
*
|
|
10924
|
-
* This configuration is not merged with global configuration and may return
|
|
10925
|
-
* `null` if no configuration files are found.
|
|
10926
|
-
*/
|
|
10927
|
-
fromFilename(filename) {
|
|
10928
|
-
var _a;
|
|
10929
|
-
if (filename === "inline") {
|
|
10930
|
-
return null;
|
|
10931
|
-
}
|
|
10932
|
-
if (this.cache.has(filename)) {
|
|
10933
|
-
return (_a = this.cache.get(filename)) !== null && _a !== void 0 ? _a : null;
|
|
10934
|
-
}
|
|
10935
|
-
let found = false;
|
|
10936
|
-
let current = path__default['default'].resolve(path__default['default'].dirname(filename));
|
|
10937
|
-
let config = this.empty();
|
|
10938
|
-
// eslint-disable-next-line no-constant-condition
|
|
10939
|
-
while (true) {
|
|
10940
|
-
/* search configuration files in current directory */
|
|
10941
|
-
for (const configFile of findConfigurationFiles(current)) {
|
|
10942
|
-
const local = this.loadFromFile(configFile);
|
|
10943
|
-
found = true;
|
|
10944
|
-
config = local.merge(config);
|
|
10945
|
-
}
|
|
10946
|
-
/* stop if a configuration with "root" is set to true */
|
|
10947
|
-
if (config.isRootFound()) {
|
|
10948
|
-
break;
|
|
10949
|
-
}
|
|
10950
|
-
/* get the parent directory */
|
|
10951
|
-
const child = current;
|
|
10952
|
-
current = path__default['default'].dirname(current);
|
|
10953
|
-
/* stop if this is the root directory */
|
|
10954
|
-
if (current === child) {
|
|
10955
|
-
break;
|
|
10956
|
-
}
|
|
10957
|
-
}
|
|
10958
|
-
/* no config was found by loader, return null and let caller decide what to do */
|
|
10959
|
-
if (!found) {
|
|
10960
|
-
this.cache.set(filename, null);
|
|
10961
|
-
return null;
|
|
10962
|
-
}
|
|
10963
|
-
this.cache.set(filename, config);
|
|
10964
|
-
return config;
|
|
11121
|
+
flushCache() {
|
|
11122
|
+
/* do nothing */
|
|
10965
11123
|
}
|
|
10966
11124
|
defaultConfig() {
|
|
10967
|
-
return this.
|
|
11125
|
+
return this.loadFromObject({
|
|
11126
|
+
extends: ["html-validate:recommended"],
|
|
11127
|
+
elements: ["html5"],
|
|
11128
|
+
});
|
|
10968
11129
|
}
|
|
10969
11130
|
}
|
|
10970
11131
|
|
|
@@ -10988,7 +11149,7 @@ function isConfigData(value) {
|
|
|
10988
11149
|
class HtmlValidate {
|
|
10989
11150
|
constructor(arg) {
|
|
10990
11151
|
const [loader, config] = arg instanceof ConfigLoader ? [arg, undefined] : [undefined, arg];
|
|
10991
|
-
this.configLoader = loader !== null && loader !== void 0 ? loader : new
|
|
11152
|
+
this.configLoader = loader !== null && loader !== void 0 ? loader : new StaticConfigLoader(config);
|
|
10992
11153
|
}
|
|
10993
11154
|
validateString(str, arg1, arg2, arg3) {
|
|
10994
11155
|
const filename = typeof arg1 === "string" ? arg1 : "inline";
|
|
@@ -11015,7 +11176,7 @@ class HtmlValidate {
|
|
|
11015
11176
|
const config = this.getConfigFor(input.filename, configOverride);
|
|
11016
11177
|
const resolved = config.resolve();
|
|
11017
11178
|
const source = resolved.transformSource(input);
|
|
11018
|
-
const engine = new Engine(resolved,
|
|
11179
|
+
const engine = new Engine(resolved, Parser);
|
|
11019
11180
|
return engine.lint(source);
|
|
11020
11181
|
}
|
|
11021
11182
|
/**
|
|
@@ -11029,7 +11190,7 @@ class HtmlValidate {
|
|
|
11029
11190
|
const config = this.getConfigFor(filename);
|
|
11030
11191
|
const resolved = config.resolve();
|
|
11031
11192
|
const source = resolved.transformFilename(filename);
|
|
11032
|
-
const engine = new Engine(resolved,
|
|
11193
|
+
const engine = new Engine(resolved, Parser);
|
|
11033
11194
|
return engine.lint(source);
|
|
11034
11195
|
}
|
|
11035
11196
|
/**
|
|
@@ -11053,7 +11214,7 @@ class HtmlValidate {
|
|
|
11053
11214
|
*/
|
|
11054
11215
|
canValidate(filename) {
|
|
11055
11216
|
/* .html is always supported */
|
|
11056
|
-
const extension = path__default[
|
|
11217
|
+
const extension = path__default["default"].extname(filename).toLowerCase();
|
|
11057
11218
|
if (extension === ".html") {
|
|
11058
11219
|
return true;
|
|
11059
11220
|
}
|
|
@@ -11074,7 +11235,7 @@ class HtmlValidate {
|
|
|
11074
11235
|
const config = this.getConfigFor(filename);
|
|
11075
11236
|
const resolved = config.resolve();
|
|
11076
11237
|
const source = resolved.transformFilename(filename);
|
|
11077
|
-
const engine = new Engine(resolved,
|
|
11238
|
+
const engine = new Engine(resolved, Parser);
|
|
11078
11239
|
return engine.dumpTokens(source);
|
|
11079
11240
|
}
|
|
11080
11241
|
/**
|
|
@@ -11089,7 +11250,7 @@ class HtmlValidate {
|
|
|
11089
11250
|
const config = this.getConfigFor(filename);
|
|
11090
11251
|
const resolved = config.resolve();
|
|
11091
11252
|
const source = resolved.transformFilename(filename);
|
|
11092
|
-
const engine = new Engine(resolved,
|
|
11253
|
+
const engine = new Engine(resolved, Parser);
|
|
11093
11254
|
return engine.dumpEvents(source);
|
|
11094
11255
|
}
|
|
11095
11256
|
/**
|
|
@@ -11104,7 +11265,7 @@ class HtmlValidate {
|
|
|
11104
11265
|
const config = this.getConfigFor(filename);
|
|
11105
11266
|
const resolved = config.resolve();
|
|
11106
11267
|
const source = resolved.transformFilename(filename);
|
|
11107
|
-
const engine = new Engine(resolved,
|
|
11268
|
+
const engine = new Engine(resolved, Parser);
|
|
11108
11269
|
return engine.dumpTree(source);
|
|
11109
11270
|
}
|
|
11110
11271
|
/**
|
|
@@ -11182,7 +11343,7 @@ class HtmlValidate {
|
|
|
11182
11343
|
*/
|
|
11183
11344
|
getRuleDocumentation(ruleId, config = null, context = null) {
|
|
11184
11345
|
const c = config || this.getConfigFor("inline");
|
|
11185
|
-
const engine = new Engine(c.resolve(),
|
|
11346
|
+
const engine = new Engine(c.resolve(), Parser);
|
|
11186
11347
|
return engine.getRuleDocumentation(ruleId, context);
|
|
11187
11348
|
}
|
|
11188
11349
|
/**
|
|
@@ -11219,41 +11380,12 @@ class HtmlValidate {
|
|
|
11219
11380
|
}
|
|
11220
11381
|
}
|
|
11221
11382
|
|
|
11222
|
-
/**
|
|
11223
|
-
* The static configuration loader does not do any per-handle lookup. Only the
|
|
11224
|
-
* global or per-call configuration is used.
|
|
11225
|
-
*
|
|
11226
|
-
* In practice this means no configuration is fetch by traversing the
|
|
11227
|
-
* filesystem.
|
|
11228
|
-
*/
|
|
11229
|
-
class StaticConfigLoader extends ConfigLoader {
|
|
11230
|
-
getConfigFor(handle, configOverride) {
|
|
11231
|
-
const override = this.loadFromObject(configOverride || {});
|
|
11232
|
-
if (override.isRootFound()) {
|
|
11233
|
-
override.init();
|
|
11234
|
-
return override;
|
|
11235
|
-
}
|
|
11236
|
-
const merged = this.globalConfig.merge(override);
|
|
11237
|
-
merged.init();
|
|
11238
|
-
return merged;
|
|
11239
|
-
}
|
|
11240
|
-
flushCache() {
|
|
11241
|
-
/* do nothing */
|
|
11242
|
-
}
|
|
11243
|
-
defaultConfig() {
|
|
11244
|
-
return this.loadFromObject({
|
|
11245
|
-
extends: ["html-validate:recommended"],
|
|
11246
|
-
elements: ["html5"],
|
|
11247
|
-
});
|
|
11248
|
-
}
|
|
11249
|
-
}
|
|
11250
|
-
|
|
11251
11383
|
const defaults$1 = {
|
|
11252
11384
|
silent: false,
|
|
11253
11385
|
version,
|
|
11254
11386
|
logger(text) {
|
|
11255
11387
|
/* eslint-disable-next-line no-console */
|
|
11256
|
-
console.error(kleur__default[
|
|
11388
|
+
console.error(kleur__default["default"].red(text));
|
|
11257
11389
|
},
|
|
11258
11390
|
};
|
|
11259
11391
|
/**
|
|
@@ -11265,8 +11397,8 @@ const defaults$1 = {
|
|
|
11265
11397
|
* @returns - `true` if version is compatible
|
|
11266
11398
|
*/
|
|
11267
11399
|
function compatibilityCheck(name, declared, options) {
|
|
11268
|
-
const { silent, version: current, logger } =
|
|
11269
|
-
const valid = semver__default[
|
|
11400
|
+
const { silent, version: current, logger } = { ...defaults$1, ...options };
|
|
11401
|
+
const valid = semver__default["default"].satisfies(current, declared);
|
|
11270
11402
|
if (valid || silent) {
|
|
11271
11403
|
return valid;
|
|
11272
11404
|
}
|
|
@@ -11295,6 +11427,137 @@ function ruleExists(ruleId) {
|
|
|
11295
11427
|
return ruleIds.has(ruleId);
|
|
11296
11428
|
}
|
|
11297
11429
|
|
|
11430
|
+
/**
|
|
11431
|
+
* @internal
|
|
11432
|
+
*/
|
|
11433
|
+
function findConfigurationFiles(directory) {
|
|
11434
|
+
return ["json", "cjs", "js"]
|
|
11435
|
+
.map((extension) => path__default["default"].join(directory, `.htmlvalidate.${extension}`))
|
|
11436
|
+
.filter((filePath) => fs__default["default"].existsSync(filePath));
|
|
11437
|
+
}
|
|
11438
|
+
/**
|
|
11439
|
+
* Loads configuration by traversing filesystem.
|
|
11440
|
+
*
|
|
11441
|
+
* Configuration is read from three sources and in the following order:
|
|
11442
|
+
*
|
|
11443
|
+
* 1. Global configuration passed to constructor.
|
|
11444
|
+
* 2. Configuration files found when traversing the directory structure.
|
|
11445
|
+
* 3. Override passed to this function.
|
|
11446
|
+
*
|
|
11447
|
+
* The following configuration filenames are searched:
|
|
11448
|
+
*
|
|
11449
|
+
* - `.htmlvalidate.json`
|
|
11450
|
+
* - `.htmlvalidate.js`
|
|
11451
|
+
* - `.htmlvalidate.cjs`
|
|
11452
|
+
*
|
|
11453
|
+
* Global configuration is used when no configuration file is found. The
|
|
11454
|
+
* result is always merged with override if present.
|
|
11455
|
+
*
|
|
11456
|
+
* The `root` property set to `true` affects the configuration as following:
|
|
11457
|
+
*
|
|
11458
|
+
* 1. If set in override the override is returned as-is.
|
|
11459
|
+
* 2. If set in the global config the override is merged into global and
|
|
11460
|
+
* returned. No configuration files are searched.
|
|
11461
|
+
* 3. Setting `root` in configuration file only stops directory traversal.
|
|
11462
|
+
*/
|
|
11463
|
+
class FileSystemConfigLoader extends ConfigLoader {
|
|
11464
|
+
/**
|
|
11465
|
+
* @param config - Global configuration
|
|
11466
|
+
* @param configFactory - Optional configuration factory
|
|
11467
|
+
*/
|
|
11468
|
+
constructor(config, configFactory = Config) {
|
|
11469
|
+
super(config, configFactory);
|
|
11470
|
+
this.cache = new Map();
|
|
11471
|
+
}
|
|
11472
|
+
/**
|
|
11473
|
+
* Get configuration for given filename.
|
|
11474
|
+
*
|
|
11475
|
+
* @param filename - Filename to get configuration for.
|
|
11476
|
+
* @param configOverride - Configuration to merge final result with.
|
|
11477
|
+
*/
|
|
11478
|
+
getConfigFor(filename, configOverride) {
|
|
11479
|
+
/* special case when the overridden configuration is marked as root, should
|
|
11480
|
+
* not try to load any more configuration files */
|
|
11481
|
+
const override = this.loadFromObject(configOverride || {});
|
|
11482
|
+
if (override.isRootFound()) {
|
|
11483
|
+
override.init();
|
|
11484
|
+
return override;
|
|
11485
|
+
}
|
|
11486
|
+
/* special case when the global configuration is marked as root, should not
|
|
11487
|
+
* try to load and more configuration files */
|
|
11488
|
+
if (this.globalConfig.isRootFound()) {
|
|
11489
|
+
const merged = this.globalConfig.merge(override);
|
|
11490
|
+
merged.init();
|
|
11491
|
+
return merged;
|
|
11492
|
+
}
|
|
11493
|
+
const config = this.fromFilename(filename);
|
|
11494
|
+
const merged = config ? config.merge(override) : this.globalConfig.merge(override);
|
|
11495
|
+
merged.init();
|
|
11496
|
+
return merged;
|
|
11497
|
+
}
|
|
11498
|
+
/**
|
|
11499
|
+
* Flush configuration cache.
|
|
11500
|
+
*
|
|
11501
|
+
* @param filename - If given only the cache for that file is flushed.
|
|
11502
|
+
*/
|
|
11503
|
+
flushCache(filename) {
|
|
11504
|
+
if (filename) {
|
|
11505
|
+
this.cache.delete(filename);
|
|
11506
|
+
}
|
|
11507
|
+
else {
|
|
11508
|
+
this.cache.clear();
|
|
11509
|
+
}
|
|
11510
|
+
}
|
|
11511
|
+
/**
|
|
11512
|
+
* Load raw configuration from directory traversal.
|
|
11513
|
+
*
|
|
11514
|
+
* This configuration is not merged with global configuration and may return
|
|
11515
|
+
* `null` if no configuration files are found.
|
|
11516
|
+
*/
|
|
11517
|
+
fromFilename(filename) {
|
|
11518
|
+
var _a;
|
|
11519
|
+
if (filename === "inline") {
|
|
11520
|
+
return null;
|
|
11521
|
+
}
|
|
11522
|
+
if (this.cache.has(filename)) {
|
|
11523
|
+
return (_a = this.cache.get(filename)) !== null && _a !== void 0 ? _a : null;
|
|
11524
|
+
}
|
|
11525
|
+
let found = false;
|
|
11526
|
+
let current = path__default["default"].resolve(path__default["default"].dirname(filename));
|
|
11527
|
+
let config = this.empty();
|
|
11528
|
+
// eslint-disable-next-line no-constant-condition
|
|
11529
|
+
while (true) {
|
|
11530
|
+
/* search configuration files in current directory */
|
|
11531
|
+
for (const configFile of findConfigurationFiles(current)) {
|
|
11532
|
+
const local = this.loadFromFile(configFile);
|
|
11533
|
+
found = true;
|
|
11534
|
+
config = local.merge(config);
|
|
11535
|
+
}
|
|
11536
|
+
/* stop if a configuration with "root" is set to true */
|
|
11537
|
+
if (config.isRootFound()) {
|
|
11538
|
+
break;
|
|
11539
|
+
}
|
|
11540
|
+
/* get the parent directory */
|
|
11541
|
+
const child = current;
|
|
11542
|
+
current = path__default["default"].dirname(current);
|
|
11543
|
+
/* stop if this is the root directory */
|
|
11544
|
+
if (current === child) {
|
|
11545
|
+
break;
|
|
11546
|
+
}
|
|
11547
|
+
}
|
|
11548
|
+
/* no config was found by loader, return null and let caller decide what to do */
|
|
11549
|
+
if (!found) {
|
|
11550
|
+
this.cache.set(filename, null);
|
|
11551
|
+
return null;
|
|
11552
|
+
}
|
|
11553
|
+
this.cache.set(filename, config);
|
|
11554
|
+
return config;
|
|
11555
|
+
}
|
|
11556
|
+
defaultConfig() {
|
|
11557
|
+
return this.configFactory.defaultConfig();
|
|
11558
|
+
}
|
|
11559
|
+
}
|
|
11560
|
+
|
|
11298
11561
|
const entities = {
|
|
11299
11562
|
">": ">",
|
|
11300
11563
|
"<": "<",
|
|
@@ -11366,12 +11629,12 @@ function pluralize(word, count) {
|
|
|
11366
11629
|
* @returns The formatted file path.
|
|
11367
11630
|
*/
|
|
11368
11631
|
function formatFilePath(filePath, line, column) {
|
|
11369
|
-
let relPath = path__default[
|
|
11632
|
+
let relPath = path__default["default"].relative(process.cwd(), filePath);
|
|
11370
11633
|
/* istanbul ignore next: safety check from original implementation */
|
|
11371
11634
|
if (line && column) {
|
|
11372
11635
|
relPath += `:${line}:${column}`;
|
|
11373
11636
|
}
|
|
11374
|
-
return kleur__default[
|
|
11637
|
+
return kleur__default["default"].green(relPath);
|
|
11375
11638
|
}
|
|
11376
11639
|
function getStartLocation(message) {
|
|
11377
11640
|
return {
|
|
@@ -11400,9 +11663,9 @@ function getEndLocation(message, source) {
|
|
|
11400
11663
|
* @returns The formatted output.
|
|
11401
11664
|
*/
|
|
11402
11665
|
function formatMessage(message, parentResult, options) {
|
|
11403
|
-
const type = message.severity === 2 ? kleur__default[
|
|
11404
|
-
const msg = `${kleur__default[
|
|
11405
|
-
const ruleId = kleur__default[
|
|
11666
|
+
const type = message.severity === 2 ? kleur__default["default"].red("error") : kleur__default["default"].yellow("warning");
|
|
11667
|
+
const msg = `${kleur__default["default"].bold(message.message.replace(/([^ ])\.$/, "$1"))}`;
|
|
11668
|
+
const ruleId = kleur__default["default"].dim(`(${message.ruleId})`);
|
|
11406
11669
|
const filePath = formatFilePath(parentResult.filePath, message.line, message.column);
|
|
11407
11670
|
const sourceCode = parentResult.source;
|
|
11408
11671
|
/* istanbul ignore next: safety check from original implementation */
|
|
@@ -11423,7 +11686,7 @@ function formatMessage(message, parentResult, options) {
|
|
|
11423
11686
|
}, { highlightCode: false }));
|
|
11424
11687
|
}
|
|
11425
11688
|
if (options.showLink && message.ruleUrl) {
|
|
11426
|
-
result.push(`${kleur__default[
|
|
11689
|
+
result.push(`${kleur__default["default"].bold("Details:")} ${message.ruleUrl}`);
|
|
11427
11690
|
}
|
|
11428
11691
|
return result.join("\n");
|
|
11429
11692
|
}
|
|
@@ -11442,10 +11705,10 @@ function formatSummary(errors, warnings) {
|
|
|
11442
11705
|
if (warnings > 0) {
|
|
11443
11706
|
summary.push(`${warnings} ${pluralize("warning", warnings)}`);
|
|
11444
11707
|
}
|
|
11445
|
-
return kleur__default[
|
|
11708
|
+
return kleur__default["default"][summaryColor]().bold(`${summary.join(" and ")} found.`);
|
|
11446
11709
|
}
|
|
11447
11710
|
function codeframe(results, options) {
|
|
11448
|
-
const merged =
|
|
11711
|
+
const merged = { ...defaults, ...options };
|
|
11449
11712
|
let errors = 0;
|
|
11450
11713
|
let warnings = 0;
|
|
11451
11714
|
const resultsWithMessages = results.filter((result) => result.messages.length > 0);
|
|
@@ -11483,10 +11746,14 @@ function linkSummary(results) {
|
|
|
11483
11746
|
return "";
|
|
11484
11747
|
}
|
|
11485
11748
|
const lines = unique.map((url) => ` ${url}\n`);
|
|
11486
|
-
return `\n${kleur__default[
|
|
11749
|
+
return `\n${kleur__default["default"].bold("More information")}:\n${lines.join("")}\n`;
|
|
11487
11750
|
}
|
|
11488
11751
|
function stylish(results) {
|
|
11489
|
-
const errors = stylishImpl__default[
|
|
11752
|
+
const errors = stylishImpl__default["default"](results.map((it) => ({
|
|
11753
|
+
...it,
|
|
11754
|
+
fixableErrorCount: 0,
|
|
11755
|
+
fixableWarningCount: 0,
|
|
11756
|
+
})));
|
|
11490
11757
|
const links = linkSummary(results);
|
|
11491
11758
|
return `${errors}${links}`;
|
|
11492
11759
|
}
|