Skip to content
This repository has been archived by the owner on Dec 14, 2018. It is now read-only.

Commit

Permalink
External functions in razor view
Browse files Browse the repository at this point in the history
Fixes #809
  • Loading branch information
pranavkm committed Aug 3, 2016
1 parent f7e9575 commit d63e486
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 33 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// 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;
using Microsoft.AspNetCore.Razor.CodeGenerators;

namespace Microsoft.AspNetCore.Mvc.Razor
{
/// <summary>
/// Specifies the contracts for a Razor host that parses Razor files and generates C# code.
/// </summary>
public interface IMvcRazorHostWithTemplateEngineContext
{
/// <summary>
/// Parses and generates the contents of a Razor file.
/// </summary>
/// <returns>The <see cref="GeneratorResults"/>.</returns>
GeneratorResults GenerateCode(TemplateEngineContext context);
}
}
49 changes: 47 additions & 2 deletions src/Microsoft.AspNetCore.Mvc.Razor.Host/MvcRazorHost.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.IO;
using System.Linq;
using Microsoft.AspNetCore.Mvc.Razor.Directives;
using Microsoft.AspNetCore.Mvc.Razor.Host;
using Microsoft.AspNetCore.Mvc.Razor.Internal;
using Microsoft.AspNetCore.Razor;
using Microsoft.AspNetCore.Razor.Chunks;
Expand All @@ -20,7 +21,7 @@

namespace Microsoft.AspNetCore.Mvc.Razor
{
public class MvcRazorHost : RazorEngineHost, IMvcRazorHost
public class MvcRazorHost : RazorEngineHost, IMvcRazorHost, IMvcRazorHostWithTemplateEngineContext
{
private const string BaseType = "Microsoft.AspNetCore.Mvc.Razor.RazorPage";
private const string HtmlHelperPropertyName = "Html";
Expand Down Expand Up @@ -118,7 +119,7 @@ internal MvcRazorHost(IChunkTreeCache chunkTreeCache, RazorPathNormalizer pathNo
TagHelperOutputContentPropertyName = nameof(TagHelperOutput.Content),
ExecutionContextSetOutputContentAsyncMethodName = nameof(TagHelperExecutionContext.SetOutputContentAsync),
TagHelperAttributeValuePropertyName = nameof(TagHelperAttribute.Value),
})
})
{
BeginContextMethodName = "BeginContext",
EndContextMethodName = "EndContext"
Expand Down Expand Up @@ -285,6 +286,23 @@ public GeneratorResults GenerateCode(string rootRelativePath, Stream inputStream
return engine.GenerateCode(inputStream, className, DefaultNamespace, rootRelativePath);
}

/// <inheritdoc />
public GeneratorResults GenerateCode(TemplateEngineContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}

if (string.IsNullOrEmpty(context.ClassName) && !string.IsNullOrEmpty(context.RelativePath))
{
context.ClassName = ParserHelpers.SanitizeClassName(context.RelativePath);
}

var engine = new RazorTemplateEngine(this);
return engine.GenerateCode(context);
}

/// <inheritdoc />
public override RazorParser DecorateRazorParser(RazorParser razorParser, string sourceFileName)
{
Expand All @@ -297,6 +315,33 @@ public override RazorParser DecorateRazorParser(RazorParser razorParser, string
return new MvcRazorParser(razorParser, inheritedChunkTrees, DefaultInheritedChunks, ModelExpressionType);
}

public override RazorParser DecorateRazorParser(
RazorParser incomingRazorParser,
TemplateEngineContext context)
{
if (context == null)
{
throw new ArgumentNullException(nameof(context));
}

if (incomingRazorParser == null)
{
throw new ArgumentNullException(nameof(incomingRazorParser));
}

if (string.IsNullOrEmpty(context.RelativePath))
{
throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpy, nameof(context.RelativePath));
}

var inheritedChunkTrees = GetInheritedChunkTrees(context.RelativePath);
return new MvcRazorParser(
incomingRazorParser,
inheritedChunkTrees,
DefaultInheritedChunks,
ModelExpressionType);
}

/// <inheritdoc />
public override ParserBase DecorateCodeParser(ParserBase incomingCodeParser)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ public CompilationResult Compile(RelativeFileInfo fileInfo, string compilationCo
if (!result.Success)
{
return GetCompilationFailedResult(
fileInfo.RelativePath,
fileInfo,
compilationContent,
assemblyName,
result.Diagnostics);
Expand Down Expand Up @@ -201,14 +201,14 @@ private CSharpCompilation Rewrite(CSharpCompilation compilation)

// Internal for unit testing
internal CompilationResult GetCompilationFailedResult(
string relativePath,
RelativeFileInfo fileInfo,
string compilationContent,
string assemblyName,
IEnumerable<Diagnostic> diagnostics)
{
var diagnosticGroups = diagnostics
.Where(IsError)
.GroupBy(diagnostic => GetFilePath(relativePath, diagnostic), StringComparer.Ordinal);
.GroupBy(diagnostic => GetFilePath(fileInfo.RelativePath, diagnostic), StringComparer.Ordinal);

var failures = new List<CompilationFailure>();
foreach (var group in diagnosticGroups)
Expand All @@ -223,7 +223,7 @@ private CSharpCompilation Rewrite(CSharpCompilation compilation)
}
else
{
sourceFileContent = ReadFileContentsSafely(_fileProvider, sourceFilePath);
sourceFileContent = ReadFileContentsSafely(fileInfo.FileInfo);
}

string additionalMessage = null;
Expand Down Expand Up @@ -265,9 +265,8 @@ private static bool IsError(Diagnostic diagnostic)
return diagnostic.IsWarningAsError || diagnostic.Severity == DiagnosticSeverity.Error;
}

private static string ReadFileContentsSafely(IFileProvider fileProvider, string filePath)
private static string ReadFileContentsSafely(IFileInfo fileInfo)
{
var fileInfo = fileProvider.GetFileInfo(filePath);
if (fileInfo.Exists)
{
try
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
using System.Linq;
using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Mvc.Razor.Compilation;
using Microsoft.AspNetCore.Mvc.Razor.Internal;
using Microsoft.AspNetCore.Razor;
using Microsoft.AspNetCore.Razor.CodeGenerators;
using Microsoft.Extensions.FileProviders;
Expand All @@ -23,6 +22,7 @@ public class RazorCompilationService : IRazorCompilationService
{
private readonly ICompilationService _compilationService;
private readonly IMvcRazorHost _razorHost;
private readonly IMvcRazorHostWithTemplateEngineContext _razorHostWithContext;
private readonly IFileProvider _fileProvider;
private readonly ILogger _logger;

Expand All @@ -41,6 +41,7 @@ public class RazorCompilationService : IRazorCompilationService
{
_compilationService = compilationService;
_razorHost = razorHost;
_razorHostWithContext = razorHost as IMvcRazorHostWithTemplateEngineContext;
_fileProvider = fileProviderAccessor.FileProvider;
_logger = loggerFactory.CreateLogger<RazorCompilationService>();
}
Expand All @@ -53,16 +54,29 @@ public CompilationResult Compile(RelativeFileInfo file)
throw new ArgumentNullException(nameof(file));
}

var filePath = file.FileInfo.PhysicalPath ?? file.RelativePath;
GeneratorResults results;
using (var inputStream = file.FileInfo.CreateReadStream())
{
_logger.RazorFileToCodeCompilationStart(file.RelativePath);
_logger.RazorFileToCodeCompilationStart(filePath);

var startTimestamp = _logger.IsEnabled(LogLevel.Debug) ? Stopwatch.GetTimestamp() : 0;

results = GenerateCode(file.RelativePath, inputStream);
if (_razorHostWithContext != null)
{
var context = new TemplateEngineContext(inputStream, filePath)
{
RelativePath = file.RelativePath,
};

results = _razorHostWithContext.GenerateCode(context);
}
else
{
results = _razorHost.GenerateCode(filePath, inputStream);
}

_logger.RazorFileToCodeCompilationEnd(file.RelativePath, startTimestamp);
_logger.RazorFileToCodeCompilationEnd(filePath, startTimestamp);
}

if (!results.Success)
Expand All @@ -73,36 +87,21 @@ public CompilationResult Compile(RelativeFileInfo file)
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);
.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 fileContent = ReadFileContentsSafely(file.FileInfo);
var compilationFailure = new CompilationFailure(
filePath,
fileContent,
Expand All @@ -127,9 +126,8 @@ private DiagnosticMessage CreateDiagnosticMessage(RazorError error, string fileP
endColumn: error.Location.CharacterIndex + error.Length);
}

private string ReadFileContentsSafely(string relativePath)
private string ReadFileContentsSafely(IFileInfo fileInfo)
{
var fileInfo = _fileProvider.GetFileInfo(relativePath);
if (fileInfo.Exists)
{
try
Expand Down

0 comments on commit d63e486

Please sign in to comment.