From 0d08cc1b2110012f5f43b7b18eb59f8db11a42e5 Mon Sep 17 00:00:00 2001 From: mck09 Date: Sun, 30 Aug 2026 13:40:40 +0200 Subject: [PATCH 1/2] fix: remove up to the fence indentation from each content line CommonMark removes up to N spaces from each line of a fenced code block when the opening fence is indented by N. indentCodeCompensation only stripped lines indented at least N, so a line indented less kept all of its indentation. Fixes CommonMark example 133. The spec suite did not catch it because htmlIsEqual leaves html-differ's ignoreWhitespaces at its default of true. --- src/Tokenizer.ts | 8 +++----- test/unit/marked.test.js | 12 ++++++++++++ 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/Tokenizer.ts b/src/Tokenizer.ts index 5f7d878599..16834b86bf 100644 --- a/src/Tokenizer.ts +++ b/src/Tokenizer.ts @@ -68,11 +68,9 @@ function indentCodeCompensation(raw: string, text: string, rules: Rules) { const [indentInNode] = matchIndentInNode; - if (indentInNode.length >= indentToCode.length) { - return node.slice(indentToCode.length); - } - - return node; + // Up to the fence's own indentation is removed from each line, so a line + // indented less than the fence loses whatever indentation it has. + return node.slice(Math.min(indentInNode.length, indentToCode.length)); }) .join('\n'); } diff --git a/test/unit/marked.test.js b/test/unit/marked.test.js index 5d1449b4aa..93b86efd1d 100644 --- a/test/unit/marked.test.js +++ b/test/unit/marked.test.js @@ -22,6 +22,18 @@ describe('marked unit', () => { }); }); + describe('fenced code block indentation', () => { + it('should remove up to the fence indentation from each line', () => { + const md = ' ```\n aaa\n aaa\n aaa\n ```\n'; + assert.strictEqual(marked.parse(md), '
aaa\n aaa\naaa\n
\n'); + }); + + it('should leave lines indented more than the fence with the remainder', () => { + const md = ' ```\n aaa\n ```\n'; + assert.strictEqual(marked.parse(md), '
    aaa\n
\n'); + }); + }); + describe('changeDefaults', () => { it('should change global defaults', async() => { const { defaults, setOptions } = await import('../../lib/marked.esm.js'); From 671374724e7c759a3810a0c3345872f1b0cb229a Mon Sep 17 00:00:00 2001 From: mck09 Date: Thu, 3 Sep 2026 04:45:30 +0200 Subject: [PATCH 2/2] test: move the fence indentation tests to test/specs/new Uses renderExact so the fixture compares exactly, which htmlIsEqual does not, and keeps the assertions out of marked.test.js. --- test/specs/new/fence_indent_stripping.html | 7 +++++++ test/specs/new/fence_indent_stripping.md | 14 ++++++++++++++ test/unit/marked.test.js | 12 ------------ 3 files changed, 21 insertions(+), 12 deletions(-) create mode 100644 test/specs/new/fence_indent_stripping.html create mode 100644 test/specs/new/fence_indent_stripping.md diff --git a/test/specs/new/fence_indent_stripping.html b/test/specs/new/fence_indent_stripping.html new file mode 100644 index 0000000000..b3feaa5773 --- /dev/null +++ b/test/specs/new/fence_indent_stripping.html @@ -0,0 +1,7 @@ +
aaa
+ aaa
+aaa
+
+

sep

+
    aaa
+
diff --git a/test/specs/new/fence_indent_stripping.md b/test/specs/new/fence_indent_stripping.md new file mode 100644 index 0000000000..ea8bba01e6 --- /dev/null +++ b/test/specs/new/fence_indent_stripping.md @@ -0,0 +1,14 @@ +--- +renderExact: true +--- + ``` + aaa + aaa + aaa + ``` + +sep + + ``` + aaa + ``` diff --git a/test/unit/marked.test.js b/test/unit/marked.test.js index 93b86efd1d..5d1449b4aa 100644 --- a/test/unit/marked.test.js +++ b/test/unit/marked.test.js @@ -22,18 +22,6 @@ describe('marked unit', () => { }); }); - describe('fenced code block indentation', () => { - it('should remove up to the fence indentation from each line', () => { - const md = ' ```\n aaa\n aaa\n aaa\n ```\n'; - assert.strictEqual(marked.parse(md), '
aaa\n aaa\naaa\n
\n'); - }); - - it('should leave lines indented more than the fence with the remainder', () => { - const md = ' ```\n aaa\n ```\n'; - assert.strictEqual(marked.parse(md), '
    aaa\n
\n'); - }); - }); - describe('changeDefaults', () => { it('should change global defaults', async() => { const { defaults, setOptions } = await import('../../lib/marked.esm.js');