140 lines
5.4 KiB
C#
140 lines
5.4 KiB
C#
// 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 Microsoft.AspNet.Diagnostics;
|
|
using Microsoft.AspNet.FileProviders;
|
|
using Microsoft.AspNet.Razor;
|
|
using Microsoft.AspNet.Razor.CodeGenerators;
|
|
using Microsoft.Extensions.OptionsModel;
|
|
|
|
namespace Microsoft.AspNet.Mvc.Razor.Compilation
|
|
{
|
|
/// <summary>
|
|
/// Default implementation of <see cref="IRazorCompilationService"/>.
|
|
/// </summary>
|
|
public class RazorCompilationService : IRazorCompilationService
|
|
{
|
|
private readonly ICompilationService _compilationService;
|
|
private readonly IMvcRazorHost _razorHost;
|
|
private readonly IFileProvider _fileProvider;
|
|
|
|
/// <summary>
|
|
/// Instantiates a new instance of the <see cref="RazorCompilationService"/> class.
|
|
/// </summary>
|
|
/// <param name="compilationService">The <see cref="ICompilationService"/> to compile generated code.</param>
|
|
/// <param name="razorHost">The <see cref="IMvcRazorHost"/> to generate code from Razor files.</param>
|
|
/// <param name="viewEngineOptions">
|
|
/// The <see cref="IFileProvider"/> to read Razor files referenced in error messages.
|
|
/// </param>
|
|
public RazorCompilationService(
|
|
ICompilationService compilationService,
|
|
IMvcRazorHost razorHost,
|
|
IOptions<RazorViewEngineOptions> viewEngineOptions)
|
|
{
|
|
_compilationService = compilationService;
|
|
_razorHost = razorHost;
|
|
_fileProvider = viewEngineOptions.Value.FileProvider;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public CompilationResult Compile(RelativeFileInfo file)
|
|
{
|
|
if (file == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(file));
|
|
}
|
|
|
|
GeneratorResults results;
|
|
using (var inputStream = file.FileInfo.CreateReadStream())
|
|
{
|
|
results = GenerateCode(file.RelativePath, inputStream);
|
|
}
|
|
|
|
if (!results.Success)
|
|
{
|
|
return GetCompilationFailedResult(file, results.ParserErrors);
|
|
}
|
|
|
|
return _compilationService.Compile(file, results.GeneratedCode);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Generate code for the Razor file at <paramref name="relativePath"/> with content
|
|
/// <paramref name="inputStream"/>.
|
|
/// </summary>
|
|
/// <param name="relativePath">
|
|
/// The path of the Razor file relative to the root of the application. Used to generate line pragmas and
|
|
/// calculate the class name of the generated type.
|
|
/// </param>
|
|
/// <param name="inputStream">A <see cref="Stream"/> that contains the Razor content.</param>
|
|
/// <returns>A <see cref="GeneratorResults"/> instance containing results of code generation.</returns>
|
|
protected virtual GeneratorResults GenerateCode(string relativePath, Stream inputStream)
|
|
{
|
|
return _razorHost.GenerateCode(relativePath, inputStream);
|
|
}
|
|
|
|
// Internal for unit testing
|
|
internal CompilationResult GetCompilationFailedResult(RelativeFileInfo file, IEnumerable<RazorError> errors)
|
|
{
|
|
// If a SourceLocation does not specify a file path, assume it is produced
|
|
// from parsing the current file.
|
|
var messageGroups = errors
|
|
.GroupBy(razorError =>
|
|
razorError.Location.FilePath ?? file.RelativePath,
|
|
StringComparer.Ordinal);
|
|
|
|
var failures = new List<CompilationFailure>();
|
|
foreach (var group in messageGroups)
|
|
{
|
|
var filePath = group.Key;
|
|
var fileContent = ReadFileContentsSafely(filePath);
|
|
var compilationFailure = new CompilationFailure(
|
|
filePath,
|
|
fileContent,
|
|
compiledContent: string.Empty,
|
|
messages: group.Select(parserError => CreateDiagnosticMessage(parserError, filePath)));
|
|
failures.Add(compilationFailure);
|
|
}
|
|
|
|
return new CompilationResult(failures);
|
|
}
|
|
|
|
private DiagnosticMessage CreateDiagnosticMessage(RazorError error, string filePath)
|
|
{
|
|
return new DiagnosticMessage(
|
|
message: error.Message,
|
|
formattedMessage: $"{error} ({error.Location.LineIndex},{error.Location.CharacterIndex}) {error.Message}",
|
|
filePath: filePath,
|
|
startLine: error.Location.LineIndex + 1,
|
|
startColumn: error.Location.CharacterIndex,
|
|
endLine: error.Location.LineIndex + 1,
|
|
endColumn: error.Location.CharacterIndex + error.Length);
|
|
}
|
|
|
|
private string ReadFileContentsSafely(string relativePath)
|
|
{
|
|
var fileInfo = _fileProvider.GetFileInfo(relativePath);
|
|
if (fileInfo.Exists)
|
|
{
|
|
try
|
|
{
|
|
using (var reader = new StreamReader(fileInfo.CreateReadStream()))
|
|
{
|
|
return reader.ReadToEnd();
|
|
}
|
|
}
|
|
catch
|
|
{
|
|
// Ignore any failures
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|
|
}
|