diff --git a/apps/server/src/integrations/import/utils/foreign-markdown.spec.ts b/apps/server/src/integrations/import/utils/foreign-markdown.spec.ts index c0d8dac4..8a43fa20 100644 --- a/apps/server/src/integrations/import/utils/foreign-markdown.spec.ts +++ b/apps/server/src/integrations/import/utils/foreign-markdown.spec.ts @@ -144,6 +144,18 @@ describe('normalizeForeignMarkdown — GFM reference footnotes', () => { expect(out).toContain(`^[body ${N}]`); }); + it('strips a CRLF (Windows) front-matter block, not just LF', () => { + // F9: the line-anchored regex needs LF after the opening `---`, so a Windows + // file (`---\r\n…`) would slip past the strip and leak the front-matter into + // the body. normalizeForeignMarkdown normalizes CRLF -> LF first. + const out = normalizeForeignMarkdown( + '---\r\ntitle: Foo\r\ntags: [a]\r\n---\r\n\r\n# Heading\r\n\r\nBody.', + ); + expect(out).toBe('# Heading\n\nBody.'); + expect(out).not.toContain('title: Foo'); + expect(out).not.toContain('---'); + }); + it('strips front-matter whose value contains a triple-dash (line-anchored)', () => { // F8: the block must close only on a `\n---` LINE, not the first inline // `---`. A value like `title: Q1 --- Q2` must not truncate the front-matter diff --git a/apps/server/src/integrations/import/utils/foreign-markdown.ts b/apps/server/src/integrations/import/utils/foreign-markdown.ts index ef253a61..2173bfdc 100644 --- a/apps/server/src/integrations/import/utils/foreign-markdown.ts +++ b/apps/server/src/integrations/import/utils/foreign-markdown.ts @@ -247,13 +247,19 @@ const YAML_FRONT_MATTER_RE = /^\uFEFF?---\n[\s\S]*?\n---\n?/; /** * Normalize a foreign markdown string into Docmost's canonical markdown surface - * so the strict canonical parser accepts it losslessly: strip a leading YAML - * front-matter block, then rewrite GFM reference footnotes into inline - * footnotes. Add further fixture-driven foreign-surface cases here as they are - * found. + * so the strict canonical parser accepts it losslessly: normalize line endings, + * strip a leading YAML front-matter block, then rewrite GFM reference footnotes + * into inline footnotes. Add further fixture-driven foreign-surface cases here as + * they are found. */ export function normalizeForeignMarkdown(markdown: string): string { if (!markdown) return markdown; - const withoutFrontMatter = markdown.replace(YAML_FRONT_MATTER_RE, '').trimStart(); + // Normalize CRLF -> LF FIRST. The line-anchored front-matter regex requires a + // bare `\n` after the opening `---`, and convertReferenceFootnotes splits on + // `\n`; a Windows/CRLF foreign file (`---\r\n…`) would otherwise slip past the + // front-matter strip and leak into the body. The canonical parser + // (page-file.ts parsePageFile) normalizes the same way before its FRONTMATTER_RE. + const src = markdown.replace(/\r\n/g, '\n'); + const withoutFrontMatter = src.replace(YAML_FRONT_MATTER_RE, '').trimStart(); return convertReferenceFootnotes(withoutFrontMatter); }