diff --git a/src/Assets/TestProjects/RazorSimpleMvc/Views/Home/#F{}i+l!e.cshtml b/src/Assets/TestProjects/RazorSimpleMvc/Views/Home/#F{}i+l!e.cshtml
new file mode 100644
index 000000000000..1882da9f18a8
--- /dev/null
+++ b/src/Assets/TestProjects/RazorSimpleMvc/Views/Home/#F{}i+l!e.cshtml
@@ -0,0 +1 @@
+
Just a file with a special character in the name!
\ No newline at end of file
diff --git a/src/RazorSdk/SourceGenerators/RazorSourceGenerationContext.cs b/src/RazorSdk/SourceGenerators/RazorSourceGenerationContext.cs
index 579daca0b927..d3e0b0a08049 100644
--- a/src/RazorSdk/SourceGenerators/RazorSourceGenerationContext.cs
+++ b/src/RazorSdk/SourceGenerators/RazorSourceGenerationContext.cs
@@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Text;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.CodeAnalysis;
@@ -147,6 +148,8 @@ private static (IReadOnlyList razorFiles, IReadOnlyList '_',
- var @default => @default,
- });
+ case ':' or '\\' or '/':
+ case char ch when !char.IsLetterOrDigit(ch):
+ builder.Append('_');
+ break;
+ default:
+ builder.Append(filePath[i]);
+ break;
+ }
}
return builder.ToString();
diff --git a/src/RazorSdk/Targets/Microsoft.NET.Sdk.Razor.SourceGenerators.targets b/src/RazorSdk/Targets/Microsoft.NET.Sdk.Razor.SourceGenerators.targets
index 956273483627..b0f3b359a602 100644
--- a/src/RazorSdk/Targets/Microsoft.NET.Sdk.Razor.SourceGenerators.targets
+++ b/src/RazorSdk/Targets/Microsoft.NET.Sdk.Razor.SourceGenerators.targets
@@ -10,6 +10,8 @@ Copyright (c) .NET Foundation. All rights reserved.
+
+
@@ -45,7 +47,6 @@ Copyright (c) .NET Foundation. All rights reserved.
-
@@ -70,6 +71,15 @@ Copyright (c) .NET Foundation. All rights reserved.
<_RazorAdditionalFile Remove="@(_RazorAdditionalFile)" Condition="$([MSBuild]::IsOSPlatform(`Windows`))" />
<_RazorAdditionalFile Include="@(_RazorOmnisharpWorkAround)" Condition="$([MSBuild]::IsOSPlatform(`Windows`))" />
+
+
+
+
+
+
+
+ <_RazorAdditionalFile Remove="@(_RazorAdditionalFile)" />
+ <_RazorAdditionalFile Include="@(_RazorSpecialCharacterWorkaround)" />
diff --git a/src/RazorSdk/Tasks/EncodeRazorInputItem.cs b/src/RazorSdk/Tasks/EncodeRazorInputItem.cs
new file mode 100644
index 000000000000..ac8c8fda5b10
--- /dev/null
+++ b/src/RazorSdk/Tasks/EncodeRazorInputItem.cs
@@ -0,0 +1,48 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Numerics;
+using System.Security.Cryptography;
+using System.Text;
+using System.Text.RegularExpressions;
+using Microsoft.Build.Framework;
+using Microsoft.Build.Utilities;
+
+namespace Microsoft.AspNetCore.Razor.Tasks
+{
+ // The compiler leverages .editorconfig files to transfer information between
+ // msbuild and the compiler. However, the transfer of data from MSBuild to the
+ // .editorconfig file to the source generator, causes a lot of issues as strings
+ // are transferred from one type to another. For example, the editorconfig file
+ // interprets "#" as comments and omits them from the produced AnalyzerConfigOptions.
+ // Characters like {} in the filename cause issues with resolving the type. To work
+ // around this, we encode everything before writing it to the editorconfig then decode
+ // inside the Razor source generator.
+ public class EncodeRazorInputItem : Task
+ {
+ [Required]
+ public ITaskItem[] RazorInputItems { get; set; }
+
+ [Output]
+ public ITaskItem[] EncodedRazorInputItems { get; set; }
+
+ public override bool Execute()
+ {
+ EncodedRazorInputItems = new ITaskItem[RazorInputItems.Length];
+
+ for (var i = 0; i < RazorInputItems.Length; i++)
+ {
+ var input = RazorInputItems[i];
+ var targetPath = Convert.ToBase64String(Encoding.UTF8.GetBytes(input.GetMetadata("TargetPath")));
+
+ var outputItem = new TaskItem(input);
+ outputItem.SetMetadata("TargetPath", targetPath);
+
+ EncodedRazorInputItems[i] = outputItem;
+ }
+
+ return !Log.HasLoggedErrors;
+ }
+ }
+}
diff --git a/src/Tests/Microsoft.NET.Sdk.Razor.Tests/ScopedCssIntegrationTests.cs b/src/Tests/Microsoft.NET.Sdk.Razor.Tests/ScopedCssIntegrationTests.cs
index 470be38927f4..586f3b7f31ce 100644
--- a/src/Tests/Microsoft.NET.Sdk.Razor.Tests/ScopedCssIntegrationTests.cs
+++ b/src/Tests/Microsoft.NET.Sdk.Razor.Tests/ScopedCssIntegrationTests.cs
@@ -102,7 +102,7 @@ public void CanOverrideScopeIdentifiers()
var scoped = Path.Combine(intermediateOutputPath, "scopedcss", "Styles", "Pages", "Counter.rz.scp.css");
new FileInfo(scoped).Should().Exist();
new FileInfo(scoped).Should().Contain("b-overriden");
- var generated = Path.Combine(intermediateOutputPath, "generated", "Microsoft.NET.Sdk.Razor.SourceGenerators", "Microsoft.NET.Sdk.Razor.SourceGenerators.RazorSourceGenerator", "_Components_Pages_Counter.razor.cs");
+ var generated = Path.Combine(intermediateOutputPath, "generated", "Microsoft.NET.Sdk.Razor.SourceGenerators", "Microsoft.NET.Sdk.Razor.SourceGenerators.RazorSourceGenerator", "_Components_Pages_Counter_razor.cs");
new FileInfo(generated).Should().Exist();
new FileInfo(generated).Should().Contain("b-overriden");
new FileInfo(Path.Combine(intermediateOutputPath, "scopedcss", "Components", "Pages", "Index.razor.rz.scp.css")).Should().NotExist();
@@ -327,7 +327,7 @@ public void Build_RemovingScopedCssAndBuilding_UpdatesGeneratedCodeAndBundle()
new FileInfo(generatedBundle).Should().Exist();
var generatedProjectBundle = Path.Combine(intermediateOutputPath, "scopedcss", "projectbundle", "ComponentApp.bundle.scp.css");
new FileInfo(generatedProjectBundle).Should().Exist();
- var generatedCounter = Path.Combine(intermediateOutputPath, "generated", "Microsoft.NET.Sdk.Razor.SourceGenerators", "Microsoft.NET.Sdk.Razor.SourceGenerators.RazorSourceGenerator", "_Components_Pages_Counter.razor.cs");
+ var generatedCounter = Path.Combine(intermediateOutputPath, "generated", "Microsoft.NET.Sdk.Razor.SourceGenerators", "Microsoft.NET.Sdk.Razor.SourceGenerators.RazorSourceGenerator", "_Components_Pages_Counter_razor.cs");
new FileInfo(generatedCounter).Should().Exist();
var componentThumbprint = FileThumbPrint.Create(generatedCounter);