diff --git a/src/NuGetizer.Tasks/CreatePackage.cs b/src/NuGetizer.Tasks/CreatePackage.cs index 92fb15b7..69d5f866 100644 --- a/src/NuGetizer.Tasks/CreatePackage.cs +++ b/src/NuGetizer.Tasks/CreatePackage.cs @@ -92,13 +92,13 @@ public Manifest CreateManifest() metadata.DevelopmentDependency = true; if (Manifest.TryGetMetadata("Title", out var title)) - metadata.Title = title; + metadata.Title = title.TrimIndent(); if (Manifest.TryGetMetadata("Description", out var description)) - metadata.Description = description; + metadata.Description = description.TrimIndent(); if (Manifest.TryGetMetadata("Summary", out var summary)) - metadata.Summary = summary; + metadata.Summary = summary.TrimIndent(); if (Manifest.TryGetMetadata("Readme", out var readme)) metadata.Readme = readme; @@ -171,7 +171,7 @@ public Manifest CreateManifest() metadata.Icon = icon; if (Manifest.TryGetMetadata("ReleaseNotes", out var releaseNotes)) - metadata.ReleaseNotes = releaseNotes; + metadata.ReleaseNotes = releaseNotes.TrimIndent(); if (Manifest.TryGetMetadata("Tags", out var tags)) metadata.Tags = tags; diff --git a/src/NuGetizer.Tasks/Extensions.cs b/src/NuGetizer.Tasks/Extensions.cs index e0b26836..935419c5 100644 --- a/src/NuGetizer.Tasks/Extensions.cs +++ b/src/NuGetizer.Tasks/Extensions.cs @@ -131,6 +131,91 @@ public static Manifest GetManifest(this IPackageCoreReader packageReader) return null; } + public static string ReplaceLineEndings(this string value) + { + if (string.IsNullOrEmpty(value)) + return value; + + return value.Replace("\r\n", "\n").Replace("\r", "\n").Replace("\n", Environment.NewLine); + } + + public static string TrimIndent(this string value) + { + if (string.IsNullOrWhiteSpace(value)) + return ""; + + // Read lines without the newline characters + using var sr = new StringReader(value); + var lines = new List(); + string? line; + while ((line = sr.ReadLine()) != null) + { + lines.Add(line); + } + + // Find the first non-empty line + var start = 0; + while (start < lines.Count && string.IsNullOrWhiteSpace(lines[start])) + { + start++; + } + if (start == lines.Count) + return ""; + + // Determine the indentation prefix from the first content line + var firstContent = lines[start]; + var indentLen = 0; + while (indentLen < firstContent.Length && char.IsWhiteSpace(firstContent[indentLen])) + { + indentLen++; + } + var indentPrefix = firstContent.Substring(0, indentLen); + + // Find the last non-empty line + var end = lines.Count - 1; + while (end >= start && string.IsNullOrWhiteSpace(lines[end])) + { + end--; + } + + // Trim indentation from each line + var trimmedLines = new List(); + for (var i = start; i <= end; i++) + { + var ln = lines[i]; + var trimmed = ln.StartsWith(indentPrefix) ? ln.Substring(indentPrefix.Length) : ln; + trimmedLines.Add(trimmed); + } + + // Collapse like Markdown: join lines within paragraphs with space, paragraphs separated by double newline + var paragraphs = new List(); + var currentPara = new List(); + for (var i = 0; i < trimmedLines.Count; i++) + { + var ln = trimmedLines[i]; + if (string.IsNullOrWhiteSpace(ln)) + { + if (currentPara.Count > 0) + { + paragraphs.Add(string.Join(" ", currentPara.Select(l => l.TrimEnd()))); + currentPara.Clear(); + } + // Skip blanks, multiple blanks collapse to one break + } + else + { + currentPara.Add(ln); + } + } + if (currentPara.Count > 0) + { + paragraphs.Add(string.Join(" ", currentPara.Select(l => l.TrimEnd()))); + } + + // Join paragraphs with double newline + return string.Join(Environment.NewLine + Environment.NewLine, paragraphs); + } + public static void LogErrorCode(this TaskLoggingHelper log, string code, string message, params object[] messageArgs) => log.LogError(string.Empty, code, string.Empty, string.Empty, 0, 0, 0, 0, message, messageArgs); diff --git a/src/NuGetizer.Tests/CreatePackageTests.cs b/src/NuGetizer.Tests/CreatePackageTests.cs index 13048cdf..ed9b219f 100644 --- a/src/NuGetizer.Tests/CreatePackageTests.cs +++ b/src/NuGetizer.Tests/CreatePackageTests.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.IO.Compression; @@ -35,7 +34,16 @@ public CreatePackageTests(ITestOutputHelper output) { MetadataName.PackageId, "package" }, { MetadataName.Version, "1.0.0" }, { "Title", "title" }, - { "Description", "description" }, + { "Description", + """ + + This is the description. + Indent will be trimmed. + + + New paragraph preserved. + + """ }, { "Summary", "summary" }, { "Language", "en" }, @@ -111,7 +119,6 @@ public void when_creating_package_then_contains_all_metadata() Assert.Equal(task.Manifest.GetMetadata("PackageId"), metadata.Id); Assert.Equal(task.Manifest.GetMetadata("Version"), metadata.Version.ToString()); Assert.Equal(task.Manifest.GetMetadata("Title"), metadata.Title); - Assert.Equal(task.Manifest.GetMetadata("Description"), metadata.Description); Assert.Equal(task.Manifest.GetMetadata("Summary"), metadata.Summary); Assert.Equal(task.Manifest.GetMetadata("Language"), metadata.Language); Assert.Equal(task.Manifest.GetMetadata("Copyright"), metadata.Copyright); @@ -126,6 +133,15 @@ public void when_creating_package_then_contains_all_metadata() Assert.Equal(PackageType.Dependency.Name, item.Name); Assert.Equal(PackageType.EmptyVersion, item.Version); }); + + // C#-style triming is applied. + Assert.Equal( + """ + This is the description. Indent will be trimmed. + + New paragraph preserved. + """.ReplaceLineEndings(), + metadata.Description.ReplaceLineEndings()); } [Fact]