Skip to content

fix: parse closing tags without matching opening tags (#149)#244

Open
maruthang wants to merge 2 commits into
microsoft:mainfrom
maruthang:fix/issue-149-closing-tag-without-opening
Open

fix: parse closing tags without matching opening tags (#149)#244
maruthang wants to merge 2 commits into
microsoft:mainfrom
maruthang:fix/issue-149-closing-tag-without-opening

Conversation

@maruthang

Copy link
Copy Markdown
Contributor

Summary

Fixes #149

Bug: The HTML parser silently discarded closing tags that had no matching opening tag (e.g., </div> appearing without a prior <div>), making it impossible for consumers to detect orphan closing tags.

Root Cause: In htmlParser.ts, the EndTagClose branch only handled the case where a matching ancestor was found. When no match existed, the closing tag was simply ignored with no node created.

Fix: Added an else branch that creates an "orphan" node for unmatched closing tags. The node has endTagStart set but no startTagEnd, allowing consumers to detect orphan closing tags by checking node.endTagStart !== undefined && node.startTagEnd === undefined.

Changes

  • src/parser/htmlParser.ts: When no matching opening tag is found for a closing tag, create an orphan node with endTagStart set and add it as a child of the current node.
  • src/test/parser.test.ts: Updated existing MissingTags test expectations and added new regression tests for standalone closing tags, mismatched closing tags, and multiple orphan closing tags.

Testing

  • Added regression tests in src/test/parser.test.ts (Closing tag without opening tag (issue #149)) that verify orphan closing tags are properly represented in the parse tree.
  • Updated existing MissingTags test expectations to match the new behavior.
  • All 154 tests pass.

@aeschli

aeschli commented Jul 12, 2026

Copy link
Copy Markdown
Collaborator

I worry that this change will break existing clients. Maybe best to make this conditional.

Guard the creation of nodes for closing tags without a matching opening
tag behind a new createNodesForOrphanEndTags option so the default parse
output is unchanged (orphan end tags remain discarded). Thread the option
through HTMLParser.parse/parseDocument and expose it on the public
parseHTMLDocument API. Revert MissingTags expectations to the original
behavior and assert the issue microsoft#149 output only on the opt-in path.
@maruthang

Copy link
Copy Markdown
Contributor Author

Good point, thanks. I've reworked this so the new behavior is fully opt-in and the default parse output is unchanged — orphan closing tags (those with no matching opening tag) are still discarded by default, so existing clients see no difference.

Callers who want the orphan nodes can now pass an option: parseHTMLDocument(document, { createNodesForOrphanEndTags: true }) (also threaded through HTMLParser.parse/parseDocument). I reverted the MissingTags assertions to their original expectations and added tests covering both the default (discard) path and the opt-in path. Full suite passes locally (157/157) with lint clean.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Parsing of incomplete html doesn't catch a closing tag without an opening.

2 participants