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.
@@ -5,7 +5,7 @@
5
5
  "toolPackages": [
6
6
  {
7
7
  "packageName": "@microsoft/api-extractor",
8
- "packageVersion": "7.47.11"
8
+ "packageVersion": "7.48.0"
9
9
  }
10
10
  ]
11
11
  }
package/dist/es/core.js CHANGED
@@ -1743,7 +1743,31 @@ class DOMNode {
1743
1743
  return text;
1744
1744
  }
1745
1745
  append(node) {
1746
+ const oldParent = node._setParent(this);
1747
+ if (oldParent && this.isSameNode(oldParent)) {
1748
+ return;
1749
+ }
1746
1750
  this.childNodes.push(node);
1751
+ if (oldParent) {
1752
+ oldParent._removeChild(node);
1753
+ }
1754
+ }
1755
+ /**
1756
+ * Insert a node before a reference node.
1757
+ *
1758
+ * @internal
1759
+ */
1760
+ insertBefore(node, reference) {
1761
+ const index = reference ? this.childNodes.findIndex((it) => it.isSameNode(reference)) : -1;
1762
+ if (index >= 0) {
1763
+ this.childNodes.splice(index, 0, node);
1764
+ } else {
1765
+ this.childNodes.push(node);
1766
+ }
1767
+ const oldParent = node._setParent(this);
1768
+ if (oldParent) {
1769
+ oldParent._removeChild(node);
1770
+ }
1747
1771
  }
1748
1772
  isRootElement() {
1749
1773
  return this.nodeType === NodeType.DOCUMENT_NODE;
@@ -1770,6 +1794,14 @@ class DOMNode {
1770
1794
  get lastChild() {
1771
1795
  return this.childNodes[this.childNodes.length - 1] || null;
1772
1796
  }
1797
+ /**
1798
+ * @internal
1799
+ */
1800
+ removeChild(node) {
1801
+ this._removeChild(node);
1802
+ node._setParent(null);
1803
+ return node;
1804
+ }
1773
1805
  /**
1774
1806
  * Block a rule for this node.
1775
1807
  *
@@ -1844,6 +1876,22 @@ class DOMNode {
1844
1876
  generateSelector() {
1845
1877
  return null;
1846
1878
  }
1879
+ /**
1880
+ * @internal
1881
+ *
1882
+ * @returns Old parent, if set.
1883
+ */
1884
+ _setParent(_node) {
1885
+ return null;
1886
+ }
1887
+ _removeChild(node) {
1888
+ const index = this.childNodes.findIndex((it) => it.isSameNode(node));
1889
+ if (index >= 0) {
1890
+ this.childNodes.splice(index, 1);
1891
+ } else {
1892
+ throw new Error("DOMException: _removeChild(..) could not find child to remove");
1893
+ }
1894
+ }
1847
1895
  }
1848
1896
 
1849
1897
  function parse(text, baseLocation) {
@@ -2363,7 +2411,7 @@ class HtmlElement extends DOMNode {
2363
2411
  throw new Error(`The tag name provided ("${tagName}") is not a valid name`);
2364
2412
  }
2365
2413
  this.tagName = tagName ?? "#document";
2366
- this.parent = parent ?? null;
2414
+ this._parent = null;
2367
2415
  this.attr = {};
2368
2416
  this.metaElement = meta ?? null;
2369
2417
  this.closed = closed;
@@ -2372,7 +2420,7 @@ class HtmlElement extends DOMNode {
2372
2420
  this.annotation = null;
2373
2421
  this._adapter = createAdapter(this);
2374
2422
  if (parent) {
2375
- parent.childNodes.push(this);
2423
+ parent.append(this);
2376
2424
  let cur = parent;
2377
2425
  while (cur.parent) {
2378
2426
  this.depth++;
@@ -2596,6 +2644,9 @@ class HtmlElement extends DOMNode {
2596
2644
  get meta() {
2597
2645
  return this.metaElement;
2598
2646
  }
2647
+ get parent() {
2648
+ return this._parent;
2649
+ }
2599
2650
  /**
2600
2651
  * Get current role for this element (explicit with `role` attribute or mapped
2601
2652
  * with implicit role).
@@ -2856,6 +2907,14 @@ class HtmlElement extends DOMNode {
2856
2907
  }
2857
2908
  return visit(this);
2858
2909
  }
2910
+ /**
2911
+ * @internal
2912
+ */
2913
+ _setParent(node) {
2914
+ const oldParent = this._parent;
2915
+ this._parent = node instanceof HtmlElement ? node : null;
2916
+ return oldParent;
2917
+ }
2859
2918
  }
2860
2919
  function isClosed(endToken, meta) {
2861
2920
  let closed = 0 /* Open */;
@@ -2880,20 +2939,22 @@ function dumpTree(root) {
2880
2939
  }
2881
2940
  return output;
2882
2941
  }
2883
- function writeNode(node, level, sibling) {
2942
+ function writeNode(node, level, indent, sibling) {
2943
+ const numSiblings = node.parent ? node.parent.childElements.length : 0;
2944
+ const lastSibling = sibling === numSiblings - 1;
2884
2945
  if (node.parent) {
2885
- const indent = " ".repeat(level - 1);
2886
- const l = node.childElements.length > 0 ? "\u252C" : "\u2500";
2887
- const b = sibling < node.parent.childElements.length - 1 ? "\u251C" : "\u2514";
2888
- lines.push(`${indent}${b}\u2500${l} ${node.tagName}${decoration(node)}`);
2946
+ const b = lastSibling ? "\u2514" : "\u251C";
2947
+ lines.push(`${indent}${b}\u2500\u2500 ${node.tagName}${decoration(node)}`);
2889
2948
  } else {
2890
2949
  lines.push("(root)");
2891
2950
  }
2892
2951
  node.childElements.forEach((child, index) => {
2893
- writeNode(child, level + 1, index);
2952
+ const s = lastSibling ? " " : "\u2502";
2953
+ const i = level > 0 ? `${indent}${s} ` : "";
2954
+ writeNode(child, level + 1, i, index);
2894
2955
  });
2895
2956
  }
2896
- writeNode(root, 0, 0);
2957
+ writeNode(root, 0, "", 0);
2897
2958
  return lines;
2898
2959
  }
2899
2960
 
@@ -4988,6 +5049,27 @@ class CloseAttr extends Rule {
4988
5049
  }
4989
5050
  }
4990
5051
 
5052
+ function* ancestors(node) {
5053
+ if (!node) {
5054
+ return;
5055
+ }
5056
+ let ancestor = node;
5057
+ while (ancestor && !ancestor.isRootElement()) {
5058
+ yield ancestor;
5059
+ ancestor = ancestor.parent;
5060
+ }
5061
+ if (ancestor) {
5062
+ yield ancestor;
5063
+ }
5064
+ }
5065
+ function findAncestor(node, predicate) {
5066
+ for (const ancestor of ancestors(node)) {
5067
+ if (predicate(ancestor)) {
5068
+ return ancestor;
5069
+ }
5070
+ }
5071
+ return null;
5072
+ }
4991
5073
  class CloseOrder extends Rule {
4992
5074
  documentation() {
4993
5075
  return {
@@ -4996,15 +5078,28 @@ class CloseOrder extends Rule {
4996
5078
  };
4997
5079
  }
4998
5080
  setup() {
5081
+ let reported;
5082
+ this.on("parse:begin", () => {
5083
+ reported = /* @__PURE__ */ new Set();
5084
+ });
5085
+ this.on("tag:end", (event) => {
5086
+ const current = event.target;
5087
+ const active = event.previous;
5088
+ if (current) {
5089
+ return;
5090
+ }
5091
+ for (const ancestor of ancestors(active)) {
5092
+ if (ancestor.isRootElement() || reported.has(ancestor.unique)) {
5093
+ continue;
5094
+ }
5095
+ this.report(ancestor, `Unclosed element '<${ancestor.tagName}>'`, ancestor.location);
5096
+ reported.add(ancestor.unique);
5097
+ }
5098
+ });
4999
5099
  this.on("tag:end", (event) => {
5000
5100
  const current = event.target;
5001
5101
  const active = event.previous;
5002
5102
  if (!current) {
5003
- this.report(
5004
- null,
5005
- `Missing close-tag, expected '</${active.tagName}>' but document ended before it was found.`,
5006
- event.location
5007
- );
5008
5103
  return;
5009
5104
  }
5010
5105
  if (current.voidElement) {
@@ -5021,15 +5116,32 @@ class CloseOrder extends Rule {
5021
5116
  offset: current.location.offset,
5022
5117
  size: current.tagName.length + 1
5023
5118
  };
5024
- this.report(null, "Unexpected close-tag, expected opening tag.", location);
5119
+ this.report(null, `Stray end tag '</${current.tagName}>'`, location);
5120
+ return;
5121
+ }
5122
+ if (current.tagName === active.tagName) {
5025
5123
  return;
5026
5124
  }
5027
- if (current.tagName !== active.tagName) {
5125
+ const ancestor = findAncestor(active.parent, (node) => node.is(current.tagName));
5126
+ if (ancestor && !ancestor.isRootElement()) {
5127
+ for (const element of ancestors(active)) {
5128
+ if (ancestor.isSameNode(element)) {
5129
+ break;
5130
+ }
5131
+ if (reported.has(element.unique)) {
5132
+ continue;
5133
+ }
5134
+ this.report(element, `Unclosed element '<${element.tagName}>'`, element.location);
5135
+ reported.add(element.unique);
5136
+ }
5028
5137
  this.report(
5029
5138
  null,
5030
- `Mismatched close-tag, expected '</${active.tagName}>' but found '</${current.tagName}>'.`,
5139
+ `End tag '</${current.tagName}>' seen but there were open elements`,
5031
5140
  current.location
5032
5141
  );
5142
+ reported.add(ancestor.unique);
5143
+ } else {
5144
+ this.report(null, `Stray end tag '</${current.tagName}>'`, current.location);
5033
5145
  }
5034
5146
  });
5035
5147
  }
@@ -7252,8 +7364,9 @@ Omitted end tags can be ambigious for humans to read and many editors have troub
7252
7364
  if (closed.closed !== NodeClosed.ImplicitClosed) {
7253
7365
  return;
7254
7366
  }
7255
- const closedByParent = closed.parent && closed.parent.tagName === by.tagName;
7256
- const closedByDocument = closedByParent && closed.parent.isRootElement();
7367
+ const parent = closed.parent;
7368
+ const closedByParent = parent && parent.tagName === by.tagName;
7369
+ const closedByDocument = closedByParent && parent.isRootElement();
7257
7370
  const sameTag = closed.tagName === by.tagName;
7258
7371
  if (closedByDocument) {
7259
7372
  this.report(
@@ -11584,8 +11697,9 @@ class Parser {
11584
11697
  node.closed = NodeClosed.EndTag;
11585
11698
  }
11586
11699
  this.closeElement(source, node, active, endToken.location);
11700
+ const mismatched = node.tagName !== active.tagName;
11587
11701
  const voidClosed = !isStartTag && node.voidElement;
11588
- if (!voidClosed) {
11702
+ if (!voidClosed && !mismatched) {
11589
11703
  this.dom.popActive();
11590
11704
  }
11591
11705
  } else if (isForeign) {
@@ -11603,6 +11717,9 @@ class Parser {
11603
11717
  location
11604
11718
  };
11605
11719
  this.trigger("tag:end", event);
11720
+ if (node && node.tagName !== active.tagName && active.closed !== NodeClosed.ImplicitClosed) {
11721
+ return;
11722
+ }
11606
11723
  if (!active.isRootElement()) {
11607
11724
  this.trigger("element:ready", {
11608
11725
  target: active,
@@ -12885,7 +13002,7 @@ class HtmlValidate {
12885
13002
  }
12886
13003
 
12887
13004
  const name = "html-validate";
12888
- const version = "8.25.0";
13005
+ const version = "8.25.1";
12889
13006
  const bugs = "https://gitlab.com/html-validate/html-validate/issues/new";
12890
13007
 
12891
13008
  function definePlugin(plugin) {