Skip to content

fix: keep empty list after blockquote as a sibling block#4004

Open
spokodev wants to merge 1 commit into
markedjs:masterfrom
spokodev:fix/empty-list-after-blockquote
Open

fix: keep empty list after blockquote as a sibling block#4004
spokodev wants to merge 1 commit into
markedjs:masterfrom
spokodev:fix/empty-list-after-blockquote

Conversation

@spokodev

Copy link
Copy Markdown

Description

A blockquote immediately followed by a bare list marker line nests an empty list inside the blockquote instead of starting a sibling list.

Minimal repro

marked.parse('> foo\n-');

Expected (matches commonmark.js and markdown-it):

<blockquote>
<p>foo</p>
</blockquote>
<ul>
<li></li>
</ul>

Actual:

<blockquote>
<p>foo</p>
<ul>
<li></li>
</ul>
</blockquote>

Reproduces for -, *, +, 1., 1) and for multi line blockquotes (> foo\n> bar\n-). Non empty lists (> foo\n- bar) already behave correctly.

Root cause

The blockquote rule reuses the paragraph list interrupt clause:

 {0,3}(?:[*+-]|1[.)])[ \t]+[^ \t\n]

The trailing [ \t]+[^ \t\n] requires content after the marker, which is correct for a top level paragraph (an empty list cannot interrupt a paragraph). But the same clause also controls when a blockquote stops its lazy continuation. A bare marker line does not match it, so the marker line is lazily continued into the blockquote paragraph and then re lexed into a nested empty list.

Per CommonMark 0.31.2 (5.1 block quotes laziness, 5.2 list items) a list begun by a bare marker is a sibling block after the blockquote, not a lazy continuation of it. commonmark.js and markdown-it both produce the sibling.

Fix

Give the blockquote its own paragraph variant (blockquoteParagraph) whose list interrupt clause also matches a bare marker:

 {0,3}(?:[*+-]|1[.)])(?:[ \t]|\n|$)

The top level paragraph rule is left untouched, so foo\n- (setext heading), foo\n* and foo\n1. (no interruption) keep their current behavior. The change only affects when a blockquote ends.

Relation to #4003

#4003 also routes the blockquote rule through a blockquoteParagraph const, but for a different bug (#4002, non empty ordered lists with a start other than 1). It keeps the [ \t]+[^ \t\n] content requirement, so it does not fix the empty marker case verified here. The two changes touch the same region; I can rebase or fold the two interrupt clauses into one if a maintainer prefers a single edit.

Tests

  • Added test/specs/new/empty_list_after_blockquote.{md,html} covering the bare unordered and bare ordered cases including a multi line blockquote.
  • npm test green, including the CommonMark and GFM conformance specs (1745 spec assertions, 188 unit tests).

Contributor

  • Test(s) exist to ensure functionality and minimize regression.
  • no tests required for this PR.
  • If submitting new feature, it has been documented in the appropriate places.

Committer

  • CI is green (no forced merge required).
  • Squash and Merge PR following conventional commit guidelines.

A blockquote followed by a bare list marker line (for example
`> foo\n-`) wrongly nested an empty list inside the blockquote.

The blockquote regex reuses the paragraph list-interrupt clause
` {0,3}(?:[*+-]|1[.)])[ \t]+[^ \t\n]`, whose trailing `[ \t]+[^ \t\n]`
requires content after the marker. A bare marker line therefore was
not seen as an interruption and got lazily continued into the
blockquote paragraph, then re-lexed into a nested empty list.

Give the blockquote its own paragraph variant whose list-interrupt
clause also matches a bare marker, so the list ends the blockquote and
becomes a sibling block. The top level paragraph rule is unchanged, so
an empty list still cannot interrupt a paragraph.
@vercel

vercel Bot commented Jun 25, 2026

Copy link
Copy Markdown

@spokodev is attempting to deploy a commit to the MarkedJS Team on Vercel.

A member of the Team first needs to authorize it.

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.

1 participant