diff --git a/Frontmatter.Test/StackedBlockPreservationTests.cs b/Frontmatter.Test/StackedBlockPreservationTests.cs index 480af95..c1410a4 100644 --- a/Frontmatter.Test/StackedBlockPreservationTests.cs +++ b/Frontmatter.Test/StackedBlockPreservationTests.cs @@ -41,6 +41,31 @@ public void AddFrontmatter_StackedBlocksRepeatAKey_FirstBlockWins() Assert.AreEqual("First", frontmatter["title"]); } + [TestMethod] + public void ExtractFrontmatter_TwoStackedBlocks_ReturnsPropertiesFromEveryBlock() + { + const string input = "---\ntitle: Hello\n---\n---\nauthor: Jane\n---\n# Body\n"; + + Dictionary? frontmatter = Frontmatter.ExtractFrontmatter(input); + + Assert.IsNotNull(frontmatter); + Assert.AreEqual("Hello", frontmatter["title"]); + Assert.AreEqual("Jane", frontmatter["author"], "The second block is header to ExtractBody, so ExtractFrontmatter must return it"); + Assert.AreEqual("# Body", Frontmatter.ExtractBody(input)); + } + + [TestMethod] + public void ExtractFrontmatter_StackedBlocksRepeatAKey_FirstBlockWins() + { + const string input = "---\ntitle: First\n---\n---\ntitle: Second\nauthor: Jane\n---\nbody\n"; + + Dictionary? frontmatter = Frontmatter.ExtractFrontmatter(input); + + Assert.IsNotNull(frontmatter); + Assert.AreEqual("First", frontmatter["title"]); + Assert.AreEqual("Jane", frontmatter["author"]); + } + // The first block is always read as frontmatter, so an unparseable first block followed by one that // parses is the case where the unreadable text sits in the header rather than the body. private const string UnreadableFirstBlock = "---\nkey: [unclosed\n---\n---\ntitle: T\n---\nbody\n"; diff --git a/Frontmatter/Frontmatter.cs b/Frontmatter/Frontmatter.cs index 9907721..1cdba09 100644 --- a/Frontmatter/Frontmatter.cs +++ b/Frontmatter/Frontmatter.cs @@ -126,6 +126,10 @@ public static string CombineFrontmatter(string input, FrontmatterNaming property /// Extracts frontmatter from a markdown document. /// /// The markdown document content as a string. + /// + /// Consecutive frontmatter blocks form one header, so their properties are combined, and the first + /// block to define a key wins. + /// /// A dictionary containing the frontmatter properties, or null if no frontmatter is found. /// Thrown when input is null. public static Dictionary? ExtractFrontmatter(string input) @@ -137,8 +141,10 @@ public static string CombineFrontmatter(string input, FrontmatterNaming property return null; } - List> frontmatterObjects = ExtractFrontmatterObjects(input, out _); - return frontmatterObjects.Count > 0 ? frontmatterObjects.First() : null; + // Stacked blocks are one header, as AddFrontmatter and ExtractBody treat them, so fold them all + // and let an earlier block win a repeated key. + Dictionary frontmatter = CombineAllFrontmatterObjects(ExtractFrontmatterObjects(input, out _)); + return frontmatter.Count > 0 ? frontmatter : null; } ///