fix(parser): close an open <p> for li, dd, dt, listing, plaintext and xmp

YB
14d ago
+54-2

Follow-up to #2493. In the WHATWG "in body" insertion mode, the li, dd, dt, listing, plaintext and xmp start tags also close a <p> in button scope (spec). They were still missing from openImpliesClose, so htmlparser2 nested them inside the open <p>.

This matters most for list items that contain a paragraph:

InputBeforeAfter (matches browsers)
<ul><li><p>a<li>b</ul><ul><li><p>a<li>b</li></p></li></ul><ul><li><p>a</p></li><li>b</li></ul>
<dl><dt><p>a<dd>b</dl><dl><dt><p>a<dd>b</dd></p></dt></dl><dl><dt><p>a</p></dt><dd>b</dd></dl>
<p>a<xmp>b</xmp><p>a<xmp>b</xmp></p><p>a</p><xmp>b</xmp>

Changes:

  • add p to the sets that li, dd and dt close, so a <p> on top of the stack is closed first and the previous list item is closed after it;
  • add listing, plaintext and xmp with pTag.

Unchanged: <div><li> and <p><button><li> (the <p> is not in button scope there).

Tests: added cases to src/Parser.spec.ts that check the order of open and close events.

The suite was run:

  • npx vitest run: 208 passed. With src/Parser.ts reverted to main, the 7 new tests fail with assertion errors such as expected [ '<p>', '<li>', '</li>', '</p>' ] to deeply equal [ '<p>', '</p>', '<li>', '</li>' ].
  • npm run lint:es, npm run lint:ts, npm run build: pass.
  • biome check src package.json README.md biome.json: clean. A plain biome check . processed no files in the local checkout because its path contains a dot-directory, which the !**/.* include pattern excludes.

Not run: tests in downstream packages (domhandler, cheerio).

Summary by CodeRabbit

  • Bug Fixes
    • Improved HTML parsing so open paragraphs close automatically before <li>, <dd>, <dt>, <listing>, <plaintext>, and <xmp> elements.
    • When a paragraph closes before a new list or description item, the preceding <li> or <dt> is now closed first, keeping tag events in the expected order.