diff --git a/lib/saxes.js b/lib/saxes.js index 33f930d1..e671b126 100644 --- a/lib/saxes.js +++ b/lib/saxes.js @@ -1713,22 +1713,24 @@ ${XMLNS_NAMESPACE}.`); * @private */ closeTag() { - if (!this.tagName) { + const { tags, tagName } = this; + + // Our state after this will be S_TEXT, no matter what, and we can clear + // tagName now. + this.state = S_TEXT; + this.tagName = ""; + + if (!tagName) { this.fail("weird empty close tag."); this.textNode += ""; - this.state = S_TEXT; return; } - const { tags } = this; - // first make sure that the closing tag actually exists. - // will close everything, otherwise. - let t = tags.length; - const { tagName } = this; - const closeTo = tagName; - while (t--) { - const close = tags[t]; - if (close.name !== closeTo) { + let l = tags.length; + while (l-- > 0) { + const tag = this.tag = tags.pop(); + this.emitNode("onclosetag", tag); + if (tag.name !== tagName) { this.fail("unexpected close tag."); } else { @@ -1736,24 +1738,14 @@ ${XMLNS_NAMESPACE}.`); } } - if (t < 0) { - this.fail(`unmatched closing tag: ${tagName}.`); - this.textNode += ``; - this.state = S_TEXT; - return; - } - let s = this.tags.length; - while (s-- > t) { - const tag = this.tag = tags.pop(); - this.emitNode("onclosetag", tag); - } - if (t === 0) { + if (l === 0) { this.inRoot = false; this.closedRoot = true; } - this.tagName = this.attribValue = this.attribName = ""; - this.attribList = []; - this.state = S_TEXT; + else if (l < 0) { + this.fail(`unmatched closing tag: ${tagName}.`); + this.textNode += ``; + } } /** diff --git a/test/close-without-open.js b/test/close-without-open.js new file mode 100644 index 00000000..dd863883 --- /dev/null +++ b/test/close-without-open.js @@ -0,0 +1,16 @@ +"use strict"; + +const { test } = require("."); + +test({ + name: "close a tag that was not opened", + xml: "", + expect: [ + ["error", "undefined:1:7: unmatched closing tag: root."], + ["error", "undefined:1:7: document must contain a root element."], + ["text", ""], + ], + opt: { + xmlns: true, + }, +});