From 6dc551a72fa0da462132cd4b181f9e8f4e89a072 Mon Sep 17 00:00:00 2001 From: abhay-codes07 Date: Wed, 1 Jul 2026 07:44:19 +0530 Subject: [PATCH] fix(converters): stop blog footer turning the last body line into a heading The footer was concatenated onto the body with no blank line, so output ended with 'last body line\n---'. CommonMark parses text immediately followed by '---' as a setext H2, so the final line of every blog post body became a heading and the intended horizontal rule was dropped. Add a blank line between the body and the footer so '---' is a thematic break. --- .changeset/blog-converter-setext-heading.md | 7 +++++++ packages/converters/src/blog.ts | 6 +++++- packages/converters/test/converters.test.ts | 12 ++++++++++++ 3 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 .changeset/blog-converter-setext-heading.md diff --git a/.changeset/blog-converter-setext-heading.md b/.changeset/blog-converter-setext-heading.md new file mode 100644 index 0000000..155f91d --- /dev/null +++ b/.changeset/blog-converter-setext-heading.md @@ -0,0 +1,7 @@ +--- +"@dualmark/converters": patch +--- + +Fix the blog converter turning a post's last body line into a heading. + +The footer was concatenated directly onto the body with no blank line, so the output ended with `last body line\n---`. CommonMark parses a line of text immediately followed by `---` as a setext H2, which turned the final line of every blog post body into a heading and dropped the intended horizontal rule before the footer links. The body and footer are now separated by a blank line so the `---` is a thematic break. diff --git a/packages/converters/src/blog.ts b/packages/converters/src/blog.ts index 450f34a..baeda77 100644 --- a/packages/converters/src/blog.ts +++ b/packages/converters/src/blog.ts @@ -52,6 +52,10 @@ export function blogConverter( footer.push(`- [All articles](${config.siteUrl}${basePath})`); } if (config.brandFooter) footer.push("", config.brandFooter); - return normalizeUnicode(base + footer.join("\n")); + // Separate the body from the footer with a blank line. `base` ends with the + // body's last line (no trailing newline) and `footer` starts with "---"; + // without the blank line CommonMark parses "lastline\n---" as a setext H2, + // turning the final body line into a heading and swallowing the rule. + return normalizeUnicode(base + "\n" + footer.join("\n")); }; } diff --git a/packages/converters/test/converters.test.ts b/packages/converters/test/converters.test.ts index 6ffb50c..38f051b 100644 --- a/packages/converters/test/converters.test.ts +++ b/packages/converters/test/converters.test.ts @@ -78,6 +78,18 @@ describe("blogConverter", () => { }); expect(out).toContain("## About Acme"); }); + + it("separates the body from the footer so the last line is not a setext heading", () => { + const out = convert({ + id: "p", + data: { title: "T", publishedDate: new Date("2026-01-01T00:00:00Z") }, + body: "Final body line.", + }); + // "Final body line.\n---" would render as a setext H2 in CommonMark, + // turning the last body line into a heading and dropping the rule. + expect(out).toContain("Final body line.\n\n---"); + expect(out).not.toContain("Final body line.\n---\n"); + }); }); describe("caseStudyConverter", () => {