Skip to content

Commit

Permalink
Encode TargetPaths before writing them to the editorconfig (#16858)
Browse files Browse the repository at this point in the history
* Encode TargetPaths before writing them to the editorconfig
* Address feedback from peer review
* Fix generated output paths in tests
  • Loading branch information
captainsafia authored Apr 15, 2021
1 parent 3560c4d commit ad420ea
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 7 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<p>Just a file with a special character in the name!</p>
3 changes: 3 additions & 0 deletions src/RazorSdk/SourceGenerators/RazorSourceGenerationContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.CodeAnalysis;

Expand Down Expand Up @@ -147,6 +148,8 @@ private static (IReadOnlyList<RazorInputItem> razorFiles, IReadOnlyList<RazorInp
continue;
}

relativePath = Encoding.UTF8.GetString(Convert.FromBase64String(relativePath));

options.TryGetValue("build_metadata.AdditionalFiles.CssScope", out var cssScope);

var fileKind = isComponent ? FileKinds.GetComponentFileKindFromFilePath(item.Path) : FileKinds.Legacy;
Expand Down
13 changes: 9 additions & 4 deletions src/RazorSdk/SourceGenerators/RazorSourceGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -278,11 +278,16 @@ private static string GetIdentifierFromPath(string filePath)

for (var i = 0; i < filePath.Length; i++)
{
builder.Append(filePath[i] switch
switch (filePath[i])
{
':' or '\\' or '/' => '_',
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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ Copyright (c) .NET Foundation. All rights reserved.

<Project ToolsVersion="14.0">

<UsingTask TaskName="Microsoft.AspNetCore.Razor.Tasks.EncodeRazorInputItem" AssemblyFile="$(RazorSdkBuildTasksAssembly)" />

<Target Name="_PrepareRazorSourceGenerators"
BeforeTargets="GenerateMSBuildEditorConfigFileShouldRun"
DependsOnTargets="PrepareForRazorGenerate;PrepareForRazorComponentGenerate">
Expand Down Expand Up @@ -45,7 +47,6 @@ Copyright (c) .NET Foundation. All rights reserved.
<ItemGroup>
<!-- Additional metadata and properties that we want the compiler to pass to the compiler we want to pass additional MSBuild properties \ metadata -->
<CompilerVisibleItemMetadata Include="AdditionalFiles" MetadataName="TargetPath" />
<CompilerVisibleItemMetadata Include="AdditionalFiles" MetadataName="GeneratedOutputFullPath" />
<CompilerVisibleItemMetadata Include="AdditionalFiles" MetadataName="CssScope" />
<CompilerVisibleProperty Include="RazorLangVersion" />
<CompilerVisibleProperty Include="RootNamespace" />
Expand All @@ -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`))" />
</ItemGroup>

<EncodeRazorInputItem RazorInputItems="@(_RazorAdditionalFile)">
<Output TaskParameter="EncodedRazorInputItems" ItemName="_RazorSpecialCharacterWorkaround" />
</EncodeRazorInputItem>

<ItemGroup>
<_RazorAdditionalFile Remove="@(_RazorAdditionalFile)" />
<_RazorAdditionalFile Include="@(_RazorSpecialCharacterWorkaround)" />

<AdditionalFiles Include="@(_RazorAdditionalFile)" />

Expand Down
48 changes: 48 additions & 0 deletions src/RazorSdk/Tasks/EncodeRazorInputItem.cs
Original file line number Diff line number Diff line change
@@ -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;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit ad420ea

Please sign in to comment.