diff --git a/src/Microsoft.AspNetCore.Razor.Runtime/Runtime/TagHelpers/TagHelperDescriptorFactory.cs b/src/Microsoft.AspNetCore.Razor.Runtime/Runtime/TagHelpers/TagHelperDescriptorFactory.cs
index b6defbda5a..92916c8d48 100644
--- a/src/Microsoft.AspNetCore.Razor.Runtime/Runtime/TagHelpers/TagHelperDescriptorFactory.cs
+++ b/src/Microsoft.AspNetCore.Razor.Runtime/Runtime/TagHelpers/TagHelperDescriptorFactory.cs
@@ -703,6 +703,7 @@ namespace Microsoft.AspNetCore.Razor.Runtime.TagHelpers
/// Converts from pascal/camel case to lower kebab-case.
///
///
+ ///
/// SomeThing => some-thing
/// capsONInside => caps-on-inside
/// CAPSOnOUTSIDE => caps-on-outside
@@ -710,8 +711,9 @@ namespace Microsoft.AspNetCore.Razor.Runtime.TagHelpers
/// One1Two2Three3 => one1-two2-three3
/// ONE1TWO2THREE3 => one1two2three3
/// First_Second_ThirdHi => first_second_third-hi
+ ///
///
- private static string ToHtmlCase(string name)
+ public static string ToHtmlCase(string name)
{
return HtmlCaseRegex.Replace(name, HtmlCaseRegexReplacement).ToLowerInvariant();
}
diff --git a/src/Microsoft.AspNetCore.Razor/CodeGenerators/CSharpCodeGenerator.cs b/src/Microsoft.AspNetCore.Razor/CodeGenerators/CSharpCodeGenerator.cs
index a67b9a865a..17141eb07e 100644
--- a/src/Microsoft.AspNetCore.Razor/CodeGenerators/CSharpCodeGenerator.cs
+++ b/src/Microsoft.AspNetCore.Razor/CodeGenerators/CSharpCodeGenerator.cs
@@ -21,7 +21,7 @@ namespace Microsoft.AspNetCore.Razor.CodeGenerators
{
}
- private ChunkTree Tree { get { return Context.ChunkTreeBuilder.Root; } }
+ protected ChunkTree Tree { get { return Context.ChunkTreeBuilder.Root; } }
public RazorEngineHost Host { get { return Context.Host; } }
///
@@ -82,12 +82,23 @@ namespace Microsoft.AspNetCore.Razor.CodeGenerators
csharpCodeVisitor.Accept(Tree.Children);
}
}
+
+ BuildAfterExecuteContent(writer, Tree.Children);
}
}
return new CodeGeneratorResult(writer.GenerateCode(), writer.LineMappingManager.Mappings);
}
+ ///
+ /// Provides an entry point to append code (after execute content) to a generated Razor class.
+ ///
+ /// The to receive the additional content.
+ /// The list of s for the generated program.
+ protected virtual void BuildAfterExecuteContent(CSharpCodeWriter writer, IList chunks)
+ {
+ }
+
protected virtual CSharpCodeVisitor CreateCSharpCodeVisitor(
CSharpCodeWriter writer,
CodeGeneratorContext context)
diff --git a/src/Microsoft.AspNetCore.Razor/CodeGenerators/CSharpCodeWriter.cs b/src/Microsoft.AspNetCore.Razor/CodeGenerators/CSharpCodeWriter.cs
index 559e8ced77..c53ff2fc62 100644
--- a/src/Microsoft.AspNetCore.Razor/CodeGenerators/CSharpCodeWriter.cs
+++ b/src/Microsoft.AspNetCore.Razor/CodeGenerators/CSharpCodeWriter.cs
@@ -319,6 +319,17 @@ namespace Microsoft.AspNetCore.Razor.CodeGenerators
.WriteEndMethodInvocation(endLine);
}
+ public CSharpCodeWriter WriteAutoPropertyDeclaration(string accessibility, string typeName, string name)
+ {
+ return Write(accessibility)
+ .Write(" ")
+ .Write(typeName)
+ .Write(" ")
+ .Write(name)
+ .Write(" { get; set; }")
+ .WriteLine();
+ }
+
public CSharpDisableWarningScope BuildDisableWarningScope(int warning)
{
return new CSharpDisableWarningScope(this, warning);
diff --git a/test/Microsoft.AspNetCore.Razor.Runtime.Test/Runtime/TagHelpers/TagHelperDescriptorFactoryTest.cs b/test/Microsoft.AspNetCore.Razor.Runtime.Test/Runtime/TagHelpers/TagHelperDescriptorFactoryTest.cs
index 02ab2a2de0..c68aa85e6f 100644
--- a/test/Microsoft.AspNetCore.Razor.Runtime.Test/Runtime/TagHelpers/TagHelperDescriptorFactoryTest.cs
+++ b/test/Microsoft.AspNetCore.Razor.Runtime.Test/Runtime/TagHelpers/TagHelperDescriptorFactoryTest.cs
@@ -2172,6 +2172,35 @@ namespace Microsoft.AspNetCore.Razor.Runtime.TagHelpers
TagHelperAttributeDescriptorComparer.Default);
}
+ public static TheoryData HtmlConversionData
+ {
+ get
+ {
+ return new TheoryData
+ {
+ { "SomeThing", "some-thing" },
+ { "someOtherThing", "some-other-thing" },
+ { "capsONInside", "caps-on-inside" },
+ { "CAPSOnOUTSIDE", "caps-on-outside" },
+ { "ALLCAPS", "allcaps" },
+ { "One1Two2Three3", "one1-two2-three3" },
+ { "ONE1TWO2THREE3", "one1two2three3" },
+ { "First_Second_ThirdHi", "first_second_third-hi" }
+ };
+ }
+ }
+
+ [Theory]
+ [MemberData(nameof(HtmlConversionData))]
+ public void ToHtmlCase_ReturnsExpectedConversions(string input, string expectedOutput)
+ {
+ // Arrange, Act
+ var output = TagHelperDescriptorFactory.ToHtmlCase(input);
+
+ // Assert
+ Assert.Equal(output, expectedOutput);
+ }
+
// TagHelperDesignTimeDescriptors are not created in CoreCLR.
#if !NETCOREAPP1_0
public static TheoryData OutputElementHintData
diff --git a/test/Microsoft.AspNetCore.Razor.Test/CodeGenerators/CSharpCodeGeneratorTest.cs b/test/Microsoft.AspNetCore.Razor.Test/CodeGenerators/CSharpCodeGeneratorTest.cs
index baaada48d7..5bab80810c 100644
--- a/test/Microsoft.AspNetCore.Razor.Test/CodeGenerators/CSharpCodeGeneratorTest.cs
+++ b/test/Microsoft.AspNetCore.Razor.Test/CodeGenerators/CSharpCodeGeneratorTest.cs
@@ -4,6 +4,10 @@
#if GENERATE_BASELINES
using System;
#endif
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.AspNetCore.Razor.Chunks;
+using Microsoft.AspNetCore.Razor.Chunks.Generators;
using Microsoft.AspNetCore.Razor.Parser.SyntaxTree;
using Microsoft.AspNetCore.Razor.Test.CodeGenerators;
using Microsoft.AspNetCore.Razor.Test.Utils;
@@ -14,9 +18,15 @@ namespace Microsoft.AspNetCore.Razor.CodeGenerators
{
public class CSharpCodeGeneratorTest
{
+#if GENERATE_BASELINES
+ private static readonly string _baselinePathStart = "test/Microsoft.AspNetCore.Razor.Test";
+#endif
+ private static readonly string _testOutputDirectory = "TestFiles/CodeGenerator/Output/CSharpCodeGenerator";
+
[Fact]
public void ChunkTreeWithUsings()
{
+ // Arrange
var syntaxTreeNode = new Mock(new SpanBuilder());
var language = new CSharpRazorCodeLanguage();
var host = new CodeGenTestHost(language);
@@ -30,7 +40,9 @@ namespace Microsoft.AspNetCore.Razor.CodeGenerators
codeGeneratorContext.ChunkTreeBuilder.AddUsingChunk("FakeNamespace1", syntaxTreeNode.Object);
codeGeneratorContext.ChunkTreeBuilder.AddUsingChunk("FakeNamespace2.SubNamespace", syntaxTreeNode.Object);
var codeGenerator = new CodeGenTestCodeGenerator(codeGeneratorContext);
- var testFile = TestFile.Create("TestFiles/CodeGenerator/Output/CSharpCodeGenerator.cs");
+
+ var path = $"{_testOutputDirectory}/ChunkTreeWithUsings.cs";
+ var testFile = TestFile.Create(path);
string expectedOutput;
#if GENERATE_BASELINES
@@ -54,14 +66,151 @@ namespace Microsoft.AspNetCore.Razor.CodeGenerators
// Update baseline files if files do not already match.
if (!string.Equals(expectedOutput, result.Code, StringComparison.Ordinal))
{
- BaselineWriter.WriteBaseline(
- @"test\Microsoft.AspNetCore.Razor.Test\TestFiles\CodeGenerator\Output\CSharpCodeGenerator.cs",
- result.Code);
+ var baselinePath = $"{_baselinePathStart}/{_testOutputDirectory}/ChunkTreeWithUsings.cs";
+ BaselineWriter.WriteBaseline( baselinePath, result.Code);
}
#else
Assert.Equal(expectedOutput, result.Code);
#endif
}
- }
-}
+ public static TheoryData ModifyOutputData
+ {
+ get
+ {
+ var addFileName = "AddGenerateChunkTest.cs";
+ var addGenerator = new AddGenerateTestCodeGenerator(CreateContext());
+ var addResult = CreateCodeGeneratorResult(addFileName);
+
+ var clearFileName = "ClearGenerateChunkTest.cs";
+ var clearGenerator = new ClearGenerateTestCodeGenerator(CreateContext());
+ var clearResult = CreateCodeGeneratorResult(clearFileName);
+
+ var defaultFileName = "DefaultGenerateChunkTest.cs";
+ var defaultGenerator = new CSharpCodeGenerator(CreateContext());
+ var defaultResult = CreateCodeGeneratorResult(defaultFileName);
+
+ var commentFileName = "BuildAfterExecuteContentTest.cs";
+ var commentGenerator = new BuildAfterExecuteContentTestCodeGenerator(CreateContext());
+ var commentResult = CreateCodeGeneratorResult(commentFileName);
+
+ return new TheoryData
+ {
+ {addGenerator, addResult, addFileName},
+ {clearGenerator, clearResult, clearFileName },
+ {defaultGenerator, defaultResult, defaultFileName },
+ {commentGenerator, commentResult, commentFileName }
+ };
+ }
+ }
+
+ [Theory]
+ [MemberData(nameof(ModifyOutputData))]
+ public void BuildAfterExecuteContent_ModifyChunks_ModifyOutput(
+ CSharpCodeGenerator generator,
+ CodeGeneratorResult expectedResult,
+ string fileName)
+ {
+ // Arrange, Act
+ var result = generator.Generate();
+
+ // Assert
+#if GENERATE_BASELINES
+ // Update baseline files if files do not already match.
+ if (!string.Equals(expectedResult.Code, result.Code, StringComparison.Ordinal))
+ {
+ var baselinePath = $"{_baselinePathStart}/{_testOutputDirectory}/{fileName}";
+ BaselineWriter.WriteBaseline( baselinePath, result.Code);
+ }
+#else
+ Assert.Equal(result.Code, expectedResult.Code);
+#endif
+ }
+
+
+ private static CodeGeneratorResult CreateCodeGeneratorResult(string fileName)
+ {
+ var path = $"{_testOutputDirectory}/{fileName}";
+ var file = TestFile.Create(path);
+ string code;
+
+#if GENERATE_BASELINES
+ if (file.Exists())
+ {
+ code = file.ReadAllText();
+ }
+ else
+ {
+ code = null;
+ }
+#else
+ code = file.ReadAllText();
+#endif
+
+ var result = new CodeGeneratorResult(code, new List());
+ return result;
+ }
+
+ // Returns a context with two literal chunks.
+ private static CodeGeneratorContext CreateContext()
+ {
+ var language = new CSharpRazorCodeLanguage();
+ var host = new CodeGenTestHost(language);
+
+ var codeGeneratorContext = new CodeGeneratorContext(
+ new ChunkGeneratorContext(
+ host,
+ host.DefaultClassName,
+ host.DefaultNamespace,
+ "",
+ shouldGenerateLinePragmas: false),
+ new ErrorSink());
+
+ codeGeneratorContext.ChunkTreeBuilder = new ChunkTreeBuilder();
+ var syntaxTreeNode = new Mock(new SpanBuilder());
+ codeGeneratorContext.ChunkTreeBuilder.AddLiteralChunk("hello", syntaxTreeNode.Object);
+ codeGeneratorContext.ChunkTreeBuilder.AddStatementChunk("// asdf", syntaxTreeNode.Object);
+ codeGeneratorContext.ChunkTreeBuilder.AddLiteralChunk("world", syntaxTreeNode.Object);
+ return codeGeneratorContext;
+ }
+
+ private class BuildAfterExecuteContentTestCodeGenerator : CSharpCodeGenerator
+ {
+ public BuildAfterExecuteContentTestCodeGenerator(CodeGeneratorContext context) : base(context)
+ {
+ }
+
+ protected override void BuildAfterExecuteContent(CSharpCodeWriter writer, IList chunks)
+ {
+ writer.WriteLine("// test add content.");
+ }
+ }
+
+ private class AddGenerateTestCodeGenerator : CSharpCodeGenerator
+ {
+ public AddGenerateTestCodeGenerator(CodeGeneratorContext context) : base(context)
+ {
+ }
+
+ public override CodeGeneratorResult Generate()
+ {
+ var firstChunk = Tree.Children.First();
+ Tree.Children.Add(firstChunk);
+ return base.Generate();
+ }
+ }
+
+ private class ClearGenerateTestCodeGenerator : CSharpCodeGenerator
+ {
+ public ClearGenerateTestCodeGenerator(CodeGeneratorContext context) : base(context)
+ {
+ }
+
+ public override CodeGeneratorResult Generate()
+ {
+ Tree.Children.Clear();
+ return base.Generate();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/Microsoft.AspNetCore.Razor.Test/TestFiles/CodeGenerator/Output/CSharpCodeGenerator/AddGenerateChunkTest.cs b/test/Microsoft.AspNetCore.Razor.Test/TestFiles/CodeGenerator/Output/CSharpCodeGenerator/AddGenerateChunkTest.cs
new file mode 100644
index 0000000000..d78105f7ef
--- /dev/null
+++ b/test/Microsoft.AspNetCore.Razor.Test/TestFiles/CodeGenerator/Output/CSharpCodeGenerator/AddGenerateChunkTest.cs
@@ -0,0 +1,27 @@
+namespace Razor
+{
+ using System.Threading.Tasks;
+
+ public class __CompiledTemplate
+ {
+ #line hidden
+ public __CompiledTemplate()
+ {
+ }
+
+ #pragma warning disable 1998
+ public override async Task ExecuteAsync()
+ {
+ WriteLiteral("hello");
+#line 1 ""
+// asdf
+
+#line default
+#line hidden
+
+ WriteLiteral("world");
+ WriteLiteral("hello");
+ }
+ #pragma warning restore 1998
+ }
+}
diff --git a/test/Microsoft.AspNetCore.Razor.Test/TestFiles/CodeGenerator/Output/CSharpCodeGenerator/BuildAfterExecuteContentTest.cs b/test/Microsoft.AspNetCore.Razor.Test/TestFiles/CodeGenerator/Output/CSharpCodeGenerator/BuildAfterExecuteContentTest.cs
new file mode 100644
index 0000000000..f7092af05a
--- /dev/null
+++ b/test/Microsoft.AspNetCore.Razor.Test/TestFiles/CodeGenerator/Output/CSharpCodeGenerator/BuildAfterExecuteContentTest.cs
@@ -0,0 +1,27 @@
+namespace Razor
+{
+ using System.Threading.Tasks;
+
+ public class __CompiledTemplate
+ {
+ #line hidden
+ public __CompiledTemplate()
+ {
+ }
+
+ #pragma warning disable 1998
+ public override async Task ExecuteAsync()
+ {
+ WriteLiteral("hello");
+#line 1 ""
+// asdf
+
+#line default
+#line hidden
+
+ WriteLiteral("world");
+ }
+ #pragma warning restore 1998
+ // test add content.
+ }
+}
diff --git a/test/Microsoft.AspNetCore.Razor.Test/TestFiles/CodeGenerator/Output/CSharpCodeGenerator.cs b/test/Microsoft.AspNetCore.Razor.Test/TestFiles/CodeGenerator/Output/CSharpCodeGenerator/ChunkTreeWithUsings.cs
similarity index 100%
rename from test/Microsoft.AspNetCore.Razor.Test/TestFiles/CodeGenerator/Output/CSharpCodeGenerator.cs
rename to test/Microsoft.AspNetCore.Razor.Test/TestFiles/CodeGenerator/Output/CSharpCodeGenerator/ChunkTreeWithUsings.cs
diff --git a/test/Microsoft.AspNetCore.Razor.Test/TestFiles/CodeGenerator/Output/CSharpCodeGenerator/ClearGenerateChunkTest.cs b/test/Microsoft.AspNetCore.Razor.Test/TestFiles/CodeGenerator/Output/CSharpCodeGenerator/ClearGenerateChunkTest.cs
new file mode 100644
index 0000000000..c3d01fabd7
--- /dev/null
+++ b/test/Microsoft.AspNetCore.Razor.Test/TestFiles/CodeGenerator/Output/CSharpCodeGenerator/ClearGenerateChunkTest.cs
@@ -0,0 +1,18 @@
+namespace Razor
+{
+ using System.Threading.Tasks;
+
+ public class __CompiledTemplate
+ {
+ #line hidden
+ public __CompiledTemplate()
+ {
+ }
+
+ #pragma warning disable 1998
+ public override async Task ExecuteAsync()
+ {
+ }
+ #pragma warning restore 1998
+ }
+}
diff --git a/test/Microsoft.AspNetCore.Razor.Test/TestFiles/CodeGenerator/Output/CSharpCodeGenerator/DefaultGenerateChunkTest.cs b/test/Microsoft.AspNetCore.Razor.Test/TestFiles/CodeGenerator/Output/CSharpCodeGenerator/DefaultGenerateChunkTest.cs
new file mode 100644
index 0000000000..2c1560599f
--- /dev/null
+++ b/test/Microsoft.AspNetCore.Razor.Test/TestFiles/CodeGenerator/Output/CSharpCodeGenerator/DefaultGenerateChunkTest.cs
@@ -0,0 +1,26 @@
+namespace Razor
+{
+ using System.Threading.Tasks;
+
+ public class __CompiledTemplate
+ {
+ #line hidden
+ public __CompiledTemplate()
+ {
+ }
+
+ #pragma warning disable 1998
+ public override async Task ExecuteAsync()
+ {
+ WriteLiteral("hello");
+#line 1 ""
+// asdf
+
+#line default
+#line hidden
+
+ WriteLiteral("world");
+ }
+ #pragma warning restore 1998
+ }
+}