Skip to content

[lexical-markdown] Bug Fix: Read a code fence's info string as CommonMark does - #9487

Open
nakultt wants to merge 1 commit into
meta:mainfrom
nakultt:fix/code-fence-info-string
Open

nakultt wants to merge 1 commit into
meta:mainfrom
nakultt:fix/code-fence-info-string

Conversation

@nakultt

@nakultt nakultt commented Oct 28, 2026

Copy link
Copy Markdown

Description

CommonMark treats everything after an opening code fence, trimmed, as the
info string, and its first word is the language. That holds whether or not
the block is closed: an unclosed fence runs to the end of the document.

Lexical read the language only when it came right after the backticks, and
kept the rest of the line out of the code only when the block was closed.
So ``` js imported with no language and js as the first line of
code, and an unclosed ```js title=x put title=x into the code.
The info string then stayed in the code on every save.

This change:

  • lets CODE_START_REGEX accept whitespace between the fence and the
    language, so ``` js is a js block;
  • reads the rest of an unclosed fence's opening line as metadata, the same
    way a closed fence's is read, and keeps it on export
    (```js title=x).

A closed ```js title="x" fence, ```js with nothing after it,
a bare ``` fence and the single-line ```code``` form are
unchanged. ``` code / more / ``` now becomes language
code with code more, as in CommonMark, so the tests that asserted the
old behaviour are updated.

Closes #9406

Test plan

Before

pnpm exec vitest --project unit --no-watch packages/lexical-markdown, with
the updated tests and the original MarkdownTransformers.ts:

     × treats text after the opening fence as the info string 16ms
     × keeps a blank first line of an unterminated fence with an info string 5ms
     × reads a language that follows whitespace after the fence 19ms
     × keeps the metadata of a fence with whitespace before the language 6ms
     × reads the info string of an unterminated fence 4ms
     × reads the language of an unterminated fence after whitespace 3ms
     × can import "```javascript Incomplete tag" 14ms
     × can import "```javascript Incomplete multiline\n\nTag" 4ms
 Test Files  3 failed | 5 passed (8)
      Tests  8 failed | 690 passed (698)

After

 Test Files  8 passed (8)
      Tests  698 passed (698)

pnpm exec vitest --project unit --no-watch packages/lexical-markdown packages/lexical-mdast packages/lexical-playground/__tests__/unit:

 Test Files  45 passed (45)
      Tests  1246 passed (1246)

tsc, ESLint and Prettier pass on the changed files. The Markdown shortcut
uses the same regular expression, and typing ``` plus a space
still gives the same match, but this was not tried in a browser, and the
E2E suites were not run.

…Mark does

## Description

CommonMark treats everything after an opening code fence, trimmed, as the
info string, and its first word is the language. That holds whether or not
the block is closed: an unclosed fence runs to the end of the document.

Lexical read the language only when it came right after the backticks, and
kept the rest of the line out of the code only when the block was closed.
So ```` ``` js ```` imported with no language and `js` as the first line of
code, and an unclosed ```` ```js title=x ```` put `title=x` into the code.
The info string then stayed in the code on every save.

This change:

- lets `CODE_START_REGEX` accept whitespace between the fence and the
  language, so ```` ``` js ```` is a `js` block;
- reads the rest of an unclosed fence's opening line as metadata, the same
  way a closed fence's is read, and keeps it on export
  (```` ```js title=x ````).

A closed ```` ```js title="x" ```` fence, `` ```js `` with nothing after it,
a bare ```` ``` ```` fence and the single-line `` ```code``` `` form are
unchanged. ```` ``` code ```` / `more` / ```` ``` ```` now becomes language
`code` with code `more`, as in CommonMark, so the tests that asserted the
old behaviour are updated.

Closes meta#9406

## Test plan

### Before

`pnpm exec vitest --project unit --no-watch packages/lexical-markdown`, with
the updated tests and the original `MarkdownTransformers.ts`:

```
     × treats text after the opening fence as the info string 16ms
     × keeps a blank first line of an unterminated fence with an info string 5ms
     × reads a language that follows whitespace after the fence 19ms
     × keeps the metadata of a fence with whitespace before the language 6ms
     × reads the info string of an unterminated fence 4ms
     × reads the language of an unterminated fence after whitespace 3ms
     × can import "```javascript Incomplete tag" 14ms
     × can import "```javascript Incomplete multiline\n\nTag" 4ms
 Test Files  3 failed | 5 passed (8)
      Tests  8 failed | 690 passed (698)
```

### After

```
 Test Files  8 passed (8)
      Tests  698 passed (698)
```

`pnpm exec vitest --project unit --no-watch packages/lexical-markdown packages/lexical-mdast packages/lexical-playground/__tests__/unit`:

```
 Test Files  45 passed (45)
      Tests  1246 passed (1246)
```

`tsc`, ESLint and Prettier pass on the changed files. The Markdown shortcut
uses the same regular expression, and typing ```` ``` ```` plus a space
still gives the same match, but this was not tried in a browser, and the
E2E suites were not run.
@vercel

vercel Bot commented Oct 28, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
lexical Ready Ready Preview Oct 28, 2026 9:17am UTC
lexical-playground Ready Ready Preview Oct 28, 2026 9:17am UTC

Request Review

@nakultt
nakultt requested a review from ivailop7 as a code owner October 28, 2026 09:15
@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Oct 28, 2026

This branch was successfully deployed

2 active deployments
Preview – lexical — 2fb85964 Deployed Oct 28, 2026 by vercel[bot]
Preview – lexical-playground — 2fb85964 Deployed Oct 28, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature: Read a code fence's info string as CommonMark does: language after spaces, and on an unclosed fence

1 participant