diff --git a/samples/StandaloneApp/Index.cshtml b/samples/StandaloneApp/Index.cshtml
index f731b5af5a..92491f81c2 100644
--- a/samples/StandaloneApp/Index.cshtml
+++ b/samples/StandaloneApp/Index.cshtml
@@ -1,3 +1,2 @@
-@inherits Microsoft.Blazor.Components.BlazorComponent
-
Hello, world!
+Hello, world!
Hello from the Razor component.
diff --git a/src/Microsoft.Blazor.Build/Core/RazorCompilation/Engine/BlazorCodeDocItems.cs b/src/Microsoft.Blazor.Build/Core/RazorCompilation/Engine/BlazorCodeDocItems.cs
new file mode 100644
index 0000000000..1eeb4b59ee
--- /dev/null
+++ b/src/Microsoft.Blazor.Build/Core/RazorCompilation/Engine/BlazorCodeDocItems.cs
@@ -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();
+ }
+}
diff --git a/src/Microsoft.Blazor.Build/Core/RazorCompilation/Engine/BlazorCodeTarget.cs b/src/Microsoft.Blazor.Build/Core/RazorCompilation/Engine/BlazorCodeTarget.cs
new file mode 100644
index 0000000000..cc3a49d8ba
--- /dev/null
+++ b/src/Microsoft.Blazor.Build/Core/RazorCompilation/Engine/BlazorCodeTarget.cs
@@ -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
+{
+ ///
+ /// Directs a to use .
+ ///
+ internal class BlazorCodeTarget : CodeTarget
+ {
+ public override IntermediateNodeWriter CreateNodeWriter()
+ => new BlazorIntermediateNodeWriter();
+
+ public override TExtension GetExtension()
+ => throw new NotImplementedException();
+
+ public override bool HasExtension()
+ => throw new NotImplementedException();
+ }
+}
diff --git a/src/Microsoft.Blazor.Build/Core/RazorCompilation/Engine/BlazorIntermediateNodeWriter.cs b/src/Microsoft.Blazor.Build/Core/RazorCompilation/Engine/BlazorIntermediateNodeWriter.cs
new file mode 100644
index 0000000000..d8c84b8c22
--- /dev/null
+++ b/src/Microsoft.Blazor.Build/Core/RazorCompilation/Engine/BlazorIntermediateNodeWriter.cs
@@ -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
+{
+ ///
+ /// Generates the C# code corresponding to Razor source document contents.
+ ///
+ 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));
+ }
+ }
+}
diff --git a/src/Microsoft.Blazor.Build/Core/RazorCompilation/Engine/BlazorLoweringPhase.cs b/src/Microsoft.Blazor.Build/Core/RazorCompilation/Engine/BlazorLoweringPhase.cs
new file mode 100644
index 0000000000..4d2d44f8f8
--- /dev/null
+++ b/src/Microsoft.Blazor.Build/Core/RazorCompilation/Engine/BlazorLoweringPhase.cs
@@ -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
+{
+ ///
+ /// A phase that builds the C# document corresponding to
+ /// a for a Blazor component.
+ ///
+ 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);
+ }
+
+ ///
+ /// Creates instances that are configured to use
+ /// .
+ ///
+ 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();
+ }
+ }
+}
diff --git a/src/Microsoft.Blazor.Build/Core/RazorCompilation/Engine/BlazorRazorEngine.cs b/src/Microsoft.Blazor.Build/Core/RazorCompilation/Engine/BlazorRazorEngine.cs
new file mode 100644
index 0000000000..8fa4b4facb
--- /dev/null
+++ b/src/Microsoft.Blazor.Build/Core/RazorCompilation/Engine/BlazorRazorEngine.cs
@@ -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
+{
+ ///
+ /// Wraps , configuring it to compile Blazor components.
+ ///
+ 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().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);
+ }
+}
diff --git a/src/Microsoft.Blazor.Build/Core/RazorCompilation/RazorCompiler.cs b/src/Microsoft.Blazor.Build/Core/RazorCompilation/RazorCompiler.cs
index 6d59fdab34..346b4a77ab 100644
--- a/src/Microsoft.Blazor.Build/Core/RazorCompilation/RazorCompiler.cs
+++ b/src/Microsoft.Blazor.Build/Core/RazorCompilation/RazorCompiler.cs
@@ -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 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();
}
diff --git a/src/Microsoft.Blazor.Build/Microsoft.Blazor.Build.csproj b/src/Microsoft.Blazor.Build/Microsoft.Blazor.Build.csproj
index 47c9859584..5a5c65e271 100644
--- a/src/Microsoft.Blazor.Build/Microsoft.Blazor.Build.csproj
+++ b/src/Microsoft.Blazor.Build/Microsoft.Blazor.Build.csproj
@@ -22,6 +22,9 @@
+
+
+
diff --git a/test/Microsoft.Blazor.Build.Test/RazorCompilerTest.cs b/test/Microsoft.Blazor.Build.Test/RazorCompilerTest.cs
index 9e50e3fdd3..8273b396e3 100644
--- a/test/Microsoft.Blazor.Build.Test/RazorCompilerTest.cs
+++ b/test/Microsoft.Blazor.Build.Test/RazorCompilerTest.cs
@@ -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);