html-validate 8.25.1 → 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 CHANGED
@@ -5059,6 +5059,27 @@ class CloseAttr extends Rule {
5059
5059
  }
5060
5060
  }
5061
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
+ }
5062
5083
  class CloseOrder extends Rule {
5063
5084
  documentation() {
5064
5085
  return {
@@ -5067,15 +5088,28 @@ class CloseOrder extends Rule {
5067
5088
  };
5068
5089
  }
5069
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
+ });
5070
5109
  this.on("tag:end", (event) => {
5071
5110
  const current = event.target;
5072
5111
  const active = event.previous;
5073
5112
  if (!current) {
5074
- this.report(
5075
- null,
5076
- `Missing close-tag, expected '</${active.tagName}>' but document ended before it was found.`,
5077
- event.location
5078
- );
5079
5113
  return;
5080
5114
  }
5081
5115
  if (current.voidElement) {
@@ -5092,15 +5126,32 @@ class CloseOrder extends Rule {
5092
5126
  offset: current.location.offset,
5093
5127
  size: current.tagName.length + 1
5094
5128
  };
5095
- this.report(null, "Unexpected close-tag, expected opening tag.", location);
5129
+ this.report(null, `Stray end tag '</${current.tagName}>'`, location);
5130
+ return;
5131
+ }
5132
+ if (current.tagName === active.tagName) {
5096
5133
  return;
5097
5134
  }
5098
- if (current.tagName !== active.tagName) {
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
+ }
5099
5147
  this.report(
5100
5148
  null,
5101
- `Mismatched close-tag, expected '</${active.tagName}>' but found '</${current.tagName}>'.`,
5149
+ `End tag '</${current.tagName}>' seen but there were open elements`,
5102
5150
  current.location
5103
5151
  );
5152
+ reported.add(ancestor.unique);
5153
+ } else {
5154
+ this.report(null, `Stray end tag '</${current.tagName}>'`, current.location);
5104
5155
  }
5105
5156
  });
5106
5157
  }
@@ -11656,8 +11707,9 @@ class Parser {
11656
11707
  node.closed = NodeClosed.EndTag;
11657
11708
  }
11658
11709
  this.closeElement(source, node, active, endToken.location);
11710
+ const mismatched = node.tagName !== active.tagName;
11659
11711
  const voidClosed = !isStartTag && node.voidElement;
11660
- if (!voidClosed) {
11712
+ if (!voidClosed && !mismatched) {
11661
11713
  this.dom.popActive();
11662
11714
  }
11663
11715
  } else if (isForeign) {
@@ -11675,6 +11727,9 @@ class Parser {
11675
11727
  location
11676
11728
  };
11677
11729
  this.trigger("tag:end", event);
11730
+ if (node && node.tagName !== active.tagName && active.closed !== NodeClosed.ImplicitClosed) {
11731
+ return;
11732
+ }
11678
11733
  if (!active.isRootElement()) {
11679
11734
  this.trigger("element:ready", {
11680
11735
  target: active,