Skip to content

[lexical-markdown] Bug Fix: Nest the format that ends first innermost on export - #12

Closed
Vedthakar wants to merge 1 commit into
mainfrom
fix/9443-bold-italic-export
Closed

Vedthakar wants to merge 1 commit into
mainfrom
fix/9443-bold-italic-export

Conversation

@Vedthakar

@Vedthakar Vedthakar commented Oct 23, 2026 •

Copy link
Copy Markdown
Owner

Description

This solves issue meta#9443: a bold and italic node followed by an italic node is exported as ***Disclaimer****: once accepted.*. CommonMark reads the four asterisks as closing both formats, so the italic on : once accepted. is lost and its asterisks show as text.

Cause. exportTextFormat() in MarkdownExport.ts opens a node's formats in transformer order (BOLD_STAR before ITALIC_STAR), so bold is always outside italic. When bold ends first, the closing loop has to close italic too and the next node reopens it, which runs the delimiters together. Swapping the transformer order only moves the problem to bold.

What this changes. Before the tags a node opens are added to unclosedTags, they're sorted by how many following text siblings keep each format. The format that continues furthest is opened first (outermost), and the one that ends first is innermost, so it closes alone. The sort is stable, so formats that end together keep the transformer order and those exports don't change.

The output for **~~Hello *world*~~**~~!~~ changes too. It used to close and reopen the strikethrough. It's now exported as ~~**Hello *world***!~~, which imports to the same nodes, so two existing mdAfterExport fixtures in LexicalMarkdown.test.ts are updated.

Relation to open PRs. meta#9420 changes the whitespace-only return at the end of exportTextFormat(), and meta#9432 changes checkHasFormat(). Neither touches the opening loop changed here, and the patches don't conflict. meta#9435 is import-side (importTextFormatTransformer.ts).

Closes meta#9443

Test plan

New packages/lexical-markdown/src/__tests__/unit/NestedEmphasisExport.test.ts. It exports nodes built directly, checks the exact Markdown, and checks that importing it gives the same nodes back:

  • bold+italic then italic → ***Disclaimer**: once accepted.* (the issue)
  • bold+italic+strikethrough, then italic+strikethrough, then strikethrough → ~~***a** b* c~~
  • bold+strikethrough, bold+italic+strikethrough, strikethrough → ~~**Hello *world***!~~
  • controls: bold+italic then bold → ***Disclaimer*: once accepted.**, and a single bold+italic node → ***a b***
  • the issue's Markdown round-trips unchanged

Before

On main (the new test file run against main's MarkdownExport.ts): Tests 4 failed | 2 passed (6). The two controls pass. These fail:

  • exports [["Disclaimer",["bold","italic"]],[": once accepted.",["italic"]]] as ***Disclaimer**: once accepted.* and imports it back
  • exports [["a",["bold","italic","strikethrough"]],[" b",["italic","strikethrough"]],[" c",["strikethrough"]]] as ~~***a** b* c~~ and imports it back
  • exports [["Hello ",["bold","strikethrough"]],["world",["bold","italic","strikethrough"]],["!",["strikethrough"]]] as ~~**Hello *world***!~~ and imports it back
  • round-trips the markdown from the issue

With this change: Tests 6 passed (6).

After

  • vitest --project unit packages/lexical-markdown: 700 passed
  • Full test-unit: everything passes except the three lexical-devtools files, which also fail on main in my environment (a transform error)
  • tsc, eslint packages/lexical-markdown and prettier --check are clean

Author: Ved Thakar (@Vedthakar)

… on export

When a text node opens several formats, open first the format that
continues furthest into the following siblings. A format that ends
sooner is then closed on its own, instead of closing and reopening the
others, which merged the delimiters into a run like `***a****: b*` and
lost the italic on import.

Fixes meta#9443

Co-authored-by: Ved Thakar <111605341+Vedthakar@users.noreply.github.com>
@Vedthakar

Copy link
Copy Markdown
Owner Author

Not submitting: meta#9450 already addresses meta#9443.

@Vedthakar Vedthakar closed this Oct 25, 2026
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.

Bug: Bold italic text followed by italic text is exported as ***a****: b*, and the italic is lost

1 participant