From e2dd09c918cfc9e77ae8f31042b1c65f94935e5a Mon Sep 17 00:00:00 2001 From: Ajay Bhargav Baaskaran Date: Tue, 13 Dec 2016 20:51:24 -0800 Subject: [PATCH] [Fixes #881] Added TagHelper IR support --- .../DefaultRazorIRLoweringPhase.cs | 140 +++++++++++++++++- .../HtmlNodeOptimizationPass.cs | 2 +- .../AddTagHelperHtmlAttributeIRNode.cs | 42 ++++++ .../Intermediate/CreateTagHelperIRNode.cs | 42 ++++++ .../DeclareTagHelperFieldsIRNode.cs | 40 +++++ .../Intermediate/ExecuteTagHelpersIRNode.cs | 38 +++++ .../InitializeTagHelperStructureIRNode.cs | 42 ++++++ .../Intermediate/RazorIRNodeVisitor.cs | 35 +++++ .../Intermediate/RazorIRNodeVisitorOfT.cs | 35 +++++ .../SetTagHelperPropertyIRNode.cs | 48 ++++++ .../Intermediate/TagHelperIRNode.cs | 38 +++++ .../Legacy/AddTagHelperChunkGenerator.cs | 1 + .../Legacy/ParserVisitor.cs | 20 +++ .../Legacy/RemoveTagHelperChunkGenerator.cs | 1 + .../Legacy/TagHelperChunkGenerator.cs | 2 + .../TagHelperPrefixDirectiveChunkGenerator.cs | 1 + .../RazorEngine.cs | 2 +- .../TagHelperBinderSyntaxTreePass.cs | 2 +- .../IntegrationTests/BasicIntegrationTest.cs | 2 - .../IntegrationTests/RazorIRNodeWriter.cs | 26 ++++ .../TagHelpersIntegrationTest.cs | 121 +++++++++++++++ ...aultRazorIRLoweringPhaseIntegrationTest.cs | 117 ++++++++++++++- .../Intermediate/RazorIRAssert.cs | 72 +++++++++ .../RazorEngineTest.cs | 4 +- .../NestedTagHelpers.cshtml | 5 + .../NestedTagHelpers.ir.txt | 32 ++++ .../SimpleTagHelpers.cshtml | 5 + .../SimpleTagHelpers.ir.txt | 18 +++ .../TagHelpersWithBoundAttributes.cshtml | 4 + .../TagHelpersWithBoundAttributes.ir.txt | 19 +++ 30 files changed, 947 insertions(+), 9 deletions(-) create mode 100644 src/Microsoft.AspNetCore.Razor.Evolution/Intermediate/AddTagHelperHtmlAttributeIRNode.cs create mode 100644 src/Microsoft.AspNetCore.Razor.Evolution/Intermediate/CreateTagHelperIRNode.cs create mode 100644 src/Microsoft.AspNetCore.Razor.Evolution/Intermediate/DeclareTagHelperFieldsIRNode.cs create mode 100644 src/Microsoft.AspNetCore.Razor.Evolution/Intermediate/ExecuteTagHelpersIRNode.cs create mode 100644 src/Microsoft.AspNetCore.Razor.Evolution/Intermediate/InitializeTagHelperStructureIRNode.cs create mode 100644 src/Microsoft.AspNetCore.Razor.Evolution/Intermediate/SetTagHelperPropertyIRNode.cs create mode 100644 src/Microsoft.AspNetCore.Razor.Evolution/Intermediate/TagHelperIRNode.cs create mode 100644 test/Microsoft.AspNetCore.Razor.Evolution.Test/IntegrationTests/TagHelpersIntegrationTest.cs create mode 100644 test/Microsoft.AspNetCore.Razor.Evolution.Test/TestFiles/IntegrationTests/TagHelpersIntegrationTest/NestedTagHelpers.cshtml create mode 100644 test/Microsoft.AspNetCore.Razor.Evolution.Test/TestFiles/IntegrationTests/TagHelpersIntegrationTest/NestedTagHelpers.ir.txt create mode 100644 test/Microsoft.AspNetCore.Razor.Evolution.Test/TestFiles/IntegrationTests/TagHelpersIntegrationTest/SimpleTagHelpers.cshtml create mode 100644 test/Microsoft.AspNetCore.Razor.Evolution.Test/TestFiles/IntegrationTests/TagHelpersIntegrationTest/SimpleTagHelpers.ir.txt create mode 100644 test/Microsoft.AspNetCore.Razor.Evolution.Test/TestFiles/IntegrationTests/TagHelpersIntegrationTest/TagHelpersWithBoundAttributes.cshtml create mode 100644 test/Microsoft.AspNetCore.Razor.Evolution.Test/TestFiles/IntegrationTests/TagHelpersIntegrationTest/TagHelpersWithBoundAttributes.ir.txt diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/DefaultRazorIRLoweringPhase.cs b/src/Microsoft.AspNetCore.Razor.Evolution/DefaultRazorIRLoweringPhase.cs index 960302f02d..8a7b2657f9 100644 --- a/src/Microsoft.AspNetCore.Razor.Evolution/DefaultRazorIRLoweringPhase.cs +++ b/src/Microsoft.AspNetCore.Razor.Evolution/DefaultRazorIRLoweringPhase.cs @@ -159,7 +159,6 @@ namespace Microsoft.AspNetCore.Razor.Evolution contentLength, sourceRangeStart.FilePath ?? _codeDocument.Source.Filename); } - } public override void VisitExpressionSpan(ExpressionChunkGenerator chunkGenerator, Span span) @@ -253,6 +252,145 @@ namespace Microsoft.AspNetCore.Razor.Evolution Builder.Pop(); } + public override void VisitStartTagHelperBlock(TagHelperChunkGenerator chunkGenerator, Block block) + { + var tagHelperBlock = block as TagHelperBlock; + if (tagHelperBlock == null) + { + return; + } + + DeclareTagHelperFields(tagHelperBlock); + + Builder.Push(new TagHelperIRNode()); + + Builder.Push(new InitializeTagHelperStructureIRNode() + { + TagName = tagHelperBlock.TagName, + TagMode = tagHelperBlock.TagMode + }); + } + + public override void VisitEndTagHelperBlock(TagHelperChunkGenerator chunkGenerator, Block block) + { + var tagHelperBlock = block as TagHelperBlock; + if (tagHelperBlock == null) + { + return; + } + + Builder.Pop(); // Pop InitializeTagHelperStructureIRNode + + AddTagHelperCreation(tagHelperBlock.Descriptors); + AddTagHelperAttributes(tagHelperBlock.Attributes, tagHelperBlock.Descriptors); + AddExecuteTagHelpers(); + + Builder.Pop(); // Pop TagHelperIRNode + } + + public override void VisitAddTagHelperSpan(AddTagHelperChunkGenerator chunkGenerator, Span span) + { + } + + public override void VisitRemoveTagHelperSpan(RemoveTagHelperChunkGenerator chunkGenerator, Span span) + { + } + + public override void VisitTagHelperPrefixDirectiveSpan(TagHelperPrefixDirectiveChunkGenerator chunkGenerator, Span span) + { + } + + private void DeclareTagHelperFields(TagHelperBlock block) + { + var declareFieldsNode = Class.Children.OfType().SingleOrDefault(); + if (declareFieldsNode == null) + { + declareFieldsNode = new DeclareTagHelperFieldsIRNode(); + declareFieldsNode.Parent = Class; + + var methodIndex = Class.Children.IndexOf(Method); + Class.Children.Insert(methodIndex, declareFieldsNode); + } + + foreach (var descriptor in block.Descriptors) + { + declareFieldsNode.UsedTagHelperTypeNames.Add(descriptor.TypeName); + } + } + + private void AddTagHelperCreation(IEnumerable descriptors) + { + foreach (var descriptor in descriptors) + { + var createTagHelper = new CreateTagHelperIRNode() + { + TagHelperTypeName = descriptor.TypeName, + Descriptor = descriptor + }; + + Builder.Add(createTagHelper); + } + } + + private void AddTagHelperAttributes(IList attributes, IEnumerable descriptors) + { + var renderedBoundAttributeNames = new HashSet(StringComparer.OrdinalIgnoreCase); + foreach (var attribute in attributes) + { + var attributeValueNode = attribute.Value; + var associatedDescriptors = descriptors.Where(descriptor => + descriptor.Attributes.Any(attributeDescriptor => attributeDescriptor.IsNameMatch(attribute.Name))); + + if (associatedDescriptors.Any() && renderedBoundAttributeNames.Add(attribute.Name)) + { + if (attributeValueNode == null) + { + // Minimized attributes are not valid for bound attributes. TagHelperBlockRewriter has already + // logged an error if it was a bound attribute; so we can skip. + continue; + } + + foreach (var associatedDescriptor in associatedDescriptors) + { + var associatedAttributeDescriptor = associatedDescriptor.Attributes.First( + attributeDescriptor => attributeDescriptor.IsNameMatch(attribute.Name)); + var setTagHelperProperty = new SetTagHelperPropertyIRNode() + { + PropertyName = associatedAttributeDescriptor.PropertyName, + AttributeName = attribute.Name, + TagHelperTypeName = associatedDescriptor.TypeName, + Descriptor = associatedAttributeDescriptor, + ValueStyle = attribute.ValueStyle + }; + + Builder.Push(setTagHelperProperty); + attributeValueNode.Accept(this); + Builder.Pop(); + } + } + else + { + var addHtmlAttribute = new AddTagHelperHtmlAttributeIRNode() + { + Name = attribute.Name, + ValueStyle = attribute.ValueStyle + }; + + Builder.Push(addHtmlAttribute); + if (attributeValueNode != null) + { + attributeValueNode.Accept(this); + } + Builder.Pop(); + } + } + } + + private void AddExecuteTagHelpers() + { + Builder.Add(new ExecuteTagHelpersIRNode()); + } + private MappingLocation BuildSourceRangeFromNode(SyntaxTreeNode node) { var location = node.Start; diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/HtmlNodeOptimizationPass.cs b/src/Microsoft.AspNetCore.Razor.Evolution/HtmlNodeOptimizationPass.cs index 78d913322b..86169d3d67 100644 --- a/src/Microsoft.AspNetCore.Razor.Evolution/HtmlNodeOptimizationPass.cs +++ b/src/Microsoft.AspNetCore.Razor.Evolution/HtmlNodeOptimizationPass.cs @@ -9,7 +9,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution { public RazorEngine Engine { get; set; } - public int Order => 150; + public int Order => 100; public RazorSyntaxTree Execute(RazorCodeDocument codeDocument, RazorSyntaxTree syntaxTree) { diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/Intermediate/AddTagHelperHtmlAttributeIRNode.cs b/src/Microsoft.AspNetCore.Razor.Evolution/Intermediate/AddTagHelperHtmlAttributeIRNode.cs new file mode 100644 index 0000000000..93f81258e9 --- /dev/null +++ b/src/Microsoft.AspNetCore.Razor.Evolution/Intermediate/AddTagHelperHtmlAttributeIRNode.cs @@ -0,0 +1,42 @@ +// 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.Legacy; + +namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate +{ + internal class AddTagHelperHtmlAttributeIRNode : RazorIRNode + { + public override IList Children { get; } = new List(); + + public override RazorIRNode Parent { get; set; } + + internal override MappingLocation SourceRange { get; set; } + + public string Name { get; set; } + + internal HtmlAttributeValueStyle ValueStyle { get; set; } + + public override void Accept(RazorIRNodeVisitor visitor) + { + if (visitor == null) + { + throw new ArgumentNullException(nameof(visitor)); + } + + visitor.VisitAddTagHelperHtmlAttribute(this); + } + + public override TResult Accept(RazorIRNodeVisitor visitor) + { + if (visitor == null) + { + throw new ArgumentNullException(nameof(visitor)); + } + + return visitor.VisitAddTagHelperHtmlAttribute(this); + } + } +} diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/Intermediate/CreateTagHelperIRNode.cs b/src/Microsoft.AspNetCore.Razor.Evolution/Intermediate/CreateTagHelperIRNode.cs new file mode 100644 index 0000000000..aadb6009c3 --- /dev/null +++ b/src/Microsoft.AspNetCore.Razor.Evolution/Intermediate/CreateTagHelperIRNode.cs @@ -0,0 +1,42 @@ +// 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.Legacy; + +namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate +{ + internal class CreateTagHelperIRNode : RazorIRNode + { + public override IList Children { get; } = EmptyArray; + + public override RazorIRNode Parent { get; set; } + + internal override MappingLocation SourceRange { get; set; } + + public string TagHelperTypeName { get; set; } + + internal TagHelperDescriptor Descriptor { get; set; } + + public override void Accept(RazorIRNodeVisitor visitor) + { + if (visitor == null) + { + throw new ArgumentNullException(nameof(visitor)); + } + + visitor.VisitCreateTagHelper(this); + } + + public override TResult Accept(RazorIRNodeVisitor visitor) + { + if (visitor == null) + { + throw new ArgumentNullException(nameof(visitor)); + } + + return visitor.VisitCreateTagHelper(this); + } + } +} diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/Intermediate/DeclareTagHelperFieldsIRNode.cs b/src/Microsoft.AspNetCore.Razor.Evolution/Intermediate/DeclareTagHelperFieldsIRNode.cs new file mode 100644 index 0000000000..5d869e9227 --- /dev/null +++ b/src/Microsoft.AspNetCore.Razor.Evolution/Intermediate/DeclareTagHelperFieldsIRNode.cs @@ -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 System; +using System.Collections.Generic; +using Microsoft.AspNetCore.Razor.Evolution.Legacy; + +namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate +{ + internal class DeclareTagHelperFieldsIRNode : RazorIRNode + { + public override IList Children { get; } = EmptyArray; + + public override RazorIRNode Parent { get; set; } + + internal override MappingLocation SourceRange { get; set; } + + public ISet UsedTagHelperTypeNames { get; set; } = new HashSet(StringComparer.Ordinal); + + public override void Accept(RazorIRNodeVisitor visitor) + { + if (visitor == null) + { + throw new ArgumentNullException(nameof(visitor)); + } + + visitor.VisitDeclareTagHelperFields(this); + } + + public override TResult Accept(RazorIRNodeVisitor visitor) + { + if (visitor == null) + { + throw new ArgumentNullException(nameof(visitor)); + } + + return visitor.VisitDeclareTagHelperFields(this); + } + } +} diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/Intermediate/ExecuteTagHelpersIRNode.cs b/src/Microsoft.AspNetCore.Razor.Evolution/Intermediate/ExecuteTagHelpersIRNode.cs new file mode 100644 index 0000000000..cd0303c119 --- /dev/null +++ b/src/Microsoft.AspNetCore.Razor.Evolution/Intermediate/ExecuteTagHelpersIRNode.cs @@ -0,0 +1,38 @@ +// 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.Legacy; + +namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate +{ + internal class ExecuteTagHelpersIRNode : RazorIRNode + { + public override IList Children { get; } = new List(); + + public override RazorIRNode Parent { get; set; } + + internal override MappingLocation SourceRange { get; set; } + + public override void Accept(RazorIRNodeVisitor visitor) + { + if (visitor == null) + { + throw new ArgumentNullException(nameof(visitor)); + } + + visitor.VisitExecuteTagHelpers(this); + } + + public override TResult Accept(RazorIRNodeVisitor visitor) + { + if (visitor == null) + { + throw new ArgumentNullException(nameof(visitor)); + } + + return visitor.VisitExecuteTagHelpers(this); + } + } +} diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/Intermediate/InitializeTagHelperStructureIRNode.cs b/src/Microsoft.AspNetCore.Razor.Evolution/Intermediate/InitializeTagHelperStructureIRNode.cs new file mode 100644 index 0000000000..a1ce6beb9a --- /dev/null +++ b/src/Microsoft.AspNetCore.Razor.Evolution/Intermediate/InitializeTagHelperStructureIRNode.cs @@ -0,0 +1,42 @@ +// 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.Legacy; + +namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate +{ + internal class InitializeTagHelperStructureIRNode : RazorIRNode + { + public override IList Children { get; } = new List(); + + public override RazorIRNode Parent { get; set; } + + internal override MappingLocation SourceRange { get; set; } + + public string TagName { get; set; } + + internal TagMode TagMode { get; set; } + + public override void Accept(RazorIRNodeVisitor visitor) + { + if (visitor == null) + { + throw new ArgumentNullException(nameof(visitor)); + } + + visitor.VisitInitializeTagHelperStructure(this); + } + + public override TResult Accept(RazorIRNodeVisitor visitor) + { + if (visitor == null) + { + throw new ArgumentNullException(nameof(visitor)); + } + + return visitor.VisitInitializeTagHelperStructure(this); + } + } +} diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/Intermediate/RazorIRNodeVisitor.cs b/src/Microsoft.AspNetCore.Razor.Evolution/Intermediate/RazorIRNodeVisitor.cs index e2488a5aba..23f88d6f77 100644 --- a/src/Microsoft.AspNetCore.Razor.Evolution/Intermediate/RazorIRNodeVisitor.cs +++ b/src/Microsoft.AspNetCore.Razor.Evolution/Intermediate/RazorIRNodeVisitor.cs @@ -93,5 +93,40 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate { VisitDefault(node); } + + internal virtual void VisitDeclareTagHelperFields(DeclareTagHelperFieldsIRNode node) + { + VisitDefault(node); + } + + internal virtual void VisitTagHelper(TagHelperIRNode node) + { + VisitDefault(node); + } + + internal virtual void VisitInitializeTagHelperStructure(InitializeTagHelperStructureIRNode node) + { + VisitDefault(node); + } + + internal virtual void VisitCreateTagHelper(CreateTagHelperIRNode node) + { + VisitDefault(node); + } + + internal virtual void VisitSetTagHelperProperty(SetTagHelperPropertyIRNode node) + { + VisitDefault(node); + } + + internal virtual void VisitAddTagHelperHtmlAttribute(AddTagHelperHtmlAttributeIRNode node) + { + VisitDefault(node); + } + + internal virtual void VisitExecuteTagHelpers(ExecuteTagHelpersIRNode node) + { + VisitDefault(node); + } } } diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/Intermediate/RazorIRNodeVisitorOfT.cs b/src/Microsoft.AspNetCore.Razor.Evolution/Intermediate/RazorIRNodeVisitorOfT.cs index 71026ca802..b056f024a7 100644 --- a/src/Microsoft.AspNetCore.Razor.Evolution/Intermediate/RazorIRNodeVisitorOfT.cs +++ b/src/Microsoft.AspNetCore.Razor.Evolution/Intermediate/RazorIRNodeVisitorOfT.cs @@ -94,5 +94,40 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate { return VisitDefault(node); } + + internal virtual TResult VisitDeclareTagHelperFields(DeclareTagHelperFieldsIRNode node) + { + return VisitDefault(node); + } + + internal virtual TResult VisitTagHelper(TagHelperIRNode node) + { + return VisitDefault(node); + } + + internal virtual TResult VisitInitializeTagHelperStructure(InitializeTagHelperStructureIRNode node) + { + return VisitDefault(node); + } + + internal virtual TResult VisitCreateTagHelper(CreateTagHelperIRNode node) + { + return VisitDefault(node); + } + + internal virtual TResult VisitSetTagHelperProperty(SetTagHelperPropertyIRNode node) + { + return VisitDefault(node); + } + + internal virtual TResult VisitAddTagHelperHtmlAttribute(AddTagHelperHtmlAttributeIRNode node) + { + return VisitDefault(node); + } + + internal virtual TResult VisitExecuteTagHelpers(ExecuteTagHelpersIRNode node) + { + return VisitDefault(node); + } } } diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/Intermediate/SetTagHelperPropertyIRNode.cs b/src/Microsoft.AspNetCore.Razor.Evolution/Intermediate/SetTagHelperPropertyIRNode.cs new file mode 100644 index 0000000000..764928543f --- /dev/null +++ b/src/Microsoft.AspNetCore.Razor.Evolution/Intermediate/SetTagHelperPropertyIRNode.cs @@ -0,0 +1,48 @@ +// 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.Legacy; + +namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate +{ + internal class SetTagHelperPropertyIRNode : RazorIRNode + { + public override IList Children { get; } = new List(); + + public override RazorIRNode Parent { get; set; } + + internal override MappingLocation SourceRange { get; set; } + + public string TagHelperTypeName { get; set; } + + public string PropertyName { get; set; } + + public string AttributeName { get; set; } + + internal HtmlAttributeValueStyle ValueStyle { get; set; } + + internal TagHelperAttributeDescriptor Descriptor { get; set; } + + public override void Accept(RazorIRNodeVisitor visitor) + { + if (visitor == null) + { + throw new ArgumentNullException(nameof(visitor)); + } + + visitor.VisitSetTagHelperProperty(this); + } + + public override TResult Accept(RazorIRNodeVisitor visitor) + { + if (visitor == null) + { + throw new ArgumentNullException(nameof(visitor)); + } + + return visitor.VisitSetTagHelperProperty(this); + } + } +} diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/Intermediate/TagHelperIRNode.cs b/src/Microsoft.AspNetCore.Razor.Evolution/Intermediate/TagHelperIRNode.cs new file mode 100644 index 0000000000..933825e23c --- /dev/null +++ b/src/Microsoft.AspNetCore.Razor.Evolution/Intermediate/TagHelperIRNode.cs @@ -0,0 +1,38 @@ +// 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.Legacy; + +namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate +{ + internal class TagHelperIRNode : RazorIRNode + { + public override IList Children { get; } = new List(); + + public override RazorIRNode Parent { get; set; } + + internal override MappingLocation SourceRange { get; set; } + + public override void Accept(RazorIRNodeVisitor visitor) + { + if (visitor == null) + { + throw new ArgumentNullException(nameof(visitor)); + } + + visitor.VisitTagHelper(this); + } + + public override TResult Accept(RazorIRNodeVisitor visitor) + { + if (visitor == null) + { + throw new ArgumentNullException(nameof(visitor)); + } + + return visitor.VisitTagHelper(this); + } + } +} diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/AddTagHelperChunkGenerator.cs b/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/AddTagHelperChunkGenerator.cs index 28f4b9337e..ca4e2d449f 100644 --- a/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/AddTagHelperChunkGenerator.cs +++ b/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/AddTagHelperChunkGenerator.cs @@ -17,6 +17,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy public override void Accept(ParserVisitor visitor, Span span) { + visitor.VisitAddTagHelperSpan(this, span); } public override void GenerateChunk(Span target, ChunkGeneratorContext context) diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/ParserVisitor.cs b/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/ParserVisitor.cs index f4db4a49d9..de5b5ac1e7 100644 --- a/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/ParserVisitor.cs +++ b/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/ParserVisitor.cs @@ -112,5 +112,25 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy public virtual void VisitStartCommentBlock(RazorCommentChunkGenerator chunkGenerator, Block block) { } + + public virtual void VisitStartTagHelperBlock(TagHelperChunkGenerator chunkGenerator, Block block) + { + } + + public virtual void VisitEndTagHelperBlock(TagHelperChunkGenerator chunkGenerator, Block block) + { + } + + public virtual void VisitAddTagHelperSpan(AddTagHelperChunkGenerator chunkGenerator, Span span) + { + } + + public virtual void VisitRemoveTagHelperSpan(RemoveTagHelperChunkGenerator chunkGenerator, Span span) + { + } + + public virtual void VisitTagHelperPrefixDirectiveSpan(TagHelperPrefixDirectiveChunkGenerator chunkGenerator, Span span) + { + } } } diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/RemoveTagHelperChunkGenerator.cs b/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/RemoveTagHelperChunkGenerator.cs index 4b1341b565..6b6506dcfa 100644 --- a/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/RemoveTagHelperChunkGenerator.cs +++ b/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/RemoveTagHelperChunkGenerator.cs @@ -17,6 +17,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy public override void Accept(ParserVisitor visitor, Span span) { + visitor.VisitRemoveTagHelperSpan(this, span); } public override void GenerateChunk(Span target, ChunkGeneratorContext context) diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/TagHelperChunkGenerator.cs b/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/TagHelperChunkGenerator.cs index 56cd2713bc..39f19000e6 100644 --- a/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/TagHelperChunkGenerator.cs +++ b/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/TagHelperChunkGenerator.cs @@ -86,10 +86,12 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy public override void AcceptStart(ParserVisitor visitor, Block block) { + visitor.VisitStartTagHelperBlock(this, block); } public override void AcceptEnd(ParserVisitor visitor, Block block) { + visitor.VisitEndTagHelperBlock(this, block); } } } \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/TagHelperPrefixDirectiveChunkGenerator.cs b/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/TagHelperPrefixDirectiveChunkGenerator.cs index 26b750e71b..ad71ebc91d 100644 --- a/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/TagHelperPrefixDirectiveChunkGenerator.cs +++ b/src/Microsoft.AspNetCore.Razor.Evolution/Legacy/TagHelperPrefixDirectiveChunkGenerator.cs @@ -17,6 +17,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy public override void Accept(ParserVisitor visitor, Span span) { + visitor.VisitTagHelperPrefixDirectiveSpan(this, span); } public override void GenerateChunk(Span target, ChunkGeneratorContext context) diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/RazorEngine.cs b/src/Microsoft.AspNetCore.Razor.Evolution/RazorEngine.cs index 73a1b8ca19..49de266da2 100644 --- a/src/Microsoft.AspNetCore.Razor.Evolution/RazorEngine.cs +++ b/src/Microsoft.AspNetCore.Razor.Evolution/RazorEngine.cs @@ -55,8 +55,8 @@ namespace Microsoft.AspNetCore.Razor.Evolution // Syntax Tree passes builder.Features.Add(new DefaultDirectiveSyntaxTreePass()); - builder.Features.Add(new TagHelperBinderSyntaxTreePass()); builder.Features.Add(new HtmlNodeOptimizationPass()); + builder.Features.Add(new TagHelperBinderSyntaxTreePass()); // IR Passes builder.Features.Add(new DefaultDirectiveIRPass()); diff --git a/src/Microsoft.AspNetCore.Razor.Evolution/TagHelperBinderSyntaxTreePass.cs b/src/Microsoft.AspNetCore.Razor.Evolution/TagHelperBinderSyntaxTreePass.cs index fe1262db5a..7451d676bd 100644 --- a/src/Microsoft.AspNetCore.Razor.Evolution/TagHelperBinderSyntaxTreePass.cs +++ b/src/Microsoft.AspNetCore.Razor.Evolution/TagHelperBinderSyntaxTreePass.cs @@ -11,7 +11,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution { public RazorEngine Engine { get; set; } - public int Order => 100; + public int Order => 150; public RazorSyntaxTree Execute(RazorCodeDocument document, RazorSyntaxTree syntaxTree) { diff --git a/test/Microsoft.AspNetCore.Razor.Evolution.Test/IntegrationTests/BasicIntegrationTest.cs b/test/Microsoft.AspNetCore.Razor.Evolution.Test/IntegrationTests/BasicIntegrationTest.cs index 80b517d15d..b7eca0a135 100644 --- a/test/Microsoft.AspNetCore.Razor.Evolution.Test/IntegrationTests/BasicIntegrationTest.cs +++ b/test/Microsoft.AspNetCore.Razor.Evolution.Test/IntegrationTests/BasicIntegrationTest.cs @@ -1,8 +1,6 @@ // 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.IntegrationTests diff --git a/test/Microsoft.AspNetCore.Razor.Evolution.Test/IntegrationTests/RazorIRNodeWriter.cs b/test/Microsoft.AspNetCore.Razor.Evolution.Test/IntegrationTests/RazorIRNodeWriter.cs index 5f3856ace8..4c52a9e768 100644 --- a/test/Microsoft.AspNetCore.Razor.Evolution.Test/IntegrationTests/RazorIRNodeWriter.cs +++ b/test/Microsoft.AspNetCore.Razor.Evolution.Test/IntegrationTests/RazorIRNodeWriter.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using Microsoft.AspNetCore.Razor.Evolution.Intermediate; +using Microsoft.AspNetCore.Razor.Evolution.Legacy; namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests { @@ -86,6 +87,31 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests WriteContentNode(node, node.Content); } + internal override void VisitDeclareTagHelperFields(DeclareTagHelperFieldsIRNode node) + { + WriteContentNode(node, node.UsedTagHelperTypeNames.ToArray()); + } + + internal override void VisitInitializeTagHelperStructure(InitializeTagHelperStructureIRNode node) + { + WriteContentNode(node, node.TagName, string.Format("{0}.{1}", nameof(TagMode), node.TagMode)); + } + + internal override void VisitCreateTagHelper(CreateTagHelperIRNode node) + { + WriteContentNode(node, node.TagHelperTypeName); + } + + internal override void VisitSetTagHelperProperty(SetTagHelperPropertyIRNode node) + { + WriteContentNode(node, node.AttributeName, node.PropertyName, string.Format("HtmlAttributeValueStyle.{0}", node.ValueStyle)); + } + + internal override void VisitAddTagHelperHtmlAttribute(AddTagHelperHtmlAttributeIRNode node) + { + WriteContentNode(node, node.Name, string.Format("{0}.{1}", nameof(HtmlAttributeValueStyle), node.ValueStyle)); + } + protected void WriteBasicNode(RazorIRNode node) { WriteIndent(); diff --git a/test/Microsoft.AspNetCore.Razor.Evolution.Test/IntegrationTests/TagHelpersIntegrationTest.cs b/test/Microsoft.AspNetCore.Razor.Evolution.Test/IntegrationTests/TagHelpersIntegrationTest.cs new file mode 100644 index 0000000000..1a236592aa --- /dev/null +++ b/test/Microsoft.AspNetCore.Razor.Evolution.Test/IntegrationTests/TagHelpersIntegrationTest.cs @@ -0,0 +1,121 @@ +// 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.Legacy; +using Xunit; + +namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests +{ + public class TagHelpersIntegrationTest : IntegrationTestBase + { + [Fact] + public void SimpleTagHelpers() + { + // Arrange + var descriptors = new[] + { + new TagHelperDescriptor + { + TagName = "input", + TypeName = "InputTagHelper" + } + }; + + var engine = RazorEngine.Create( + builder => builder.Features.Add(new TagHelperFeature(new TestTagHelperDescriptorResolver(descriptors)))); + var document = CreateCodeDocument(); + + // Act + engine.Process(document); + + // Assert + AssertIRMatchesBaseline(document.GetIRDocument()); + } + + [Fact] + public void TagHelpersWithBoundAttributes() + { + // Arrange + var descriptors = new[] + { + new TagHelperDescriptor + { + TagName = "input", + TypeName = "InputTagHelper", + Attributes = new[] { new TagHelperAttributeDescriptor + { + Name = "bound", + PropertyName = "FooProp", + TypeName = "System.String" + } } + } + }; + + var engine = RazorEngine.Create( + builder => builder.Features.Add(new TagHelperFeature(new TestTagHelperDescriptorResolver(descriptors)))); + var document = CreateCodeDocument(); + + // Act + engine.Process(document); + + // Assert + AssertIRMatchesBaseline(document.GetIRDocument()); + } + + [Fact] + public void NestedTagHelpers() + { + // Arrange + var descriptors = new[] + { + new TagHelperDescriptor + { + TagName = "p", + TypeName = "PTagHelper" + }, + new TagHelperDescriptor + { + TagName = "form", + TypeName = "FormTagHelper" + }, + new TagHelperDescriptor + { + TagName = "input", + TypeName = "InputTagHelper", + Attributes = new[] { new TagHelperAttributeDescriptor + { + Name = "value", + PropertyName = "FooProp", + TypeName = "System.String" + } } + } + }; + + var engine = RazorEngine.Create( + builder => builder.Features.Add(new TagHelperFeature(new TestTagHelperDescriptorResolver(descriptors)))); + var document = CreateCodeDocument(); + + // Act + engine.Process(document); + + // Assert + AssertIRMatchesBaseline(document.GetIRDocument()); + } + + private class TestTagHelperDescriptorResolver : ITagHelperDescriptorResolver + { + private readonly IEnumerable _descriptors; + + public TestTagHelperDescriptorResolver(IEnumerable descriptors) + { + _descriptors = descriptors; + } + + public IEnumerable Resolve(TagHelperDescriptorResolutionContext resolutionContext) + { + return _descriptors; + } + } + } +} diff --git a/test/Microsoft.AspNetCore.Razor.Evolution.Test/Intermediate/DefaultRazorIRLoweringPhaseIntegrationTest.cs b/test/Microsoft.AspNetCore.Razor.Evolution.Test/Intermediate/DefaultRazorIRLoweringPhaseIntegrationTest.cs index 37268f8cc8..3109207c16 100644 --- a/test/Microsoft.AspNetCore.Razor.Evolution.Test/Intermediate/DefaultRazorIRLoweringPhaseIntegrationTest.cs +++ b/test/Microsoft.AspNetCore.Razor.Evolution.Test/Intermediate/DefaultRazorIRLoweringPhaseIntegrationTest.cs @@ -5,6 +5,9 @@ using static Microsoft.AspNetCore.Razor.Evolution.Intermediate.RazorIRAssert; using Xunit; using System; using System.Threading.Tasks; +using Microsoft.AspNetCore.Razor.Evolution.Legacy; +using System.Collections.Generic; +using System.Linq; namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate { @@ -188,9 +191,106 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate n => Assert.IsType(n)); } + [Fact] + public void Lower_TagHelpers() + { + // Arrange + var codeDocument = TestRazorCodeDocument.Create(@""); + var descriptors = new[] + { + new TagHelperDescriptor + { + TagName = "span", + TypeName = "SpanTagHelper" + } + }; + + // Act + var irDocument = Lower(codeDocument, descriptors); + + // Assert + Children(irDocument, + n => Assert.IsType(n), + n => Assert.IsType(n)); + var @namespace = irDocument.Children[1]; + Children(@namespace, + n => Using("System", n), + n => Using(typeof(Task).Namespace, n), + n => Assert.IsType(n)); + var @class = @namespace.Children[2]; + Children(@class, + n => TagHelperFieldDeclaration(n, "SpanTagHelper"), + n => Assert.IsType(n)); + var method = @class.Children[1]; + var tagHelperNode = SingleChild(method); + Children(tagHelperNode, + n => TagHelperStructure("span", TagMode.StartTagAndEndTag, n), + n => Assert.IsType(n), + n => TagHelperHtmlAttribute( + "val", + HtmlAttributeValueStyle.DoubleQuotes, + n, + v => CSharpAttributeValue(string.Empty, "Hello", v), + v => LiteralAttributeValue(" ", "World", v)), + n => Assert.IsType(n)); + } + + [Fact] + public void Lower_TagHelpersWithBoundAttribute() + { + // Arrange + var codeDocument = TestRazorCodeDocument.Create(""); + var descriptor = new TagHelperDescriptor + { + TagName = "input", + TypeName = "InputTagHelper", + Attributes = new[] { new TagHelperAttributeDescriptor + { + Name = "bound", + PropertyName = "FooProp", + TypeName = "System.String" + } } + }; + + // Act + var irDocument = Lower(codeDocument, new[] { descriptor }); + + // Assert + Children(irDocument, + n => Assert.IsType(n), + n => Assert.IsType(n)); + var @namespace = irDocument.Children[1]; + Children(@namespace, + n => Using("System", n), + n => Using(typeof(Task).Namespace, n), + n => Assert.IsType(n)); + var @class = @namespace.Children[2]; + Children(@class, + n => TagHelperFieldDeclaration(n, "InputTagHelper"), + n => Assert.IsType(n)); + var method = @class.Children[1]; + var tagHelperNode = SingleChild(method); + Children(tagHelperNode, + n => TagHelperStructure("input", TagMode.SelfClosing, n), + n => Assert.IsType(n), + n => SetTagHelperProperty( + "bound", + "FooProp", + HtmlAttributeValueStyle.SingleQuotes, + n, + v => Html("foo", v)), + n => Assert.IsType(n)); + } + private DocumentIRNode Lower(RazorCodeDocument codeDocument) { - var engine = RazorEngine.Create(); + return Lower(codeDocument, Enumerable.Empty()); + } + + private DocumentIRNode Lower(RazorCodeDocument codeDocument, IEnumerable descriptors) + { + var engine = RazorEngine.Create( + builder => builder.Features.Add(new TagHelperFeature(new TestTagHelperDescriptorResolver(descriptors)))); for (var i = 0; i < engine.Phases.Count; i++) { @@ -207,5 +307,20 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate Assert.NotNull(irDocument); return irDocument; } + + private class TestTagHelperDescriptorResolver : ITagHelperDescriptorResolver + { + private readonly IEnumerable _descriptors; + + public TestTagHelperDescriptorResolver(IEnumerable descriptors) + { + _descriptors = descriptors; + } + + public IEnumerable Resolve(TagHelperDescriptorResolutionContext resolutionContext) + { + return _descriptors; + } + } } } diff --git a/test/Microsoft.AspNetCore.Razor.Evolution.Test/Intermediate/RazorIRAssert.cs b/test/Microsoft.AspNetCore.Razor.Evolution.Test/Intermediate/RazorIRAssert.cs index f5ac1b207c..1ac61c217e 100644 --- a/test/Microsoft.AspNetCore.Razor.Evolution.Test/Intermediate/RazorIRAssert.cs +++ b/test/Microsoft.AspNetCore.Razor.Evolution.Test/Intermediate/RazorIRAssert.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Text; +using Microsoft.AspNetCore.Razor.Evolution.Legacy; using Xunit; using Xunit.Sdk; @@ -188,6 +189,77 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate } } + internal static void TagHelperFieldDeclaration(RazorIRNode node, params string[] tagHelperTypes) + { + var declareTagHelperFields = Assert.IsType(node); + + try + { + Assert.Equal(tagHelperTypes, declareTagHelperFields.UsedTagHelperTypeNames); + } + catch (XunitException e) + { + throw new IRAssertException(declareTagHelperFields, e.Message); + } + } + + internal static void TagHelperStructure(string tagName, TagMode tagMode, RazorIRNode node) + { + var tagHelperStructureNode = Assert.IsType(node); + + try + { + Assert.Equal(tagName, tagHelperStructureNode.TagName); + Assert.Equal(tagMode, tagHelperStructureNode.TagMode); + } + catch (XunitException e) + { + throw new IRAssertException(tagHelperStructureNode, e.Message); + } + } + + internal static void TagHelperHtmlAttribute( + string name, + HtmlAttributeValueStyle valueStyle, + RazorIRNode node, + params Action[] valueValidators) + { + var tagHelperHtmlAttribute = Assert.IsType(node); + + try + { + Assert.Equal(name, tagHelperHtmlAttribute.Name); + Assert.Equal(valueStyle, tagHelperHtmlAttribute.ValueStyle); + Children(tagHelperHtmlAttribute, valueValidators); + } + catch (XunitException e) + { + throw new IRAssertException(tagHelperHtmlAttribute, tagHelperHtmlAttribute.Children, e.Message, e); + } + } + + internal static void SetTagHelperProperty( + string name, + string propertyName, + HtmlAttributeValueStyle valueStyle, + RazorIRNode node, + params Action[] valueValidators) + { + var tagHelperBoundAttribute = Assert.IsType(node); + + try + { + Assert.Equal(name, tagHelperBoundAttribute.AttributeName); + Assert.Equal(propertyName, tagHelperBoundAttribute.PropertyName); + Assert.Equal(valueStyle, tagHelperBoundAttribute.ValueStyle); + Children(tagHelperBoundAttribute, valueValidators); + } + catch (XunitException e) + { + throw new IRAssertException(tagHelperBoundAttribute, tagHelperBoundAttribute.Children, e.Message, e); + } + } + private class IRAssertException : XunitException { public IRAssertException(RazorIRNode node, string userMessage) diff --git a/test/Microsoft.AspNetCore.Razor.Evolution.Test/RazorEngineTest.cs b/test/Microsoft.AspNetCore.Razor.Evolution.Test/RazorEngineTest.cs index 9050249df7..553712d4b0 100644 --- a/test/Microsoft.AspNetCore.Razor.Evolution.Test/RazorEngineTest.cs +++ b/test/Microsoft.AspNetCore.Razor.Evolution.Test/RazorEngineTest.cs @@ -137,8 +137,8 @@ namespace Microsoft.AspNetCore.Razor.Evolution Assert.Collection( features, feature => Assert.IsType(feature), - feature => Assert.IsType(feature), feature => Assert.IsType(feature), + feature => Assert.IsType(feature), feature => Assert.IsType(feature)); } @@ -158,8 +158,8 @@ namespace Microsoft.AspNetCore.Razor.Evolution Assert.Collection( features, feature => Assert.IsType(feature), - feature => Assert.IsType(feature), feature => Assert.IsType(feature), + feature => Assert.IsType(feature), feature => Assert.IsType(feature), feature => Assert.IsType(feature)); } diff --git a/test/Microsoft.AspNetCore.Razor.Evolution.Test/TestFiles/IntegrationTests/TagHelpersIntegrationTest/NestedTagHelpers.cshtml b/test/Microsoft.AspNetCore.Razor.Evolution.Test/TestFiles/IntegrationTests/TagHelpersIntegrationTest/NestedTagHelpers.cshtml new file mode 100644 index 0000000000..e8c8937563 --- /dev/null +++ b/test/Microsoft.AspNetCore.Razor.Evolution.Test/TestFiles/IntegrationTests/TagHelpersIntegrationTest/NestedTagHelpers.cshtml @@ -0,0 +1,5 @@ +@addTagHelper *, TestAssembly +

Hola

+
+ +
\ No newline at end of file diff --git a/test/Microsoft.AspNetCore.Razor.Evolution.Test/TestFiles/IntegrationTests/TagHelpersIntegrationTest/NestedTagHelpers.ir.txt b/test/Microsoft.AspNetCore.Razor.Evolution.Test/TestFiles/IntegrationTests/TagHelpersIntegrationTest/NestedTagHelpers.ir.txt new file mode 100644 index 0000000000..8dc72abdf6 --- /dev/null +++ b/test/Microsoft.AspNetCore.Razor.Evolution.Test/TestFiles/IntegrationTests/TagHelpersIntegrationTest/NestedTagHelpers.ir.txt @@ -0,0 +1,32 @@ +Document - + Checksum - + NamespaceDeclaration - - + UsingStatement - - System + UsingStatement - - System.Threading.Tasks + ClassDeclaration - - - - - + DeclareTagHelperFields - - PTagHelper - FormTagHelper - InputTagHelper + RazorMethodDeclaration - - - - - + HtmlContent - (0:0,0 [0] NestedTagHelpers.cshtml) - + TagHelper - + InitializeTagHelperStructure - - p - TagMode.StartTagAndEndTag + HtmlContent - (43:1,12 [4] NestedTagHelpers.cshtml) - Hola + CreateTagHelper - - PTagHelper + AddTagHelperHtmlAttribute - - someattr - HtmlAttributeValueStyle.Minimized + ExecuteTagHelpers - + HtmlContent - (51:1,20 [2] NestedTagHelpers.cshtml) - \n + TagHelper - + InitializeTagHelperStructure - - form - TagMode.StartTagAndEndTag + HtmlContent - (73:2,20 [6] NestedTagHelpers.cshtml) - \n + TagHelper - + InitializeTagHelperStructure - - input - TagMode.SelfClosing + CreateTagHelper - - InputTagHelper + SetTagHelperProperty - - value - FooProp - HtmlAttributeValueStyle.DoubleQuotes + HtmlContent - (92:3,17 [5] NestedTagHelpers.cshtml) - Hello + AddTagHelperHtmlAttribute - - type - HtmlAttributeValueStyle.SingleQuotes + HtmlContent - (104:3,29 [4] NestedTagHelpers.cshtml) - text + ExecuteTagHelpers - + HtmlContent - (112:3,37 [2] NestedTagHelpers.cshtml) - \n + CreateTagHelper - - FormTagHelper + AddTagHelperHtmlAttribute - - unbound - HtmlAttributeValueStyle.DoubleQuotes + HtmlContent - (68:2,15 [3] NestedTagHelpers.cshtml) - foo + ExecuteTagHelpers - diff --git a/test/Microsoft.AspNetCore.Razor.Evolution.Test/TestFiles/IntegrationTests/TagHelpersIntegrationTest/SimpleTagHelpers.cshtml b/test/Microsoft.AspNetCore.Razor.Evolution.Test/TestFiles/IntegrationTests/TagHelpersIntegrationTest/SimpleTagHelpers.cshtml new file mode 100644 index 0000000000..bd4ce8107a --- /dev/null +++ b/test/Microsoft.AspNetCore.Razor.Evolution.Test/TestFiles/IntegrationTests/TagHelpersIntegrationTest/SimpleTagHelpers.cshtml @@ -0,0 +1,5 @@ +@addTagHelper *, TestAssembly +

Hola

+
+ +
\ No newline at end of file diff --git a/test/Microsoft.AspNetCore.Razor.Evolution.Test/TestFiles/IntegrationTests/TagHelpersIntegrationTest/SimpleTagHelpers.ir.txt b/test/Microsoft.AspNetCore.Razor.Evolution.Test/TestFiles/IntegrationTests/TagHelpersIntegrationTest/SimpleTagHelpers.ir.txt new file mode 100644 index 0000000000..370e9352c7 --- /dev/null +++ b/test/Microsoft.AspNetCore.Razor.Evolution.Test/TestFiles/IntegrationTests/TagHelpersIntegrationTest/SimpleTagHelpers.ir.txt @@ -0,0 +1,18 @@ +Document - + Checksum - + NamespaceDeclaration - - + UsingStatement - - System + UsingStatement - - System.Threading.Tasks + ClassDeclaration - - - - - + DeclareTagHelperFields - - InputTagHelper + RazorMethodDeclaration - - - - - + HtmlContent - (0:0,0 [0] SimpleTagHelpers.cshtml) -

Hola

\n
\n + TagHelper - + InitializeTagHelperStructure - - input - TagMode.SelfClosing + CreateTagHelper - - InputTagHelper + AddTagHelperHtmlAttribute - - value - HtmlAttributeValueStyle.SingleQuotes + HtmlContent - (70:3,18 [5] SimpleTagHelpers.cshtml) - Hello + AddTagHelperHtmlAttribute - - type - HtmlAttributeValueStyle.SingleQuotes + HtmlContent - (83:3,31 [4] SimpleTagHelpers.cshtml) - text + ExecuteTagHelpers - + HtmlContent - (91:3,39 [2] SimpleTagHelpers.cshtml) - \n
diff --git a/test/Microsoft.AspNetCore.Razor.Evolution.Test/TestFiles/IntegrationTests/TagHelpersIntegrationTest/TagHelpersWithBoundAttributes.cshtml b/test/Microsoft.AspNetCore.Razor.Evolution.Test/TestFiles/IntegrationTests/TagHelpersIntegrationTest/TagHelpersWithBoundAttributes.cshtml new file mode 100644 index 0000000000..83023559d3 --- /dev/null +++ b/test/Microsoft.AspNetCore.Razor.Evolution.Test/TestFiles/IntegrationTests/TagHelpersIntegrationTest/TagHelpersWithBoundAttributes.cshtml @@ -0,0 +1,4 @@ +@addTagHelper *, TestAssembly +
+ +
\ No newline at end of file diff --git a/test/Microsoft.AspNetCore.Razor.Evolution.Test/TestFiles/IntegrationTests/TagHelpersIntegrationTest/TagHelpersWithBoundAttributes.ir.txt b/test/Microsoft.AspNetCore.Razor.Evolution.Test/TestFiles/IntegrationTests/TagHelpersIntegrationTest/TagHelpersWithBoundAttributes.ir.txt new file mode 100644 index 0000000000..25adb9722e --- /dev/null +++ b/test/Microsoft.AspNetCore.Razor.Evolution.Test/TestFiles/IntegrationTests/TagHelpersIntegrationTest/TagHelpersWithBoundAttributes.ir.txt @@ -0,0 +1,19 @@ +Document - + Checksum - + NamespaceDeclaration - - + UsingStatement - - System + UsingStatement - - System.Threading.Tasks + ClassDeclaration - - - - - + DeclareTagHelperFields - - InputTagHelper + RazorMethodDeclaration - - - - - + HtmlContent - (0:0,0 [0] TagHelpersWithBoundAttributes.cshtml) -
\n + TagHelper - + InitializeTagHelperStructure - - input - TagMode.SelfClosing + CreateTagHelper - - InputTagHelper + SetTagHelperProperty - - bound - FooProp - HtmlAttributeValueStyle.DoubleQuotes + CSharpExpression - (57:2,18 [5] TagHelpersWithBoundAttributes.cshtml) + CSharpToken - (57:2,18 [5] TagHelpersWithBoundAttributes.cshtml) - Hello + AddTagHelperHtmlAttribute - - type - HtmlAttributeValueStyle.SingleQuotes + HtmlContent - (69:2,30 [4] TagHelpersWithBoundAttributes.cshtml) - text + ExecuteTagHelpers - + HtmlContent - (77:2,38 [2] TagHelpersWithBoundAttributes.cshtml) - \n