Skip to content

feat(parser): add openImpliesClose option to disable implicit HTML changes#2462

Open
Gravirei wants to merge 4 commits into
fb55:masterfrom
Gravirei:fix/issue-2428-implicit-option
Open

feat(parser): add openImpliesClose option to disable implicit HTML changes#2462
Gravirei wants to merge 4 commits into
fb55:masterfrom
Gravirei:fix/issue-2428-implicit-option

Conversation

@Gravirei

@Gravirei Gravirei commented Jul 2, 2026

Copy link
Copy Markdown

Summary

Adds a new openImpliesClose option to ParserOptions that controls whether the parser performs implicit HTML tag open/close operations per the HTML5 spec. When set to false, the parser preserves the original document structure without adding or removing tags that weren't explicitly in the source.

This is useful for users who want to parse and serialize HTML without having the parser alter invalid or non-standard markup.

Changes

  • Added openImpliesClose option to ParserOptions (default: !xmlMode, i.e. true in HTML mode)
  • Guarded all implicit behavior behind this option:
    • Implicit close via the openImpliesClose map (e.g. <p> closing another open <p>)
    • Second <form> suppression
    • Implicit <p> open before </p> close
    • Implicit <br> open before </br> close
  • All existing tests continue to pass (backward compatible)

Related

Closes #2428

Summary by CodeRabbit

  • New Features
    • Added a new parser option to control HTML5-style implicit open/close behavior (openImpliesClose).
    • When enabled/disabled, implicit tag opening/closing behavior is applied accordingly in HTML mode (including nested paragraphs and line-break related cases).
  • Tests
    • Added new event test coverage for openImpliesClose: false to confirm implicit close/open sequences are suppressed.

@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 1ee1a85c-b2ba-48d0-8f87-a8dff1ebe070

📥 Commits

Reviewing files that changed from the base of the PR and between b5fb420 and ed5881a.

📒 Files selected for processing (1)
  • src/Parser.ts

📝 Walkthrough

Walkthrough

A new optional openImpliesClose property is added to ParserOptions, stored on Parser, and used to control implicit open/close behavior in tag emission and close handling. Tests now cover the disabled behavior path.

Changes

openImpliesClose Option

Layer / File(s) Summary
Option declaration and initialization
src/Parser.ts
ParserOptions gains openImpliesClose?: boolean, and Parser adds internal state initialized from options.openImpliesClose ?? this.htmlMode and this.htmlMode && (options.openImpliesClose ?? this.htmlMode).
Gate implicit open/close logic
src/Parser.ts
emitOpenTag now uses impliesCloseMap and requires impliesCloseEnabled for form suppression and implied closes; onclosetag also requires the same flag for the </p> and </br> implicit handling.
Disabled behavior tests
src/Parser.events.spec.ts
New event tests verify that openImpliesClose: false blocks the implicit close/open behavior for nested <p>, </p>, and </br> cases.

Estimated code review effort: 2 (Simple) | ~10 minutes

Poem

A bunny hopped through tags and keys,
With open-close now set to please.
When flags say “no,” the parser learns,
And every hidden tag return.
Hop hop—behave, HTML breezes! 🐇

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: adding an openImpliesClose parser option to disable implicit HTML tag behavior.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@Gravirei Gravirei marked this pull request as ready for review July 2, 2026 02:14
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Credits must be used to enable repository wide code reviews.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/Parser.ts (1)

415-506: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

No test appears to cover openImpliesClose: false.

The existing "Implicit open p and br tags" and "Implicit close tags" cases in src/Parser.events.spec.ts run with defaults, so all four newly gated branches (form suppression, impliesClose map, </p> implicit open, </br> implicit open) are only exercised in the enabled state. Adding a case with { openImpliesClose: false } asserting the preserved structure would lock in the new contract.

Do you want me to draft these test cases?

#!/bin/bash
# Confirm no existing test references the new option.
rg -nP --type=ts '\bopenImpliesClose\b' -g '*spec*' -g '*test*'
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/Parser.ts` around lines 415 - 506, The parser branches in emitOpenTag and
onclosetag are only tested with openImpliesClose enabled, so add coverage in
Parser.events.spec.ts for the false case. Create a test that instantiates the
parser with { openImpliesClose: false } and verifies the tag stack and emitted
events stay unchanged for the affected paths (form suppression, implied-close
mapping, and the implicit p/br close handling). Use the existing Parser,
emitOpenTag, and onclosetag behavior as the reference points when asserting the
preserved structure.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/Parser.ts`:
- Around line 425-430: Rename the module-level Map currently called
openImpliesClose in Parser.ts to a clearer internal name such as
impliesCloseMap, and update the lookup in the Parser logic that uses this map so
it no longer reads ambiguously alongside the public this.openImpliesClose field.
Keep the public option/property name unchanged, and adjust any related
references or imports in the Parser class to use the new map identifier
consistently.

---

Outside diff comments:
In `@src/Parser.ts`:
- Around line 415-506: The parser branches in emitOpenTag and onclosetag are
only tested with openImpliesClose enabled, so add coverage in
Parser.events.spec.ts for the false case. Create a test that instantiates the
parser with { openImpliesClose: false } and verifies the tag stack and emitted
events stay unchanged for the affected paths (form suppression, implied-close
mapping, and the implicit p/br close handling). Use the existing Parser,
emitOpenTag, and onclosetag behavior as the reference points when asserting the
preserved structure.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 35cf4807-bb4d-4fc7-b2e5-e7b9b943ea36

📥 Commits

Reviewing files that changed from the base of the PR and between 2172806 and 5a7555e.

📒 Files selected for processing (1)
  • src/Parser.ts

Comment thread src/Parser.ts Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/Parser.ts (1)

425-436: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Repeated this.htmlMode && this.openImpliesClose gate — extract to a single field.

The compound condition appears four times (Lines 425, 430, 496, 501). Since both fields are readonly and set once in the constructor, compute the combined flag once to reduce duplication and make future changes to the gating logic less error-prone.

♻️ Proposed refactor
     private readonly htmlMode: boolean;
     private readonly openImpliesClose: boolean;
+    private readonly impliesCloseEnabled: boolean;
     private readonly tokenizer: Tokenizer;
...
         this.openImpliesClose = options.openImpliesClose ?? this.htmlMode;
+        this.impliesCloseEnabled = this.htmlMode && this.openImpliesClose;

Then replace each this.htmlMode && this.openImpliesClose occurrence with this.impliesCloseEnabled.

Note: since this.openImpliesClose already defaults to this.htmlMode when unset, the extra this.htmlMode && guard only changes behavior when a caller explicitly sets openImpliesClose: true together with xmlMode: true — in that case the option silently has no effect. Worth confirming this is intentional and documenting it on the ParserOptions.openImpliesClose JSDoc.

Also applies to: 496-506

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/Parser.ts` around lines 425 - 436, The repeated html/open-implies-close
gating in Parser should be centralized into a single computed field. Add a
readonly parser flag (for example, `impliesCloseEnabled`) in `Parser`’s
constructor and use it everywhere the compound `this.htmlMode &&
this.openImpliesClose` check appears, including the logic around the form guard
and the implied-close map handling. Also review `ParserOptions.openImpliesClose`
documentation to note that the extra HTML-mode gate makes the option ineffective
in XML mode when explicitly enabled.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/Parser.ts`:
- Around line 425-436: The repeated html/open-implies-close gating in Parser
should be centralized into a single computed field. Add a readonly parser flag
(for example, `impliesCloseEnabled`) in `Parser`’s constructor and use it
everywhere the compound `this.htmlMode && this.openImpliesClose` check appears,
including the logic around the form guard and the implied-close map handling.
Also review `ParserOptions.openImpliesClose` documentation to note that the
extra HTML-mode gate makes the option ineffective in XML mode when explicitly
enabled.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 14fb5a0d-eacf-45c4-a8d8-dd5633af758c

📥 Commits

Reviewing files that changed from the base of the PR and between 5a7555e and 8405766.

⛔ Files ignored due to path filters (1)
  • src/__snapshots__/Parser.events.spec.ts.snap is excluded by !**/*.snap
📒 Files selected for processing (2)
  • src/Parser.events.spec.ts
  • src/Parser.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/Parser.ts`:
- Around line 295-297: The Parser class has a redundant `openImpliesClose` field
that is only used to initialize `impliesCloseEnabled`. Simplify the constructor
and class state by removing the intermediate `openImpliesClose` property and
deriving `impliesCloseEnabled` directly from the incoming options where it is
currently assigned, keeping the existing HTML-mode gating logic intact.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: dba8fde3-9c83-4e33-a0bf-67739d4b47bb

📥 Commits

Reviewing files that changed from the base of the PR and between 8405766 and b5fb420.

📒 Files selected for processing (1)
  • src/Parser.ts

Comment thread src/Parser.ts Outdated
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.

Option to disable implicit HTML changes

1 participant