Implement a simple base for document classifiers
This should allow us to de-dupe a lot of code in MVC.
This commit is contained in:
parent
eaadfb70eb
commit
ea778b9b6d
|
|
@ -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.
|
||||
|
||||
using Microsoft.AspNetCore.Razor.Evolution.Intermediate;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Evolution
|
||||
{
|
||||
internal class DefaultDocumentClassifierPass : DocumentClassifierPassBase
|
||||
{
|
||||
public override int Order => RazorIRPass.DefaultDocumentClassifierOrder;
|
||||
|
||||
protected override string DocumentKind => "default";
|
||||
|
||||
protected override bool IsMatch(RazorCodeDocument codeDocument, DocumentIRNode irDocument)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -6,22 +6,35 @@ using Microsoft.AspNetCore.Razor.Evolution.Intermediate;
|
|||
|
||||
namespace Microsoft.AspNetCore.Razor.Evolution
|
||||
{
|
||||
internal class DefaultDocumentClassifier : RazorIRPassBase
|
||||
public abstract class DocumentClassifierPassBase : RazorIRPassBase
|
||||
{
|
||||
public override int Order => RazorIRPass.DefaultDocumentClassifierOrder;
|
||||
protected abstract string DocumentKind { get; }
|
||||
|
||||
public static string DocumentKind = "default";
|
||||
public override int Order => RazorIRPass.DocumentClassifierOrder;
|
||||
|
||||
public override DocumentIRNode ExecuteCore(RazorCodeDocument codeDocument, DocumentIRNode irDocument)
|
||||
public sealed override DocumentIRNode ExecuteCore(RazorCodeDocument codeDocument, DocumentIRNode irDocument)
|
||||
{
|
||||
if (irDocument.DocumentKind != null)
|
||||
{
|
||||
return irDocument;
|
||||
}
|
||||
|
||||
if (!IsMatch(codeDocument, irDocument))
|
||||
{
|
||||
return irDocument;
|
||||
}
|
||||
|
||||
irDocument.DocumentKind = DocumentKind;
|
||||
|
||||
// Rewrite a use default namespace and class declaration.
|
||||
Rewrite(codeDocument, irDocument);
|
||||
|
||||
return irDocument;
|
||||
}
|
||||
|
||||
private void Rewrite(RazorCodeDocument codeDocument, DocumentIRNode irDocument)
|
||||
{
|
||||
// Rewrite the document from a flat structure to use a sensible default structure,
|
||||
// a namespace and class declaration with a single 'razor' method.
|
||||
var children = new List<RazorIRNode>(irDocument.Children);
|
||||
irDocument.Children.Clear();
|
||||
|
||||
|
|
@ -39,7 +52,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
var method = new RazorMethodDeclarationIRNode()
|
||||
{
|
||||
//AccessModifier = "public",
|
||||
// Modifiers = new List<string>() { "async" },
|
||||
// Modifiers = new List<string>() { "async" },
|
||||
//Name = "Execute",
|
||||
//ReturnType = "Task",
|
||||
};
|
||||
|
|
@ -62,7 +75,20 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
visitor.Visit(children[i]);
|
||||
}
|
||||
|
||||
return irDocument;
|
||||
// Note that this is called at the *end* of rewriting so that user code can see the tree
|
||||
// and look at its content to make a decision.
|
||||
OnDocumentStructureCreated(codeDocument, @namespace, @class, method);
|
||||
}
|
||||
|
||||
protected abstract bool IsMatch(RazorCodeDocument codeDocument, DocumentIRNode irDocument);
|
||||
|
||||
protected virtual void OnDocumentStructureCreated(
|
||||
RazorCodeDocument codeDocument,
|
||||
NamespaceDeclarationIRNode @namespace,
|
||||
ClassDeclarationIRNode @class,
|
||||
RazorMethodDeclarationIRNode @method)
|
||||
{
|
||||
// Intentionally empty.
|
||||
}
|
||||
|
||||
private class Visitor : RazorIRNodeVisitor
|
||||
|
|
@ -84,7 +110,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
{
|
||||
_document.Insert(0, node);
|
||||
}
|
||||
|
||||
|
||||
public override void VisitUsingStatement(UsingStatementIRNode node)
|
||||
{
|
||||
_namespace.AddAfter<UsingStatementIRNode>(node);
|
||||
|
|
@ -59,7 +59,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
builder.Features.Add(new TagHelperBinderSyntaxTreePass());
|
||||
|
||||
// IR Passes
|
||||
builder.Features.Add(new DefaultDocumentClassifier());
|
||||
builder.Features.Add(new DefaultDocumentClassifierPass());
|
||||
builder.Features.Add(new DefaultDirectiveIRPass());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -226,7 +226,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
Assert.NotNull(irDocument);
|
||||
|
||||
// These tests depend on the document->namespace->class structure.
|
||||
irDocument = new DefaultDocumentClassifier() { Engine = engine, }.Execute(codeDocument, irDocument);
|
||||
irDocument = new DefaultDocumentClassifierPass() { Engine = engine, }.Execute(codeDocument, irDocument);
|
||||
return irDocument;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,56 @@
|
|||
// 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.Evolution.Intermediate;
|
||||
using Xunit;
|
||||
using static Microsoft.AspNetCore.Razor.Evolution.Intermediate.RazorIRAssert;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Evolution
|
||||
{
|
||||
// We're purposely lean on tests here because the functionality is well covered by
|
||||
// integration tests, and is mostly implemented by the base class.
|
||||
public class DefaultDocumentClassifierPassTest
|
||||
{
|
||||
[Fact]
|
||||
public void Execute_IgnoresDocumentsWithDocumentKind()
|
||||
{
|
||||
// Arrange
|
||||
var irDocument = new DocumentIRNode()
|
||||
{
|
||||
DocumentKind = "ignore",
|
||||
};
|
||||
|
||||
var pass = new DefaultDocumentClassifierPass();
|
||||
pass.Engine = RazorEngine.CreateEmpty(b => { });
|
||||
|
||||
// Act
|
||||
pass.Execute(TestRazorCodeDocument.CreateEmpty(), irDocument);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("ignore", irDocument.DocumentKind);
|
||||
NoChildren(irDocument);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_CreatesClassStructure()
|
||||
{
|
||||
// Arrange
|
||||
var irDocument = new DocumentIRNode();
|
||||
|
||||
var pass = new DefaultDocumentClassifierPass();
|
||||
pass.Engine = RazorEngine.CreateEmpty(b =>{ });
|
||||
|
||||
// Act
|
||||
pass.Execute(TestRazorCodeDocument.CreateEmpty(), irDocument);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("default", irDocument.DocumentKind);
|
||||
|
||||
var @namespace = SingleChild<NamespaceDeclarationIRNode>(irDocument);
|
||||
var @class = SingleChild<ClassDeclarationIRNode>(@namespace);
|
||||
var method = SingleChild<RazorMethodDeclarationIRNode>(@class);
|
||||
NoChildren(method);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,16 +2,17 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Razor.Evolution.Intermediate;
|
||||
using Xunit;
|
||||
using static Microsoft.AspNetCore.Razor.Evolution.Intermediate.RazorIRAssert;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Evolution
|
||||
{
|
||||
public class DefaultDocumentClassifierTest
|
||||
public class DocumentClassifierPassBaseTest
|
||||
{
|
||||
[Fact]
|
||||
public void Execute_IgnoresDocumentsWithDocumentKind()
|
||||
public void Execute_HasDocumentKind_IgnoresDocument()
|
||||
{
|
||||
// Arrange
|
||||
var irDocument = new DocumentIRNode()
|
||||
|
|
@ -19,7 +20,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
DocumentKind = "ignore",
|
||||
};
|
||||
|
||||
var pass = new DefaultDocumentClassifier();
|
||||
var pass = new TestDocumentClassifierPass();
|
||||
pass.Engine = RazorEngine.CreateEmpty(b => { });
|
||||
|
||||
// Act
|
||||
|
|
@ -31,19 +32,39 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_CreatesClassStructure()
|
||||
public void Execute_NoMatch_IgnoresDocument()
|
||||
{
|
||||
// Arrange
|
||||
var irDocument = new DocumentIRNode();
|
||||
|
||||
var pass = new DefaultDocumentClassifier();
|
||||
pass.Engine = RazorEngine.CreateEmpty(b =>{ });
|
||||
var pass = new TestDocumentClassifierPass()
|
||||
{
|
||||
Engine = RazorEngine.CreateEmpty(b => { }),
|
||||
ShouldMatch = false,
|
||||
};
|
||||
|
||||
// Act
|
||||
pass.Execute(TestRazorCodeDocument.CreateEmpty(), irDocument);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(DefaultDocumentClassifier.DocumentKind, irDocument.DocumentKind);
|
||||
Assert.Null(irDocument.DocumentKind);
|
||||
NoChildren(irDocument);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_Match_SetsDocumentType_AndCreatesStructure()
|
||||
{
|
||||
// Arrange
|
||||
var irDocument = new DocumentIRNode();
|
||||
|
||||
var pass = new TestDocumentClassifierPass();
|
||||
pass.Engine = RazorEngine.CreateEmpty(b => { });
|
||||
|
||||
// Act
|
||||
pass.Execute(TestRazorCodeDocument.CreateEmpty(), irDocument);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("test", irDocument.DocumentKind);
|
||||
|
||||
var @namespace = SingleChild<NamespaceDeclarationIRNode>(irDocument);
|
||||
var @class = SingleChild<ClassDeclarationIRNode>(@namespace);
|
||||
|
|
@ -60,7 +81,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
var builder = RazorIRBuilder.Create(irDocument);
|
||||
builder.Add(new ChecksumIRNode());
|
||||
|
||||
var pass = new DefaultDocumentClassifier();
|
||||
var pass = new TestDocumentClassifierPass();
|
||||
pass.Engine = RazorEngine.CreateEmpty(b => { });
|
||||
|
||||
// Act
|
||||
|
|
@ -82,7 +103,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
var builder = RazorIRBuilder.Create(irDocument);
|
||||
builder.Add(new UsingStatementIRNode());
|
||||
|
||||
var pass = new DefaultDocumentClassifier();
|
||||
var pass = new TestDocumentClassifierPass();
|
||||
pass.Engine = RazorEngine.CreateEmpty(b => { });
|
||||
|
||||
// Act
|
||||
|
|
@ -105,7 +126,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
var builder = RazorIRBuilder.Create(irDocument);
|
||||
builder.Add(new DeclareTagHelperFieldsIRNode());
|
||||
|
||||
var pass = new DefaultDocumentClassifier();
|
||||
var pass = new TestDocumentClassifierPass();
|
||||
pass.Engine = RazorEngine.CreateEmpty(b => { });
|
||||
|
||||
// Act
|
||||
|
|
@ -130,7 +151,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
builder.Add(new HtmlContentIRNode());
|
||||
builder.Add(new CSharpStatementIRNode());
|
||||
|
||||
var pass = new DefaultDocumentClassifier();
|
||||
var pass = new TestDocumentClassifierPass();
|
||||
pass.Engine = RazorEngine.CreateEmpty(b => { });
|
||||
|
||||
// Act
|
||||
|
|
@ -145,5 +166,66 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
n => Assert.IsType<HtmlContentIRNode>(n),
|
||||
n => Assert.IsType<CSharpStatementIRNode>(n));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_CanInitializeDefaults()
|
||||
{
|
||||
// Arrange
|
||||
var irDocument = new DocumentIRNode();
|
||||
|
||||
var builder = RazorIRBuilder.Create(irDocument);
|
||||
builder.Add(new HtmlContentIRNode());
|
||||
builder.Add(new CSharpStatementIRNode());
|
||||
|
||||
var pass = new TestDocumentClassifierPass()
|
||||
{
|
||||
Engine = RazorEngine.CreateEmpty(b => { }),
|
||||
Namespace = "TestNamespace",
|
||||
Class = "TestClass",
|
||||
Method = "TestMethod",
|
||||
};
|
||||
|
||||
// Act
|
||||
pass.Execute(TestRazorCodeDocument.CreateEmpty(), irDocument);
|
||||
|
||||
// Assert
|
||||
var @namespace = SingleChild<NamespaceDeclarationIRNode>(irDocument);
|
||||
Assert.Equal("TestNamespace", @namespace.Content);
|
||||
|
||||
var @class = SingleChild<ClassDeclarationIRNode>(@namespace);
|
||||
Assert.Equal("TestClass", @class.Name);
|
||||
|
||||
var method = SingleChild<RazorMethodDeclarationIRNode>(@class);
|
||||
Assert.Equal("TestMethod", method.Name);
|
||||
}
|
||||
|
||||
private class TestDocumentClassifierPass : DocumentClassifierPassBase
|
||||
{
|
||||
public bool ShouldMatch { get; set; } = true;
|
||||
|
||||
public string Namespace { get; set; }
|
||||
|
||||
public string Class { get; set; }
|
||||
|
||||
public string Method { get; set; }
|
||||
|
||||
protected override string DocumentKind => "test";
|
||||
|
||||
protected override bool IsMatch(RazorCodeDocument codeDocument, DocumentIRNode irDocument)
|
||||
{
|
||||
return ShouldMatch;
|
||||
}
|
||||
|
||||
protected override void OnDocumentStructureCreated(
|
||||
RazorCodeDocument codeDocument,
|
||||
NamespaceDeclarationIRNode @namespace,
|
||||
ClassDeclarationIRNode @class,
|
||||
RazorMethodDeclarationIRNode method)
|
||||
{
|
||||
@namespace.Content = Namespace;
|
||||
@class.Name = Class;
|
||||
@method.Name = Method;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -139,7 +139,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
feature => Assert.IsType<DefaultDirectiveSyntaxTreePass>(feature),
|
||||
feature => Assert.IsType<HtmlNodeOptimizationPass>(feature),
|
||||
feature => Assert.IsType<TagHelperBinderSyntaxTreePass>(feature),
|
||||
feature => Assert.IsType<DefaultDocumentClassifier>(feature),
|
||||
feature => Assert.IsType<DefaultDocumentClassifierPass>(feature),
|
||||
feature => Assert.IsType<DefaultDirectiveIRPass>(feature),
|
||||
feature => Assert.IsType<RazorPreallocatedTagHelperAttributeOptimizationPass>(feature));
|
||||
}
|
||||
|
|
@ -162,7 +162,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
feature => Assert.IsType<DefaultDirectiveSyntaxTreePass>(feature),
|
||||
feature => Assert.IsType<HtmlNodeOptimizationPass>(feature),
|
||||
feature => Assert.IsType<TagHelperBinderSyntaxTreePass>(feature),
|
||||
feature => Assert.IsType<DefaultDocumentClassifier>(feature),
|
||||
feature => Assert.IsType<DefaultDocumentClassifierPass>(feature),
|
||||
feature => Assert.IsType<DefaultDirectiveIRPass>(feature),
|
||||
feature => Assert.IsType<RazorEngine.ConfigureDesignTimeOptions>(feature),
|
||||
feature => Assert.IsType<RazorDesignTimeIRPass>(feature));
|
||||
|
|
|
|||
Loading…
Reference in New Issue