html-validate 8.25.0 → 8.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/core.js +138 -21
- package/dist/cjs/core.js.map +1 -1
- package/dist/cjs/tsdoc-metadata.json +1 -1
- package/dist/es/core.js +138 -21
- package/dist/es/core.js.map +1 -1
- package/dist/tsdoc-metadata.json +1 -1
- package/dist/types/browser.d.ts +7 -1
- package/dist/types/index.d.ts +7 -1
- package/package.json +2 -162
package/dist/cjs/core.js
CHANGED
|
@@ -1753,7 +1753,31 @@ class DOMNode {
|
|
|
1753
1753
|
return text;
|
|
1754
1754
|
}
|
|
1755
1755
|
append(node) {
|
|
1756
|
+
const oldParent = node._setParent(this);
|
|
1757
|
+
if (oldParent && this.isSameNode(oldParent)) {
|
|
1758
|
+
return;
|
|
1759
|
+
}
|
|
1756
1760
|
this.childNodes.push(node);
|
|
1761
|
+
if (oldParent) {
|
|
1762
|
+
oldParent._removeChild(node);
|
|
1763
|
+
}
|
|
1764
|
+
}
|
|
1765
|
+
/**
|
|
1766
|
+
* Insert a node before a reference node.
|
|
1767
|
+
*
|
|
1768
|
+
* @internal
|
|
1769
|
+
*/
|
|
1770
|
+
insertBefore(node, reference) {
|
|
1771
|
+
const index = reference ? this.childNodes.findIndex((it) => it.isSameNode(reference)) : -1;
|
|
1772
|
+
if (index >= 0) {
|
|
1773
|
+
this.childNodes.splice(index, 0, node);
|
|
1774
|
+
} else {
|
|
1775
|
+
this.childNodes.push(node);
|
|
1776
|
+
}
|
|
1777
|
+
const oldParent = node._setParent(this);
|
|
1778
|
+
if (oldParent) {
|
|
1779
|
+
oldParent._removeChild(node);
|
|
1780
|
+
}
|
|
1757
1781
|
}
|
|
1758
1782
|
isRootElement() {
|
|
1759
1783
|
return this.nodeType === NodeType.DOCUMENT_NODE;
|
|
@@ -1780,6 +1804,14 @@ class DOMNode {
|
|
|
1780
1804
|
get lastChild() {
|
|
1781
1805
|
return this.childNodes[this.childNodes.length - 1] || null;
|
|
1782
1806
|
}
|
|
1807
|
+
/**
|
|
1808
|
+
* @internal
|
|
1809
|
+
*/
|
|
1810
|
+
removeChild(node) {
|
|
1811
|
+
this._removeChild(node);
|
|
1812
|
+
node._setParent(null);
|
|
1813
|
+
return node;
|
|
1814
|
+
}
|
|
1783
1815
|
/**
|
|
1784
1816
|
* Block a rule for this node.
|
|
1785
1817
|
*
|
|
@@ -1854,6 +1886,22 @@ class DOMNode {
|
|
|
1854
1886
|
generateSelector() {
|
|
1855
1887
|
return null;
|
|
1856
1888
|
}
|
|
1889
|
+
/**
|
|
1890
|
+
* @internal
|
|
1891
|
+
*
|
|
1892
|
+
* @returns Old parent, if set.
|
|
1893
|
+
*/
|
|
1894
|
+
_setParent(_node) {
|
|
1895
|
+
return null;
|
|
1896
|
+
}
|
|
1897
|
+
_removeChild(node) {
|
|
1898
|
+
const index = this.childNodes.findIndex((it) => it.isSameNode(node));
|
|
1899
|
+
if (index >= 0) {
|
|
1900
|
+
this.childNodes.splice(index, 1);
|
|
1901
|
+
} else {
|
|
1902
|
+
throw new Error("DOMException: _removeChild(..) could not find child to remove");
|
|
1903
|
+
}
|
|
1904
|
+
}
|
|
1857
1905
|
}
|
|
1858
1906
|
|
|
1859
1907
|
function parse(text, baseLocation) {
|
|
@@ -2373,7 +2421,7 @@ class HtmlElement extends DOMNode {
|
|
|
2373
2421
|
throw new Error(`The tag name provided ("${tagName}") is not a valid name`);
|
|
2374
2422
|
}
|
|
2375
2423
|
this.tagName = tagName ?? "#document";
|
|
2376
|
-
this.
|
|
2424
|
+
this._parent = null;
|
|
2377
2425
|
this.attr = {};
|
|
2378
2426
|
this.metaElement = meta ?? null;
|
|
2379
2427
|
this.closed = closed;
|
|
@@ -2382,7 +2430,7 @@ class HtmlElement extends DOMNode {
|
|
|
2382
2430
|
this.annotation = null;
|
|
2383
2431
|
this._adapter = createAdapter(this);
|
|
2384
2432
|
if (parent) {
|
|
2385
|
-
parent.
|
|
2433
|
+
parent.append(this);
|
|
2386
2434
|
let cur = parent;
|
|
2387
2435
|
while (cur.parent) {
|
|
2388
2436
|
this.depth++;
|
|
@@ -2606,6 +2654,9 @@ class HtmlElement extends DOMNode {
|
|
|
2606
2654
|
get meta() {
|
|
2607
2655
|
return this.metaElement;
|
|
2608
2656
|
}
|
|
2657
|
+
get parent() {
|
|
2658
|
+
return this._parent;
|
|
2659
|
+
}
|
|
2609
2660
|
/**
|
|
2610
2661
|
* Get current role for this element (explicit with `role` attribute or mapped
|
|
2611
2662
|
* with implicit role).
|
|
@@ -2866,6 +2917,14 @@ class HtmlElement extends DOMNode {
|
|
|
2866
2917
|
}
|
|
2867
2918
|
return visit(this);
|
|
2868
2919
|
}
|
|
2920
|
+
/**
|
|
2921
|
+
* @internal
|
|
2922
|
+
*/
|
|
2923
|
+
_setParent(node) {
|
|
2924
|
+
const oldParent = this._parent;
|
|
2925
|
+
this._parent = node instanceof HtmlElement ? node : null;
|
|
2926
|
+
return oldParent;
|
|
2927
|
+
}
|
|
2869
2928
|
}
|
|
2870
2929
|
function isClosed(endToken, meta) {
|
|
2871
2930
|
let closed = 0 /* Open */;
|
|
@@ -2890,20 +2949,22 @@ function dumpTree(root) {
|
|
|
2890
2949
|
}
|
|
2891
2950
|
return output;
|
|
2892
2951
|
}
|
|
2893
|
-
function writeNode(node, level, sibling) {
|
|
2952
|
+
function writeNode(node, level, indent, sibling) {
|
|
2953
|
+
const numSiblings = node.parent ? node.parent.childElements.length : 0;
|
|
2954
|
+
const lastSibling = sibling === numSiblings - 1;
|
|
2894
2955
|
if (node.parent) {
|
|
2895
|
-
const
|
|
2896
|
-
|
|
2897
|
-
const b = sibling < node.parent.childElements.length - 1 ? "\u251C" : "\u2514";
|
|
2898
|
-
lines.push(`${indent}${b}\u2500${l} ${node.tagName}${decoration(node)}`);
|
|
2956
|
+
const b = lastSibling ? "\u2514" : "\u251C";
|
|
2957
|
+
lines.push(`${indent}${b}\u2500\u2500 ${node.tagName}${decoration(node)}`);
|
|
2899
2958
|
} else {
|
|
2900
2959
|
lines.push("(root)");
|
|
2901
2960
|
}
|
|
2902
2961
|
node.childElements.forEach((child, index) => {
|
|
2903
|
-
|
|
2962
|
+
const s = lastSibling ? " " : "\u2502";
|
|
2963
|
+
const i = level > 0 ? `${indent}${s} ` : "";
|
|
2964
|
+
writeNode(child, level + 1, i, index);
|
|
2904
2965
|
});
|
|
2905
2966
|
}
|
|
2906
|
-
writeNode(root, 0, 0);
|
|
2967
|
+
writeNode(root, 0, "", 0);
|
|
2907
2968
|
return lines;
|
|
2908
2969
|
}
|
|
2909
2970
|
|
|
@@ -4998,6 +5059,27 @@ class CloseAttr extends Rule {
|
|
|
4998
5059
|
}
|
|
4999
5060
|
}
|
|
5000
5061
|
|
|
5062
|
+
function* ancestors(node) {
|
|
5063
|
+
if (!node) {
|
|
5064
|
+
return;
|
|
5065
|
+
}
|
|
5066
|
+
let ancestor = node;
|
|
5067
|
+
while (ancestor && !ancestor.isRootElement()) {
|
|
5068
|
+
yield ancestor;
|
|
5069
|
+
ancestor = ancestor.parent;
|
|
5070
|
+
}
|
|
5071
|
+
if (ancestor) {
|
|
5072
|
+
yield ancestor;
|
|
5073
|
+
}
|
|
5074
|
+
}
|
|
5075
|
+
function findAncestor(node, predicate) {
|
|
5076
|
+
for (const ancestor of ancestors(node)) {
|
|
5077
|
+
if (predicate(ancestor)) {
|
|
5078
|
+
return ancestor;
|
|
5079
|
+
}
|
|
5080
|
+
}
|
|
5081
|
+
return null;
|
|
5082
|
+
}
|
|
5001
5083
|
class CloseOrder extends Rule {
|
|
5002
5084
|
documentation() {
|
|
5003
5085
|
return {
|
|
@@ -5006,15 +5088,28 @@ class CloseOrder extends Rule {
|
|
|
5006
5088
|
};
|
|
5007
5089
|
}
|
|
5008
5090
|
setup() {
|
|
5091
|
+
let reported;
|
|
5092
|
+
this.on("parse:begin", () => {
|
|
5093
|
+
reported = /* @__PURE__ */ new Set();
|
|
5094
|
+
});
|
|
5095
|
+
this.on("tag:end", (event) => {
|
|
5096
|
+
const current = event.target;
|
|
5097
|
+
const active = event.previous;
|
|
5098
|
+
if (current) {
|
|
5099
|
+
return;
|
|
5100
|
+
}
|
|
5101
|
+
for (const ancestor of ancestors(active)) {
|
|
5102
|
+
if (ancestor.isRootElement() || reported.has(ancestor.unique)) {
|
|
5103
|
+
continue;
|
|
5104
|
+
}
|
|
5105
|
+
this.report(ancestor, `Unclosed element '<${ancestor.tagName}>'`, ancestor.location);
|
|
5106
|
+
reported.add(ancestor.unique);
|
|
5107
|
+
}
|
|
5108
|
+
});
|
|
5009
5109
|
this.on("tag:end", (event) => {
|
|
5010
5110
|
const current = event.target;
|
|
5011
5111
|
const active = event.previous;
|
|
5012
5112
|
if (!current) {
|
|
5013
|
-
this.report(
|
|
5014
|
-
null,
|
|
5015
|
-
`Missing close-tag, expected '</${active.tagName}>' but document ended before it was found.`,
|
|
5016
|
-
event.location
|
|
5017
|
-
);
|
|
5018
5113
|
return;
|
|
5019
5114
|
}
|
|
5020
5115
|
if (current.voidElement) {
|
|
@@ -5031,15 +5126,32 @@ class CloseOrder extends Rule {
|
|
|
5031
5126
|
offset: current.location.offset,
|
|
5032
5127
|
size: current.tagName.length + 1
|
|
5033
5128
|
};
|
|
5034
|
-
this.report(null,
|
|
5129
|
+
this.report(null, `Stray end tag '</${current.tagName}>'`, location);
|
|
5130
|
+
return;
|
|
5131
|
+
}
|
|
5132
|
+
if (current.tagName === active.tagName) {
|
|
5035
5133
|
return;
|
|
5036
5134
|
}
|
|
5037
|
-
|
|
5135
|
+
const ancestor = findAncestor(active.parent, (node) => node.is(current.tagName));
|
|
5136
|
+
if (ancestor && !ancestor.isRootElement()) {
|
|
5137
|
+
for (const element of ancestors(active)) {
|
|
5138
|
+
if (ancestor.isSameNode(element)) {
|
|
5139
|
+
break;
|
|
5140
|
+
}
|
|
5141
|
+
if (reported.has(element.unique)) {
|
|
5142
|
+
continue;
|
|
5143
|
+
}
|
|
5144
|
+
this.report(element, `Unclosed element '<${element.tagName}>'`, element.location);
|
|
5145
|
+
reported.add(element.unique);
|
|
5146
|
+
}
|
|
5038
5147
|
this.report(
|
|
5039
5148
|
null,
|
|
5040
|
-
`
|
|
5149
|
+
`End tag '</${current.tagName}>' seen but there were open elements`,
|
|
5041
5150
|
current.location
|
|
5042
5151
|
);
|
|
5152
|
+
reported.add(ancestor.unique);
|
|
5153
|
+
} else {
|
|
5154
|
+
this.report(null, `Stray end tag '</${current.tagName}>'`, current.location);
|
|
5043
5155
|
}
|
|
5044
5156
|
});
|
|
5045
5157
|
}
|
|
@@ -7262,8 +7374,9 @@ Omitted end tags can be ambigious for humans to read and many editors have troub
|
|
|
7262
7374
|
if (closed.closed !== NodeClosed.ImplicitClosed) {
|
|
7263
7375
|
return;
|
|
7264
7376
|
}
|
|
7265
|
-
const
|
|
7266
|
-
const
|
|
7377
|
+
const parent = closed.parent;
|
|
7378
|
+
const closedByParent = parent && parent.tagName === by.tagName;
|
|
7379
|
+
const closedByDocument = closedByParent && parent.isRootElement();
|
|
7267
7380
|
const sameTag = closed.tagName === by.tagName;
|
|
7268
7381
|
if (closedByDocument) {
|
|
7269
7382
|
this.report(
|
|
@@ -11594,8 +11707,9 @@ class Parser {
|
|
|
11594
11707
|
node.closed = NodeClosed.EndTag;
|
|
11595
11708
|
}
|
|
11596
11709
|
this.closeElement(source, node, active, endToken.location);
|
|
11710
|
+
const mismatched = node.tagName !== active.tagName;
|
|
11597
11711
|
const voidClosed = !isStartTag && node.voidElement;
|
|
11598
|
-
if (!voidClosed) {
|
|
11712
|
+
if (!voidClosed && !mismatched) {
|
|
11599
11713
|
this.dom.popActive();
|
|
11600
11714
|
}
|
|
11601
11715
|
} else if (isForeign) {
|
|
@@ -11613,6 +11727,9 @@ class Parser {
|
|
|
11613
11727
|
location
|
|
11614
11728
|
};
|
|
11615
11729
|
this.trigger("tag:end", event);
|
|
11730
|
+
if (node && node.tagName !== active.tagName && active.closed !== NodeClosed.ImplicitClosed) {
|
|
11731
|
+
return;
|
|
11732
|
+
}
|
|
11616
11733
|
if (!active.isRootElement()) {
|
|
11617
11734
|
this.trigger("element:ready", {
|
|
11618
11735
|
target: active,
|
|
@@ -12895,7 +13012,7 @@ class HtmlValidate {
|
|
|
12895
13012
|
}
|
|
12896
13013
|
|
|
12897
13014
|
const name = "html-validate";
|
|
12898
|
-
const version = "8.25.
|
|
13015
|
+
const version = "8.25.1";
|
|
12899
13016
|
const bugs = "https://gitlab.com/html-validate/html-validate/issues/new";
|
|
12900
13017
|
|
|
12901
13018
|
function definePlugin(plugin) {
|