Skip to content

fix(utils): escape every heading id token on a line#12315

Open
spokodev wants to merge 1 commit into
facebook:mainfrom
spokodev:fix/escape-all-heading-ids
Open

fix(utils): escape every heading id token on a line#12315
spokodev wants to merge 1 commit into
facebook:mainfrom
spokodev:fix/escape-all-heading-ids

Conversation

@spokodev

Copy link
Copy Markdown
Contributor

Problem

escapeMarkdownHeadingIds escapes MDX-breaking {#…} tokens in headings, but
uses String.prototype.replace, which replaces only the first occurrence:

substring
  .replace('{#', '\\{#')
  .replace('\\\\{#', '\\{#'),

So a heading containing more than one {#…} token gets only the first escaped:

escapeMarkdownHeadingIds('## Some heading {#id1} {#id2}');
// '## Some heading \{#id1} {#id2}'   ← second {# left unescaped

Multiple ids on a heading is a supported input: parseMarkdownHeadingId has an
explicit test for it (## Some heading {#id1} {#id2} → text ## Some heading {#id1}, id id2), so the leading {#id1} stays as literal heading text
alongside the real anchor. The leftover unescaped {# then defeats this
function's sole purpose — mdx1Compat.headingIds is on by default, and MDX v2
treats the unescaped { as a JS expression → "Could not parse expression with
acorn" build failure, exactly the error this function exists to prevent.

Fix

Use replaceAll so every {# on the heading line is escaped.

Test

Added a case asserting ## Some heading {#id1} {#id2} escapes both tokens.
Fails on main; passes with the fix. The existing 58 tests (including the
already-escaped idempotency case and the multi-heading realistic example) still
pass.

escapeMarkdownHeadingIds used String.prototype.replace, which replaces
only the first occurrence, so a heading that contains more than one {#…}
token had only the first escaped. parseMarkdownHeadingId officially
supports multiple ids on a heading (e.g. `## heading {#id1} {#id2}`), so
the leftover unescaped {# defeats the function's purpose and makes MDX v2
fail to compile the heading (mdx1Compat.headingIds is on by default).

Use replaceAll so every {# on the line is escaped.
@meta-cla meta-cla Bot added the CLA Signed Signed Facebook CLA label Jul 23, 2026
@netlify

netlify Bot commented Jul 23, 2026

Copy link
Copy Markdown

[V2]

Built without sensitive environment variables

Name Link
🔨 Latest commit d19f1d4
🔍 Latest deploy log https://app.netlify.com/projects/docusaurus-2/deploys/6a6228bd75667f00087e464f
😎 Deploy Preview https://deploy-preview-12315--docusaurus-2.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed Signed Facebook CLA

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant