Skip to content

Commit 0728f7d

Browse files
Add section link support for READMEs (#9193)
* Add auto identifiers merkdig extension * Re-add UseReferralLinks markdig extension * Allow internal section links in markdown * Add unit tests
1 parent 6bae10c commit 0728f7d

2 files changed

Lines changed: 34 additions & 15 deletions

File tree

src/NuGetGallery/Services/MarkdownService.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,7 @@ private RenderedMarkdownResult GetHtmlFromMarkdownMarkdig(string markdownString,
208208
.UseEmojiAndSmiley()
209209
.UseAutoLinks()
210210
.UseReferralLinks("noopener noreferrer nofollow")
211+
.UseAutoIdentifiers()
211212
.UseEmphasisExtras(EmphasisExtraOptions.Strikethrough)
212213
.DisableHtml() //block inline html
213214
.UseBootstrap()
@@ -267,7 +268,10 @@ private RenderedMarkdownResult GetHtmlFromMarkdownMarkdig(string markdownString,
267268
// Allow only http or https links in markdown. Transform link to https for known domains.
268269
if (!PackageHelper.TryPrepareUrlForRendering(linkInline.Url, out string readyUriString))
269270
{
270-
linkInline.Url = string.Empty;
271+
if (!linkInline.Url.StartsWith("#")) //allow internal section links
272+
{
273+
linkInline.Url = string.Empty;
274+
}
271275
}
272276
else
273277
{

tests/NuGetGallery.Facts/Services/MarkdownServiceFacts.cs

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -54,15 +54,15 @@ public void EncodesHtmlInMarkdown(string originalMd, string expectedHtml, bool i
5454
}
5555

5656
[Theory]
57-
[InlineData("# Heading", "<h1>Heading</h1>", true, 0)]
57+
[InlineData("# Heading", "<h1 id=\"heading\">Heading</h1>", true, 0)]
5858
[InlineData("# Heading", "<h1>Heading</h1>", false, 0)]
59-
[InlineData("# Heading", "<h2>Heading</h2>", true, 1)]
59+
[InlineData("# Heading", "<h2 id=\"heading\">Heading</h2>", true, 1)]
6060
[InlineData("# Heading", "<h2>Heading</h2>", false, 1)]
61-
[InlineData("# Heading", "<h6>Heading</h6>", true, 6)]
61+
[InlineData("# Heading", "<h6 id=\"heading\">Heading</h6>", true, 6)]
6262
[InlineData("# Heading", "<h6>Heading</h6>", false, 6)]
63-
[InlineData("# Heading", "<h6>Heading</h6>", true, 7)]
63+
[InlineData("# Heading", "<h6 id=\"heading\">Heading</h6>", true, 7)]
6464
[InlineData("# Heading", "<h6>Heading</h6>", false, 7)]
65-
[InlineData("# Heading", "<h6>Heading</h6>", true, 5)]
65+
[InlineData("# Heading", "<h6 id=\"heading\">Heading</h6>", true, 5)]
6666
[InlineData("# Heading", "<h6>Heading</h6>", false, 5)]
6767
public void EncodesHtmlInMarkdownWithAdaptiveHeader(string originalMd, string expectedHtml, bool isMarkdigMdRenderingEnabled, int incrementHeadersBy)
6868
{
@@ -71,11 +71,11 @@ public void EncodesHtmlInMarkdownWithAdaptiveHeader(string originalMd, string ex
7171
}
7272

7373
[Theory]
74-
[InlineData("# Heading", "<h2>Heading</h2>", false, true)]
74+
[InlineData("# Heading", "<h2 id=\"heading\">Heading</h2>", false, true)]
7575
[InlineData("# Heading", "<h2>Heading</h2>", false, false)]
7676
[InlineData("<!-- foo --> <!-- foo \n bar --> baz", "<p>baz</p>", false, true)]
7777
[InlineData("<!-- foo --> <!-- foo \n bar --> baz", "<p>baz</p>", false, false)]
78-
[InlineData("\ufeff# Heading with BOM", "<h2>Heading with BOM</h2>", false, true)]
78+
[InlineData("\ufeff# Heading with BOM", "<h2 id=\"heading-with-bom\">Heading with BOM</h2>", false, true)]
7979
[InlineData("\ufeff# Heading with BOM", "<h2>Heading with BOM</h2>", false, false)]
8080
[InlineData("- List", "<ul>\n<li>List</li>\n</ul>", false, true)]
8181
[InlineData("- List", "<ul>\r\n<li>List</li>\r\n</ul>", false, false)]
@@ -103,8 +103,8 @@ public void EncodesHtmlInMarkdownWithAdaptiveHeader(string originalMd, string ex
103103
[InlineData("![image](https://www.asp.net/fake.jpg)", "<p><img src=\"https://www.asp.net/fake.jpg\" alt=\"image\" /></p>", false, false)]
104104
[InlineData("![image](http://www.otherurl.net/fake.jpg)", "<p><img src=\"https://www.otherurl.net/fake.jpg\" class=\"img-fluid\" alt=\"image\" /></p>", true, true)]
105105
[InlineData("![image](http://www.otherurl.net/fake.jpg)", "<p><img src=\"https://www.otherurl.net/fake.jpg\" alt=\"image\" /></p>", true, false)]
106-
[InlineData("## License\n\tLicensed under the Apache License, Version 2.0 (the \"License\");", "<h3>License</h3>\n<pre><code>Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);\n</code></pre>", false, true)]
107-
[InlineData("## License\n\tLicensed under the Apache License, Version 2.0 (the \"License\");", "<h3>License</h3>\n<pre><code>Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);\n</code></pre>", false, true)]
106+
[InlineData("## License\n\tLicensed under the Apache License, Version 2.0 (the \"License\");", "<h3 id=\"license\">License</h3>\n<pre><code>Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);\n</code></pre>", false, true)]
107+
[InlineData("## License\n\tLicensed under the Apache License, Version 2.0 (the \"License\");", "<h3 id=\"license\">License</h3>\n<pre><code>Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);\n</code></pre>", false, true)]
108108
public void ConvertsMarkdownToHtml(string originalMd, string expectedHtml, bool imageRewriteExpected, bool isMarkdigMdRenderingEnabled)
109109
{
110110
_featureFlagService.Setup(x => x.IsMarkdigMdRenderingEnabled()).Returns(isMarkdigMdRenderingEnabled);
@@ -223,12 +223,11 @@ Some text
223223
Assert.False(readMeResult.ImagesRewritten);
224224
}
225225

226-
[Fact]
227-
public void TestToHtmlWithAutoLinks()
226+
[Theory]
227+
[InlineData("This is a http://www.google.com URL and https://www.google.com", "<p>This is a <a href=\"http://www.google.com/\" rel=\"noopener noreferrer nofollow\">http://www.google.com</a> URL and <a href=\"https://www.google.com/\" rel=\"noopener noreferrer nofollow\">https://www.google.com</a></p>")]
228+
[InlineData("# This is a heading\n[Link](#this-is-a-heading)", "<h2 id=\"this-is-a-heading\">This is a heading</h2>\n<p><a href=\"#this-is-a-heading\" rel=\"noopener noreferrer nofollow\">Link</a></p>")]
229+
public void TestToHtmlWithAutoLinks(string originalMd, string expectedHtml)
228230
{
229-
var originalMd = "This is a http://www.google.com URL and https://www.google.com";
230-
231-
var expectedHtml = "<p>This is a <a href=\"http://www.google.com/\" rel=\"noopener noreferrer nofollow\">http://www.google.com</a> URL and <a href=\"https://www.google.com/\" rel=\"noopener noreferrer nofollow\">https://www.google.com</a></p>";
232231
_featureFlagService.Setup(x => x.IsMarkdigMdRenderingEnabled()).Returns(true);
233232
var readMeResult = _markdownService.GetHtmlFromMarkdown(originalMd);
234233
Assert.Equal(expectedHtml, readMeResult.Content);
@@ -244,6 +243,22 @@ public void TestToHtmlWithStrikethrough(string originalMd, string expectedHtml)
244243
Assert.Equal(expectedHtml, readMeResult.Content);
245244
Assert.False(readMeResult.ImagesRewritten);
246245
}
246+
247+
[Theory]
248+
[InlineData("# Heading", "<h2 id=\"heading\">Heading</h2>")]
249+
[InlineData("# This is a heading", "<h2 id=\"this-is-a-heading\">This is a heading</h2>")]
250+
[InlineData("# This - is a &@! heading _ with . and ! -", "<h2 id=\"this-is-a-heading_with.and\">This - is a &amp;@! heading _ with . and ! -</h2>")]
251+
[InlineData("# This is a *heading*", "<h2 id=\"this-is-a-heading\">This is a <em>heading</em></h2>")]
252+
[InlineData("# This is a [heading](https://www.google.com)", "<h2 id=\"this-is-a-heading\">This is a <a href=\"https://www.google.com/\" rel=\"noopener noreferrer nofollow\">heading</a></h2>")]
253+
[InlineData("# Heading\n# Heading", "<h2 id=\"heading\">Heading</h2>\n<h2 id=\"heading-1\">Heading</h2>")]
254+
[InlineData("# 1.0 This is a heading", "<h2 id=\"this-is-a-heading\">1.0 This is a heading</h2>")]
255+
[InlineData("# 1.0 & ^ % *\n# 1.0 & ^ % *", "<h2 id=\"section\">1.0 &amp; ^ % *</h2>\n<h2 id=\"section-1\">1.0 &amp; ^ % *</h2>")]
256+
public void TestToHtmlWithAutoIdentifiers(string originalMd, string expectedHtml)
257+
{
258+
_featureFlagService.Setup(x => x.IsMarkdigMdRenderingEnabled()).Returns(true);
259+
var readMeResult = _markdownService.GetHtmlFromMarkdown(originalMd);
260+
Assert.Equal(expectedHtml, readMeResult.Content);
261+
}
247262
}
248263
}
249264
}

0 commit comments

Comments
 (0)