Skip to content

Commit 091259e

Browse files
authored
Merge branch 'main' into merge/release/10.0.2xx-to-main
2 parents 1097af5 + b300744 commit 091259e

File tree

2 files changed

+94
-2
lines changed

2 files changed

+94
-2
lines changed

src/Cli/Microsoft.DotNet.FileBasedPrograms/FileLevelDirectiveHelpers.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,8 @@ internal readonly record struct SourceFile(string Path, SourceText Text)
228228
public static SourceFile Load(string filePath)
229229
{
230230
using var stream = File.OpenRead(filePath);
231-
return new SourceFile(filePath, SourceText.From(stream, Encoding.UTF8));
231+
// Let SourceText.From auto-detect the encoding (including BOM detection)
232+
return new SourceFile(filePath, SourceText.From(stream, encoding: null));
232233
}
233234

234235
public SourceFile WithText(SourceText newText)
@@ -239,7 +240,9 @@ public SourceFile WithText(SourceText newText)
239240
public void Save()
240241
{
241242
using var stream = File.Open(Path, FileMode.Create, FileAccess.Write);
242-
using var writer = new StreamWriter(stream, Encoding.UTF8);
243+
// Use the encoding from SourceText, which preserves the original BOM state
244+
var encoding = Text.Encoding ?? new UTF8Encoding(encoderShouldEmitUTF8Identifier: false);
245+
using var writer = new StreamWriter(stream, encoding);
243246
Text.Write(writer);
244247
}
245248

test/dotnet.Tests/CommandTests/Run/FileBasedAppSourceEditorTests.cs

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,95 @@ public void RemoveMultiple()
517517
"""));
518518
}
519519

520+
/// <summary>
521+
/// Verifies that files without UTF-8 BOM don't get one added when saved.
522+
/// This is critical for shebang (#!) scripts on Unix-like systems.
523+
/// <see href="https://github.com/dotnet/sdk/issues/52054"/>
524+
/// </summary>
525+
[Fact]
526+
public void PreservesNoBomEncoding()
527+
{
528+
var testInstance = _testAssetsManager.CreateTestDirectory();
529+
var tempFile = Path.Join(testInstance.Path, "test.cs");
530+
531+
// Create a file without BOM
532+
var content = "#!/usr/bin/env dotnet run\nConsole.WriteLine();";
533+
File.WriteAllText(tempFile, content, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
534+
535+
// Load, modify, and save
536+
var sourceFile = SourceFile.Load(tempFile);
537+
var editor = FileBasedAppSourceEditor.Load(sourceFile);
538+
editor.Add(new CSharpDirective.Package(default) { Name = "MyPackage", Version = "1.0.0" });
539+
editor.SourceFile.Save();
540+
541+
// Verify no BOM was added
542+
var bytes = File.ReadAllBytes(tempFile);
543+
Assert.True(bytes is not [0xEF, 0xBB, 0xBF, ..],
544+
"File should not have UTF-8 BOM");
545+
546+
// Verify the complete file content is correct
547+
var savedContent = File.ReadAllText(tempFile);
548+
var expectedContent = "#!/usr/bin/env dotnet run\n\n#:package [email protected]\n\nConsole.WriteLine();";
549+
Assert.Equal(expectedContent, savedContent);
550+
}
551+
552+
/// <summary>
553+
/// Verifies that files with UTF-8 BOM preserve it when saved.
554+
/// <see href="https://github.com/dotnet/sdk/issues/52054"/>
555+
/// </summary>
556+
[Fact]
557+
public void PreservesBomEncoding()
558+
{
559+
var testInstance = _testAssetsManager.CreateTestDirectory();
560+
var tempFile = Path.Join(testInstance.Path, "test.cs");
561+
562+
// Create a file with BOM
563+
var content = "Console.WriteLine();";
564+
File.WriteAllText(tempFile, content, new UTF8Encoding(encoderShouldEmitUTF8Identifier: true));
565+
566+
// Load, modify, and save
567+
var sourceFile = SourceFile.Load(tempFile);
568+
var editor = FileBasedAppSourceEditor.Load(sourceFile);
569+
editor.Add(new CSharpDirective.Package(default) { Name = "MyPackage", Version = "1.0.0" });
570+
editor.SourceFile.Save();
571+
572+
// Verify BOM is still present
573+
var bytes = File.ReadAllBytes(tempFile);
574+
Assert.True(bytes is [0xEF, 0xBB, 0xBF, ..],
575+
"File should have UTF-8 BOM");
576+
}
577+
578+
/// <summary>
579+
/// Verifies that files with non-UTF-8 encodings (like UTF-16) preserve their encoding when saved.
580+
/// <see href="https://github.com/dotnet/sdk/issues/52054"/>
581+
/// </summary>
582+
[Fact]
583+
public void PreservesNonUtf8Encoding()
584+
{
585+
var testInstance = _testAssetsManager.CreateTestDirectory();
586+
var tempFile = Path.Join(testInstance.Path, "test.cs");
587+
588+
// Create a file with UTF-16 encoding (includes BOM by default)
589+
var content = "Console.WriteLine(\"UTF-16 test\");";
590+
File.WriteAllText(tempFile, content, Encoding.Unicode);
591+
592+
// Load, modify, and save
593+
var sourceFile = SourceFile.Load(tempFile);
594+
var editor = FileBasedAppSourceEditor.Load(sourceFile);
595+
editor.Add(new CSharpDirective.Package(default) { Name = "MyPackage", Version = "1.0.0" });
596+
editor.SourceFile.Save();
597+
598+
// Verify UTF-16 BOM is still present (0xFF 0xFE for UTF-16 LE)
599+
var bytes = File.ReadAllBytes(tempFile);
600+
Assert.True(bytes is [0xFF, 0xFE, ..],
601+
"File should have UTF-16 LE BOM");
602+
603+
// Verify content is still readable as UTF-16
604+
var savedContent = File.ReadAllText(tempFile, Encoding.Unicode);
605+
Assert.Contains("#:package [email protected]", savedContent);
606+
Assert.Contains("Console.WriteLine", savedContent);
607+
}
608+
520609
private void Verify(
521610
string input,
522611
params ReadOnlySpan<(Action<FileBasedAppSourceEditor> action, string expectedOutput)> verify)

0 commit comments

Comments
 (0)