diff --git a/src/Parser.spec.ts b/src/Parser.spec.ts index 87a2fd2b..e159a100 100644 --- a/src/Parser.spec.ts +++ b/src/Parser.spec.ts @@ -334,6 +334,35 @@ describe("API", () => { expect(events).toStrictEqual(["×", "bar", "end"]); }); + it("should parse chunks queued after reset in a callback", () => { + const texts: string[] = []; + const parser = new Parser({ + ontext(text) { + texts.push(text); + if (text === "first") parser.reset(); + else if (text === "second") parser.pause(); + }, + }); + parser.write("first"); + parser.write("second"); + parser.write("third"); + parser.resume(); + parser.end(); + expect(texts).toStrictEqual(["first", "second", "third"]); + }); + + it("should stop closing elements after reset in onclosetag", () => { + const closed: string[] = []; + const parser = new Parser({ + onclosetag(name) { + closed.push(name); + if (name === "c") parser.reset(); + }, + }); + parser.write(""); + expect(closed).toStrictEqual(["c"]); + }); + it("should support custom tokenizer", () => { class CustomTokenizer extends Tokenizer {} diff --git a/src/Parser.ts b/src/Parser.ts index 84581c4f..58fff47e 100644 --- a/src/Parser.ts +++ b/src/Parser.ts @@ -553,10 +553,11 @@ export class Parser implements Callbacks { if (!this.isVoidElement(name)) { const pos = this.stack.lastIndexOf(name); if (pos !== -1) { - for (let index = this.stack.length - 1; index > pos; index--) { + // A callback may have reset the stack. + while (this.stack.length > pos + 1) { this.popElement(true); } - this.popElement(false); + if (this.stack.length === pos + 1) this.popElement(false); } else if (this.htmlMode && name === "p") { // Implicit open before close this.emitOpenTag("p"); @@ -790,8 +791,9 @@ export class Parser implements Callbacks { this.buffers.push(chunk); if (this.tokenizer.running) { - this.tokenizer.write(chunk); + // Count the chunk first, as a callback may call `reset()`. this.writeIndex++; + this.tokenizer.write(chunk); } }