-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Description
Lexical version: 0.40.0
Steps To Reproduce
- Open a Lexical editor with the Markdown plugin enabled.
- Type a word, apply Bold formatting, then type a space (while still in bold mode).
- Type a second word and apply Italic formatting to it (while still in bold mode).
- Type a space and toggle Bold off, then continue typing in Italic only.
- Export the content using $convertToMarkdownString.
Link to code example: https://stackblitz.com/edit/github-afacmpsw also reproducible on https://playground.lexical.dev/
Note: Using the stackblitz will show you a preview using CommonMark markdown
Actual vs. Expected Comparison
| User Input Pattern | Current Output (Broken) | Expected Output (Compliant) | Parser Result |
|---|---|---|---|
| bold + space | **bold **unbold | bold unbold | Renders as literal ** |
| bold → italic | **bold **italic | bold italic | Fails to close Bold properly |
| bold → boldeditalic → italic | **bold *boldeditalic ***italic | bold boldeditalic italic | Fails to close Bold and Bolded Italic properly |
The current behavior
The Markdown exporter attempts to "optimize" delimiters and capture whitespace inside the formatting nodes. This results in:
Space Capturing: Spaces are escaped as   inside delimiters (e.g., **text **).
Illegal Overlapping: Delimiters are stacked in a way that violates the "nesting" rules of Markdown when formats are toggled at different intervals, resulting in output like:
**bold *boldeditalic ***italic
Because the closing delimiters (_**) are preceded by a space, they fail the Right-Flanking delimiter run requirement of the CommonMark spec. Consequently, most Markdown parsers (like react-markdown or GitHub) render the closing markers as literal text rather than formatting.
The expected behavior
The exporter should prioritize "Hugging" (placing delimiters immediately adjacent to non-whitespace characters) over "Optimization" (keeping tags open across nodes).
Spaces should be "hoisted" outside of formatting delimiters: **bold** ***boldeditalic*** *italic*.
Every text node should ideally close and reopen its own delimiters if it contains trailing whitespace, ensuring all markers satisfy flanking rules.
Impact of fix
Severity: High for apps using Lexical as a headless CMS or Chat editor where the recipient uses a standard Markdown renderer.
Frequency: Happens every time a user types a space before toggling a format off—a very common typing pattern.
Benefit: Anyone using Lexical to generate Markdown for external consumption. Currently, Lexical produces "valid Lexical Markdown" that is "invalid CommonMark," creating a significant break in the ecosystem's interoperability.