Skip to content

Commit 03e306d

Browse files
committed
Removed empty lines within <PackageReference> elements
1 parent 636660a commit 03e306d

3 files changed

Lines changed: 111 additions & 82 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
## TBD
44

55
- Added input validation for the mutually exclusive --backup and --dry-run command line options
6+
- Removed empty lines within `<PackageReference>` elements
67

78
## v1.0.1103.27 (November 3<sup>rd</sup>, 2024)
89
- Fixed [#5](https://github.com/icnocop/PackageReferenceVersionToAttribute/issues/5) - Fixed assembly binding redirection error which may occur when loading the extension.

src/PackageReferenceVersionToAttribute/ProjectConverter.cs

Lines changed: 110 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ namespace PackageReferenceVersionToAttribute
1111
using System.Threading.Tasks;
1212
using System.Xml;
1313
using System.Xml.Linq;
14+
using System.Xml.XPath;
1415
using Microsoft.Extensions.Logging;
1516
using Microsoft.Extensions.Options;
1617

@@ -54,103 +55,131 @@ public async Task ConvertAsync(IEnumerable<string> projectFilePaths)
5455
{
5556
try
5657
{
57-
if (string.IsNullOrEmpty(projectFilePath) || !File.Exists(projectFilePath))
58-
{
59-
continue;
60-
}
58+
await this.ConvertAsync(projectFilePath);
59+
}
60+
catch (Exception ex)
61+
{
62+
this.logger.LogError(ex, $"Error processing project file \"{projectFilePath}\"");
63+
}
64+
}
65+
}
6166

62-
string message = $"Converting PackageReference Version child elements to attributes in the project file \"{projectFilePath}\"...";
63-
this.logger.LogInformation(message);
67+
private async Task ConvertAsync(string projectFilePath)
68+
{
69+
if (string.IsNullOrEmpty(projectFilePath) || !File.Exists(projectFilePath))
70+
{
71+
return;
72+
}
6473

65-
var document = XDocument.Load(projectFilePath, LoadOptions.PreserveWhitespace);
74+
string message = $"Converting PackageReference Version child elements to attributes in the project file \"{projectFilePath}\"...";
75+
this.logger.LogInformation(message);
6676

67-
// Find all PackageReference elements with a <Version> child element
68-
// Use the XML namespace if one is set
69-
XNamespace ns = document.Root.GetDefaultNamespace();
77+
var document = XDocument.Load(projectFilePath, LoadOptions.PreserveWhitespace);
7078

71-
if (!string.IsNullOrWhiteSpace(ns.NamespaceName))
72-
{
73-
this.logger.LogInformation($"Found XML namespace \"{ns.NamespaceName}\".");
74-
}
79+
// Find all PackageReference elements with a <Version> child element
80+
// Use the XML namespace if one is set
81+
XNamespace ns = document.Root.GetDefaultNamespace();
7582

76-
// Query for PackageReference elements
77-
var packageReferences = document.Descendants(ns != null ? ns + "PackageReference" : "PackageReference")
78-
.Where(pr => pr.Element(ns != null ? ns + "Version" : "Version") != null)
79-
.ToList();
80-
if (!packageReferences.Any())
81-
{
82-
this.logger.LogInformation($"No PackageReference Version child elements found in the project file \"{projectFilePath}\".");
83-
continue;
84-
}
83+
if (!string.IsNullOrWhiteSpace(ns.NamespaceName))
84+
{
85+
this.logger.LogInformation($"Found XML namespace \"{ns.NamespaceName}\".");
86+
}
8587

86-
bool modified = false;
88+
// Query for PackageReference elements
89+
var packageReferenceVersionElements = document.Descendants(ns != null ? ns + "PackageReference" : "PackageReference")
90+
.Where(pr => pr.Element(ns != null ? ns + "Version" : "Version") != null)
91+
.ToList();
92+
if (!packageReferenceVersionElements.Any())
93+
{
94+
this.logger.LogInformation($"No PackageReference Version child elements found in the project file \"{projectFilePath}\".");
95+
return;
96+
}
8797

88-
if (this.options.Backup)
89-
{
90-
// backup project file
91-
this.fileService.BackupFile(projectFilePath);
92-
}
98+
bool modified = false;
9399

94-
// check out file from source control
95-
await this.sourceControlService.CheckOutFileAsync(projectFilePath);
100+
foreach (var packageReferenceVersionElement in packageReferenceVersionElements)
101+
{
102+
var versionElement = packageReferenceVersionElement.Element(ns != null ? ns + "Version" : "Version");
103+
if (versionElement != null)
104+
{
105+
// Move the Version element content to an attribute
106+
packageReferenceVersionElement.SetAttributeValue("Version", versionElement.Value);
107+
versionElement.Remove();
96108

97-
if (this.options.Force)
109+
// Check if the PackageReference is empty and set it to self-closing if so
110+
if (!packageReferenceVersionElement.HasElements)
98111
{
99-
this.fileService.RemoveReadOnlyAttribute(projectFilePath);
112+
// This will make sure it's treated as a self-closing tag when saved
113+
// Optionally, trim empty lines around the parent element
114+
packageReferenceVersionElement.RemoveNodes(); // Remove empty nodes
100115
}
101116

102-
foreach (var packageReference in packageReferences)
103-
{
104-
var versionElement = packageReference.Element(ns != null ? ns + "Version" : "Version");
105-
if (versionElement != null)
106-
{
107-
// Move the Version element content to an attribute
108-
packageReference.SetAttributeValue("Version", versionElement.Value);
109-
versionElement.Remove();
110-
111-
// Check if the PackageReference is empty and set it to self-closing if so
112-
if (!packageReference.HasElements)
113-
{
114-
// This will make sure it's treated as a self-closing tag when saved
115-
// Optionally, trim empty lines around the parent element
116-
packageReference.RemoveNodes(); // Remove empty nodes
117-
}
118-
119-
modified = true;
120-
}
121-
}
117+
modified = true;
118+
}
119+
}
120+
121+
// Save changes only if any modifications were made
122+
if (!modified)
123+
{
124+
return;
125+
}
126+
127+
if (this.options.Backup)
128+
{
129+
// backup project file
130+
this.fileService.BackupFile(projectFilePath);
131+
}
132+
133+
// check out file from source control
134+
await this.sourceControlService.CheckOutFileAsync(projectFilePath);
135+
136+
if (this.options.Force)
137+
{
138+
this.fileService.RemoveReadOnlyAttribute(projectFilePath);
139+
}
140+
141+
// remove empty lines within <PackageReference> elements
142+
// Select all <PackageReference> elements
143+
var packageReferenceElements = document.XPathSelectElements("//PackageReference");
144+
145+
foreach (var packageReferenceElement in packageReferenceElements)
146+
{
147+
// Get all the child nodes of the <PackageReference>
148+
var childNodes = packageReferenceElement.Nodes();
122149

123-
// Save changes if any modifications were made
124-
if (modified)
150+
// Iterate through the child nodes
151+
foreach (var node in childNodes)
152+
{
153+
// Check if the node is a text node and contains only whitespace
154+
if (node is XText textNode && string.IsNullOrWhiteSpace(textNode.Value))
125155
{
126-
var settings = new XmlWriterSettings
127-
{
128-
OmitXmlDeclaration = document.Declaration == null, // Preserve the XML declaration if it exists.
129-
Indent = false, // Prevents adding any extra indentation
130-
NewLineHandling = NewLineHandling.Replace,
131-
};
132-
133-
if (this.options.DryRun)
134-
{
135-
// Output the modified document to the console for review
136-
using var stringWriter = new StringWriter();
137-
using var xmlWriter = XmlWriter.Create(stringWriter, settings);
138-
139-
document.WriteTo(xmlWriter);
140-
xmlWriter.Flush();
141-
this.logger.LogInformation(stringWriter.ToString());
142-
}
143-
else
144-
{
145-
using var writer = XmlWriter.Create(projectFilePath, settings);
146-
document.Save(writer); // Preserves original formatting, avoids extra lines
147-
}
156+
// Remove the empty line
157+
textNode.Remove();
148158
}
149159
}
150-
catch (Exception ex)
151-
{
152-
this.logger.LogError(ex, $"Error processing project file \"{projectFilePath}\"");
153-
}
160+
}
161+
162+
var settings = new XmlWriterSettings
163+
{
164+
OmitXmlDeclaration = document.Declaration == null, // Preserve the XML declaration if it exists.
165+
Indent = false, // Prevents adding any extra indentation
166+
NewLineHandling = NewLineHandling.Replace,
167+
};
168+
169+
if (this.options.DryRun)
170+
{
171+
// Output the modified document to the console for review
172+
using var stringWriter = new StringWriter();
173+
using var xmlWriter = XmlWriter.Create(stringWriter, settings);
174+
175+
document.WriteTo(xmlWriter);
176+
xmlWriter.Flush();
177+
this.logger.LogInformation(stringWriter.ToString());
178+
}
179+
else
180+
{
181+
using var writer = XmlWriter.Create(projectFilePath, settings);
182+
document.Save(writer); // Preserves original formatting, avoids extra lines
154183
}
155184
}
156185
}

src/PackageReferenceVersionToAttributeExtensionTests/ConvertPackageReferenceVersionElementsToAttributesCommandTests.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,6 @@ public async Task ExecuteAsync_WithOneSelectedProjectWithMixedStyles_SucceedsAsy
225225
</PropertyGroup>
226226
<ItemGroup>
227227
<PackageReference Include="PackageA" Version="1.2.3">
228-
229228
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
230229
<PrivateAssets>all</PrivateAssets>
231230
</PackageReference>

0 commit comments

Comments
 (0)