Begin integrating with real Razor compiler

This commit is contained in:
Steve Sanderson 2018-01-14 16:37:01 +00:00
parent a03cb48c7a
commit 3ccdc1d16f
9 changed files with 229 additions and 16 deletions

View File

@ -1,3 +1,2 @@
@inherits Microsoft.Blazor.Components.BlazorComponent
<h1>Hello, world!</h1>
<h1>Hello, world!</h1>
Hello from the Razor component.

View File

@ -0,0 +1,11 @@
// 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.
namespace Microsoft.Blazor.Build.Core.RazorCompilation.Engine
{
internal static class BlazorCodeDocItems
{
public static object ClassName = new object();
public static object Namespace = new object();
}
}

View File

@ -0,0 +1,23 @@
// 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 Microsoft.AspNetCore.Razor.Language.CodeGeneration;
using System;
namespace Microsoft.Blazor.Build.Core.RazorCompilation.Engine
{
/// <summary>
/// Directs a <see cref="DocumentWriter"/> to use <see cref="BlazorIntermediateNodeWriter"/>.
/// </summary>
internal class BlazorCodeTarget : CodeTarget
{
public override IntermediateNodeWriter CreateNodeWriter()
=> new BlazorIntermediateNodeWriter();
public override TExtension GetExtension<TExtension>()
=> throw new NotImplementedException();
public override bool HasExtension<TExtension>()
=> throw new NotImplementedException();
}
}

View File

@ -0,0 +1,64 @@
// 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 Microsoft.AspNetCore.Razor.Language.CodeGeneration;
using Microsoft.AspNetCore.Razor.Language.Intermediate;
namespace Microsoft.Blazor.Build.Core.RazorCompilation.Engine
{
/// <summary>
/// Generates the C# code corresponding to Razor source document contents.
/// </summary>
internal class BlazorIntermediateNodeWriter : IntermediateNodeWriter
{
public override void BeginWriterScope(CodeRenderingContext context, string writer)
{
throw new System.NotImplementedException(nameof(BeginWriterScope));
}
public override void EndWriterScope(CodeRenderingContext context)
{
throw new System.NotImplementedException(nameof(EndWriterScope));
}
public override void WriteCSharpCode(CodeRenderingContext context, CSharpCodeIntermediateNode node)
{
throw new System.NotImplementedException(nameof(WriteCSharpCode));
}
public override void WriteCSharpCodeAttributeValue(CodeRenderingContext context, CSharpCodeAttributeValueIntermediateNode node)
{
throw new System.NotImplementedException(nameof(WriteCSharpCodeAttributeValue));
}
public override void WriteCSharpExpression(CodeRenderingContext context, CSharpExpressionIntermediateNode node)
{
throw new System.NotImplementedException(nameof(WriteCSharpExpression));
}
public override void WriteCSharpExpressionAttributeValue(CodeRenderingContext context, CSharpExpressionAttributeValueIntermediateNode node)
{
throw new System.NotImplementedException(nameof(WriteCSharpExpressionAttributeValue));
}
public override void WriteHtmlAttribute(CodeRenderingContext context, HtmlAttributeIntermediateNode node)
{
throw new System.NotImplementedException(nameof(WriteHtmlAttribute));
}
public override void WriteHtmlAttributeValue(CodeRenderingContext context, HtmlAttributeValueIntermediateNode node)
{
throw new System.NotImplementedException(nameof(WriteHtmlAttributeValue));
}
public override void WriteHtmlContent(CodeRenderingContext context, HtmlContentIntermediateNode node)
{
context.CodeWriter.Write("/* HTML content */");
}
public override void WriteUsingDirective(CodeRenderingContext context, UsingDirectiveIntermediateNode node)
{
throw new System.NotImplementedException(nameof(WriteUsingDirective));
}
}
}

View File

@ -0,0 +1,52 @@
// 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 Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.Language.CodeGeneration;
using Microsoft.AspNetCore.Razor.Language.Intermediate;
using System;
namespace Microsoft.Blazor.Build.Core.RazorCompilation.Engine
{
/// <summary>
/// A <see cref="RazorEngine"/> phase that builds the C# document corresponding to
/// a <see cref="RazorCodeDocument"/> for a Blazor component.
/// </summary>
internal class BlazorLoweringPhase : IRazorCSharpLoweringPhase
{
private readonly RazorCodeGenerationOptions _codegenOptions;
public BlazorLoweringPhase(RazorCodeGenerationOptions codegenOptions)
{
_codegenOptions = codegenOptions
?? throw new ArgumentNullException(nameof(codegenOptions));
}
public RazorEngine Engine { get; set; }
public void Execute(RazorCodeDocument codeDocument)
{
var writer = BlazorComponentDocumentWriter.Create(_codegenOptions);
var documentNode = codeDocument.GetDocumentIntermediateNode();
var csharpDoc = writer.WriteDocument(codeDocument, documentNode);
codeDocument.SetCSharpDocument(csharpDoc);
}
/// <summary>
/// Creates <see cref="DocumentWriter"/> instances that are configured to use
/// <see cref="BlazorCodeTarget"/>.
/// </summary>
private class BlazorComponentDocumentWriter : DocumentWriter
{
public static DocumentWriter Create(RazorCodeGenerationOptions options)
=> Instance.Create(new BlazorCodeTarget(), options);
private static BlazorComponentDocumentWriter Instance
= new BlazorComponentDocumentWriter();
public override RazorCSharpDocument WriteDocument(
RazorCodeDocument codeDocument, DocumentIntermediateNode documentNode)
=> throw new NotImplementedException();
}
}
}

View File

@ -0,0 +1,41 @@
// 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 Microsoft.AspNetCore.Razor.Language;
using Microsoft.Blazor.Components;
using System.Linq;
namespace Microsoft.Blazor.Build.Core.RazorCompilation.Engine
{
/// <summary>
/// Wraps <see cref="RazorEngine"/>, configuring it to compile Blazor components.
/// </summary>
internal class BlazorRazorEngine
{
private readonly RazorEngine _engine;
private readonly RazorCodeGenerationOptions _codegenOptions;
public BlazorRazorEngine()
{
_codegenOptions = RazorCodeGenerationOptions.CreateDefault();
_engine = RazorEngine.Create(configure =>
{
configure.SetBaseType(typeof(BlazorComponent).FullName);
configure.Phases.Remove(
configure.Phases.OfType<IRazorCSharpLoweringPhase>().Single());
configure.Phases.Add(new BlazorLoweringPhase(_codegenOptions));
configure.ConfigureClass((codeDoc, classNode) =>
{
configure.SetNamespace((string)codeDoc.Items[BlazorCodeDocItems.Namespace]);
classNode.ClassName = (string)codeDoc.Items[BlazorCodeDocItems.ClassName];
});
});
}
public void Process(RazorCodeDocument document)
=> _engine.Process(document);
}
}

View File

@ -1,6 +1,10 @@
// 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 Microsoft.AspNetCore.Razor.Language;
using Microsoft.Blazor.Build.Core.RazorCompilation.Engine;
using Microsoft.Blazor.Components;
using Microsoft.Blazor.RenderTree;
using System;
using System.Collections.Generic;
using System.IO;
@ -35,7 +39,7 @@ namespace Microsoft.Blazor.Build.Core.RazorCompilation
TextWriter verboseOutput)
=> inputPaths.SelectMany(path =>
{
using (var reader = File.OpenText(path))
using (var reader = File.OpenRead(path))
{
return CompileSingleFile(inputRootPath, path, reader, baseNamespace, resultOutput, verboseOutput);
}
@ -53,14 +57,14 @@ namespace Microsoft.Blazor.Build.Core.RazorCompilation
public IEnumerable<RazorCompilerDiagnostic> CompileSingleFile(
string inputRootPath,
string inputFilePath,
TextReader inputFileReader,
Stream inputFileContents,
string baseNamespace,
TextWriter resultOutput,
TextWriter verboseOutput)
{
if (inputFileReader == null)
if (inputFileContents == null)
{
throw new ArgumentNullException(nameof(inputFileReader));
throw new ArgumentNullException(nameof(inputFileContents));
}
if (resultOutput == null)
@ -86,12 +90,20 @@ namespace Microsoft.Blazor.Build.Core.RazorCompilation
? baseNamespace
: $"{baseNamespace}.{itemNamespace}";
resultOutput.WriteLine($"namespace {combinedNamespace} {{");
resultOutput.WriteLine($"public class {itemClassName}");
resultOutput.WriteLine("{");
resultOutput.WriteLine("}");
resultOutput.WriteLine("}");
resultOutput.WriteLine();
var engine = new BlazorRazorEngine();
var sourceDoc = RazorSourceDocument.ReadFrom(inputFileContents, inputFilePath);
var codeDoc = RazorCodeDocument.Create(sourceDoc);
codeDoc.Items[BlazorCodeDocItems.Namespace] = combinedNamespace;
codeDoc.Items[BlazorCodeDocItems.ClassName] = itemClassName;
engine.Process(codeDoc);
var csharpDocument = codeDoc.GetCSharpDocument();
var generatedCode = csharpDocument.GeneratedCode;
generatedCode = generatedCode.Replace(
"public async override global::System.Threading.Tasks.Task ExecuteAsync()",
$"public override void {nameof(BlazorComponent.BuildRenderTree)}({typeof(RenderTreeBuilder).FullName} builder)");
resultOutput.WriteLine(generatedCode);
return Enumerable.Empty<RazorCompilerDiagnostic>();
}

View File

@ -22,6 +22,9 @@
<ItemGroup>
<ProjectReference Include="..\Microsoft.Blazor.Browser.JS\Microsoft.Blazor.Browser.JS.csproj" />
<ProjectReference Include="..\Microsoft.Blazor.Mono\Microsoft.Blazor.Mono.csproj" />
<ProjectReference Include="..\Microsoft.Blazor\Microsoft.Blazor.csproj" />
<PackageReference Include="Microsoft.AspNetCore.Razor" Version="2.0.1" />
<PackageReference Include="Microsoft.AspNetCore.Razor.Language" Version="2.0.1" />
<PackageReference Include="Microsoft.Extensions.CommandLineUtils" Version="1.1.1" />
<PackageReference Include="Microsoft.Extensions.FileProviders.Composite" Version="2.0.0" />
<PackageReference Include="Microsoft.Extensions.FileProviders.Physical" Version="2.0.0" />

View File

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.Blazor.Build.Core.RazorCompilation;
using Microsoft.Blazor.Components;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using System;
@ -95,10 +96,17 @@ namespace Microsoft.Blazor.Build.Test
{
CSharpSyntaxTree.ParseText(csharpResult.Code)
};
var references = new[]
var referenceAssembliesContainingTypes = new[]
{
MetadataReference.CreateFromFile(typeof(object).GetTypeInfo().Assembly.Location)
typeof(System.Runtime.AssemblyTargetedPatchBandAttribute), // System.Runtime
typeof(BlazorComponent)
};
var references = referenceAssembliesContainingTypes
.SelectMany(type => type.Assembly.GetReferencedAssemblies().Concat(new[] { type.Assembly.GetName() }))
.Distinct()
.Select(Assembly.Load)
.Select(assembly => MetadataReference.CreateFromFile(assembly.Location))
.ToList();
var options = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary);
var assemblyName = "TestAssembly" + Guid.NewGuid().ToString("N");
var compilation = CSharpCompilation.Create(assemblyName,
@ -126,12 +134,12 @@ namespace Microsoft.Blazor.Build.Test
using (var resultWriter = new StreamWriter(resultStream))
using (var verboseLogStream = new MemoryStream())
using (var verboseWriter = new StreamWriter(verboseLogStream))
using (var inputReader = new StringReader(cshtmlContent))
using (var inputContents = new MemoryStream(Encoding.UTF8.GetBytes(cshtmlContent)))
{
var diagnostics = new RazorCompiler().CompileSingleFile(
cshtmlRootPath,
cshtmlRelativePath,
inputReader,
inputContents,
outputNamespace,
resultWriter,
verboseWriter);