Skip to content

fix: strip whitespace when normalizing link reference labels - #4091

Merged
UziTech merged 1 commit into
markedjs:masterfrom
NgoQuocViet2001:fix-normalize-label-strip
Sep 21, 2026
Merged

UziTech merged 1 commit into
markedjs:masterfrom
NgoQuocViet2001:fix-normalize-label-strip

Conversation

@NgoQuocViet2001

Copy link
Copy Markdown

Problem

CommonMark normalizes a link label in three steps — case-fold, strip leading and trailing whitespace, collapse internal runs. normalizeLabel does only the first:

export function normalizeLabel(label: string) {
  return label.toLowerCase().toUpperCase().toLowerCase();
}

The collapse is bolted onto one of its four call sites (Tokenizer.ts:531, the def rule); reflink and the two Lexer lookups don't have it, and nothing strips anywhere. A definition and a reference that differ only in surrounding whitespace therefore normalize to different keys and never match.

Compared against the commonmark 0.31.2 reference implementation already in devDependencies, normalizing away marked's <img> vs <img /> difference:

DIFF  trailing space in the reference       [foo ] + [foo]: /url
        marked     : <p>[foo ]</p>
        commonmark : <p><a href="/url">foo </a></p>
DIFF  leading space in the reference        [ foo] + [foo]: /url
DIFF  trailing space in the definition      [foo]  + [foo ]: /url
DIFF  emphasis around a spaced reference    *[foo ]* + [foo]: /url
        marked     : <p><em>[foo ]</em></p>
        commonmark : <p><em><a href="/url">foo </a></em></p>

4 of 9 probe inputs diverge. The collapse cases (internal double space, tab, newline) all pass — the missing step is the strip.

The failure is silent: a valid reference link renders as literal bracket text, and in the masking case the surrounding emphasis is disturbed too.

Fix

Do the strip inside normalizeLabel, so all four call sites agree by construction.

return label.trim().toLowerCase().toUpperCase().toLowerCase();

Test plan

  • Added test/specs/new/link_reference_label_whitespace.{md,html}. The expected HTML is the commonmark reference implementation's own output for that input, not hand-written.
  • Ran: node --test test/run-spec-tests.js → 1815 passing (1813 before, plus the new pair).
  • Ran: node --test test/unit/*.test.js → 191 passing.
  • Checked: reverting only helpers.ts fails the new spec. Worth noting that the existing 1813 pass identically with and without this change — the CommonMark spec files in the repo don't cover surrounding whitespace in a label, which is why this survived.
  • Ran: eslint src/helpers.ts → clean.

CommonMark normalizes a link label by case-folding it, stripping leading and
trailing whitespace, and collapsing internal runs. normalizeLabel does only
the case fold, and the collapse is bolted onto one of its four call sites, so
a definition and a reference that differ only in surrounding whitespace
normalize to different keys and never match.

Strip inside normalizeLabel, where all four call sites agree by construction.
@vercel

vercel Bot commented Sep 12, 2026

Copy link
Copy Markdown

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

A member of the Team first needs to authorize it.

@vercel

vercel Bot commented Sep 12, 2026 •

Copy link
Copy Markdown

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

Project Deployment Actions Updated
marked-website Ready Ready Preview Sep 12, 2026 3:46pm UTC

Request Review

@UziTech UziTech left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice catch! 💯

@UziTech
UziTech merged commit c7ee43a into markedjs:master Sep 21, 2026
8 checks passed
github-actions Bot pushed a commit that referenced this pull request Sep 22, 2026
## [18.0.14](v18.0.13...v18.0.14) (2026-09-22)

### Bug Fixes

* allow indented lines in setext heading text ([#4095](#4095)) ([7d05530](7d05530))
* decode numeric character references in text ([#4076](#4076)) ([cead8de](cead8de))
* keep text after empty nested blockquote ([#4101](#4101)) ([1b89557](1b89557)), closes [#4098](#4098)
* preserve internal tabs in list item content ([#4086](#4086)) ([e136da7](e136da7))
* strip whitespace when normalizing link reference labels ([#4091](#4091)) ([c7ee43a](c7ee43a))
* support GFM protocol autolinks ([#4067](#4067)) ([7f7a496](7f7a496))

This branch was successfully deployed

1 active deployment
Preview — 3a9cdf7e Deployed Sep 12, 2026 by vercel[bot]
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.

2 participants