From 367ffdb1a9f8d262266b58d5276987bf5491b10f Mon Sep 17 00:00:00 2001 From: Jan Calanog Date: Mon, 20 Jan 2025 11:39:22 +0100 Subject: [PATCH 1/8] Add support for extensionless URLs --- src/Elastic.Markdown/IO/MarkdownFile.cs | 4 ++- .../DiagnosticLinkInlineParser.cs | 2 +- src/Elastic.Markdown/Slices/HtmlWriter.cs | 27 ++++++++++++++++++- src/docs-builder/Http/DocumentationWebHost.cs | 15 ++++++++--- .../Inline/AnchorLinkTests.cs | 8 +++--- .../Inline/DirectiveBlockLinkTests.cs | 2 +- .../Inline/InlineLinkTests.cs | 14 +++++----- 7 files changed, 54 insertions(+), 18 deletions(-) diff --git a/src/Elastic.Markdown/IO/MarkdownFile.cs b/src/Elastic.Markdown/IO/MarkdownFile.cs index 64ec90f52..c765d49c9 100644 --- a/src/Elastic.Markdown/IO/MarkdownFile.cs +++ b/src/Elastic.Markdown/IO/MarkdownFile.cs @@ -59,7 +59,9 @@ public string? NavigationTitle public string FilePath { get; } public string FileName { get; } - public string Url => $"{UrlPathPrefix}/{RelativePath.Replace(".md", ".html")}"; + public string Url => Path.GetFileName(RelativePath) == "index.md" + ? $"{UrlPathPrefix}/{RelativePath.Remove(RelativePath.LastIndexOf("index.md", StringComparison.Ordinal), "index.md".Length)}" + : $"{UrlPathPrefix}/{RelativePath.Remove(RelativePath.LastIndexOf(".md", StringComparison.Ordinal), 3)}"; public int NavigationIndex { get; set; } diff --git a/src/Elastic.Markdown/Myst/InlineParsers/DiagnosticLinkInlineParser.cs b/src/Elastic.Markdown/Myst/InlineParsers/DiagnosticLinkInlineParser.cs index b6d8472ab..90e0ee925 100644 --- a/src/Elastic.Markdown/Myst/InlineParsers/DiagnosticLinkInlineParser.cs +++ b/src/Elastic.Markdown/Myst/InlineParsers/DiagnosticLinkInlineParser.cs @@ -149,7 +149,7 @@ public override bool Match(InlineProcessor processor, ref StringSlice slice) } if (url.EndsWith(".md")) - link.Url = Path.ChangeExtension(url, ".html"); + link.Url = url.Remove(url.LastIndexOf(".md", StringComparison.Ordinal), 3); // rooted links might need the configured path prefix to properly link var prefix = processor.GetBuildContext().UrlPathPrefix; if (url.StartsWith("/") && !string.IsNullOrWhiteSpace(prefix)) diff --git a/src/Elastic.Markdown/Slices/HtmlWriter.cs b/src/Elastic.Markdown/Slices/HtmlWriter.cs index aac30edf6..e856ac8c1 100644 --- a/src/Elastic.Markdown/Slices/HtmlWriter.cs +++ b/src/Elastic.Markdown/Slices/HtmlWriter.cs @@ -77,7 +77,32 @@ public async Task WriteAsync(IFileInfo outputFile, MarkdownFile markdown, Cancel outputFile.Directory.Create(); var rendered = await RenderLayout(markdown, ctx); - var path = Path.ChangeExtension(outputFile.FullName, ".html"); + // var path = Path.ChangeExtension(outputFile.FullName, ".html"); + string path; + if (outputFile.Name == "index.md") + { + path = Path.ChangeExtension(outputFile.FullName, ".html"); + } + else + { + if (outputFile.Directory is null) + { + path = Path.GetFileNameWithoutExtension(outputFile.Name) + ".html"; + } + else + { + + var dir = Path.Combine(outputFile.Directory.FullName, Path.GetFileNameWithoutExtension(outputFile.Name)); + if (!_writeFileSystem.Directory.Exists(dir)) + _writeFileSystem.Directory.CreateDirectory(dir); + path = Path.Combine([ + outputFile.Directory.FullName, + Path.GetFileNameWithoutExtension(outputFile.Name), + "index.html"] + ); + } + } + await _writeFileSystem.File.WriteAllTextAsync(path, rendered, ctx); } diff --git a/src/docs-builder/Http/DocumentationWebHost.cs b/src/docs-builder/Http/DocumentationWebHost.cs index 9e7803948..d0bf0e313 100644 --- a/src/docs-builder/Http/DocumentationWebHost.cs +++ b/src/docs-builder/Http/DocumentationWebHost.cs @@ -91,9 +91,18 @@ private void SetUpRoutes() private static async Task ServeDocumentationFile(ReloadableGeneratorState holder, string slug, Cancel ctx) { var generator = holder.Generator; - slug = slug.Replace(".html", ".md"); - if (!generator.DocumentationSet.FlatMappedFiles.TryGetValue(slug, out var documentationFile)) - return Results.NotFound(); + + // For now, the logic is backwards compatible. + // Hence, both http://localhost:5000/migration/versioning.html and http://localhost:5000/migration/versioning works, + // so it's easier to copy links from issues created during the bug bounty. + // However, we can remove this logic in the future and only support links without the .html extension. + var s = Path.GetExtension(slug) == string.Empty ? Path.Combine(slug, "index.md") : slug.Replace(".html", ".md"); + if (!generator.DocumentationSet.FlatMappedFiles.TryGetValue(s, out var documentationFile)) + { + s = Path.GetExtension(slug) == string.Empty ? slug + ".md" : s.Replace("/index.md", ".md"); + if (!generator.DocumentationSet.FlatMappedFiles.TryGetValue(s, out documentationFile)) + return Results.NotFound(); + } switch (documentationFile) { diff --git a/tests/Elastic.Markdown.Tests/Inline/AnchorLinkTests.cs b/tests/Elastic.Markdown.Tests/Inline/AnchorLinkTests.cs index 2b7c91311..ec52f71bd 100644 --- a/tests/Elastic.Markdown.Tests/Inline/AnchorLinkTests.cs +++ b/tests/Elastic.Markdown.Tests/Inline/AnchorLinkTests.cs @@ -71,7 +71,7 @@ [Sub Requirements](testing/req.md#sub-requirements) public void GeneratesHtml() => // language=html Html.Should().Contain( - """

Sub Requirements

""" + """

Sub Requirements

""" ); [Fact] @@ -89,7 +89,7 @@ [Sub Requirements](testing/req.md#new-reqs) public void GeneratesHtml() => // language=html Html.Should().Contain( - """

Sub Requirements

""" + """

Sub Requirements

""" ); [Fact] @@ -106,7 +106,7 @@ public class ExternalPageAnchorAutoTitleTests(ITestOutputHelper output) : Anchor public void GeneratesHtml() => // language=html Html.Should().Contain( - """

Special Requirements > Sub Requirements

""" + """

Special Requirements > Sub Requirements

""" ); [Fact] @@ -142,7 +142,7 @@ [Sub Requirements](testing/req.md#sub-requirements2) public void GeneratesHtml() => // language=html Html.Should().Contain( - """

Sub Requirements

""" + """

Sub Requirements

""" ); [Fact] diff --git a/tests/Elastic.Markdown.Tests/Inline/DirectiveBlockLinkTests.cs b/tests/Elastic.Markdown.Tests/Inline/DirectiveBlockLinkTests.cs index 2be2c78d5..c20c9bb49 100644 --- a/tests/Elastic.Markdown.Tests/Inline/DirectiveBlockLinkTests.cs +++ b/tests/Elastic.Markdown.Tests/Inline/DirectiveBlockLinkTests.cs @@ -69,7 +69,7 @@ [Sub Requirements](testing/req.md#hint_ref) public void GeneratesHtml() => // language=html Html.Should().Contain( - """

Sub Requirements

""" + """

Sub Requirements

""" ); [Fact] diff --git a/tests/Elastic.Markdown.Tests/Inline/InlineLinkTests.cs b/tests/Elastic.Markdown.Tests/Inline/InlineLinkTests.cs index 3a90cf828..3f3728ce9 100644 --- a/tests/Elastic.Markdown.Tests/Inline/InlineLinkTests.cs +++ b/tests/Elastic.Markdown.Tests/Inline/InlineLinkTests.cs @@ -60,7 +60,7 @@ public class LinkToPageTests(ITestOutputHelper output) : LinkTestBase(output, public void GeneratesHtml() => // language=html Html.Should().Contain( - """

Requirements

""" + """

Requirements

""" ); [Fact] @@ -83,7 +83,7 @@ public class InsertPageTitleTests(ITestOutputHelper output) : LinkTestBase(outpu public void GeneratesHtml() => // language=html Html.Should().Contain( - """

Special Requirements

""" + """

Special Requirements

""" ); [Fact] @@ -108,7 +108,7 @@ public class LinkReferenceTest(ITestOutputHelper output) : LinkTestBase(output, public void GeneratesHtml() => // language=html Html.Should().Contain( - """

test

""" + """

test

""" ); [Fact] @@ -134,7 +134,7 @@ public void GeneratesHtml() => // language=html Html.Should().Contain( // TODO: The link is not rendered correctly yet, will be fixed in a follow-up - """

test

""" + """

test

""" ); [Fact] @@ -160,7 +160,7 @@ public void GeneratesHtml() => // language=html Html.Should().Contain( // TODO: The link is not rendered correctly yet, will be fixed in a follow-up - """

Go to test

""" + """

Go to test

""" ); [Fact] @@ -227,10 +227,10 @@ public void GeneratesHtml() => Html.TrimEnd().Should().Be("""

Links:

"""); From e36707743507ffcde6839420e59437838a874ba5 Mon Sep 17 00:00:00 2001 From: Jan Calanog Date: Mon, 20 Jan 2025 11:46:53 +0100 Subject: [PATCH 2/8] format --- src/docs-builder/Http/DocumentationWebHost.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/docs-builder/Http/DocumentationWebHost.cs b/src/docs-builder/Http/DocumentationWebHost.cs index d0bf0e313..7f805c9ea 100644 --- a/src/docs-builder/Http/DocumentationWebHost.cs +++ b/src/docs-builder/Http/DocumentationWebHost.cs @@ -99,9 +99,9 @@ private static async Task ServeDocumentationFile(ReloadableGeneratorSta var s = Path.GetExtension(slug) == string.Empty ? Path.Combine(slug, "index.md") : slug.Replace(".html", ".md"); if (!generator.DocumentationSet.FlatMappedFiles.TryGetValue(s, out var documentationFile)) { - s = Path.GetExtension(slug) == string.Empty ? slug + ".md" : s.Replace("/index.md", ".md"); - if (!generator.DocumentationSet.FlatMappedFiles.TryGetValue(s, out documentationFile)) - return Results.NotFound(); + s = Path.GetExtension(slug) == string.Empty ? slug + ".md" : s.Replace("/index.md", ".md"); + if (!generator.DocumentationSet.FlatMappedFiles.TryGetValue(s, out documentationFile)) + return Results.NotFound(); } switch (documentationFile) From 3c5bab90cbd9071c8b2ee2d124efa8b85ecf8be1 Mon Sep 17 00:00:00 2001 From: Jan Calanog Date: Mon, 20 Jan 2025 11:50:49 +0100 Subject: [PATCH 3/8] Refactor --- src/Elastic.Markdown/Slices/HtmlWriter.cs | 26 +++++++++-------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/src/Elastic.Markdown/Slices/HtmlWriter.cs b/src/Elastic.Markdown/Slices/HtmlWriter.cs index e856ac8c1..73082f808 100644 --- a/src/Elastic.Markdown/Slices/HtmlWriter.cs +++ b/src/Elastic.Markdown/Slices/HtmlWriter.cs @@ -85,22 +85,16 @@ public async Task WriteAsync(IFileInfo outputFile, MarkdownFile markdown, Cancel } else { - if (outputFile.Directory is null) - { - path = Path.GetFileNameWithoutExtension(outputFile.Name) + ".html"; - } - else - { - - var dir = Path.Combine(outputFile.Directory.FullName, Path.GetFileNameWithoutExtension(outputFile.Name)); - if (!_writeFileSystem.Directory.Exists(dir)) - _writeFileSystem.Directory.CreateDirectory(dir); - path = Path.Combine([ - outputFile.Directory.FullName, - Path.GetFileNameWithoutExtension(outputFile.Name), - "index.html"] - ); - } + var dir = outputFile.Directory is null + ? null + : Path.Combine(outputFile.Directory.FullName, Path.GetFileNameWithoutExtension(outputFile.Name)); + + if (dir is not null && !_writeFileSystem.Directory.Exists(dir)) + _writeFileSystem.Directory.CreateDirectory(dir); + + path = dir is null + ? Path.GetFileNameWithoutExtension(outputFile.Name) + ".html" + : Path.Combine(dir, "index.html"); } await _writeFileSystem.File.WriteAllTextAsync(path, rendered, ctx); From 6fa8dfef6c9d49264db65d50d1c6ef9aa9da77f5 Mon Sep 17 00:00:00 2001 From: Jan Calanog Date: Mon, 20 Jan 2025 13:04:47 +0100 Subject: [PATCH 4/8] Remove commented line --- src/Elastic.Markdown/Slices/HtmlWriter.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Elastic.Markdown/Slices/HtmlWriter.cs b/src/Elastic.Markdown/Slices/HtmlWriter.cs index 73082f808..459911ef5 100644 --- a/src/Elastic.Markdown/Slices/HtmlWriter.cs +++ b/src/Elastic.Markdown/Slices/HtmlWriter.cs @@ -77,7 +77,6 @@ public async Task WriteAsync(IFileInfo outputFile, MarkdownFile markdown, Cancel outputFile.Directory.Create(); var rendered = await RenderLayout(markdown, ctx); - // var path = Path.ChangeExtension(outputFile.FullName, ".html"); string path; if (outputFile.Name == "index.md") { From 2219aa4532be6514141aaa604ca50b5e82846a78 Mon Sep 17 00:00:00 2001 From: Jan Calanog Date: Fri, 31 Jan 2025 10:07:22 +0100 Subject: [PATCH 5/8] Fix tests --- tests/Elastic.Markdown.Tests/Inline/AnchorLinkTests.cs | 2 +- tests/Elastic.Markdown.Tests/Inline/InlineAnchorTests.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Elastic.Markdown.Tests/Inline/AnchorLinkTests.cs b/tests/Elastic.Markdown.Tests/Inline/AnchorLinkTests.cs index 68edaa793..cce5a5ce3 100644 --- a/tests/Elastic.Markdown.Tests/Inline/AnchorLinkTests.cs +++ b/tests/Elastic.Markdown.Tests/Inline/AnchorLinkTests.cs @@ -166,7 +166,7 @@ [Heading inside dropdown](testing/req.md#heading-inside-dropdown) public void GeneratesHtml() => // language=html Html.Should().Contain( - """Heading inside dropdown""" + """Heading inside dropdown""" ); [Fact] diff --git a/tests/Elastic.Markdown.Tests/Inline/InlineAnchorTests.cs b/tests/Elastic.Markdown.Tests/Inline/InlineAnchorTests.cs index 8fdde44cf..e2b7b41d8 100644 --- a/tests/Elastic.Markdown.Tests/Inline/InlineAnchorTests.cs +++ b/tests/Elastic.Markdown.Tests/Inline/InlineAnchorTests.cs @@ -203,7 +203,7 @@ [Sub Requirements](testing/req.md#custom-anchor) public void GeneratesHtml() => // language=html Html.Should().Contain( - """

Sub Requirements

""" + """

Sub Requirements

""" ); [Fact] From 9e88dad7bd09737bb3bc832c96f75b89da5a1629 Mon Sep 17 00:00:00 2001 From: Jan Calanog Date: Thu, 6 Feb 2025 16:14:48 +0100 Subject: [PATCH 6/8] Also strip index --- .../Myst/InlineParsers/DiagnosticLinkInlineParser.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Elastic.Markdown/Myst/InlineParsers/DiagnosticLinkInlineParser.cs b/src/Elastic.Markdown/Myst/InlineParsers/DiagnosticLinkInlineParser.cs index 34bcdc601..45f360957 100644 --- a/src/Elastic.Markdown/Myst/InlineParsers/DiagnosticLinkInlineParser.cs +++ b/src/Elastic.Markdown/Myst/InlineParsers/DiagnosticLinkInlineParser.cs @@ -239,7 +239,11 @@ private static void ValidateAnchor(InlineProcessor processor, MarkdownFile markd private static void UpdateLinkUrl(LinkInline link, string url, string? anchor, string urlPathPrefix) { if (url.EndsWith(".md")) - url = url.Remove(url.LastIndexOf(".md", StringComparison.Ordinal), 3); + { + url = url.EndsWith("index.md") + ? url.Remove(url.LastIndexOf("index.md", StringComparison.Ordinal), "index.md".Length) + : url.Remove(url.LastIndexOf(".md", StringComparison.Ordinal), ".md".Length); + } if (url.StartsWith("/") && !string.IsNullOrWhiteSpace(urlPathPrefix)) url = $"{urlPathPrefix.TrimEnd('/')}{url}"; From f6f17c6c9d2f4ae412db36cd6454d6680dd3060e Mon Sep 17 00:00:00 2001 From: Jan Calanog Date: Fri, 7 Feb 2025 10:13:58 +0100 Subject: [PATCH 7/8] Update src/Elastic.Markdown/Myst/InlineParsers/DiagnosticLinkInlineParser.cs --- .../Myst/InlineParsers/DiagnosticLinkInlineParser.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Elastic.Markdown/Myst/InlineParsers/DiagnosticLinkInlineParser.cs b/src/Elastic.Markdown/Myst/InlineParsers/DiagnosticLinkInlineParser.cs index 45f360957..959f2b1a4 100644 --- a/src/Elastic.Markdown/Myst/InlineParsers/DiagnosticLinkInlineParser.cs +++ b/src/Elastic.Markdown/Myst/InlineParsers/DiagnosticLinkInlineParser.cs @@ -240,7 +240,7 @@ private static void UpdateLinkUrl(LinkInline link, string url, string? anchor, s { if (url.EndsWith(".md")) { - url = url.EndsWith("index.md") + url = url.EndsWith("/index.md") ? url.Remove(url.LastIndexOf("index.md", StringComparison.Ordinal), "index.md".Length) : url.Remove(url.LastIndexOf(".md", StringComparison.Ordinal), ".md".Length); } From 5a8694d6f369271e9f76bec720687436a00204b4 Mon Sep 17 00:00:00 2001 From: Jan Calanog Date: Mon, 10 Feb 2025 22:30:35 +0100 Subject: [PATCH 8/8] Fix test --- tests/Elastic.Markdown.Tests/Inline/InlineLinkTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/Elastic.Markdown.Tests/Inline/InlineLinkTests.cs b/tests/Elastic.Markdown.Tests/Inline/InlineLinkTests.cs index 7d75d9b29..8e2e3959e 100644 --- a/tests/Elastic.Markdown.Tests/Inline/InlineLinkTests.cs +++ b/tests/Elastic.Markdown.Tests/Inline/InlineLinkTests.cs @@ -226,10 +226,10 @@ public void GeneratesHtml() => Html.TrimEnd().Should().Be("""

Links:

""");