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; }
|
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
|
private static readonly ResourceManager _resourceManager
|
||||||
= new ResourceManager("Microsoft.AspNetCore.Razor.Evolution.Resources", typeof(Resources).GetTypeInfo().Assembly);
|
= 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)
|
private static string GetString(string name, params string[] formatterNames)
|
||||||
{
|
{
|
||||||
var value = _resourceManager.GetString(name);
|
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)
|
public static RazorEngine Create(Action<IRazorEngineBuilder> configure)
|
||||||
{
|
{
|
||||||
var builder = new DefaultRazorEngineBuilder();
|
var builder = new DefaultRazorEngineBuilder();
|
||||||
|
AddDefaults(builder);
|
||||||
configure?.Invoke(builder);
|
configure?.Invoke(builder);
|
||||||
return builder.Build();
|
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<IRazorEngineFeature> Features { get; }
|
||||||
|
|
||||||
public abstract IReadOnlyList<IRazorEnginePhase> Phases { 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">
|
<resheader name="writer">
|
||||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
</resheader>
|
</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>
|
</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);
|
engine.Process(document);
|
||||||
|
|
||||||
// Assert
|
// 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
|
// Act
|
||||||
var engine = RazorEngine.Create(builder =>
|
var engine = RazorEngine.Create(builder =>
|
||||||
{
|
{
|
||||||
|
builder.Features.Clear();
|
||||||
|
builder.Phases.Clear();
|
||||||
|
|
||||||
builder.Features.Add(Mock.Of<IRazorEngineFeature>());
|
builder.Features.Add(Mock.Of<IRazorEngineFeature>());
|
||||||
builder.Features.Add(Mock.Of<IRazorEngineFeature>());
|
builder.Features.Add(Mock.Of<IRazorEngineFeature>());
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue