From f05fe5754d01774a2f522311b6e4f44399f8d2cf Mon Sep 17 00:00:00 2001 From: Justin Charles <143245796+justin212407@users.noreply.github.com> Date: Tue, 10 Mar 2026 17:01:38 +0530 Subject: [PATCH] Desktop: Fixes #14542: Fix Prevent unclosed frontmatter from breaking Markdown rendering (#14563) Signed-off-by: justin212407 --- .../markdownFrontMatterExtension.test.ts | 11 ++++ .../markdownFrontMatterExtension.ts | 56 +++++++++---------- 2 files changed, 38 insertions(+), 29 deletions(-) diff --git a/packages/editor/CodeMirror/extensions/markdownFrontMatterExtension.test.ts b/packages/editor/CodeMirror/extensions/markdownFrontMatterExtension.test.ts index 1a309a63d1..3bf58a212e 100644 --- a/packages/editor/CodeMirror/extensions/markdownFrontMatterExtension.test.ts +++ b/packages/editor/CodeMirror/extensions/markdownFrontMatterExtension.test.ts @@ -54,6 +54,17 @@ describe('MarkdownFrontMatterExtension', () => { expect(frontMatterNodes.length).toBe(0); }); + it('should treat the entire document as frontmatter when closing delimiter is missing (issue #14542)', async () => { + const documentText = '---\nsome: frontmatter\n--\n\n# Hey'; + const editor = await createEditorState(documentText, [frontMatterTagName]); + const frontMatterNodes = findNodesWithName(editor, frontMatterTagName); + + // Frontmatter block must be recognised and span the entire document + expect(frontMatterNodes.length).toBe(1); + expect(frontMatterNodes[0].from).toBe(0); + expect(frontMatterNodes[0].to).toBe(documentText.length); + }); + it('should handle empty FrontMatter block', async () => { const documentText = '---\n---\n\n# Heading'; const editor = await createEditorState(documentText, [frontMatterTagName, 'ATXHeading1']); diff --git a/packages/editor/CodeMirror/extensions/markdownFrontMatterExtension.ts b/packages/editor/CodeMirror/extensions/markdownFrontMatterExtension.ts index d332e60718..4e29f4946d 100644 --- a/packages/editor/CodeMirror/extensions/markdownFrontMatterExtension.ts +++ b/packages/editor/CodeMirror/extensions/markdownFrontMatterExtension.ts @@ -66,14 +66,15 @@ const frontMatterConfig: MarkdownConfig = { return false; } - // Store the opening delimiter position + // If the document starts with --- always claim it as a frontmatter block, + // even when the closing delimiter is absent. const openingMarkerStart = cx.lineStart; const openingMarkerEnd = cx.lineStart + line.text.length; + const contentStart = openingMarkerEnd + 1; - const contentStart = openingMarkerEnd + 1; // Start after the opening --- and newline let foundEnd = false; - // Consume lines until we find the closing --- + // Consume lines until we find the closing --- or reach end of document. while (cx.nextLine()) { if (frontMatterDelimiterRegex.test(line.text)) { foundEnd = true; @@ -81,37 +82,34 @@ const frontMatterConfig: MarkdownConfig = { } } - if (!foundEnd) { - // No closing delimiter found - not a valid FrontMatter block - return false; - } + // cx.lineStart now points to the closing --- (if found) or end of document (if not). + const contentEnd = cx.lineStart; - // The content is between the two --- delimiters - const contentEnd = cx.lineStart; // Start of the closing --- line - - // Closing delimiter positions - const closingMarkerStart = cx.lineStart; - const closingMarkerEnd = cx.lineStart + line.text.length; - - // Create marker elements for the --- delimiters const openingMarkerElem = cx.elt(frontMatterMarkerTagName, openingMarkerStart, openingMarkerEnd); - const closingMarkerElem = cx.elt(frontMatterMarkerTagName, closingMarkerStart, closingMarkerEnd); - - // Create the content element (the YAML content between delimiters) const contentElem = cx.elt(frontMatterContentTagName, contentStart, contentEnd); - // Create the container element spanning from start of first --- to end of last --- - const containerElement = cx.elt( - frontMatterTagName, - 0, // Start at document beginning - closingMarkerEnd, // End after closing --- - [openingMarkerElem, contentElem, closingMarkerElem], - ); + if (foundEnd) { + const closingMarkerStart = cx.lineStart; + const closingMarkerEnd = cx.lineStart + line.text.length; + const closingMarkerElem = cx.elt(frontMatterMarkerTagName, closingMarkerStart, closingMarkerEnd); - cx.addElement(containerElement); - - // Move past the closing delimiter - cx.nextLine(); + const containerElement = cx.elt( + frontMatterTagName, + 0, + closingMarkerEnd, + [openingMarkerElem, contentElem, closingMarkerElem], + ); + cx.addElement(containerElement); + cx.nextLine(); + } else { + const containerElement = cx.elt( + frontMatterTagName, + 0, + contentEnd, + [openingMarkerElem, contentElem], + ); + cx.addElement(containerElement); + } return true; },