From bbe66ec16c2dca7776ddb004b247f1e65aea1e6a Mon Sep 17 00:00:00 2001 From: Simon Cropp Date: Sat, 21 Mar 2026 16:34:46 +1100 Subject: [PATCH] Snippets in JSX static site generators https://github.com/SimonCropp/MarkdownSnippets/issues/453 --- readme.md | 25 ++++++ readme.source.md | 25 ++++++ .../Processing/IncludeProcessor.cs | 1 + .../Processing/MarkdownProcessor.cs | 67 +++++++++++---- src/MarkdownSnippets/Processing/SnippetKey.cs | 82 +++++++++++++++++++ ...ests.LinkRefComment_Overwrite.verified.txt | 36 ++++++++ ...kRefComment_Overwrite_Missing.verified.txt | 14 ++++ .../MarkdownProcessorTests.cs | 50 +++++++++++ 8 files changed, 284 insertions(+), 16 deletions(-) create mode 100644 src/Tests/MarkdownProcessor/MarkdownProcessorTests.LinkRefComment_Overwrite.verified.txt create mode 100644 src/Tests/MarkdownProcessor/MarkdownProcessorTests.LinkRefComment_Overwrite_Missing.verified.txt diff --git a/readme.md b/readme.md index ab781038..3656594d 100644 --- a/readme.md +++ b/readme.md @@ -199,6 +199,31 @@ Notes: * [H33: Supplementing link text with the title attribute](https://www.w3.org/TR/WCAG20-TECHS/H33.html) +### MDX / Link Reference Comment Syntax + +When using MDX files (e.g. with [fumadocs](https://fumadocs.dev), [Astro](https://astro.build/), or other JSX-based static site generators), HTML comments (``) are not supported. An alternative comment syntax using [markdown link reference definitions](https://spec.commonmark.org/0.31.2/#link-reference-definitions) can be used instead: + +
+[//]: # (snippet: MySnippetName)
+[//]: # (endSnippet)
+
+ +This works the same way as the HTML comment syntax but is compatible with both standard markdown and MDX parsers. When using InPlaceOverwrite convention, the content between the markers will be replaced with the snippet content, and the link reference format will be preserved: + + [//]: # (snippet: MySnippetName) + ```cs + My Snippet Code + ``` + [//]: # (endSnippet) + +This syntax also works for web-snippets: + +
+[//]: # (web-snippet: https://example.com/file.cs#snippetKey)
+[//]: # (endSnippet)
+
+ + ### Including a snippet from the web Snippets that start with `http` will be downloaded and the contents rendered. For example: diff --git a/readme.source.md b/readme.source.md index 7ef05ba8..161aa725 100644 --- a/readme.source.md +++ b/readme.source.md @@ -192,6 +192,31 @@ Notes: * [H33: Supplementing link text with the title attribute](https://www.w3.org/TR/WCAG20-TECHS/H33.html) +### MDX / Link Reference Comment Syntax + +When using MDX files (e.g. with [fumadocs](https://fumadocs.dev), [Astro](https://astro.build/), or other JSX-based static site generators), HTML comments (``) are not supported. An alternative comment syntax using [markdown link reference definitions](https://spec.commonmark.org/0.31.2/#link-reference-definitions) can be used instead: + +
+[//]: # (snippet: MySnippetName)
+[//]: # (endSnippet)
+
+ +This works the same way as the HTML comment syntax but is compatible with both standard markdown and MDX parsers. When using InPlaceOverwrite convention, the content between the markers will be replaced with the snippet content, and the link reference format will be preserved: + + [//]: # (snippet: MySnippetName) + ```cs + My Snippet Code + ``` + [//]: # (endSnippet) + +This syntax also works for web-snippets: + +
+[//]: # (web-snippet: https://example.com/file.cs#snippetKey)
+[//]: # (endSnippet)
+
+ + ### Including a snippet from the web Snippets that start with `http` will be downloaded and the contents rendered. For example: diff --git a/src/MarkdownSnippets/Processing/IncludeProcessor.cs b/src/MarkdownSnippets/Processing/IncludeProcessor.cs index d7afcd57..41b253c0 100644 --- a/src/MarkdownSnippets/Processing/IncludeProcessor.cs +++ b/src/MarkdownSnippets/Processing/IncludeProcessor.cs @@ -210,6 +210,7 @@ static IEnumerable BuildMultiple(Line line, string? path, Include include, static bool ShouldWriteIncludeOnDiffLine(string line) => SnippetKey.IsSnippetLine(line) || line.StartsWith("") || + line.StartsWith("[//]: # (endSnippet)") || line.EndsWith("```") || line.StartsWith('|') || line.EndsWith('|'); diff --git a/src/MarkdownSnippets/Processing/MarkdownProcessor.cs b/src/MarkdownSnippets/Processing/MarkdownProcessor.cs index ca1aeea3..0acf2b33 100644 --- a/src/MarkdownSnippets/Processing/MarkdownProcessor.cs +++ b/src/MarkdownSnippets/Processing/MarkdownProcessor.cs @@ -181,20 +181,20 @@ Action CreateIndentedAppendLine(string indent) => s => continue; } - void AppendSnippet(string key1) + void AppendSnippet(string key1, bool useLinkRefFormat = false) { builder.Clear(); var indentedAppendLine = CreateIndentedAppendLine(line.LeadingWhitespace); - ProcessSnippetLine(indentedAppendLine, missingSnippets, usedSnippets, key1, relativePath, line); + ProcessSnippetLine(indentedAppendLine, missingSnippets, usedSnippets, key1, relativePath, line, useLinkRefFormat); builder.TrimEnd(); line.Current = builder.ToString(); } - void AppendWebSnippet(string url, string snippetKey, string? viewUrl = null) + void AppendWebSnippet(string url, string snippetKey, string? viewUrl = null, bool useLinkRefFormat = false) { builder.Clear(); var indentedAppendLine = CreateIndentedAppendLine(line.LeadingWhitespace); - ProcessWebSnippetLine(indentedAppendLine, missingSnippets, usedSnippets, url, snippetKey, viewUrl, line); + ProcessWebSnippetLine(indentedAppendLine, missingSnippets, usedSnippets, url, snippetKey, viewUrl, line, useLinkRefFormat); builder.TrimEnd(); line.Current = builder.ToString(); } @@ -244,6 +244,34 @@ void AppendWebSnippet(string url, string snippetKey, string? viewUrl = null) continue; } + if (SnippetKey.ExtractLinkRefCommentSnippet(line, out key)) + { + AppendSnippet(key, useLinkRefFormat: true); + + index++; + + lines.RemoveUntil( + index, + "[//]: # (endSnippet)", + relativePath, + line); + continue; + } + + if (SnippetKey.ExtractLinkRefCommentWebSnippet(line, out url, out snippetKey, out viewUrl)) + { + AppendWebSnippet(url, snippetKey, viewUrl, useLinkRefFormat: true); + + index++; + + lines.RemoveUntil( + index, + "[//]: # (endSnippet)", + relativePath, + line); + continue; + } + if (line.Current.TrimStart() == "") { tocLine = line; @@ -292,14 +320,16 @@ bool ValidateContent(string? relativePath, Line line, List vali return true; } - void ProcessSnippetLine(Action appendLine, List missings, List used, string key, string? relativePath, Line line) + void ProcessSnippetLine(Action appendLine, List missings, List used, string key, string? relativePath, Line line, bool useLinkRefFormat = false) { - appendLine($""); + var startMarker = useLinkRefFormat ? $"[//]: # (snippet: {key})" : $""; + var endMarker = useLinkRefFormat ? "[//]: # (endSnippet)" : ""; + appendLine(startMarker); if (TryGetSnippets(key, relativePath, line.Path, out var snippetsForKey)) { appendSnippets(key, snippetsForKey, appendLine); - appendLine(""); + appendLine(endMarker); used.AddRange(snippetsForKey); return; } @@ -309,14 +339,19 @@ void ProcessSnippetLine(Action appendLine, List missings appendLine("```"); appendLine($"** Could not find snippet '{key}' **"); appendLine("```"); - appendLine(""); + appendLine(endMarker); } - void ProcessWebSnippetLine(Action appendLine, List missings, List used, string url, string snippetKey, string? viewUrl, Line line) + void ProcessWebSnippetLine(Action appendLine, List missings, List used, string url, string snippetKey, string? viewUrl, Line line, bool useLinkRefFormat = false) { - var commentText = viewUrl == null - ? $"" - : $""; + var endMarker = useLinkRefFormat ? "[//]: # (endSnippet)" : ""; + var commentText = useLinkRefFormat + ? (viewUrl == null + ? $"[//]: # (web-snippet: {url}#{snippetKey})" + : $"[//]: # (web-snippet: {url}#{snippetKey} {viewUrl})") + : (viewUrl == null + ? $"" + : $""); appendLine(commentText); // Download file content try @@ -329,7 +364,7 @@ void ProcessWebSnippetLine(Action appendLine, List missi appendLine("```"); appendLine($"** Could not fetch or parse web-snippet '{url}#{snippetKey}' **"); appendLine("```"); - appendLine(""); + appendLine(endMarker); return; } // Extract snippets from content @@ -343,7 +378,7 @@ void ProcessWebSnippetLine(Action appendLine, List missi appendLine("```"); appendLine($"** Could not find snippet '{snippetKey}' in '{url}' **"); appendLine("```"); - appendLine(""); + appendLine(endMarker); return; } // Create new snippet with viewUrl if provided @@ -359,7 +394,7 @@ void ProcessWebSnippetLine(Action appendLine, List missi expressiveCode: found.ExpressiveCode, viewUrl: viewUrl); appendSnippets(snippetKey, [snippetToAppend], appendLine); - appendLine(""); + appendLine(endMarker); used.Add(snippetToAppend); } catch @@ -369,7 +404,7 @@ void ProcessWebSnippetLine(Action appendLine, List missi appendLine("```"); appendLine($"** Could not fetch or parse web-snippet '{url}#{snippetKey}' **"); appendLine("```"); - appendLine(""); + appendLine(endMarker); } } diff --git a/src/MarkdownSnippets/Processing/SnippetKey.cs b/src/MarkdownSnippets/Processing/SnippetKey.cs index b858f8f3..76dcf110 100644 --- a/src/MarkdownSnippets/Processing/SnippetKey.cs +++ b/src/MarkdownSnippets/Processing/SnippetKey.cs @@ -158,4 +158,86 @@ public static bool IsStartCommentWebSnippetLine(string line) => public static bool IsStartCommentWebSnippetLine(CharSpan line) => line.StartsWith("