Add skeleton of syntax phases
This commit is contained in:
parent
49d3574677
commit
b341340d1f
|
|
@ -0,0 +1,16 @@
|
|||
// 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.AspNetCore.Razor.Evolution
|
||||
{
|
||||
internal class DefaultRazorParsingPhase : RazorEnginePhaseBase, IRazorParsingPhase
|
||||
{
|
||||
protected override void ExecuteCore(RazorCodeDocument codeDocument)
|
||||
{
|
||||
var syntaxTree = RazorSyntaxTree.Parse(codeDocument.Source);
|
||||
codeDocument.SetSyntaxTree(syntaxTree);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
// 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.Linq;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Evolution
|
||||
{
|
||||
internal class DefaultRazorSyntaxTreePhase : RazorEnginePhaseBase, IRazorSyntaxTreePhase
|
||||
{
|
||||
public IRazorSyntaxTreePass[] Passes { get; private set; }
|
||||
|
||||
protected override void OnIntialized()
|
||||
{
|
||||
Passes = Engine.Features.OfType<IRazorSyntaxTreePass>().OrderBy(p => p.Order).ToArray();
|
||||
}
|
||||
|
||||
protected override void ExecuteCore(RazorCodeDocument codeDocument)
|
||||
{
|
||||
var syntaxTree = codeDocument.GetSyntaxTree();
|
||||
ThrowForMissingDependency(syntaxTree);
|
||||
|
||||
foreach (var pass in Passes)
|
||||
{
|
||||
syntaxTree = pass.Execute(codeDocument, syntaxTree);
|
||||
}
|
||||
|
||||
codeDocument.SetSyntaxTree(syntaxTree);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -7,6 +7,6 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
{
|
||||
RazorEngine Engine { get; set; }
|
||||
|
||||
void Execute(RazorCodeDocument document);
|
||||
void Execute(RazorCodeDocument codeDocument);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
// 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
|
||||
{
|
||||
public interface IRazorParsingPhase : IRazorEnginePhase
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
// 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 until we flesh out public RazorSyntaxTree API
|
||||
internal interface IRazorSyntaxTreePass : IRazorEngineFeature
|
||||
{
|
||||
int Order { get; }
|
||||
|
||||
RazorSyntaxTree Execute(RazorCodeDocument codeDocument, RazorSyntaxTree syntaxTree);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
// 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 until we flesh out public RazorSyntaxTree API.
|
||||
internal interface IRazorSyntaxTreePhase : IRazorEnginePhase
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -10,6 +10,38 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
private static readonly ResourceManager _resourceManager
|
||||
= new ResourceManager("Microsoft.AspNetCore.Razor.Evolution.Resources", typeof(Resources).GetTypeInfo().Assembly);
|
||||
|
||||
/// <summary>
|
||||
/// The '{0}' phase requires a '{1}' provided by the '{2}'.
|
||||
/// </summary>
|
||||
internal static string PhaseDependencyMissing
|
||||
{
|
||||
get { return GetString("PhaseDependencyMissing"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The '{0}' phase requires a '{1}' provided by the '{2}'.
|
||||
/// </summary>
|
||||
internal static string FormatPhaseDependencyMissing(object p0, object p1, object p2)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("PhaseDependencyMissing"), p0, p1, p2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The phase must be initialized by setting the '{0}' property.
|
||||
/// </summary>
|
||||
internal static string PhaseMustBeInitialized
|
||||
{
|
||||
get { return GetString("PhaseMustBeInitialized"); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The phase must be initialized by setting the '{0}' property.
|
||||
/// </summary>
|
||||
internal static string FormatPhaseMustBeInitialized(object p0)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("PhaseMustBeInitialized"), p0);
|
||||
}
|
||||
|
||||
private static string GetString(string name, params string[] formatterNames)
|
||||
{
|
||||
var value = _resourceManager.GetString(name);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
// 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.AspNetCore.Razor.Evolution
|
||||
{
|
||||
public static class RazorCodeDocumentExtensions
|
||||
{
|
||||
public static RazorSyntaxTree GetSyntaxTree(this RazorCodeDocument document)
|
||||
{
|
||||
if (document == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(document));
|
||||
}
|
||||
|
||||
return document.Items[typeof(RazorSyntaxTree)] as RazorSyntaxTree;
|
||||
}
|
||||
|
||||
public static void SetSyntaxTree(this RazorCodeDocument document, RazorSyntaxTree syntaxTree)
|
||||
{
|
||||
if (document == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(document));
|
||||
}
|
||||
|
||||
document.Items[typeof(RazorSyntaxTree)] = syntaxTree;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -16,10 +16,24 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
public static RazorEngine Create(Action<IRazorEngineBuilder> configure)
|
||||
{
|
||||
var builder = new DefaultRazorEngineBuilder();
|
||||
AddDefaults(builder);
|
||||
configure?.Invoke(builder);
|
||||
return builder.Build();
|
||||
}
|
||||
|
||||
public static RazorEngine CreateEmpty(Action<IRazorEngineBuilder> configure)
|
||||
{
|
||||
var builder = new DefaultRazorEngineBuilder();
|
||||
configure?.Invoke(builder);
|
||||
return builder.Build();
|
||||
}
|
||||
|
||||
internal static void AddDefaults(IRazorEngineBuilder builder)
|
||||
{
|
||||
builder.Phases.Add(new DefaultRazorParsingPhase());
|
||||
builder.Phases.Add(new DefaultRazorSyntaxTreePhase());
|
||||
}
|
||||
|
||||
public abstract IReadOnlyList<IRazorEngineFeature> Features { get; }
|
||||
|
||||
public abstract IReadOnlyList<IRazorEnginePhase> Phases { get; }
|
||||
|
|
|
|||
|
|
@ -0,0 +1,59 @@
|
|||
// 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.AspNetCore.Razor.Evolution
|
||||
{
|
||||
internal abstract class RazorEnginePhaseBase : IRazorEnginePhase
|
||||
{
|
||||
private RazorEngine _engine;
|
||||
|
||||
public RazorEngine Engine
|
||||
{
|
||||
get { return _engine; }
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(value));
|
||||
}
|
||||
|
||||
_engine = value;
|
||||
OnIntialized();
|
||||
}
|
||||
}
|
||||
|
||||
public void Execute(RazorCodeDocument codeDocument)
|
||||
{
|
||||
if (codeDocument == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(codeDocument));
|
||||
}
|
||||
|
||||
if (Engine == null)
|
||||
{
|
||||
throw new InvalidOperationException(Resources.FormatPhaseMustBeInitialized(nameof(Engine)));
|
||||
}
|
||||
|
||||
ExecuteCore(codeDocument);
|
||||
}
|
||||
|
||||
protected void ThrowForMissingDependency<T>(T value)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
throw new InvalidOperationException(Resources.FormatPhaseDependencyMissing(
|
||||
GetType().Name,
|
||||
typeof(T).Name,
|
||||
typeof(RazorCodeDocument).Name));
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnIntialized()
|
||||
{
|
||||
}
|
||||
|
||||
protected abstract void ExecuteCore(RazorCodeDocument codeDocument);
|
||||
}
|
||||
}
|
||||
|
|
@ -117,4 +117,10 @@
|
|||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="PhaseDependencyMissing" xml:space="preserve">
|
||||
<value>The '{0}' phase requires a '{1}' provided by the '{2}'.</value>
|
||||
</data>
|
||||
<data name="PhaseMustBeInitialized" xml:space="preserve">
|
||||
<value>The phase must be initialized by setting the '{0}' property.</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
@ -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 Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Evolution
|
||||
{
|
||||
public class DefaultRazorParsingPhaseTest
|
||||
{
|
||||
[Fact]
|
||||
public void Execute_AddsSyntaxTree()
|
||||
{
|
||||
// Arrange
|
||||
var phase = new DefaultRazorParsingPhase();
|
||||
var engine = RazorEngine.CreateEmpty(b => b.Phases.Add(phase));
|
||||
|
||||
var codeDocument = TestRazorCodeDocument.CreateEmpty();
|
||||
|
||||
// Act
|
||||
phase.Execute(codeDocument);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(codeDocument.GetSyntaxTree());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
// 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.AspNetCore.Testing;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Evolution
|
||||
{
|
||||
public class DefaultRazorSyntaxTreePhaseTest
|
||||
{
|
||||
[Fact]
|
||||
public void OnInitialized_OrdersPassesInAscendingOrder()
|
||||
{
|
||||
// Arrange & Act
|
||||
var phase = new DefaultRazorSyntaxTreePhase();
|
||||
|
||||
var first = Mock.Of<IRazorSyntaxTreePass>(p => p.Order == 15);
|
||||
var second = Mock.Of<IRazorSyntaxTreePass>(p => p.Order == 17);
|
||||
|
||||
var engine = RazorEngine.CreateEmpty(b =>
|
||||
{
|
||||
b.Phases.Add(phase);
|
||||
|
||||
b.Features.Add(second);
|
||||
b.Features.Add(first);
|
||||
});
|
||||
|
||||
// Assert
|
||||
Assert.Collection(
|
||||
phase.Passes,
|
||||
p => Assert.Same(first, p),
|
||||
p => Assert.Same(second, p));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_ThrowsForMissingDependency()
|
||||
{
|
||||
// Arrange
|
||||
var phase = new DefaultRazorSyntaxTreePhase();
|
||||
|
||||
var engine = RazorEngine.CreateEmpty(b => b.Phases.Add(phase));
|
||||
|
||||
var codeDocument = TestRazorCodeDocument.CreateEmpty();
|
||||
|
||||
// Act & Assert
|
||||
ExceptionAssert.Throws<InvalidOperationException>(
|
||||
() => phase.Execute(codeDocument),
|
||||
$"The '{nameof(DefaultRazorSyntaxTreePhase)}' phase requires a '{nameof(RazorSyntaxTree)}' " +
|
||||
$"provided by the '{nameof(RazorCodeDocument)}'.");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_ExecutesPhasesInOrder()
|
||||
{
|
||||
// Arrange
|
||||
|
||||
var codeDocument = TestRazorCodeDocument.CreateEmpty();
|
||||
|
||||
// We're going to set up mocks to simulate a sequence of passes. We don't care about
|
||||
// what's in the trees, we're just going to look at the identity via strict mocks.
|
||||
var originalSyntaxTree = RazorSyntaxTree.Parse(codeDocument.Source);
|
||||
var firstPassSyntaxTree = RazorSyntaxTree.Parse(codeDocument.Source);
|
||||
var secondPassSyntaxTree = RazorSyntaxTree.Parse(codeDocument.Source);
|
||||
|
||||
var firstPass = new Mock<IRazorSyntaxTreePass>(MockBehavior.Strict);
|
||||
firstPass.SetupGet(m => m.Order).Returns(0);
|
||||
firstPass.Setup(m => m.Execute(codeDocument, originalSyntaxTree)).Returns(firstPassSyntaxTree);
|
||||
|
||||
var secondPass = new Mock<IRazorSyntaxTreePass>(MockBehavior.Strict);
|
||||
secondPass.SetupGet(m => m.Order).Returns(0);
|
||||
secondPass.Setup(m => m.Execute(codeDocument, firstPassSyntaxTree)).Returns(secondPassSyntaxTree);
|
||||
|
||||
var phase = new DefaultRazorSyntaxTreePhase();
|
||||
|
||||
var engine = RazorEngine.CreateEmpty(b =>
|
||||
{
|
||||
b.Phases.Add(phase);
|
||||
|
||||
b.Features.Add(secondPass.Object);
|
||||
b.Features.Add(firstPass.Object);
|
||||
});
|
||||
|
||||
// Act
|
||||
phase.Execute(codeDocument);
|
||||
|
||||
// Assert
|
||||
Assert.Same(secondPassSyntaxTree, codeDocument.GetSyntaxTree());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -19,7 +19,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
engine.Process(document);
|
||||
|
||||
// Assert
|
||||
// (nothing to verify yet)
|
||||
Assert.NotNull(document.GetSyntaxTree());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,41 @@
|
|||
// 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 Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Evolution
|
||||
{
|
||||
public class RazorCodeDocumentExtensionsTest
|
||||
{
|
||||
[Fact]
|
||||
public void GetRazorSyntaxTree_ReturnsSyntaxTree()
|
||||
{
|
||||
// Arrange
|
||||
var codeDocument = TestRazorCodeDocument.CreateEmpty();
|
||||
|
||||
var expected = RazorSyntaxTree.Parse(codeDocument.Source);
|
||||
codeDocument.Items[typeof(RazorSyntaxTree)] = expected;
|
||||
|
||||
// Act
|
||||
var actual = codeDocument.GetSyntaxTree();
|
||||
|
||||
// Assert
|
||||
Assert.Same(expected, actual);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SetRazorSyntaxTree_SetsSyntaxTree()
|
||||
{
|
||||
// Arrange
|
||||
var codeDocument = TestRazorCodeDocument.CreateEmpty();
|
||||
|
||||
var expected = RazorSyntaxTree.Parse(codeDocument.Source);
|
||||
|
||||
// Act
|
||||
codeDocument.SetSyntaxTree(expected);
|
||||
|
||||
// Assert
|
||||
Assert.Same(expected, codeDocument.Items[typeof(RazorSyntaxTree)]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -45,6 +45,9 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
// Act
|
||||
var engine = RazorEngine.Create(builder =>
|
||||
{
|
||||
builder.Features.Clear();
|
||||
builder.Phases.Clear();
|
||||
|
||||
builder.Features.Add(Mock.Of<IRazorEngineFeature>());
|
||||
builder.Features.Add(Mock.Of<IRazorEngineFeature>());
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue