From 046dcefd14692e6205ddc6cdb2cc5d1fe4ebe662 Mon Sep 17 00:00:00 2001 From: Pavel Krymets Date: Wed, 2 Dec 2015 10:34:06 -0800 Subject: [PATCH] Add extensibility point to Razor compilation --- .../Compilation/RoslynCompilationContext.cs | 33 ++++++++++++++ .../Compilation/RoslynCompilationService.cs | 6 +++ .../RazorViewEngineOptions.cs | 21 +++++++++ .../RazorViewEngineOptionsSetup.cs | 8 ++-- .../RoslynCompilationServiceTest.cs | 45 ++++++++++++++++++- 5 files changed, 108 insertions(+), 5 deletions(-) create mode 100644 src/Microsoft.AspNet.Mvc.Razor/Compilation/RoslynCompilationContext.cs diff --git a/src/Microsoft.AspNet.Mvc.Razor/Compilation/RoslynCompilationContext.cs b/src/Microsoft.AspNet.Mvc.Razor/Compilation/RoslynCompilationContext.cs new file mode 100644 index 0000000000..31fe25899e --- /dev/null +++ b/src/Microsoft.AspNet.Mvc.Razor/Compilation/RoslynCompilationContext.cs @@ -0,0 +1,33 @@ +// 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 Microsoft.CodeAnalysis.CSharp; + +namespace Microsoft.AspNet.Mvc.Razor.Compilation +{ + /// + /// Context object used to pass information about the current Razor page compilation. + /// + public class RoslynCompilationContext + { + /// + /// Constructs a new instance of the type. + /// + /// to be set to property. + public RoslynCompilationContext(CSharpCompilation compilation) + { + if (compilation == null) + { + throw new ArgumentNullException(nameof(compilation)); + } + + Compilation = compilation; + } + + /// + /// Gets or sets the used for current source file compilation. + /// + public CSharpCompilation Compilation { get; set; } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Razor/Compilation/RoslynCompilationService.cs b/src/Microsoft.AspNet.Mvc.Razor/Compilation/RoslynCompilationService.cs index bacd815042..7af077d285 100644 --- a/src/Microsoft.AspNet.Mvc.Razor/Compilation/RoslynCompilationService.cs +++ b/src/Microsoft.AspNet.Mvc.Razor/Compilation/RoslynCompilationService.cs @@ -40,6 +40,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation private readonly Lazy> _applicationReferences; private readonly string _classPrefix; private readonly string _configuration; + private Action _compilationCallback; #if DOTNET5_5 private readonly RazorLoadContext _razorLoadContext; @@ -71,6 +72,7 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation _fileProvider = optionsAccessor.Value.FileProvider; _classPrefix = host.MainClassNamePrefix; _configuration = optionsAccessor.Value.Configuration; + _compilationCallback = optionsAccessor.Value.CompilationCallback; #if DOTNET5_5 _razorLoadContext = new RazorLoadContext(); @@ -111,6 +113,10 @@ namespace Microsoft.AspNet.Mvc.Razor.Compilation compilation = Rewrite(compilation); + var compilationContext = new RoslynCompilationContext(compilation); + _compilationCallback(compilationContext); + compilation = compilationContext.Compilation; + using (var ms = new MemoryStream()) { using (var pdb = new MemoryStream()) diff --git a/src/Microsoft.AspNet.Mvc.Razor/RazorViewEngineOptions.cs b/src/Microsoft.AspNet.Mvc.Razor/RazorViewEngineOptions.cs index 39327befb5..479224c4b5 100644 --- a/src/Microsoft.AspNet.Mvc.Razor/RazorViewEngineOptions.cs +++ b/src/Microsoft.AspNet.Mvc.Razor/RazorViewEngineOptions.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using Microsoft.AspNet.FileProviders; +using Microsoft.AspNet.Mvc.Razor.Compilation; namespace Microsoft.AspNet.Mvc.Razor { @@ -16,6 +17,8 @@ namespace Microsoft.AspNet.Mvc.Razor private string _configuration; + private Action _compilationCallback = c => { }; + /// /// Get a used by the . /// @@ -65,5 +68,23 @@ namespace Microsoft.AspNet.Mvc.Razor _configuration = value; } } + + /// + /// Gets or sets the callback that is used to customize Razor compilation + /// to change compilation settings you can update property. + /// Customizations made here would not reflect in tooling (Intellisense). + /// + public Action CompilationCallback + { + get { return _compilationCallback; } + set + { + if (value == null) + { + throw new ArgumentNullException(nameof(value)); + } + _compilationCallback = value; + } + } } } diff --git a/src/Microsoft.AspNet.Mvc.Razor/RazorViewEngineOptionsSetup.cs b/src/Microsoft.AspNet.Mvc.Razor/RazorViewEngineOptionsSetup.cs index 5925e1d9a3..7276b64126 100644 --- a/src/Microsoft.AspNet.Mvc.Razor/RazorViewEngineOptionsSetup.cs +++ b/src/Microsoft.AspNet.Mvc.Razor/RazorViewEngineOptionsSetup.cs @@ -4,6 +4,8 @@ using Microsoft.AspNet.FileProviders; using Microsoft.AspNet.Hosting; using Microsoft.AspNet.Mvc.Razor; +using Microsoft.AspNet.Mvc.Razor.Compilation; +using Microsoft.Extensions.CompilationAbstractions; using Microsoft.Extensions.OptionsModel; using Microsoft.Extensions.PlatformAbstractions; @@ -19,13 +21,13 @@ namespace Microsoft.AspNet.Mvc /// /// for the application. /// for the application. - public RazorViewEngineOptionsSetup(IApplicationEnvironment applicationEnvironment, IHostingEnvironment hostingEnvironment) + public RazorViewEngineOptionsSetup(IApplicationEnvironment applicationEnvironment, + IHostingEnvironment hostingEnvironment) : base(options => ConfigureRazor(options, applicationEnvironment, hostingEnvironment)) { } - private static void ConfigureRazor( - RazorViewEngineOptions razorOptions, + private static void ConfigureRazor(RazorViewEngineOptions razorOptions, IApplicationEnvironment applicationEnvironment, IHostingEnvironment hostingEnvironment) { diff --git a/test/Microsoft.AspNet.Mvc.Razor.Test/Compilation/RoslynCompilationServiceTest.cs b/test/Microsoft.AspNet.Mvc.Razor.Test/Compilation/RoslynCompilationServiceTest.cs index 66c0647927..21611b7cc8 100644 --- a/test/Microsoft.AspNet.Mvc.Razor.Test/Compilation/RoslynCompilationServiceTest.cs +++ b/test/Microsoft.AspNet.Mvc.Razor.Test/Compilation/RoslynCompilationServiceTest.cs @@ -362,6 +362,45 @@ public class NotRazorPrefixType {}"; }); } + [Fact] + public void Compile_RunsCallback() + { + var content = "public class MyTestType {}"; + var applicationEnvironment = PlatformServices.Default.Application; + var libraryExporter = CompilationServices.Default.LibraryExporter; + + var compilerOptionsProvider = new Mock(); + compilerOptionsProvider + .Setup(p => p.GetCompilerOptions( + applicationEnvironment.ApplicationName, + applicationEnvironment.RuntimeFramework, + ConfigurationName)) + .Returns(new CompilerOptions()); + + RoslynCompilationContext usedCompilation = null; + var mvcRazorHost = new Mock(); + mvcRazorHost.SetupGet(m => m.MainClassNamePrefix) + .Returns(string.Empty); + + var compilationService = new RoslynCompilationService( + applicationEnvironment, + libraryExporter, + compilerOptionsProvider.Object, + mvcRazorHost.Object, + GetOptions(callback: c => usedCompilation = c)); + + var relativeFileInfo = new RelativeFileInfo( + new TestFileInfo { PhysicalPath = "SomePath" }, + "some-relative-path"); + + // Act + var result = compilationService.Compile(relativeFileInfo, content); + + Assert.NotNull(usedCompilation); + Assert.NotNull(usedCompilation.Compilation); + Assert.Equal(1, usedCompilation.Compilation.SyntaxTrees.Length); + } + private static DiagnosticDescriptor GetDiagnosticDescriptor(string messageFormat) { return new DiagnosticDescriptor( @@ -373,12 +412,14 @@ public class NotRazorPrefixType {}"; isEnabledByDefault: true); } - private static IOptions GetOptions(IFileProvider fileProvider = null) + private static IOptions GetOptions(IFileProvider fileProvider = null, + Action callback = null) { var razorViewEngineOptions = new RazorViewEngineOptions { FileProvider = fileProvider ?? new TestFileProvider(), - Configuration = ConfigurationName + Configuration = ConfigurationName, + CompilationCallback = callback ?? (c => { }) }; var options = new Mock>(); options