Begin emitting C# classes for Razor components

This commit is contained in:
Steve Sanderson 2018-01-11 11:51:27 +00:00
parent 3f522ab216
commit 5b3f05bdc1
3 changed files with 101 additions and 19 deletions

View File

@ -1,30 +1,91 @@
// 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.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
namespace Microsoft.Blazor.Build.Core.RazorCompilation
{
/// <summary>
/// Provides facilities for transforming Razor files into Blazor component classes.
/// </summary>
public class RazorCompiler
{
public ICollection<RazorCompilerDiagnostic> CompileFiles(IEnumerable<string> inputPaths, string outputNamespace, TextWriter resultOutput, TextWriter verboseOutput)
{
var diagnostics = new List<RazorCompilerDiagnostic>();
// TODO: Relax this to allow for whatever C# allows
private static Regex ClassNameRegex
= new Regex("^[a-z][a-z0-9_]*$", RegexOptions.IgnoreCase);
foreach (var inputFilePath in inputPaths)
/// <summary>
/// Writes C# source code representing Blazor components defined by Razor files.
/// </summary>
/// <param name="inputPaths">Paths to the input files.</param>
/// <param name="outputNamespace">The namespace for the generated classes.</param>
/// <param name="resultOutput">A <see cref="TextWriter"/> to which C# source code will be written.</param>
/// <param name="verboseOutput">If not null, additional information will be written to this <see cref="TextWriter"/>.</param>
/// <returns>A collection of <see cref="RazorCompilerDiagnostic"/> instances representing any warnings or errors that were encountered.</returns>
public ICollection<RazorCompilerDiagnostic> CompileFiles(IEnumerable<string> inputPaths, string outputNamespace, TextWriter resultOutput, TextWriter verboseOutput)
=> inputPaths.SelectMany(
path => CompileSingleFile(path, outputNamespace, resultOutput, verboseOutput)).ToList();
/// <summary>
/// Writes C# source code representing a Blazor component defined by a Razor file.
/// </summary>
/// <param name="inputPaths">The path to the input file.</param>
/// <param name="outputNamespace">The namespace for the generated class.</param>
/// <param name="resultOutput">A <see cref="TextWriter"/> to which C# source code will be written.</param>
/// <param name="verboseOutput">If not null, additional information will be written to this <see cref="TextWriter"/>.</param>
/// <returns>An enumerable of <see cref="RazorCompilerDiagnostic"/> instances representing any warnings or errors that were encountered.</returns>
public IEnumerable<RazorCompilerDiagnostic> CompileSingleFile(string inputFilePath, string outputNamespace, TextWriter resultOutput, TextWriter verboseOutput)
{
if (resultOutput == null)
{
verboseOutput?.WriteLine($"Compiling {inputFilePath}...");
resultOutput.WriteLine($"// TODO: Compile {inputFilePath}");
diagnostics.Add(new RazorCompilerDiagnostic(
RazorCompilerDiagnostic.DiagnosticType.Warning,
inputFilePath,
1,
1,
"Compiler not implemented"));
throw new ArgumentNullException(nameof(resultOutput));
}
return diagnostics;
try
{
verboseOutput?.WriteLine($"Compiling {inputFilePath}...");
var className = GetClassName(inputFilePath);
resultOutput.WriteLine($"namespace {outputNamespace} {{");
resultOutput.WriteLine($"public class {className}");
resultOutput.WriteLine("{");
resultOutput.WriteLine("}");
resultOutput.WriteLine("}");
resultOutput.WriteLine();
return Enumerable.Empty<RazorCompilerDiagnostic>();
}
catch (RazorCompilerException ex)
{
return new[] { ex.ToDiagnostic(inputFilePath) };
}
catch (Exception ex)
{
return new[]
{
new RazorCompilerDiagnostic(
RazorCompilerDiagnostic.DiagnosticType.Error,
inputFilePath,
1,
1,
$"Unexpected exception: {ex.Message}{Environment.NewLine}{ex.StackTrace}")
};
}
}
private static string GetClassName(string inputFilePath)
{
var basename = Path.GetFileNameWithoutExtension(inputFilePath);
if (!ClassNameRegex.IsMatch(basename))
{
throw new RazorCompilerException($"Invalid name '{basename}'. The name must be valid for a C# class name.");
}
return basename;
}
}
}

View File

@ -0,0 +1,26 @@
// 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;
namespace Microsoft.Blazor.Build.Core.RazorCompilation
{
/// <summary>
/// Represents a fatal error during the transformation of a Blazor component from
/// Razor source code to C# source code.
/// </summary>
internal class RazorCompilerException : Exception
{
public RazorCompilerException(string message): base(message)
{
}
public RazorCompilerDiagnostic ToDiagnostic(string sourceFilePath)
=> new RazorCompilerDiagnostic(
RazorCompilerDiagnostic.DiagnosticType.Error,
sourceFilePath,
line: 1, // Later it might be necessary to take line/col constructor args, but not needed yet
column: 1,
message: Message);
}
}

View File

@ -1,9 +1,4 @@
<Project>
<!-- Temporarily, the Razor 'compile' process just produces a set of empty classes. This is to
show it's possible to codegen the classes both during regular builds and at design time so
that the Razor components can be referenced with intellisense from .cs files. -->
<Project>
<Target Name="BlazorCompileRazorComponents" BeforeTargets="CoreCompile">
<PropertyGroup>
<BlazorComponentsNamespace>BlazorComponents</BlazorComponentsNamespace>