diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/DefaultRazorDesignTimeCSharpLoweringPhase.cs b/src/Microsoft.AspNetCore.Razor.Evolution/DefaultRazorDesignTimeCSharpLoweringPhase.cs new file mode 100644 index 0000000000..121148ea09 --- /dev/null +++ b/src/Microsoft.AspNetCore.Razor.Evolution/DefaultRazorDesignTimeCSharpLoweringPhase.cs @@ -0,0 +1,19 @@ +// 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.AspNetCore.Razor.Evolution +{ + internal class DefaultRazorDesignTimeCSharpLoweringPhase : RazorEnginePhaseBase, IRazorCSharpLoweringPhase + { + protected override void ExecuteCore(RazorCodeDocument codeDocument) + { + var irDocument = codeDocument.GetIRDocument(); + ThrowForMissingDependency(irDocument); + + var syntaxTree = codeDocument.GetSyntaxTree(); + ThrowForMissingDependency(syntaxTree); + + // Render design time CSharp + } + } +} diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/DefaultRazorEngineBuilder.cs b/src/Microsoft.AspNetCore.Razor.Evolution/DefaultRazorEngineBuilder.cs index d6c8515098..4a8c75ba95 100644 --- a/src/Microsoft.AspNetCore.Razor.Evolution/DefaultRazorEngineBuilder.cs +++ b/src/Microsoft.AspNetCore.Razor.Evolution/DefaultRazorEngineBuilder.cs @@ -1,6 +1,7 @@ // 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; namespace Microsoft.AspNetCore.Razor.Evolution @@ -17,6 +18,8 @@ namespace Microsoft.AspNetCore.Razor.Evolution public IList Phases { get; } + public bool DesignTime { get; set; } + public RazorEngine Build() { var features = new IRazorEngineFeature[Features.Count]; diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/DefaultRazorCSharpLoweringPhase.cs b/src/Microsoft.AspNetCore.Razor.Evolution/DefaultRazorRuntimeCSharpLoweringPhase.cs similarity index 99% rename from src/Microsoft.AspNetCore.Razor.Evolution/DefaultRazorCSharpLoweringPhase.cs rename to src/Microsoft.AspNetCore.Razor.Evolution/DefaultRazorRuntimeCSharpLoweringPhase.cs index d1a179012d..e3837b3503 100644 --- a/src/Microsoft.AspNetCore.Razor.Evolution/DefaultRazorCSharpLoweringPhase.cs +++ b/src/Microsoft.AspNetCore.Razor.Evolution/DefaultRazorRuntimeCSharpLoweringPhase.cs @@ -11,7 +11,7 @@ using Microsoft.AspNetCore.Razor.Evolution.Legacy; namespace Microsoft.AspNetCore.Razor.Evolution { - internal class DefaultRazorCSharpLoweringPhase : RazorEnginePhaseBase, IRazorCSharpLoweringPhase + internal class DefaultRazorRuntimeCSharpLoweringPhase : RazorEnginePhaseBase, IRazorCSharpLoweringPhase { protected override void ExecuteCore(RazorCodeDocument codeDocument) { diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/IRazorEngineBuilder.cs b/src/Microsoft.AspNetCore.Razor.Evolution/IRazorEngineBuilder.cs index 5f7848a753..9902368791 100644 --- a/src/Microsoft.AspNetCore.Razor.Evolution/IRazorEngineBuilder.cs +++ b/src/Microsoft.AspNetCore.Razor.Evolution/IRazorEngineBuilder.cs @@ -11,6 +11,8 @@ namespace Microsoft.AspNetCore.Razor.Evolution IList Phases { get; } + bool DesignTime { get; set; } + RazorEngine Build(); } } diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/RazorEngine.cs b/src/Microsoft.AspNetCore.Razor.Evolution/RazorEngine.cs index b84a851f34..73a1b8ca19 100644 --- a/src/Microsoft.AspNetCore.Razor.Evolution/RazorEngine.cs +++ b/src/Microsoft.AspNetCore.Razor.Evolution/RazorEngine.cs @@ -17,6 +17,24 @@ namespace Microsoft.AspNetCore.Razor.Evolution { var builder = new DefaultRazorEngineBuilder(); AddDefaults(builder); + AddRuntimeDefaults(builder); + configure?.Invoke(builder); + return builder.Build(); + } + + public static RazorEngine CreateDesignTime() + { + return CreateDesignTime(configure: null); + } + + public static RazorEngine CreateDesignTime(Action configure) + { + var builder = new DefaultRazorEngineBuilder() + { + DesignTime = true, + }; + AddDefaults(builder); + AddDesignTimeDefaults(builder); configure?.Invoke(builder); return builder.Build(); } @@ -34,7 +52,6 @@ namespace Microsoft.AspNetCore.Razor.Evolution builder.Phases.Add(new DefaultRazorSyntaxTreePhase()); builder.Phases.Add(new DefaultRazorIRLoweringPhase()); builder.Phases.Add(new DefaultRazorIRPhase()); - builder.Phases.Add(new DefaultRazorCSharpLoweringPhase()); // Syntax Tree passes builder.Features.Add(new DefaultDirectiveSyntaxTreePass()); @@ -45,10 +62,34 @@ namespace Microsoft.AspNetCore.Razor.Evolution builder.Features.Add(new DefaultDirectiveIRPass()); } + internal static void AddRuntimeDefaults(IRazorEngineBuilder builder) + { + builder.Phases.Add(new DefaultRazorRuntimeCSharpLoweringPhase()); + } + + internal static void AddDesignTimeDefaults(IRazorEngineBuilder builder) + { + builder.Phases.Add(new DefaultRazorDesignTimeCSharpLoweringPhase()); + + builder.Features.Add(new ConfigureDesignTimeOptions()); + } + public abstract IReadOnlyList Features { get; } public abstract IReadOnlyList Phases { get; } public abstract void Process(RazorCodeDocument document); + + internal class ConfigureDesignTimeOptions : IRazorConfigureParserFeature + { + public RazorEngine Engine { get; set; } + + public int Order { get; set; } + + public void Configure(RazorParserOptions options) + { + options.DesignTimeMode = true; + } + } } } diff --git a/test/Microsoft.AspNetCore.Razor.Evolution.Test/DefaultRazorCSharpLoweringPhaseTest.cs b/test/Microsoft.AspNetCore.Razor.Evolution.Test/DefaultRazorRuntimeCSharpLoweringPhaseTest.cs similarity index 76% rename from test/Microsoft.AspNetCore.Razor.Evolution.Test/DefaultRazorCSharpLoweringPhaseTest.cs rename to test/Microsoft.AspNetCore.Razor.Evolution.Test/DefaultRazorRuntimeCSharpLoweringPhaseTest.cs index d30aad9895..d189c2cce2 100644 --- a/test/Microsoft.AspNetCore.Razor.Evolution.Test/DefaultRazorCSharpLoweringPhaseTest.cs +++ b/test/Microsoft.AspNetCore.Razor.Evolution.Test/DefaultRazorRuntimeCSharpLoweringPhaseTest.cs @@ -8,13 +8,13 @@ using Xunit; namespace Microsoft.AspNetCore.Razor.Evolution { - public class DefaultRazorCSharpLoweringPhaseTest + public class DefaultRazorRuntimeCSharpLoweringPhaseTest { [Fact] public void Execute_ThrowsForMissingDependency() { // Arrange - var phase = new DefaultRazorCSharpLoweringPhase(); + var phase = new DefaultRazorRuntimeCSharpLoweringPhase(); var engine = RazorEngine.CreateEmpty(b => b.Phases.Add(phase)); @@ -23,7 +23,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution // Act & Assert ExceptionAssert.Throws( () => phase.Execute(codeDocument), - $"The '{nameof(DefaultRazorCSharpLoweringPhase)}' phase requires a '{nameof(DocumentIRNode)}' " + + $"The '{nameof(DefaultRazorRuntimeCSharpLoweringPhase)}' phase requires a '{nameof(DocumentIRNode)}' " + $"provided by the '{nameof(RazorCodeDocument)}'."); } } diff --git a/test/Microsoft.AspNetCore.Razor.Evolution.Test/RazorEngineTest.cs b/test/Microsoft.AspNetCore.Razor.Evolution.Test/RazorEngineTest.cs index 3c990517a4..9050249df7 100644 --- a/test/Microsoft.AspNetCore.Razor.Evolution.Test/RazorEngineTest.cs +++ b/test/Microsoft.AspNetCore.Razor.Evolution.Test/RazorEngineTest.cs @@ -11,7 +11,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution public class RazorEngineTest { [Fact] - public void Create_NoArg_CreatesDefaultEngine() + public void Create_NoArg_CreatesDefaultRuntimeEngine() { // Arrange // Act @@ -19,12 +19,25 @@ namespace Microsoft.AspNetCore.Razor.Evolution // Assert Assert.IsType(engine); - AssertDefaultFeatures(engine.Features); - AssertDefaultPhases(engine.Phases); + AssertDefaultRuntimeFeatures(engine.Features); + AssertDefaultRuntimePhases(engine.Phases); } [Fact] - public void Create_Null_CreatesDefaultEngine() + public void CreateDesignTime_NoArg_CreatesDefaultDesignTimeEngine() + { + // Arrange + // Act + var engine = RazorEngine.CreateDesignTime(); + + // Assert + Assert.IsType(engine); + AssertDefaultDesignTimeFeatures(engine.Features); + AssertDefaultDesignTimePhases(engine.Phases); + } + + [Fact] + public void Create_Null_CreatesDefaultRuntimeEngine() { // Arrange // Act @@ -32,8 +45,21 @@ namespace Microsoft.AspNetCore.Razor.Evolution // Assert Assert.IsType(engine); - AssertDefaultFeatures(engine.Features); - AssertDefaultPhases(engine.Phases); + AssertDefaultRuntimeFeatures(engine.Features); + AssertDefaultRuntimePhases(engine.Phases); + } + + [Fact] + public void CreateDesignTime_Null_CreatesDefaultDesignTimeEngine() + { + // Arrange + // Act + var engine = RazorEngine.CreateDesignTime(configure: null); + + // Assert + Assert.IsType(engine); + AssertDefaultDesignTimeFeatures(engine.Features); + AssertDefaultDesignTimePhases(engine.Phases); } [Fact] @@ -71,7 +97,42 @@ namespace Microsoft.AspNetCore.Razor.Evolution p => Assert.Same(phases[1], p)); } - private static void AssertDefaultFeatures(IEnumerable features) + [Fact] + public void CreateDesignTime_Lambda_AddsFeaturesAndPhases() + { + // Arrange + IRazorEngineFeature[] features = null; + IRazorEnginePhase[] phases = null; + + // Act + var engine = RazorEngine.CreateDesignTime(builder => + { + builder.Features.Clear(); + builder.Phases.Clear(); + + builder.Features.Add(Mock.Of()); + builder.Features.Add(Mock.Of()); + + builder.Phases.Add(Mock.Of()); + builder.Phases.Add(Mock.Of()); + + features = builder.Features.ToArray(); + phases = builder.Phases.ToArray(); + }); + + // Assert + Assert.Collection( + engine.Features, + f => Assert.Same(features[0], f), + f => Assert.Same(features[1], f)); + + Assert.Collection( + engine.Phases, + p => Assert.Same(phases[0], p), + p => Assert.Same(phases[1], p)); + } + + private static void AssertDefaultRuntimeFeatures(IEnumerable features) { Assert.Collection( features, @@ -81,7 +142,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution feature => Assert.IsType(feature)); } - private static void AssertDefaultPhases(IReadOnlyList phases) + private static void AssertDefaultRuntimePhases(IReadOnlyList phases) { Assert.Collection( phases, @@ -89,7 +150,29 @@ namespace Microsoft.AspNetCore.Razor.Evolution phase => Assert.IsType(phase), phase => Assert.IsType(phase), phase => Assert.IsType(phase), - phase => Assert.IsType(phase)); + phase => Assert.IsType(phase)); + } + + private static void AssertDefaultDesignTimeFeatures(IEnumerable features) + { + Assert.Collection( + features, + feature => Assert.IsType(feature), + feature => Assert.IsType(feature), + feature => Assert.IsType(feature), + feature => Assert.IsType(feature), + feature => Assert.IsType(feature)); + } + + private static void AssertDefaultDesignTimePhases(IReadOnlyList phases) + { + Assert.Collection( + phases, + phase => Assert.IsType(phase), + phase => Assert.IsType(phase), + phase => Assert.IsType(phase), + phase => Assert.IsType(phase), + phase => Assert.IsType(phase)); } } } \ No newline at end of file