Part 2 of RuntimeTarget
Introducing ExtensionIRNode and an implementation of templates based on the new feature set. Now TemplateIRNode is-a ExtensionIRNode. It's implemented using just extensibility and isn't part of the standard razor codegen. I'm adding it to the RazorEngine so that it's still there by default. I've also included a pattern for visitors to special case ExtensionIRNode-derived classes that they know about. This requires a little bit of boilerplate but makes it easy to traverse just the nodes you care about while keeping the set of nodes open. For now the general codegen feature still hasn't had a refactor, but this opens things up for us to start finishing things like MVC's @inject directive.
This commit is contained in:
parent
4b2245eeb9
commit
7a1a6dd1d6
|
|
@ -7,22 +7,22 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration
|
|||
{
|
||||
internal class CSharpRedirectRenderingConventions : CSharpRenderingConventions
|
||||
{
|
||||
private readonly string _redirectWriter;
|
||||
|
||||
public CSharpRedirectRenderingConventions(string redirectWriter, CSharpCodeWriter writer)
|
||||
: base(writer)
|
||||
{
|
||||
_redirectWriter = redirectWriter;
|
||||
RedirectWriter = redirectWriter;
|
||||
}
|
||||
|
||||
public override string StartWriteMethod => "WriteTo(" + _redirectWriter + ", " /* ORIGINAL: WriteToMethodName */;
|
||||
public string RedirectWriter { get; }
|
||||
|
||||
public override string StartWriteLiteralMethod => "WriteLiteralTo(" + _redirectWriter + ", " /* ORIGINAL: WriteLiteralToMethodName */;
|
||||
public override string StartWriteMethod => "WriteTo(" + RedirectWriter + ", " /* ORIGINAL: WriteToMethodName */;
|
||||
|
||||
public override string StartBeginWriteAttributeMethod => "BeginWriteAttributeTo(" + _redirectWriter + ", " /* ORIGINAL: BeginWriteAttributeToMethodName */;
|
||||
public override string StartWriteLiteralMethod => "WriteLiteralTo(" + RedirectWriter + ", " /* ORIGINAL: WriteLiteralToMethodName */;
|
||||
|
||||
public override string StartWriteAttributeValueMethod => "WriteAttributeValueTo(" + _redirectWriter + ", " /* ORIGINAL: WriteAttributeValueToMethodName */;
|
||||
public override string StartBeginWriteAttributeMethod => "BeginWriteAttributeTo(" + RedirectWriter + ", " /* ORIGINAL: BeginWriteAttributeToMethodName */;
|
||||
|
||||
public override string StartEndWriteAttributeMethod => "EndWriteAttributeTo(" + _redirectWriter /* ORIGINAL: EndWriteAttributeToMethodName */;
|
||||
public override string StartWriteAttributeValueMethod => "WriteAttributeValueTo(" + RedirectWriter + ", " /* ORIGINAL: WriteAttributeValueToMethodName */;
|
||||
|
||||
public override string StartEndWriteAttributeMethod => "EndWriteAttributeTo(" + RedirectWriter /* ORIGINAL: EndWriteAttributeToMethodName */;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Razor.Evolution.Legacy;
|
||||
using Microsoft.AspNetCore.Razor.Evolution.Intermediate;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration
|
||||
{
|
||||
|
|
@ -43,5 +44,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration
|
|||
public RazorParserOptions Options { get; set; }
|
||||
|
||||
public TagHelperRenderingContext TagHelperRenderingContext { get; set; }
|
||||
|
||||
public Action<RazorIRNode> RenderChildren { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
// 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;
|
||||
using System.Linq;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration
|
||||
{
|
||||
|
|
@ -9,31 +10,52 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration
|
|||
{
|
||||
private readonly RazorParserOptions _options;
|
||||
|
||||
public DefaultRuntimeTarget(RazorParserOptions options)
|
||||
public DefaultRuntimeTarget(RazorParserOptions options, IEnumerable<IRuntimeTargetExtension> extensions)
|
||||
{
|
||||
_options = options;
|
||||
Extensions = extensions.ToArray();
|
||||
}
|
||||
|
||||
public IRuntimeTargetExtension[] Extensions { get; }
|
||||
|
||||
internal override PageStructureCSharpRenderer CreateRenderer(CSharpRenderingContext context)
|
||||
{
|
||||
if (_options.DesignTimeMode)
|
||||
{
|
||||
return new DesignTimeCSharpRenderer(context);
|
||||
return new DesignTimeCSharpRenderer(this, context);
|
||||
}
|
||||
else
|
||||
{
|
||||
return new RuntimeCSharpRenderer(context);
|
||||
return new RuntimeCSharpRenderer(this, context);
|
||||
}
|
||||
}
|
||||
|
||||
public override TExtension GetExtension<TExtension>()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
for (var i = 0; i < Extensions.Length; i++)
|
||||
{
|
||||
var match = Extensions[i] as TExtension;
|
||||
if (match != null)
|
||||
{
|
||||
return match;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public override bool HasExtension<TExtension>()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
for (var i = 0; i < Extensions.Length; i++)
|
||||
{
|
||||
var match = Extensions[i] as TExtension;
|
||||
if (match != null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
// 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.CodeGeneration
|
||||
{
|
||||
|
|
@ -11,15 +12,19 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration
|
|||
{
|
||||
CodeDocument = codeDocument;
|
||||
Options = options;
|
||||
|
||||
TargetExtensions = new List<IRuntimeTargetExtension>();
|
||||
}
|
||||
|
||||
public RazorCodeDocument CodeDocument { get; }
|
||||
|
||||
public RazorParserOptions Options { get; }
|
||||
|
||||
public ICollection<IRuntimeTargetExtension> TargetExtensions { get; }
|
||||
|
||||
public RuntimeTarget Build()
|
||||
{
|
||||
return new DefaultRuntimeTarget(Options);
|
||||
return new DefaultRuntimeTarget(Options, TargetExtensions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,8 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration
|
|||
{
|
||||
internal class DesignTimeCSharpRenderer : PageStructureCSharpRenderer
|
||||
{
|
||||
public DesignTimeCSharpRenderer(CSharpRenderingContext context) : base(context)
|
||||
public DesignTimeCSharpRenderer(RuntimeTarget target, CSharpRenderingContext context)
|
||||
: base(target, context)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -150,27 +151,6 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration
|
|||
Context.Writer.WriteLine("))();");
|
||||
}
|
||||
|
||||
public override void VisitTemplate(TemplateIRNode node)
|
||||
{
|
||||
const string ItemParameterName = "item";
|
||||
const string TemplateWriterName = "__razor_template_writer";
|
||||
|
||||
Context.Writer
|
||||
.Write(ItemParameterName).Write(" => ")
|
||||
.WriteStartNewObject("Microsoft.AspNetCore.Mvc.Razor.HelperResult" /* ORIGINAL: TemplateTypeName */);
|
||||
|
||||
var initialRenderingConventions = Context.RenderingConventions;
|
||||
var redirectConventions = new CSharpRedirectRenderingConventions(TemplateWriterName, Context.Writer);
|
||||
Context.RenderingConventions = redirectConventions;
|
||||
using (Context.Writer.BuildAsyncLambda(endLine: false, parameterNames: TemplateWriterName))
|
||||
{
|
||||
VisitDefault(node);
|
||||
}
|
||||
Context.RenderingConventions = initialRenderingConventions;
|
||||
|
||||
Context.Writer.WriteEndMethodInvocation(endLine: false);
|
||||
}
|
||||
|
||||
public override void VisitTagHelper(TagHelperIRNode node)
|
||||
{
|
||||
var initialTagHelperRenderingContext = Context.TagHelperRenderingContext;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
// 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.Collections.Generic;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration
|
||||
{
|
||||
public interface IRuntimeTargetBuilder
|
||||
|
|
@ -9,6 +11,8 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration
|
|||
|
||||
RazorParserOptions Options { get; }
|
||||
|
||||
ICollection<IRuntimeTargetExtension> TargetExtensions { get; }
|
||||
|
||||
RuntimeTarget Build();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
// 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.CodeGeneration
|
||||
{
|
||||
internal interface ITemplateTargetExtension : IRuntimeTargetExtension
|
||||
{
|
||||
void WriteTemplate(CSharpRenderingContext context, TemplateIRNode node);
|
||||
}
|
||||
}
|
||||
|
|
@ -9,10 +9,12 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration
|
|||
internal class PageStructureCSharpRenderer : RazorIRNodeWalker
|
||||
{
|
||||
protected readonly CSharpRenderingContext Context;
|
||||
protected readonly RuntimeTarget Target;
|
||||
|
||||
public PageStructureCSharpRenderer(CSharpRenderingContext context)
|
||||
public PageStructureCSharpRenderer(RuntimeTarget target, CSharpRenderingContext context)
|
||||
{
|
||||
Context = context;
|
||||
Target = target;
|
||||
}
|
||||
|
||||
public override void VisitNamespace(NamespaceDeclarationIRNode node)
|
||||
|
|
@ -107,6 +109,11 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration
|
|||
}
|
||||
}
|
||||
|
||||
public override void VisitExtension(ExtensionIRNode node)
|
||||
{
|
||||
node.WriteNode(Target, Context);
|
||||
}
|
||||
|
||||
protected static void RenderExpressionInline(RazorIRNode node, CSharpRenderingContext context)
|
||||
{
|
||||
if (node is CSharpTokenIRNode)
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration
|
|||
{
|
||||
internal class RuntimeCSharpRenderer : PageStructureCSharpRenderer
|
||||
{
|
||||
public RuntimeCSharpRenderer(CSharpRenderingContext context)
|
||||
: base(context)
|
||||
public RuntimeCSharpRenderer(RuntimeTarget target, CSharpRenderingContext context)
|
||||
: base(target, context)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -213,26 +213,6 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration
|
|||
}
|
||||
}
|
||||
|
||||
public override void VisitTemplate(TemplateIRNode node)
|
||||
{
|
||||
const string ItemParameterName = "item";
|
||||
const string TemplateWriterName = "__razor_template_writer";
|
||||
|
||||
Context.Writer
|
||||
.Write(ItemParameterName).Write(" => ")
|
||||
.WriteStartNewObject("Microsoft.AspNetCore.Mvc.Razor.HelperResult" /* ORIGINAL: TemplateTypeName */);
|
||||
|
||||
var initialRenderingConventions = Context.RenderingConventions;
|
||||
Context.RenderingConventions = new CSharpRedirectRenderingConventions(TemplateWriterName, Context.Writer);
|
||||
using (Context.Writer.BuildAsyncLambda(endLine: false, parameterNames: TemplateWriterName))
|
||||
{
|
||||
VisitDefault(node);
|
||||
}
|
||||
Context.RenderingConventions = initialRenderingConventions;
|
||||
|
||||
Context.Writer.WriteEndMethodInvocation(endLine: false);
|
||||
}
|
||||
|
||||
public override void VisitTagHelper(TagHelperIRNode node)
|
||||
{
|
||||
var initialTagHelperRenderingContext = Context.TagHelperRenderingContext;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,34 @@
|
|||
// 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.CodeGeneration
|
||||
{
|
||||
internal class TemplateTargetExtension : ITemplateTargetExtension
|
||||
{
|
||||
public static readonly string DefaultTemplateTypeName = "Microsoft.AspNetCore.Mvc.Razor.HelperResult";
|
||||
|
||||
public string TemplateTypeName { get; set; } = DefaultTemplateTypeName;
|
||||
|
||||
public void WriteTemplate(CSharpRenderingContext context, TemplateIRNode node)
|
||||
{
|
||||
const string ItemParameterName = "item";
|
||||
const string TemplateWriterName = "__razor_template_writer";
|
||||
|
||||
context.Writer
|
||||
.Write(ItemParameterName).Write(" => ")
|
||||
.WriteStartNewObject(TemplateTypeName);
|
||||
|
||||
var initialRenderingConventions = context.RenderingConventions;
|
||||
context.RenderingConventions = new CSharpRedirectRenderingConventions(TemplateWriterName, context.Writer);
|
||||
using (context.Writer.BuildAsyncLambda(endLine: false, parameterNames: TemplateWriterName))
|
||||
{
|
||||
context.RenderChildren(node);
|
||||
}
|
||||
context.RenderingConventions = initialRenderingConventions;
|
||||
|
||||
context.Writer.WriteEndMethodInvocation(endLine: false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -56,6 +56,8 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
}
|
||||
|
||||
var renderer = target.CreateRenderer(renderingContext);
|
||||
renderingContext.RenderChildren = renderer.VisitDefault;
|
||||
|
||||
renderer.VisitDocument(irDocument);
|
||||
|
||||
var combinedErrors = syntaxTree.Diagnostics.Concat(renderingContext.ErrorSink.Errors).ToList();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
// 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.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Razor.Evolution.CodeGeneration;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Evolution
|
||||
{
|
||||
internal class DefaultRazorTargetExtensionFeature : IRazorTargetExtensionFeature
|
||||
{
|
||||
public RazorEngine Engine { get; set; }
|
||||
|
||||
public ICollection<IRuntimeTargetExtension> TargetExtensions { get; } = new List<IRuntimeTargetExtension>();
|
||||
}
|
||||
}
|
||||
|
|
@ -2,15 +2,27 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Razor.Evolution.Intermediate;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Razor.Evolution.CodeGeneration;
|
||||
using Microsoft.AspNetCore.Razor.Evolution.Intermediate;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Evolution
|
||||
{
|
||||
public abstract class DocumentClassifierPassBase : RazorIRPassBase, IRazorDocumentClassifierPass
|
||||
{
|
||||
private static readonly IRuntimeTargetExtension[] EmptyExtensionArray = new IRuntimeTargetExtension[0];
|
||||
|
||||
protected abstract string DocumentKind { get; }
|
||||
|
||||
protected IRuntimeTargetExtension[] TargetExtensions { get; private set; }
|
||||
|
||||
protected override void OnIntialized()
|
||||
{
|
||||
var feature = Engine.Features.OfType<IRazorTargetExtensionFeature>();
|
||||
|
||||
TargetExtensions = feature.FirstOrDefault()?.TargetExtensions.ToArray() ?? EmptyExtensionArray;
|
||||
}
|
||||
|
||||
public sealed override void ExecuteCore(RazorCodeDocument codeDocument, DocumentIRNode irDocument)
|
||||
{
|
||||
if (irDocument.DocumentKind != null)
|
||||
|
|
@ -82,10 +94,18 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
|
||||
private RuntimeTarget CreateTarget(RazorCodeDocument codeDocument, RazorParserOptions options)
|
||||
{
|
||||
return RuntimeTarget.CreateDefault(codeDocument, options, (builder) => ConfigureTarget(codeDocument, builder));
|
||||
return RuntimeTarget.CreateDefault(codeDocument, options, (builder) =>
|
||||
{
|
||||
for (var i = 0; i < TargetExtensions.Length; i++)
|
||||
{
|
||||
builder.TargetExtensions.Add(TargetExtensions[i]);
|
||||
}
|
||||
|
||||
ConfigureTarget(builder);
|
||||
});
|
||||
}
|
||||
|
||||
protected virtual void ConfigureTarget(RazorCodeDocument codeDocument, IRuntimeTargetBuilder builder)
|
||||
protected virtual void ConfigureTarget(IRuntimeTargetBuilder builder)
|
||||
{
|
||||
// Intentionally empty.
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Razor.Evolution.CodeGeneration;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Evolution
|
||||
{
|
||||
public interface IRazorTargetExtensionFeature : IRazorEngineFeature
|
||||
{
|
||||
ICollection<IRuntimeTargetExtension> TargetExtensions { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
// 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.CodeGeneration;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate
|
||||
{
|
||||
public abstract class ExtensionIRNode : RazorIRNode
|
||||
{
|
||||
internal abstract void WriteNode(RuntimeTarget target, CSharpRenderingContext context);
|
||||
|
||||
protected static void AcceptExtensionNode<TNode>(TNode node, RazorIRNodeVisitor visitor)
|
||||
where TNode : ExtensionIRNode
|
||||
{
|
||||
var typedVisitor = visitor as IExtensionIRNodeVisitor<TNode>;
|
||||
if (typedVisitor == null)
|
||||
{
|
||||
visitor.VisitExtension(node);
|
||||
}
|
||||
else
|
||||
{
|
||||
typedVisitor.VisitExtension(node);
|
||||
}
|
||||
}
|
||||
|
||||
protected static TResult AcceptExtensionNode<TNode, TResult>(TNode node, RazorIRNodeVisitor<TResult> visitor)
|
||||
where TNode : ExtensionIRNode
|
||||
{
|
||||
var typedVisitor = visitor as IExtensionIRNodeVisitor<TNode, TResult>;
|
||||
if (typedVisitor == null)
|
||||
{
|
||||
return visitor.VisitExtension(node);
|
||||
}
|
||||
else
|
||||
{
|
||||
return typedVisitor.VisitExtension(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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.Intermediate
|
||||
{
|
||||
public interface IExtensionIRNodeVisitor<TNode> where TNode : ExtensionIRNode
|
||||
{
|
||||
void VisitExtension(TNode node);
|
||||
}
|
||||
}
|
||||
|
|
@ -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.Intermediate
|
||||
{
|
||||
public interface IExtensionIRNodeVisitor<TNode, TResult> where TNode : ExtensionIRNode
|
||||
{
|
||||
TResult VisitExtension(TNode node);
|
||||
}
|
||||
}
|
||||
|
|
@ -29,7 +29,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate
|
|||
VisitDefault(node);
|
||||
}
|
||||
|
||||
public virtual void VisitTemplate(TemplateIRNode node)
|
||||
public virtual void VisitExtension(ExtensionIRNode node)
|
||||
{
|
||||
VisitDefault(node);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate
|
|||
return VisitDefault(node);
|
||||
}
|
||||
|
||||
public virtual TResult VisitTemplate(TemplateIRNode node)
|
||||
public virtual TResult VisitExtension(ExtensionIRNode node)
|
||||
{
|
||||
return VisitDefault(node);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,10 +3,11 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Razor.Evolution.CodeGeneration;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate
|
||||
{
|
||||
public class TemplateIRNode : RazorIRNode
|
||||
public class TemplateIRNode : ExtensionIRNode
|
||||
{
|
||||
public override IList<RazorIRNode> Children { get; } = new List<RazorIRNode>();
|
||||
|
||||
|
|
@ -21,7 +22,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate
|
|||
throw new ArgumentNullException(nameof(visitor));
|
||||
}
|
||||
|
||||
visitor.VisitTemplate(this);
|
||||
AcceptExtensionNode<TemplateIRNode>(this, visitor);
|
||||
}
|
||||
|
||||
public override TResult Accept<TResult>(RazorIRNodeVisitor<TResult> visitor)
|
||||
|
|
@ -31,7 +32,13 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate
|
|||
throw new ArgumentNullException(nameof(visitor));
|
||||
}
|
||||
|
||||
return visitor.VisitTemplate(this);
|
||||
return AcceptExtensionNode<TemplateIRNode, TResult>(this, visitor);
|
||||
}
|
||||
|
||||
internal override void WriteNode(RuntimeTarget target, CSharpRenderingContext context)
|
||||
{
|
||||
var extension = target.GetExtension<ITemplateTargetExtension>();
|
||||
extension.WriteTemplate(context, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Razor.Evolution.CodeGeneration;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Evolution
|
||||
{
|
||||
|
|
@ -56,6 +57,10 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
builder.Phases.Add(new DefaultRazorIROptimizationPhase());
|
||||
builder.Phases.Add(new DefaultRazorCSharpLoweringPhase());
|
||||
|
||||
// General extensibility
|
||||
builder.Features.Add(new DefaultRazorDirectiveFeature());
|
||||
builder.Features.Add(new DefaultRazorTargetExtensionFeature());
|
||||
|
||||
// Syntax Tree passes
|
||||
builder.Features.Add(new DefaultDirectiveSyntaxTreePass());
|
||||
builder.Features.Add(new HtmlNodeOptimizationPass());
|
||||
|
|
@ -65,6 +70,9 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
builder.Features.Add(new DefaultDocumentClassifierPass());
|
||||
builder.Features.Add(new DefaultDirectiveIRPass());
|
||||
builder.Features.Add(new DirectiveRemovalIROptimizationPass());
|
||||
|
||||
// Default Runtime Targets
|
||||
builder.AddTargetExtension(new TemplateTargetExtension());
|
||||
}
|
||||
|
||||
internal static void AddRuntimeDefaults(IRazorEngineBuilder builder)
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Razor.Evolution.CodeGeneration;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Evolution
|
||||
{
|
||||
|
|
@ -12,7 +13,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
{
|
||||
if (builder == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(directive));
|
||||
throw new ArgumentNullException(nameof(builder));
|
||||
}
|
||||
|
||||
if (directive == null)
|
||||
|
|
@ -26,6 +27,24 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
return builder;
|
||||
}
|
||||
|
||||
public static IRazorEngineBuilder AddTargetExtension(this IRazorEngineBuilder builder, IRuntimeTargetExtension extension)
|
||||
{
|
||||
if (builder == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(builder));
|
||||
}
|
||||
|
||||
if (extension == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(extension));
|
||||
}
|
||||
|
||||
var targetExtensionFeature = GetTargetExtensionFeature(builder);
|
||||
targetExtensionFeature.TargetExtensions.Add(extension);
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
private static IRazorDirectiveFeature GetDirectiveFeature(IRazorEngineBuilder builder)
|
||||
{
|
||||
var directiveFeature = builder.Features.OfType<IRazorDirectiveFeature>().FirstOrDefault();
|
||||
|
|
@ -37,5 +56,17 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
|
||||
return directiveFeature;
|
||||
}
|
||||
|
||||
private static IRazorTargetExtensionFeature GetTargetExtensionFeature(IRazorEngineBuilder builder)
|
||||
{
|
||||
var targetExtensionFeature = builder.Features.OfType<IRazorTargetExtensionFeature>().FirstOrDefault();
|
||||
if (targetExtensionFeature == null)
|
||||
{
|
||||
targetExtensionFeature = new DefaultRazorTargetExtensionFeature();
|
||||
builder.Features.Add(targetExtensionFeature);
|
||||
}
|
||||
|
||||
return targetExtensionFeature;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,11 +16,31 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration
|
|||
|
||||
var builder = new DefaultRuntimeTargetBuilder(codeDocument, options);
|
||||
|
||||
var extensions = new IRuntimeTargetExtension[]
|
||||
{
|
||||
new MyExtension1(),
|
||||
new MyExtension2(),
|
||||
};
|
||||
|
||||
for (var i = 0; i < extensions.Length; i++)
|
||||
{
|
||||
builder.TargetExtensions.Add(extensions[i]);
|
||||
}
|
||||
|
||||
// Act
|
||||
var target = builder.Build();
|
||||
var result = builder.Build();
|
||||
|
||||
// Assert
|
||||
Assert.IsType<DefaultRuntimeTarget>(target);
|
||||
var target = Assert.IsType<DefaultRuntimeTarget>(result);
|
||||
Assert.Equal(extensions, target.Extensions);
|
||||
}
|
||||
|
||||
private class MyExtension1 : IRuntimeTargetExtension
|
||||
{
|
||||
}
|
||||
|
||||
private class MyExtension2 : IRuntimeTargetExtension
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,32 @@
|
|||
// 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;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration
|
||||
{
|
||||
public class DefaultRuntimeTargetTest
|
||||
{
|
||||
[Fact]
|
||||
public void Constructor_CreatesDefensiveCopy()
|
||||
{
|
||||
// Arrange
|
||||
var options = RazorParserOptions.CreateDefaultOptions();
|
||||
|
||||
var extensions = new IRuntimeTargetExtension[]
|
||||
{
|
||||
new MyExtension2(),
|
||||
new MyExtension1(),
|
||||
};
|
||||
|
||||
// Act
|
||||
var target = new DefaultRuntimeTarget(options, extensions);
|
||||
|
||||
// Assert
|
||||
Assert.NotSame(extensions, target);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CreateRenderer_DesignTime_CreatesDesignTimeRenderer()
|
||||
{
|
||||
|
|
@ -14,7 +34,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration
|
|||
var options = RazorParserOptions.CreateDefaultOptions();
|
||||
options.DesignTimeMode = true;
|
||||
|
||||
var target = new DefaultRuntimeTarget(options);
|
||||
var target = new DefaultRuntimeTarget(options, Enumerable.Empty<IRuntimeTargetExtension>());
|
||||
|
||||
// Act
|
||||
var renderer = target.CreateRenderer(new CSharpRenderingContext());
|
||||
|
|
@ -30,7 +50,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration
|
|||
var options = RazorParserOptions.CreateDefaultOptions();
|
||||
options.DesignTimeMode = false;
|
||||
|
||||
var target = new DefaultRuntimeTarget(options);
|
||||
var target = new DefaultRuntimeTarget(options, Enumerable.Empty<IRuntimeTargetExtension>());
|
||||
|
||||
// Act
|
||||
var renderer = target.CreateRenderer(new CSharpRenderingContext());
|
||||
|
|
@ -38,5 +58,121 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration
|
|||
// Assert
|
||||
Assert.IsType<RuntimeCSharpRenderer>(renderer);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasExtension_ReturnsTrue_WhenExtensionFound()
|
||||
{
|
||||
// Arrange
|
||||
var options = RazorParserOptions.CreateDefaultOptions();
|
||||
|
||||
var extensions = new IRuntimeTargetExtension[]
|
||||
{
|
||||
new MyExtension2(),
|
||||
new MyExtension1(),
|
||||
};
|
||||
|
||||
var target = new DefaultRuntimeTarget(options, extensions);
|
||||
|
||||
// Act
|
||||
var result = target.HasExtension<MyExtension1>();
|
||||
|
||||
// Assert
|
||||
Assert.True(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HasExtension_ReturnsFalse_WhenExtensionNotFound()
|
||||
{
|
||||
// Arrange
|
||||
var options = RazorParserOptions.CreateDefaultOptions();
|
||||
|
||||
var extensions = new IRuntimeTargetExtension[]
|
||||
{
|
||||
new MyExtension2(),
|
||||
new MyExtension2(),
|
||||
};
|
||||
|
||||
var target = new DefaultRuntimeTarget(options, extensions);
|
||||
|
||||
// Act
|
||||
var result = target.HasExtension<MyExtension1>();
|
||||
|
||||
// Assert
|
||||
Assert.False(result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetExtension_ReturnsExtension_WhenExtensionFound()
|
||||
{
|
||||
// Arrange
|
||||
var options = RazorParserOptions.CreateDefaultOptions();
|
||||
|
||||
var extensions = new IRuntimeTargetExtension[]
|
||||
{
|
||||
new MyExtension2(),
|
||||
new MyExtension1(),
|
||||
};
|
||||
|
||||
var target = new DefaultRuntimeTarget(options, extensions);
|
||||
|
||||
// Act
|
||||
var result = target.GetExtension<MyExtension1>();
|
||||
|
||||
// Assert
|
||||
Assert.Same(extensions[1], result);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetExtension_ReturnsFirstMatch_WhenExtensionFound()
|
||||
{
|
||||
// Arrange
|
||||
var options = RazorParserOptions.CreateDefaultOptions();
|
||||
|
||||
var extensions = new IRuntimeTargetExtension[]
|
||||
{
|
||||
new MyExtension2(),
|
||||
new MyExtension1(),
|
||||
new MyExtension2(),
|
||||
new MyExtension1(),
|
||||
};
|
||||
|
||||
var target = new DefaultRuntimeTarget(options, extensions);
|
||||
|
||||
// Act
|
||||
var result = target.GetExtension<MyExtension1>();
|
||||
|
||||
// Assert
|
||||
Assert.Same(extensions[1], result);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public void GetExtension_ReturnsNull_WhenExtensionNotFound()
|
||||
{
|
||||
// Arrange
|
||||
var options = RazorParserOptions.CreateDefaultOptions();
|
||||
|
||||
var extensions = new IRuntimeTargetExtension[]
|
||||
{
|
||||
new MyExtension2(),
|
||||
new MyExtension2(),
|
||||
};
|
||||
|
||||
var target = new DefaultRuntimeTarget(options, extensions);
|
||||
|
||||
// Act
|
||||
var result = target.GetExtension<MyExtension1>();
|
||||
|
||||
// Assert
|
||||
Assert.Null(result);
|
||||
}
|
||||
|
||||
private class MyExtension1 : IRuntimeTargetExtension
|
||||
{
|
||||
}
|
||||
|
||||
private class MyExtension2 : IRuntimeTargetExtension
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,51 @@
|
|||
// 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 Microsoft.AspNetCore.Razor.Evolution.Legacy;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration
|
||||
{
|
||||
public class TemplateTargetExtensionTest
|
||||
{
|
||||
[Fact]
|
||||
public void WriteTemplate_WritesTemplateCode()
|
||||
{
|
||||
// Arrange
|
||||
var node = new TemplateIRNode();
|
||||
|
||||
var extension = new TemplateTargetExtension()
|
||||
{
|
||||
TemplateTypeName = "global::TestTemplate",
|
||||
};
|
||||
|
||||
var context = new CSharpRenderingContext()
|
||||
{
|
||||
Writer = new CSharpCodeWriter(),
|
||||
};
|
||||
|
||||
context.RenderChildren = (n) =>
|
||||
{
|
||||
Assert.Same(node, n);
|
||||
|
||||
var conventions = Assert.IsType<CSharpRedirectRenderingConventions>(context.RenderingConventions);
|
||||
Assert.Equal("__razor_template_writer", conventions.RedirectWriter);
|
||||
|
||||
context.Writer.Write(" var s = \"Inside\"");
|
||||
};
|
||||
|
||||
// Act
|
||||
extension.WriteTemplate(context, node);
|
||||
|
||||
// Assert
|
||||
var expected = @"item => new global::TestTemplate(async(__razor_template_writer) => {
|
||||
var s = ""Inside""
|
||||
}
|
||||
)";
|
||||
|
||||
var output = context.Writer.Builder.ToString();
|
||||
Assert.Equal(expected, output);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Razor.Evolution.CodeGeneration;
|
||||
using Microsoft.AspNetCore.Razor.Evolution.Intermediate;
|
||||
using Xunit;
|
||||
using static Microsoft.AspNetCore.Razor.Evolution.Intermediate.RazorIRAssert;
|
||||
|
|
@ -55,6 +57,41 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
NoChildren(irDocument);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_Match_AddsGlobalTargetExtensions()
|
||||
{
|
||||
// Arrange
|
||||
var irDocument = new DocumentIRNode()
|
||||
{
|
||||
Options = RazorParserOptions.CreateDefaultOptions(),
|
||||
};
|
||||
|
||||
var expected = new IRuntimeTargetExtension[]
|
||||
{
|
||||
new MyExtension1(),
|
||||
new MyExtension2(),
|
||||
};
|
||||
|
||||
var pass = new TestDocumentClassifierPass();
|
||||
pass.Engine = RazorEngine.CreateEmpty(b =>
|
||||
{
|
||||
for (var i = 0; i < expected.Length; i++)
|
||||
{
|
||||
b.AddTargetExtension(expected[i]);
|
||||
}
|
||||
});
|
||||
|
||||
IRuntimeTargetExtension[] extensions = null;
|
||||
|
||||
pass.RuntimeTargetCallback = (builder) => extensions = builder.TargetExtensions.ToArray();
|
||||
|
||||
// Act
|
||||
pass.Execute(TestRazorCodeDocument.CreateEmpty(), irDocument);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expected, extensions);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_Match_SetsDocumentType_AndCreatesStructure()
|
||||
{
|
||||
|
|
@ -228,6 +265,8 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
|
||||
public bool ShouldMatch { get; set; } = true;
|
||||
|
||||
public Action<IRuntimeTargetBuilder> RuntimeTargetCallback { get; set; }
|
||||
|
||||
public string Namespace { get; set; }
|
||||
|
||||
public string Class { get; set; }
|
||||
|
|
@ -251,6 +290,19 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
@class.Name = Class;
|
||||
@method.Name = Method;
|
||||
}
|
||||
|
||||
protected override void ConfigureTarget(IRuntimeTargetBuilder builder)
|
||||
{
|
||||
RuntimeTargetCallback?.Invoke(builder);
|
||||
}
|
||||
}
|
||||
|
||||
private class MyExtension1 : IRuntimeTargetExtension
|
||||
{
|
||||
}
|
||||
|
||||
private class MyExtension2 : IRuntimeTargetExtension
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,168 @@
|
|||
// 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;
|
||||
using Microsoft.AspNetCore.Razor.Evolution.CodeGeneration;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate
|
||||
{
|
||||
// These tests cover the methods on ExtensionIRNode that are used to implement visitors
|
||||
// that special case an extension node.
|
||||
public class ExtensionIRNodeTest
|
||||
{
|
||||
[Fact]
|
||||
public void Accept_CallsStandardVisitExtension_ForStandardVisitor()
|
||||
{
|
||||
// Arrange
|
||||
var node = new TestExtensionIRNode();
|
||||
var visitor = new StandardVisitor();
|
||||
|
||||
// Act
|
||||
node.Accept(visitor);
|
||||
|
||||
// Assert
|
||||
Assert.True(visitor.WasStandardMethodCalled);
|
||||
Assert.False(visitor.WasSpecificMethodCalled);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Accept_CallsSpecialVisitExtension_ForSpecialVisitor()
|
||||
{
|
||||
// Arrange
|
||||
var node = new TestExtensionIRNode();
|
||||
var visitor = new SpecialVisitor();
|
||||
|
||||
// Act
|
||||
node.Accept(visitor);
|
||||
|
||||
// Assert
|
||||
Assert.False(visitor.WasStandardMethodCalled);
|
||||
Assert.True(visitor.WasSpecificMethodCalled);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Accept_TResult_CallsStandardVisitExtension_ForStandardVisitor()
|
||||
{
|
||||
// Arrange
|
||||
var node = new TestExtensionIRNode();
|
||||
var visitor = new StandardVisitor<bool>();
|
||||
|
||||
// Act
|
||||
node.Accept(visitor);
|
||||
|
||||
// Assert
|
||||
Assert.True(visitor.WasStandardMethodCalled);
|
||||
Assert.False(visitor.WasSpecificMethodCalled);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Accept_TResult_CallsSpecialVisitExtension_ForSpecialVisitor()
|
||||
{
|
||||
// Arrange
|
||||
var node = new TestExtensionIRNode();
|
||||
var visitor = new SpecialVisitor<bool>();
|
||||
|
||||
// Act
|
||||
node.Accept(visitor);
|
||||
|
||||
// Assert
|
||||
Assert.False(visitor.WasStandardMethodCalled);
|
||||
Assert.True(visitor.WasSpecificMethodCalled);
|
||||
}
|
||||
|
||||
private class TestExtensionIRNode : ExtensionIRNode
|
||||
{
|
||||
public override IList<RazorIRNode> Children => throw new NotImplementedException();
|
||||
|
||||
public override RazorIRNode Parent { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
|
||||
public override SourceSpan? Source { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
|
||||
|
||||
public override void Accept(RazorIRNodeVisitor visitor)
|
||||
{
|
||||
// This is the standard visitor boilerplate for an extension node.
|
||||
AcceptExtensionNode<TestExtensionIRNode>(this, visitor);
|
||||
}
|
||||
|
||||
public override TResult Accept<TResult>(RazorIRNodeVisitor<TResult> visitor)
|
||||
{
|
||||
// This is the standard visitor boilerplate for an extension node.
|
||||
return AcceptExtensionNode<TestExtensionIRNode, TResult>(this, visitor);
|
||||
}
|
||||
|
||||
internal override void WriteNode(RuntimeTarget target, CSharpRenderingContext context)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
private class StandardVisitor : RazorIRNodeVisitor
|
||||
{
|
||||
public bool WasStandardMethodCalled { get; private set; }
|
||||
public bool WasSpecificMethodCalled { get; private set; }
|
||||
|
||||
public override void VisitExtension(ExtensionIRNode node)
|
||||
{
|
||||
WasStandardMethodCalled = true;
|
||||
}
|
||||
|
||||
public void VisitExtension(TestExtensionIRNode node)
|
||||
{
|
||||
WasSpecificMethodCalled = true;
|
||||
}
|
||||
}
|
||||
|
||||
private class StandardVisitor<T> : RazorIRNodeVisitor<T>
|
||||
{
|
||||
public bool WasStandardMethodCalled { get; private set; }
|
||||
public bool WasSpecificMethodCalled { get; private set; }
|
||||
|
||||
public override T VisitExtension(ExtensionIRNode node)
|
||||
{
|
||||
WasStandardMethodCalled = true;
|
||||
return default(T);
|
||||
}
|
||||
|
||||
public T VisitExtension(TestExtensionIRNode node)
|
||||
{
|
||||
WasSpecificMethodCalled = true;
|
||||
return default(T);
|
||||
}
|
||||
}
|
||||
|
||||
private class SpecialVisitor : RazorIRNodeVisitor, IExtensionIRNodeVisitor<TestExtensionIRNode>
|
||||
{
|
||||
public bool WasStandardMethodCalled { get; private set; }
|
||||
public bool WasSpecificMethodCalled { get; private set; }
|
||||
|
||||
public override void VisitExtension(ExtensionIRNode node)
|
||||
{
|
||||
WasStandardMethodCalled = true;
|
||||
}
|
||||
|
||||
public void VisitExtension(TestExtensionIRNode node)
|
||||
{
|
||||
WasSpecificMethodCalled = true;
|
||||
}
|
||||
}
|
||||
|
||||
private class SpecialVisitor<T> : RazorIRNodeVisitor<T>, IExtensionIRNodeVisitor<TestExtensionIRNode, T>
|
||||
{
|
||||
public bool WasStandardMethodCalled { get; private set; }
|
||||
public bool WasSpecificMethodCalled { get; private set; }
|
||||
|
||||
public override T VisitExtension(ExtensionIRNode node)
|
||||
{
|
||||
WasStandardMethodCalled = true;
|
||||
return default(T);
|
||||
}
|
||||
|
||||
public T VisitExtension(TestExtensionIRNode node)
|
||||
{
|
||||
WasSpecificMethodCalled = true;
|
||||
return default(T);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Razor.Evolution.CodeGeneration;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Evolution
|
||||
|
|
@ -29,6 +30,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
Assert.Equal("test_directive", directive.Name);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddDirective_NoFeature_CreatesFeature()
|
||||
{
|
||||
// Arrange
|
||||
|
|
@ -45,5 +47,50 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
var directive = Assert.Single(actual.Directives);
|
||||
Assert.Equal("test_directive", directive.Name);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddTargetExtensions_ExistingFeature_UsesFeature()
|
||||
{
|
||||
// Arrange
|
||||
var extension = new MyTargetExtension();
|
||||
|
||||
var expected = new DefaultRazorTargetExtensionFeature();
|
||||
var engine = RazorEngine.CreateEmpty(b =>
|
||||
{
|
||||
b.Features.Add(expected);
|
||||
|
||||
// Act
|
||||
b.AddTargetExtension(extension);
|
||||
});
|
||||
|
||||
// Assert
|
||||
var actual = Assert.Single(engine.Features.OfType<IRazorTargetExtensionFeature>());
|
||||
Assert.Same(expected, actual);
|
||||
|
||||
Assert.Same(extension, Assert.Single(actual.TargetExtensions));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AddTargetExtensions_NoFeature_CreatesFeature()
|
||||
{
|
||||
// Arrange
|
||||
var extension = new MyTargetExtension();
|
||||
|
||||
var engine = RazorEngine.CreateEmpty(b =>
|
||||
{
|
||||
// Act
|
||||
b.AddTargetExtension(extension);
|
||||
});
|
||||
|
||||
// Assert
|
||||
var actual = Assert.Single(engine.Features.OfType<IRazorTargetExtensionFeature>());
|
||||
Assert.IsType<DefaultRazorTargetExtensionFeature>(actual);
|
||||
|
||||
Assert.Same(extension, Assert.Single(actual.TargetExtensions));
|
||||
}
|
||||
|
||||
private class MyTargetExtension : IRuntimeTargetExtension
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ using System.Collections.Generic;
|
|||
using System.Linq;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
using Microsoft.AspNetCore.Razor.Evolution.CodeGeneration;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Evolution
|
||||
{
|
||||
|
|
@ -132,10 +133,22 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
p => Assert.Same(phases[1], p));
|
||||
}
|
||||
|
||||
private static void AssertDefaultTargetExtensions(RazorEngine engine)
|
||||
{
|
||||
var feature = engine.Features.OfType<IRazorTargetExtensionFeature>().FirstOrDefault();
|
||||
Assert.NotNull(feature);
|
||||
|
||||
Assert.Collection(
|
||||
feature.TargetExtensions,
|
||||
f => Assert.IsType<TemplateTargetExtension>(f));
|
||||
}
|
||||
|
||||
private static void AssertDefaultRuntimeFeatures(IEnumerable<IRazorEngineFeature> features)
|
||||
{
|
||||
Assert.Collection(
|
||||
features,
|
||||
feature => Assert.IsType<DefaultRazorDirectiveFeature>(feature),
|
||||
feature => Assert.IsType<DefaultRazorTargetExtensionFeature>(feature),
|
||||
feature => Assert.IsType<DefaultDirectiveSyntaxTreePass>(feature),
|
||||
feature => Assert.IsType<HtmlNodeOptimizationPass>(feature),
|
||||
feature => Assert.IsType<TagHelperBinderSyntaxTreePass>(feature),
|
||||
|
|
@ -162,6 +175,8 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
{
|
||||
Assert.Collection(
|
||||
features,
|
||||
feature => Assert.IsType<DefaultRazorDirectiveFeature>(feature),
|
||||
feature => Assert.IsType<DefaultRazorTargetExtensionFeature>(feature),
|
||||
feature => Assert.IsType<DefaultDirectiveSyntaxTreePass>(feature),
|
||||
feature => Assert.IsType<HtmlNodeOptimizationPass>(feature),
|
||||
feature => Assert.IsType<TagHelperBinderSyntaxTreePass>(feature),
|
||||
|
|
|
|||
Loading…
Reference in New Issue