Clean up TagHelperDescriptor APIs.
- Removed all design time descriptors and put their API surface into their corresponding descriptor. Part of removing the design time API surface was removing the tracking of `<Remarks>`, it wasn't used so there's on need to track it until we need it. - Removed the Type requirement from `TagHelperDescriptor`. With this separation we'll be able to have abstract `TagHelper`s that aren't based on a class implementation. - Removed Prefix from the `TagHelperDescriptor` API surface. It was a legacy requirement based on how the Razor parser was put together. We can work around this now. - Stripped correlation information from the immediate `TagHelperDescriptor` API surface. Instead this information is now tracked in `TagMatchingRule`s. This change means that you will not have multiple `TagHelperDescriptor`s per `TagHelper`; instead it's all tracked in a single descriptor. A side effect of this change was the transformation of `IsIndexer` => 3 new properties. - Renamed many descriptor types and property names. - Added builder APIs to construct TagHelpers since they're inherently immutable in their API surface. - Added `ITagHelperDescriptorBuilder` to represent `TagHelper`s that are built from an `ITagHelper` implementing class. It re-introduces the `TypeName` association of a `TagHelper`. - Added `ITagHelperBoundAttributeDescriptorBuilder` to represent that an attribute was associated with a property. - Added validation methods to the descriptor builders to enable consumers to validate the current state of the builder and add diagnostics as necessary. - Moved descriptors away from RazorError. - Updated the various comparers to understand the descriptors new API. - Added a new `RazorDiagnosticFactory` abstraction to handle `RazorDiagnostic`s and their corresponding errors/ids etc. This new API should allow for easy addition of new `RazorDiagnostic` errors. - Updated the `DefaultTagHelperDescriptorFactory` to construct `TagHelperDescriptor`s using the new builder APIs and in the new descriptor format (1 descriptor per type). - Updated `ViewComponentTagHelperDescriptorFactory` to construct `TagHelperDescriptor`s with the builder API. - With both factory implementations code was duplicated because the ViewComponent work will be moving outside of Razor once we have the proper hooks. - Updated `TagHelper` binding bits to capture a binding result in order to query which rules appy to a given tag name. Addressed feedback - Update tests to react to new `TagHelperDescriptor` API. - Remove case sensitive comparers and some cleanup - Added TagHelperDescriptorJsonConverter, RazorDiagnosticJsonConverter and added serialization tests
This commit is contained in:
parent
90b48347a5
commit
8f9ff1abd9
|
|
@ -0,0 +1,53 @@
|
||||||
|
// 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 System.Linq;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A metadata class describing a tag helper attribute.
|
||||||
|
/// </summary>
|
||||||
|
public abstract class BoundAttributeDescriptor
|
||||||
|
{
|
||||||
|
protected BoundAttributeDescriptor(string kind)
|
||||||
|
{
|
||||||
|
Kind = kind;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Kind { get; }
|
||||||
|
|
||||||
|
public bool IsIndexerStringProperty { get; protected set; }
|
||||||
|
|
||||||
|
public bool IsEnum { get; protected set; }
|
||||||
|
|
||||||
|
public bool IsStringProperty { get; protected set; }
|
||||||
|
|
||||||
|
public string Name { get; protected set; }
|
||||||
|
|
||||||
|
public string IndexerNamePrefix { get; set; }
|
||||||
|
|
||||||
|
public string TypeName { get; protected set; }
|
||||||
|
|
||||||
|
public string IndexerTypeName { get; protected set; }
|
||||||
|
|
||||||
|
public string Documentation { get; protected set; }
|
||||||
|
|
||||||
|
public string DisplayName { get; protected set; }
|
||||||
|
|
||||||
|
public IReadOnlyList<RazorDiagnostic> Diagnostics { get; protected set; }
|
||||||
|
|
||||||
|
public IReadOnlyDictionary<string, string> Metadata { get; protected set; }
|
||||||
|
|
||||||
|
public bool HasAnyErrors
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var anyErrors = Diagnostics.Any(diagnostic => diagnostic.Severity == RazorDiagnosticSeverity.Error);
|
||||||
|
|
||||||
|
return anyErrors;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,90 @@
|
||||||
|
// 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;
|
||||||
|
using Microsoft.Extensions.Internal;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
|
{
|
||||||
|
internal class BoundAttributeDescriptorComparer : IEqualityComparer<BoundAttributeDescriptor>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A default instance of the <see cref="BoundAttributeDescriptorComparer"/>.
|
||||||
|
/// </summary>
|
||||||
|
public static readonly BoundAttributeDescriptorComparer Default = new BoundAttributeDescriptorComparer();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A default instance of the <see cref="BoundAttributeDescriptorComparer"/> that does case-sensitive comparison.
|
||||||
|
/// </summary>
|
||||||
|
internal static readonly BoundAttributeDescriptorComparer CaseSensitive =
|
||||||
|
new BoundAttributeDescriptorComparer(caseSensitive: true);
|
||||||
|
|
||||||
|
private readonly StringComparer _stringComparer;
|
||||||
|
private readonly StringComparison _stringComparison;
|
||||||
|
|
||||||
|
private BoundAttributeDescriptorComparer(bool caseSensitive = false)
|
||||||
|
{
|
||||||
|
if (caseSensitive)
|
||||||
|
{
|
||||||
|
_stringComparer = StringComparer.Ordinal;
|
||||||
|
_stringComparison = StringComparison.Ordinal;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_stringComparer = StringComparer.OrdinalIgnoreCase;
|
||||||
|
_stringComparison = StringComparison.OrdinalIgnoreCase;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual bool Equals(BoundAttributeDescriptor descriptorX, BoundAttributeDescriptor descriptorY)
|
||||||
|
{
|
||||||
|
if (object.ReferenceEquals(descriptorX, descriptorY))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (descriptorX == null ^ descriptorY == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return descriptorX != null &&
|
||||||
|
string.Equals(descriptorX.Kind, descriptorY.Kind, StringComparison.Ordinal) &&
|
||||||
|
descriptorX.IsIndexerStringProperty == descriptorY.IsIndexerStringProperty &&
|
||||||
|
descriptorX.IsEnum == descriptorY.IsEnum &&
|
||||||
|
string.Equals(descriptorX.Name, descriptorY.Name, _stringComparison) &&
|
||||||
|
string.Equals(descriptorX.IndexerNamePrefix, descriptorY.IndexerNamePrefix, _stringComparison) &&
|
||||||
|
string.Equals(descriptorX.TypeName, descriptorY.TypeName, StringComparison.Ordinal) &&
|
||||||
|
string.Equals(descriptorX.IndexerTypeName, descriptorY.IndexerTypeName, StringComparison.Ordinal) &&
|
||||||
|
string.Equals(descriptorX.Documentation, descriptorY.Documentation, StringComparison.Ordinal) &&
|
||||||
|
string.Equals(descriptorX.DisplayName, descriptorY.DisplayName, StringComparison.Ordinal) &&
|
||||||
|
Enumerable.SequenceEqual(descriptorX.Diagnostics, descriptorY.Diagnostics) &&
|
||||||
|
Enumerable.SequenceEqual(
|
||||||
|
descriptorX.Metadata.OrderBy(propertyX => propertyX.Key, StringComparer.Ordinal),
|
||||||
|
descriptorY.Metadata.OrderBy(propertyY => propertyY.Key, StringComparer.Ordinal));
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual int GetHashCode(BoundAttributeDescriptor descriptor)
|
||||||
|
{
|
||||||
|
if (descriptor == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(descriptor));
|
||||||
|
}
|
||||||
|
|
||||||
|
var hashCodeCombiner = HashCodeCombiner.Start();
|
||||||
|
hashCodeCombiner.Add(descriptor.Kind);
|
||||||
|
hashCodeCombiner.Add(descriptor.IsIndexerStringProperty);
|
||||||
|
hashCodeCombiner.Add(descriptor.IsEnum);
|
||||||
|
hashCodeCombiner.Add(descriptor.Name, _stringComparer);
|
||||||
|
hashCodeCombiner.Add(descriptor.IndexerNamePrefix, _stringComparer);
|
||||||
|
hashCodeCombiner.Add(descriptor.TypeName, StringComparer.Ordinal);
|
||||||
|
hashCodeCombiner.Add(descriptor.IndexerTypeName, StringComparer.Ordinal);
|
||||||
|
hashCodeCombiner.Add(descriptor.Documentation, StringComparer.Ordinal);
|
||||||
|
hashCodeCombiner.Add(descriptor.DisplayName, StringComparer.Ordinal);
|
||||||
|
|
||||||
|
return hashCodeCombiner.CombinedHash;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -191,7 +191,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration
|
||||||
{
|
{
|
||||||
var tagHelperVariableName = GetTagHelperVariableName(node.TagHelperTypeName);
|
var tagHelperVariableName = GetTagHelperVariableName(node.TagHelperTypeName);
|
||||||
var tagHelperRenderingContext = Context.TagHelperRenderingContext;
|
var tagHelperRenderingContext = Context.TagHelperRenderingContext;
|
||||||
var propertyValueAccessor = GetTagHelperPropertyAccessor(tagHelperVariableName, node.AttributeName, node.Descriptor);
|
var propertyValueAccessor = GetTagHelperPropertyAccessor(node.IsIndexerNameMatch, tagHelperVariableName, node.AttributeName, node.Descriptor);
|
||||||
|
|
||||||
string previousValueAccessor;
|
string previousValueAccessor;
|
||||||
if (tagHelperRenderingContext.RenderedBoundAttributes.TryGetValue(node.AttributeName, out previousValueAccessor))
|
if (tagHelperRenderingContext.RenderedBoundAttributes.TryGetValue(node.AttributeName, out previousValueAccessor))
|
||||||
|
|
@ -208,7 +208,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration
|
||||||
tagHelperRenderingContext.RenderedBoundAttributes[node.AttributeName] = propertyValueAccessor;
|
tagHelperRenderingContext.RenderedBoundAttributes[node.AttributeName] = propertyValueAccessor;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node.Descriptor.IsStringProperty)
|
if (node.Descriptor.IsStringProperty || (node.IsIndexerNameMatch && node.Descriptor.IsIndexerStringProperty))
|
||||||
{
|
{
|
||||||
VisitDefault(node);
|
VisitDefault(node);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -83,15 +83,16 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration
|
||||||
protected static string GetTagHelperVariableName(string tagHelperTypeName) => "__" + tagHelperTypeName.Replace('.', '_');
|
protected static string GetTagHelperVariableName(string tagHelperTypeName) => "__" + tagHelperTypeName.Replace('.', '_');
|
||||||
|
|
||||||
protected static string GetTagHelperPropertyAccessor(
|
protected static string GetTagHelperPropertyAccessor(
|
||||||
|
bool isIndexerNameMatch,
|
||||||
string tagHelperVariableName,
|
string tagHelperVariableName,
|
||||||
string attributeName,
|
string attributeName,
|
||||||
TagHelperAttributeDescriptor descriptor)
|
BoundAttributeDescriptor descriptor)
|
||||||
{
|
{
|
||||||
var propertyAccessor = $"{tagHelperVariableName}.{descriptor.PropertyName}";
|
var propertyAccessor = $"{tagHelperVariableName}.{descriptor.Metadata[ITagHelperBoundAttributeDescriptorBuilder.PropertyNameKey]}";
|
||||||
|
|
||||||
if (descriptor.IsIndexer)
|
if (isIndexerNameMatch)
|
||||||
{
|
{
|
||||||
var dictionaryKey = attributeName.Substring(descriptor.Name.Length);
|
var dictionaryKey = attributeName.Substring(descriptor.IndexerNamePrefix.Length);
|
||||||
propertyAccessor += $"[\"{dictionaryKey}\"]";
|
propertyAccessor += $"[\"{dictionaryKey}\"]";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -373,7 +373,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration
|
||||||
public override void VisitSetPreallocatedTagHelperProperty(SetPreallocatedTagHelperPropertyIRNode node)
|
public override void VisitSetPreallocatedTagHelperProperty(SetPreallocatedTagHelperPropertyIRNode node)
|
||||||
{
|
{
|
||||||
var tagHelperVariableName = GetTagHelperVariableName(node.TagHelperTypeName);
|
var tagHelperVariableName = GetTagHelperVariableName(node.TagHelperTypeName);
|
||||||
var propertyValueAccessor = GetTagHelperPropertyAccessor(tagHelperVariableName, node.AttributeName, node.Descriptor);
|
var propertyValueAccessor = GetTagHelperPropertyAccessor(node.IsIndexerNameMatch, tagHelperVariableName, node.AttributeName, node.Descriptor);
|
||||||
var attributeValueAccessor = $"{node.VariableName}.Value" /* ORIGINAL: TagHelperAttributeValuePropertyName */;
|
var attributeValueAccessor = $"{node.VariableName}.Value" /* ORIGINAL: TagHelperAttributeValuePropertyName */;
|
||||||
Context.Writer
|
Context.Writer
|
||||||
.WriteStartAssignment(propertyValueAccessor)
|
.WriteStartAssignment(propertyValueAccessor)
|
||||||
|
|
@ -391,17 +391,18 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration
|
||||||
{
|
{
|
||||||
var tagHelperVariableName = GetTagHelperVariableName(node.TagHelperTypeName);
|
var tagHelperVariableName = GetTagHelperVariableName(node.TagHelperTypeName);
|
||||||
var tagHelperRenderingContext = Context.TagHelperRenderingContext;
|
var tagHelperRenderingContext = Context.TagHelperRenderingContext;
|
||||||
|
var propertyName = node.Descriptor.Metadata[ITagHelperBoundAttributeDescriptorBuilder.PropertyNameKey];
|
||||||
|
|
||||||
// Ensure that the property we're trying to set has initialized its dictionary bound properties.
|
// Ensure that the property we're trying to set has initialized its dictionary bound properties.
|
||||||
if (node.Descriptor.IsIndexer &&
|
if (node.IsIndexerNameMatch &&
|
||||||
tagHelperRenderingContext.VerifiedPropertyDictionaries.Add(node.Descriptor.PropertyName))
|
tagHelperRenderingContext.VerifiedPropertyDictionaries.Add(propertyName))
|
||||||
{
|
{
|
||||||
// Throw a reasonable Exception at runtime if the dictionary property is null.
|
// Throw a reasonable Exception at runtime if the dictionary property is null.
|
||||||
Context.Writer
|
Context.Writer
|
||||||
.Write("if (")
|
.Write("if (")
|
||||||
.Write(tagHelperVariableName)
|
.Write(tagHelperVariableName)
|
||||||
.Write(".")
|
.Write(".")
|
||||||
.Write(node.Descriptor.PropertyName)
|
.Write(propertyName)
|
||||||
.WriteLine(" == null)");
|
.WriteLine(" == null)");
|
||||||
using (Context.Writer.BuildScope())
|
using (Context.Writer.BuildScope())
|
||||||
{
|
{
|
||||||
|
|
@ -415,13 +416,13 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration
|
||||||
.WriteParameterSeparator()
|
.WriteParameterSeparator()
|
||||||
.WriteStringLiteral(node.TagHelperTypeName)
|
.WriteStringLiteral(node.TagHelperTypeName)
|
||||||
.WriteParameterSeparator()
|
.WriteParameterSeparator()
|
||||||
.WriteStringLiteral(node.Descriptor.PropertyName)
|
.WriteStringLiteral(propertyName)
|
||||||
.WriteEndMethodInvocation(endLine: false) // End of method call
|
.WriteEndMethodInvocation(endLine: false) // End of method call
|
||||||
.WriteEndMethodInvocation(); // End of new expression / throw statement
|
.WriteEndMethodInvocation(); // End of new expression / throw statement
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var propertyValueAccessor = GetTagHelperPropertyAccessor(tagHelperVariableName, node.AttributeName, node.Descriptor);
|
var propertyValueAccessor = GetTagHelperPropertyAccessor(node.IsIndexerNameMatch, tagHelperVariableName, node.AttributeName, node.Descriptor);
|
||||||
|
|
||||||
string previousValueAccessor;
|
string previousValueAccessor;
|
||||||
if (tagHelperRenderingContext.RenderedBoundAttributes.TryGetValue(node.AttributeName, out previousValueAccessor))
|
if (tagHelperRenderingContext.RenderedBoundAttributes.TryGetValue(node.AttributeName, out previousValueAccessor))
|
||||||
|
|
@ -438,7 +439,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration
|
||||||
tagHelperRenderingContext.RenderedBoundAttributes[node.AttributeName] = propertyValueAccessor;
|
tagHelperRenderingContext.RenderedBoundAttributes[node.AttributeName] = propertyValueAccessor;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node.Descriptor.IsStringProperty)
|
if (node.Descriptor.IsStringProperty || (node.IsIndexerNameMatch && node.Descriptor.IsIndexerStringProperty))
|
||||||
{
|
{
|
||||||
Context.Writer.WriteMethodInvocation("BeginWriteTagHelperAttribute" /* ORIGINAL: BeginWriteTagHelperAttributeMethodName */);
|
Context.Writer.WriteMethodInvocation("BeginWriteTagHelperAttribute" /* ORIGINAL: BeginWriteTagHelperAttributeMethodName */);
|
||||||
|
|
||||||
|
|
@ -683,8 +684,11 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration
|
||||||
else if (node is TemplateIRNode)
|
else if (node is TemplateIRNode)
|
||||||
{
|
{
|
||||||
var attributeValueNode = (SetTagHelperPropertyIRNode)node.Parent;
|
var attributeValueNode = (SetTagHelperPropertyIRNode)node.Parent;
|
||||||
|
var expectedTypeName = attributeValueNode.IsIndexerNameMatch ?
|
||||||
|
attributeValueNode.Descriptor.IndexerTypeName :
|
||||||
|
attributeValueNode.Descriptor.TypeName;
|
||||||
var error = new RazorError(
|
var error = new RazorError(
|
||||||
LegacyResources.FormatTagHelpers_InlineMarkupBlocks_NotSupported_InAttributes(attributeValueNode.Descriptor.TypeName),
|
LegacyResources.FormatTagHelpers_InlineMarkupBlocks_NotSupported_InAttributes(expectedTypeName),
|
||||||
new SourceLocation(documentLocation.AbsoluteIndex, documentLocation.CharacterIndex, documentLocation.Length),
|
new SourceLocation(documentLocation.AbsoluteIndex, documentLocation.CharacterIndex, documentLocation.Length),
|
||||||
documentLocation.Length);
|
documentLocation.Length);
|
||||||
Context.Diagnostics.Add(RazorDiagnostic.Create(error));
|
Context.Diagnostics.Add(RazorDiagnostic.Create(error));
|
||||||
|
|
|
||||||
|
|
@ -18,8 +18,8 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
ThrowForMissingDependency(syntaxTree);
|
ThrowForMissingDependency(syntaxTree);
|
||||||
|
|
||||||
var builder = RazorIRBuilder.Document();
|
var builder = RazorIRBuilder.Document();
|
||||||
|
|
||||||
var document = (DocumentIRNode)builder.Current;
|
var document = (DocumentIRNode)builder.Current;
|
||||||
|
|
||||||
document.Options = syntaxTree.Options;
|
document.Options = syntaxTree.Options;
|
||||||
|
|
||||||
var namespaces = new HashSet<string>();
|
var namespaces = new HashSet<string>();
|
||||||
|
|
@ -56,7 +56,8 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var visitor = new MainSourceVisitor(document, builder, namespaces)
|
var tagHelperPrefix = codeDocument.GetTagHelperPrefix();
|
||||||
|
var visitor = new MainSourceVisitor(document, builder, namespaces, tagHelperPrefix)
|
||||||
{
|
{
|
||||||
FileName = syntaxTree.Source.FileName,
|
FileName = syntaxTree.Source.FileName,
|
||||||
};
|
};
|
||||||
|
|
@ -220,10 +221,12 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
private class MainSourceVisitor : LoweringVisitor
|
private class MainSourceVisitor : LoweringVisitor
|
||||||
{
|
{
|
||||||
private DeclareTagHelperFieldsIRNode _tagHelperFields;
|
private DeclareTagHelperFieldsIRNode _tagHelperFields;
|
||||||
|
private readonly string _tagHelperPrefix;
|
||||||
|
|
||||||
public MainSourceVisitor(DocumentIRNode document, RazorIRBuilder builder, HashSet<string> namespaces)
|
public MainSourceVisitor(DocumentIRNode document, RazorIRBuilder builder, HashSet<string> namespaces, string tagHelperPrefix)
|
||||||
: base(document, builder, namespaces)
|
: base(document, builder, namespaces)
|
||||||
{
|
{
|
||||||
|
_tagHelperPrefix = tagHelperPrefix;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void VisitDirectiveToken(DirectiveTokenChunkGenerator chunkGenerator, Span span)
|
public override void VisitDirectiveToken(DirectiveTokenChunkGenerator chunkGenerator, Span span)
|
||||||
|
|
@ -467,9 +470,9 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
});
|
});
|
||||||
|
|
||||||
var tagName = tagHelperBlock.TagName;
|
var tagName = tagHelperBlock.TagName;
|
||||||
if (tagHelperBlock.Descriptors.First().Prefix != null)
|
if (_tagHelperPrefix != null)
|
||||||
{
|
{
|
||||||
tagName = tagName.Substring(tagHelperBlock.Descriptors.First().Prefix.Length);
|
tagName = tagName.Substring(_tagHelperPrefix.Length);
|
||||||
}
|
}
|
||||||
|
|
||||||
_builder.Push(new InitializeTagHelperStructureIRNode()
|
_builder.Push(new InitializeTagHelperStructureIRNode()
|
||||||
|
|
@ -482,8 +485,8 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
|
|
||||||
_builder.Pop(); // Pop InitializeTagHelperStructureIRNode
|
_builder.Pop(); // Pop InitializeTagHelperStructureIRNode
|
||||||
|
|
||||||
AddTagHelperCreation(tagHelperBlock.Descriptors);
|
AddTagHelperCreation(tagHelperBlock.Binding);
|
||||||
AddTagHelperAttributes(tagHelperBlock.Attributes, tagHelperBlock.Descriptors);
|
AddTagHelperAttributes(tagHelperBlock.Attributes, tagHelperBlock.Binding);
|
||||||
AddExecuteTagHelpers();
|
AddExecuteTagHelpers();
|
||||||
|
|
||||||
_builder.Pop(); // Pop TagHelperIRNode
|
_builder.Pop(); // Pop TagHelperIRNode
|
||||||
|
|
@ -497,19 +500,22 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
_document.Children.Add(_tagHelperFields);
|
_document.Children.Add(_tagHelperFields);
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var descriptor in block.Descriptors)
|
foreach (var descriptor in block.Binding.Descriptors)
|
||||||
{
|
{
|
||||||
_tagHelperFields.UsedTagHelperTypeNames.Add(descriptor.TypeName);
|
var typeName = descriptor.Metadata[ITagHelperDescriptorBuilder.TypeNameKey];
|
||||||
|
_tagHelperFields.UsedTagHelperTypeNames.Add(typeName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddTagHelperCreation(IEnumerable<TagHelperDescriptor> descriptors)
|
private void AddTagHelperCreation(TagHelperBinding tagHelperBinding)
|
||||||
{
|
{
|
||||||
|
var descriptors = tagHelperBinding.Descriptors;
|
||||||
foreach (var descriptor in descriptors)
|
foreach (var descriptor in descriptors)
|
||||||
{
|
{
|
||||||
|
var typeName = descriptor.Metadata[ITagHelperDescriptorBuilder.TypeNameKey];
|
||||||
var createTagHelper = new CreateTagHelperIRNode()
|
var createTagHelper = new CreateTagHelperIRNode()
|
||||||
{
|
{
|
||||||
TagHelperTypeName = descriptor.TypeName,
|
TagHelperTypeName = typeName,
|
||||||
Descriptor = descriptor
|
Descriptor = descriptor
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -517,14 +523,15 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddTagHelperAttributes(IList<TagHelperAttributeNode> attributes, IEnumerable<TagHelperDescriptor> descriptors)
|
private void AddTagHelperAttributes(IList<TagHelperAttributeNode> attributes, TagHelperBinding tagHelperBinding)
|
||||||
{
|
{
|
||||||
|
var descriptors = tagHelperBinding.Descriptors;
|
||||||
var renderedBoundAttributeNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
var renderedBoundAttributeNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||||
foreach (var attribute in attributes)
|
foreach (var attribute in attributes)
|
||||||
{
|
{
|
||||||
var attributeValueNode = attribute.Value;
|
var attributeValueNode = attribute.Value;
|
||||||
var associatedDescriptors = descriptors.Where(descriptor =>
|
var associatedDescriptors = descriptors.Where(descriptor =>
|
||||||
descriptor.Attributes.Any(attributeDescriptor => attributeDescriptor.IsNameMatch(attribute.Name)));
|
descriptor.BoundAttributes.Any(attributeDescriptor => attributeDescriptor.CanMatchName(attribute.Name)));
|
||||||
|
|
||||||
if (associatedDescriptors.Any() && renderedBoundAttributeNames.Add(attribute.Name))
|
if (associatedDescriptors.Any() && renderedBoundAttributeNames.Add(attribute.Name))
|
||||||
{
|
{
|
||||||
|
|
@ -537,16 +544,21 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
|
|
||||||
foreach (var associatedDescriptor in associatedDescriptors)
|
foreach (var associatedDescriptor in associatedDescriptors)
|
||||||
{
|
{
|
||||||
var associatedAttributeDescriptor = associatedDescriptor.Attributes.First(
|
var associatedAttributeDescriptor = associatedDescriptor.BoundAttributes.First(
|
||||||
attributeDescriptor => attributeDescriptor.IsNameMatch(attribute.Name));
|
attributeDescriptor => attributeDescriptor.CanMatchName(attribute.Name));
|
||||||
|
var tagHelperTypeName = associatedDescriptor.Metadata[ITagHelperDescriptorBuilder.TypeNameKey];
|
||||||
|
var attributePropertyName = associatedAttributeDescriptor.Metadata[ITagHelperBoundAttributeDescriptorBuilder.PropertyNameKey];
|
||||||
|
|
||||||
var setTagHelperProperty = new SetTagHelperPropertyIRNode()
|
var setTagHelperProperty = new SetTagHelperPropertyIRNode()
|
||||||
{
|
{
|
||||||
PropertyName = associatedAttributeDescriptor.PropertyName,
|
PropertyName = attributePropertyName,
|
||||||
AttributeName = attribute.Name,
|
AttributeName = attribute.Name,
|
||||||
TagHelperTypeName = associatedDescriptor.TypeName,
|
TagHelperTypeName = tagHelperTypeName,
|
||||||
Descriptor = associatedAttributeDescriptor,
|
Descriptor = associatedAttributeDescriptor,
|
||||||
|
Binding = tagHelperBinding,
|
||||||
ValueStyle = attribute.ValueStyle,
|
ValueStyle = attribute.ValueStyle,
|
||||||
Source = BuildSourceSpanFromNode(attributeValueNode)
|
Source = BuildSourceSpanFromNode(attributeValueNode),
|
||||||
|
IsIndexerNameMatch = associatedAttributeDescriptor.IsIndexerNameMatch(attribute.Name),
|
||||||
};
|
};
|
||||||
|
|
||||||
_builder.Push(setTagHelperProperty);
|
_builder.Push(setTagHelperProperty);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,281 @@
|
||||||
|
// 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
|
||||||
|
{
|
||||||
|
public sealed class ITagHelperBoundAttributeDescriptorBuilder
|
||||||
|
{
|
||||||
|
public static readonly string DescriptorKind = "ITagHelper";
|
||||||
|
public static readonly string PropertyNameKey = "ITagHelper.PropertyName";
|
||||||
|
|
||||||
|
private static readonly IReadOnlyDictionary<string, string> PrimitiveDisplayTypeNameLookups = new Dictionary<string, string>(StringComparer.Ordinal)
|
||||||
|
{
|
||||||
|
[typeof(byte).FullName] = "byte",
|
||||||
|
[typeof(sbyte).FullName] = "sbyte",
|
||||||
|
[typeof(int).FullName] = "int",
|
||||||
|
[typeof(uint).FullName] = "uint",
|
||||||
|
[typeof(short).FullName] = "short",
|
||||||
|
[typeof(ushort).FullName] = "ushort",
|
||||||
|
[typeof(long).FullName] = "long",
|
||||||
|
[typeof(ulong).FullName] = "ulong",
|
||||||
|
[typeof(float).FullName] = "float",
|
||||||
|
[typeof(double).FullName] = "double",
|
||||||
|
[typeof(char).FullName] = "char",
|
||||||
|
[typeof(bool).FullName] = "bool",
|
||||||
|
[typeof(object).FullName] = "object",
|
||||||
|
[typeof(string).FullName] = "string",
|
||||||
|
[typeof(decimal).FullName] = "decimal",
|
||||||
|
};
|
||||||
|
|
||||||
|
private static ICollection<char> InvalidNonWhitespaceAttributeNameCharacters { get; } = new HashSet<char>(
|
||||||
|
new[] { '@', '!', '<', '/', '?', '[', '>', ']', '=', '"', '\'', '*' });
|
||||||
|
|
||||||
|
private bool _isEnum;
|
||||||
|
private string _indexerValueTypeName;
|
||||||
|
private string _name;
|
||||||
|
private string _propertyName;
|
||||||
|
private string _typeName;
|
||||||
|
private string _documentation;
|
||||||
|
private string _indexerNamePrefix;
|
||||||
|
private readonly string _containingTypeName;
|
||||||
|
private readonly Dictionary<string, string> _metadata;
|
||||||
|
private HashSet<RazorDiagnostic> _diagnostics;
|
||||||
|
|
||||||
|
private ITagHelperBoundAttributeDescriptorBuilder(string containingTypeName)
|
||||||
|
{
|
||||||
|
_containingTypeName = containingTypeName;
|
||||||
|
_metadata = new Dictionary<string, string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ITagHelperBoundAttributeDescriptorBuilder Create(string containingTypeName)
|
||||||
|
{
|
||||||
|
return new ITagHelperBoundAttributeDescriptorBuilder(containingTypeName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ITagHelperBoundAttributeDescriptorBuilder Name(string name)
|
||||||
|
{
|
||||||
|
_name = name;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ITagHelperBoundAttributeDescriptorBuilder PropertyName(string propertyName)
|
||||||
|
{
|
||||||
|
_propertyName = propertyName;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ITagHelperBoundAttributeDescriptorBuilder TypeName(string typeName)
|
||||||
|
{
|
||||||
|
_typeName = typeName;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ITagHelperBoundAttributeDescriptorBuilder AsEnum()
|
||||||
|
{
|
||||||
|
_isEnum = true;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ITagHelperBoundAttributeDescriptorBuilder AsDictionary(string attributeNamePrefix, string valueTypeName)
|
||||||
|
{
|
||||||
|
_indexerNamePrefix = attributeNamePrefix;
|
||||||
|
_indexerValueTypeName = valueTypeName;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ITagHelperBoundAttributeDescriptorBuilder Documentation(string documentation)
|
||||||
|
{
|
||||||
|
_documentation = documentation;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ITagHelperBoundAttributeDescriptorBuilder AddMetadata(string key, string value)
|
||||||
|
{
|
||||||
|
_metadata[key] = value;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ITagHelperBoundAttributeDescriptorBuilder AddDiagnostic(RazorDiagnostic diagnostic)
|
||||||
|
{
|
||||||
|
EnsureDiagnostics();
|
||||||
|
_diagnostics.Add(diagnostic);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BoundAttributeDescriptor Build()
|
||||||
|
{
|
||||||
|
var validationDiagnostics = Validate();
|
||||||
|
var diagnostics = new HashSet<RazorDiagnostic>(validationDiagnostics);
|
||||||
|
if (_diagnostics != null)
|
||||||
|
{
|
||||||
|
diagnostics.UnionWith(_diagnostics);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!PrimitiveDisplayTypeNameLookups.TryGetValue(_typeName, out var simpleName))
|
||||||
|
{
|
||||||
|
simpleName = _typeName;
|
||||||
|
}
|
||||||
|
|
||||||
|
var displayName = $"{simpleName} {_containingTypeName}.{_propertyName}";
|
||||||
|
var descriptor = new ITagHelperBoundAttributeDescriptor(
|
||||||
|
_isEnum,
|
||||||
|
_name,
|
||||||
|
_propertyName,
|
||||||
|
_typeName,
|
||||||
|
_indexerNamePrefix,
|
||||||
|
_indexerValueTypeName,
|
||||||
|
_documentation,
|
||||||
|
displayName,
|
||||||
|
_metadata,
|
||||||
|
diagnostics);
|
||||||
|
|
||||||
|
return descriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Reset()
|
||||||
|
{
|
||||||
|
_name = null;
|
||||||
|
_propertyName = null;
|
||||||
|
_typeName = null;
|
||||||
|
_documentation = null;
|
||||||
|
_isEnum = false;
|
||||||
|
_indexerNamePrefix = null;
|
||||||
|
_indexerValueTypeName = null;
|
||||||
|
_metadata.Clear();
|
||||||
|
_diagnostics?.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerable<RazorDiagnostic> Validate()
|
||||||
|
{
|
||||||
|
// data-* attributes are explicitly not implemented by user agents and are not intended for use on
|
||||||
|
// the server; therefore it's invalid for TagHelpers to bind to them.
|
||||||
|
const string DataDashPrefix = "data-";
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(_name))
|
||||||
|
{
|
||||||
|
if (_indexerNamePrefix == null)
|
||||||
|
{
|
||||||
|
var diagnostic = RazorDiagnosticFactory.CreateTagHelper_InvalidBoundAttributeNullOrWhitespace(
|
||||||
|
_containingTypeName,
|
||||||
|
_propertyName);
|
||||||
|
|
||||||
|
yield return diagnostic;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (_name.StartsWith(DataDashPrefix, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
var diagnostic = RazorDiagnosticFactory.CreateTagHelper_InvalidBoundAttributeNameStartsWith(
|
||||||
|
_containingTypeName,
|
||||||
|
_propertyName,
|
||||||
|
_name);
|
||||||
|
|
||||||
|
yield return diagnostic;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var character in _name)
|
||||||
|
{
|
||||||
|
if (char.IsWhiteSpace(character) || InvalidNonWhitespaceAttributeNameCharacters.Contains(character))
|
||||||
|
{
|
||||||
|
var diagnostic = RazorDiagnosticFactory.CreateTagHelper_InvalidBoundAttributeName(
|
||||||
|
_containingTypeName,
|
||||||
|
_propertyName,
|
||||||
|
_name,
|
||||||
|
character);
|
||||||
|
|
||||||
|
yield return diagnostic;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_indexerNamePrefix != null)
|
||||||
|
{
|
||||||
|
if (_indexerNamePrefix.StartsWith(DataDashPrefix, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
var diagnostic = RazorDiagnosticFactory.CreateTagHelper_InvalidBoundAttributePrefixStartsWith(
|
||||||
|
_containingTypeName,
|
||||||
|
_propertyName,
|
||||||
|
_indexerNamePrefix);
|
||||||
|
|
||||||
|
yield return diagnostic;
|
||||||
|
}
|
||||||
|
else if (_indexerNamePrefix.Length > 0 && string.IsNullOrWhiteSpace(_indexerNamePrefix))
|
||||||
|
{
|
||||||
|
var diagnostic = RazorDiagnosticFactory.CreateTagHelper_InvalidBoundAttributeNullOrWhitespace(
|
||||||
|
_containingTypeName,
|
||||||
|
_propertyName);
|
||||||
|
|
||||||
|
yield return diagnostic;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach (var character in _indexerNamePrefix)
|
||||||
|
{
|
||||||
|
if (char.IsWhiteSpace(character) || InvalidNonWhitespaceAttributeNameCharacters.Contains(character))
|
||||||
|
{
|
||||||
|
var diagnostic = RazorDiagnosticFactory.CreateTagHelper_InvalidBoundAttributePrefix(
|
||||||
|
_containingTypeName,
|
||||||
|
_propertyName,
|
||||||
|
_indexerNamePrefix,
|
||||||
|
character);
|
||||||
|
|
||||||
|
yield return diagnostic;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EnsureDiagnostics()
|
||||||
|
{
|
||||||
|
if (_diagnostics == null)
|
||||||
|
{
|
||||||
|
_diagnostics = new HashSet<RazorDiagnostic>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ITagHelperBoundAttributeDescriptor : BoundAttributeDescriptor
|
||||||
|
{
|
||||||
|
public ITagHelperBoundAttributeDescriptor(
|
||||||
|
bool isEnum,
|
||||||
|
string name,
|
||||||
|
string propertyName,
|
||||||
|
string typeName,
|
||||||
|
string dictionaryAttributeNamePrefix,
|
||||||
|
string dictionaryValueTypeName,
|
||||||
|
string documentation,
|
||||||
|
string displayName,
|
||||||
|
Dictionary<string, string> metadata,
|
||||||
|
IEnumerable<RazorDiagnostic> diagnostics) : base(DescriptorKind)
|
||||||
|
{
|
||||||
|
IsEnum = isEnum;
|
||||||
|
IsIndexerStringProperty = dictionaryValueTypeName == typeof(string).FullName || dictionaryValueTypeName == "string";
|
||||||
|
IsStringProperty = typeName == typeof(string).FullName || typeName == "string";
|
||||||
|
Name = name;
|
||||||
|
TypeName = typeName;
|
||||||
|
IndexerNamePrefix = dictionaryAttributeNamePrefix;
|
||||||
|
IndexerTypeName = dictionaryValueTypeName;
|
||||||
|
Documentation = documentation;
|
||||||
|
DisplayName = displayName;
|
||||||
|
Diagnostics = new List<RazorDiagnostic>(diagnostics);
|
||||||
|
Metadata = new Dictionary<string, string>(metadata)
|
||||||
|
{
|
||||||
|
[PropertyNameKey] = propertyName
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,263 @@
|
||||||
|
// 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;
|
||||||
|
using Microsoft.AspNetCore.Razor.Evolution.Legacy;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
|
{
|
||||||
|
public sealed class ITagHelperDescriptorBuilder
|
||||||
|
{
|
||||||
|
public static readonly string DescriptorKind = "ITagHelper";
|
||||||
|
public static readonly string TypeNameKey = "ITagHelper.TypeName";
|
||||||
|
|
||||||
|
private static ICollection<char> InvalidNonWhitespaceAllowedChildCharacters { get; } = new HashSet<char>(
|
||||||
|
new[] { '@', '!', '<', '/', '?', '[', '>', ']', '=', '"', '\'', '*' });
|
||||||
|
|
||||||
|
private string _documentation;
|
||||||
|
private string _tagOutputHint;
|
||||||
|
private HashSet<string> _allowedChildTags;
|
||||||
|
private HashSet<BoundAttributeDescriptor> _attributeDescriptors;
|
||||||
|
private HashSet<TagMatchingRule> _tagMatchingRules;
|
||||||
|
private readonly string _assemblyName;
|
||||||
|
private readonly string _typeName;
|
||||||
|
private readonly Dictionary<string, string> _metadata;
|
||||||
|
private HashSet<RazorDiagnostic> _diagnostics;
|
||||||
|
|
||||||
|
private ITagHelperDescriptorBuilder(string typeName, string assemblyName)
|
||||||
|
{
|
||||||
|
_typeName = typeName;
|
||||||
|
_assemblyName = assemblyName;
|
||||||
|
_metadata = new Dictionary<string, string>(StringComparer.Ordinal);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ITagHelperDescriptorBuilder Create(string typeName, string assemblyName)
|
||||||
|
{
|
||||||
|
return new ITagHelperDescriptorBuilder(typeName, assemblyName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ITagHelperDescriptorBuilder BindAttribute(BoundAttributeDescriptor descriptor)
|
||||||
|
{
|
||||||
|
if (descriptor == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(descriptor));
|
||||||
|
}
|
||||||
|
|
||||||
|
EnsureAttributeDescriptors();
|
||||||
|
_attributeDescriptors.Add(descriptor);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ITagHelperDescriptorBuilder BindAttribute(Action<ITagHelperBoundAttributeDescriptorBuilder> configure)
|
||||||
|
{
|
||||||
|
if (configure == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(configure));
|
||||||
|
}
|
||||||
|
|
||||||
|
var builder = ITagHelperBoundAttributeDescriptorBuilder.Create(_typeName);
|
||||||
|
|
||||||
|
configure(builder);
|
||||||
|
|
||||||
|
var attributeDescriptor = builder.Build();
|
||||||
|
|
||||||
|
return BindAttribute(attributeDescriptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ITagHelperDescriptorBuilder TagMatchingRule(TagMatchingRule rule)
|
||||||
|
{
|
||||||
|
if (rule == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(rule));
|
||||||
|
}
|
||||||
|
|
||||||
|
EnsureTagMatchingRules();
|
||||||
|
_tagMatchingRules.Add(rule);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ITagHelperDescriptorBuilder TagMatchingRule(Action<TagMatchingRuleBuilder> configure)
|
||||||
|
{
|
||||||
|
if (configure == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(configure));
|
||||||
|
}
|
||||||
|
|
||||||
|
var builder = TagMatchingRuleBuilder.Create();
|
||||||
|
|
||||||
|
configure(builder);
|
||||||
|
|
||||||
|
var rule = builder.Build();
|
||||||
|
|
||||||
|
return TagMatchingRule(rule);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ITagHelperDescriptorBuilder AllowChildTag(string allowedChild)
|
||||||
|
{
|
||||||
|
EnsureAllowedChildTags();
|
||||||
|
_allowedChildTags.Add(allowedChild);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ITagHelperDescriptorBuilder TagOutputHint(string hint)
|
||||||
|
{
|
||||||
|
_tagOutputHint = hint;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ITagHelperDescriptorBuilder Documentation(string documentation)
|
||||||
|
{
|
||||||
|
_documentation = documentation;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ITagHelperDescriptorBuilder AddMetadata(string key, string value)
|
||||||
|
{
|
||||||
|
_metadata[key] = value;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ITagHelperDescriptorBuilder AddDiagnostic(RazorDiagnostic diagnostic)
|
||||||
|
{
|
||||||
|
EnsureDiagnostics();
|
||||||
|
_diagnostics.Add(diagnostic);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TagHelperDescriptor Build()
|
||||||
|
{
|
||||||
|
var validationDiagnostics = Validate();
|
||||||
|
var diagnostics = new HashSet<RazorDiagnostic>(validationDiagnostics);
|
||||||
|
if (_diagnostics != null)
|
||||||
|
{
|
||||||
|
diagnostics.UnionWith(_diagnostics);
|
||||||
|
}
|
||||||
|
|
||||||
|
var descriptor = new ITagHelperDescriptor(
|
||||||
|
_typeName,
|
||||||
|
_assemblyName,
|
||||||
|
_typeName /* Name */,
|
||||||
|
_typeName /* DisplayName */,
|
||||||
|
_documentation,
|
||||||
|
_tagOutputHint,
|
||||||
|
_tagMatchingRules ?? Enumerable.Empty<TagMatchingRule>(),
|
||||||
|
_attributeDescriptors ?? Enumerable.Empty<BoundAttributeDescriptor>(),
|
||||||
|
_allowedChildTags ?? Enumerable.Empty<string>(),
|
||||||
|
_metadata,
|
||||||
|
diagnostics);
|
||||||
|
|
||||||
|
return descriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Reset()
|
||||||
|
{
|
||||||
|
_documentation = null;
|
||||||
|
_tagOutputHint = null;
|
||||||
|
_allowedChildTags?.Clear();
|
||||||
|
_attributeDescriptors?.Clear();
|
||||||
|
_tagMatchingRules?.Clear();
|
||||||
|
_metadata.Clear();
|
||||||
|
_diagnostics?.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerable<RazorDiagnostic> Validate()
|
||||||
|
{
|
||||||
|
if (_allowedChildTags != null)
|
||||||
|
{
|
||||||
|
foreach (var name in _allowedChildTags)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(name))
|
||||||
|
{
|
||||||
|
var diagnostic = RazorDiagnosticFactory.CreateTagHelper_InvalidRestrictedChildNullOrWhitespace(_typeName);
|
||||||
|
|
||||||
|
yield return diagnostic;
|
||||||
|
}
|
||||||
|
else if (name != TagHelperDescriptorProvider.ElementCatchAllTarget)
|
||||||
|
{
|
||||||
|
foreach (var character in name)
|
||||||
|
{
|
||||||
|
if (char.IsWhiteSpace(character) || InvalidNonWhitespaceAllowedChildCharacters.Contains(character))
|
||||||
|
{
|
||||||
|
var diagnostic = RazorDiagnosticFactory.CreateTagHelper_InvalidRestrictedChild(name, _typeName, character);
|
||||||
|
|
||||||
|
yield return diagnostic;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EnsureAttributeDescriptors()
|
||||||
|
{
|
||||||
|
if (_attributeDescriptors == null)
|
||||||
|
{
|
||||||
|
_attributeDescriptors = new HashSet<BoundAttributeDescriptor>(BoundAttributeDescriptorComparer.Default);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EnsureTagMatchingRules()
|
||||||
|
{
|
||||||
|
if (_tagMatchingRules == null)
|
||||||
|
{
|
||||||
|
_tagMatchingRules = new HashSet<TagMatchingRule>(TagMatchingRuleComparer.Default);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EnsureAllowedChildTags()
|
||||||
|
{
|
||||||
|
if (_allowedChildTags == null)
|
||||||
|
{
|
||||||
|
_allowedChildTags = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EnsureDiagnostics()
|
||||||
|
{
|
||||||
|
if (_diagnostics == null)
|
||||||
|
{
|
||||||
|
_diagnostics = new HashSet<RazorDiagnostic>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ITagHelperDescriptor : TagHelperDescriptor
|
||||||
|
{
|
||||||
|
public ITagHelperDescriptor(
|
||||||
|
string typeName,
|
||||||
|
string assemblyName,
|
||||||
|
string name,
|
||||||
|
string displayName,
|
||||||
|
string documentation,
|
||||||
|
string tagOutputHint,
|
||||||
|
IEnumerable<TagMatchingRule> tagMatchingRules,
|
||||||
|
IEnumerable<BoundAttributeDescriptor> attributeDescriptors,
|
||||||
|
IEnumerable<string> allowedChildTags,
|
||||||
|
Dictionary<string, string> metadata,
|
||||||
|
IEnumerable<RazorDiagnostic> diagnostics) : base(DescriptorKind)
|
||||||
|
{
|
||||||
|
Name = typeName;
|
||||||
|
AssemblyName = assemblyName;
|
||||||
|
DisplayName = displayName;
|
||||||
|
Documentation = documentation;
|
||||||
|
TagOutputHint = tagOutputHint;
|
||||||
|
TagMatchingRules = new List<TagMatchingRule>(tagMatchingRules);
|
||||||
|
BoundAttributes = new List<BoundAttributeDescriptor>(attributeDescriptors);
|
||||||
|
AllowedChildTags = new List<string>(allowedChildTags);
|
||||||
|
Diagnostics = new List<RazorDiagnostic>(diagnostics);
|
||||||
|
Metadata = new Dictionary<string, string>(metadata)
|
||||||
|
{
|
||||||
|
[TypeNameKey] = typeName
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Microsoft.AspNetCore.Razor.Evolution.Legacy;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate
|
namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using Microsoft.AspNetCore.Razor.Evolution.Legacy;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate
|
namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate
|
||||||
{
|
{
|
||||||
|
|
@ -21,7 +22,11 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate
|
||||||
|
|
||||||
public string PropertyName { get; set; }
|
public string PropertyName { get; set; }
|
||||||
|
|
||||||
public TagHelperAttributeDescriptor Descriptor { get; set; }
|
public BoundAttributeDescriptor Descriptor { get; set; }
|
||||||
|
|
||||||
|
public TagHelperBinding Binding { get; set; }
|
||||||
|
|
||||||
|
public bool IsIndexerNameMatch { get; set; }
|
||||||
|
|
||||||
public override void Accept(RazorIRNodeVisitor visitor)
|
public override void Accept(RazorIRNodeVisitor visitor)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,11 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate
|
||||||
|
|
||||||
internal HtmlAttributeValueStyle ValueStyle { get; set; }
|
internal HtmlAttributeValueStyle ValueStyle { get; set; }
|
||||||
|
|
||||||
public TagHelperAttributeDescriptor Descriptor { get; set; }
|
public BoundAttributeDescriptor Descriptor { get; set; }
|
||||||
|
|
||||||
|
public TagHelperBinding Binding { get; set; }
|
||||||
|
|
||||||
|
public bool IsIndexerNameMatch { get; set; }
|
||||||
|
|
||||||
public override void Accept(RazorIRNodeVisitor visitor)
|
public override void Accept(RazorIRNodeVisitor visitor)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,81 @@
|
||||||
|
// 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;
|
||||||
|
using Microsoft.Extensions.Internal;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// An <see cref="IEqualityComparer{TagHelperRequiredAttributeDescriptor}"/> used to check equality between
|
||||||
|
/// two <see cref="RequiredAttributeDescriptor"/>s.
|
||||||
|
/// </summary>
|
||||||
|
internal class RequiredAttributeDescriptorComparer : IEqualityComparer<RequiredAttributeDescriptor>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A default instance of the <see cref="RequiredAttributeDescriptorComparer"/>.
|
||||||
|
/// </summary>
|
||||||
|
public static readonly RequiredAttributeDescriptorComparer Default =
|
||||||
|
new RequiredAttributeDescriptorComparer();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A default instance of the <see cref="RequiredAttributeDescriptorComparer"/> that does case-sensitive comparison.
|
||||||
|
/// </summary>
|
||||||
|
internal static readonly RequiredAttributeDescriptorComparer CaseSensitive =
|
||||||
|
new RequiredAttributeDescriptorComparer(caseSensitive: true);
|
||||||
|
|
||||||
|
private readonly StringComparer _stringComparer;
|
||||||
|
private readonly StringComparison _stringComparison;
|
||||||
|
|
||||||
|
private RequiredAttributeDescriptorComparer(bool caseSensitive = false)
|
||||||
|
{
|
||||||
|
if (caseSensitive)
|
||||||
|
{
|
||||||
|
_stringComparer = StringComparer.Ordinal;
|
||||||
|
_stringComparison = StringComparison.Ordinal;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_stringComparer = StringComparer.OrdinalIgnoreCase;
|
||||||
|
_stringComparison = StringComparison.OrdinalIgnoreCase;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public virtual bool Equals(
|
||||||
|
RequiredAttributeDescriptor descriptorX,
|
||||||
|
RequiredAttributeDescriptor descriptorY)
|
||||||
|
{
|
||||||
|
if (object.ReferenceEquals(descriptorX, descriptorY))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (descriptorX == null ^ descriptorY == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return descriptorX != null &&
|
||||||
|
descriptorX.NameComparison == descriptorY.NameComparison &&
|
||||||
|
descriptorX.ValueComparison == descriptorY.ValueComparison &&
|
||||||
|
string.Equals(descriptorX.Name, descriptorY.Name, _stringComparison) &&
|
||||||
|
string.Equals(descriptorX.Value, descriptorY.Value, StringComparison.Ordinal) &&
|
||||||
|
Enumerable.SequenceEqual(descriptorX.Diagnostics, descriptorY.Diagnostics);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public virtual int GetHashCode(RequiredAttributeDescriptor descriptor)
|
||||||
|
{
|
||||||
|
var hashCodeCombiner = HashCodeCombiner.Start();
|
||||||
|
hashCodeCombiner.Add(descriptor.NameComparison);
|
||||||
|
hashCodeCombiner.Add(descriptor.ValueComparison);
|
||||||
|
hashCodeCombiner.Add(descriptor.Name, _stringComparer);
|
||||||
|
hashCodeCombiner.Add(descriptor.Value, StringComparer.Ordinal);
|
||||||
|
|
||||||
|
return hashCodeCombiner.CombinedHash;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
// 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.Legacy
|
||||||
|
{
|
||||||
|
public sealed class TagHelperBinding
|
||||||
|
{
|
||||||
|
public IReadOnlyDictionary<TagHelperDescriptor, IEnumerable<TagMatchingRule>> _mappings;
|
||||||
|
|
||||||
|
internal TagHelperBinding(IReadOnlyDictionary<TagHelperDescriptor, IEnumerable<TagMatchingRule>> mappings)
|
||||||
|
{
|
||||||
|
_mappings = mappings;
|
||||||
|
Descriptors = _mappings.Keys;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<TagHelperDescriptor> Descriptors { get; }
|
||||||
|
|
||||||
|
public IEnumerable<TagMatchingRule> GetBoundRules(TagHelperDescriptor descriptor)
|
||||||
|
{
|
||||||
|
return _mappings[descriptor];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -25,7 +25,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
: base(source.Type, source.Children, source.ChunkGenerator)
|
: base(source.Type, source.Children, source.ChunkGenerator)
|
||||||
{
|
{
|
||||||
TagName = source.TagName;
|
TagName = source.TagName;
|
||||||
Descriptors = source.Descriptors;
|
Binding = source.BindingResult;
|
||||||
Attributes = new List<TagHelperAttributeNode>(source.Attributes);
|
Attributes = new List<TagHelperAttributeNode>(source.Attributes);
|
||||||
_start = source.Start;
|
_start = source.Start;
|
||||||
TagMode = source.TagMode;
|
TagMode = source.TagMode;
|
||||||
|
|
@ -61,9 +61,9 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
public TagMode TagMode { get; }
|
public TagMode TagMode { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// <see cref="TagHelperDescriptor"/>s for the HTML element.
|
/// <see cref="TagHelperDescriptor"/> bindings for the HTML element.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IEnumerable<TagHelperDescriptor> Descriptors { get; }
|
public TagHelperBinding Binding { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The HTML attributes.
|
/// The HTML attributes.
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
: base(original)
|
: base(original)
|
||||||
{
|
{
|
||||||
TagName = original.TagName;
|
TagName = original.TagName;
|
||||||
Descriptors = original.Descriptors;
|
BindingResult = original.Binding;
|
||||||
Attributes = new List<TagHelperAttributeNode>(original.Attributes);
|
Attributes = new List<TagHelperAttributeNode>(original.Attributes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -31,22 +31,21 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
/// <param name="tagMode">HTML syntax of the element in the Razor source.</param>
|
/// <param name="tagMode">HTML syntax of the element in the Razor source.</param>
|
||||||
/// <param name="start">Starting location of the <see cref="TagHelperBlock"/>.</param>
|
/// <param name="start">Starting location of the <see cref="TagHelperBlock"/>.</param>
|
||||||
/// <param name="attributes">Attributes of the <see cref="TagHelperBlock"/>.</param>
|
/// <param name="attributes">Attributes of the <see cref="TagHelperBlock"/>.</param>
|
||||||
/// <param name="descriptors">The <see cref="TagHelperDescriptor"/>s associated with the current HTML
|
/// <param name="bindingResult"></param>
|
||||||
/// tag.</param>
|
|
||||||
public TagHelperBlockBuilder(
|
public TagHelperBlockBuilder(
|
||||||
string tagName,
|
string tagName,
|
||||||
TagMode tagMode,
|
TagMode tagMode,
|
||||||
SourceLocation start,
|
SourceLocation start,
|
||||||
IList<TagHelperAttributeNode> attributes,
|
IList<TagHelperAttributeNode> attributes,
|
||||||
IEnumerable<TagHelperDescriptor> descriptors)
|
TagHelperBinding bindingResult)
|
||||||
{
|
{
|
||||||
TagName = tagName;
|
TagName = tagName;
|
||||||
TagMode = tagMode;
|
TagMode = tagMode;
|
||||||
Start = start;
|
Start = start;
|
||||||
Descriptors = descriptors;
|
BindingResult = bindingResult;
|
||||||
Attributes = new List<TagHelperAttributeNode>(attributes);
|
Attributes = new List<TagHelperAttributeNode>(attributes);
|
||||||
Type = BlockType.Tag;
|
Type = BlockType.Tag;
|
||||||
ChunkGenerator = new TagHelperChunkGenerator(descriptors);
|
ChunkGenerator = new TagHelperChunkGenerator();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Internal for testing
|
// Internal for testing
|
||||||
|
|
@ -60,7 +59,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
TagMode = tagMode;
|
TagMode = tagMode;
|
||||||
Attributes = attributes;
|
Attributes = attributes;
|
||||||
Type = BlockType.Tag;
|
Type = BlockType.Tag;
|
||||||
ChunkGenerator = new TagHelperChunkGenerator(tagHelperDescriptors: null);
|
ChunkGenerator = new TagHelperChunkGenerator();
|
||||||
|
|
||||||
// Children is IList, no AddRange
|
// Children is IList, no AddRange
|
||||||
foreach (var child in children)
|
foreach (var child in children)
|
||||||
|
|
@ -89,7 +88,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// <see cref="TagHelperDescriptor"/>s for the HTML element.
|
/// <see cref="TagHelperDescriptor"/>s for the HTML element.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IEnumerable<TagHelperDescriptor> Descriptors { get; }
|
public TagHelperBinding BindingResult { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The HTML attributes.
|
/// The HTML attributes.
|
||||||
|
|
|
||||||
|
|
@ -17,28 +17,24 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
string tagName,
|
string tagName,
|
||||||
bool validStructure,
|
bool validStructure,
|
||||||
Block tag,
|
Block tag,
|
||||||
IEnumerable<TagHelperDescriptor> descriptors,
|
TagHelperBinding bindingResult,
|
||||||
ErrorSink errorSink)
|
ErrorSink errorSink)
|
||||||
{
|
{
|
||||||
// There will always be at least one child for the '<'.
|
// There will always be at least one child for the '<'.
|
||||||
var start = tag.Children.First().Start;
|
var start = tag.Children.First().Start;
|
||||||
var attributes = GetTagAttributes(tagName, validStructure, tag, descriptors, errorSink);
|
var attributes = GetTagAttributes(tagName, validStructure, tag, bindingResult, errorSink);
|
||||||
var tagMode = GetTagMode(tagName, tag, descriptors, errorSink);
|
var tagMode = GetTagMode(tagName, tag, bindingResult, errorSink);
|
||||||
|
|
||||||
return new TagHelperBlockBuilder(tagName, tagMode, start, attributes, descriptors);
|
return new TagHelperBlockBuilder(tagName, tagMode, start, attributes, bindingResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IList<TagHelperAttributeNode> GetTagAttributes(
|
private static IList<TagHelperAttributeNode> GetTagAttributes(
|
||||||
string tagName,
|
string tagName,
|
||||||
bool validStructure,
|
bool validStructure,
|
||||||
Block tagBlock,
|
Block tagBlock,
|
||||||
IEnumerable<TagHelperDescriptor> descriptors,
|
TagHelperBinding bindingResult,
|
||||||
ErrorSink errorSink)
|
ErrorSink errorSink)
|
||||||
{
|
{
|
||||||
// Ignore all but one descriptor per type since this method uses the TagHelperDescriptors only to get the
|
|
||||||
// contained TagHelperAttributeDescriptor's.
|
|
||||||
descriptors = descriptors.Distinct(TypeBasedTagHelperDescriptorComparer.Default);
|
|
||||||
|
|
||||||
var attributes = new List<TagHelperAttributeNode>();
|
var attributes = new List<TagHelperAttributeNode>();
|
||||||
|
|
||||||
// We skip the first child "<tagname" and take everything up to the ending portion of the tag ">" or "/>".
|
// We skip the first child "<tagname" and take everything up to the ending portion of the tag ">" or "/>".
|
||||||
|
|
@ -52,11 +48,11 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
TryParseResult result;
|
TryParseResult result;
|
||||||
if (child.IsBlock)
|
if (child.IsBlock)
|
||||||
{
|
{
|
||||||
result = TryParseBlock(tagName, (Block)child, descriptors, errorSink);
|
result = TryParseBlock(tagName, (Block)child, bindingResult.Descriptors, errorSink);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
result = TryParseSpan((Span)child, descriptors, errorSink);
|
result = TryParseSpan((Span)child, bindingResult.Descriptors, errorSink);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only want to track the attribute if we succeeded in parsing its corresponding Block/Span.
|
// Only want to track the attribute if we succeeded in parsing its corresponding Block/Span.
|
||||||
|
|
@ -77,7 +73,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
LegacyResources.FormatRewriterError_EmptyTagHelperBoundAttribute(
|
LegacyResources.FormatRewriterError_EmptyTagHelperBoundAttribute(
|
||||||
result.AttributeName,
|
result.AttributeName,
|
||||||
tagName,
|
tagName,
|
||||||
GetPropertyType(result.AttributeName, descriptors)),
|
GetPropertyType(result.AttributeName, bindingResult.Descriptors)),
|
||||||
result.AttributeName.Length);
|
result.AttributeName.Length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -118,7 +114,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
private static TagMode GetTagMode(
|
private static TagMode GetTagMode(
|
||||||
string tagName,
|
string tagName,
|
||||||
Block beginTagBlock,
|
Block beginTagBlock,
|
||||||
IEnumerable<TagHelperDescriptor> descriptors,
|
TagHelperBinding bindingResult,
|
||||||
ErrorSink errorSink)
|
ErrorSink errorSink)
|
||||||
{
|
{
|
||||||
var childSpan = beginTagBlock.FindLastDescendentSpan();
|
var childSpan = beginTagBlock.FindLastDescendentSpan();
|
||||||
|
|
@ -129,12 +125,15 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
return TagMode.SelfClosing;
|
return TagMode.SelfClosing;
|
||||||
}
|
}
|
||||||
|
|
||||||
var baseDescriptor = descriptors.FirstOrDefault(
|
foreach (var descriptor in bindingResult.Descriptors)
|
||||||
descriptor => descriptor.TagStructure != TagStructure.Unspecified);
|
|
||||||
var resolvedTagStructure = baseDescriptor?.TagStructure ?? TagStructure.Unspecified;
|
|
||||||
if (resolvedTagStructure == TagStructure.WithoutEndTag)
|
|
||||||
{
|
{
|
||||||
return TagMode.StartTagOnly;
|
var boundRules = bindingResult.GetBoundRules(descriptor);
|
||||||
|
var nonDefaultRule = boundRules.FirstOrDefault(rule => rule.TagStructure != TagStructure.Unspecified);
|
||||||
|
|
||||||
|
if (nonDefaultRule?.TagStructure == TagStructure.WithoutEndTag)
|
||||||
|
{
|
||||||
|
return TagMode.StartTagOnly;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return TagMode.StartTagAndEndTag;
|
return TagMode.StartTagAndEndTag;
|
||||||
|
|
@ -656,8 +655,16 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
private static string GetPropertyType(string name, IEnumerable<TagHelperDescriptor> descriptors)
|
private static string GetPropertyType(string name, IEnumerable<TagHelperDescriptor> descriptors)
|
||||||
{
|
{
|
||||||
var firstBoundAttribute = FindFirstBoundAttribute(name, descriptors);
|
var firstBoundAttribute = FindFirstBoundAttribute(name, descriptors);
|
||||||
|
var isBoundToIndexer = firstBoundAttribute.IsIndexerNameMatch(name);
|
||||||
|
|
||||||
return firstBoundAttribute?.TypeName;
|
if (isBoundToIndexer)
|
||||||
|
{
|
||||||
|
return firstBoundAttribute?.IndexerTypeName;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return firstBoundAttribute?.TypeName;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a TryParseResult for given name, filling in binding details.
|
// Create a TryParseResult for given name, filling in binding details.
|
||||||
|
|
@ -665,10 +672,12 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
{
|
{
|
||||||
var firstBoundAttribute = FindFirstBoundAttribute(name, descriptors);
|
var firstBoundAttribute = FindFirstBoundAttribute(name, descriptors);
|
||||||
var isBoundAttribute = firstBoundAttribute != null;
|
var isBoundAttribute = firstBoundAttribute != null;
|
||||||
var isBoundNonStringAttribute = isBoundAttribute && !firstBoundAttribute.IsStringProperty;
|
var isBoundNonStringAttribute = isBoundAttribute &&
|
||||||
|
!(firstBoundAttribute.IsStringProperty ||
|
||||||
|
(firstBoundAttribute.IsIndexerNameMatch(name) && firstBoundAttribute.IsIndexerStringProperty));
|
||||||
var isMissingDictionaryKey = isBoundAttribute &&
|
var isMissingDictionaryKey = isBoundAttribute &&
|
||||||
firstBoundAttribute.IsIndexer &&
|
firstBoundAttribute.IndexerNamePrefix != null &&
|
||||||
name.Length == firstBoundAttribute.Name.Length;
|
name.Length == firstBoundAttribute.IndexerNamePrefix.Length;
|
||||||
|
|
||||||
return new TryParseResult
|
return new TryParseResult
|
||||||
{
|
{
|
||||||
|
|
@ -680,15 +689,13 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finds first TagHelperAttributeDescriptor matching given name.
|
// Finds first TagHelperAttributeDescriptor matching given name.
|
||||||
private static TagHelperAttributeDescriptor FindFirstBoundAttribute(
|
private static BoundAttributeDescriptor FindFirstBoundAttribute(
|
||||||
string name,
|
string name,
|
||||||
IEnumerable<TagHelperDescriptor> descriptors)
|
IEnumerable<TagHelperDescriptor> descriptors)
|
||||||
{
|
{
|
||||||
// Non-indexers (exact HTML attribute name matches) have higher precedence than indexers (prefix matches).
|
|
||||||
// Attributes already sorted to ensure this precedence.
|
|
||||||
var firstBoundAttribute = descriptors
|
var firstBoundAttribute = descriptors
|
||||||
.SelectMany(descriptor => descriptor.Attributes)
|
.SelectMany(descriptor => descriptor.BoundAttributes)
|
||||||
.FirstOrDefault(attributeDescriptor => attributeDescriptor.IsNameMatch(name));
|
.FirstOrDefault(attributeDescriptor => attributeDescriptor.CanMatchName(name));
|
||||||
|
|
||||||
return firstBoundAttribute;
|
return firstBoundAttribute;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,25 +1,10 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// 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.Legacy
|
namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
{
|
{
|
||||||
internal class TagHelperChunkGenerator : ParentChunkGenerator
|
internal class TagHelperChunkGenerator : ParentChunkGenerator
|
||||||
{
|
{
|
||||||
private IEnumerable<TagHelperDescriptor> _tagHelperDescriptors;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Instantiates a new <see cref="TagHelperChunkGenerator"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="tagHelperDescriptors">
|
|
||||||
/// <see cref="TagHelperDescriptor"/>s associated with the current HTML tag.
|
|
||||||
/// </param>
|
|
||||||
public TagHelperChunkGenerator(IEnumerable<TagHelperDescriptor> tagHelperDescriptors)
|
|
||||||
{
|
|
||||||
_tagHelperDescriptors = tagHelperDescriptors;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void GenerateStartParentChunk(Block target, ChunkGeneratorContext context)
|
public override void GenerateStartParentChunk(Block target, ChunkGeneratorContext context)
|
||||||
{
|
{
|
||||||
//var tagHelperBlock = target as TagHelperBlock;
|
//var tagHelperBlock = target as TagHelperBlock;
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,6 @@ using Microsoft.Extensions.Internal;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// An <see cref="IEqualityComparer{TagHelperDescriptor}"/> used to check equality between
|
|
||||||
/// two <see cref="TagHelperDescriptor"/>s.
|
|
||||||
/// </summary>
|
|
||||||
internal class TagHelperDescriptorComparer : IEqualityComparer<TagHelperDescriptor>
|
internal class TagHelperDescriptorComparer : IEqualityComparer<TagHelperDescriptor>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -20,27 +16,34 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
public static readonly TagHelperDescriptorComparer Default = new TagHelperDescriptorComparer();
|
public static readonly TagHelperDescriptorComparer Default = new TagHelperDescriptorComparer();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// An instance of <see cref="TagHelperDescriptorComparer"/> that only compares
|
/// A default instance of the <see cref="TagHelperDescriptorComparer"/> that does case-sensitive comparison.
|
||||||
/// <see cref="TagHelperDescriptor.TypeName"/>.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static readonly TagHelperDescriptorComparer TypeName = new TypeNameTagHelperDescriptorComparer();
|
internal static readonly TagHelperDescriptorComparer CaseSensitive =
|
||||||
|
new TagHelperDescriptorComparer(caseSensitive: true);
|
||||||
|
|
||||||
/// <summary>
|
private readonly StringComparer _stringComparer;
|
||||||
/// Initializes a new <see cref="TagHelperDescriptorComparer"/> instance.
|
private readonly StringComparison _stringComparison;
|
||||||
/// </summary>
|
private readonly BoundAttributeDescriptorComparer _boundAttributeComparer;
|
||||||
protected TagHelperDescriptorComparer()
|
private readonly TagMatchingRuleComparer _tagMatchingRuleComparer;
|
||||||
|
|
||||||
|
private TagHelperDescriptorComparer(bool caseSensitive = false)
|
||||||
{
|
{
|
||||||
|
if (caseSensitive)
|
||||||
|
{
|
||||||
|
_stringComparer = StringComparer.Ordinal;
|
||||||
|
_stringComparison = StringComparison.Ordinal;
|
||||||
|
_boundAttributeComparer = BoundAttributeDescriptorComparer.CaseSensitive;
|
||||||
|
_tagMatchingRuleComparer = TagMatchingRuleComparer.CaseSensitive;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_stringComparer = StringComparer.OrdinalIgnoreCase;
|
||||||
|
_stringComparison = StringComparison.OrdinalIgnoreCase;
|
||||||
|
_boundAttributeComparer = BoundAttributeDescriptorComparer.Default;
|
||||||
|
_tagMatchingRuleComparer = TagMatchingRuleComparer.Default;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
/// <remarks>
|
|
||||||
/// Determines equality based on <see cref="TagHelperDescriptor.TypeName"/>,
|
|
||||||
/// <see cref="TagHelperDescriptor.AssemblyName"/>, <see cref="TagHelperDescriptor.TagName"/>,
|
|
||||||
/// <see cref="TagHelperDescriptor.RequiredAttributes"/>, <see cref="TagHelperDescriptor.AllowedChildren"/>,
|
|
||||||
/// and <see cref="TagHelperDescriptor.TagStructure"/>.
|
|
||||||
/// Ignores <see cref="TagHelperDescriptor.DesignTimeDescriptor"/> because it can be inferred directly from
|
|
||||||
/// <see cref="TagHelperDescriptor.TypeName"/> and <see cref="TagHelperDescriptor.AssemblyName"/>.
|
|
||||||
/// </remarks>
|
|
||||||
public virtual bool Equals(TagHelperDescriptor descriptorX, TagHelperDescriptor descriptorY)
|
public virtual bool Equals(TagHelperDescriptor descriptorX, TagHelperDescriptor descriptorY)
|
||||||
{
|
{
|
||||||
if (descriptorX == descriptorY)
|
if (descriptorX == descriptorY)
|
||||||
|
|
@ -49,28 +52,30 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
}
|
}
|
||||||
|
|
||||||
return descriptorX != null &&
|
return descriptorX != null &&
|
||||||
string.Equals(descriptorX.TypeName, descriptorY.TypeName, StringComparison.Ordinal) &&
|
string.Equals(descriptorX.Kind, descriptorY.Kind, StringComparison.Ordinal) &&
|
||||||
string.Equals(descriptorX.TagName, descriptorY.TagName, StringComparison.OrdinalIgnoreCase) &&
|
|
||||||
string.Equals(descriptorX.AssemblyName, descriptorY.AssemblyName, StringComparison.Ordinal) &&
|
string.Equals(descriptorX.AssemblyName, descriptorY.AssemblyName, StringComparison.Ordinal) &&
|
||||||
string.Equals(
|
|
||||||
descriptorX.RequiredParent,
|
|
||||||
descriptorY.RequiredParent,
|
|
||||||
StringComparison.OrdinalIgnoreCase) &&
|
|
||||||
Enumerable.SequenceEqual(
|
Enumerable.SequenceEqual(
|
||||||
descriptorX.RequiredAttributes.OrderBy(attribute => attribute.Name, StringComparer.OrdinalIgnoreCase),
|
descriptorX.BoundAttributes.OrderBy(attribute => attribute.Name, _stringComparer),
|
||||||
descriptorY.RequiredAttributes.OrderBy(attribute => attribute.Name, StringComparer.OrdinalIgnoreCase),
|
descriptorY.BoundAttributes.OrderBy(attribute => attribute.Name, _stringComparer),
|
||||||
TagHelperRequiredAttributeDescriptorComparer.Default) &&
|
_boundAttributeComparer) &&
|
||||||
(descriptorX.AllowedChildren == descriptorY.AllowedChildren ||
|
|
||||||
(descriptorX.AllowedChildren != null &&
|
|
||||||
descriptorY.AllowedChildren != null &&
|
|
||||||
Enumerable.SequenceEqual(
|
Enumerable.SequenceEqual(
|
||||||
descriptorX.AllowedChildren.OrderBy(child => child, StringComparer.OrdinalIgnoreCase),
|
descriptorX.TagMatchingRules.OrderBy(rule => rule.TagName, _stringComparer),
|
||||||
descriptorY.AllowedChildren.OrderBy(child => child, StringComparer.OrdinalIgnoreCase),
|
descriptorY.TagMatchingRules.OrderBy(rule => rule.TagName, _stringComparer),
|
||||||
StringComparer.OrdinalIgnoreCase))) &&
|
_tagMatchingRuleComparer) &&
|
||||||
descriptorX.TagStructure == descriptorY.TagStructure &&
|
(descriptorX.AllowedChildTags == descriptorY.AllowedChildTags ||
|
||||||
|
(descriptorX.AllowedChildTags != null &&
|
||||||
|
descriptorY.AllowedChildTags != null &&
|
||||||
Enumerable.SequenceEqual(
|
Enumerable.SequenceEqual(
|
||||||
descriptorX.PropertyBag.OrderBy(propertyX => propertyX.Key, StringComparer.Ordinal),
|
descriptorX.AllowedChildTags.OrderBy(child => child, _stringComparer),
|
||||||
descriptorY.PropertyBag.OrderBy(propertyY => propertyY.Key, StringComparer.Ordinal));
|
descriptorY.AllowedChildTags.OrderBy(child => child, _stringComparer),
|
||||||
|
_stringComparer))) &&
|
||||||
|
string.Equals(descriptorX.Documentation, descriptorY.Documentation, StringComparison.Ordinal) &&
|
||||||
|
string.Equals(descriptorX.DisplayName, descriptorY.DisplayName, StringComparison.Ordinal) &&
|
||||||
|
string.Equals(descriptorX.TagOutputHint, descriptorY.TagOutputHint, _stringComparison) &&
|
||||||
|
Enumerable.SequenceEqual(descriptorX.Diagnostics, descriptorY.Diagnostics) &&
|
||||||
|
Enumerable.SequenceEqual(
|
||||||
|
descriptorX.Metadata.OrderBy(metadataX => metadataX.Key, StringComparer.Ordinal),
|
||||||
|
descriptorY.Metadata.OrderBy(metadataY => metadataY.Key, StringComparer.Ordinal));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|
@ -82,59 +87,35 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
}
|
}
|
||||||
|
|
||||||
var hashCodeCombiner = HashCodeCombiner.Start();
|
var hashCodeCombiner = HashCodeCombiner.Start();
|
||||||
hashCodeCombiner.Add(descriptor.TypeName, StringComparer.Ordinal);
|
hashCodeCombiner.Add(descriptor.Kind);
|
||||||
hashCodeCombiner.Add(descriptor.TagName, StringComparer.OrdinalIgnoreCase);
|
|
||||||
hashCodeCombiner.Add(descriptor.AssemblyName, StringComparer.Ordinal);
|
hashCodeCombiner.Add(descriptor.AssemblyName, StringComparer.Ordinal);
|
||||||
hashCodeCombiner.Add(descriptor.RequiredParent, StringComparer.OrdinalIgnoreCase);
|
|
||||||
hashCodeCombiner.Add(descriptor.TagStructure);
|
|
||||||
|
|
||||||
var attributes = descriptor.RequiredAttributes.OrderBy(
|
var boundAttributes = descriptor.BoundAttributes.OrderBy(attribute => attribute.Name, _stringComparer);
|
||||||
attribute => attribute.Name,
|
foreach (var attribute in boundAttributes)
|
||||||
StringComparer.OrdinalIgnoreCase);
|
|
||||||
foreach (var attribute in attributes)
|
|
||||||
{
|
{
|
||||||
hashCodeCombiner.Add(TagHelperRequiredAttributeDescriptorComparer.Default.GetHashCode(attribute));
|
hashCodeCombiner.Add(_boundAttributeComparer.GetHashCode(attribute));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (descriptor.AllowedChildren != null)
|
var rules = descriptor.TagMatchingRules.OrderBy(rule => rule.TagName, _stringComparer);
|
||||||
|
foreach (var rule in rules)
|
||||||
{
|
{
|
||||||
var allowedChildren = descriptor.AllowedChildren.OrderBy(child => child, StringComparer.OrdinalIgnoreCase);
|
hashCodeCombiner.Add(_tagMatchingRuleComparer.GetHashCode(rule));
|
||||||
|
}
|
||||||
|
|
||||||
|
hashCodeCombiner.Add(descriptor.Documentation, StringComparer.Ordinal);
|
||||||
|
hashCodeCombiner.Add(descriptor.DisplayName, StringComparer.Ordinal);
|
||||||
|
hashCodeCombiner.Add(descriptor.TagOutputHint, _stringComparer);
|
||||||
|
|
||||||
|
if (descriptor.AllowedChildTags != null)
|
||||||
|
{
|
||||||
|
var allowedChildren = descriptor.AllowedChildTags.OrderBy(child => child, _stringComparer);
|
||||||
foreach (var child in allowedChildren)
|
foreach (var child in allowedChildren)
|
||||||
{
|
{
|
||||||
hashCodeCombiner.Add(child, StringComparer.OrdinalIgnoreCase);
|
hashCodeCombiner.Add(child, _stringComparer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return hashCodeCombiner.CombinedHash;
|
return hashCodeCombiner.CombinedHash;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class TypeNameTagHelperDescriptorComparer : TagHelperDescriptorComparer
|
|
||||||
{
|
|
||||||
public override bool Equals(TagHelperDescriptor descriptorX, TagHelperDescriptor descriptorY)
|
|
||||||
{
|
|
||||||
if (object.ReferenceEquals(descriptorX, descriptorY))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else if (descriptorX == null ^ descriptorY == null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return string.Equals(descriptorX.TypeName, descriptorY.TypeName, StringComparison.Ordinal);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int GetHashCode(TagHelperDescriptor descriptor)
|
|
||||||
{
|
|
||||||
if (descriptor == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(descriptor));
|
|
||||||
}
|
|
||||||
|
|
||||||
return descriptor.TypeName == null ? 0 : StringComparer.Ordinal.GetHashCode(descriptor.TypeName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -15,14 +15,16 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
public const string ElementCatchAllTarget = "*";
|
public const string ElementCatchAllTarget = "*";
|
||||||
|
|
||||||
private IDictionary<string, HashSet<TagHelperDescriptor>> _registrations;
|
private IDictionary<string, HashSet<TagHelperDescriptor>> _registrations;
|
||||||
private string _tagHelperPrefix;
|
private readonly string _tagHelperPrefix;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Instantiates a new instance of the <see cref="TagHelperDescriptorProvider"/>.
|
/// Instantiates a new instance of the <see cref="TagHelperDescriptorProvider"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="tagHelperPrefix">The tag helper prefix being used by the document.</param>
|
||||||
/// <param name="descriptors">The descriptors that the <see cref="TagHelperDescriptorProvider"/> will pull from.</param>
|
/// <param name="descriptors">The descriptors that the <see cref="TagHelperDescriptorProvider"/> will pull from.</param>
|
||||||
public TagHelperDescriptorProvider(IEnumerable<TagHelperDescriptor> descriptors)
|
public TagHelperDescriptorProvider(string tagHelperPrefix, IEnumerable<TagHelperDescriptor> descriptors)
|
||||||
{
|
{
|
||||||
|
_tagHelperPrefix = tagHelperPrefix;
|
||||||
_registrations = new Dictionary<string, HashSet<TagHelperDescriptor>>(StringComparer.OrdinalIgnoreCase);
|
_registrations = new Dictionary<string, HashSet<TagHelperDescriptor>>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
// Populate our registrations
|
// Populate our registrations
|
||||||
|
|
@ -42,7 +44,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
/// <returns><see cref="TagHelperDescriptor"/>s that apply to the given <paramref name="tagName"/>.
|
/// <returns><see cref="TagHelperDescriptor"/>s that apply to the given <paramref name="tagName"/>.
|
||||||
/// Will return an empty <see cref="Enumerable" /> if no <see cref="TagHelperDescriptor"/>s are
|
/// Will return an empty <see cref="Enumerable" /> if no <see cref="TagHelperDescriptor"/>s are
|
||||||
/// found.</returns>
|
/// found.</returns>
|
||||||
public IEnumerable<TagHelperDescriptor> GetDescriptors(
|
public TagHelperBinding GetTagHelperBinding(
|
||||||
string tagName,
|
string tagName,
|
||||||
IEnumerable<KeyValuePair<string, string>> attributes,
|
IEnumerable<KeyValuePair<string, string>> attributes,
|
||||||
string parentTagName)
|
string parentTagName)
|
||||||
|
|
@ -52,14 +54,13 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
!tagName.StartsWith(_tagHelperPrefix, StringComparison.OrdinalIgnoreCase)))
|
!tagName.StartsWith(_tagHelperPrefix, StringComparison.OrdinalIgnoreCase)))
|
||||||
{
|
{
|
||||||
// The tagName doesn't have the tag helper prefix, we can short circuit.
|
// The tagName doesn't have the tag helper prefix, we can short circuit.
|
||||||
return Enumerable.Empty<TagHelperDescriptor>();
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
HashSet<TagHelperDescriptor> catchAllDescriptors;
|
|
||||||
IEnumerable<TagHelperDescriptor> descriptors;
|
IEnumerable<TagHelperDescriptor> descriptors;
|
||||||
|
|
||||||
// Ensure there's a HashSet to use.
|
// Ensure there's a HashSet to use.
|
||||||
if (!_registrations.TryGetValue(ElementCatchAllTarget, out catchAllDescriptors))
|
if (!_registrations.TryGetValue(ElementCatchAllTarget, out HashSet<TagHelperDescriptor> catchAllDescriptors))
|
||||||
{
|
{
|
||||||
descriptors = new HashSet<TagHelperDescriptor>(TagHelperDescriptorComparer.Default);
|
descriptors = new HashSet<TagHelperDescriptor>(TagHelperDescriptorComparer.Default);
|
||||||
}
|
}
|
||||||
|
|
@ -70,64 +71,87 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
|
|
||||||
// If we have a tag name associated with the requested name, we need to combine matchingDescriptors
|
// If we have a tag name associated with the requested name, we need to combine matchingDescriptors
|
||||||
// with all the catch-all descriptors.
|
// with all the catch-all descriptors.
|
||||||
HashSet<TagHelperDescriptor> matchingDescriptors;
|
if (_registrations.TryGetValue(tagName, out HashSet<TagHelperDescriptor> matchingDescriptors))
|
||||||
if (_registrations.TryGetValue(tagName, out matchingDescriptors))
|
|
||||||
{
|
{
|
||||||
descriptors = matchingDescriptors.Concat(descriptors);
|
descriptors = matchingDescriptors.Concat(descriptors);
|
||||||
}
|
}
|
||||||
|
|
||||||
var applicableDescriptors = new List<TagHelperDescriptor>();
|
var tagNameWithoutPrefix = _tagHelperPrefix != null ? tagName.Substring(_tagHelperPrefix.Length) : tagName;
|
||||||
|
Dictionary<TagHelperDescriptor, IEnumerable<TagMatchingRule>> applicableDescriptorMappings = null;
|
||||||
foreach (var descriptor in descriptors)
|
foreach (var descriptor in descriptors)
|
||||||
{
|
{
|
||||||
if (HasRequiredAttributes(descriptor, attributes) &&
|
var applicableRules = descriptor.TagMatchingRules.Where(
|
||||||
HasRequiredParentTag(descriptor, parentTagName))
|
rule => MatchesRule(rule, attributes, tagNameWithoutPrefix, parentTagName));
|
||||||
|
|
||||||
|
if (applicableRules.Any())
|
||||||
{
|
{
|
||||||
applicableDescriptors.Add(descriptor);
|
if (applicableDescriptorMappings == null)
|
||||||
|
{
|
||||||
|
applicableDescriptorMappings = new Dictionary<TagHelperDescriptor, IEnumerable<TagMatchingRule>>();
|
||||||
|
}
|
||||||
|
|
||||||
|
applicableDescriptorMappings[descriptor] = applicableRules;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return applicableDescriptors.Distinct(TagHelperDescriptorComparer.TypeName);
|
if (applicableDescriptorMappings == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var tagMappingResult = new TagHelperBinding(applicableDescriptorMappings);
|
||||||
|
|
||||||
|
return tagMappingResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool HasRequiredParentTag(
|
private bool MatchesRule(
|
||||||
TagHelperDescriptor descriptor,
|
TagMatchingRule rule,
|
||||||
|
IEnumerable<KeyValuePair<string, string>> tagAttributes,
|
||||||
|
string tagNameWithoutPrefix,
|
||||||
string parentTagName)
|
string parentTagName)
|
||||||
{
|
{
|
||||||
return descriptor.RequiredParent == null ||
|
// Verify tag name
|
||||||
string.Equals(parentTagName, descriptor.RequiredParent, StringComparison.OrdinalIgnoreCase);
|
if (rule.TagName != ElementCatchAllTarget &&
|
||||||
}
|
rule.TagName != null &&
|
||||||
|
!string.Equals(tagNameWithoutPrefix, rule.TagName, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private bool HasRequiredAttributes(
|
// Verify parent tag
|
||||||
TagHelperDescriptor descriptor,
|
if (rule.ParentTag != null && !string.Equals(parentTagName, rule.ParentTag, StringComparison.OrdinalIgnoreCase))
|
||||||
IEnumerable<KeyValuePair<string, string>> attributes)
|
{
|
||||||
{
|
return false;
|
||||||
return descriptor.RequiredAttributes.All(
|
}
|
||||||
requiredAttribute => attributes.Any(
|
|
||||||
attribute => requiredAttribute.IsMatch(attribute.Key, attribute.Value)));
|
if (!rule.Attributes.All(
|
||||||
|
requiredAttribute => tagAttributes.Any(
|
||||||
|
attribute => requiredAttribute.IsMatch(attribute.Key, attribute.Value))))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Register(TagHelperDescriptor descriptor)
|
private void Register(TagHelperDescriptor descriptor)
|
||||||
{
|
{
|
||||||
HashSet<TagHelperDescriptor> descriptorSet;
|
foreach (var rule in descriptor.TagMatchingRules)
|
||||||
|
|
||||||
if (_tagHelperPrefix == null)
|
|
||||||
{
|
{
|
||||||
_tagHelperPrefix = descriptor.Prefix;
|
var registrationKey =
|
||||||
|
string.Equals(rule.TagName, ElementCatchAllTarget, StringComparison.Ordinal) ?
|
||||||
|
ElementCatchAllTarget :
|
||||||
|
_tagHelperPrefix + rule.TagName;
|
||||||
|
|
||||||
|
// Ensure there's a HashSet to add the descriptor to.
|
||||||
|
if (!_registrations.TryGetValue(registrationKey, out HashSet<TagHelperDescriptor> descriptorSet))
|
||||||
|
{
|
||||||
|
descriptorSet = new HashSet<TagHelperDescriptor>(TagHelperDescriptorComparer.Default);
|
||||||
|
_registrations[registrationKey] = descriptorSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
descriptorSet.Add(descriptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
var registrationKey =
|
|
||||||
string.Equals(descriptor.TagName, ElementCatchAllTarget, StringComparison.Ordinal) ?
|
|
||||||
ElementCatchAllTarget :
|
|
||||||
descriptor.FullTagName;
|
|
||||||
|
|
||||||
// Ensure there's a HashSet to add the descriptor to.
|
|
||||||
if (!_registrations.TryGetValue(registrationKey, out descriptorSet))
|
|
||||||
{
|
|
||||||
descriptorSet = new HashSet<TagHelperDescriptor>(TagHelperDescriptorComparer.Default);
|
|
||||||
_registrations[registrationKey] = descriptorSet;
|
|
||||||
}
|
|
||||||
|
|
||||||
descriptorSet.Add(descriptor);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -36,6 +36,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
"wbr"
|
"wbr"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private readonly string _tagHelperPrefix;
|
||||||
private readonly List<KeyValuePair<string, string>> _htmlAttributeTracker;
|
private readonly List<KeyValuePair<string, string>> _htmlAttributeTracker;
|
||||||
private readonly StringBuilder _attributeValueBuilder;
|
private readonly StringBuilder _attributeValueBuilder;
|
||||||
private readonly TagHelperDescriptorProvider _provider;
|
private readonly TagHelperDescriptorProvider _provider;
|
||||||
|
|
@ -45,8 +46,9 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
private BlockBuilder _currentBlock;
|
private BlockBuilder _currentBlock;
|
||||||
private string _currentParentTagName;
|
private string _currentParentTagName;
|
||||||
|
|
||||||
public TagHelperParseTreeRewriter(TagHelperDescriptorProvider provider)
|
public TagHelperParseTreeRewriter(string tagHelperPrefix, TagHelperDescriptorProvider provider)
|
||||||
{
|
{
|
||||||
|
_tagHelperPrefix = tagHelperPrefix;
|
||||||
_provider = provider;
|
_provider = provider;
|
||||||
_trackerStack = new Stack<TagBlockTracker>();
|
_trackerStack = new Stack<TagBlockTracker>();
|
||||||
_blockStack = new Stack<BlockBuilder>();
|
_blockStack = new Stack<BlockBuilder>();
|
||||||
|
|
@ -172,7 +174,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var descriptors = Enumerable.Empty<TagHelperDescriptor>();
|
TagHelperBinding tagHelperBinding;
|
||||||
|
|
||||||
if (!IsPotentialTagHelper(tagName, tagBlock))
|
if (!IsPotentialTagHelper(tagName, tagBlock))
|
||||||
{
|
{
|
||||||
|
|
@ -187,10 +189,10 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
// We're now in a start tag block, we first need to see if the tag block is a tag helper.
|
// We're now in a start tag block, we first need to see if the tag block is a tag helper.
|
||||||
var providedAttributes = GetAttributeNameValuePairs(tagBlock);
|
var providedAttributes = GetAttributeNameValuePairs(tagBlock);
|
||||||
|
|
||||||
descriptors = _provider.GetDescriptors(tagName, providedAttributes, _currentParentTagName);
|
tagHelperBinding = _provider.GetTagHelperBinding(tagName, providedAttributes, _currentParentTagName);
|
||||||
|
|
||||||
// If there aren't any TagHelperDescriptors registered then we aren't a TagHelper
|
// If there aren't any TagHelperDescriptors registered then we aren't a TagHelper
|
||||||
if (!descriptors.Any())
|
if (tagHelperBinding == null)
|
||||||
{
|
{
|
||||||
// If the current tag matches the current TagHelper scope it means the parent TagHelper matched
|
// If the current tag matches the current TagHelper scope it means the parent TagHelper matched
|
||||||
// all the required attributes but the current one did not; therefore, we need to increment the
|
// all the required attributes but the current one did not; therefore, we need to increment the
|
||||||
|
|
@ -206,7 +208,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
}
|
}
|
||||||
|
|
||||||
ValidateParentAllowsTagHelper(tagName, tagBlock, errorSink);
|
ValidateParentAllowsTagHelper(tagName, tagBlock, errorSink);
|
||||||
ValidateDescriptors(descriptors, tagName, tagBlock, errorSink);
|
ValidateBinding(tagHelperBinding, tagName, tagBlock, errorSink);
|
||||||
|
|
||||||
// We're in a start TagHelper block.
|
// We're in a start TagHelper block.
|
||||||
var validTagStructure = ValidateTagSyntax(tagName, tagBlock, errorSink);
|
var validTagStructure = ValidateTagSyntax(tagName, tagBlock, errorSink);
|
||||||
|
|
@ -215,7 +217,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
tagName,
|
tagName,
|
||||||
validTagStructure,
|
validTagStructure,
|
||||||
tagBlock,
|
tagBlock,
|
||||||
descriptors,
|
tagHelperBinding,
|
||||||
errorSink);
|
errorSink);
|
||||||
|
|
||||||
// Track the original start tag so the editor knows where each piece of the TagHelperBlock lies
|
// Track the original start tag so the editor knows where each piece of the TagHelperBlock lies
|
||||||
|
|
@ -252,32 +254,38 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
descriptors = _provider.GetDescriptors(
|
tagHelperBinding = _provider.GetTagHelperBinding(
|
||||||
tagName,
|
tagName,
|
||||||
attributes: Enumerable.Empty<KeyValuePair<string, string>>(),
|
attributes: Enumerable.Empty<KeyValuePair<string, string>>(),
|
||||||
parentTagName: _currentParentTagName);
|
parentTagName: _currentParentTagName);
|
||||||
|
|
||||||
// If there are not TagHelperDescriptors associated with the end tag block that also have no
|
// If there are not TagHelperDescriptors associated with the end tag block that also have no
|
||||||
// required attributes then it means we can't be a TagHelper, bail out.
|
// required attributes then it means we can't be a TagHelper, bail out.
|
||||||
if (!descriptors.Any())
|
if (tagHelperBinding == null)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var invalidDescriptor = descriptors.FirstOrDefault(
|
foreach (var descriptor in tagHelperBinding.Descriptors)
|
||||||
descriptor => descriptor.TagStructure == TagStructure.WithoutEndTag);
|
|
||||||
if (invalidDescriptor != null)
|
|
||||||
{
|
{
|
||||||
// End tag TagHelper that states it shouldn't have an end tag.
|
var boundRules = tagHelperBinding.GetBoundRules(descriptor);
|
||||||
errorSink.OnError(
|
var invalidRule = boundRules.FirstOrDefault(rule => rule.TagStructure == TagStructure.WithoutEndTag);
|
||||||
SourceLocationTracker.Advance(tagBlock.Start, "</"),
|
|
||||||
LegacyResources.FormatTagHelperParseTreeRewriter_EndTagTagHelperMustNotHaveAnEndTag(
|
|
||||||
tagName,
|
|
||||||
invalidDescriptor.TypeName,
|
|
||||||
invalidDescriptor.TagStructure),
|
|
||||||
tagName.Length);
|
|
||||||
|
|
||||||
return false;
|
if (invalidRule != null)
|
||||||
|
{
|
||||||
|
var typeName = descriptor.Metadata[ITagHelperDescriptorBuilder.TypeNameKey];
|
||||||
|
|
||||||
|
// End tag TagHelper that states it shouldn't have an end tag.
|
||||||
|
errorSink.OnError(
|
||||||
|
SourceLocationTracker.Advance(tagBlock.Start, "</"),
|
||||||
|
LegacyResources.FormatTagHelperParseTreeRewriter_EndTagTagHelperMustNotHaveAnEndTag(
|
||||||
|
tagName,
|
||||||
|
typeName,
|
||||||
|
invalidRule.TagStructure),
|
||||||
|
tagName.Length);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Current tag helper scope does not match the end tag. Attempt to recover the tag
|
// Current tag helper scope does not match the end tag. Attempt to recover the tag
|
||||||
|
|
@ -463,7 +471,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return _currentTagHelperTracker.AllowedChildren != null;
|
return _currentTagHelperTracker.AllowedChildren != null && _currentTagHelperTracker.AllowedChildren.Count > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ValidateParentAllowsContent(Span child, ErrorSink errorSink)
|
private void ValidateParentAllowsContent(Span child, ErrorSink errorSink)
|
||||||
|
|
@ -536,8 +544,8 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
errorSink.OnError(errorStart, errorMessage, tagName.Length);
|
errorSink.OnError(errorStart, errorMessage, tagName.Length);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ValidateDescriptors(
|
private static void ValidateBinding(
|
||||||
IEnumerable<TagHelperDescriptor> descriptors,
|
TagHelperBinding bindingResult,
|
||||||
string tagName,
|
string tagName,
|
||||||
Block tagBlock,
|
Block tagBlock,
|
||||||
ErrorSink errorSink)
|
ErrorSink errorSink)
|
||||||
|
|
@ -545,24 +553,32 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
// Ensure that all descriptors associated with this tag have appropriate TagStructures. Cannot have
|
// Ensure that all descriptors associated with this tag have appropriate TagStructures. Cannot have
|
||||||
// multiple descriptors that expect different TagStructures (other than TagStructure.Unspecified).
|
// multiple descriptors that expect different TagStructures (other than TagStructure.Unspecified).
|
||||||
TagHelperDescriptor baseDescriptor = null;
|
TagHelperDescriptor baseDescriptor = null;
|
||||||
foreach (var descriptor in descriptors)
|
TagStructure? baseStructure = null;
|
||||||
|
foreach (var descriptor in bindingResult.Descriptors)
|
||||||
{
|
{
|
||||||
if (descriptor.TagStructure != TagStructure.Unspecified)
|
var boundRules = bindingResult.GetBoundRules(descriptor);
|
||||||
|
foreach (var rule in boundRules)
|
||||||
{
|
{
|
||||||
// Can't have a set of TagHelpers that expect different structures.
|
if (rule.TagStructure != TagStructure.Unspecified)
|
||||||
if (baseDescriptor != null && baseDescriptor.TagStructure != descriptor.TagStructure)
|
|
||||||
{
|
{
|
||||||
errorSink.OnError(
|
// Can't have a set of TagHelpers that expect different structures.
|
||||||
tagBlock.Start,
|
if (baseStructure.HasValue && baseStructure != rule.TagStructure)
|
||||||
LegacyResources.FormatTagHelperParseTreeRewriter_InconsistentTagStructure(
|
{
|
||||||
baseDescriptor.TypeName,
|
var baseDescriptorTypeName = baseDescriptor.Metadata[ITagHelperDescriptorBuilder.TypeNameKey];
|
||||||
descriptor.TypeName,
|
var descriptorTypeName = descriptor.Metadata[ITagHelperDescriptorBuilder.TypeNameKey];
|
||||||
tagName,
|
errorSink.OnError(
|
||||||
nameof(TagHelperDescriptor.TagStructure)),
|
tagBlock.Start,
|
||||||
tagBlock.Length);
|
LegacyResources.FormatTagHelperParseTreeRewriter_InconsistentTagStructure(
|
||||||
}
|
baseDescriptorTypeName,
|
||||||
|
descriptorTypeName,
|
||||||
|
tagName,
|
||||||
|
nameof(TagMatchingRule.TagStructure)),
|
||||||
|
tagBlock.Length);
|
||||||
|
}
|
||||||
|
|
||||||
baseDescriptor = descriptor;
|
baseDescriptor = descriptor;
|
||||||
|
baseStructure = rule.TagStructure;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -680,7 +696,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
|
|
||||||
private void TrackTagHelperBlock(TagHelperBlockBuilder builder)
|
private void TrackTagHelperBlock(TagHelperBlockBuilder builder)
|
||||||
{
|
{
|
||||||
_currentTagHelperTracker = new TagHelperBlockTracker(builder);
|
_currentTagHelperTracker = new TagHelperBlockTracker(_tagHelperPrefix, builder);
|
||||||
PushTrackerStack(_currentTagHelperTracker);
|
PushTrackerStack(_currentTagHelperTracker);
|
||||||
|
|
||||||
TrackBlock(builder);
|
TrackBlock(builder);
|
||||||
|
|
@ -830,19 +846,22 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
|
|
||||||
private class TagHelperBlockTracker : TagBlockTracker
|
private class TagHelperBlockTracker : TagBlockTracker
|
||||||
{
|
{
|
||||||
private IEnumerable<string> _prefixedAllowedChildren;
|
private IReadOnlyList<string> _prefixedAllowedChildren;
|
||||||
|
private readonly string _tagHelperPrefix;
|
||||||
|
|
||||||
public TagHelperBlockTracker(TagHelperBlockBuilder builder)
|
public TagHelperBlockTracker(string tagHelperPrefix, TagHelperBlockBuilder builder)
|
||||||
: base(builder.TagName, isTagHelper: true, depth: 0)
|
: base(builder.TagName, isTagHelper: true, depth: 0)
|
||||||
{
|
{
|
||||||
|
_tagHelperPrefix = tagHelperPrefix;
|
||||||
Builder = builder;
|
Builder = builder;
|
||||||
|
|
||||||
if (Builder.Descriptors.Any(descriptor => descriptor.AllowedChildren != null))
|
if (Builder.BindingResult.Descriptors.Any(descriptor => descriptor.AllowedChildTags != null))
|
||||||
{
|
{
|
||||||
AllowedChildren = Builder.Descriptors
|
AllowedChildren = Builder.BindingResult.Descriptors
|
||||||
.Where(descriptor => descriptor.AllowedChildren != null)
|
.Where(descriptor => descriptor.AllowedChildTags != null)
|
||||||
.SelectMany(descriptor => descriptor.AllowedChildren)
|
.SelectMany(descriptor => descriptor.AllowedChildTags)
|
||||||
.Distinct(StringComparer.OrdinalIgnoreCase);
|
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||||
|
.ToList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -850,18 +869,17 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
|
|
||||||
public uint OpenMatchingTags { get; set; }
|
public uint OpenMatchingTags { get; set; }
|
||||||
|
|
||||||
public IEnumerable<string> AllowedChildren { get; }
|
public IReadOnlyList<string> AllowedChildren { get; }
|
||||||
|
|
||||||
public IEnumerable<string> PrefixedAllowedChildren
|
public IReadOnlyList<string> PrefixedAllowedChildren
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (AllowedChildren != null && _prefixedAllowedChildren == null)
|
if (AllowedChildren != null && _prefixedAllowedChildren == null)
|
||||||
{
|
{
|
||||||
Debug.Assert(Builder.Descriptors.Count() >= 1);
|
Debug.Assert(Builder.BindingResult.Descriptors.Count() >= 1);
|
||||||
|
|
||||||
var prefix = Builder.Descriptors.First().Prefix;
|
_prefixedAllowedChildren = AllowedChildren.Select(allowedChild => _tagHelperPrefix + allowedChild).ToList();
|
||||||
_prefixedAllowedChildren = AllowedChildren.Select(allowedChild => prefix + allowedChild);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return _prefixedAllowedChildren;
|
return _prefixedAllowedChildren;
|
||||||
|
|
|
||||||
|
|
@ -1,58 +0,0 @@
|
||||||
// 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.Extensions.Internal;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// An <see cref="IEqualityComparer{TagHelperRequiredAttributeDescriptor}"/> used to check equality between
|
|
||||||
/// two <see cref="TagHelperRequiredAttributeDescriptor"/>s.
|
|
||||||
/// </summary>
|
|
||||||
internal class TagHelperRequiredAttributeDescriptorComparer : IEqualityComparer<TagHelperRequiredAttributeDescriptor>
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// A default instance of the <see cref="TagHelperRequiredAttributeDescriptor"/>.
|
|
||||||
/// </summary>
|
|
||||||
public static readonly TagHelperRequiredAttributeDescriptorComparer Default =
|
|
||||||
new TagHelperRequiredAttributeDescriptorComparer();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new <see cref="TagHelperRequiredAttributeDescriptor"/> instance.
|
|
||||||
/// </summary>
|
|
||||||
protected TagHelperRequiredAttributeDescriptorComparer()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public virtual bool Equals(
|
|
||||||
TagHelperRequiredAttributeDescriptor descriptorX,
|
|
||||||
TagHelperRequiredAttributeDescriptor descriptorY)
|
|
||||||
{
|
|
||||||
if (descriptorX == descriptorY)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return descriptorX != null &&
|
|
||||||
descriptorX.NameComparison == descriptorY.NameComparison &&
|
|
||||||
descriptorX.ValueComparison == descriptorY.ValueComparison &&
|
|
||||||
string.Equals(descriptorX.Name, descriptorY.Name, StringComparison.OrdinalIgnoreCase) &&
|
|
||||||
string.Equals(descriptorX.Value, descriptorY.Value, StringComparison.Ordinal);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public virtual int GetHashCode(TagHelperRequiredAttributeDescriptor descriptor)
|
|
||||||
{
|
|
||||||
var hashCodeCombiner = HashCodeCombiner.Start();
|
|
||||||
hashCodeCombiner.Add(descriptor.NameComparison);
|
|
||||||
hashCodeCombiner.Add(descriptor.ValueComparison);
|
|
||||||
hashCodeCombiner.Add(descriptor.Name, StringComparer.OrdinalIgnoreCase);
|
|
||||||
hashCodeCombiner.Add(descriptor.Value, StringComparer.Ordinal);
|
|
||||||
|
|
||||||
return hashCodeCombiner.CombinedHash;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,64 +0,0 @@
|
||||||
// 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.Extensions.Internal;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// An <see cref="IEqualityComparer{TagHelperDescriptor}"/> that checks equality between two
|
|
||||||
/// <see cref="TagHelperDescriptor"/>s using only their <see cref="TagHelperDescriptor.AssemblyName"/>s and
|
|
||||||
/// <see cref="TagHelperDescriptor.TypeName"/>s.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// This class is intended for scenarios where Reflection-based information is all important i.e.
|
|
||||||
/// <see cref="TagHelperDescriptor.RequiredAttributes"/>, <see cref="TagHelperDescriptor.TagName"/>, and related
|
|
||||||
/// properties are not relevant.
|
|
||||||
/// </remarks>
|
|
||||||
internal class TypeBasedTagHelperDescriptorComparer : IEqualityComparer<TagHelperDescriptor>
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// A default instance of the <see cref="TypeBasedTagHelperDescriptorComparer"/>.
|
|
||||||
/// </summary>
|
|
||||||
public static readonly TypeBasedTagHelperDescriptorComparer Default =
|
|
||||||
new TypeBasedTagHelperDescriptorComparer();
|
|
||||||
|
|
||||||
private TypeBasedTagHelperDescriptorComparer()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
/// <remarks>
|
|
||||||
/// Determines equality based on <see cref="TagHelperDescriptor.AssemblyName"/> and
|
|
||||||
/// <see cref="TagHelperDescriptor.TypeName"/>.
|
|
||||||
/// </remarks>
|
|
||||||
public bool Equals(TagHelperDescriptor descriptorX, TagHelperDescriptor descriptorY)
|
|
||||||
{
|
|
||||||
if (descriptorX == descriptorY)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return descriptorX != null &&
|
|
||||||
string.Equals(descriptorX.AssemblyName, descriptorY.AssemblyName, StringComparison.Ordinal) &&
|
|
||||||
string.Equals(descriptorX.TypeName, descriptorY.TypeName, StringComparison.Ordinal);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public int GetHashCode(TagHelperDescriptor descriptor)
|
|
||||||
{
|
|
||||||
if (descriptor == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(descriptor));
|
|
||||||
}
|
|
||||||
|
|
||||||
var hashCodeCombiner = HashCodeCombiner.Start();
|
|
||||||
hashCodeCombiner.Add(descriptor.AssemblyName, StringComparer.Ordinal);
|
|
||||||
hashCodeCombiner.Add(descriptor.TypeName, StringComparer.Ordinal);
|
|
||||||
|
|
||||||
return hashCodeCombiner;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -9,5 +9,6 @@ using System.Runtime.CompilerServices;
|
||||||
[assembly: InternalsVisibleTo("Microsoft.CodeAnalysis.Razor.Workspaces, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
[assembly: InternalsVisibleTo("Microsoft.CodeAnalysis.Razor.Workspaces, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||||
[assembly: InternalsVisibleTo("Microsoft.CodeAnalysis.Remote.Razor, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
[assembly: InternalsVisibleTo("Microsoft.CodeAnalysis.Remote.Razor, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||||
[assembly: InternalsVisibleTo("Microsoft.VisualStudio.LanguageServices.Razor, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
[assembly: InternalsVisibleTo("Microsoft.VisualStudio.LanguageServices.Razor, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||||
|
[assembly: InternalsVisibleTo("Microsoft.VisualStudio.LanguageServices.Razor.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||||
[assembly: InternalsVisibleTo("Microsoft.VisualStudio.RazorExtension, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
[assembly: InternalsVisibleTo("Microsoft.VisualStudio.RazorExtension, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||||
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")]
|
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")]
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -15,225 +15,379 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string ArgumentCannotBeNullOrEmpty
|
internal static string ArgumentCannotBeNullOrEmpty
|
||||||
{
|
{
|
||||||
get { return GetString("ArgumentCannotBeNullOrEmpty"); }
|
get => GetString("ArgumentCannotBeNullOrEmpty");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Value cannot be null or an empty string.
|
/// Value cannot be null or an empty string.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string FormatArgumentCannotBeNullOrEmpty()
|
internal static string FormatArgumentCannotBeNullOrEmpty()
|
||||||
{
|
=> GetString("ArgumentCannotBeNullOrEmpty");
|
||||||
return GetString("ArgumentCannotBeNullOrEmpty");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The '{0}' feature requires a '{1}' provided by the '{2}'.
|
/// The '{0}' feature requires a '{1}' provided by the '{2}'.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string FeatureDependencyMissing
|
internal static string FeatureDependencyMissing
|
||||||
{
|
{
|
||||||
get { return GetString("FeatureDependencyMissing"); }
|
get => GetString("FeatureDependencyMissing");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The '{0}' feature requires a '{1}' provided by the '{2}'.
|
/// The '{0}' feature requires a '{1}' provided by the '{2}'.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string FormatFeatureDependencyMissing(object p0, object p1, object p2)
|
internal static string FormatFeatureDependencyMissing(object p0, object p1, object p2)
|
||||||
{
|
=> string.Format(CultureInfo.CurrentCulture, GetString("FeatureDependencyMissing"), p0, p1, p2);
|
||||||
return string.Format(CultureInfo.CurrentCulture, GetString("FeatureDependencyMissing"), p0, p1, p2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Invalid tag helper directive look up text '{0}'. The correct look up text format is: "typeName, assemblyName".
|
/// Invalid tag helper directive look up text '{0}'. The correct look up text format is: "typeName, assemblyName".
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string InvalidTagHelperLookupText
|
internal static string InvalidTagHelperLookupText
|
||||||
{
|
{
|
||||||
get { return GetString("InvalidTagHelperLookupText"); }
|
get => GetString("InvalidTagHelperLookupText");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Invalid tag helper directive look up text '{0}'. The correct look up text format is: "typeName, assemblyName".
|
/// Invalid tag helper directive look up text '{0}'. The correct look up text format is: "typeName, assemblyName".
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string FormatInvalidTagHelperLookupText(object p0)
|
internal static string FormatInvalidTagHelperLookupText(object p0)
|
||||||
{
|
=> string.Format(CultureInfo.CurrentCulture, GetString("InvalidTagHelperLookupText"), p0);
|
||||||
return string.Format(CultureInfo.CurrentCulture, GetString("InvalidTagHelperLookupText"), p0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Invalid tag helper directive '{0}' value. '{1}' is not allowed in prefix '{2}'.
|
/// Invalid tag helper directive '{0}' value. '{1}' is not allowed in prefix '{2}'.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string InvalidTagHelperPrefixValue
|
internal static string InvalidTagHelperPrefixValue
|
||||||
{
|
{
|
||||||
get { return GetString("InvalidTagHelperPrefixValue"); }
|
get => GetString("InvalidTagHelperPrefixValue");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Invalid tag helper directive '{0}' value. '{1}' is not allowed in prefix '{2}'.
|
/// Invalid tag helper directive '{0}' value. '{1}' is not allowed in prefix '{2}'.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string FormatInvalidTagHelperPrefixValue(object p0, object p1, object p2)
|
internal static string FormatInvalidTagHelperPrefixValue(object p0, object p1, object p2)
|
||||||
{
|
=> string.Format(CultureInfo.CurrentCulture, GetString("InvalidTagHelperPrefixValue"), p0, p1, p2);
|
||||||
return string.Format(CultureInfo.CurrentCulture, GetString("InvalidTagHelperPrefixValue"), p0, p1, p2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The '{0}' operation is not valid when the builder is empty.
|
/// The '{0}' operation is not valid when the builder is empty.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string IRBuilder_PopInvalid
|
internal static string IRBuilder_PopInvalid
|
||||||
{
|
{
|
||||||
get { return GetString("IRBuilder_PopInvalid"); }
|
get => GetString("IRBuilder_PopInvalid");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The '{0}' operation is not valid when the builder is empty.
|
/// The '{0}' operation is not valid when the builder is empty.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string FormatIRBuilder_PopInvalid(object p0)
|
internal static string FormatIRBuilder_PopInvalid(object p0)
|
||||||
{
|
=> string.Format(CultureInfo.CurrentCulture, GetString("IRBuilder_PopInvalid"), p0);
|
||||||
return string.Format(CultureInfo.CurrentCulture, GetString("IRBuilder_PopInvalid"), p0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The specified encoding '{0}' does not match the content's encoding '{1}'.
|
/// The specified encoding '{0}' does not match the content's encoding '{1}'.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string MismatchedContentEncoding
|
internal static string MismatchedContentEncoding
|
||||||
{
|
{
|
||||||
get { return GetString("MismatchedContentEncoding"); }
|
get => GetString("MismatchedContentEncoding");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The specified encoding '{0}' does not match the content's encoding '{1}'.
|
/// The specified encoding '{0}' does not match the content's encoding '{1}'.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string FormatMismatchedContentEncoding(object p0, object p1)
|
internal static string FormatMismatchedContentEncoding(object p0, object p1)
|
||||||
{
|
=> string.Format(CultureInfo.CurrentCulture, GetString("MismatchedContentEncoding"), p0, p1);
|
||||||
return string.Format(CultureInfo.CurrentCulture, GetString("MismatchedContentEncoding"), p0, p1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The '{0}' phase requires a '{1}' provided by the '{2}'.
|
/// The '{0}' phase requires a '{1}' provided by the '{2}'.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string PhaseDependencyMissing
|
internal static string PhaseDependencyMissing
|
||||||
{
|
{
|
||||||
get { return GetString("PhaseDependencyMissing"); }
|
get => GetString("PhaseDependencyMissing");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The '{0}' phase requires a '{1}' provided by the '{2}'.
|
/// The '{0}' phase requires a '{1}' provided by the '{2}'.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string FormatPhaseDependencyMissing(object p0, object p1, object p2)
|
internal static string FormatPhaseDependencyMissing(object p0, object p1, object p2)
|
||||||
{
|
=> string.Format(CultureInfo.CurrentCulture, GetString("PhaseDependencyMissing"), p0, p1, p2);
|
||||||
return string.Format(CultureInfo.CurrentCulture, GetString("PhaseDependencyMissing"), p0, p1, p2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The phase must be initialized by setting the '{0}' property.
|
/// The phase must be initialized by setting the '{0}' property.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string PhaseMustBeInitialized
|
internal static string PhaseMustBeInitialized
|
||||||
{
|
{
|
||||||
get { return GetString("PhaseMustBeInitialized"); }
|
get => GetString("PhaseMustBeInitialized");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The phase must be initialized by setting the '{0}' property.
|
/// The phase must be initialized by setting the '{0}' property.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string FormatPhaseMustBeInitialized(object p0)
|
internal static string FormatPhaseMustBeInitialized(object p0)
|
||||||
{
|
=> string.Format(CultureInfo.CurrentCulture, GetString("PhaseMustBeInitialized"), p0);
|
||||||
return string.Format(CultureInfo.CurrentCulture, GetString("PhaseMustBeInitialized"), p0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tag helper directive assembly name cannot be null or empty.
|
/// Tag helper directive assembly name cannot be null or empty.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string TagHelperAssemblyNameCannotBeEmptyOrNull
|
internal static string TagHelperAssemblyNameCannotBeEmptyOrNull
|
||||||
{
|
{
|
||||||
get { return GetString("TagHelperAssemblyNameCannotBeEmptyOrNull"); }
|
get => GetString("TagHelperAssemblyNameCannotBeEmptyOrNull");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tag helper directive assembly name cannot be null or empty.
|
/// Tag helper directive assembly name cannot be null or empty.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string FormatTagHelperAssemblyNameCannotBeEmptyOrNull()
|
internal static string FormatTagHelperAssemblyNameCannotBeEmptyOrNull()
|
||||||
{
|
=> GetString("TagHelperAssemblyNameCannotBeEmptyOrNull");
|
||||||
return GetString("TagHelperAssemblyNameCannotBeEmptyOrNull");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The assembly '{0}' could not be resolved or contains no tag helpers.
|
/// The assembly '{0}' could not be resolved or contains no tag helpers.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string TagHelperAssemblyCouldNotBeResolved
|
internal static string TagHelperAssemblyCouldNotBeResolved
|
||||||
{
|
{
|
||||||
get { return GetString("TagHelperAssemblyCouldNotBeResolved"); }
|
get => GetString("TagHelperAssemblyCouldNotBeResolved");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The assembly '{0}' could not be resolved or contains no tag helpers.
|
/// The assembly '{0}' could not be resolved or contains no tag helpers.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string FormatTagHelperAssemblyCouldNotBeResolved(object p0)
|
internal static string FormatTagHelperAssemblyCouldNotBeResolved(object p0)
|
||||||
{
|
=> string.Format(CultureInfo.CurrentCulture, GetString("TagHelperAssemblyCouldNotBeResolved"), p0);
|
||||||
return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperAssemblyCouldNotBeResolved"), p0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Path must begin with a forward slash '/'.
|
/// Path must begin with a forward slash '/'.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string RazorProject_PathMustStartWithForwardSlash
|
internal static string RazorProject_PathMustStartWithForwardSlash
|
||||||
{
|
{
|
||||||
get { return GetString("RazorProject_PathMustStartWithForwardSlash"); }
|
get => GetString("RazorProject_PathMustStartWithForwardSlash");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Path must begin with a forward slash '/'.
|
/// Path must begin with a forward slash '/'.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string FormatRazorProject_PathMustStartWithForwardSlash()
|
internal static string FormatRazorProject_PathMustStartWithForwardSlash()
|
||||||
{
|
=> GetString("RazorProject_PathMustStartWithForwardSlash");
|
||||||
return GetString("RazorProject_PathMustStartWithForwardSlash");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The method '{0}' has already been invoked.
|
/// The method '{0}' has already been invoked.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string DirectiveDescriptor_BeginOptionalsAlreadyInvoked
|
internal static string DirectiveDescriptor_BeginOptionalsAlreadyInvoked
|
||||||
{
|
{
|
||||||
get { return GetString("DirectiveDescriptor_BeginOptionalsAlreadyInvoked"); }
|
get => GetString("DirectiveDescriptor_BeginOptionalsAlreadyInvoked");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The method '{0}' has already been invoked.
|
/// The method '{0}' has already been invoked.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string FormatDirectiveDescriptor_BeginOptionalsAlreadyInvoked(object p0)
|
internal static string FormatDirectiveDescriptor_BeginOptionalsAlreadyInvoked(object p0)
|
||||||
{
|
=> string.Format(CultureInfo.CurrentCulture, GetString("DirectiveDescriptor_BeginOptionalsAlreadyInvoked"), p0);
|
||||||
return string.Format(CultureInfo.CurrentCulture, GetString("DirectiveDescriptor_BeginOptionalsAlreadyInvoked"), p0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The document of kind '{0}' does not have a '{1}'. The document classifier must set a value for '{2}'.
|
/// The document of kind '{0}' does not have a '{1}'. The document classifier must set a value for '{2}'.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string DocumentMissingTarget
|
internal static string DocumentMissingTarget
|
||||||
{
|
{
|
||||||
get { return GetString("DocumentMissingTarget"); }
|
get => GetString("DocumentMissingTarget");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The document of kind '{0}' does not have a '{1}'. The document classifier must set a value for '{2}'.
|
/// The document of kind '{0}' does not have a '{1}'. The document classifier must set a value for '{2}'.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string FormatDocumentMissingTarget(object p0, object p1, object p2)
|
internal static string FormatDocumentMissingTarget(object p0, object p1, object p2)
|
||||||
{
|
=> string.Format(CultureInfo.CurrentCulture, GetString("DocumentMissingTarget"), p0, p1, p2);
|
||||||
return string.Format(CultureInfo.CurrentCulture, GetString("DocumentMissingTarget"), p0, p1, p2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The item '{0}' could not be found.
|
/// The item '{0}' could not be found.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string RazorTemplateEngine_ItemCouldNotBeFound
|
internal static string RazorTemplateEngine_ItemCouldNotBeFound
|
||||||
{
|
{
|
||||||
get { return GetString("RazorTemplateEngine_ItemCouldNotBeFound"); }
|
get => GetString("RazorTemplateEngine_ItemCouldNotBeFound");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The item '{0}' could not be found.
|
/// The item '{0}' could not be found.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string FormatRazorTemplateEngine_ItemCouldNotBeFound(object p0)
|
internal static string FormatRazorTemplateEngine_ItemCouldNotBeFound(object p0)
|
||||||
|
=> string.Format(CultureInfo.CurrentCulture, GetString("RazorTemplateEngine_ItemCouldNotBeFound"), p0);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with name '{2}' because the name contains a '{3}' character.
|
||||||
|
/// </summary>
|
||||||
|
internal static string InvalidBoundAttributeName
|
||||||
{
|
{
|
||||||
return string.Format(CultureInfo.CurrentCulture, GetString("RazorTemplateEngine_ItemCouldNotBeFound"), p0);
|
get => GetString("InvalidBoundAttributeName");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with name '{2}' because the name contains a '{3}' character.
|
||||||
|
/// </summary>
|
||||||
|
internal static string FormatInvalidBoundAttributeName(object p0, object p1, object p2, object p3)
|
||||||
|
=> string.Format(CultureInfo.CurrentCulture, GetString("InvalidBoundAttributeName"), p0, p1, p2, p3);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with name '{2}' because the name starts with '{3}'.
|
||||||
|
/// </summary>
|
||||||
|
internal static string InvalidBoundAttributeNameStartsWith
|
||||||
|
{
|
||||||
|
get => GetString("InvalidBoundAttributeNameStartsWith");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with name '{2}' because the name starts with '{3}'.
|
||||||
|
/// </summary>
|
||||||
|
internal static string FormatInvalidBoundAttributeNameStartsWith(object p0, object p1, object p2, object p3)
|
||||||
|
=> string.Format(CultureInfo.CurrentCulture, GetString("InvalidBoundAttributeNameStartsWith"), p0, p1, p2, p3);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with a null or empty name.
|
||||||
|
/// </summary>
|
||||||
|
internal static string InvalidBoundAttributeNullOrWhitespace
|
||||||
|
{
|
||||||
|
get => GetString("InvalidBoundAttributeNullOrWhitespace");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with a null or empty name.
|
||||||
|
/// </summary>
|
||||||
|
internal static string FormatInvalidBoundAttributeNullOrWhitespace(object p0, object p1)
|
||||||
|
=> string.Format(CultureInfo.CurrentCulture, GetString("InvalidBoundAttributeNullOrWhitespace"), p0, p1);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with prefix '{2}' because the prefix contains a '{3}' character.
|
||||||
|
/// </summary>
|
||||||
|
internal static string InvalidBoundAttributePrefix
|
||||||
|
{
|
||||||
|
get => GetString("InvalidBoundAttributePrefix");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with prefix '{2}' because the prefix contains a '{3}' character.
|
||||||
|
/// </summary>
|
||||||
|
internal static string FormatInvalidBoundAttributePrefix(object p0, object p1, object p2, object p3)
|
||||||
|
=> string.Format(CultureInfo.CurrentCulture, GetString("InvalidBoundAttributePrefix"), p0, p1, p2, p3);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with prefix '{2}' because the prefix starts with '{3}'.
|
||||||
|
/// </summary>
|
||||||
|
internal static string InvalidBoundAttributePrefixStartsWith
|
||||||
|
{
|
||||||
|
get => GetString("InvalidBoundAttributePrefixStartsWith");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with prefix '{2}' because the prefix starts with '{3}'.
|
||||||
|
/// </summary>
|
||||||
|
internal static string FormatInvalidBoundAttributePrefixStartsWith(object p0, object p1, object p2, object p3)
|
||||||
|
=> string.Format(CultureInfo.CurrentCulture, GetString("InvalidBoundAttributePrefixStartsWith"), p0, p1, p2, p3);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invalid restricted child '{0}' for tag helper '{1}'. Tag helpers cannot restrict child elements that contain a '{2}' character.
|
||||||
|
/// </summary>
|
||||||
|
internal static string InvalidRestrictedChild
|
||||||
|
{
|
||||||
|
get => GetString("InvalidRestrictedChild");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invalid restricted child '{0}' for tag helper '{1}'. Tag helpers cannot restrict child elements that contain a '{2}' character.
|
||||||
|
/// </summary>
|
||||||
|
internal static string FormatInvalidRestrictedChild(object p0, object p1, object p2)
|
||||||
|
=> string.Format(CultureInfo.CurrentCulture, GetString("InvalidRestrictedChild"), p0, p1, p2);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invalid restricted child for tag helper '{0}'. Name cannot be null or whitespace.
|
||||||
|
/// </summary>
|
||||||
|
internal static string InvalidRestrictedChildNullOrWhitespace
|
||||||
|
{
|
||||||
|
get => GetString("InvalidRestrictedChildNullOrWhitespace");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invalid restricted child for tag helper '{0}'. Name cannot be null or whitespace.
|
||||||
|
/// </summary>
|
||||||
|
internal static string FormatInvalidRestrictedChildNullOrWhitespace(object p0)
|
||||||
|
=> string.Format(CultureInfo.CurrentCulture, GetString("InvalidRestrictedChildNullOrWhitespace"), p0);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tag helpers cannot target attribute name '{0}' because it contains a '{1}' character.
|
||||||
|
/// </summary>
|
||||||
|
internal static string InvalidTargetedAttributeName
|
||||||
|
{
|
||||||
|
get => GetString("InvalidTargetedAttributeName");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tag helpers cannot target attribute name '{0}' because it contains a '{1}' character.
|
||||||
|
/// </summary>
|
||||||
|
internal static string FormatInvalidTargetedAttributeName(object p0, object p1)
|
||||||
|
=> string.Format(CultureInfo.CurrentCulture, GetString("InvalidTargetedAttributeName"), p0, p1);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Targeted attribute name cannot be null or whitespace.
|
||||||
|
/// </summary>
|
||||||
|
internal static string InvalidTargetedAttributeNameNullOrWhitespace
|
||||||
|
{
|
||||||
|
get => GetString("InvalidTargetedAttributeNameNullOrWhitespace");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Targeted attribute name cannot be null or whitespace.
|
||||||
|
/// </summary>
|
||||||
|
internal static string FormatInvalidTargetedAttributeNameNullOrWhitespace()
|
||||||
|
=> GetString("InvalidTargetedAttributeNameNullOrWhitespace");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tag helpers cannot target parent tag name '{0}' because it contains a '{1}' character.
|
||||||
|
/// </summary>
|
||||||
|
internal static string InvalidTargetedParentTagName
|
||||||
|
{
|
||||||
|
get => GetString("InvalidTargetedParentTagName");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tag helpers cannot target parent tag name '{0}' because it contains a '{1}' character.
|
||||||
|
/// </summary>
|
||||||
|
internal static string FormatInvalidTargetedParentTagName(object p0, object p1)
|
||||||
|
=> string.Format(CultureInfo.CurrentCulture, GetString("InvalidTargetedParentTagName"), p0, p1);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Targeted parent tag name cannot be null or whitespace.
|
||||||
|
/// </summary>
|
||||||
|
internal static string InvalidTargetedParentTagNameNullOrWhitespace
|
||||||
|
{
|
||||||
|
get => GetString("InvalidTargetedParentTagNameNullOrWhitespace");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Targeted parent tag name cannot be null or whitespace.
|
||||||
|
/// </summary>
|
||||||
|
internal static string FormatInvalidTargetedParentTagNameNullOrWhitespace()
|
||||||
|
=> GetString("InvalidTargetedParentTagNameNullOrWhitespace");
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tag helpers cannot target tag name '{0}' because it contains a '{1}' character.
|
||||||
|
/// </summary>
|
||||||
|
internal static string InvalidTargetedTagName
|
||||||
|
{
|
||||||
|
get => GetString("InvalidTargetedTagName");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tag helpers cannot target tag name '{0}' because it contains a '{1}' character.
|
||||||
|
/// </summary>
|
||||||
|
internal static string FormatInvalidTargetedTagName(object p0, object p1)
|
||||||
|
=> string.Format(CultureInfo.CurrentCulture, GetString("InvalidTargetedTagName"), p0, p1);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Targeted tag name cannot be null or whitespace.
|
||||||
|
/// </summary>
|
||||||
|
internal static string InvalidTargetedTagNameNullOrWhitespace
|
||||||
|
{
|
||||||
|
get => GetString("InvalidTargetedTagNameNullOrWhitespace");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Targeted tag name cannot be null or whitespace.
|
||||||
|
/// </summary>
|
||||||
|
internal static string FormatInvalidTargetedTagNameNullOrWhitespace()
|
||||||
|
=> GetString("InvalidTargetedTagNameNullOrWhitespace");
|
||||||
|
|
||||||
private static string GetString(string name, params string[] formatterNames)
|
private static string GetString(string name, params string[] formatterNames)
|
||||||
{
|
{
|
||||||
var value = _resourceManager.GetString(name);
|
var value = _resourceManager.GetString(name);
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,28 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
{
|
{
|
||||||
public static class RazorCodeDocumentExtensions
|
public static class RazorCodeDocumentExtensions
|
||||||
{
|
{
|
||||||
|
private static object TagHelperPrefixKey = new object();
|
||||||
|
|
||||||
|
public static string GetTagHelperPrefix(this RazorCodeDocument document)
|
||||||
|
{
|
||||||
|
if (document == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(document));
|
||||||
|
}
|
||||||
|
|
||||||
|
return document.Items[TagHelperPrefixKey] as string;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SetTagHelperPrefix(this RazorCodeDocument document, string tagHelperPrefix)
|
||||||
|
{
|
||||||
|
if (document == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(document));
|
||||||
|
}
|
||||||
|
|
||||||
|
document.Items[TagHelperPrefixKey] = tagHelperPrefix;
|
||||||
|
}
|
||||||
|
|
||||||
public static RazorSyntaxTree GetSyntaxTree(this RazorCodeDocument document)
|
public static RazorSyntaxTree GetSyntaxTree(this RazorCodeDocument document)
|
||||||
{
|
{
|
||||||
if (document == null)
|
if (document == null)
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ using System.Diagnostics;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Razor.Evolution
|
namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
{
|
{
|
||||||
[DebuggerDisplay("{Error} {Id}: {GetMessageFormat()}")]
|
[DebuggerDisplay("Error {Id}: {GetMessageFormat()}")]
|
||||||
public sealed class RazorDiagnosticDescriptor : IEquatable<RazorDiagnosticDescriptor>
|
public sealed class RazorDiagnosticDescriptor : IEquatable<RazorDiagnosticDescriptor>
|
||||||
{
|
{
|
||||||
private readonly Func<string> _messageFormat;
|
private readonly Func<string> _messageFormat;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,268 @@
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
|
{
|
||||||
|
internal static class RazorDiagnosticFactory
|
||||||
|
{
|
||||||
|
private const string DiagnosticPrefix = "RZ";
|
||||||
|
|
||||||
|
#region General Errors
|
||||||
|
|
||||||
|
/*
|
||||||
|
* General Errors ID Offset = 0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Language Errors
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Language Errors ID Offset = 1000
|
||||||
|
*/
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Semantic Errors
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Semantic Errors ID Offset = 2000
|
||||||
|
*/
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region TagHelper Errors
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TagHelper Errors ID Offset = 3000
|
||||||
|
*/
|
||||||
|
|
||||||
|
private static readonly RazorDiagnosticDescriptor TagHelper_InvalidRestrictedChildNullOrWhitespace =
|
||||||
|
new RazorDiagnosticDescriptor(
|
||||||
|
$"{DiagnosticPrefix}3000",
|
||||||
|
() => Resources.InvalidRestrictedChildNullOrWhitespace,
|
||||||
|
RazorDiagnosticSeverity.Error);
|
||||||
|
public static RazorDiagnostic CreateTagHelper_InvalidRestrictedChildNullOrWhitespace(string tagHelperType)
|
||||||
|
{
|
||||||
|
var diagnostic = RazorDiagnostic.Create(
|
||||||
|
TagHelper_InvalidRestrictedChildNullOrWhitespace,
|
||||||
|
new SourceSpan(SourceLocation.Undefined, contentLength: 0),
|
||||||
|
tagHelperType);
|
||||||
|
|
||||||
|
return diagnostic;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static readonly RazorDiagnosticDescriptor TagHelper_InvalidRestrictedChild =
|
||||||
|
new RazorDiagnosticDescriptor(
|
||||||
|
$"{DiagnosticPrefix}3001",
|
||||||
|
() => Resources.InvalidRestrictedChild,
|
||||||
|
RazorDiagnosticSeverity.Error);
|
||||||
|
public static RazorDiagnostic CreateTagHelper_InvalidRestrictedChild(string restrictedChild, string tagHelperType, char invalidCharacter)
|
||||||
|
{
|
||||||
|
var diagnostic = RazorDiagnostic.Create(
|
||||||
|
TagHelper_InvalidRestrictedChild,
|
||||||
|
new SourceSpan(SourceLocation.Undefined, contentLength: 0),
|
||||||
|
restrictedChild,
|
||||||
|
tagHelperType,
|
||||||
|
invalidCharacter);
|
||||||
|
|
||||||
|
return diagnostic;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static readonly RazorDiagnosticDescriptor TagHelper_InvalidBoundAttributeNullOrWhitespace =
|
||||||
|
new RazorDiagnosticDescriptor(
|
||||||
|
$"{DiagnosticPrefix}3002",
|
||||||
|
() => Resources.InvalidBoundAttributeNullOrWhitespace,
|
||||||
|
RazorDiagnosticSeverity.Error);
|
||||||
|
public static RazorDiagnostic CreateTagHelper_InvalidBoundAttributeNullOrWhitespace(string containingTypeName, string propertyName)
|
||||||
|
{
|
||||||
|
var diagnostic = RazorDiagnostic.Create(
|
||||||
|
TagHelper_InvalidBoundAttributeNullOrWhitespace,
|
||||||
|
new SourceSpan(SourceLocation.Undefined, contentLength: 0),
|
||||||
|
containingTypeName,
|
||||||
|
propertyName);
|
||||||
|
|
||||||
|
return diagnostic;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static readonly RazorDiagnosticDescriptor TagHelper_InvalidBoundAttributeName =
|
||||||
|
new RazorDiagnosticDescriptor(
|
||||||
|
$"{DiagnosticPrefix}3003",
|
||||||
|
() => Resources.InvalidBoundAttributeName,
|
||||||
|
RazorDiagnosticSeverity.Error);
|
||||||
|
public static RazorDiagnostic CreateTagHelper_InvalidBoundAttributeName(
|
||||||
|
string containingTypeName,
|
||||||
|
string propertyName,
|
||||||
|
string invalidName,
|
||||||
|
char invalidCharacter)
|
||||||
|
{
|
||||||
|
var diagnostic = RazorDiagnostic.Create(
|
||||||
|
TagHelper_InvalidBoundAttributeName,
|
||||||
|
new SourceSpan(SourceLocation.Undefined, contentLength: 0),
|
||||||
|
containingTypeName,
|
||||||
|
propertyName,
|
||||||
|
invalidName,
|
||||||
|
invalidCharacter);
|
||||||
|
|
||||||
|
return diagnostic;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static readonly RazorDiagnosticDescriptor TagHelper_InvalidBoundAttributeNameStartsWith =
|
||||||
|
new RazorDiagnosticDescriptor(
|
||||||
|
$"{DiagnosticPrefix}3004",
|
||||||
|
() => Resources.InvalidBoundAttributeNameStartsWith,
|
||||||
|
RazorDiagnosticSeverity.Error);
|
||||||
|
public static RazorDiagnostic CreateTagHelper_InvalidBoundAttributeNameStartsWith(
|
||||||
|
string containingTypeName,
|
||||||
|
string propertyName,
|
||||||
|
string invalidName)
|
||||||
|
{
|
||||||
|
var diagnostic = RazorDiagnostic.Create(
|
||||||
|
TagHelper_InvalidBoundAttributeNameStartsWith,
|
||||||
|
new SourceSpan(SourceLocation.Undefined, contentLength: 0),
|
||||||
|
containingTypeName,
|
||||||
|
propertyName,
|
||||||
|
invalidName,
|
||||||
|
"data-");
|
||||||
|
|
||||||
|
return diagnostic;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static readonly RazorDiagnosticDescriptor TagHelper_InvalidBoundAttributePrefix =
|
||||||
|
new RazorDiagnosticDescriptor(
|
||||||
|
$"{DiagnosticPrefix}3005",
|
||||||
|
() => Resources.InvalidBoundAttributePrefix,
|
||||||
|
RazorDiagnosticSeverity.Error);
|
||||||
|
public static RazorDiagnostic CreateTagHelper_InvalidBoundAttributePrefix(
|
||||||
|
string containingTypeName,
|
||||||
|
string propertyName,
|
||||||
|
string invalidName,
|
||||||
|
char invalidCharacter)
|
||||||
|
{
|
||||||
|
var diagnostic = RazorDiagnostic.Create(
|
||||||
|
TagHelper_InvalidBoundAttributePrefix,
|
||||||
|
new SourceSpan(SourceLocation.Undefined, contentLength: 0),
|
||||||
|
containingTypeName,
|
||||||
|
propertyName,
|
||||||
|
invalidName,
|
||||||
|
invalidCharacter);
|
||||||
|
|
||||||
|
return diagnostic;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static readonly RazorDiagnosticDescriptor TagHelper_InvalidBoundAttributePrefixStartsWith =
|
||||||
|
new RazorDiagnosticDescriptor(
|
||||||
|
$"{DiagnosticPrefix}3006",
|
||||||
|
() => Resources.InvalidBoundAttributePrefixStartsWith,
|
||||||
|
RazorDiagnosticSeverity.Error);
|
||||||
|
public static RazorDiagnostic CreateTagHelper_InvalidBoundAttributePrefixStartsWith(
|
||||||
|
string containingTypeName,
|
||||||
|
string propertyName,
|
||||||
|
string invalidName)
|
||||||
|
{
|
||||||
|
var diagnostic = RazorDiagnostic.Create(
|
||||||
|
TagHelper_InvalidBoundAttributePrefixStartsWith,
|
||||||
|
new SourceSpan(SourceLocation.Undefined, contentLength: 0),
|
||||||
|
containingTypeName,
|
||||||
|
propertyName,
|
||||||
|
invalidName,
|
||||||
|
"data-");
|
||||||
|
|
||||||
|
return diagnostic;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static readonly RazorDiagnosticDescriptor TagHelper_InvalidTargetedTagNameNullOrWhitespace =
|
||||||
|
new RazorDiagnosticDescriptor(
|
||||||
|
$"{DiagnosticPrefix}3007",
|
||||||
|
() => Resources.InvalidTargetedTagNameNullOrWhitespace,
|
||||||
|
RazorDiagnosticSeverity.Error);
|
||||||
|
public static RazorDiagnostic CreateTagHelper_InvalidTargetedTagNameNullOrWhitespace()
|
||||||
|
{
|
||||||
|
var diagnostic = RazorDiagnostic.Create(
|
||||||
|
TagHelper_InvalidTargetedTagNameNullOrWhitespace,
|
||||||
|
new SourceSpan(SourceLocation.Undefined, contentLength: 0));
|
||||||
|
|
||||||
|
return diagnostic;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static readonly RazorDiagnosticDescriptor TagHelper_InvalidTargetedTagName =
|
||||||
|
new RazorDiagnosticDescriptor(
|
||||||
|
$"{DiagnosticPrefix}3008",
|
||||||
|
() => Resources.InvalidTargetedTagName,
|
||||||
|
RazorDiagnosticSeverity.Error);
|
||||||
|
public static RazorDiagnostic CreateTagHelper_InvalidTargetedTagName(string invalidTagName, char invalidCharacter)
|
||||||
|
{
|
||||||
|
var diagnostic = RazorDiagnostic.Create(
|
||||||
|
TagHelper_InvalidTargetedTagName,
|
||||||
|
new SourceSpan(SourceLocation.Undefined, contentLength: 0),
|
||||||
|
invalidTagName,
|
||||||
|
invalidCharacter);
|
||||||
|
|
||||||
|
return diagnostic;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static readonly RazorDiagnosticDescriptor TagHelper_InvalidTargetedParentTagNameNullOrWhitespace =
|
||||||
|
new RazorDiagnosticDescriptor(
|
||||||
|
$"{DiagnosticPrefix}3009",
|
||||||
|
() => Resources.InvalidTargetedParentTagNameNullOrWhitespace,
|
||||||
|
RazorDiagnosticSeverity.Error);
|
||||||
|
public static RazorDiagnostic CreateTagHelper_InvalidTargetedParentTagNameNullOrWhitespace()
|
||||||
|
{
|
||||||
|
var diagnostic = RazorDiagnostic.Create(
|
||||||
|
TagHelper_InvalidTargetedParentTagNameNullOrWhitespace,
|
||||||
|
new SourceSpan(SourceLocation.Undefined, contentLength: 0));
|
||||||
|
|
||||||
|
return diagnostic;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static readonly RazorDiagnosticDescriptor TagHelper_InvalidTargetedParentTagName =
|
||||||
|
new RazorDiagnosticDescriptor(
|
||||||
|
$"{DiagnosticPrefix}3010",
|
||||||
|
() => Resources.InvalidTargetedParentTagName,
|
||||||
|
RazorDiagnosticSeverity.Error);
|
||||||
|
public static RazorDiagnostic CreateTagHelper_InvalidTargetedParentTagName(string invalidTagName, char invalidCharacter)
|
||||||
|
{
|
||||||
|
var diagnostic = RazorDiagnostic.Create(
|
||||||
|
TagHelper_InvalidTargetedParentTagName,
|
||||||
|
new SourceSpan(SourceLocation.Undefined, contentLength: 0),
|
||||||
|
invalidTagName,
|
||||||
|
invalidCharacter);
|
||||||
|
|
||||||
|
return diagnostic;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static readonly RazorDiagnosticDescriptor TagHelper_InvalidTargetedAttributeNameNullOrWhitespace =
|
||||||
|
new RazorDiagnosticDescriptor(
|
||||||
|
$"{DiagnosticPrefix}3009",
|
||||||
|
() => Resources.InvalidTargetedAttributeNameNullOrWhitespace,
|
||||||
|
RazorDiagnosticSeverity.Error);
|
||||||
|
public static RazorDiagnostic CreateTagHelper_InvalidTargetedAttributeNameNullOrWhitespace()
|
||||||
|
{
|
||||||
|
var diagnostic = RazorDiagnostic.Create(
|
||||||
|
TagHelper_InvalidTargetedAttributeNameNullOrWhitespace,
|
||||||
|
new SourceSpan(SourceLocation.Undefined, contentLength: 0));
|
||||||
|
|
||||||
|
return diagnostic;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static readonly RazorDiagnosticDescriptor TagHelper_InvalidTargetedAttributeName =
|
||||||
|
new RazorDiagnosticDescriptor(
|
||||||
|
$"{DiagnosticPrefix}3010",
|
||||||
|
() => Resources.InvalidTargetedAttributeName,
|
||||||
|
RazorDiagnosticSeverity.Error);
|
||||||
|
public static RazorDiagnostic CreateTagHelper_InvalidTargetedAttributeName(string invalidAttributeName, char invalidCharacter)
|
||||||
|
{
|
||||||
|
var diagnostic = RazorDiagnostic.Create(
|
||||||
|
TagHelper_InvalidTargetedAttributeName,
|
||||||
|
new SourceSpan(SourceLocation.Undefined, contentLength: 0),
|
||||||
|
invalidAttributeName,
|
||||||
|
invalidCharacter);
|
||||||
|
|
||||||
|
return diagnostic;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -88,7 +88,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
|
|
||||||
public override void VisitSetTagHelperProperty(SetTagHelperPropertyIRNode node)
|
public override void VisitSetTagHelperProperty(SetTagHelperPropertyIRNode node)
|
||||||
{
|
{
|
||||||
if (!node.Descriptor.IsStringProperty ||
|
if (!(node.Descriptor.IsStringProperty || (node.IsIndexerNameMatch && node.Descriptor.IsIndexerStringProperty)) ||
|
||||||
node.Children.Count != 1 ||
|
node.Children.Count != 1 ||
|
||||||
!(node.Children.First() is HtmlContentIRNode))
|
!(node.Children.First() is HtmlContentIRNode))
|
||||||
{
|
{
|
||||||
|
|
@ -139,7 +139,9 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
TagHelperTypeName = node.TagHelperTypeName,
|
TagHelperTypeName = node.TagHelperTypeName,
|
||||||
PropertyName = node.PropertyName,
|
PropertyName = node.PropertyName,
|
||||||
Descriptor = node.Descriptor,
|
Descriptor = node.Descriptor,
|
||||||
Parent = node.Parent
|
Binding = node.Binding,
|
||||||
|
Parent = node.Parent,
|
||||||
|
IsIndexerNameMatch = node.IsIndexerNameMatch,
|
||||||
};
|
};
|
||||||
|
|
||||||
var nodeIndex = node.Parent.Children.IndexOf(node);
|
var nodeIndex = node.Parent.Children.IndexOf(node);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,75 @@
|
||||||
|
// 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.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
|
{
|
||||||
|
public abstract class RequiredAttributeDescriptor
|
||||||
|
{
|
||||||
|
public string Name { get; protected set; }
|
||||||
|
|
||||||
|
public NameComparisonMode NameComparison { get; protected set; }
|
||||||
|
|
||||||
|
public string Value { get; protected set; }
|
||||||
|
|
||||||
|
public ValueComparisonMode ValueComparison { get; protected set; }
|
||||||
|
|
||||||
|
public IReadOnlyList<RazorDiagnostic> Diagnostics { get; protected set; }
|
||||||
|
|
||||||
|
public bool HasAnyErrors
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var anyErrors = Diagnostics.Any(diagnostic => diagnostic.Severity == RazorDiagnosticSeverity.Error);
|
||||||
|
|
||||||
|
return anyErrors;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Acceptable <see cref="RequiredAttributeDescriptor.Name"/> comparison modes.
|
||||||
|
/// </summary>
|
||||||
|
public enum NameComparisonMode
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// HTML attribute name case insensitively matches <see cref="RequiredAttributeDescriptor.Name"/>.
|
||||||
|
/// </summary>
|
||||||
|
FullMatch,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// HTML attribute name case insensitively starts with <see cref="RequiredAttributeDescriptor.Name"/>.
|
||||||
|
/// </summary>
|
||||||
|
PrefixMatch,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Acceptable <see cref="RequiredAttributeDescriptor.Value"/> comparison modes.
|
||||||
|
/// </summary>
|
||||||
|
public enum ValueComparisonMode
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// HTML attribute value always matches <see cref="RequiredAttributeDescriptor.Value"/>.
|
||||||
|
/// </summary>
|
||||||
|
None,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// HTML attribute value case sensitively matches <see cref="RequiredAttributeDescriptor.Value"/>.
|
||||||
|
/// </summary>
|
||||||
|
FullMatch,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// HTML attribute value case sensitively starts with <see cref="RequiredAttributeDescriptor.Value"/>.
|
||||||
|
/// </summary>
|
||||||
|
PrefixMatch,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// HTML attribute value case sensitively ends with <see cref="RequiredAttributeDescriptor.Value"/>.
|
||||||
|
/// </summary>
|
||||||
|
SuffixMatch,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,141 @@
|
||||||
|
// 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
|
||||||
|
{
|
||||||
|
public sealed class RequiredAttributeDescriptorBuilder
|
||||||
|
{
|
||||||
|
private static ICollection<char> InvalidNonWhitespaceAttributeNameCharacters { get; } = new HashSet<char>(
|
||||||
|
new[] { '@', '!', '<', '/', '?', '[', '>', ']', '=', '"', '\'', '*' });
|
||||||
|
|
||||||
|
private string _name;
|
||||||
|
private RequiredAttributeDescriptor.NameComparisonMode _nameComparison;
|
||||||
|
private string _value;
|
||||||
|
private RequiredAttributeDescriptor.ValueComparisonMode _valueComparison;
|
||||||
|
private HashSet<RazorDiagnostic> _diagnostics;
|
||||||
|
|
||||||
|
private RequiredAttributeDescriptorBuilder()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public static RequiredAttributeDescriptorBuilder Create()
|
||||||
|
{
|
||||||
|
return new RequiredAttributeDescriptorBuilder();
|
||||||
|
}
|
||||||
|
|
||||||
|
public RequiredAttributeDescriptorBuilder Name(string name)
|
||||||
|
{
|
||||||
|
_name = name;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RequiredAttributeDescriptorBuilder NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode nameComparison)
|
||||||
|
{
|
||||||
|
_nameComparison = nameComparison;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RequiredAttributeDescriptorBuilder Value(string value)
|
||||||
|
{
|
||||||
|
_value = value;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RequiredAttributeDescriptorBuilder ValueComparisonMode(RequiredAttributeDescriptor.ValueComparisonMode valueComparison)
|
||||||
|
{
|
||||||
|
_valueComparison = valueComparison;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RequiredAttributeDescriptorBuilder AddDiagnostic(RazorDiagnostic diagnostic)
|
||||||
|
{
|
||||||
|
EnsureDiagnostics();
|
||||||
|
_diagnostics.Add(diagnostic);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RequiredAttributeDescriptor Build()
|
||||||
|
{
|
||||||
|
var validationDiagnostics = Validate();
|
||||||
|
var diagnostics = new HashSet<RazorDiagnostic>(validationDiagnostics);
|
||||||
|
if (_diagnostics != null)
|
||||||
|
{
|
||||||
|
diagnostics.UnionWith(_diagnostics);
|
||||||
|
}
|
||||||
|
|
||||||
|
var rule = new DefaultTagHelperRequiredAttributeDescriptor(
|
||||||
|
_name,
|
||||||
|
_nameComparison,
|
||||||
|
_value,
|
||||||
|
_valueComparison,
|
||||||
|
diagnostics);
|
||||||
|
|
||||||
|
return rule;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Reset()
|
||||||
|
{
|
||||||
|
_name = null;
|
||||||
|
_value = null;
|
||||||
|
_nameComparison = default(RequiredAttributeDescriptor.NameComparisonMode);
|
||||||
|
_valueComparison = default(RequiredAttributeDescriptor.ValueComparisonMode);
|
||||||
|
_diagnostics?.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerable<RazorDiagnostic> Validate()
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(_name))
|
||||||
|
{
|
||||||
|
var diagnostic = RazorDiagnosticFactory.CreateTagHelper_InvalidTargetedAttributeNameNullOrWhitespace();
|
||||||
|
|
||||||
|
yield return diagnostic;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach (var character in _name)
|
||||||
|
{
|
||||||
|
if (char.IsWhiteSpace(character) || InvalidNonWhitespaceAttributeNameCharacters.Contains(character))
|
||||||
|
{
|
||||||
|
var diagnostic = RazorDiagnosticFactory.CreateTagHelper_InvalidTargetedAttributeName(_name, character);
|
||||||
|
|
||||||
|
yield return diagnostic;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EnsureDiagnostics()
|
||||||
|
{
|
||||||
|
if (_diagnostics == null)
|
||||||
|
{
|
||||||
|
_diagnostics = new HashSet<RazorDiagnostic>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DefaultTagHelperRequiredAttributeDescriptor : RequiredAttributeDescriptor
|
||||||
|
{
|
||||||
|
public DefaultTagHelperRequiredAttributeDescriptor(
|
||||||
|
string name,
|
||||||
|
NameComparisonMode nameComparison,
|
||||||
|
string value,
|
||||||
|
ValueComparisonMode valueComparison,
|
||||||
|
IEnumerable<RazorDiagnostic> diagnostics)
|
||||||
|
{
|
||||||
|
Name = name;
|
||||||
|
NameComparison = nameComparison;
|
||||||
|
Value = value;
|
||||||
|
ValueComparison = valueComparison;
|
||||||
|
Diagnostics = new List<RazorDiagnostic>(diagnostics);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -159,4 +159,43 @@
|
||||||
<data name="RazorTemplateEngine_ItemCouldNotBeFound" xml:space="preserve">
|
<data name="RazorTemplateEngine_ItemCouldNotBeFound" xml:space="preserve">
|
||||||
<value>The item '{0}' could not be found.</value>
|
<value>The item '{0}' could not be found.</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="InvalidBoundAttributeName" xml:space="preserve">
|
||||||
|
<value>Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with name '{2}' because the name contains a '{3}' character.</value>
|
||||||
|
</data>
|
||||||
|
<data name="InvalidBoundAttributeNameStartsWith" xml:space="preserve">
|
||||||
|
<value>Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with name '{2}' because the name starts with '{3}'.</value>
|
||||||
|
</data>
|
||||||
|
<data name="InvalidBoundAttributeNullOrWhitespace" xml:space="preserve">
|
||||||
|
<value>Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with a null or empty name.</value>
|
||||||
|
</data>
|
||||||
|
<data name="InvalidBoundAttributePrefix" xml:space="preserve">
|
||||||
|
<value>Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with prefix '{2}' because the prefix contains a '{3}' character.</value>
|
||||||
|
</data>
|
||||||
|
<data name="InvalidBoundAttributePrefixStartsWith" xml:space="preserve">
|
||||||
|
<value>Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with prefix '{2}' because the prefix starts with '{3}'.</value>
|
||||||
|
</data>
|
||||||
|
<data name="InvalidRestrictedChild" xml:space="preserve">
|
||||||
|
<value>Invalid restricted child '{0}' for tag helper '{1}'. Tag helpers cannot restrict child elements that contain a '{2}' character.</value>
|
||||||
|
</data>
|
||||||
|
<data name="InvalidRestrictedChildNullOrWhitespace" xml:space="preserve">
|
||||||
|
<value>Invalid restricted child for tag helper '{0}'. Name cannot be null or whitespace.</value>
|
||||||
|
</data>
|
||||||
|
<data name="InvalidTargetedAttributeName" xml:space="preserve">
|
||||||
|
<value>Tag helpers cannot target attribute name '{0}' because it contains a '{1}' character.</value>
|
||||||
|
</data>
|
||||||
|
<data name="InvalidTargetedAttributeNameNullOrWhitespace" xml:space="preserve">
|
||||||
|
<value>Targeted attribute name cannot be null or whitespace.</value>
|
||||||
|
</data>
|
||||||
|
<data name="InvalidTargetedParentTagName" xml:space="preserve">
|
||||||
|
<value>Tag helpers cannot target parent tag name '{0}' because it contains a '{1}' character.</value>
|
||||||
|
</data>
|
||||||
|
<data name="InvalidTargetedParentTagNameNullOrWhitespace" xml:space="preserve">
|
||||||
|
<value>Targeted parent tag name cannot be null or whitespace.</value>
|
||||||
|
</data>
|
||||||
|
<data name="InvalidTargetedTagName" xml:space="preserve">
|
||||||
|
<value>Tag helpers cannot target tag name '{0}' because it contains a '{1}' character.</value>
|
||||||
|
</data>
|
||||||
|
<data name="InvalidTargetedTagNameNullOrWhitespace" xml:space="preserve">
|
||||||
|
<value>Targeted tag name cannot be null or whitespace.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|
@ -1,156 +0,0 @@
|
||||||
// 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.Reflection;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Razor.Evolution
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// A metadata class describing a tag helper attribute.
|
|
||||||
/// </summary>
|
|
||||||
public class TagHelperAttributeDescriptor
|
|
||||||
{
|
|
||||||
private string _typeName;
|
|
||||||
private string _name;
|
|
||||||
private string _propertyName;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Instantiates a new instance of the <see cref="TagHelperAttributeDescriptor"/> class.
|
|
||||||
/// </summary>
|
|
||||||
public TagHelperAttributeDescriptor()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
// Internal for testing i.e. for easy TagHelperAttributeDescriptor creation when PropertyInfo is available.
|
|
||||||
internal TagHelperAttributeDescriptor(string name, PropertyInfo propertyInfo)
|
|
||||||
{
|
|
||||||
Name = name;
|
|
||||||
PropertyName = propertyInfo.Name;
|
|
||||||
TypeName = propertyInfo.PropertyType.FullName;
|
|
||||||
IsEnum = propertyInfo.PropertyType.GetTypeInfo().IsEnum;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets an indication whether this <see cref="TagHelperAttributeDescriptor"/> is used for dictionary indexer
|
|
||||||
/// assignments.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// If <c>true</c> this <see cref="TagHelperAttributeDescriptor"/> should be associated with all HTML
|
|
||||||
/// attributes that have names starting with <see cref="Name"/>. Otherwise this
|
|
||||||
/// <see cref="TagHelperAttributeDescriptor"/> is used for property assignment and is only associated with an
|
|
||||||
/// HTML attribute that has the exact <see cref="Name"/>.
|
|
||||||
/// </value>
|
|
||||||
/// <remarks>
|
|
||||||
/// HTML attribute names are matched case-insensitively, regardless of <see cref="IsIndexer"/>.
|
|
||||||
/// </remarks>
|
|
||||||
public bool IsIndexer { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets an indication whether this property is an <see cref="Enum"/>.
|
|
||||||
/// </summary>
|
|
||||||
public bool IsEnum { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets an indication whether this property is of type <see cref="string"/> or, if
|
|
||||||
/// <see cref="IsIndexer"/> is <c>true</c>, whether the indexer's value is of type <see cref="string"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>
|
|
||||||
/// If <c>true</c> the <see cref="TypeName"/> is for <see cref="string"/>. This causes the Razor parser
|
|
||||||
/// to allow empty values for HTML attributes matching this <see cref="TagHelperAttributeDescriptor"/>. If
|
|
||||||
/// <c>false</c> empty values for such matching attributes lead to errors.
|
|
||||||
/// </value>
|
|
||||||
public bool IsStringProperty { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The HTML attribute name or, if <see cref="IsIndexer"/> is <c>true</c>, the prefix for matching attribute
|
|
||||||
/// names.
|
|
||||||
/// </summary>
|
|
||||||
public string Name
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return _name;
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (value == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
_name = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The name of the CLR property that corresponds to the HTML attribute.
|
|
||||||
/// </summary>
|
|
||||||
public string PropertyName
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return _propertyName;
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (value == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
_propertyName = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The full name of the named (see <see name="PropertyName"/>) property's <see cref="Type"/> or, if
|
|
||||||
/// <see cref="IsIndexer"/> is <c>true</c>, the full name of the indexer's value <see cref="Type"/>.
|
|
||||||
/// </summary>
|
|
||||||
public string TypeName
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return _typeName;
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (value == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
_typeName = value;
|
|
||||||
IsStringProperty = string.Equals(TypeName, typeof(string).FullName, StringComparison.Ordinal);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The <see cref="TagHelperAttributeDesignTimeDescriptor"/> that contains design time information about
|
|
||||||
/// this attribute.
|
|
||||||
/// </summary>
|
|
||||||
public TagHelperAttributeDesignTimeDescriptor DesignTimeDescriptor { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Determines whether HTML attribute <paramref name="name"/> matches this
|
|
||||||
/// <see cref="TagHelperAttributeDescriptor"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="name">Name of the HTML attribute to check.</param>
|
|
||||||
/// <returns>
|
|
||||||
/// <c>true</c> if this <see cref="TagHelperAttributeDescriptor"/> matches <paramref name="name"/>.
|
|
||||||
/// <c>false</c> otherwise.
|
|
||||||
/// </returns>
|
|
||||||
public bool IsNameMatch(string name)
|
|
||||||
{
|
|
||||||
if (IsIndexer)
|
|
||||||
{
|
|
||||||
return name.StartsWith(Name, StringComparison.OrdinalIgnoreCase);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return string.Equals(name, Name, StringComparison.OrdinalIgnoreCase);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
// 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
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// A metadata class containing information about tag helper use.
|
|
||||||
/// </summary>
|
|
||||||
public class TagHelperAttributeDesignTimeDescriptor
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// A summary of how to use a tag helper.
|
|
||||||
/// </summary>
|
|
||||||
public string Summary { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Remarks about how to use a tag helper.
|
|
||||||
/// </summary>
|
|
||||||
public string Remarks { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -42,7 +42,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
visitor.VisitBlock(import.Root);
|
visitor.VisitBlock(import.Root);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
visitor.VisitBlock(syntaxTree.Root);
|
visitor.VisitBlock(syntaxTree.Root);
|
||||||
|
|
||||||
var errorList = new List<RazorDiagnostic>();
|
var errorList = new List<RazorDiagnostic>();
|
||||||
|
|
@ -52,6 +52,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
var directives = visitor.Directives;
|
var directives = visitor.Directives;
|
||||||
descriptors = ProcessDirectives(directives, descriptors, errorSink);
|
descriptors = ProcessDirectives(directives, descriptors, errorSink);
|
||||||
|
|
||||||
|
var tagHelperPrefix = ProcessTagHelperPrefix(directives, codeDocument, errorSink);
|
||||||
var root = syntaxTree.Root;
|
var root = syntaxTree.Root;
|
||||||
|
|
||||||
if (descriptors.Count == 0)
|
if (descriptors.Count == 0)
|
||||||
|
|
@ -64,20 +65,51 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var descriptorProvider = new TagHelperDescriptorProvider(descriptors);
|
var descriptorProvider = new TagHelperDescriptorProvider(tagHelperPrefix, descriptors);
|
||||||
var rewriter = new TagHelperParseTreeRewriter(descriptorProvider);
|
var rewriter = new TagHelperParseTreeRewriter(tagHelperPrefix, descriptorProvider);
|
||||||
root = rewriter.Rewrite(root, errorSink);
|
root = rewriter.Rewrite(root, errorSink);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Temporary code while we're still using legacy diagnostics in the SyntaxTree.
|
// Temporary code while we're still using legacy diagnostics in the SyntaxTree.
|
||||||
errorList.AddRange(errorSink.Errors.Select(error => RazorDiagnostic.Create(error)));
|
errorList.AddRange(errorSink.Errors.Select(error => RazorDiagnostic.Create(error)));
|
||||||
|
|
||||||
|
errorList.AddRange(descriptors.SelectMany(d => d.GetAllDiagnostics()));
|
||||||
|
|
||||||
var diagnostics = CombineErrors(syntaxTree.Diagnostics, errorList);
|
var diagnostics = CombineErrors(syntaxTree.Diagnostics, errorList);
|
||||||
|
|
||||||
var newSyntaxTree = RazorSyntaxTree.Create(root, syntaxTree.Source, diagnostics, syntaxTree.Options);
|
var newSyntaxTree = RazorSyntaxTree.Create(root, syntaxTree.Source, diagnostics, syntaxTree.Options);
|
||||||
return newSyntaxTree;
|
return newSyntaxTree;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Internal for testing
|
||||||
|
internal string ProcessTagHelperPrefix(List<TagHelperDirectiveDescriptor> directives, RazorCodeDocument codeDocument, ErrorSink errorSink)
|
||||||
|
{
|
||||||
|
// We only support a single prefix directive.
|
||||||
|
TagHelperDirectiveDescriptor prefixDirective = null;
|
||||||
|
for (var i = 0; i < directives.Count; i++)
|
||||||
|
{
|
||||||
|
if (directives[i].DirectiveType == TagHelperDirectiveType.TagHelperPrefix)
|
||||||
|
{
|
||||||
|
// We only expect to see a single one of these per file, but that's enforced at another level.
|
||||||
|
prefixDirective = directives[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var prefix = prefixDirective?.DirectiveText;
|
||||||
|
if (prefix != null && !IsValidTagHelperPrefix(prefix, prefixDirective.Location, errorSink))
|
||||||
|
{
|
||||||
|
prefix = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(prefix))
|
||||||
|
{
|
||||||
|
codeDocument.SetTagHelperPrefix(prefixDirective.DirectiveText);
|
||||||
|
return prefixDirective.DirectiveText;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
internal IReadOnlyList<TagHelperDescriptor> ProcessDirectives(
|
internal IReadOnlyList<TagHelperDescriptor> ProcessDirectives(
|
||||||
IReadOnlyList<TagHelperDirectiveDescriptor> directives,
|
IReadOnlyList<TagHelperDirectiveDescriptor> directives,
|
||||||
IReadOnlyList<TagHelperDescriptor> tagHelpers,
|
IReadOnlyList<TagHelperDescriptor> tagHelpers,
|
||||||
|
|
@ -85,9 +117,6 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
{
|
{
|
||||||
var matches = new HashSet<TagHelperDescriptor>(TagHelperDescriptorComparer.Default);
|
var matches = new HashSet<TagHelperDescriptor>(TagHelperDescriptorComparer.Default);
|
||||||
|
|
||||||
// We only support a single prefix directive.
|
|
||||||
TagHelperDirectiveDescriptor prefixDirective = null;
|
|
||||||
|
|
||||||
for (var i = 0; i < directives.Count; i++)
|
for (var i = 0; i < directives.Count; i++)
|
||||||
{
|
{
|
||||||
var directive = directives[i];
|
var directive = directives[i];
|
||||||
|
|
@ -156,24 +185,11 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case TagHelperDirectiveType.TagHelperPrefix:
|
|
||||||
|
|
||||||
// We only expect to see a single one of these per file, but that's enforced at another level.
|
|
||||||
prefixDirective = directive;
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var prefix = prefixDirective?.DirectiveText;
|
return matches.ToArray();
|
||||||
if (prefix != null && !IsValidTagHelperPrefix(prefix, prefixDirective.Location, errorSink))
|
|
||||||
{
|
|
||||||
prefix = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return PrefixDescriptors(prefix, matches);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool AssemblyContainsTagHelpers(string assemblyName, IReadOnlyList<TagHelperDescriptor> tagHelpers)
|
private bool AssemblyContainsTagHelpers(string assemblyName, IReadOnlyList<TagHelperDescriptor> tagHelpers)
|
||||||
|
|
@ -254,21 +270,6 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IReadOnlyList<TagHelperDescriptor> PrefixDescriptors(
|
|
||||||
string prefix,
|
|
||||||
IEnumerable<TagHelperDescriptor> descriptors)
|
|
||||||
{
|
|
||||||
if (!string.IsNullOrEmpty(prefix))
|
|
||||||
{
|
|
||||||
return descriptors.Select(descriptor => new TagHelperDescriptor(descriptor)
|
|
||||||
{
|
|
||||||
Prefix = prefix
|
|
||||||
}).ToList();
|
|
||||||
}
|
|
||||||
|
|
||||||
return descriptors.ToList();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool MatchesDirective(TagHelperDescriptor descriptor, ParsedDirective lookupInfo)
|
private static bool MatchesDirective(TagHelperDescriptor descriptor, ParsedDirective lookupInfo)
|
||||||
{
|
{
|
||||||
if (!string.Equals(descriptor.AssemblyName, lookupInfo.AssemblyName, StringComparison.Ordinal))
|
if (!string.Equals(descriptor.AssemblyName, lookupInfo.AssemblyName, StringComparison.Ordinal))
|
||||||
|
|
@ -276,6 +277,14 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (descriptor.Kind != ITagHelperDescriptorBuilder.DescriptorKind)
|
||||||
|
{
|
||||||
|
// We only understand TagHelperDescriptors generated from ITagHelpers.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var descriptorTypeName = descriptor.Metadata[ITagHelperDescriptorBuilder.TypeNameKey];
|
||||||
|
|
||||||
if (lookupInfo.TypePattern.EndsWith("*", StringComparison.Ordinal))
|
if (lookupInfo.TypePattern.EndsWith("*", StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
if (lookupInfo.TypePattern.Length == 1)
|
if (lookupInfo.TypePattern.Length == 1)
|
||||||
|
|
@ -286,10 +295,10 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
|
|
||||||
var lookupTypeName = lookupInfo.TypePattern.Substring(0, lookupInfo.TypePattern.Length - 1);
|
var lookupTypeName = lookupInfo.TypePattern.Substring(0, lookupInfo.TypePattern.Length - 1);
|
||||||
|
|
||||||
return descriptor.TypeName.StartsWith(lookupTypeName, StringComparison.Ordinal);
|
return descriptorTypeName.StartsWith(lookupTypeName, StringComparison.Ordinal);
|
||||||
}
|
}
|
||||||
|
|
||||||
return string.Equals(descriptor.TypeName, lookupInfo.TypePattern, StringComparison.Ordinal);
|
return string.Equals(descriptorTypeName, lookupInfo.TypePattern, StringComparison.Ordinal);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int GetErrorLength(string directiveText)
|
private static int GetErrorLength(string directiveText)
|
||||||
|
|
|
||||||
|
|
@ -1,251 +1,64 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// 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.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Razor.Evolution
|
namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
{
|
{
|
||||||
/// <summary>
|
public abstract class TagHelperDescriptor
|
||||||
/// A metadata class describing a tag helper.
|
|
||||||
/// </summary>
|
|
||||||
public class TagHelperDescriptor
|
|
||||||
{
|
{
|
||||||
private string _prefix = string.Empty;
|
private IEnumerable<RazorDiagnostic> _allDiagnostics;
|
||||||
private string _tagName;
|
|
||||||
private string _typeName;
|
|
||||||
private string _assemblyName;
|
|
||||||
private IDictionary<string, string> _propertyBag;
|
|
||||||
private IEnumerable<TagHelperAttributeDescriptor> _attributes =
|
|
||||||
Enumerable.Empty<TagHelperAttributeDescriptor>();
|
|
||||||
private IEnumerable<TagHelperRequiredAttributeDescriptor> _requiredAttributes =
|
|
||||||
Enumerable.Empty<TagHelperRequiredAttributeDescriptor>();
|
|
||||||
|
|
||||||
/// <summary>
|
protected TagHelperDescriptor(string kind)
|
||||||
/// Creates a new <see cref="TagHelperDescriptor"/>.
|
|
||||||
/// </summary>
|
|
||||||
public TagHelperDescriptor()
|
|
||||||
{
|
{
|
||||||
|
Kind = kind;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
public string Kind { get; }
|
||||||
/// Creates a shallow copy of the given <see cref="TagHelperDescriptor"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="descriptor">The <see cref="TagHelperDescriptor"/> to copy.</param>
|
|
||||||
public TagHelperDescriptor(TagHelperDescriptor descriptor)
|
|
||||||
{
|
|
||||||
Prefix = descriptor.Prefix;
|
|
||||||
TagName = descriptor.TagName;
|
|
||||||
TypeName = descriptor.TypeName;
|
|
||||||
AssemblyName = descriptor.AssemblyName;
|
|
||||||
Attributes = descriptor.Attributes;
|
|
||||||
RequiredAttributes = descriptor.RequiredAttributes;
|
|
||||||
AllowedChildren = descriptor.AllowedChildren;
|
|
||||||
RequiredParent = descriptor.RequiredParent;
|
|
||||||
TagStructure = descriptor.TagStructure;
|
|
||||||
DesignTimeDescriptor = descriptor.DesignTimeDescriptor;
|
|
||||||
|
|
||||||
foreach (var property in descriptor.PropertyBag)
|
public string Name { get; protected set; }
|
||||||
{
|
|
||||||
PropertyBag.Add(property.Key, property.Value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
public IEnumerable<TagMatchingRule> TagMatchingRules { get; protected set; }
|
||||||
/// Text used as a required prefix when matching HTML start and end tags in the Razor source to available
|
|
||||||
/// tag helpers.
|
public string AssemblyName { get; protected set; }
|
||||||
/// </summary>
|
|
||||||
public string Prefix
|
public IEnumerable<BoundAttributeDescriptor> BoundAttributes { get; protected set; }
|
||||||
|
|
||||||
|
public IEnumerable<string> AllowedChildTags { get; protected set; }
|
||||||
|
|
||||||
|
public string Documentation { get; protected set; }
|
||||||
|
|
||||||
|
public string DisplayName { get; protected set; }
|
||||||
|
|
||||||
|
public string TagOutputHint { get; protected set; }
|
||||||
|
|
||||||
|
public IReadOnlyList<RazorDiagnostic> Diagnostics { get; protected set; }
|
||||||
|
|
||||||
|
public IReadOnlyDictionary<string, string> Metadata { get; protected set; }
|
||||||
|
|
||||||
|
public bool HasAnyErrors
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return _prefix;
|
var allDiagnostics = GetAllDiagnostics();
|
||||||
}
|
var anyErrors = allDiagnostics.Any(diagnostic => diagnostic.Severity == RazorDiagnosticSeverity.Error);
|
||||||
set
|
|
||||||
{
|
|
||||||
if (value == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
_prefix = value;
|
return anyErrors;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
public virtual IEnumerable<RazorDiagnostic> GetAllDiagnostics()
|
||||||
/// The tag name that the tag helper should target.
|
|
||||||
/// </summary>
|
|
||||||
public string TagName
|
|
||||||
{
|
{
|
||||||
get
|
if (_allDiagnostics == null)
|
||||||
{
|
{
|
||||||
return _tagName;
|
var attributeDiagnostics = BoundAttributes.SelectMany(attribute => attribute.Diagnostics);
|
||||||
|
var ruleDiagnostics = TagMatchingRules.SelectMany(rule => rule.GetAllDiagnostics());
|
||||||
|
var combinedDiagnostics = attributeDiagnostics.Concat(ruleDiagnostics).Concat(Diagnostics);
|
||||||
|
_allDiagnostics = combinedDiagnostics.ToArray();
|
||||||
}
|
}
|
||||||
set
|
|
||||||
{
|
|
||||||
if (value == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
_tagName = value;
|
return _allDiagnostics;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The full tag name that is required for the tag helper to target an HTML element.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>This is equivalent to <see cref="Prefix"/> and <see cref="TagName"/> concatenated.</remarks>
|
|
||||||
public string FullTagName
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return Prefix + TagName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The full name of the tag helper class.
|
|
||||||
/// </summary>
|
|
||||||
public string TypeName
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return _typeName;
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (value == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
_typeName = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The name of the assembly containing the tag helper class.
|
|
||||||
/// </summary>
|
|
||||||
public string AssemblyName
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return _assemblyName;
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (value == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
_assemblyName = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The list of attributes the tag helper expects.
|
|
||||||
/// </summary>
|
|
||||||
public IEnumerable<TagHelperAttributeDescriptor> Attributes
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return _attributes;
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (value == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
_attributes = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The list of required attribute names the tag helper expects to target an element.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// <c>*</c> at the end of an attribute name acts as a prefix match.
|
|
||||||
/// </remarks>
|
|
||||||
public IEnumerable<TagHelperRequiredAttributeDescriptor> RequiredAttributes
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return _requiredAttributes;
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (value == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
_requiredAttributes = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Get the names of elements allowed as children.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks><c>null</c> indicates all children are allowed.</remarks>
|
|
||||||
public IEnumerable<string> AllowedChildren { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Get the name of the HTML element required as the immediate parent.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks><c>null</c> indicates no restriction on parent tag.</remarks>
|
|
||||||
public string RequiredParent { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The expected tag structure.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// If <see cref="TagStructure.Unspecified"/> and no other tag helpers applying to the same element specify
|
|
||||||
/// their <see cref="TagStructure"/> the <see cref="TagStructure.NormalOrSelfClosing"/> behavior is used:
|
|
||||||
/// <para>
|
|
||||||
/// <code>
|
|
||||||
/// <my-tag-helper></my-tag-helper>
|
|
||||||
/// <!-- OR -->
|
|
||||||
/// <my-tag-helper />
|
|
||||||
/// </code>
|
|
||||||
/// Otherwise, if another tag helper applying to the same element does specify their behavior, that behavior
|
|
||||||
/// is used.
|
|
||||||
/// </para>
|
|
||||||
/// <para>
|
|
||||||
/// If <see cref="TagStructure.WithoutEndTag"/> HTML elements can be written in the following formats:
|
|
||||||
/// <code>
|
|
||||||
/// <my-tag-helper>
|
|
||||||
/// <!-- OR -->
|
|
||||||
/// <my-tag-helper />
|
|
||||||
/// </code>
|
|
||||||
/// </para>
|
|
||||||
/// </remarks>
|
|
||||||
public TagStructure TagStructure { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The <see cref="TagHelperDesignTimeDescriptor"/> that contains design time information about this
|
|
||||||
/// tag helper.
|
|
||||||
/// </summary>
|
|
||||||
public TagHelperDesignTimeDescriptor DesignTimeDescriptor { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A dictionary containing additional information about the <see cref="TagHelperDescriptor"/>.
|
|
||||||
/// </summary>
|
|
||||||
public IDictionary<string, string> PropertyBag
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (_propertyBag == null)
|
|
||||||
{
|
|
||||||
_propertyBag = new Dictionary<string, string>();
|
|
||||||
}
|
|
||||||
|
|
||||||
return _propertyBag;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,67 @@
|
||||||
|
// 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.Diagnostics;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
|
{
|
||||||
|
internal static class TagHelperDescriptorMatchingConventions
|
||||||
|
{
|
||||||
|
public static bool CanMatchName(this BoundAttributeDescriptor descriptor, string name)
|
||||||
|
{
|
||||||
|
return IsFullNameMatch(descriptor, name) || IsIndexerNameMatch(descriptor, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsFullNameMatch(this BoundAttributeDescriptor descriptor, string name)
|
||||||
|
{
|
||||||
|
return string.Equals(descriptor.Name, name, StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsIndexerNameMatch(this BoundAttributeDescriptor descriptor, string name)
|
||||||
|
{
|
||||||
|
return descriptor.IndexerNamePrefix != null &&
|
||||||
|
!IsFullNameMatch(descriptor, name) &&
|
||||||
|
name.StartsWith(descriptor.IndexerNamePrefix, StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsMatch(this RequiredAttributeDescriptor descriptor, string attributeName, string attributeValue)
|
||||||
|
{
|
||||||
|
var nameMatches = false;
|
||||||
|
if (descriptor.NameComparison == RequiredAttributeDescriptor.NameComparisonMode.FullMatch)
|
||||||
|
{
|
||||||
|
nameMatches = string.Equals(descriptor.Name, attributeName, StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
else if (descriptor.NameComparison == RequiredAttributeDescriptor.NameComparisonMode.PrefixMatch)
|
||||||
|
{
|
||||||
|
// attributeName cannot equal the Name if comparing as a PrefixMatch.
|
||||||
|
nameMatches = attributeName.Length != descriptor.Name.Length &&
|
||||||
|
attributeName.StartsWith(descriptor.Name, StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.Assert(false, "Unknown name comparison.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!nameMatches)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (descriptor.ValueComparison)
|
||||||
|
{
|
||||||
|
case RequiredAttributeDescriptor.ValueComparisonMode.None:
|
||||||
|
return true;
|
||||||
|
case RequiredAttributeDescriptor.ValueComparisonMode.PrefixMatch: // Value starts with
|
||||||
|
return attributeValue.StartsWith(descriptor.Value, StringComparison.Ordinal);
|
||||||
|
case RequiredAttributeDescriptor.ValueComparisonMode.SuffixMatch: // Value ends with
|
||||||
|
return attributeValue.EndsWith(descriptor.Value, StringComparison.Ordinal);
|
||||||
|
case RequiredAttributeDescriptor.ValueComparisonMode.FullMatch: // Value equals
|
||||||
|
return string.Equals(attributeValue, descriptor.Value, StringComparison.Ordinal);
|
||||||
|
default:
|
||||||
|
Debug.Assert(false, "Unknown value comparison.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
// 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
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// A metadata class containing design time information about a tag helper.
|
|
||||||
/// </summary>
|
|
||||||
public class TagHelperDesignTimeDescriptor
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// A summary of how to use a tag helper.
|
|
||||||
/// </summary>
|
|
||||||
public string Summary { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Remarks about how to use a tag helper.
|
|
||||||
/// </summary>
|
|
||||||
public string Remarks { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The HTML element a tag helper may output.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// In IDEs supporting IntelliSense, may override the HTML information provided at design time.
|
|
||||||
/// </remarks>
|
|
||||||
public string OutputElementHint { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,82 +0,0 @@
|
||||||
// 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.Diagnostics;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Razor.Evolution
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// A metadata class describing a required tag helper attribute.
|
|
||||||
/// </summary>
|
|
||||||
public class TagHelperRequiredAttributeDescriptor
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The HTML attribute name.
|
|
||||||
/// </summary>
|
|
||||||
public string Name { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The comparison method to use for <see cref="Name"/> when determining if an HTML attribute name matches.
|
|
||||||
/// </summary>
|
|
||||||
public TagHelperRequiredAttributeNameComparison NameComparison { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The HTML attribute value.
|
|
||||||
/// </summary>
|
|
||||||
public string Value { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The comparison method to use for <see cref="Value"/> when determining if an HTML attribute value matches.
|
|
||||||
/// </summary>
|
|
||||||
public TagHelperRequiredAttributeValueComparison ValueComparison { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Determines if the current <see cref="TagHelperRequiredAttributeDescriptor"/> matches the given
|
|
||||||
/// <paramref name="attributeName"/> and <paramref name="attributeValue"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="attributeName">An HTML attribute name.</param>
|
|
||||||
/// <param name="attributeValue">An HTML attribute value.</param>
|
|
||||||
/// <returns><c>true</c> if the current <see cref="TagHelperRequiredAttributeDescriptor"/> matches
|
|
||||||
/// <paramref name="attributeName"/> and <paramref name="attributeValue"/>; <c>false</c> otherwise.</returns>
|
|
||||||
public bool IsMatch(string attributeName, string attributeValue)
|
|
||||||
{
|
|
||||||
var nameMatches = false;
|
|
||||||
if (NameComparison == TagHelperRequiredAttributeNameComparison.FullMatch)
|
|
||||||
{
|
|
||||||
nameMatches = string.Equals(Name, attributeName, StringComparison.OrdinalIgnoreCase);
|
|
||||||
}
|
|
||||||
else if (NameComparison == TagHelperRequiredAttributeNameComparison.PrefixMatch)
|
|
||||||
{
|
|
||||||
// attributeName cannot equal the Name if comparing as a PrefixMatch.
|
|
||||||
nameMatches = attributeName.Length != Name.Length &&
|
|
||||||
attributeName.StartsWith(Name, StringComparison.OrdinalIgnoreCase);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Debug.Assert(false, "Unknown name comparison.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!nameMatches)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (ValueComparison)
|
|
||||||
{
|
|
||||||
case TagHelperRequiredAttributeValueComparison.None:
|
|
||||||
return true;
|
|
||||||
case TagHelperRequiredAttributeValueComparison.PrefixMatch: // Value starts with
|
|
||||||
return attributeValue.StartsWith(Value, StringComparison.Ordinal);
|
|
||||||
case TagHelperRequiredAttributeValueComparison.SuffixMatch: // Value ends with
|
|
||||||
return attributeValue.EndsWith(Value, StringComparison.Ordinal);
|
|
||||||
case TagHelperRequiredAttributeValueComparison.FullMatch: // Value equals
|
|
||||||
return string.Equals(attributeValue, Value, StringComparison.Ordinal);
|
|
||||||
default:
|
|
||||||
Debug.Assert(false, "Unknown value comparison.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
// 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
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Acceptable <see cref="TagHelperRequiredAttributeDescriptor.Name"/> comparison modes.
|
|
||||||
/// </summary>
|
|
||||||
public enum TagHelperRequiredAttributeNameComparison
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// HTML attribute name case insensitively matches <see cref="TagHelperRequiredAttributeDescriptor.Name"/>.
|
|
||||||
/// </summary>
|
|
||||||
FullMatch,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// HTML attribute name case insensitively starts with <see cref="TagHelperRequiredAttributeDescriptor.Name"/>.
|
|
||||||
/// </summary>
|
|
||||||
PrefixMatch,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
// 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
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Acceptable <see cref="TagHelperRequiredAttributeDescriptor.Value"/> comparison modes.
|
|
||||||
/// </summary>
|
|
||||||
public enum TagHelperRequiredAttributeValueComparison
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// HTML attribute value always matches <see cref="TagHelperRequiredAttributeDescriptor.Value"/>.
|
|
||||||
/// </summary>
|
|
||||||
None,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// HTML attribute value case sensitively matches <see cref="TagHelperRequiredAttributeDescriptor.Value"/>.
|
|
||||||
/// </summary>
|
|
||||||
FullMatch,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// HTML attribute value case sensitively starts with <see cref="TagHelperRequiredAttributeDescriptor.Value"/>.
|
|
||||||
/// </summary>
|
|
||||||
PrefixMatch,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// HTML attribute value case sensitively ends with <see cref="TagHelperRequiredAttributeDescriptor.Value"/>.
|
|
||||||
/// </summary>
|
|
||||||
SuffixMatch,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
// 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 System.Linq;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
|
{
|
||||||
|
public abstract class TagMatchingRule
|
||||||
|
{
|
||||||
|
private IEnumerable<RazorDiagnostic> _allDiagnostics;
|
||||||
|
|
||||||
|
public string TagName { get; protected set; }
|
||||||
|
|
||||||
|
public IEnumerable<RequiredAttributeDescriptor> Attributes { get; protected set; }
|
||||||
|
|
||||||
|
public string ParentTag { get; protected set; }
|
||||||
|
|
||||||
|
public TagStructure TagStructure { get; protected set; }
|
||||||
|
|
||||||
|
public IReadOnlyList<RazorDiagnostic> Diagnostics { get; protected set; }
|
||||||
|
|
||||||
|
public bool HasAnyErrors
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var allDiagnostics = GetAllDiagnostics();
|
||||||
|
var anyErrors = allDiagnostics.Any(diagnostic => diagnostic.Severity == RazorDiagnosticSeverity.Error);
|
||||||
|
|
||||||
|
return anyErrors;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual IEnumerable<RazorDiagnostic> GetAllDiagnostics()
|
||||||
|
{
|
||||||
|
if (_allDiagnostics == null)
|
||||||
|
{
|
||||||
|
var attributeDiagnostics = Attributes.SelectMany(attribute => attribute.Diagnostics);
|
||||||
|
var combinedDiagnostics = Diagnostics.Concat(attributeDiagnostics);
|
||||||
|
_allDiagnostics = combinedDiagnostics.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
return _allDiagnostics;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,194 @@
|
||||||
|
// 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;
|
||||||
|
using Microsoft.AspNetCore.Razor.Evolution.Legacy;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
|
{
|
||||||
|
public sealed class TagMatchingRuleBuilder
|
||||||
|
{
|
||||||
|
private static ICollection<char> InvalidNonWhitespaceTagNameCharacters { get; } = new HashSet<char>(
|
||||||
|
new[] { '@', '!', '<', '/', '?', '[', '>', ']', '=', '"', '\'', '*' });
|
||||||
|
|
||||||
|
private string _tagName;
|
||||||
|
private string _parentTag;
|
||||||
|
private TagStructure _tagStructure;
|
||||||
|
private HashSet<RequiredAttributeDescriptor> _requiredAttributeDescriptors;
|
||||||
|
private HashSet<RazorDiagnostic> _diagnostics;
|
||||||
|
|
||||||
|
private TagMatchingRuleBuilder()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TagMatchingRuleBuilder Create()
|
||||||
|
{
|
||||||
|
return new TagMatchingRuleBuilder();
|
||||||
|
}
|
||||||
|
|
||||||
|
public TagMatchingRuleBuilder RequireTagName(string tagName)
|
||||||
|
{
|
||||||
|
_tagName = tagName;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TagMatchingRuleBuilder RequireParentTag(string parentTag)
|
||||||
|
{
|
||||||
|
_parentTag = parentTag;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TagMatchingRuleBuilder RequireTagStructure(TagStructure tagStructure)
|
||||||
|
{
|
||||||
|
_tagStructure = tagStructure;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TagMatchingRuleBuilder RequireAttribute(RequiredAttributeDescriptor requiredAttributeDescriptor)
|
||||||
|
{
|
||||||
|
if (requiredAttributeDescriptor == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(requiredAttributeDescriptor));
|
||||||
|
}
|
||||||
|
|
||||||
|
EnsureRequiredAttributeDescriptors();
|
||||||
|
_requiredAttributeDescriptors.Add(requiredAttributeDescriptor);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TagMatchingRuleBuilder RequireAttribute(Action<RequiredAttributeDescriptorBuilder> configure)
|
||||||
|
{
|
||||||
|
if (configure == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(configure));
|
||||||
|
}
|
||||||
|
|
||||||
|
var builder = RequiredAttributeDescriptorBuilder.Create();
|
||||||
|
|
||||||
|
configure(builder);
|
||||||
|
|
||||||
|
var requiredAttributeDescriptor = builder.Build();
|
||||||
|
|
||||||
|
return RequireAttribute(requiredAttributeDescriptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TagMatchingRuleBuilder AddDiagnostic(RazorDiagnostic diagnostic)
|
||||||
|
{
|
||||||
|
EnsureDiagnostics();
|
||||||
|
_diagnostics.Add(diagnostic);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TagMatchingRule Build()
|
||||||
|
{
|
||||||
|
var validationDiagnostics = Validate();
|
||||||
|
var diagnostics = new HashSet<RazorDiagnostic>(validationDiagnostics);
|
||||||
|
if (_diagnostics != null)
|
||||||
|
{
|
||||||
|
diagnostics.UnionWith(_diagnostics);
|
||||||
|
}
|
||||||
|
|
||||||
|
var rule = new DefaultTagMatchingRule(
|
||||||
|
_tagName,
|
||||||
|
_parentTag,
|
||||||
|
_tagStructure,
|
||||||
|
_requiredAttributeDescriptors ?? Enumerable.Empty<RequiredAttributeDescriptor>(),
|
||||||
|
diagnostics);
|
||||||
|
|
||||||
|
return rule;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Reset()
|
||||||
|
{
|
||||||
|
_tagName = null;
|
||||||
|
_parentTag = null;
|
||||||
|
_tagStructure = default(TagStructure);
|
||||||
|
_requiredAttributeDescriptors?.Clear();
|
||||||
|
_diagnostics?.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerable<RazorDiagnostic> Validate()
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(_tagName))
|
||||||
|
{
|
||||||
|
var diagnostic = RazorDiagnosticFactory.CreateTagHelper_InvalidTargetedTagNameNullOrWhitespace();
|
||||||
|
|
||||||
|
yield return diagnostic;
|
||||||
|
}
|
||||||
|
else if (_tagName != TagHelperDescriptorProvider.ElementCatchAllTarget)
|
||||||
|
{
|
||||||
|
foreach (var character in _tagName)
|
||||||
|
{
|
||||||
|
if (char.IsWhiteSpace(character) || InvalidNonWhitespaceTagNameCharacters.Contains(character))
|
||||||
|
{
|
||||||
|
var diagnostic = RazorDiagnosticFactory.CreateTagHelper_InvalidTargetedTagName(_tagName, character);
|
||||||
|
|
||||||
|
yield return diagnostic;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_parentTag != null)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(_parentTag))
|
||||||
|
{
|
||||||
|
var diagnostic = RazorDiagnosticFactory.CreateTagHelper_InvalidTargetedParentTagNameNullOrWhitespace();
|
||||||
|
|
||||||
|
AddDiagnostic(diagnostic);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach (var character in _parentTag)
|
||||||
|
{
|
||||||
|
if (char.IsWhiteSpace(character) || InvalidNonWhitespaceTagNameCharacters.Contains(character))
|
||||||
|
{
|
||||||
|
var diagnostic = RazorDiagnosticFactory.CreateTagHelper_InvalidTargetedParentTagName(_parentTag, character);
|
||||||
|
|
||||||
|
AddDiagnostic(diagnostic);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EnsureRequiredAttributeDescriptors()
|
||||||
|
{
|
||||||
|
if (_requiredAttributeDescriptors == null)
|
||||||
|
{
|
||||||
|
_requiredAttributeDescriptors = new HashSet<RequiredAttributeDescriptor>(RequiredAttributeDescriptorComparer.Default);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EnsureDiagnostics()
|
||||||
|
{
|
||||||
|
if (_diagnostics == null)
|
||||||
|
{
|
||||||
|
_diagnostics = new HashSet<RazorDiagnostic>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DefaultTagMatchingRule : TagMatchingRule
|
||||||
|
{
|
||||||
|
public DefaultTagMatchingRule(
|
||||||
|
string tagName,
|
||||||
|
string parentTag,
|
||||||
|
TagStructure tagStructure,
|
||||||
|
IEnumerable<RequiredAttributeDescriptor> requiredAttributeDescriptors,
|
||||||
|
IEnumerable<RazorDiagnostic> diagnostics)
|
||||||
|
{
|
||||||
|
TagName = tagName;
|
||||||
|
ParentTag = parentTag;
|
||||||
|
TagStructure = tagStructure;
|
||||||
|
Attributes = new List<RequiredAttributeDescriptor>(requiredAttributeDescriptors);
|
||||||
|
Diagnostics = new List<RazorDiagnostic>(diagnostics);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,86 @@
|
||||||
|
// 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;
|
||||||
|
using Microsoft.AspNetCore.Razor.Evolution.Legacy;
|
||||||
|
using Microsoft.Extensions.Internal;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
|
{
|
||||||
|
internal class TagMatchingRuleComparer : IEqualityComparer<TagMatchingRule>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A default instance of the <see cref="TagMatchingRuleComparer"/>.
|
||||||
|
/// </summary>
|
||||||
|
public static readonly TagMatchingRuleComparer Default = new TagMatchingRuleComparer();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A default instance of the <see cref="TagMatchingRuleComparer"/> that does case-sensitive comparison.
|
||||||
|
/// </summary>
|
||||||
|
internal static readonly TagMatchingRuleComparer CaseSensitive =
|
||||||
|
new TagMatchingRuleComparer(caseSensitive: true);
|
||||||
|
|
||||||
|
private readonly StringComparer _stringComparer;
|
||||||
|
private readonly StringComparison _stringComparison;
|
||||||
|
private readonly RequiredAttributeDescriptorComparer _requiredAttributeComparer;
|
||||||
|
|
||||||
|
private TagMatchingRuleComparer(bool caseSensitive = false)
|
||||||
|
{
|
||||||
|
if (caseSensitive)
|
||||||
|
{
|
||||||
|
_stringComparer = StringComparer.Ordinal;
|
||||||
|
_stringComparison = StringComparison.Ordinal;
|
||||||
|
_requiredAttributeComparer = RequiredAttributeDescriptorComparer.CaseSensitive;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_stringComparer = StringComparer.OrdinalIgnoreCase;
|
||||||
|
_stringComparison = StringComparison.OrdinalIgnoreCase;
|
||||||
|
_requiredAttributeComparer = RequiredAttributeDescriptorComparer.Default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual bool Equals(TagMatchingRule ruleX, TagMatchingRule ruleY)
|
||||||
|
{
|
||||||
|
if (object.ReferenceEquals(ruleX, ruleY))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ruleX == null ^ ruleY == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ruleX != null &&
|
||||||
|
string.Equals(ruleX.TagName, ruleY.TagName, _stringComparison) &&
|
||||||
|
string.Equals(ruleX.ParentTag, ruleY.ParentTag, _stringComparison) &&
|
||||||
|
ruleX.TagStructure == ruleY.TagStructure &&
|
||||||
|
Enumerable.SequenceEqual(ruleX.Attributes, ruleY.Attributes, _requiredAttributeComparer) &&
|
||||||
|
Enumerable.SequenceEqual(ruleX.Diagnostics, ruleY.Diagnostics);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual int GetHashCode(TagMatchingRule rule)
|
||||||
|
{
|
||||||
|
if (rule == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(rule));
|
||||||
|
}
|
||||||
|
|
||||||
|
var hashCodeCombiner = HashCodeCombiner.Start();
|
||||||
|
hashCodeCombiner.Add(rule.TagName, _stringComparer);
|
||||||
|
hashCodeCombiner.Add(rule.ParentTag, _stringComparer);
|
||||||
|
hashCodeCombiner.Add(rule.TagStructure);
|
||||||
|
|
||||||
|
var attributes = rule.Attributes.OrderBy(attribute => attribute.Name, _stringComparer);
|
||||||
|
foreach (var attribute in attributes)
|
||||||
|
{
|
||||||
|
hashCodeCombiner.Add(_requiredAttributeComparer.GetHashCode(attribute));
|
||||||
|
}
|
||||||
|
|
||||||
|
return hashCodeCombiner.CombinedHash;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -37,11 +37,16 @@ namespace Microsoft.AspNetCore.Razor.Compilation.TagHelpers
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public virtual bool Equals(TagHelperDescriptor descriptorX, TagHelperDescriptor descriptorY)
|
public virtual bool Equals(TagHelperDescriptor descriptorX, TagHelperDescriptor descriptorY)
|
||||||
{
|
{
|
||||||
if (descriptorX == descriptorY)
|
if (object.ReferenceEquals(descriptorX, descriptorY))
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (descriptorX == null ^ descriptorY == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return descriptorX != null &&
|
return descriptorX != null &&
|
||||||
string.Equals(descriptorX.TypeName, descriptorY.TypeName, StringComparison.Ordinal) &&
|
string.Equals(descriptorX.TypeName, descriptorY.TypeName, StringComparison.Ordinal) &&
|
||||||
string.Equals(descriptorX.TagName, descriptorY.TagName, StringComparison.OrdinalIgnoreCase) &&
|
string.Equals(descriptorX.TagName, descriptorY.TagName, StringComparison.OrdinalIgnoreCase) &&
|
||||||
|
|
|
||||||
|
|
@ -91,7 +91,7 @@ namespace Microsoft.AspNetCore.Razor.Parser.TagHelpers
|
||||||
public TagMode TagMode { get; }
|
public TagMode TagMode { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// <see cref="TagHelperDescriptor"/>s for the HTML element.
|
/// <see cref="TagHelperDescriptor"/> bindings for the HTML element.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IEnumerable<TagHelperDescriptor> Descriptors { get; }
|
public IEnumerable<TagHelperDescriptor> Descriptors { get; }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,9 +21,10 @@ namespace Microsoft.CodeAnalysis.Razor
|
||||||
public override TagHelperResolutionResult GetTagHelpers(Compilation compilation, IEnumerable<string> assemblyNameFilters)
|
public override TagHelperResolutionResult GetTagHelpers(Compilation compilation, IEnumerable<string> assemblyNameFilters)
|
||||||
{
|
{
|
||||||
var descriptors = new List<TagHelperDescriptor>();
|
var descriptors = new List<TagHelperDescriptor>();
|
||||||
var errors = new ErrorSink();
|
|
||||||
|
|
||||||
VisitTagHelpers(compilation, assemblyNameFilters, descriptors, errors);
|
VisitTagHelpers(compilation, assemblyNameFilters, descriptors);
|
||||||
|
|
||||||
|
var errors = new ErrorSink();
|
||||||
VisitViewComponents(compilation, assemblyNameFilters, descriptors, errors);
|
VisitViewComponents(compilation, assemblyNameFilters, descriptors, errors);
|
||||||
|
|
||||||
var diagnostics = new List<RazorDiagnostic>();
|
var diagnostics = new List<RazorDiagnostic>();
|
||||||
|
|
@ -37,7 +38,7 @@ namespace Microsoft.CodeAnalysis.Razor
|
||||||
return resolutionResult;
|
return resolutionResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void VisitTagHelpers(Compilation compilation, IEnumerable<string> assemblyNameFilters, List<TagHelperDescriptor> results, ErrorSink errors)
|
private void VisitTagHelpers(Compilation compilation, IEnumerable<string> assemblyNameFilters, List<TagHelperDescriptor> results)
|
||||||
{
|
{
|
||||||
var types = new List<INamedTypeSymbol>();
|
var types = new List<INamedTypeSymbol>();
|
||||||
var visitor = TagHelperTypeVisitor.Create(compilation, types);
|
var visitor = TagHelperTypeVisitor.Create(compilation, types);
|
||||||
|
|
@ -50,8 +51,12 @@ namespace Microsoft.CodeAnalysis.Razor
|
||||||
{
|
{
|
||||||
if (assemblyNameFilters.Contains(type.ContainingAssembly.Identity.Name))
|
if (assemblyNameFilters.Contains(type.ContainingAssembly.Identity.Name))
|
||||||
{
|
{
|
||||||
var descriptors = factory.CreateDescriptors(type, errors);
|
var descriptor = factory.CreateDescriptor(type);
|
||||||
results.AddRange(descriptors);
|
|
||||||
|
if (descriptor != null)
|
||||||
|
{
|
||||||
|
results.Add(descriptor);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -10,373 +10,117 @@ namespace Microsoft.CodeAnalysis.Razor
|
||||||
private static readonly ResourceManager _resourceManager
|
private static readonly ResourceManager _resourceManager
|
||||||
= new ResourceManager("Microsoft.CodeAnalysis.Razor.Resources", typeof(Resources).GetTypeInfo().Assembly);
|
= new ResourceManager("Microsoft.CodeAnalysis.Razor.Resources", typeof(Resources).GetTypeInfo().Assembly);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// {0} cannot be null or an empty string.
|
|
||||||
/// </summary>
|
|
||||||
internal static string Argument_Cannot_Be_Null_Or_Empty
|
|
||||||
{
|
|
||||||
get { return GetString("Argument_Cannot_Be_Null_Or_Empty"); }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// {0} cannot be null or an empty string.
|
|
||||||
/// </summary>
|
|
||||||
internal static string FormatArgument_Cannot_Be_Null_Or_Empty(object p0)
|
|
||||||
{
|
|
||||||
return string.Format(CultureInfo.CurrentCulture, GetString("Argument_Cannot_Be_Null_Or_Empty"), p0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Tag helpers cannot target {0} name '{1}' because it contains a '{2}' character.
|
|
||||||
/// </summary>
|
|
||||||
internal static string HtmlTargetElementAttribute_InvalidName
|
|
||||||
{
|
|
||||||
get { return GetString("HtmlTargetElementAttribute_InvalidName"); }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Tag helpers cannot target {0} name '{1}' because it contains a '{2}' character.
|
|
||||||
/// </summary>
|
|
||||||
internal static string FormatHtmlTargetElementAttribute_InvalidName(object p0, object p1, object p2)
|
|
||||||
{
|
|
||||||
return string.Format(CultureInfo.CurrentCulture, GetString("HtmlTargetElementAttribute_InvalidName"), p0, p1, p2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// {0} name cannot be null or whitespace.
|
|
||||||
/// </summary>
|
|
||||||
internal static string HtmlTargetElementAttribute_NameCannotBeNullOrWhitespace
|
|
||||||
{
|
|
||||||
get { return GetString("HtmlTargetElementAttribute_NameCannotBeNullOrWhitespace"); }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// {0} name cannot be null or whitespace.
|
|
||||||
/// </summary>
|
|
||||||
internal static string FormatHtmlTargetElementAttribute_NameCannotBeNullOrWhitespace(object p0)
|
|
||||||
{
|
|
||||||
return string.Format(CultureInfo.CurrentCulture, GetString("HtmlTargetElementAttribute_NameCannotBeNullOrWhitespace"), p0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Attribute
|
|
||||||
/// </summary>
|
|
||||||
internal static string TagHelperDescriptorFactory_Attribute
|
|
||||||
{
|
|
||||||
get { return GetString("TagHelperDescriptorFactory_Attribute"); }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Attribute
|
|
||||||
/// </summary>
|
|
||||||
internal static string FormatTagHelperDescriptorFactory_Attribute()
|
|
||||||
{
|
|
||||||
return GetString("TagHelperDescriptorFactory_Attribute");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Could not find matching ']' for required attribute '{0}'.
|
/// Could not find matching ']' for required attribute '{0}'.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string TagHelperDescriptorFactory_CouldNotFindMatchingEndBrace
|
internal static string TagHelperDescriptorFactory_CouldNotFindMatchingEndBrace
|
||||||
{
|
{
|
||||||
get { return GetString("TagHelperDescriptorFactory_CouldNotFindMatchingEndBrace"); }
|
get => GetString("TagHelperDescriptorFactory_CouldNotFindMatchingEndBrace");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Could not find matching ']' for required attribute '{0}'.
|
/// Could not find matching ']' for required attribute '{0}'.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string FormatTagHelperDescriptorFactory_CouldNotFindMatchingEndBrace(object p0)
|
internal static string FormatTagHelperDescriptorFactory_CouldNotFindMatchingEndBrace(object p0)
|
||||||
{
|
=> string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_CouldNotFindMatchingEndBrace"), p0);
|
||||||
return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_CouldNotFindMatchingEndBrace"), p0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Invalid tag helper bound property '{0}.{1}'. An '{2}' must not be associated with a property with no public setter unless its type implements '{3}'.
|
|
||||||
/// </summary>
|
|
||||||
internal static string TagHelperDescriptorFactory_InvalidAttributeNameAttribute
|
|
||||||
{
|
|
||||||
get { return GetString("TagHelperDescriptorFactory_InvalidAttributeNameAttribute"); }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Invalid tag helper bound property '{0}.{1}'. An '{2}' must not be associated with a property with no public setter unless its type implements '{3}'.
|
|
||||||
/// </summary>
|
|
||||||
internal static string FormatTagHelperDescriptorFactory_InvalidAttributeNameAttribute(object p0, object p1, object p2, object p3)
|
|
||||||
{
|
|
||||||
return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidAttributeNameAttribute"), p0, p1, p2, p3);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Invalid tag helper bound property '{0}.{1}'. '{2}.{3}' must be null or empty if property has no public setter.
|
|
||||||
/// </summary>
|
|
||||||
internal static string TagHelperDescriptorFactory_InvalidAttributeNameNotNullOrEmpty
|
|
||||||
{
|
|
||||||
get { return GetString("TagHelperDescriptorFactory_InvalidAttributeNameNotNullOrEmpty"); }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Invalid tag helper bound property '{0}.{1}'. '{2}.{3}' must be null or empty if property has no public setter.
|
|
||||||
/// </summary>
|
|
||||||
internal static string FormatTagHelperDescriptorFactory_InvalidAttributeNameNotNullOrEmpty(object p0, object p1, object p2, object p3)
|
|
||||||
{
|
|
||||||
return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidAttributeNameNotNullOrEmpty"), p0, p1, p2, p3);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with a null or empty name.
|
|
||||||
/// </summary>
|
|
||||||
internal static string TagHelperDescriptorFactory_InvalidAttributeNameNullOrEmpty
|
|
||||||
{
|
|
||||||
get { return GetString("TagHelperDescriptorFactory_InvalidAttributeNameNullOrEmpty"); }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with a null or empty name.
|
|
||||||
/// </summary>
|
|
||||||
internal static string FormatTagHelperDescriptorFactory_InvalidAttributeNameNullOrEmpty(object p0, object p1)
|
|
||||||
{
|
|
||||||
return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidAttributeNameNullOrEmpty"), p0, p1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with {2} '{3}' because {2} contains a '{4}' character.
|
|
||||||
/// </summary>
|
|
||||||
internal static string TagHelperDescriptorFactory_InvalidAttributeNameOrPrefixCharacter
|
|
||||||
{
|
|
||||||
get { return GetString("TagHelperDescriptorFactory_InvalidAttributeNameOrPrefixCharacter"); }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with {2} '{3}' because {2} contains a '{4}' character.
|
|
||||||
/// </summary>
|
|
||||||
internal static string FormatTagHelperDescriptorFactory_InvalidAttributeNameOrPrefixCharacter(object p0, object p1, object p2, object p3, object p4)
|
|
||||||
{
|
|
||||||
return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidAttributeNameOrPrefixCharacter"), p0, p1, p2, p3, p4);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with {2} '{3}' because {2} starts with '{4}'.
|
|
||||||
/// </summary>
|
|
||||||
internal static string TagHelperDescriptorFactory_InvalidAttributeNameOrPrefixStart
|
|
||||||
{
|
|
||||||
get { return GetString("TagHelperDescriptorFactory_InvalidAttributeNameOrPrefixStart"); }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with {2} '{3}' because {2} starts with '{4}'.
|
|
||||||
/// </summary>
|
|
||||||
internal static string FormatTagHelperDescriptorFactory_InvalidAttributeNameOrPrefixStart(object p0, object p1, object p2, object p3, object p4)
|
|
||||||
{
|
|
||||||
return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidAttributeNameOrPrefixStart"), p0, p1, p2, p3, p4);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with a whitespace {2}.
|
|
||||||
/// </summary>
|
|
||||||
internal static string TagHelperDescriptorFactory_InvalidAttributeNameOrPrefixWhitespace
|
|
||||||
{
|
|
||||||
get { return GetString("TagHelperDescriptorFactory_InvalidAttributeNameOrPrefixWhitespace"); }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with a whitespace {2}.
|
|
||||||
/// </summary>
|
|
||||||
internal static string FormatTagHelperDescriptorFactory_InvalidAttributeNameOrPrefixWhitespace(object p0, object p1, object p2)
|
|
||||||
{
|
|
||||||
return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidAttributeNameOrPrefixWhitespace"), p0, p1, p2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Invalid tag helper bound property '{0}.{1}'. '{2}.{3}' must be null unless property type implements '{4}'.
|
/// Invalid tag helper bound property '{0}.{1}'. '{2}.{3}' must be null unless property type implements '{4}'.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string TagHelperDescriptorFactory_InvalidAttributePrefixNotNull
|
internal static string TagHelperDescriptorFactory_InvalidAttributePrefixNotNull
|
||||||
{
|
{
|
||||||
get { return GetString("TagHelperDescriptorFactory_InvalidAttributePrefixNotNull"); }
|
get => GetString("TagHelperDescriptorFactory_InvalidAttributePrefixNotNull");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Invalid tag helper bound property '{0}.{1}'. '{2}.{3}' must be null unless property type implements '{4}'.
|
/// Invalid tag helper bound property '{0}.{1}'. '{2}.{3}' must be null unless property type implements '{4}'.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string FormatTagHelperDescriptorFactory_InvalidAttributePrefixNotNull(object p0, object p1, object p2, object p3, object p4)
|
internal static string FormatTagHelperDescriptorFactory_InvalidAttributePrefixNotNull(object p0, object p1, object p2, object p3, object p4)
|
||||||
|
=> string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidAttributePrefixNotNull"), p0, p1, p2, p3, p4);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invalid tag helper bound property '{0}.{1}'. '{2}.{3}' must be null or empty if property has no public setter.
|
||||||
|
/// </summary>
|
||||||
|
internal static string TagHelperDescriptorFactory_InvalidAttributeNameNotNullOrEmpty
|
||||||
{
|
{
|
||||||
return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidAttributePrefixNotNull"), p0, p1, p2, p3, p4);
|
get => GetString("TagHelperDescriptorFactory_InvalidAttributeNameNotNullOrEmpty");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invalid tag helper bound property '{0}.{1}'. '{2}.{3}' must be null or empty if property has no public setter.
|
||||||
|
/// </summary>
|
||||||
|
internal static string FormatTagHelperDescriptorFactory_InvalidAttributeNameNotNullOrEmpty(object p0, object p1, object p2, object p3)
|
||||||
|
=> string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidAttributeNameNotNullOrEmpty"), p0, p1, p2, p3);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Invalid tag helper bound property '{0}.{1}'. '{2}.{3}' must not be null if property has no public setter and its type implements '{4}'.
|
/// Invalid tag helper bound property '{0}.{1}'. '{2}.{3}' must not be null if property has no public setter and its type implements '{4}'.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string TagHelperDescriptorFactory_InvalidAttributePrefixNull
|
internal static string TagHelperDescriptorFactory_InvalidAttributePrefixNull
|
||||||
{
|
{
|
||||||
get { return GetString("TagHelperDescriptorFactory_InvalidAttributePrefixNull"); }
|
get => GetString("TagHelperDescriptorFactory_InvalidAttributePrefixNull");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Invalid tag helper bound property '{0}.{1}'. '{2}.{3}' must not be null if property has no public setter and its type implements '{4}'.
|
/// Invalid tag helper bound property '{0}.{1}'. '{2}.{3}' must not be null if property has no public setter and its type implements '{4}'.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string FormatTagHelperDescriptorFactory_InvalidAttributePrefixNull(object p0, object p1, object p2, object p3, object p4)
|
internal static string FormatTagHelperDescriptorFactory_InvalidAttributePrefixNull(object p0, object p1, object p2, object p3, object p4)
|
||||||
{
|
=> string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidAttributePrefixNull"), p0, p1, p2, p3, p4);
|
||||||
return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidAttributePrefixNull"), p0, p1, p2, p3, p4);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Invalid required attribute character '{0}' in required attribute '{1}'. Separate required attributes with commas.
|
/// Invalid required attribute character '{0}' in required attribute '{1}'. Separate required attributes with commas.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string TagHelperDescriptorFactory_InvalidRequiredAttributeCharacter
|
internal static string TagHelperDescriptorFactory_InvalidRequiredAttributeCharacter
|
||||||
{
|
{
|
||||||
get { return GetString("TagHelperDescriptorFactory_InvalidRequiredAttributeCharacter"); }
|
get => GetString("TagHelperDescriptorFactory_InvalidRequiredAttributeCharacter");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Invalid required attribute character '{0}' in required attribute '{1}'. Separate required attributes with commas.
|
/// Invalid required attribute character '{0}' in required attribute '{1}'. Separate required attributes with commas.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string FormatTagHelperDescriptorFactory_InvalidRequiredAttributeCharacter(object p0, object p1)
|
internal static string FormatTagHelperDescriptorFactory_InvalidRequiredAttributeCharacter(object p0, object p1)
|
||||||
{
|
=> string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidRequiredAttributeCharacter"), p0, p1);
|
||||||
return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidRequiredAttributeCharacter"), p0, p1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Required attribute '{0}' has mismatched quotes '{1}' around value.
|
/// Required attribute '{0}' has mismatched quotes '{1}' around value.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string TagHelperDescriptorFactory_InvalidRequiredAttributeMismatchedQuotes
|
internal static string TagHelperDescriptorFactory_InvalidRequiredAttributeMismatchedQuotes
|
||||||
{
|
{
|
||||||
get { return GetString("TagHelperDescriptorFactory_InvalidRequiredAttributeMismatchedQuotes"); }
|
get => GetString("TagHelperDescriptorFactory_InvalidRequiredAttributeMismatchedQuotes");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Required attribute '{0}' has mismatched quotes '{1}' around value.
|
/// Required attribute '{0}' has mismatched quotes '{1}' around value.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string FormatTagHelperDescriptorFactory_InvalidRequiredAttributeMismatchedQuotes(object p0, object p1)
|
internal static string FormatTagHelperDescriptorFactory_InvalidRequiredAttributeMismatchedQuotes(object p0, object p1)
|
||||||
{
|
=> string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidRequiredAttributeMismatchedQuotes"), p0, p1);
|
||||||
return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidRequiredAttributeMismatchedQuotes"), p0, p1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Invalid character '{0}' in required attribute '{1}'. Expected supported CSS operator or ']'.
|
/// Invalid character '{0}' in required attribute '{1}'. Expected supported CSS operator or ']'.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string TagHelperDescriptorFactory_InvalidRequiredAttributeOperator
|
internal static string TagHelperDescriptorFactory_InvalidRequiredAttributeOperator
|
||||||
{
|
{
|
||||||
get { return GetString("TagHelperDescriptorFactory_InvalidRequiredAttributeOperator"); }
|
get => GetString("TagHelperDescriptorFactory_InvalidRequiredAttributeOperator");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Invalid character '{0}' in required attribute '{1}'. Expected supported CSS operator or ']'.
|
/// Invalid character '{0}' in required attribute '{1}'. Expected supported CSS operator or ']'.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string FormatTagHelperDescriptorFactory_InvalidRequiredAttributeOperator(object p0, object p1)
|
internal static string FormatTagHelperDescriptorFactory_InvalidRequiredAttributeOperator(object p0, object p1)
|
||||||
{
|
=> string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidRequiredAttributeOperator"), p0, p1);
|
||||||
return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidRequiredAttributeOperator"), p0, p1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Invalid '{0}' tag name '{1}' for tag helper '{2}'. Tag helpers cannot restrict child elements that contain a '{3}' character.
|
|
||||||
/// </summary>
|
|
||||||
internal static string TagHelperDescriptorFactory_InvalidRestrictChildrenAttributeName
|
|
||||||
{
|
|
||||||
get { return GetString("TagHelperDescriptorFactory_InvalidRestrictChildrenAttributeName"); }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Invalid '{0}' tag name '{1}' for tag helper '{2}'. Tag helpers cannot restrict child elements that contain a '{3}' character.
|
|
||||||
/// </summary>
|
|
||||||
internal static string FormatTagHelperDescriptorFactory_InvalidRestrictChildrenAttributeName(object p0, object p1, object p2, object p3)
|
|
||||||
{
|
|
||||||
return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidRestrictChildrenAttributeName"), p0, p1, p2, p3);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Invalid '{0}' tag name for tag helper '{1}'. Name cannot be null or whitespace.
|
|
||||||
/// </summary>
|
|
||||||
internal static string TagHelperDescriptorFactory_InvalidRestrictChildrenAttributeNameNullWhitespace
|
|
||||||
{
|
|
||||||
get { return GetString("TagHelperDescriptorFactory_InvalidRestrictChildrenAttributeNameNullWhitespace"); }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Invalid '{0}' tag name for tag helper '{1}'. Name cannot be null or whitespace.
|
|
||||||
/// </summary>
|
|
||||||
internal static string FormatTagHelperDescriptorFactory_InvalidRestrictChildrenAttributeNameNullWhitespace(object p0, object p1)
|
|
||||||
{
|
|
||||||
return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidRestrictChildrenAttributeNameNullWhitespace"), p0, p1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// name
|
|
||||||
/// </summary>
|
|
||||||
internal static string TagHelperDescriptorFactory_Name
|
|
||||||
{
|
|
||||||
get { return GetString("TagHelperDescriptorFactory_Name"); }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// name
|
|
||||||
/// </summary>
|
|
||||||
internal static string FormatTagHelperDescriptorFactory_Name()
|
|
||||||
{
|
|
||||||
return GetString("TagHelperDescriptorFactory_Name");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Parent Tag
|
|
||||||
/// </summary>
|
|
||||||
internal static string TagHelperDescriptorFactory_ParentTag
|
|
||||||
{
|
|
||||||
get { return GetString("TagHelperDescriptorFactory_ParentTag"); }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Parent Tag
|
|
||||||
/// </summary>
|
|
||||||
internal static string FormatTagHelperDescriptorFactory_ParentTag()
|
|
||||||
{
|
|
||||||
return GetString("TagHelperDescriptorFactory_ParentTag");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Required attribute '{0}' has a partial CSS operator. '{1}' must be followed by an equals.
|
/// Required attribute '{0}' has a partial CSS operator. '{1}' must be followed by an equals.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string TagHelperDescriptorFactory_PartialRequiredAttributeOperator
|
internal static string TagHelperDescriptorFactory_PartialRequiredAttributeOperator
|
||||||
{
|
{
|
||||||
get { return GetString("TagHelperDescriptorFactory_PartialRequiredAttributeOperator"); }
|
get => GetString("TagHelperDescriptorFactory_PartialRequiredAttributeOperator");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Required attribute '{0}' has a partial CSS operator. '{1}' must be followed by an equals.
|
/// Required attribute '{0}' has a partial CSS operator. '{1}' must be followed by an equals.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string FormatTagHelperDescriptorFactory_PartialRequiredAttributeOperator(object p0, object p1)
|
internal static string FormatTagHelperDescriptorFactory_PartialRequiredAttributeOperator(object p0, object p1)
|
||||||
{
|
=> string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_PartialRequiredAttributeOperator"), p0, p1);
|
||||||
return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_PartialRequiredAttributeOperator"), p0, p1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// prefix
|
|
||||||
/// </summary>
|
|
||||||
internal static string TagHelperDescriptorFactory_Prefix
|
|
||||||
{
|
|
||||||
get { return GetString("TagHelperDescriptorFactory_Prefix"); }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// prefix
|
|
||||||
/// </summary>
|
|
||||||
internal static string FormatTagHelperDescriptorFactory_Prefix()
|
|
||||||
{
|
|
||||||
return GetString("TagHelperDescriptorFactory_Prefix");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Tag
|
|
||||||
/// </summary>
|
|
||||||
internal static string TagHelperDescriptorFactory_Tag
|
|
||||||
{
|
|
||||||
get { return GetString("TagHelperDescriptorFactory_Tag"); }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Tag
|
|
||||||
/// </summary>
|
|
||||||
internal static string FormatTagHelperDescriptorFactory_Tag()
|
|
||||||
{
|
|
||||||
return GetString("TagHelperDescriptorFactory_Tag");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string GetString(string name, params string[] formatterNames)
|
private static string GetString(string name, params string[] formatterNames)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -15,80 +15,70 @@ namespace Microsoft.CodeAnalysis.Razor
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string ViewComponent_AmbiguousMethods
|
internal static string ViewComponent_AmbiguousMethods
|
||||||
{
|
{
|
||||||
get { return GetString("ViewComponent_AmbiguousMethods"); }
|
get => GetString("ViewComponent_AmbiguousMethods");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// View component '{0}' must have exactly one public method named '{1}' or '{2}'.
|
/// View component '{0}' must have exactly one public method named '{1}' or '{2}'.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string FormatViewComponent_AmbiguousMethods(object p0, object p1, object p2)
|
internal static string FormatViewComponent_AmbiguousMethods(object p0, object p1, object p2)
|
||||||
{
|
=> string.Format(CultureInfo.CurrentCulture, GetString("ViewComponent_AmbiguousMethods"), p0, p1, p2);
|
||||||
return string.Format(CultureInfo.CurrentCulture, GetString("ViewComponent_AmbiguousMethods"), p0, p1, p2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Method '{0}' of view component '{1}' should be declared to return {2}&lt;T&gt;.
|
/// Method '{0}' of view component '{1}' should be declared to return {2}&lt;T&gt;.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string ViewComponent_AsyncMethod_ShouldReturnTask
|
internal static string ViewComponent_AsyncMethod_ShouldReturnTask
|
||||||
{
|
{
|
||||||
get { return GetString("ViewComponent_AsyncMethod_ShouldReturnTask"); }
|
get => GetString("ViewComponent_AsyncMethod_ShouldReturnTask");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Method '{0}' of view component '{1}' should be declared to return {2}&lt;T&gt;.
|
/// Method '{0}' of view component '{1}' should be declared to return {2}&lt;T&gt;.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string FormatViewComponent_AsyncMethod_ShouldReturnTask(object p0, object p1, object p2)
|
internal static string FormatViewComponent_AsyncMethod_ShouldReturnTask(object p0, object p1, object p2)
|
||||||
{
|
=> string.Format(CultureInfo.CurrentCulture, GetString("ViewComponent_AsyncMethod_ShouldReturnTask"), p0, p1, p2);
|
||||||
return string.Format(CultureInfo.CurrentCulture, GetString("ViewComponent_AsyncMethod_ShouldReturnTask"), p0, p1, p2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Could not find an '{0}' or '{1}' method for the view component '{2}'.
|
/// Could not find an '{0}' or '{1}' method for the view component '{2}'.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string ViewComponent_CannotFindMethod
|
internal static string ViewComponent_CannotFindMethod
|
||||||
{
|
{
|
||||||
get { return GetString("ViewComponent_CannotFindMethod"); }
|
get => GetString("ViewComponent_CannotFindMethod");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Could not find an '{0}' or '{1}' method for the view component '{2}'.
|
/// Could not find an '{0}' or '{1}' method for the view component '{2}'.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string FormatViewComponent_CannotFindMethod(object p0, object p1, object p2)
|
internal static string FormatViewComponent_CannotFindMethod(object p0, object p1, object p2)
|
||||||
{
|
=> string.Format(CultureInfo.CurrentCulture, GetString("ViewComponent_CannotFindMethod"), p0, p1, p2);
|
||||||
return string.Format(CultureInfo.CurrentCulture, GetString("ViewComponent_CannotFindMethod"), p0, p1, p2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Method '{0}' of view component '{1}' cannot return a {2}.
|
/// Method '{0}' of view component '{1}' cannot return a {2}.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string ViewComponent_SyncMethod_CannotReturnTask
|
internal static string ViewComponent_SyncMethod_CannotReturnTask
|
||||||
{
|
{
|
||||||
get { return GetString("ViewComponent_SyncMethod_CannotReturnTask"); }
|
get => GetString("ViewComponent_SyncMethod_CannotReturnTask");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Method '{0}' of view component '{1}' cannot return a {2}.
|
/// Method '{0}' of view component '{1}' cannot return a {2}.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string FormatViewComponent_SyncMethod_CannotReturnTask(object p0, object p1, object p2)
|
internal static string FormatViewComponent_SyncMethod_CannotReturnTask(object p0, object p1, object p2)
|
||||||
{
|
=> string.Format(CultureInfo.CurrentCulture, GetString("ViewComponent_SyncMethod_CannotReturnTask"), p0, p1, p2);
|
||||||
return string.Format(CultureInfo.CurrentCulture, GetString("ViewComponent_SyncMethod_CannotReturnTask"), p0, p1, p2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Method '{0}' of view component '{1}' should be declared to return a value.
|
/// Method '{0}' of view component '{1}' should be declared to return a value.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string ViewComponent_SyncMethod_ShouldReturnValue
|
internal static string ViewComponent_SyncMethod_ShouldReturnValue
|
||||||
{
|
{
|
||||||
get { return GetString("ViewComponent_SyncMethod_ShouldReturnValue"); }
|
get => GetString("ViewComponent_SyncMethod_ShouldReturnValue");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Method '{0}' of view component '{1}' should be declared to return a value.
|
/// Method '{0}' of view component '{1}' should be declared to return a value.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string FormatViewComponent_SyncMethod_ShouldReturnValue(object p0, object p1)
|
internal static string FormatViewComponent_SyncMethod_ShouldReturnValue(object p0, object p1)
|
||||||
{
|
=> string.Format(CultureInfo.CurrentCulture, GetString("ViewComponent_SyncMethod_ShouldReturnValue"), p0, p1);
|
||||||
return string.Format(CultureInfo.CurrentCulture, GetString("ViewComponent_SyncMethod_ShouldReturnValue"), p0, p1);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string GetString(string name, params string[] formatterNames)
|
private static string GetString(string name, params string[] formatterNames)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,185 @@
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
namespace Microsoft.CodeAnalysis.Razor
|
||||||
|
{
|
||||||
|
internal static class RazorDiagnosticFactory
|
||||||
|
{
|
||||||
|
private const string DiagnosticPrefix = "RZ";
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Razor.Evolution starts at 0, 1000, 2000, 3000. Therefore, we should offset by 500 to ensure we can easily
|
||||||
|
* maintain this list of diagnostic descriptors in conjunction with the one in Razor.Evolution.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#region General Errors
|
||||||
|
|
||||||
|
/*
|
||||||
|
* General Errors ID Offset = 500
|
||||||
|
*/
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Language Errors
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Language Errors ID Offset = 1500
|
||||||
|
*/
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Semantic Errors
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Semantic Errors ID Offset = 2500
|
||||||
|
*/
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region TagHelper Errors
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TagHelper Errors ID Offset = 3500
|
||||||
|
*/
|
||||||
|
|
||||||
|
private static readonly RazorDiagnosticDescriptor TagHelper_InvalidAttributeNameNullOrEmpty =
|
||||||
|
new RazorDiagnosticDescriptor(
|
||||||
|
$"{DiagnosticPrefix}3500",
|
||||||
|
() => Resources.TagHelperDescriptorFactory_InvalidAttributeNameNotNullOrEmpty,
|
||||||
|
RazorDiagnosticSeverity.Error);
|
||||||
|
public static RazorDiagnostic CreateTagHelper_InvalidAttributeNameNullOrEmpty(string containingTypeName, string boundPropertyName)
|
||||||
|
{
|
||||||
|
var diagnostic = RazorDiagnostic.Create(
|
||||||
|
TagHelper_InvalidAttributeNameNullOrEmpty,
|
||||||
|
new SourceSpan(SourceLocation.Undefined, contentLength: 0),
|
||||||
|
containingTypeName,
|
||||||
|
boundPropertyName,
|
||||||
|
TagHelperTypes.HtmlAttributeNameAttribute,
|
||||||
|
TagHelperTypes.HtmlAttributeName.Name);
|
||||||
|
|
||||||
|
return diagnostic;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static readonly RazorDiagnosticDescriptor TagHelper_InvalidAttributePrefixNotNull =
|
||||||
|
new RazorDiagnosticDescriptor(
|
||||||
|
$"{DiagnosticPrefix}3501",
|
||||||
|
() => Resources.TagHelperDescriptorFactory_InvalidAttributePrefixNotNull,
|
||||||
|
RazorDiagnosticSeverity.Error);
|
||||||
|
public static RazorDiagnostic CreateTagHelper_InvalidAttributePrefixNotNull(string containingTypeName, string boundPropertyName)
|
||||||
|
{
|
||||||
|
var diagnostic = RazorDiagnostic.Create(
|
||||||
|
TagHelper_InvalidAttributePrefixNotNull,
|
||||||
|
new SourceSpan(SourceLocation.Undefined, contentLength: 0),
|
||||||
|
containingTypeName,
|
||||||
|
boundPropertyName,
|
||||||
|
TagHelperTypes.HtmlAttributeNameAttribute,
|
||||||
|
TagHelperTypes.HtmlAttributeName.DictionaryAttributePrefix,
|
||||||
|
"IDictionary<string, TValue>");
|
||||||
|
|
||||||
|
return diagnostic;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static readonly RazorDiagnosticDescriptor TagHelper_InvalidAttributePrefixNull =
|
||||||
|
new RazorDiagnosticDescriptor(
|
||||||
|
$"{DiagnosticPrefix}3502",
|
||||||
|
() => Resources.TagHelperDescriptorFactory_InvalidAttributePrefixNull,
|
||||||
|
RazorDiagnosticSeverity.Error);
|
||||||
|
public static RazorDiagnostic CreateTagHelper_InvalidAttributePrefixNull(string containingTypeName, string boundPropertyName)
|
||||||
|
{
|
||||||
|
var diagnostic = RazorDiagnostic.Create(
|
||||||
|
TagHelper_InvalidAttributePrefixNull,
|
||||||
|
new SourceSpan(SourceLocation.Undefined, contentLength: 0),
|
||||||
|
containingTypeName,
|
||||||
|
boundPropertyName,
|
||||||
|
TagHelperTypes.HtmlAttributeNameAttribute,
|
||||||
|
TagHelperTypes.HtmlAttributeName.DictionaryAttributePrefix,
|
||||||
|
"IDictionary<string, TValue>");
|
||||||
|
|
||||||
|
return diagnostic;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static readonly RazorDiagnosticDescriptor TagHelper_InvalidRequiredAttributeCharacter =
|
||||||
|
new RazorDiagnosticDescriptor(
|
||||||
|
$"{DiagnosticPrefix}3503",
|
||||||
|
() => Resources.TagHelperDescriptorFactory_InvalidRequiredAttributeCharacter,
|
||||||
|
RazorDiagnosticSeverity.Error);
|
||||||
|
public static RazorDiagnostic CreateTagHelper_InvalidRequiredAttributeCharacter(char invalidCharacter, string requiredAttributes)
|
||||||
|
{
|
||||||
|
var diagnostic = RazorDiagnostic.Create(
|
||||||
|
TagHelper_InvalidRequiredAttributeCharacter,
|
||||||
|
new SourceSpan(SourceLocation.Undefined, contentLength: 0),
|
||||||
|
invalidCharacter,
|
||||||
|
requiredAttributes);
|
||||||
|
|
||||||
|
return diagnostic;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static readonly RazorDiagnosticDescriptor TagHelper_PartialRequiredAttributeOperator =
|
||||||
|
new RazorDiagnosticDescriptor(
|
||||||
|
$"{DiagnosticPrefix}3504",
|
||||||
|
() => Resources.TagHelperDescriptorFactory_PartialRequiredAttributeOperator,
|
||||||
|
RazorDiagnosticSeverity.Error);
|
||||||
|
public static RazorDiagnostic CreateTagHelper_PartialRequiredAttributeOperator(char partialOperator, string requiredAttributes)
|
||||||
|
{
|
||||||
|
var diagnostic = RazorDiagnostic.Create(
|
||||||
|
TagHelper_PartialRequiredAttributeOperator,
|
||||||
|
new SourceSpan(SourceLocation.Undefined, contentLength: 0),
|
||||||
|
requiredAttributes,
|
||||||
|
partialOperator);
|
||||||
|
|
||||||
|
return diagnostic;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static readonly RazorDiagnosticDescriptor TagHelper_InvalidRequiredAttributeOperator =
|
||||||
|
new RazorDiagnosticDescriptor(
|
||||||
|
$"{DiagnosticPrefix}3505",
|
||||||
|
() => Resources.TagHelperDescriptorFactory_InvalidRequiredAttributeOperator,
|
||||||
|
RazorDiagnosticSeverity.Error);
|
||||||
|
public static RazorDiagnostic CreateTagHelper_InvalidRequiredAttributeOperator(char invalidOperator, string requiredAttributes)
|
||||||
|
{
|
||||||
|
var diagnostic = RazorDiagnostic.Create(
|
||||||
|
TagHelper_InvalidRequiredAttributeOperator,
|
||||||
|
new SourceSpan(SourceLocation.Undefined, contentLength: 0),
|
||||||
|
invalidOperator,
|
||||||
|
requiredAttributes);
|
||||||
|
|
||||||
|
return diagnostic;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static readonly RazorDiagnosticDescriptor TagHelper_InvalidRequiredAttributeMismatchedQuotes =
|
||||||
|
new RazorDiagnosticDescriptor(
|
||||||
|
$"{DiagnosticPrefix}3506",
|
||||||
|
() => Resources.TagHelperDescriptorFactory_InvalidRequiredAttributeMismatchedQuotes,
|
||||||
|
RazorDiagnosticSeverity.Error);
|
||||||
|
public static RazorDiagnostic CreateTagHelper_InvalidRequiredAttributeMismatchedQuotes(char quote, string requiredAttributes)
|
||||||
|
{
|
||||||
|
var diagnostic = RazorDiagnostic.Create(
|
||||||
|
TagHelper_InvalidRequiredAttributeMismatchedQuotes,
|
||||||
|
new SourceSpan(SourceLocation.Undefined, contentLength: 0),
|
||||||
|
requiredAttributes,
|
||||||
|
quote);
|
||||||
|
|
||||||
|
return diagnostic;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static readonly RazorDiagnosticDescriptor TagHelper_CouldNotFindMatchingEndBrace =
|
||||||
|
new RazorDiagnosticDescriptor(
|
||||||
|
$"{DiagnosticPrefix}3507",
|
||||||
|
() => Resources.TagHelperDescriptorFactory_CouldNotFindMatchingEndBrace,
|
||||||
|
RazorDiagnosticSeverity.Error);
|
||||||
|
public static RazorDiagnostic CreateTagHelper_CouldNotFindMatchingEndBrace(string requiredAttributes)
|
||||||
|
{
|
||||||
|
var diagnostic = RazorDiagnostic.Create(
|
||||||
|
TagHelper_CouldNotFindMatchingEndBrace,
|
||||||
|
new SourceSpan(SourceLocation.Undefined, contentLength: 0),
|
||||||
|
requiredAttributes);
|
||||||
|
|
||||||
|
return diagnostic;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,304 @@
|
||||||
|
// 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 System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
using Microsoft.AspNetCore.Razor.Evolution;
|
||||||
|
|
||||||
|
namespace Microsoft.CodeAnalysis.Razor
|
||||||
|
{
|
||||||
|
// Internal for testing
|
||||||
|
internal static class RequiredAttributeParser
|
||||||
|
{
|
||||||
|
public static void AddRequiredAttributes(string requiredAttributes, TagMatchingRuleBuilder ruleBuilder)
|
||||||
|
{
|
||||||
|
var requiredAttributeParser = new DefaultRequiredAttributeParser(requiredAttributes);
|
||||||
|
requiredAttributeParser.AddRequiredAttributes(ruleBuilder);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DefaultRequiredAttributeParser
|
||||||
|
{
|
||||||
|
private const char RequiredAttributeWildcardSuffix = '*';
|
||||||
|
|
||||||
|
private static readonly IReadOnlyDictionary<char, RequiredAttributeDescriptor.ValueComparisonMode> CssValueComparisons =
|
||||||
|
new Dictionary<char, RequiredAttributeDescriptor.ValueComparisonMode>
|
||||||
|
{
|
||||||
|
{ '=', RequiredAttributeDescriptor.ValueComparisonMode.FullMatch },
|
||||||
|
{ '^', RequiredAttributeDescriptor.ValueComparisonMode.PrefixMatch },
|
||||||
|
{ '$', RequiredAttributeDescriptor.ValueComparisonMode.SuffixMatch }
|
||||||
|
};
|
||||||
|
private static readonly char[] InvalidPlainAttributeNameCharacters = { ' ', '\t', ',', RequiredAttributeWildcardSuffix };
|
||||||
|
private static readonly char[] InvalidCssAttributeNameCharacters = (new[] { ' ', '\t', ',', ']' })
|
||||||
|
.Concat(CssValueComparisons.Keys)
|
||||||
|
.ToArray();
|
||||||
|
private static readonly char[] InvalidCssQuotelessValueCharacters = { ' ', '\t', ']' };
|
||||||
|
|
||||||
|
private int _index;
|
||||||
|
private string _requiredAttributes;
|
||||||
|
|
||||||
|
public DefaultRequiredAttributeParser(string requiredAttributes)
|
||||||
|
{
|
||||||
|
_requiredAttributes = requiredAttributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
private char Current => _requiredAttributes[_index];
|
||||||
|
|
||||||
|
private bool AtEnd => _index >= _requiredAttributes.Length;
|
||||||
|
|
||||||
|
public void AddRequiredAttributes(TagMatchingRuleBuilder ruleBuilder)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(_requiredAttributes))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var descriptors = new List<RequiredAttributeDescriptor>();
|
||||||
|
|
||||||
|
PassOptionalWhitespace();
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
var successfulParse = true;
|
||||||
|
ruleBuilder.RequireAttribute(attributeBuilder =>
|
||||||
|
{
|
||||||
|
if (At('['))
|
||||||
|
{
|
||||||
|
if (!TryParseCssSelector(attributeBuilder))
|
||||||
|
{
|
||||||
|
successfulParse = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ParsePlainSelector(attributeBuilder);
|
||||||
|
}
|
||||||
|
|
||||||
|
PassOptionalWhitespace();
|
||||||
|
|
||||||
|
if (At(','))
|
||||||
|
{
|
||||||
|
_index++;
|
||||||
|
|
||||||
|
if (!EnsureNotAtEnd(attributeBuilder))
|
||||||
|
{
|
||||||
|
successfulParse = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!AtEnd)
|
||||||
|
{
|
||||||
|
var diagnostic = RazorDiagnosticFactory.CreateTagHelper_InvalidRequiredAttributeCharacter(Current, _requiredAttributes);
|
||||||
|
attributeBuilder.AddDiagnostic(diagnostic);
|
||||||
|
successfulParse = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PassOptionalWhitespace();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!successfulParse)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (!AtEnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ParsePlainSelector(RequiredAttributeDescriptorBuilder attributeBuilder)
|
||||||
|
{
|
||||||
|
var nameEndIndex = _requiredAttributes.IndexOfAny(InvalidPlainAttributeNameCharacters, _index);
|
||||||
|
string attributeName;
|
||||||
|
|
||||||
|
var nameComparison = RequiredAttributeDescriptor.NameComparisonMode.FullMatch;
|
||||||
|
if (nameEndIndex == -1)
|
||||||
|
{
|
||||||
|
attributeName = _requiredAttributes.Substring(_index);
|
||||||
|
_index = _requiredAttributes.Length;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
attributeName = _requiredAttributes.Substring(_index, nameEndIndex - _index);
|
||||||
|
_index = nameEndIndex;
|
||||||
|
|
||||||
|
if (_requiredAttributes[nameEndIndex] == RequiredAttributeWildcardSuffix)
|
||||||
|
{
|
||||||
|
nameComparison = RequiredAttributeDescriptor.NameComparisonMode.PrefixMatch;
|
||||||
|
|
||||||
|
// Move past wild card
|
||||||
|
_index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
attributeBuilder.Name(attributeName);
|
||||||
|
attributeBuilder.NameComparisonMode(nameComparison);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ParseCssAttributeName(RequiredAttributeDescriptorBuilder builder)
|
||||||
|
{
|
||||||
|
var nameStartIndex = _index;
|
||||||
|
var nameEndIndex = _requiredAttributes.IndexOfAny(InvalidCssAttributeNameCharacters, _index);
|
||||||
|
nameEndIndex = nameEndIndex == -1 ? _requiredAttributes.Length : nameEndIndex;
|
||||||
|
_index = nameEndIndex;
|
||||||
|
|
||||||
|
var attributeName = _requiredAttributes.Substring(nameStartIndex, nameEndIndex - nameStartIndex);
|
||||||
|
|
||||||
|
builder.Name(attributeName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool TryParseCssValueComparison(RequiredAttributeDescriptorBuilder builder, out RequiredAttributeDescriptor.ValueComparisonMode valueComparison)
|
||||||
|
{
|
||||||
|
Debug.Assert(!AtEnd);
|
||||||
|
|
||||||
|
if (CssValueComparisons.TryGetValue(Current, out valueComparison))
|
||||||
|
{
|
||||||
|
var op = Current;
|
||||||
|
_index++;
|
||||||
|
|
||||||
|
if (op != '=' && At('='))
|
||||||
|
{
|
||||||
|
// Two length operator (ex: ^=). Move past the second piece
|
||||||
|
_index++;
|
||||||
|
}
|
||||||
|
else if (op != '=') // We're at an incomplete operator (ex: [foo^]
|
||||||
|
{
|
||||||
|
var diagnostic = RazorDiagnosticFactory.CreateTagHelper_PartialRequiredAttributeOperator(op, _requiredAttributes);
|
||||||
|
builder.AddDiagnostic(diagnostic);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!At(']'))
|
||||||
|
{
|
||||||
|
var diagnostic = RazorDiagnosticFactory.CreateTagHelper_InvalidRequiredAttributeOperator(Current, _requiredAttributes);
|
||||||
|
builder.AddDiagnostic(diagnostic);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.ValueComparisonMode(valueComparison);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool TryParseCssValue(RequiredAttributeDescriptorBuilder builder)
|
||||||
|
{
|
||||||
|
int valueStart;
|
||||||
|
int valueEnd;
|
||||||
|
if (At('\'') || At('"'))
|
||||||
|
{
|
||||||
|
var quote = Current;
|
||||||
|
|
||||||
|
// Move past the quote
|
||||||
|
_index++;
|
||||||
|
|
||||||
|
valueStart = _index;
|
||||||
|
valueEnd = _requiredAttributes.IndexOf(quote, _index);
|
||||||
|
if (valueEnd == -1)
|
||||||
|
{
|
||||||
|
var diagnostic = RazorDiagnosticFactory.CreateTagHelper_InvalidRequiredAttributeMismatchedQuotes(quote, _requiredAttributes);
|
||||||
|
builder.AddDiagnostic(diagnostic);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
_index = valueEnd + 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
valueStart = _index;
|
||||||
|
var valueEndIndex = _requiredAttributes.IndexOfAny(InvalidCssQuotelessValueCharacters, _index);
|
||||||
|
valueEnd = valueEndIndex == -1 ? _requiredAttributes.Length : valueEndIndex;
|
||||||
|
_index = valueEnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
var value = _requiredAttributes.Substring(valueStart, valueEnd - valueStart);
|
||||||
|
|
||||||
|
builder.Value(value);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool TryParseCssSelector(RequiredAttributeDescriptorBuilder attributeBuilder)
|
||||||
|
{
|
||||||
|
Debug.Assert(At('['));
|
||||||
|
|
||||||
|
// Move past '['.
|
||||||
|
_index++;
|
||||||
|
PassOptionalWhitespace();
|
||||||
|
|
||||||
|
ParseCssAttributeName(attributeBuilder);
|
||||||
|
|
||||||
|
PassOptionalWhitespace();
|
||||||
|
|
||||||
|
if (!EnsureNotAtEnd(attributeBuilder))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!TryParseCssValueComparison(attributeBuilder, out RequiredAttributeDescriptor.ValueComparisonMode valueComparison))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
PassOptionalWhitespace();
|
||||||
|
|
||||||
|
if (!EnsureNotAtEnd(attributeBuilder))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (valueComparison != RequiredAttributeDescriptor.ValueComparisonMode.None && !TryParseCssValue(attributeBuilder))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
PassOptionalWhitespace();
|
||||||
|
|
||||||
|
if (At(']'))
|
||||||
|
{
|
||||||
|
// Move past the ending bracket.
|
||||||
|
_index++;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (AtEnd)
|
||||||
|
{
|
||||||
|
var diagnostic = RazorDiagnosticFactory.CreateTagHelper_CouldNotFindMatchingEndBrace(_requiredAttributes);
|
||||||
|
attributeBuilder.AddDiagnostic(diagnostic);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var diagnostic = RazorDiagnosticFactory.CreateTagHelper_InvalidRequiredAttributeCharacter(Current, _requiredAttributes);
|
||||||
|
attributeBuilder.AddDiagnostic(diagnostic);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool EnsureNotAtEnd(RequiredAttributeDescriptorBuilder builder)
|
||||||
|
{
|
||||||
|
if (AtEnd)
|
||||||
|
{
|
||||||
|
var diagnostic = RazorDiagnosticFactory.CreateTagHelper_CouldNotFindMatchingEndBrace(_requiredAttributes);
|
||||||
|
builder.AddDiagnostic(diagnostic);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool At(char c)
|
||||||
|
{
|
||||||
|
return !AtEnd && Current == c;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PassOptionalWhitespace()
|
||||||
|
{
|
||||||
|
while (!AtEnd && (Current == ' ' || Current == '\t'))
|
||||||
|
{
|
||||||
|
_index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -117,42 +117,15 @@
|
||||||
<resheader name="writer">
|
<resheader name="writer">
|
||||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
</resheader>
|
</resheader>
|
||||||
<data name="Argument_Cannot_Be_Null_Or_Empty" xml:space="preserve">
|
|
||||||
<value>{0} cannot be null or an empty string.</value>
|
|
||||||
</data>
|
|
||||||
<data name="HtmlTargetElementAttribute_InvalidName" xml:space="preserve">
|
|
||||||
<value>Tag helpers cannot target {0} name '{1}' because it contains a '{2}' character.</value>
|
|
||||||
</data>
|
|
||||||
<data name="HtmlTargetElementAttribute_NameCannotBeNullOrWhitespace" xml:space="preserve">
|
|
||||||
<value>{0} name cannot be null or whitespace.</value>
|
|
||||||
</data>
|
|
||||||
<data name="TagHelperDescriptorFactory_Attribute" xml:space="preserve">
|
|
||||||
<value>Attribute</value>
|
|
||||||
</data>
|
|
||||||
<data name="TagHelperDescriptorFactory_CouldNotFindMatchingEndBrace" xml:space="preserve">
|
<data name="TagHelperDescriptorFactory_CouldNotFindMatchingEndBrace" xml:space="preserve">
|
||||||
<value>Could not find matching ']' for required attribute '{0}'.</value>
|
<value>Could not find matching ']' for required attribute '{0}'.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TagHelperDescriptorFactory_InvalidAttributeNameAttribute" xml:space="preserve">
|
<data name="TagHelperDescriptorFactory_InvalidAttributePrefixNotNull" xml:space="preserve">
|
||||||
<value>Invalid tag helper bound property '{0}.{1}'. An '{2}' must not be associated with a property with no public setter unless its type implements '{3}'.</value>
|
<value>Invalid tag helper bound property '{0}.{1}'. '{2}.{3}' must be null unless property type implements '{4}'.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TagHelperDescriptorFactory_InvalidAttributeNameNotNullOrEmpty" xml:space="preserve">
|
<data name="TagHelperDescriptorFactory_InvalidAttributeNameNotNullOrEmpty" xml:space="preserve">
|
||||||
<value>Invalid tag helper bound property '{0}.{1}'. '{2}.{3}' must be null or empty if property has no public setter.</value>
|
<value>Invalid tag helper bound property '{0}.{1}'. '{2}.{3}' must be null or empty if property has no public setter.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TagHelperDescriptorFactory_InvalidAttributeNameNullOrEmpty" xml:space="preserve">
|
|
||||||
<value>Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with a null or empty name.</value>
|
|
||||||
</data>
|
|
||||||
<data name="TagHelperDescriptorFactory_InvalidAttributeNameOrPrefixCharacter" xml:space="preserve">
|
|
||||||
<value>Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with {2} '{3}' because {2} contains a '{4}' character.</value>
|
|
||||||
</data>
|
|
||||||
<data name="TagHelperDescriptorFactory_InvalidAttributeNameOrPrefixStart" xml:space="preserve">
|
|
||||||
<value>Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with {2} '{3}' because {2} starts with '{4}'.</value>
|
|
||||||
</data>
|
|
||||||
<data name="TagHelperDescriptorFactory_InvalidAttributeNameOrPrefixWhitespace" xml:space="preserve">
|
|
||||||
<value>Invalid tag helper bound property '{0}.{1}'. Tag helpers cannot bind to HTML attributes with a whitespace {2}.</value>
|
|
||||||
</data>
|
|
||||||
<data name="TagHelperDescriptorFactory_InvalidAttributePrefixNotNull" xml:space="preserve">
|
|
||||||
<value>Invalid tag helper bound property '{0}.{1}'. '{2}.{3}' must be null unless property type implements '{4}'.</value>
|
|
||||||
</data>
|
|
||||||
<data name="TagHelperDescriptorFactory_InvalidAttributePrefixNull" xml:space="preserve">
|
<data name="TagHelperDescriptorFactory_InvalidAttributePrefixNull" xml:space="preserve">
|
||||||
<value>Invalid tag helper bound property '{0}.{1}'. '{2}.{3}' must not be null if property has no public setter and its type implements '{4}'.</value>
|
<value>Invalid tag helper bound property '{0}.{1}'. '{2}.{3}' must not be null if property has no public setter and its type implements '{4}'.</value>
|
||||||
</data>
|
</data>
|
||||||
|
|
@ -165,25 +138,7 @@
|
||||||
<data name="TagHelperDescriptorFactory_InvalidRequiredAttributeOperator" xml:space="preserve">
|
<data name="TagHelperDescriptorFactory_InvalidRequiredAttributeOperator" xml:space="preserve">
|
||||||
<value>Invalid character '{0}' in required attribute '{1}'. Expected supported CSS operator or ']'.</value>
|
<value>Invalid character '{0}' in required attribute '{1}'. Expected supported CSS operator or ']'.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TagHelperDescriptorFactory_InvalidRestrictChildrenAttributeName" xml:space="preserve">
|
|
||||||
<value>Invalid '{0}' tag name '{1}' for tag helper '{2}'. Tag helpers cannot restrict child elements that contain a '{3}' character.</value>
|
|
||||||
</data>
|
|
||||||
<data name="TagHelperDescriptorFactory_InvalidRestrictChildrenAttributeNameNullWhitespace" xml:space="preserve">
|
|
||||||
<value>Invalid '{0}' tag name for tag helper '{1}'. Name cannot be null or whitespace.</value>
|
|
||||||
</data>
|
|
||||||
<data name="TagHelperDescriptorFactory_Name" xml:space="preserve">
|
|
||||||
<value>name</value>
|
|
||||||
</data>
|
|
||||||
<data name="TagHelperDescriptorFactory_ParentTag" xml:space="preserve">
|
|
||||||
<value>Parent Tag</value>
|
|
||||||
</data>
|
|
||||||
<data name="TagHelperDescriptorFactory_PartialRequiredAttributeOperator" xml:space="preserve">
|
<data name="TagHelperDescriptorFactory_PartialRequiredAttributeOperator" xml:space="preserve">
|
||||||
<value>Required attribute '{0}' has a partial CSS operator. '{1}' must be followed by an equals.</value>
|
<value>Required attribute '{0}' has a partial CSS operator. '{1}' must be followed by an equals.</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="TagHelperDescriptorFactory_Prefix" xml:space="preserve">
|
|
||||||
<value>prefix</value>
|
|
||||||
</data>
|
|
||||||
<data name="TagHelperDescriptorFactory_Tag" xml:space="preserve">
|
|
||||||
<value>Tag</value>
|
|
||||||
</data>
|
|
||||||
</root>
|
</root>
|
||||||
|
|
@ -32,8 +32,11 @@ namespace Microsoft.CodeAnalysis.Razor
|
||||||
|
|
||||||
foreach (var type in types)
|
foreach (var type in types)
|
||||||
{
|
{
|
||||||
var descriptors = factory.CreateDescriptors(type, errors);
|
var descriptor = factory.CreateDescriptor(type);
|
||||||
results.AddRange(descriptors);
|
if (descriptor != null)
|
||||||
|
{
|
||||||
|
results.Add(descriptor);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -52,7 +55,10 @@ namespace Microsoft.CodeAnalysis.Razor
|
||||||
{
|
{
|
||||||
var descriptor = factory.CreateDescriptor(type);
|
var descriptor = factory.CreateDescriptor(type);
|
||||||
|
|
||||||
results.Add(descriptor);
|
if (descriptor != null)
|
||||||
|
{
|
||||||
|
results.Add(descriptor);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
@ -36,66 +35,69 @@ namespace Microsoft.CodeAnalysis.Razor
|
||||||
var shortName = GetShortName(type);
|
var shortName = GetShortName(type);
|
||||||
var tagName = $"vc:{DefaultTagHelperDescriptorFactory.ToHtmlCase(shortName)}";
|
var tagName = $"vc:{DefaultTagHelperDescriptorFactory.ToHtmlCase(shortName)}";
|
||||||
var typeName = $"__Generated__{shortName}ViewComponentTagHelper";
|
var typeName = $"__Generated__{shortName}ViewComponentTagHelper";
|
||||||
|
var descriptorBuilder = ITagHelperDescriptorBuilder.Create(typeName, assemblyName);
|
||||||
var descriptor = new TagHelperDescriptor
|
var methodParameters = GetInvokeMethodParameters(type);
|
||||||
|
descriptorBuilder.TagMatchingRule(ruleBuilder =>
|
||||||
{
|
{
|
||||||
TagName = tagName,
|
ruleBuilder.RequireTagName(tagName);
|
||||||
TypeName = typeName,
|
AddRequiredAttributes(methodParameters, ruleBuilder);
|
||||||
AssemblyName = assemblyName
|
});
|
||||||
};
|
|
||||||
|
|
||||||
SetAttributeDescriptors(type, descriptor);
|
AddBoundAttributes(methodParameters, descriptorBuilder);
|
||||||
|
|
||||||
descriptor.PropertyBag.Add(ViewComponentTypes.ViewComponentNameKey, shortName);
|
descriptorBuilder.AddMetadata(ViewComponentTypes.ViewComponentNameKey, shortName);
|
||||||
|
|
||||||
|
var descriptor = descriptorBuilder.Build();
|
||||||
return descriptor;
|
return descriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetAttributeDescriptors(INamedTypeSymbol type, TagHelperDescriptor descriptor)
|
private void AddRequiredAttributes(ImmutableArray<IParameterSymbol> methodParameters, TagMatchingRuleBuilder builder)
|
||||||
{
|
{
|
||||||
var methodParameters = GetInvokeMethodParameters(type);
|
foreach (var parameter in methodParameters)
|
||||||
var attributeDescriptors = new List<TagHelperAttributeDescriptor>();
|
{
|
||||||
var indexerDescriptors = new List<TagHelperAttributeDescriptor>();
|
if (GetIndexerValueTypeName(parameter) == null)
|
||||||
var requiredAttributeDescriptors = new List<TagHelperRequiredAttributeDescriptor>();
|
{
|
||||||
|
// Set required attributes only for non-indexer attributes. Indexer attributes can't be required attributes
|
||||||
|
// because there are two ways of setting values for the attribute.
|
||||||
|
builder.RequireAttribute(attributeBuilder =>
|
||||||
|
{
|
||||||
|
var lowerKebabName = DefaultTagHelperDescriptorFactory.ToHtmlCase(parameter.Name);
|
||||||
|
attributeBuilder.Name(lowerKebabName);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddBoundAttributes(ImmutableArray<IParameterSymbol> methodParameters, ITagHelperDescriptorBuilder builder)
|
||||||
|
{
|
||||||
foreach (var parameter in methodParameters)
|
foreach (var parameter in methodParameters)
|
||||||
{
|
{
|
||||||
var lowerKebabName = DefaultTagHelperDescriptorFactory.ToHtmlCase(parameter.Name);
|
var lowerKebabName = DefaultTagHelperDescriptorFactory.ToHtmlCase(parameter.Name);
|
||||||
var typeName = parameter.Type.ToDisplayString(FullNameTypeDisplayFormat);
|
var typeName = parameter.Type.ToDisplayString(FullNameTypeDisplayFormat);
|
||||||
var attributeDescriptor = new TagHelperAttributeDescriptor
|
builder.BindAttribute(attributeBuilder =>
|
||||||
{
|
{
|
||||||
Name = lowerKebabName,
|
attributeBuilder
|
||||||
PropertyName = parameter.Name,
|
.Name(lowerKebabName)
|
||||||
TypeName = typeName
|
.PropertyName(parameter.Name)
|
||||||
};
|
.TypeName(typeName);
|
||||||
|
|
||||||
attributeDescriptor.IsEnum = parameter.Type.TypeKind == TypeKind.Enum;
|
if (parameter.Type.TypeKind == TypeKind.Enum)
|
||||||
attributeDescriptor.IsIndexer = false;
|
|
||||||
|
|
||||||
attributeDescriptors.Add(attributeDescriptor);
|
|
||||||
|
|
||||||
var indexerDescriptor = GetIndexerAttributeDescriptor(parameter, lowerKebabName);
|
|
||||||
if (indexerDescriptor != null)
|
|
||||||
{
|
|
||||||
indexerDescriptors.Add(indexerDescriptor);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Set required attributes only for non-indexer attributes. Indexer attributes can't be required attributes
|
|
||||||
// because there are two ways of setting values for the attribute.
|
|
||||||
requiredAttributeDescriptors.Add(new TagHelperRequiredAttributeDescriptor
|
|
||||||
{
|
{
|
||||||
Name = lowerKebabName
|
attributeBuilder.AsEnum();
|
||||||
});
|
}
|
||||||
}
|
else
|
||||||
|
{
|
||||||
|
var dictionaryValueType = GetIndexerValueTypeName(parameter);
|
||||||
|
if (dictionaryValueType != null)
|
||||||
|
{
|
||||||
|
attributeBuilder.AsDictionary(lowerKebabName + "-", dictionaryValueType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
attributeDescriptors.AddRange(indexerDescriptors);
|
|
||||||
descriptor.Attributes = attributeDescriptors;
|
|
||||||
descriptor.RequiredAttributes = requiredAttributeDescriptors;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private TagHelperAttributeDescriptor GetIndexerAttributeDescriptor(IParameterSymbol parameter, string name)
|
private string GetIndexerValueTypeName(IParameterSymbol parameter)
|
||||||
{
|
{
|
||||||
INamedTypeSymbol dictionaryType;
|
INamedTypeSymbol dictionaryType;
|
||||||
if ((parameter.Type as INamedTypeSymbol)?.ConstructedFrom == _iDictionarySymbol)
|
if ((parameter.Type as INamedTypeSymbol)?.ConstructedFrom == _iDictionarySymbol)
|
||||||
|
|
@ -117,16 +119,9 @@ namespace Microsoft.CodeAnalysis.Razor
|
||||||
}
|
}
|
||||||
|
|
||||||
var type = dictionaryType.TypeArguments[1];
|
var type = dictionaryType.TypeArguments[1];
|
||||||
var descriptor = new TagHelperAttributeDescriptor
|
var typeName = type.ToDisplayString(FullNameTypeDisplayFormat);
|
||||||
{
|
|
||||||
Name = name + "-",
|
|
||||||
PropertyName = parameter.Name,
|
|
||||||
TypeName = type.ToDisplayString(FullNameTypeDisplayFormat),
|
|
||||||
IsEnum = type.TypeKind == TypeKind.Enum,
|
|
||||||
IsIndexer = true
|
|
||||||
};
|
|
||||||
|
|
||||||
return descriptor;
|
return typeName;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ImmutableArray<IParameterSymbol> GetInvokeMethodParameters(INamedTypeSymbol componentType)
|
private ImmutableArray<IParameterSymbol> GetInvokeMethodParameters(INamedTypeSymbol componentType)
|
||||||
|
|
|
||||||
|
|
@ -1,80 +0,0 @@
|
||||||
// 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.Diagnostics;
|
|
||||||
using System.Text;
|
|
||||||
using System.Xml.Linq;
|
|
||||||
|
|
||||||
namespace Microsoft.CodeAnalysis.Razor
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Extracts summary and remarks XML documentation from XML member documentation.
|
|
||||||
/// </summary>
|
|
||||||
internal class XmlMemberDocumentation
|
|
||||||
{
|
|
||||||
private readonly XElement _element;
|
|
||||||
|
|
||||||
public XmlMemberDocumentation(string content)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(content))
|
|
||||||
{
|
|
||||||
throw new ArgumentException(Resources.FormatArgument_Cannot_Be_Null_Or_Empty(nameof(content)));
|
|
||||||
}
|
|
||||||
|
|
||||||
// the structure of the XML is defined by: https://msdn.microsoft.com/en-us/library/fsbx0t7x.aspx
|
|
||||||
// we expect the root node of the content we are passed to always be 'member' or 'doc'.
|
|
||||||
_element = XElement.Parse(content);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Retrieves the <c><summary></c> documentation.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns><c><summary></c> documentation.</returns>
|
|
||||||
public string GetSummary()
|
|
||||||
{
|
|
||||||
var summaryElement = _element.Element("summary");
|
|
||||||
if (summaryElement != null)
|
|
||||||
{
|
|
||||||
var summaryValue = GetElementValue(summaryElement);
|
|
||||||
|
|
||||||
return summaryValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Retrieves the <c><remarks></c> documentation.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns><c><remarks></c> documentation.</returns>
|
|
||||||
public string GetRemarks()
|
|
||||||
{
|
|
||||||
var remarksElement = _element.Element("remarks");
|
|
||||||
|
|
||||||
if (remarksElement != null)
|
|
||||||
{
|
|
||||||
var remarksValue = GetElementValue(remarksElement);
|
|
||||||
|
|
||||||
return remarksValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string GetElementValue(XElement element)
|
|
||||||
{
|
|
||||||
var stringBuilder = new StringBuilder();
|
|
||||||
var node = element.FirstNode;
|
|
||||||
|
|
||||||
while (node != null)
|
|
||||||
{
|
|
||||||
stringBuilder.Append(node.ToString(SaveOptions.DisableFormatting));
|
|
||||||
|
|
||||||
node = node.NextNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
return stringBuilder.ToString().Trim();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -50,11 +50,12 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||||
// Per https://github.com/dotnet/roslyn/issues/12770 - there's currently no support for documentation in the OOP host
|
// Per https://github.com/dotnet/roslyn/issues/12770 - there's currently no support for documentation in the OOP host
|
||||||
// until that's available we add the documentation on the VS side by looking up each symbol again.
|
// until that's available we add the documentation on the VS side by looking up each symbol again.
|
||||||
var compilation = await project.GetCompilationAsync().ConfigureAwait(false);
|
var compilation = await project.GetCompilationAsync().ConfigureAwait(false);
|
||||||
AddXmlDocumentation(compilation, result.Descriptors);
|
var documentedTagHelpers = GetDocumentedTagHelpers(compilation, result.Descriptors);
|
||||||
return result;
|
var documentedResult = new TagHelperResolutionResult(documentedTagHelpers, result.Diagnostics);
|
||||||
|
|
||||||
|
return documentedResult;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -82,42 +83,123 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddXmlDocumentation(Compilation compilation, IReadOnlyList<TagHelperDescriptor> tagHelpers)
|
private IReadOnlyList<TagHelperDescriptor> GetDocumentedTagHelpers(Compilation compilation, IReadOnlyList<TagHelperDescriptor> tagHelpers)
|
||||||
{
|
{
|
||||||
|
var documentedTagHelpers = new List<TagHelperDescriptor>();
|
||||||
for (var i = 0; i < tagHelpers.Count; i++)
|
for (var i = 0; i < tagHelpers.Count; i++)
|
||||||
{
|
{
|
||||||
var tagHelper = tagHelpers[i];
|
var tagHelper = tagHelpers[i];
|
||||||
tagHelper.DesignTimeDescriptor = tagHelper.DesignTimeDescriptor ?? new TagHelperDesignTimeDescriptor();
|
|
||||||
|
|
||||||
var symbol = compilation.GetTypeByMetadataName(tagHelper.TypeName);
|
if (tagHelper.Documentation != null)
|
||||||
|
{
|
||||||
|
documentedTagHelpers.Add(tagHelper);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var typeName = tagHelper.Metadata[ITagHelperDescriptorBuilder.TypeNameKey];
|
||||||
|
var symbol = compilation.GetTypeByMetadataName(typeName);
|
||||||
if (symbol != null)
|
if (symbol != null)
|
||||||
{
|
{
|
||||||
|
var tagHelperBuilder = ShallowCopy(typeName, tagHelper);
|
||||||
var xml = symbol.GetDocumentationCommentXml();
|
var xml = symbol.GetDocumentationCommentXml();
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(xml))
|
if (!string.IsNullOrEmpty(xml))
|
||||||
{
|
{
|
||||||
var documentation = new XmlMemberDocumentation(xml);
|
tagHelperBuilder.Documentation(xml);
|
||||||
tagHelper.DesignTimeDescriptor.Summary = documentation.GetSummary();
|
|
||||||
tagHelper.DesignTimeDescriptor.Remarks = documentation.GetRemarks();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var attribute in tagHelper.Attributes)
|
foreach (var attribute in tagHelper.BoundAttributes)
|
||||||
{
|
{
|
||||||
attribute.DesignTimeDescriptor = attribute.DesignTimeDescriptor ?? new TagHelperAttributeDesignTimeDescriptor();
|
var propertyName = attribute.Metadata[ITagHelperBoundAttributeDescriptorBuilder.PropertyNameKey];
|
||||||
|
|
||||||
var attributeSymbol = symbol.GetMembers(attribute.PropertyName).FirstOrDefault();
|
var resolvedAttribute = attribute;
|
||||||
|
var attributeSymbol = symbol.GetMembers(propertyName).FirstOrDefault();
|
||||||
if (attributeSymbol != null)
|
if (attributeSymbol != null)
|
||||||
{
|
{
|
||||||
xml = attributeSymbol.GetDocumentationCommentXml();
|
xml = attributeSymbol.GetDocumentationCommentXml();
|
||||||
if (!string.IsNullOrEmpty(xml))
|
if (!string.IsNullOrEmpty(xml))
|
||||||
{
|
{
|
||||||
var documentation = new XmlMemberDocumentation(xml);
|
var attributeBuilder = ShallowCopy(typeName, resolvedAttribute);
|
||||||
attribute.DesignTimeDescriptor.Summary = documentation.GetSummary();
|
attributeBuilder.Documentation(xml);
|
||||||
attribute.DesignTimeDescriptor.Remarks = documentation.GetRemarks();
|
resolvedAttribute = attributeBuilder.Build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tagHelperBuilder.BindAttribute(resolvedAttribute);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tagHelper = tagHelperBuilder.Build();
|
||||||
|
}
|
||||||
|
|
||||||
|
documentedTagHelpers.Add(tagHelper);
|
||||||
|
}
|
||||||
|
|
||||||
|
return documentedTagHelpers;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ITagHelperBoundAttributeDescriptorBuilder ShallowCopy(string tagHelperTypeName, BoundAttributeDescriptor attribute)
|
||||||
|
{
|
||||||
|
var builder = ITagHelperBoundAttributeDescriptorBuilder.Create(tagHelperTypeName);
|
||||||
|
|
||||||
|
if (attribute.IsEnum)
|
||||||
|
{
|
||||||
|
builder.AsEnum();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (attribute.IndexerNamePrefix != null)
|
||||||
|
{
|
||||||
|
builder.AsDictionary(attribute.IndexerNamePrefix, attribute.IndexerTypeName);
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.Name(attribute.Name);
|
||||||
|
builder.TypeName(attribute.TypeName);
|
||||||
|
|
||||||
|
var propertyName = attribute.Metadata[ITagHelperBoundAttributeDescriptorBuilder.PropertyNameKey];
|
||||||
|
builder.PropertyName(propertyName);
|
||||||
|
|
||||||
|
foreach (var metadata in attribute.Metadata)
|
||||||
|
{
|
||||||
|
builder.AddMetadata(metadata.Key, metadata.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var diagnostic in attribute.Diagnostics)
|
||||||
|
{
|
||||||
|
builder.AddDiagnostic(diagnostic);
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ITagHelperDescriptorBuilder ShallowCopy(string tagHelperTypeName, TagHelperDescriptor tagHelper)
|
||||||
|
{
|
||||||
|
var builder = ITagHelperDescriptorBuilder.Create(tagHelperTypeName, tagHelper.AssemblyName);
|
||||||
|
|
||||||
|
foreach (var rule in tagHelper.TagMatchingRules)
|
||||||
|
{
|
||||||
|
builder.TagMatchingRule(rule);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tagHelper.AllowedChildTags != null)
|
||||||
|
{
|
||||||
|
foreach (var allowedChild in tagHelper.AllowedChildTags)
|
||||||
|
{
|
||||||
|
builder.AllowChildTag(allowedChild);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
builder.TagOutputHint(tagHelper.TagOutputHint);
|
||||||
|
|
||||||
|
foreach (var metadata in tagHelper.Metadata)
|
||||||
|
{
|
||||||
|
builder.AddMetadata(metadata.Key, metadata.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var diagnostic in tagHelper.Diagnostics)
|
||||||
|
{
|
||||||
|
builder.AddDiagnostic(diagnostic);
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
private IVsActivityLog GetActivityLog()
|
private IVsActivityLog GetActivityLog()
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@
|
||||||
<PackageReference Include="Microsoft.VisualStudio.Shell.Interop.11.0" Version="11.0.61030" />
|
<PackageReference Include="Microsoft.VisualStudio.Shell.Interop.11.0" Version="11.0.61030" />
|
||||||
<PackageReference Include="Microsoft.VisualStudio.Shell.Interop.12.0" Version="12.0.30110" />
|
<PackageReference Include="Microsoft.VisualStudio.Shell.Interop.12.0" Version="12.0.30110" />
|
||||||
<PackageReference Include="Microsoft.VisualStudio.Text.Data" Version="15.0.26201" />
|
<PackageReference Include="Microsoft.VisualStudio.Text.Data" Version="15.0.26201" />
|
||||||
|
<PackageReference Include="Newtonsoft.Json" Version="$(JsonNetVersion)" />
|
||||||
<ProjectReference Include="..\..\src\Microsoft.CodeAnalysis.Razor.Workspaces\Microsoft.CodeAnalysis.Razor.Workspaces.csproj" />
|
<ProjectReference Include="..\..\src\Microsoft.CodeAnalysis.Razor.Workspaces\Microsoft.CodeAnalysis.Razor.Workspaces.csproj" />
|
||||||
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Razor.Evolution\Microsoft.AspNetCore.Razor.Evolution.csproj" />
|
<ProjectReference Include="..\..\src\Microsoft.AspNetCore.Razor.Evolution\Microsoft.AspNetCore.Razor.Evolution.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
|
||||||
|
|
@ -10,21 +10,33 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||||
private static readonly ResourceManager _resourceManager
|
private static readonly ResourceManager _resourceManager
|
||||||
= new ResourceManager("Microsoft.VisualStudio.LanguageServices.Razor.Resources", typeof(Resources).GetTypeInfo().Assembly);
|
= new ResourceManager("Microsoft.VisualStudio.LanguageServices.Razor.Resources", typeof(Resources).GetTypeInfo().Assembly);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Deserialization of {0} type '{1}' is not supported.
|
||||||
|
/// </summary>
|
||||||
|
internal static string RazorDiagnosticJsonConverter_UnsupportedRazorDiagnosticType
|
||||||
|
{
|
||||||
|
get => GetString("RazorDiagnosticJsonConverter_UnsupportedRazorDiagnosticType");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Deserialization of {0} type '{1}' is not supported.
|
||||||
|
/// </summary>
|
||||||
|
internal static string FormatRazorDiagnosticJsonConverter_UnsupportedRazorDiagnosticType(object p0, object p1)
|
||||||
|
=> string.Format(CultureInfo.CurrentCulture, GetString("RazorDiagnosticJsonConverter_UnsupportedRazorDiagnosticType"), p0, p1);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// An unexpected exception occurred when invoking '{0}.{1}' on the Razor language service.
|
/// An unexpected exception occurred when invoking '{0}.{1}' on the Razor language service.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string UnexpectedException
|
internal static string UnexpectedException
|
||||||
{
|
{
|
||||||
get { return GetString("UnexpectedException"); }
|
get => GetString("UnexpectedException");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// An unexpected exception occurred when invoking '{0}.{1}' on the Razor language service.
|
/// An unexpected exception occurred when invoking '{0}.{1}' on the Razor language service.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static string FormatUnexpectedException(object p0, object p1)
|
internal static string FormatUnexpectedException(object p0, object p1)
|
||||||
{
|
=> string.Format(CultureInfo.CurrentCulture, GetString("UnexpectedException"), p0, p1);
|
||||||
return string.Format(CultureInfo.CurrentCulture, GetString("UnexpectedException"), p0, p1);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string GetString(string name, params string[] formatterNames)
|
private static string GetString(string name, params string[] formatterNames)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,83 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using Microsoft.AspNetCore.Razor.Evolution;
|
||||||
|
using Microsoft.AspNetCore.Razor.Evolution.Legacy;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
|
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||||
|
{
|
||||||
|
public class RazorDiagnosticJsonConverter : JsonConverter
|
||||||
|
{
|
||||||
|
public static readonly RazorDiagnosticJsonConverter Instance = new RazorDiagnosticJsonConverter();
|
||||||
|
private const string RazorDiagnosticMessageKey = "Message";
|
||||||
|
private const string RazorDiagnosticTypeNameKey = "TypeName";
|
||||||
|
|
||||||
|
public override bool CanConvert(Type objectType)
|
||||||
|
{
|
||||||
|
return typeof(RazorDiagnostic).IsAssignableFrom(objectType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
if (reader.TokenType != JsonToken.StartObject)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var diagnostic = JObject.Load(reader);
|
||||||
|
var span = diagnostic[nameof(RazorDiagnostic.Span)].Value<JObject>();
|
||||||
|
var absoluteIndex = span[nameof(SourceSpan.AbsoluteIndex)].Value<int>();
|
||||||
|
var lineIndex = span[nameof(SourceSpan.LineIndex)].Value<int>();
|
||||||
|
var characterIndex = span[nameof(SourceSpan.CharacterIndex)].Value<int>();
|
||||||
|
var length = span[nameof(SourceSpan.Length)].Value<int>();
|
||||||
|
var filePath = span[nameof(SourceSpan.FilePath)].Value<string>();
|
||||||
|
var message = diagnostic[RazorDiagnosticMessageKey].Value<string>();
|
||||||
|
var typeName = diagnostic[RazorDiagnosticTypeNameKey].Value<string>();
|
||||||
|
|
||||||
|
if (string.Equals(typeName, typeof(DefaultRazorDiagnostic).FullName, StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
var id = diagnostic[nameof(RazorDiagnostic.Id)].Value<string>();
|
||||||
|
var severity = diagnostic[nameof(RazorDiagnostic.Severity)].Value<int>();
|
||||||
|
|
||||||
|
var descriptor = new RazorDiagnosticDescriptor(id, () => message, (RazorDiagnosticSeverity)severity);
|
||||||
|
var sourceSpan = new SourceSpan(filePath, absoluteIndex, lineIndex, characterIndex, length);
|
||||||
|
|
||||||
|
return RazorDiagnostic.Create(descriptor, sourceSpan);
|
||||||
|
}
|
||||||
|
else if (string.Equals(typeName, typeof(LegacyRazorDiagnostic).FullName, StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
var error = new RazorError(message, absoluteIndex, lineIndex, characterIndex, length);
|
||||||
|
|
||||||
|
return RazorDiagnostic.Create(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new NotSupportedException(
|
||||||
|
Resources.FormatRazorDiagnosticJsonConverter_UnsupportedRazorDiagnosticType(typeof(RazorDiagnostic).Name, typeName));
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
var diagnostic = (RazorDiagnostic)value;
|
||||||
|
|
||||||
|
writer.WriteStartObject();
|
||||||
|
WriteProperty(writer, nameof(RazorDiagnostic.Id), diagnostic.Id);
|
||||||
|
WriteProperty(writer, nameof(RazorDiagnostic.Severity), (int)diagnostic.Severity);
|
||||||
|
WriteProperty(writer, RazorDiagnosticMessageKey, diagnostic.GetMessage());
|
||||||
|
WriteProperty(writer, RazorDiagnosticTypeNameKey, diagnostic.GetType().FullName);
|
||||||
|
|
||||||
|
writer.WritePropertyName(nameof(RazorDiagnostic.Span));
|
||||||
|
serializer.Serialize(writer, diagnostic.Span);
|
||||||
|
|
||||||
|
writer.WriteEndObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WriteProperty<T>(JsonWriter writer, string key, T value)
|
||||||
|
{
|
||||||
|
writer.WritePropertyName(key);
|
||||||
|
writer.WriteValue(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -117,6 +117,9 @@
|
||||||
<resheader name="writer">
|
<resheader name="writer">
|
||||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
</resheader>
|
</resheader>
|
||||||
|
<data name="RazorDiagnosticJsonConverter_UnsupportedRazorDiagnosticType" xml:space="preserve">
|
||||||
|
<value>Deserialization of {0} type '{1}' is not supported.</value>
|
||||||
|
</data>
|
||||||
<data name="UnexpectedException" xml:space="preserve">
|
<data name="UnexpectedException" xml:space="preserve">
|
||||||
<value>An unexpected exception occurred when invoking '{0}.{1}' on the Razor language service.</value>
|
<value>An unexpected exception occurred when invoking '{0}.{1}' on the Razor language service.</value>
|
||||||
</data>
|
</data>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,189 @@
|
||||||
|
// 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;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
|
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||||
|
{
|
||||||
|
internal class TagHelperDescriptorJsonConverter : JsonConverter
|
||||||
|
{
|
||||||
|
public static readonly TagHelperDescriptorJsonConverter Instance = new TagHelperDescriptorJsonConverter();
|
||||||
|
|
||||||
|
public override bool CanWrite => false;
|
||||||
|
|
||||||
|
public override bool CanConvert(Type objectType)
|
||||||
|
{
|
||||||
|
return typeof(TagHelperDescriptor).IsAssignableFrom(objectType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
if (reader.TokenType != JsonToken.StartObject)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var descriptor = JObject.Load(reader);
|
||||||
|
var descriptorKind = descriptor[nameof(TagHelperDescriptor.Kind)].Value<string>();
|
||||||
|
var typeName = descriptor[nameof(TagHelperDescriptor.Name)].Value<string>();
|
||||||
|
var assemblyName = descriptor[nameof(TagHelperDescriptor.AssemblyName)].Value<string>();
|
||||||
|
var tagMatchingRules = descriptor[nameof(TagHelperDescriptor.TagMatchingRules)].Value<JArray>();
|
||||||
|
var boundAttributes = descriptor[nameof(TagHelperDescriptor.BoundAttributes)].Value<JArray>();
|
||||||
|
var childTags = descriptor[nameof(TagHelperDescriptor.AllowedChildTags)].Value<JArray>();
|
||||||
|
var documentation = descriptor[nameof(TagHelperDescriptor.Documentation)].Value<string>();
|
||||||
|
var tagOutputHint = descriptor[nameof(TagHelperDescriptor.TagOutputHint)].Value<string>();
|
||||||
|
var diagnostics = descriptor[nameof(TagHelperDescriptor.Diagnostics)].Value<JArray>();
|
||||||
|
var metadata = descriptor[nameof(TagHelperDescriptor.Metadata)].Value<JObject>();
|
||||||
|
|
||||||
|
var builder = ITagHelperDescriptorBuilder.Create(typeName, assemblyName);
|
||||||
|
|
||||||
|
builder
|
||||||
|
.Documentation(documentation)
|
||||||
|
.TagOutputHint(tagOutputHint);
|
||||||
|
|
||||||
|
foreach (var tagMatchingRule in tagMatchingRules)
|
||||||
|
{
|
||||||
|
var rule = tagMatchingRule.Value<JObject>();
|
||||||
|
builder.TagMatchingRule(b => ReadTagMatchingRule(b, rule, serializer));
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var boundAttribute in boundAttributes)
|
||||||
|
{
|
||||||
|
var attribute = boundAttribute.Value<JObject>();
|
||||||
|
builder.BindAttribute(b => ReadBoundAttribute(b, attribute, serializer));
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var childTag in childTags)
|
||||||
|
{
|
||||||
|
var tagValue = childTag.Value<string>();
|
||||||
|
builder.AllowChildTag(tagValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var diagnostic in diagnostics)
|
||||||
|
{
|
||||||
|
var diagnosticReader = diagnostic.CreateReader();
|
||||||
|
var diagnosticObject = serializer.Deserialize<RazorDiagnostic>(diagnosticReader);
|
||||||
|
builder.AddDiagnostic(diagnosticObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
var metadataReader = metadata.CreateReader();
|
||||||
|
var metadataValue = serializer.Deserialize<Dictionary<string, string>>(metadataReader);
|
||||||
|
foreach (var item in metadataValue)
|
||||||
|
{
|
||||||
|
builder.AddMetadata(item.Key, item.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.Build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
// We should never get here because CanWrite returns false.
|
||||||
|
// We want the default serializer to handle TagHelperDescriptor serialization.
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ReadTagMatchingRule(TagMatchingRuleBuilder builder, JObject rule, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
var tagName = rule[nameof(TagMatchingRule.TagName)].Value<string>();
|
||||||
|
var attributes = rule[nameof(TagMatchingRule.Attributes)].Value<JArray>();
|
||||||
|
var parentTag = rule[nameof(TagMatchingRule.ParentTag)].Value<string>();
|
||||||
|
var tagStructure = rule[nameof(TagMatchingRule.TagStructure)].Value<int>();
|
||||||
|
var diagnostics = rule[nameof(TagMatchingRule.Diagnostics)].Value<JArray>();
|
||||||
|
|
||||||
|
builder
|
||||||
|
.RequireTagName(tagName)
|
||||||
|
.RequireParentTag(parentTag)
|
||||||
|
.RequireTagStructure((TagStructure)tagStructure);
|
||||||
|
|
||||||
|
foreach (var attribute in attributes)
|
||||||
|
{
|
||||||
|
var attibuteValue = attribute.Value<JObject>();
|
||||||
|
builder.RequireAttribute(b => ReadRequiredAttribute(b, attibuteValue, serializer));
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var diagnostic in diagnostics)
|
||||||
|
{
|
||||||
|
var diagnosticReader = diagnostic.CreateReader();
|
||||||
|
var diagnosticObject = serializer.Deserialize<RazorDiagnostic>(diagnosticReader);
|
||||||
|
builder.AddDiagnostic(diagnosticObject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ReadRequiredAttribute(RequiredAttributeDescriptorBuilder builder, JObject attribute, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
var name = attribute[nameof(RequiredAttributeDescriptor.Name)].Value<string>();
|
||||||
|
var nameComparison = attribute[nameof(RequiredAttributeDescriptor.NameComparison)].Value<int>();
|
||||||
|
var value = attribute[nameof(RequiredAttributeDescriptor.Value)].Value<string>();
|
||||||
|
var valueComparison = attribute[nameof(RequiredAttributeDescriptor.ValueComparison)].Value<int>();
|
||||||
|
var diagnostics = attribute[nameof(RequiredAttributeDescriptor.Diagnostics)].Value<JArray>();
|
||||||
|
|
||||||
|
builder
|
||||||
|
.Name(name)
|
||||||
|
.NameComparisonMode((RequiredAttributeDescriptor.NameComparisonMode)nameComparison)
|
||||||
|
.Value(value)
|
||||||
|
.ValueComparisonMode((RequiredAttributeDescriptor.ValueComparisonMode)valueComparison);
|
||||||
|
|
||||||
|
foreach (var diagnostic in diagnostics)
|
||||||
|
{
|
||||||
|
var diagnosticReader = diagnostic.CreateReader();
|
||||||
|
var diagnosticObject = serializer.Deserialize<RazorDiagnostic>(diagnosticReader);
|
||||||
|
builder.AddDiagnostic(diagnosticObject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ReadBoundAttribute(ITagHelperBoundAttributeDescriptorBuilder builder, JObject attribute, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
var descriptorKind = attribute[nameof(BoundAttributeDescriptor.Kind)].Value<string>();
|
||||||
|
if (descriptorKind != ITagHelperBoundAttributeDescriptorBuilder.DescriptorKind)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
var name = attribute[nameof(BoundAttributeDescriptor.Name)].Value<string>();
|
||||||
|
var typeName = attribute[nameof(BoundAttributeDescriptor.TypeName)].Value<string>();
|
||||||
|
var isEnum = attribute[nameof(BoundAttributeDescriptor.IsEnum)].Value<bool>();
|
||||||
|
var indexerNamePrefix = attribute[nameof(BoundAttributeDescriptor.IndexerNamePrefix)].Value<string>();
|
||||||
|
var indexerTypeName = attribute[nameof(BoundAttributeDescriptor.IndexerTypeName)].Value<string>();
|
||||||
|
var documentation = attribute[nameof(BoundAttributeDescriptor.Documentation)].Value<string>();
|
||||||
|
var diagnostics = attribute[nameof(BoundAttributeDescriptor.Diagnostics)].Value<JArray>();
|
||||||
|
var metadata = attribute[nameof(BoundAttributeDescriptor.Metadata)].Value<JObject>();
|
||||||
|
|
||||||
|
builder
|
||||||
|
.Name(name)
|
||||||
|
.TypeName(typeName)
|
||||||
|
.Documentation(documentation);
|
||||||
|
|
||||||
|
if (indexerNamePrefix != null)
|
||||||
|
{
|
||||||
|
builder.AsDictionary(indexerNamePrefix, indexerTypeName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isEnum)
|
||||||
|
{
|
||||||
|
builder.AsEnum();
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var diagnostic in diagnostics)
|
||||||
|
{
|
||||||
|
var diagnosticReader = diagnostic.CreateReader();
|
||||||
|
var diagnosticObject = serializer.Deserialize<RazorDiagnostic>(diagnosticReader);
|
||||||
|
builder.AddDiagnostic(diagnosticObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
var metadataReader = metadata.CreateReader();
|
||||||
|
var metadataValue = serializer.Deserialize<Dictionary<string, string>>(metadataReader);
|
||||||
|
foreach (var item in metadataValue)
|
||||||
|
{
|
||||||
|
builder.AddMetadata(item.Key, item.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
var propertyName = metadataValue[ITagHelperBoundAttributeDescriptorBuilder.PropertyNameKey];
|
||||||
|
builder.PropertyName(propertyName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -155,7 +155,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
{
|
{
|
||||||
var phase = engine.Phases[i];
|
var phase = engine.Phases[i];
|
||||||
phase.Execute(codeDocument);
|
phase.Execute(codeDocument);
|
||||||
|
|
||||||
if (phase is IRazorDocumentClassifierPhase)
|
if (phase is IRazorDocumentClassifierPhase)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
|
|
@ -168,4 +168,4 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
return irDocument;
|
return irDocument;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -174,12 +174,10 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
<span val=""@Hello World""></span>");
|
<span val=""@Hello World""></span>");
|
||||||
var tagHelpers = new[]
|
var tagHelpers = new[]
|
||||||
{
|
{
|
||||||
new TagHelperDescriptor
|
CreateTagHelperDescriptor(
|
||||||
{
|
tagName: "span",
|
||||||
TagName = "span",
|
typeName: "SpanTagHelper",
|
||||||
TypeName = "SpanTagHelper",
|
assemblyName: "TestAssembly")
|
||||||
AssemblyName = "TestAssembly",
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|
@ -221,12 +219,10 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
<cool:span val=""@Hello World""></cool:span>");
|
<cool:span val=""@Hello World""></cool:span>");
|
||||||
var tagHelpers = new[]
|
var tagHelpers = new[]
|
||||||
{
|
{
|
||||||
new TagHelperDescriptor
|
CreateTagHelperDescriptor(
|
||||||
{
|
tagName: "span",
|
||||||
TagName = "span",
|
typeName: "SpanTagHelper",
|
||||||
TypeName = "SpanTagHelper",
|
assemblyName: "TestAssembly")
|
||||||
AssemblyName = "TestAssembly",
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|
@ -273,12 +269,10 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
}");
|
}");
|
||||||
var tagHelpers = new[]
|
var tagHelpers = new[]
|
||||||
{
|
{
|
||||||
new TagHelperDescriptor
|
CreateTagHelperDescriptor(
|
||||||
{
|
tagName: "span",
|
||||||
TagName = "span",
|
typeName: "SpanTagHelper",
|
||||||
TypeName = "SpanTagHelper",
|
assemblyName: "TestAssembly")
|
||||||
AssemblyName = "TestAssembly",
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|
@ -299,7 +293,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
n,
|
n,
|
||||||
c1 => DirectiveToken(DirectiveTokenKind.Member, "test", c1),
|
c1 => DirectiveToken(DirectiveTokenKind.Member, "test", c1),
|
||||||
c1 => Html(Environment.NewLine, c1),
|
c1 => Html(Environment.NewLine, c1),
|
||||||
c1 =>
|
c1 =>
|
||||||
{
|
{
|
||||||
var tagHelperNode = Assert.IsType<TagHelperIRNode>(c1);
|
var tagHelperNode = Assert.IsType<TagHelperIRNode>(c1);
|
||||||
Children(
|
Children(
|
||||||
|
|
@ -324,24 +318,23 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
// Arrange
|
// Arrange
|
||||||
var codeDocument = TestRazorCodeDocument.Create(@"@addTagHelper *, TestAssembly
|
var codeDocument = TestRazorCodeDocument.Create(@"@addTagHelper *, TestAssembly
|
||||||
<input bound='foo' />");
|
<input bound='foo' />");
|
||||||
var descriptor = new TagHelperDescriptor
|
var tagHelpers = new[]
|
||||||
{
|
{
|
||||||
TagName = "input",
|
CreateTagHelperDescriptor(
|
||||||
TypeName = "InputTagHelper",
|
tagName: "input",
|
||||||
AssemblyName = "TestAssembly",
|
typeName: "InputTagHelper",
|
||||||
Attributes = new[]
|
assemblyName: "TestAssembly",
|
||||||
{
|
attributes: new Action<ITagHelperBoundAttributeDescriptorBuilder>[]
|
||||||
new TagHelperAttributeDescriptor
|
|
||||||
{
|
{
|
||||||
Name = "bound",
|
builder => builder
|
||||||
PropertyName = "FooProp",
|
.Name("bound")
|
||||||
TypeName = "System.String"
|
.PropertyName("FooProp")
|
||||||
}
|
.TypeName("System.String"),
|
||||||
}
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var irDocument = Lower(codeDocument, tagHelpers: new[] { descriptor });
|
var irDocument = Lower(codeDocument, tagHelpers: tagHelpers);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Children(
|
Children(
|
||||||
|
|
@ -484,5 +477,28 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
|
|
||||||
return irDocument;
|
return irDocument;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static TagHelperDescriptor CreateTagHelperDescriptor(
|
||||||
|
string tagName,
|
||||||
|
string typeName,
|
||||||
|
string assemblyName,
|
||||||
|
IEnumerable<Action<ITagHelperBoundAttributeDescriptorBuilder>> attributes = null)
|
||||||
|
{
|
||||||
|
var builder = ITagHelperDescriptorBuilder.Create(typeName, assemblyName);
|
||||||
|
|
||||||
|
if (attributes != null)
|
||||||
|
{
|
||||||
|
foreach (var attributeBuilder in attributes)
|
||||||
|
{
|
||||||
|
builder.BindAttribute(attributeBuilder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.TagMatchingRule(ruleBuilder => ruleBuilder.RequireTagName(tagName));
|
||||||
|
|
||||||
|
var descriptor = builder.Build();
|
||||||
|
|
||||||
|
return descriptor;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -666,7 +666,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
|
||||||
public void BasicTagHelpers_Prefixed_Runtime()
|
public void BasicTagHelpers_Prefixed_Runtime()
|
||||||
{
|
{
|
||||||
// Arrange, Act & Assert
|
// Arrange, Act & Assert
|
||||||
RunRuntimeTagHelpersTest(TestTagHelperDescriptors.PrefixedPAndInputTagHelperDescriptors);
|
RunRuntimeTagHelpersTest(TestTagHelperDescriptors.DefaultPAndInputTagHelperDescriptors, tagHelperPrefix: "THS");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -690,13 +690,6 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
|
||||||
RunRuntimeTagHelpersTest(TestTagHelperDescriptors.DefaultPAndInputTagHelperDescriptors);
|
RunRuntimeTagHelpersTest(TestTagHelperDescriptors.DefaultPAndInputTagHelperDescriptors);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void DuplicateTargetTagHelper_Runtime()
|
|
||||||
{
|
|
||||||
// Arrange, Act & Assert
|
|
||||||
RunRuntimeTagHelpersTest(TestTagHelperDescriptors.DuplicateTargetTagHelperDescriptors);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void EmptyAttributeTagHelpers_Runtime()
|
public void EmptyAttributeTagHelpers_Runtime()
|
||||||
{
|
{
|
||||||
|
|
@ -1480,7 +1473,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
|
||||||
public void BasicTagHelpers_Prefixed_DesignTime()
|
public void BasicTagHelpers_Prefixed_DesignTime()
|
||||||
{
|
{
|
||||||
// Arrange, Act & Assert
|
// Arrange, Act & Assert
|
||||||
RunDesignTimeTagHelpersTest(TestTagHelperDescriptors.PrefixedPAndInputTagHelperDescriptors);
|
RunDesignTimeTagHelpersTest(TestTagHelperDescriptors.DefaultPAndInputTagHelperDescriptors, tagHelperPrefix: "THS");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -1490,13 +1483,6 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
|
||||||
RunDesignTimeTagHelpersTest(TestTagHelperDescriptors.DefaultPAndInputTagHelperDescriptors);
|
RunDesignTimeTagHelpersTest(TestTagHelperDescriptors.DefaultPAndInputTagHelperDescriptors);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void DuplicateTargetTagHelper_DesignTime()
|
|
||||||
{
|
|
||||||
// Arrange, Act & Assert
|
|
||||||
RunDesignTimeTagHelpersTest(TestTagHelperDescriptors.DuplicateTargetTagHelperDescriptors);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void EmptyAttributeTagHelpers_DesignTime()
|
public void EmptyAttributeTagHelpers_DesignTime()
|
||||||
{
|
{
|
||||||
|
|
@ -1616,7 +1602,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
|
||||||
return codeDocument;
|
return codeDocument;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RunRuntimeTagHelpersTest(IEnumerable<TagHelperDescriptor> descriptors)
|
private void RunRuntimeTagHelpersTest(IEnumerable<TagHelperDescriptor> descriptors, string tagHelperPrefix = null)
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var engine = RazorEngine.Create(
|
var engine = RazorEngine.Create(
|
||||||
|
|
@ -1626,6 +1612,10 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
|
||||||
builder.AddTagHelpers(descriptors);
|
builder.AddTagHelpers(descriptors);
|
||||||
});
|
});
|
||||||
var document = CreateCodeDocument();
|
var document = CreateCodeDocument();
|
||||||
|
if (tagHelperPrefix != null)
|
||||||
|
{
|
||||||
|
document.SetTagHelperPrefix(tagHelperPrefix);
|
||||||
|
}
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
engine.Process(document);
|
engine.Process(document);
|
||||||
|
|
@ -1635,7 +1625,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
|
||||||
AssertCSharpDocumentMatchesBaseline(document.GetCSharpDocument());
|
AssertCSharpDocumentMatchesBaseline(document.GetCSharpDocument());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RunDesignTimeTagHelpersTest(IEnumerable<TagHelperDescriptor> descriptors)
|
private void RunDesignTimeTagHelpersTest(IEnumerable<TagHelperDescriptor> descriptors, string tagHelperPrefix = null)
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var engine = RazorEngine.CreateDesignTime(
|
var engine = RazorEngine.CreateDesignTime(
|
||||||
|
|
@ -1645,6 +1635,10 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
|
||||||
builder.AddTagHelpers(descriptors);
|
builder.AddTagHelpers(descriptors);
|
||||||
});
|
});
|
||||||
var document = CreateCodeDocument();
|
var document = CreateCodeDocument();
|
||||||
|
if (tagHelperPrefix != null)
|
||||||
|
{
|
||||||
|
document.SetTagHelperPrefix(tagHelperPrefix);
|
||||||
|
}
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
engine.Process(document);
|
engine.Process(document);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// 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 Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
|
namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
|
||||||
|
|
@ -13,39 +15,29 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
|
||||||
// Arrange
|
// Arrange
|
||||||
var descriptors = new[]
|
var descriptors = new[]
|
||||||
{
|
{
|
||||||
new TagHelperDescriptor
|
CreateTagHelperDescriptor(
|
||||||
{
|
tagName: "p",
|
||||||
TagName = "p",
|
typeName: "PTagHelper",
|
||||||
TypeName = "PTagHelper",
|
assemblyName: "TestAssembly"),
|
||||||
AssemblyName = "TestAssembly",
|
CreateTagHelperDescriptor(
|
||||||
},
|
tagName: "form",
|
||||||
new TagHelperDescriptor
|
typeName: "FormTagHelper",
|
||||||
{
|
assemblyName: "TestAssembly"),
|
||||||
TagName = "form",
|
CreateTagHelperDescriptor(
|
||||||
TypeName = "FormTagHelper",
|
tagName: "input",
|
||||||
AssemblyName = "TestAssembly",
|
typeName: "InputTagHelper",
|
||||||
},
|
assemblyName: "TestAssembly",
|
||||||
new TagHelperDescriptor
|
attributes: new Action<ITagHelperBoundAttributeDescriptorBuilder>[]
|
||||||
{
|
|
||||||
TagName = "input",
|
|
||||||
TypeName = "InputTagHelper",
|
|
||||||
AssemblyName = "TestAssembly",
|
|
||||||
Attributes = new[]
|
|
||||||
{
|
{
|
||||||
new TagHelperAttributeDescriptor
|
builder => builder
|
||||||
{
|
.Name("value")
|
||||||
Name = "value",
|
.PropertyName("FooProp")
|
||||||
PropertyName = "FooProp",
|
.TypeName("System.String"), // Gets preallocated
|
||||||
TypeName = "System.String" // Gets preallocated
|
builder => builder
|
||||||
},
|
.Name("date")
|
||||||
new TagHelperAttributeDescriptor
|
.PropertyName("BarProp")
|
||||||
{
|
.TypeName("System.DateTime"), // Doesn't get preallocated
|
||||||
Name = "date",
|
})
|
||||||
PropertyName = "BarProp",
|
|
||||||
TypeName = "System.DateTime" // Doesn't get preallocated
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var engine = RazorEngine.Create(b =>
|
var engine = RazorEngine.Create(b =>
|
||||||
|
|
@ -53,7 +45,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
|
||||||
b.AddTagHelpers(descriptors);
|
b.AddTagHelpers(descriptors);
|
||||||
b.Features.Add(new DefaultInstrumentationPass());
|
b.Features.Add(new DefaultInstrumentationPass());
|
||||||
});
|
});
|
||||||
|
|
||||||
var document = CreateCodeDocument();
|
var document = CreateCodeDocument();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
|
|
@ -66,5 +58,28 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
|
||||||
AssertCSharpDocumentMatchesBaseline(csharpDocument);
|
AssertCSharpDocumentMatchesBaseline(csharpDocument);
|
||||||
Assert.Empty(csharpDocument.Diagnostics);
|
Assert.Empty(csharpDocument.Diagnostics);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static TagHelperDescriptor CreateTagHelperDescriptor(
|
||||||
|
string tagName,
|
||||||
|
string typeName,
|
||||||
|
string assemblyName,
|
||||||
|
IEnumerable<Action<ITagHelperBoundAttributeDescriptorBuilder>> attributes = null)
|
||||||
|
{
|
||||||
|
var builder = ITagHelperDescriptorBuilder.Create(typeName, assemblyName);
|
||||||
|
|
||||||
|
if (attributes != null)
|
||||||
|
{
|
||||||
|
foreach (var attributeBuilder in attributes)
|
||||||
|
{
|
||||||
|
builder.BindAttribute(attributeBuilder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.TagMatchingRule(ruleBuilder => ruleBuilder.RequireTagName(tagName));
|
||||||
|
|
||||||
|
var descriptor = builder.Build();
|
||||||
|
|
||||||
|
return descriptor;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// 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 Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
|
namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
|
||||||
|
|
@ -13,12 +15,10 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
|
||||||
// Arrange
|
// Arrange
|
||||||
var descriptors = new[]
|
var descriptors = new[]
|
||||||
{
|
{
|
||||||
new TagHelperDescriptor
|
CreateTagHelperDescriptor(
|
||||||
{
|
tagName: "input",
|
||||||
TagName = "input",
|
typeName: "InputTagHelper",
|
||||||
TypeName = "InputTagHelper",
|
assemblyName: "TestAssembly")
|
||||||
AssemblyName = "TestAssembly",
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var engine = RazorEngine.Create(builder => builder.AddTagHelpers(descriptors));
|
var engine = RazorEngine.Create(builder => builder.AddTagHelpers(descriptors));
|
||||||
|
|
@ -37,18 +37,17 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
|
||||||
// Arrange
|
// Arrange
|
||||||
var descriptors = new[]
|
var descriptors = new[]
|
||||||
{
|
{
|
||||||
new TagHelperDescriptor
|
CreateTagHelperDescriptor(
|
||||||
{
|
tagName: "input",
|
||||||
TagName = "input",
|
typeName: "InputTagHelper",
|
||||||
TypeName = "InputTagHelper",
|
assemblyName: "TestAssembly",
|
||||||
AssemblyName = "TestAssembly",
|
attributes: new Action<ITagHelperBoundAttributeDescriptorBuilder>[]
|
||||||
Attributes = new[] { new TagHelperAttributeDescriptor
|
|
||||||
{
|
{
|
||||||
Name = "bound",
|
builder => builder
|
||||||
PropertyName = "FooProp",
|
.Name("bound")
|
||||||
TypeName = "System.String"
|
.PropertyName("FooProp")
|
||||||
} }
|
.TypeName("System.String"),
|
||||||
}
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
var engine = RazorEngine.Create(builder => builder.AddTagHelpers(descriptors));
|
var engine = RazorEngine.Create(builder => builder.AddTagHelpers(descriptors));
|
||||||
|
|
@ -67,30 +66,25 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
|
||||||
// Arrange
|
// Arrange
|
||||||
var descriptors = new[]
|
var descriptors = new[]
|
||||||
{
|
{
|
||||||
new TagHelperDescriptor
|
CreateTagHelperDescriptor(
|
||||||
{
|
tagName: "p",
|
||||||
TagName = "p",
|
typeName: "PTagHelper",
|
||||||
TypeName = "PTagHelper",
|
assemblyName: "TestAssembly"),
|
||||||
AssemblyName = "TestAssembly",
|
CreateTagHelperDescriptor(
|
||||||
},
|
tagName: "form",
|
||||||
new TagHelperDescriptor
|
typeName: "FormTagHelper",
|
||||||
{
|
assemblyName: "TestAssembly"),
|
||||||
TagName = "form",
|
CreateTagHelperDescriptor(
|
||||||
TypeName = "FormTagHelper",
|
tagName: "input",
|
||||||
AssemblyName = "TestAssembly",
|
typeName: "InputTagHelper",
|
||||||
},
|
assemblyName: "TestAssembly",
|
||||||
new TagHelperDescriptor
|
attributes: new Action<ITagHelperBoundAttributeDescriptorBuilder>[]
|
||||||
{
|
|
||||||
TagName = "input",
|
|
||||||
TypeName = "InputTagHelper",
|
|
||||||
AssemblyName = "TestAssembly",
|
|
||||||
Attributes = new[] { new TagHelperAttributeDescriptor
|
|
||||||
{
|
{
|
||||||
Name = "value",
|
builder => builder
|
||||||
PropertyName = "FooProp",
|
.Name("value")
|
||||||
TypeName = "System.String"
|
.PropertyName("FooProp")
|
||||||
} }
|
.TypeName("System.String"),
|
||||||
}
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
var engine = RazorEngine.Create(builder => builder.AddTagHelpers(descriptors));
|
var engine = RazorEngine.Create(builder => builder.AddTagHelpers(descriptors));
|
||||||
|
|
@ -102,5 +96,28 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
|
||||||
// Assert
|
// Assert
|
||||||
AssertIRMatchesBaseline(document.GetIRDocument());
|
AssertIRMatchesBaseline(document.GetIRDocument());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static TagHelperDescriptor CreateTagHelperDescriptor(
|
||||||
|
string tagName,
|
||||||
|
string typeName,
|
||||||
|
string assemblyName,
|
||||||
|
IEnumerable<Action<ITagHelperBoundAttributeDescriptorBuilder>> attributes = null)
|
||||||
|
{
|
||||||
|
var builder = ITagHelperDescriptorBuilder.Create(typeName, assemblyName);
|
||||||
|
|
||||||
|
if (attributes != null)
|
||||||
|
{
|
||||||
|
foreach (var attributeBuilder in attributes)
|
||||||
|
{
|
||||||
|
builder.BindAttribute(attributeBuilder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.TagMatchingRule(ruleBuilder => ruleBuilder.RequireTagName(tagName));
|
||||||
|
|
||||||
|
var descriptor = builder.Build();
|
||||||
|
|
||||||
|
return descriptor;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,50 +9,35 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
|
||||||
{
|
{
|
||||||
public class TestTagHelperDescriptors
|
public class TestTagHelperDescriptors
|
||||||
{
|
{
|
||||||
internal static IEnumerable<TagHelperDescriptor> DefaultPAndInputTagHelperDescriptors { get; }
|
|
||||||
= BuildPAndInputTagHelperDescriptors(prefix: string.Empty);
|
|
||||||
internal static IEnumerable<TagHelperDescriptor> PrefixedPAndInputTagHelperDescriptors { get; }
|
|
||||||
= BuildPAndInputTagHelperDescriptors(prefix: "THS");
|
|
||||||
|
|
||||||
internal static IEnumerable<TagHelperDescriptor> SimpleTagHelperDescriptors
|
internal static IEnumerable<TagHelperDescriptor> SimpleTagHelperDescriptors
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return new[]
|
return new[]
|
||||||
{
|
{
|
||||||
new TagHelperDescriptor
|
CreateTagHelperDescriptor(
|
||||||
{
|
tagName: "span",
|
||||||
TagName = "span",
|
typeName: "SpanTagHelper",
|
||||||
TypeName = "SpanTagHelper",
|
assemblyName: "TestAssembly"),
|
||||||
AssemblyName = "TestAssembly",
|
CreateTagHelperDescriptor(
|
||||||
},
|
tagName: "div",
|
||||||
new TagHelperDescriptor
|
typeName: "DivTagHelper",
|
||||||
{
|
assemblyName: "TestAssembly"),
|
||||||
TagName = "div",
|
CreateTagHelperDescriptor(
|
||||||
TypeName = "DivTagHelper",
|
tagName: "input",
|
||||||
AssemblyName = "TestAssembly",
|
typeName: "InputTagHelper",
|
||||||
},
|
assemblyName: "TestAssembly",
|
||||||
new TagHelperDescriptor
|
attributes: new Action<ITagHelperBoundAttributeDescriptorBuilder>[]
|
||||||
{
|
|
||||||
TagName = "input",
|
|
||||||
TypeName = "InputTagHelper",
|
|
||||||
AssemblyName = "TestAssembly",
|
|
||||||
Attributes = new[]
|
|
||||||
{
|
{
|
||||||
new TagHelperAttributeDescriptor
|
builder => builder
|
||||||
{
|
.Name("value")
|
||||||
Name = "value",
|
.PropertyName("FooProp")
|
||||||
PropertyName = "FooProp",
|
.TypeName("System.String"),
|
||||||
TypeName = "System.String"
|
builder => builder
|
||||||
},
|
.Name("bound")
|
||||||
new TagHelperAttributeDescriptor
|
.PropertyName("BoundProp")
|
||||||
{
|
.TypeName("System.String"),
|
||||||
Name = "bound",
|
})
|
||||||
PropertyName = "BoundProp",
|
|
||||||
TypeName = "System.String"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -66,113 +51,93 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
|
||||||
|
|
||||||
return new[]
|
return new[]
|
||||||
{
|
{
|
||||||
new TagHelperDescriptor
|
CreateTagHelperDescriptor(
|
||||||
{
|
tagName: "a",
|
||||||
TagName = "a",
|
typeName: "TestNamespace.ATagHelper",
|
||||||
TypeName = "TestNamespace.ATagHelper",
|
assemblyName: "TestAssembly",
|
||||||
AssemblyName = "TestAssembly",
|
ruleBuilders: new Action<TagMatchingRuleBuilder>[]
|
||||||
RequiredAttributes = new[]
|
|
||||||
{
|
{
|
||||||
new TagHelperRequiredAttributeDescriptor
|
builder => builder
|
||||||
{
|
.RequireAttribute(attribute => attribute
|
||||||
Name = "href",
|
.Name("href")
|
||||||
NameComparison = TagHelperRequiredAttributeNameComparison.FullMatch,
|
.NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.FullMatch)
|
||||||
Value = "~/",
|
.Value("~/")
|
||||||
ValueComparison = TagHelperRequiredAttributeValueComparison.FullMatch,
|
.ValueComparisonMode(RequiredAttributeDescriptor.ValueComparisonMode.FullMatch)),
|
||||||
}
|
}),
|
||||||
},
|
CreateTagHelperDescriptor(
|
||||||
},
|
tagName: "a",
|
||||||
new TagHelperDescriptor
|
typeName: "TestNamespace.ATagHelperMultipleSelectors",
|
||||||
{
|
assemblyName: "TestAssembly",
|
||||||
TagName = "a",
|
ruleBuilders: new Action<TagMatchingRuleBuilder>[]
|
||||||
TypeName = "TestNamespace.ATagHelperMultipleSelectors",
|
|
||||||
AssemblyName = "TestAssembly",
|
|
||||||
RequiredAttributes = new[]
|
|
||||||
{
|
{
|
||||||
new TagHelperRequiredAttributeDescriptor
|
builder => builder
|
||||||
{
|
.RequireAttribute(attribute => attribute
|
||||||
Name = "href",
|
.Name("href")
|
||||||
NameComparison = TagHelperRequiredAttributeNameComparison.FullMatch,
|
.NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.FullMatch)
|
||||||
Value = "~/",
|
.Value("~/")
|
||||||
ValueComparison = TagHelperRequiredAttributeValueComparison.PrefixMatch,
|
.ValueComparisonMode(RequiredAttributeDescriptor.ValueComparisonMode.PrefixMatch))
|
||||||
},
|
.RequireAttribute(attribute => attribute
|
||||||
new TagHelperRequiredAttributeDescriptor
|
.Name("href")
|
||||||
{
|
.NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.FullMatch)
|
||||||
Name = "href",
|
.Value("?hello=world")
|
||||||
NameComparison = TagHelperRequiredAttributeNameComparison.FullMatch,
|
.ValueComparisonMode(RequiredAttributeDescriptor.ValueComparisonMode.SuffixMatch)),
|
||||||
Value = "?hello=world",
|
}),
|
||||||
ValueComparison = TagHelperRequiredAttributeValueComparison.SuffixMatch,
|
CreateTagHelperDescriptor(
|
||||||
}
|
tagName: "input",
|
||||||
},
|
typeName: "TestNamespace.InputTagHelper",
|
||||||
},
|
assemblyName: "TestAssembly",
|
||||||
new TagHelperDescriptor
|
attributes: new Action<ITagHelperBoundAttributeDescriptorBuilder>[]
|
||||||
{
|
|
||||||
TagName = "input",
|
|
||||||
TypeName = "TestNamespace.InputTagHelper",
|
|
||||||
AssemblyName = "TestAssembly",
|
|
||||||
Attributes = new TagHelperAttributeDescriptor[]
|
|
||||||
{
|
{
|
||||||
new TagHelperAttributeDescriptor("type", inputTypePropertyInfo),
|
builder => BuildBoundAttributeDescriptorFromPropertyInfo(builder, "type", inputTypePropertyInfo),
|
||||||
},
|
},
|
||||||
RequiredAttributes = new[]
|
ruleBuilders: new Action<TagMatchingRuleBuilder>[]
|
||||||
{
|
{
|
||||||
new TagHelperRequiredAttributeDescriptor
|
builder => builder
|
||||||
{
|
.RequireAttribute(attribute => attribute
|
||||||
Name = "type",
|
.Name("type")
|
||||||
NameComparison = TagHelperRequiredAttributeNameComparison.FullMatch,
|
.NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.FullMatch)
|
||||||
Value = "text",
|
.Value("text")
|
||||||
ValueComparison = TagHelperRequiredAttributeValueComparison.FullMatch,
|
.ValueComparisonMode(RequiredAttributeDescriptor.ValueComparisonMode.FullMatch)),
|
||||||
}
|
}),
|
||||||
},
|
CreateTagHelperDescriptor(
|
||||||
},
|
tagName: "input",
|
||||||
new TagHelperDescriptor
|
typeName: "TestNamespace.InputTagHelper2",
|
||||||
{
|
assemblyName: "TestAssembly",
|
||||||
TagName = "input",
|
attributes: new Action<ITagHelperBoundAttributeDescriptorBuilder>[]
|
||||||
TypeName = "TestNamespace.InputTagHelper2",
|
|
||||||
AssemblyName = "TestAssembly",
|
|
||||||
Attributes = new TagHelperAttributeDescriptor[]
|
|
||||||
{
|
{
|
||||||
new TagHelperAttributeDescriptor("type", inputTypePropertyInfo),
|
builder => BuildBoundAttributeDescriptorFromPropertyInfo(builder, "type", inputTypePropertyInfo),
|
||||||
},
|
},
|
||||||
RequiredAttributes = new[]
|
ruleBuilders: new Action<TagMatchingRuleBuilder>[]
|
||||||
{
|
{
|
||||||
new TagHelperRequiredAttributeDescriptor
|
builder => builder
|
||||||
{
|
.RequireAttribute(attribute => attribute
|
||||||
Name = "ty",
|
.Name("ty")
|
||||||
NameComparison = TagHelperRequiredAttributeNameComparison.PrefixMatch,
|
.NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.PrefixMatch)),
|
||||||
}
|
}),
|
||||||
},
|
CreateTagHelperDescriptor(
|
||||||
},
|
tagName: "*",
|
||||||
new TagHelperDescriptor
|
typeName: "TestNamespace.CatchAllTagHelper",
|
||||||
{
|
assemblyName: "TestAssembly",
|
||||||
TagName = "*",
|
ruleBuilders: new Action<TagMatchingRuleBuilder>[]
|
||||||
TypeName = "TestNamespace.CatchAllTagHelper",
|
|
||||||
AssemblyName = "TestAssembly",
|
|
||||||
RequiredAttributes = new[]
|
|
||||||
{
|
{
|
||||||
new TagHelperRequiredAttributeDescriptor
|
builder => builder
|
||||||
{
|
.RequireAttribute(attribute => attribute
|
||||||
Name = "href",
|
.Name("href")
|
||||||
NameComparison = TagHelperRequiredAttributeNameComparison.FullMatch,
|
.NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.FullMatch)
|
||||||
Value = "~/",
|
.Value("~/")
|
||||||
ValueComparison = TagHelperRequiredAttributeValueComparison.PrefixMatch,
|
.ValueComparisonMode(RequiredAttributeDescriptor.ValueComparisonMode.PrefixMatch)),
|
||||||
}
|
}),
|
||||||
},
|
CreateTagHelperDescriptor(
|
||||||
},
|
tagName: "*",
|
||||||
new TagHelperDescriptor
|
typeName: "TestNamespace.CatchAllTagHelper2",
|
||||||
{
|
assemblyName: "TestAssembly",
|
||||||
TagName = "*",
|
ruleBuilders: new Action<TagMatchingRuleBuilder>[]
|
||||||
TypeName = "TestNamespace.CatchAllTagHelper2",
|
|
||||||
AssemblyName = "TestAssembly",
|
|
||||||
RequiredAttributes = new[]
|
|
||||||
{
|
{
|
||||||
new TagHelperRequiredAttributeDescriptor
|
builder => builder
|
||||||
{
|
.RequireAttribute(attribute => attribute
|
||||||
Name = "type",
|
.Name("type")
|
||||||
NameComparison = TagHelperRequiredAttributeNameComparison.FullMatch,
|
.NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.FullMatch)),
|
||||||
}
|
}),
|
||||||
},
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -183,38 +148,30 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
|
||||||
{
|
{
|
||||||
return new[]
|
return new[]
|
||||||
{
|
{
|
||||||
new TagHelperDescriptor
|
CreateTagHelperDescriptor(
|
||||||
{
|
tagName: "*",
|
||||||
TagName = "*",
|
typeName: "TestNamespace.CatchAllTagHelper",
|
||||||
TypeName = "TestNamespace.CatchAllTagHelper",
|
assemblyName: "TestAssembly",
|
||||||
AssemblyName = "TestAssembly",
|
attributes: new Action<ITagHelperBoundAttributeDescriptorBuilder>[]
|
||||||
Attributes = new[]
|
|
||||||
{
|
{
|
||||||
new TagHelperAttributeDescriptor
|
builder => builder
|
||||||
{
|
.Name("catch-all")
|
||||||
Name = "catch-all",
|
.PropertyName("CatchAll")
|
||||||
PropertyName = "CatchAll",
|
.AsEnum()
|
||||||
IsEnum = true,
|
.TypeName(typeof(MyEnum).FullName),
|
||||||
TypeName = typeof(MyEnum).FullName
|
}),
|
||||||
},
|
CreateTagHelperDescriptor(
|
||||||
}
|
tagName: "input",
|
||||||
},
|
typeName: "TestNamespace.InputTagHelper",
|
||||||
new TagHelperDescriptor
|
assemblyName: "TestAssembly",
|
||||||
{
|
attributes: new Action<ITagHelperBoundAttributeDescriptorBuilder>[]
|
||||||
TagName = "input",
|
|
||||||
TypeName = "TestNamespace.InputTagHelper",
|
|
||||||
AssemblyName = "TestAssembly",
|
|
||||||
Attributes = new[]
|
|
||||||
{
|
{
|
||||||
new TagHelperAttributeDescriptor
|
builder => builder
|
||||||
{
|
.Name("value")
|
||||||
Name = "value",
|
.PropertyName("Value")
|
||||||
PropertyName = "Value",
|
.AsEnum()
|
||||||
IsEnum = true,
|
.TypeName(typeof(MyEnum).FullName),
|
||||||
TypeName = typeof(MyEnum).FullName
|
}),
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -225,52 +182,41 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
|
||||||
{
|
{
|
||||||
return new[]
|
return new[]
|
||||||
{
|
{
|
||||||
new TagHelperDescriptor
|
CreateTagHelperDescriptor(
|
||||||
{
|
tagName: "*",
|
||||||
TagName = "*",
|
typeName: "TestNamespace.CatchAllTagHelper",
|
||||||
TypeName = "TestNamespace.CatchAllTagHelper",
|
assemblyName: "TestAssembly",
|
||||||
AssemblyName = "TestAssembly",
|
attributes: new Action<ITagHelperBoundAttributeDescriptorBuilder>[]
|
||||||
Attributes = new[]
|
|
||||||
{
|
{
|
||||||
new TagHelperAttributeDescriptor
|
builder => builder
|
||||||
{
|
.Name("[item]")
|
||||||
Name = "[item]",
|
.PropertyName("ListItems")
|
||||||
PropertyName = "ListItems",
|
.TypeName(typeof(List<string>).FullName),
|
||||||
TypeName = typeof(List<string>).FullName
|
builder => builder
|
||||||
},
|
.Name("[(item)]")
|
||||||
new TagHelperAttributeDescriptor
|
.PropertyName("ArrayItems")
|
||||||
{
|
.TypeName(typeof(string[]).FullName),
|
||||||
Name = "[(item)]",
|
builder => builder
|
||||||
PropertyName = "ArrayItems",
|
.Name("(click)")
|
||||||
TypeName = typeof(string[]).FullName
|
.PropertyName("Event1")
|
||||||
},
|
.TypeName(typeof(Action).FullName),
|
||||||
new TagHelperAttributeDescriptor
|
builder => builder
|
||||||
{
|
.Name("(^click)")
|
||||||
Name = "(click)",
|
.PropertyName("Event2")
|
||||||
PropertyName = "Event1",
|
.TypeName(typeof(Action).FullName),
|
||||||
TypeName = typeof(Action).FullName
|
builder => builder
|
||||||
},
|
.Name("*something")
|
||||||
new TagHelperAttributeDescriptor
|
.PropertyName("StringProperty1")
|
||||||
{
|
.TypeName(typeof(string).FullName),
|
||||||
Name = "(^click)",
|
builder => builder
|
||||||
PropertyName = "Event2",
|
.Name("#local")
|
||||||
TypeName = typeof(Action).FullName
|
.PropertyName("StringProperty2")
|
||||||
},
|
.TypeName(typeof(string).FullName),
|
||||||
new TagHelperAttributeDescriptor
|
|
||||||
{
|
|
||||||
Name = "*something",
|
|
||||||
PropertyName = "StringProperty1",
|
|
||||||
TypeName = typeof(string).FullName
|
|
||||||
},
|
|
||||||
new TagHelperAttributeDescriptor
|
|
||||||
{
|
|
||||||
Name = "#local",
|
|
||||||
PropertyName = "StringProperty2",
|
|
||||||
TypeName = typeof(string).FullName
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
RequiredAttributes = new[] { new TagHelperRequiredAttributeDescriptor { Name = "bound" } },
|
ruleBuilders: new Action<TagMatchingRuleBuilder>[]
|
||||||
},
|
{
|
||||||
|
builder => builder.RequireAttribute(attribute => attribute.Name("bound")),
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -281,54 +227,42 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
|
||||||
{
|
{
|
||||||
return new[]
|
return new[]
|
||||||
{
|
{
|
||||||
new TagHelperDescriptor
|
CreateTagHelperDescriptor(
|
||||||
{
|
tagName: "*",
|
||||||
TagName = "*",
|
typeName: "TestNamespace.CatchAllTagHelper",
|
||||||
TypeName = "TestNamespace.CatchAllTagHelper",
|
assemblyName: "TestAssembly",
|
||||||
AssemblyName = "TestAssembly",
|
attributes: new Action<ITagHelperBoundAttributeDescriptorBuilder>[]
|
||||||
Attributes = new[]
|
|
||||||
{
|
{
|
||||||
new TagHelperAttributeDescriptor
|
builder => builder
|
||||||
{
|
.Name("catchall-bound-string")
|
||||||
Name = "catchall-bound-string",
|
.PropertyName("BoundRequiredString")
|
||||||
PropertyName = "BoundRequiredString",
|
.TypeName(typeof(string).FullName),
|
||||||
TypeName = typeof(string).FullName,
|
|
||||||
IsStringProperty = true
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
RequiredAttributes = new[]
|
ruleBuilders: new Action<TagMatchingRuleBuilder>[]
|
||||||
{
|
{
|
||||||
new TagHelperRequiredAttributeDescriptor { Name = "catchall-unbound-required" }
|
builder => builder.RequireAttribute(attribute => attribute.Name("catchall-unbound-required")),
|
||||||
},
|
}),
|
||||||
},
|
CreateTagHelperDescriptor(
|
||||||
new TagHelperDescriptor
|
tagName: "input",
|
||||||
{
|
typeName: "TestNamespace.InputTagHelper",
|
||||||
TagName = "input",
|
assemblyName: "TestAssembly",
|
||||||
TypeName = "TestNamespace.InputTagHelper",
|
attributes: new Action<ITagHelperBoundAttributeDescriptorBuilder>[]
|
||||||
AssemblyName = "TestAssembly",
|
|
||||||
Attributes = new[]
|
|
||||||
{
|
{
|
||||||
new TagHelperAttributeDescriptor
|
builder => builder
|
||||||
{
|
.Name("input-bound-required-string")
|
||||||
Name = "input-bound-required-string",
|
.PropertyName("BoundRequiredString")
|
||||||
PropertyName = "BoundRequiredString",
|
.TypeName(typeof(string).FullName),
|
||||||
TypeName = typeof(string).FullName,
|
builder => builder
|
||||||
IsStringProperty = true
|
.Name("input-bound-string")
|
||||||
},
|
.PropertyName("BoundString")
|
||||||
new TagHelperAttributeDescriptor
|
.TypeName(typeof(string).FullName),
|
||||||
{
|
|
||||||
Name = "input-bound-string",
|
|
||||||
PropertyName = "BoundString",
|
|
||||||
TypeName = typeof(string).FullName,
|
|
||||||
IsStringProperty = true
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
RequiredAttributes = new[]
|
ruleBuilders: new Action<TagMatchingRuleBuilder>[]
|
||||||
{
|
{
|
||||||
new TagHelperRequiredAttributeDescriptor { Name = "input-bound-required-string" },
|
builder => builder
|
||||||
new TagHelperRequiredAttributeDescriptor { Name = "input-unbound-required" }
|
.RequireAttribute(attribute => attribute.Name("input-bound-required-string"))
|
||||||
},
|
.RequireAttribute(attribute => attribute.Name("input-unbound-required")),
|
||||||
}
|
}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -339,82 +273,17 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
|
||||||
{
|
{
|
||||||
return new[]
|
return new[]
|
||||||
{
|
{
|
||||||
new TagHelperDescriptor
|
CreateTagHelperDescriptor(
|
||||||
{
|
tagName: "input",
|
||||||
TagName = "input",
|
typeName: "TestNamespace.InputTagHelper",
|
||||||
TypeName = "TestNamespace.InputTagHelper",
|
assemblyName: "TestAssembly",
|
||||||
AssemblyName = "TestAssembly",
|
attributes: new Action<ITagHelperBoundAttributeDescriptorBuilder>[]
|
||||||
Attributes = new[]
|
|
||||||
{
|
{
|
||||||
new TagHelperAttributeDescriptor
|
builder => builder
|
||||||
{
|
.Name("bound")
|
||||||
Name = "bound",
|
.PropertyName("Bound")
|
||||||
PropertyName = "Bound",
|
.TypeName(typeof(string).FullName)
|
||||||
TypeName = typeof(string).FullName,
|
}),
|
||||||
IsStringProperty = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static IEnumerable<TagHelperDescriptor> DuplicateTargetTagHelperDescriptors
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
var inputTypePropertyInfo = typeof(TestType).GetProperty("Type");
|
|
||||||
var inputCheckedPropertyInfo = typeof(TestType).GetProperty("Checked");
|
|
||||||
return new[]
|
|
||||||
{
|
|
||||||
new TagHelperDescriptor
|
|
||||||
{
|
|
||||||
TagName = "*",
|
|
||||||
TypeName = "TestNamespace.CatchAllTagHelper",
|
|
||||||
AssemblyName = "TestAssembly",
|
|
||||||
Attributes = new TagHelperAttributeDescriptor[]
|
|
||||||
{
|
|
||||||
new TagHelperAttributeDescriptor("type", inputTypePropertyInfo),
|
|
||||||
new TagHelperAttributeDescriptor("checked", inputCheckedPropertyInfo)
|
|
||||||
},
|
|
||||||
RequiredAttributes = new[] { new TagHelperRequiredAttributeDescriptor { Name = "type" } },
|
|
||||||
},
|
|
||||||
new TagHelperDescriptor
|
|
||||||
{
|
|
||||||
TagName = "*",
|
|
||||||
TypeName = "TestNamespace.CatchAllTagHelper",
|
|
||||||
AssemblyName = "TestAssembly",
|
|
||||||
Attributes = new TagHelperAttributeDescriptor[]
|
|
||||||
{
|
|
||||||
new TagHelperAttributeDescriptor("type", inputTypePropertyInfo),
|
|
||||||
new TagHelperAttributeDescriptor("checked", inputCheckedPropertyInfo)
|
|
||||||
},
|
|
||||||
RequiredAttributes = new[] { new TagHelperRequiredAttributeDescriptor { Name = "checked" } },
|
|
||||||
},
|
|
||||||
new TagHelperDescriptor
|
|
||||||
{
|
|
||||||
TagName = "input",
|
|
||||||
TypeName = "TestNamespace.InputTagHelper",
|
|
||||||
AssemblyName = "TestAssembly",
|
|
||||||
Attributes = new TagHelperAttributeDescriptor[]
|
|
||||||
{
|
|
||||||
new TagHelperAttributeDescriptor("type", inputTypePropertyInfo),
|
|
||||||
new TagHelperAttributeDescriptor("checked", inputCheckedPropertyInfo)
|
|
||||||
},
|
|
||||||
RequiredAttributes = new[] { new TagHelperRequiredAttributeDescriptor { Name = "type" } },
|
|
||||||
},
|
|
||||||
new TagHelperDescriptor
|
|
||||||
{
|
|
||||||
TagName = "input",
|
|
||||||
TypeName = "TestNamespace.InputTagHelper",
|
|
||||||
AssemblyName = "TestAssembly",
|
|
||||||
Attributes = new TagHelperAttributeDescriptor[]
|
|
||||||
{
|
|
||||||
new TagHelperAttributeDescriptor("type", inputTypePropertyInfo),
|
|
||||||
new TagHelperAttributeDescriptor("checked", inputCheckedPropertyInfo)
|
|
||||||
},
|
|
||||||
RequiredAttributes = new[] { new TagHelperRequiredAttributeDescriptor { Name = "checked" } },
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -427,47 +296,49 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
|
||||||
var inputCheckedPropertyInfo = typeof(TestType).GetProperty("Checked");
|
var inputCheckedPropertyInfo = typeof(TestType).GetProperty("Checked");
|
||||||
return new[]
|
return new[]
|
||||||
{
|
{
|
||||||
new TagHelperDescriptor
|
CreateTagHelperDescriptor(
|
||||||
{
|
tagName: "p",
|
||||||
TagName = "p",
|
typeName: "TestNamespace.PTagHelper",
|
||||||
TypeName = "TestNamespace.PTagHelper",
|
assemblyName: "TestAssembly",
|
||||||
AssemblyName = "TestAssembly",
|
ruleBuilders: new Action<TagMatchingRuleBuilder>[]
|
||||||
RequiredAttributes = new[] { new TagHelperRequiredAttributeDescriptor { Name = "class" } },
|
|
||||||
},
|
|
||||||
new TagHelperDescriptor
|
|
||||||
{
|
|
||||||
TagName = "input",
|
|
||||||
TypeName = "TestNamespace.InputTagHelper",
|
|
||||||
AssemblyName = "TestAssembly",
|
|
||||||
Attributes = new TagHelperAttributeDescriptor[]
|
|
||||||
{
|
{
|
||||||
new TagHelperAttributeDescriptor("type", inputTypePropertyInfo)
|
builder => builder.RequireAttribute(attribute => attribute.Name("class")),
|
||||||
},
|
}),
|
||||||
RequiredAttributes = new[] { new TagHelperRequiredAttributeDescriptor { Name = "type" } },
|
CreateTagHelperDescriptor(
|
||||||
},
|
tagName: "input",
|
||||||
new TagHelperDescriptor
|
typeName: "TestNamespace.InputTagHelper",
|
||||||
{
|
assemblyName: "TestAssembly",
|
||||||
TagName = "input",
|
attributes: new Action<ITagHelperBoundAttributeDescriptorBuilder>[]
|
||||||
TypeName = "TestNamespace.InputTagHelper2",
|
|
||||||
AssemblyName = "TestAssembly",
|
|
||||||
Attributes = new TagHelperAttributeDescriptor[]
|
|
||||||
{
|
{
|
||||||
new TagHelperAttributeDescriptor("type", inputTypePropertyInfo),
|
builder => BuildBoundAttributeDescriptorFromPropertyInfo(builder, "type", inputTypePropertyInfo),
|
||||||
new TagHelperAttributeDescriptor("checked", inputCheckedPropertyInfo)
|
|
||||||
},
|
},
|
||||||
RequiredAttributes = new[]
|
ruleBuilders: new Action<TagMatchingRuleBuilder>[]
|
||||||
{
|
{
|
||||||
new TagHelperRequiredAttributeDescriptor { Name = "type" },
|
builder => builder.RequireAttribute(attribute => attribute.Name("type")),
|
||||||
new TagHelperRequiredAttributeDescriptor { Name = "checked" }
|
}),
|
||||||
|
CreateTagHelperDescriptor(
|
||||||
|
tagName: "input",
|
||||||
|
typeName: "TestNamespace.InputTagHelper2",
|
||||||
|
assemblyName: "TestAssembly",
|
||||||
|
attributes: new Action<ITagHelperBoundAttributeDescriptorBuilder>[]
|
||||||
|
{
|
||||||
|
builder => BuildBoundAttributeDescriptorFromPropertyInfo(builder, "type", inputTypePropertyInfo),
|
||||||
|
builder => BuildBoundAttributeDescriptorFromPropertyInfo(builder, "checked", inputCheckedPropertyInfo),
|
||||||
},
|
},
|
||||||
},
|
ruleBuilders: new Action<TagMatchingRuleBuilder>[]
|
||||||
new TagHelperDescriptor
|
{
|
||||||
{
|
builder => builder
|
||||||
TagName = "*",
|
.RequireAttribute(attribute => attribute.Name("type"))
|
||||||
TypeName = "TestNamespace.CatchAllTagHelper",
|
.RequireAttribute(attribute => attribute.Name("checked")),
|
||||||
AssemblyName = "TestAssembly",
|
}),
|
||||||
RequiredAttributes = new[] { new TagHelperRequiredAttributeDescriptor { Name = "catchAll" } },
|
CreateTagHelperDescriptor(
|
||||||
}
|
tagName: "*",
|
||||||
|
typeName: "TestNamespace.CatchAllTagHelper",
|
||||||
|
assemblyName: "TestAssembly",
|
||||||
|
ruleBuilders: new Action<TagMatchingRuleBuilder>[]
|
||||||
|
{
|
||||||
|
builder => builder.RequireAttribute(attribute => attribute.Name("catchAll")),
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -478,91 +349,48 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
|
||||||
{
|
{
|
||||||
return new[]
|
return new[]
|
||||||
{
|
{
|
||||||
new TagHelperDescriptor
|
CreateTagHelperDescriptor(
|
||||||
{
|
tagName: "input",
|
||||||
TagName = "input",
|
typeName: "TestNamespace.InputTagHelper1",
|
||||||
TypeName = "TestNamespace.InputTagHelper1",
|
assemblyName: "TestAssembly",
|
||||||
AssemblyName = "TestAssembly",
|
attributes: new Action<ITagHelperBoundAttributeDescriptorBuilder>[]
|
||||||
Attributes = new[]
|
|
||||||
{
|
{
|
||||||
new TagHelperAttributeDescriptor
|
builder => builder
|
||||||
{
|
.Name("int-prefix-grabber")
|
||||||
Name = "int-prefix-grabber",
|
.PropertyName("IntProperty")
|
||||||
PropertyName = "IntProperty",
|
.TypeName(typeof(int).FullName),
|
||||||
TypeName = typeof(int).FullName
|
builder => builder
|
||||||
},
|
.Name("int-dictionary")
|
||||||
new TagHelperAttributeDescriptor
|
.PropertyName("IntDictionaryProperty")
|
||||||
{
|
.TypeName(typeof(IDictionary<string, int>).FullName)
|
||||||
Name = "int-dictionary",
|
.AsDictionary("int-prefix-", typeof(int).FullName),
|
||||||
PropertyName = "IntDictionaryProperty",
|
builder => builder
|
||||||
TypeName = typeof(IDictionary<string, int>).FullName
|
.Name("string-prefix-grabber")
|
||||||
},
|
.PropertyName("StringProperty")
|
||||||
new TagHelperAttributeDescriptor
|
.TypeName(typeof(string).FullName),
|
||||||
{
|
builder => builder
|
||||||
Name = "string-dictionary",
|
.Name("string-dictionary")
|
||||||
PropertyName = "StringDictionaryProperty",
|
.PropertyName("StringDictionaryProperty")
|
||||||
TypeName = "Namespace.DictionaryWithoutParameterlessConstructor<string, string>"
|
.TypeName("Namespace.DictionaryWithoutParameterlessConstructor<string, string>")
|
||||||
},
|
.AsDictionary("string-prefix-", typeof(string).FullName),
|
||||||
new TagHelperAttributeDescriptor
|
}),
|
||||||
{
|
CreateTagHelperDescriptor(
|
||||||
Name = "string-prefix-grabber",
|
tagName: "input",
|
||||||
PropertyName = "StringProperty",
|
typeName: "TestNamespace.InputTagHelper2",
|
||||||
TypeName = typeof(string).FullName,
|
assemblyName: "TestAssembly",
|
||||||
IsStringProperty = true
|
attributes: new Action<ITagHelperBoundAttributeDescriptorBuilder>[]
|
||||||
},
|
|
||||||
new TagHelperAttributeDescriptor
|
|
||||||
{
|
|
||||||
Name = "int-prefix-",
|
|
||||||
PropertyName = "IntDictionaryProperty",
|
|
||||||
TypeName = typeof(int).FullName,
|
|
||||||
IsIndexer = true
|
|
||||||
},
|
|
||||||
new TagHelperAttributeDescriptor
|
|
||||||
{
|
|
||||||
Name = "string-prefix-",
|
|
||||||
PropertyName = "StringDictionaryProperty",
|
|
||||||
TypeName = typeof(string).FullName,
|
|
||||||
IsIndexer = true,
|
|
||||||
IsStringProperty = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
new TagHelperDescriptor
|
|
||||||
{
|
|
||||||
TagName = "input",
|
|
||||||
TypeName = "TestNamespace.InputTagHelper2",
|
|
||||||
AssemblyName = "TestAssembly",
|
|
||||||
Attributes = new[]
|
|
||||||
{
|
{
|
||||||
new TagHelperAttributeDescriptor
|
builder => builder
|
||||||
{
|
.Name("int-dictionary")
|
||||||
Name = "int-dictionary",
|
.PropertyName("IntDictionaryProperty")
|
||||||
PropertyName = "IntDictionaryProperty",
|
.TypeName(typeof(int).FullName)
|
||||||
TypeName = typeof(int).FullName
|
.AsDictionary("int-prefix-", typeof(int).FullName),
|
||||||
},
|
builder => builder
|
||||||
new TagHelperAttributeDescriptor
|
.Name("string-dictionary")
|
||||||
{
|
.PropertyName("StringDictionaryProperty")
|
||||||
Name = "string-dictionary",
|
.TypeName("Namespace.DictionaryWithoutParameterlessConstructor<string, string>")
|
||||||
PropertyName = "StringDictionaryProperty",
|
.AsDictionary("string-prefix-", typeof(string).FullName),
|
||||||
TypeName = "Namespace.DictionaryWithoutParameterlessConstructor<string, string>"
|
}),
|
||||||
},
|
|
||||||
new TagHelperAttributeDescriptor
|
|
||||||
{
|
|
||||||
Name = "int-prefix-",
|
|
||||||
PropertyName = "IntDictionaryProperty",
|
|
||||||
TypeName = typeof(int).FullName,
|
|
||||||
IsIndexer = true
|
|
||||||
},
|
|
||||||
new TagHelperAttributeDescriptor
|
|
||||||
{
|
|
||||||
Name = "string-prefix-",
|
|
||||||
PropertyName = "StringDictionaryProperty",
|
|
||||||
TypeName = typeof(string).FullName,
|
|
||||||
IsIndexer = true,
|
|
||||||
IsStringProperty = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -574,71 +402,120 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
|
||||||
var propertyInfo = typeof(TestType).GetProperty("BoundProperty");
|
var propertyInfo = typeof(TestType).GetProperty("BoundProperty");
|
||||||
return new[]
|
return new[]
|
||||||
{
|
{
|
||||||
new TagHelperDescriptor
|
CreateTagHelperDescriptor(
|
||||||
{
|
tagName: "MyTagHelper",
|
||||||
TagName = "MyTagHelper",
|
typeName: "TestNamespace.MyTagHelper",
|
||||||
TypeName = "TestNamespace.MyTagHelper",
|
assemblyName: "TestAssembly",
|
||||||
AssemblyName = "TestAssembly",
|
attributes: new Action<ITagHelperBoundAttributeDescriptorBuilder>[]
|
||||||
Attributes = new []
|
|
||||||
{
|
{
|
||||||
new TagHelperAttributeDescriptor("BoundProperty", propertyInfo)
|
builder => BuildBoundAttributeDescriptorFromPropertyInfo(builder, "BoundProperty", propertyInfo),
|
||||||
}
|
}),
|
||||||
},
|
CreateTagHelperDescriptor(
|
||||||
new TagHelperDescriptor
|
tagName: "NestedTagHelper",
|
||||||
{
|
typeName: "TestNamespace.NestedTagHelper",
|
||||||
TagName = "NestedTagHelper",
|
assemblyName: "TestAssembly"),
|
||||||
TypeName = "TestNamespace.NestedTagHelper",
|
|
||||||
AssemblyName = "TestAssembly"
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IEnumerable<TagHelperDescriptor> BuildPAndInputTagHelperDescriptors(string prefix)
|
internal static IEnumerable<TagHelperDescriptor> DefaultPAndInputTagHelperDescriptors
|
||||||
{
|
{
|
||||||
var pAgePropertyInfo = typeof(TestType).GetProperty("Age");
|
get
|
||||||
var inputTypePropertyInfo = typeof(TestType).GetProperty("Type");
|
|
||||||
var checkedPropertyInfo = typeof(TestType).GetProperty("Checked");
|
|
||||||
|
|
||||||
return new[]
|
|
||||||
{
|
{
|
||||||
new TagHelperDescriptor
|
var pAgePropertyInfo = typeof(TestType).GetProperty("Age");
|
||||||
|
var inputTypePropertyInfo = typeof(TestType).GetProperty("Type");
|
||||||
|
var checkedPropertyInfo = typeof(TestType).GetProperty("Checked");
|
||||||
|
|
||||||
|
return new[]
|
||||||
{
|
{
|
||||||
Prefix = prefix,
|
CreateTagHelperDescriptor(
|
||||||
TagName = "p",
|
tagName: "p",
|
||||||
TypeName = "TestNamespace.PTagHelper",
|
typeName: "TestNamespace.PTagHelper",
|
||||||
AssemblyName = "TestAssembly",
|
assemblyName: "TestAssembly",
|
||||||
Attributes = new TagHelperAttributeDescriptor[]
|
attributes: new Action<ITagHelperBoundAttributeDescriptorBuilder>[]
|
||||||
{
|
{
|
||||||
new TagHelperAttributeDescriptor("age", pAgePropertyInfo)
|
builder => BuildBoundAttributeDescriptorFromPropertyInfo(builder, "age", pAgePropertyInfo),
|
||||||
},
|
},
|
||||||
TagStructure = TagStructure.NormalOrSelfClosing
|
ruleBuilders: new Action<TagMatchingRuleBuilder>[]
|
||||||
},
|
{
|
||||||
new TagHelperDescriptor
|
builder => builder.RequireTagStructure(TagStructure.NormalOrSelfClosing)
|
||||||
|
}),
|
||||||
|
CreateTagHelperDescriptor(
|
||||||
|
tagName: "input",
|
||||||
|
typeName: "TestNamespace.InputTagHelper",
|
||||||
|
assemblyName: "TestAssembly",
|
||||||
|
attributes: new Action<ITagHelperBoundAttributeDescriptorBuilder>[]
|
||||||
|
{
|
||||||
|
builder => BuildBoundAttributeDescriptorFromPropertyInfo(builder, "type", inputTypePropertyInfo),
|
||||||
|
},
|
||||||
|
ruleBuilders: new Action<TagMatchingRuleBuilder>[]
|
||||||
|
{
|
||||||
|
builder => builder.RequireTagStructure(TagStructure.WithoutEndTag)
|
||||||
|
}),
|
||||||
|
CreateTagHelperDescriptor(
|
||||||
|
tagName: "input",
|
||||||
|
typeName: "TestNamespace.InputTagHelper2",
|
||||||
|
assemblyName: "TestAssembly",
|
||||||
|
attributes: new Action<ITagHelperBoundAttributeDescriptorBuilder>[]
|
||||||
|
{
|
||||||
|
builder => BuildBoundAttributeDescriptorFromPropertyInfo(builder, "type", inputTypePropertyInfo),
|
||||||
|
builder => BuildBoundAttributeDescriptorFromPropertyInfo(builder, "checked", checkedPropertyInfo),
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TagHelperDescriptor CreateTagHelperDescriptor(
|
||||||
|
string tagName,
|
||||||
|
string typeName,
|
||||||
|
string assemblyName,
|
||||||
|
IEnumerable<Action<ITagHelperBoundAttributeDescriptorBuilder>> attributes = null,
|
||||||
|
IEnumerable<Action<TagMatchingRuleBuilder>> ruleBuilders = null)
|
||||||
|
{
|
||||||
|
var builder = ITagHelperDescriptorBuilder.Create(typeName, assemblyName);
|
||||||
|
|
||||||
|
if (attributes != null)
|
||||||
|
{
|
||||||
|
foreach (var attributeBuilder in attributes)
|
||||||
{
|
{
|
||||||
Prefix = prefix,
|
builder.BindAttribute(attributeBuilder);
|
||||||
TagName = "input",
|
|
||||||
TypeName = "TestNamespace.InputTagHelper",
|
|
||||||
AssemblyName = "TestAssembly",
|
|
||||||
Attributes = new TagHelperAttributeDescriptor[]
|
|
||||||
{
|
|
||||||
new TagHelperAttributeDescriptor("type", inputTypePropertyInfo)
|
|
||||||
},
|
|
||||||
TagStructure = TagStructure.WithoutEndTag
|
|
||||||
},
|
|
||||||
new TagHelperDescriptor
|
|
||||||
{
|
|
||||||
Prefix = prefix,
|
|
||||||
TagName = "input",
|
|
||||||
TypeName = "TestNamespace.InputTagHelper2",
|
|
||||||
AssemblyName = "TestAssembly",
|
|
||||||
Attributes = new TagHelperAttributeDescriptor[]
|
|
||||||
{
|
|
||||||
new TagHelperAttributeDescriptor("type", inputTypePropertyInfo),
|
|
||||||
new TagHelperAttributeDescriptor("checked", checkedPropertyInfo)
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
|
if (ruleBuilders != null)
|
||||||
|
{
|
||||||
|
foreach (var ruleBuilder in ruleBuilders)
|
||||||
|
{
|
||||||
|
builder.TagMatchingRule(innerRuleBuilder => {
|
||||||
|
innerRuleBuilder.RequireTagName(tagName);
|
||||||
|
ruleBuilder(innerRuleBuilder);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
builder.TagMatchingRule(ruleBuilder => ruleBuilder.RequireTagName(tagName));
|
||||||
|
}
|
||||||
|
|
||||||
|
var descriptor = builder.Build();
|
||||||
|
|
||||||
|
return descriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void BuildBoundAttributeDescriptorFromPropertyInfo(
|
||||||
|
ITagHelperBoundAttributeDescriptorBuilder builder,
|
||||||
|
string name,
|
||||||
|
PropertyInfo propertyInfo)
|
||||||
|
{
|
||||||
|
builder
|
||||||
|
.Name(name)
|
||||||
|
.PropertyName(propertyInfo.Name)
|
||||||
|
.TypeName(propertyInfo.PropertyType.FullName);
|
||||||
|
|
||||||
|
if (propertyInfo.PropertyType.GetTypeInfo().IsEnum)
|
||||||
|
{
|
||||||
|
builder.AsEnum();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class TestType
|
private class TestType
|
||||||
|
|
|
||||||
|
|
@ -1,95 +0,0 @@
|
||||||
// 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.Linq;
|
|
||||||
using Microsoft.Extensions.Internal;
|
|
||||||
using Xunit;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
|
||||||
{
|
|
||||||
internal class CaseSensitiveTagHelperDescriptorComparer : TagHelperDescriptorComparer
|
|
||||||
{
|
|
||||||
public new static readonly CaseSensitiveTagHelperDescriptorComparer Default =
|
|
||||||
new CaseSensitiveTagHelperDescriptorComparer();
|
|
||||||
|
|
||||||
private CaseSensitiveTagHelperDescriptorComparer()
|
|
||||||
: base()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool Equals(TagHelperDescriptor descriptorX, TagHelperDescriptor descriptorY)
|
|
||||||
{
|
|
||||||
if (descriptorX == descriptorY)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Assert.True(base.Equals(descriptorX, descriptorY));
|
|
||||||
|
|
||||||
// Normal comparer doesn't care about the case, required attribute order, allowed children order,
|
|
||||||
// attributes or prefixes. In tests we do.
|
|
||||||
Assert.Equal(descriptorX.TagName, descriptorY.TagName, StringComparer.Ordinal);
|
|
||||||
Assert.Equal(descriptorX.Prefix, descriptorY.Prefix, StringComparer.Ordinal);
|
|
||||||
Assert.Equal(
|
|
||||||
descriptorX.RequiredAttributes,
|
|
||||||
descriptorY.RequiredAttributes,
|
|
||||||
CaseSensitiveTagHelperRequiredAttributeDescriptorComparer.Default);
|
|
||||||
Assert.Equal(descriptorX.RequiredParent, descriptorY.RequiredParent, StringComparer.Ordinal);
|
|
||||||
|
|
||||||
if (descriptorX.AllowedChildren != descriptorY.AllowedChildren)
|
|
||||||
{
|
|
||||||
Assert.Equal(descriptorX.AllowedChildren, descriptorY.AllowedChildren, StringComparer.Ordinal);
|
|
||||||
}
|
|
||||||
|
|
||||||
Assert.Equal(
|
|
||||||
descriptorX.Attributes,
|
|
||||||
descriptorY.Attributes,
|
|
||||||
TagHelperAttributeDescriptorComparer.Default);
|
|
||||||
Assert.Equal(
|
|
||||||
descriptorX.DesignTimeDescriptor,
|
|
||||||
descriptorY.DesignTimeDescriptor,
|
|
||||||
TagHelperDesignTimeDescriptorComparer.Default);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int GetHashCode(TagHelperDescriptor descriptor)
|
|
||||||
{
|
|
||||||
var hashCodeCombiner = HashCodeCombiner.Start();
|
|
||||||
hashCodeCombiner.Add(base.GetHashCode(descriptor));
|
|
||||||
hashCodeCombiner.Add(descriptor.TagName, StringComparer.Ordinal);
|
|
||||||
hashCodeCombiner.Add(descriptor.Prefix, StringComparer.Ordinal);
|
|
||||||
|
|
||||||
if (descriptor.DesignTimeDescriptor != null)
|
|
||||||
{
|
|
||||||
hashCodeCombiner.Add(
|
|
||||||
TagHelperDesignTimeDescriptorComparer.Default.GetHashCode(descriptor.DesignTimeDescriptor));
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var requiredAttribute in descriptor.RequiredAttributes.OrderBy(attribute => attribute.Name))
|
|
||||||
{
|
|
||||||
hashCodeCombiner.Add(
|
|
||||||
CaseSensitiveTagHelperRequiredAttributeDescriptorComparer.Default.GetHashCode(requiredAttribute));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (descriptor.AllowedChildren != null)
|
|
||||||
{
|
|
||||||
foreach (var child in descriptor.AllowedChildren.OrderBy(child => child))
|
|
||||||
{
|
|
||||||
hashCodeCombiner.Add(child, StringComparer.Ordinal);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var orderedAttributeHashCodes = descriptor.Attributes
|
|
||||||
.Select(attribute => TagHelperAttributeDescriptorComparer.Default.GetHashCode(attribute))
|
|
||||||
.OrderBy(hashcode => hashcode);
|
|
||||||
foreach (var attributeHashCode in orderedAttributeHashCodes)
|
|
||||||
{
|
|
||||||
hashCodeCombiner.Add(attributeHashCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
return hashCodeCombiner.CombinedHash;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,42 +0,0 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using Microsoft.Extensions.Internal;
|
|
||||||
using Xunit;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
|
||||||
{
|
|
||||||
internal class CaseSensitiveTagHelperRequiredAttributeDescriptorComparer : TagHelperRequiredAttributeDescriptorComparer
|
|
||||||
{
|
|
||||||
public new static readonly CaseSensitiveTagHelperRequiredAttributeDescriptorComparer Default =
|
|
||||||
new CaseSensitiveTagHelperRequiredAttributeDescriptorComparer();
|
|
||||||
|
|
||||||
private CaseSensitiveTagHelperRequiredAttributeDescriptorComparer()
|
|
||||||
: base()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool Equals(TagHelperRequiredAttributeDescriptor descriptorX, TagHelperRequiredAttributeDescriptor descriptorY)
|
|
||||||
{
|
|
||||||
if (descriptorX == descriptorY)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Assert.True(base.Equals(descriptorX, descriptorY));
|
|
||||||
Assert.Equal(descriptorX.Name, descriptorY.Name, StringComparer.Ordinal);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int GetHashCode(TagHelperRequiredAttributeDescriptor descriptor)
|
|
||||||
{
|
|
||||||
var hashCodeCombiner = HashCodeCombiner.Start();
|
|
||||||
hashCodeCombiner.Add(base.GetHashCode(descriptor));
|
|
||||||
hashCodeCombiner.Add(descriptor.Name, StringComparer.Ordinal);
|
|
||||||
|
|
||||||
return hashCodeCombiner.CombinedHash;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -86,11 +86,9 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
// Arrange
|
// Arrange
|
||||||
var descriptors = new[]
|
var descriptors = new[]
|
||||||
{
|
{
|
||||||
new TagHelperDescriptor
|
ITagHelperDescriptorBuilder.Create("PTagHelper", "TestAssembly")
|
||||||
{
|
.TagMatchingRule(rule => rule.RequireTagName("p"))
|
||||||
TagName = "p",
|
.Build()
|
||||||
TypeName = "PTagHelper"
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var parser = new RazorEditorParser(CreateTemplateEngine(@"C:\This\Is\A\Test\Path"), @"C:\This\Is\A\Test\Path");
|
var parser = new RazorEditorParser(CreateTemplateEngine(@"C:\This\Is\A\Test\Path"), @"C:\This\Is\A\Test\Path");
|
||||||
|
|
@ -233,27 +231,17 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
// Arrange
|
// Arrange
|
||||||
var descriptors = new[]
|
var descriptors = new[]
|
||||||
{
|
{
|
||||||
new TagHelperDescriptor
|
ITagHelperDescriptorBuilder.Create("PTagHelper", "Test")
|
||||||
{
|
.TagMatchingRule(rule => rule.RequireTagName("p"))
|
||||||
TagName = "p",
|
.BindAttribute(attribute => attribute
|
||||||
TypeName = "PTagHelper",
|
.Name("obj-attr")
|
||||||
AssemblyName = "Test",
|
.TypeName(typeof(object).FullName)
|
||||||
Attributes = new[]
|
.PropertyName("ObjectAttribute"))
|
||||||
{
|
.BindAttribute(attribute => attribute
|
||||||
new TagHelperAttributeDescriptor
|
.Name("str-attr")
|
||||||
{
|
.TypeName(typeof(string).FullName)
|
||||||
Name = "obj-attr",
|
.PropertyName("StringAttribute"))
|
||||||
TypeName = typeof(object).FullName,
|
.Build()
|
||||||
PropertyName = "ObjectAttribute",
|
|
||||||
},
|
|
||||||
new TagHelperAttributeDescriptor
|
|
||||||
{
|
|
||||||
Name = "str-attr",
|
|
||||||
TypeName = typeof(string).FullName,
|
|
||||||
PropertyName = "StringAttribute",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var parser = new RazorEditorParser(CreateTemplateEngine(@"C:\This\Is\A\Test\Path", descriptors), @"C:\This\Is\A\Test\Path");
|
var parser = new RazorEditorParser(CreateTemplateEngine(@"C:\This\Is\A\Test\Path", descriptors), @"C:\This\Is\A\Test\Path");
|
||||||
|
|
|
||||||
|
|
@ -1,56 +0,0 @@
|
||||||
// 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.Extensions.Internal;
|
|
||||||
using Xunit;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
|
||||||
{
|
|
||||||
internal class TagHelperAttributeDescriptorComparer : IEqualityComparer<TagHelperAttributeDescriptor>
|
|
||||||
{
|
|
||||||
public static readonly TagHelperAttributeDescriptorComparer Default =
|
|
||||||
new TagHelperAttributeDescriptorComparer();
|
|
||||||
|
|
||||||
private TagHelperAttributeDescriptorComparer()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Equals(TagHelperAttributeDescriptor descriptorX, TagHelperAttributeDescriptor descriptorY)
|
|
||||||
{
|
|
||||||
if (descriptorX == descriptorY)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Assert.NotNull(descriptorX);
|
|
||||||
Assert.NotNull(descriptorY);
|
|
||||||
Assert.Equal(descriptorX.IsIndexer, descriptorY.IsIndexer);
|
|
||||||
Assert.Equal(descriptorX.Name, descriptorY.Name, StringComparer.Ordinal);
|
|
||||||
Assert.Equal(descriptorX.PropertyName, descriptorY.PropertyName, StringComparer.Ordinal);
|
|
||||||
Assert.Equal(descriptorX.TypeName, descriptorY.TypeName, StringComparer.Ordinal);
|
|
||||||
Assert.Equal(descriptorX.IsEnum, descriptorY.IsEnum);
|
|
||||||
Assert.Equal(descriptorX.IsStringProperty, descriptorY.IsStringProperty);
|
|
||||||
|
|
||||||
return TagHelperAttributeDesignTimeDescriptorComparer.Default.Equals(
|
|
||||||
descriptorX.DesignTimeDescriptor,
|
|
||||||
descriptorY.DesignTimeDescriptor);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int GetHashCode(TagHelperAttributeDescriptor descriptor)
|
|
||||||
{
|
|
||||||
var hashCodeCombiner = HashCodeCombiner.Start();
|
|
||||||
hashCodeCombiner.Add(descriptor.IsIndexer);
|
|
||||||
hashCodeCombiner.Add(descriptor.Name, StringComparer.Ordinal);
|
|
||||||
hashCodeCombiner.Add(descriptor.PropertyName, StringComparer.Ordinal);
|
|
||||||
hashCodeCombiner.Add(descriptor.TypeName, StringComparer.Ordinal);
|
|
||||||
hashCodeCombiner.Add(descriptor.IsEnum);
|
|
||||||
hashCodeCombiner.Add(descriptor.IsStringProperty);
|
|
||||||
hashCodeCombiner.Add(TagHelperAttributeDesignTimeDescriptorComparer.Default.GetHashCode(
|
|
||||||
descriptor.DesignTimeDescriptor));
|
|
||||||
|
|
||||||
return hashCodeCombiner;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,47 +0,0 @@
|
||||||
// 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.Extensions.Internal;
|
|
||||||
using Xunit;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
|
||||||
{
|
|
||||||
internal class TagHelperAttributeDesignTimeDescriptorComparer :
|
|
||||||
IEqualityComparer<TagHelperAttributeDesignTimeDescriptor>
|
|
||||||
{
|
|
||||||
public static readonly TagHelperAttributeDesignTimeDescriptorComparer Default =
|
|
||||||
new TagHelperAttributeDesignTimeDescriptorComparer();
|
|
||||||
|
|
||||||
private TagHelperAttributeDesignTimeDescriptorComparer()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Equals(
|
|
||||||
TagHelperAttributeDesignTimeDescriptor descriptorX,
|
|
||||||
TagHelperAttributeDesignTimeDescriptor descriptorY)
|
|
||||||
{
|
|
||||||
if (descriptorX == descriptorY)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Assert.NotNull(descriptorX);
|
|
||||||
Assert.NotNull(descriptorY);
|
|
||||||
Assert.Equal(descriptorX.Summary, descriptorY.Summary, StringComparer.Ordinal);
|
|
||||||
Assert.Equal(descriptorX.Remarks, descriptorY.Remarks, StringComparer.Ordinal);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int GetHashCode(TagHelperAttributeDesignTimeDescriptor descriptor)
|
|
||||||
{
|
|
||||||
var hashCodeCombiner = HashCodeCombiner.Start();
|
|
||||||
hashCodeCombiner.Add(descriptor.Summary, StringComparer.Ordinal);
|
|
||||||
hashCodeCombiner.Add(descriptor.Remarks, StringComparer.Ordinal);
|
|
||||||
|
|
||||||
return hashCodeCombiner;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -111,54 +111,44 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
// Arrange
|
// Arrange
|
||||||
var descriptors = new[]
|
var descriptors = new[]
|
||||||
{
|
{
|
||||||
new TagHelperDescriptor
|
ITagHelperDescriptorBuilder.Create("CatchAllTagHelper", "SomeAssembly")
|
||||||
{
|
.TagMatchingRule(rule =>
|
||||||
TagName = "*",
|
rule
|
||||||
TypeName = "CatchAllTagHelper",
|
.RequireTagName("*")
|
||||||
AssemblyName = "SomeAssembly",
|
.RequireAttribute(attribute => attribute.Name("bound")))
|
||||||
Attributes = new[]
|
.BindAttribute(attribute =>
|
||||||
{
|
attribute
|
||||||
new TagHelperAttributeDescriptor
|
.Name("[item]")
|
||||||
{
|
.PropertyName("ListItems")
|
||||||
Name = "[item]",
|
.TypeName(typeof(List<string>).Namespace + "List<System.String>"))
|
||||||
PropertyName = "ListItems",
|
.BindAttribute(attribute =>
|
||||||
TypeName = typeof(List<string>).FullName
|
attribute
|
||||||
},
|
.Name("[(item)]")
|
||||||
new TagHelperAttributeDescriptor
|
.PropertyName("ArrayItems")
|
||||||
{
|
.TypeName(typeof(string[]).Namespace + "System.String[]"))
|
||||||
Name = "[(item)]",
|
.BindAttribute(attribute =>
|
||||||
PropertyName = "ArrayItems",
|
attribute
|
||||||
TypeName = typeof(string[]).FullName
|
.Name("(click)")
|
||||||
},
|
.PropertyName("Event1")
|
||||||
new TagHelperAttributeDescriptor
|
.TypeName(typeof(Action).FullName))
|
||||||
{
|
.BindAttribute(attribute =>
|
||||||
Name = "(click)",
|
attribute
|
||||||
PropertyName = "Event1",
|
.Name("(^click)")
|
||||||
TypeName = typeof(Action).FullName
|
.PropertyName("Event2")
|
||||||
},
|
.TypeName(typeof(Action).FullName))
|
||||||
new TagHelperAttributeDescriptor
|
.BindAttribute(attribute =>
|
||||||
{
|
attribute
|
||||||
Name = "(^click)",
|
.Name("*something")
|
||||||
PropertyName = "Event2",
|
.PropertyName("StringProperty1")
|
||||||
TypeName = typeof(Action).FullName
|
.TypeName(typeof(string).FullName))
|
||||||
},
|
.BindAttribute(attribute =>
|
||||||
new TagHelperAttributeDescriptor
|
attribute
|
||||||
{
|
.Name("#local")
|
||||||
Name = "*something",
|
.PropertyName("StringProperty2")
|
||||||
PropertyName = "StringProperty1",
|
.TypeName(typeof(string).FullName))
|
||||||
TypeName = typeof(string).FullName
|
.Build()
|
||||||
},
|
|
||||||
new TagHelperAttributeDescriptor
|
|
||||||
{
|
|
||||||
Name = "#local",
|
|
||||||
PropertyName = "StringProperty2",
|
|
||||||
TypeName = typeof(string).FullName
|
|
||||||
},
|
|
||||||
},
|
|
||||||
RequiredAttributes = new[] { new TagHelperRequiredAttributeDescriptor { Name = "bound" } },
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
var descriptorProvider = new TagHelperDescriptorProvider(descriptors);
|
var descriptorProvider = new TagHelperDescriptorProvider(null, descriptors);
|
||||||
|
|
||||||
// Act & Assert
|
// Act & Assert
|
||||||
EvaluateData(descriptorProvider, documentContent, (MarkupBlock)expectedOutput, expectedErrors: new RazorError[0]);
|
EvaluateData(descriptorProvider, documentContent, (MarkupBlock)expectedOutput, expectedErrors: new RazorError[0]);
|
||||||
|
|
@ -225,16 +215,15 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var descriptors = new TagHelperDescriptor[]
|
var descriptors = new TagHelperDescriptor[]
|
||||||
{
|
{
|
||||||
new TagHelperDescriptor
|
ITagHelperDescriptorBuilder.Create("InputTagHelper", "SomeAssembly")
|
||||||
{
|
.TagMatchingRule(rule =>
|
||||||
TagName = "input",
|
rule
|
||||||
TypeName = "InputTagHelper",
|
.RequireTagName("input")
|
||||||
AssemblyName = "SomeAssembly",
|
.RequireTagStructure(TagStructure.WithoutEndTag))
|
||||||
TagStructure = TagStructure.WithoutEndTag,
|
.Build()
|
||||||
}
|
};
|
||||||
};
|
var descriptorProvider = new TagHelperDescriptorProvider(null, descriptors);
|
||||||
var descriptorProvider = new TagHelperDescriptorProvider(descriptors);
|
|
||||||
|
|
||||||
// Act & Assert
|
// Act & Assert
|
||||||
EvaluateData(descriptorProvider, documentContent, (MarkupBlock)expectedOutput, expectedErrors: new RazorError[0]);
|
EvaluateData(descriptorProvider, documentContent, (MarkupBlock)expectedOutput, expectedErrors: new RazorError[0]);
|
||||||
|
|
@ -317,31 +306,29 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
[MemberData(nameof(TagStructureCompatibilityData))]
|
[MemberData(nameof(TagStructureCompatibilityData))]
|
||||||
public void Rewrite_AllowsCompatibleTagStructures(
|
public void Rewrite_AllowsCompatibleTagStructures(
|
||||||
string documentContent,
|
string documentContent,
|
||||||
int structure1,
|
TagStructure structure1,
|
||||||
int structure2,
|
TagStructure structure2,
|
||||||
object expectedOutput)
|
object expectedOutput)
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var factory = new SpanFactory();
|
var factory = new SpanFactory();
|
||||||
var blockFactory = new BlockFactory(factory);
|
var blockFactory = new BlockFactory(factory);
|
||||||
var descriptors = new TagHelperDescriptor[]
|
var descriptors = new TagHelperDescriptor[]
|
||||||
{
|
{
|
||||||
new TagHelperDescriptor
|
ITagHelperDescriptorBuilder.Create("InputTagHelper1", "SomeAssembly")
|
||||||
{
|
.TagMatchingRule(rule =>
|
||||||
TagName = "input",
|
rule
|
||||||
TypeName = "InputTagHelper1",
|
.RequireTagName("input")
|
||||||
AssemblyName = "SomeAssembly",
|
.RequireTagStructure(structure1))
|
||||||
TagStructure = (TagStructure)structure1
|
.Build(),
|
||||||
},
|
ITagHelperDescriptorBuilder.Create("InputTagHelper2", "SomeAssembly")
|
||||||
new TagHelperDescriptor
|
.TagMatchingRule(rule =>
|
||||||
{
|
rule
|
||||||
TagName = "input",
|
.RequireTagName("input")
|
||||||
TypeName = "InputTagHelper2",
|
.RequireTagStructure(structure2))
|
||||||
AssemblyName = "SomeAssembly",
|
.Build()
|
||||||
TagStructure = (TagStructure)structure2
|
};
|
||||||
}
|
var descriptorProvider = new TagHelperDescriptorProvider(null, descriptors);
|
||||||
};
|
|
||||||
var descriptorProvider = new TagHelperDescriptorProvider(descriptors);
|
|
||||||
|
|
||||||
// Act & Assert
|
// Act & Assert
|
||||||
EvaluateData(descriptorProvider, documentContent, (MarkupBlock)expectedOutput, expectedErrors: new RazorError[0]);
|
EvaluateData(descriptorProvider, documentContent, (MarkupBlock)expectedOutput, expectedErrors: new RazorError[0]);
|
||||||
|
|
@ -1199,43 +1186,33 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[MemberData(nameof(CodeTagHelperAttributesData))]
|
[MemberData(nameof(CodeTagHelperAttributesData))]
|
||||||
public void TagHelperParseTreeRewriter_CreatesMarkupCodeSpansForNonStringTagHelperAttributes(
|
public void Rewrite_CreatesMarkupCodeSpansForNonStringTagHelperAttributes(
|
||||||
string documentContent,
|
string documentContent,
|
||||||
object expectedOutput)
|
object expectedOutput)
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var descriptors = new TagHelperDescriptor[]
|
var descriptors = new TagHelperDescriptor[]
|
||||||
{
|
{
|
||||||
new TagHelperDescriptor
|
ITagHelperDescriptorBuilder.Create("PersonTagHelper", "personAssembly")
|
||||||
{
|
.TagMatchingRule(rule => rule.RequireTagName("person"))
|
||||||
TagName = "person",
|
.BindAttribute(attribute =>
|
||||||
TypeName = "PersonTagHelper",
|
attribute
|
||||||
AssemblyName = "personAssembly",
|
.Name("age")
|
||||||
Attributes = new[]
|
.PropertyName("Age")
|
||||||
{
|
.TypeName(typeof(int).FullName))
|
||||||
new TagHelperAttributeDescriptor
|
.BindAttribute(attribute =>
|
||||||
{
|
attribute
|
||||||
Name = "age",
|
.Name("birthday")
|
||||||
PropertyName = "Age",
|
.PropertyName("BirthDay")
|
||||||
TypeName = typeof(int).FullName
|
.TypeName(typeof(DateTime).FullName))
|
||||||
},
|
.BindAttribute(attribute =>
|
||||||
new TagHelperAttributeDescriptor
|
attribute
|
||||||
{
|
.Name("name")
|
||||||
Name = "birthday",
|
.PropertyName("Name")
|
||||||
PropertyName = "BirthDay",
|
.TypeName(typeof(string).FullName))
|
||||||
TypeName = typeof(DateTime).FullName
|
.Build()
|
||||||
},
|
|
||||||
new TagHelperAttributeDescriptor
|
|
||||||
{
|
|
||||||
Name = "name",
|
|
||||||
PropertyName = "Name",
|
|
||||||
TypeName = typeof(string).FullName,
|
|
||||||
IsStringProperty = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
var providerContext = new TagHelperDescriptorProvider(descriptors);
|
var providerContext = new TagHelperDescriptorProvider(null, descriptors);
|
||||||
|
|
||||||
// Act & Assert
|
// Act & Assert
|
||||||
EvaluateData(providerContext,
|
EvaluateData(providerContext,
|
||||||
|
|
@ -2253,31 +2230,22 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var descriptors = new TagHelperDescriptor[]
|
var descriptors = new TagHelperDescriptor[]
|
||||||
{
|
{
|
||||||
new TagHelperDescriptor
|
ITagHelperDescriptorBuilder.Create("mythTagHelper", "SomeAssembly")
|
||||||
{
|
.TagMatchingRule(rule => rule.RequireTagName("myth"))
|
||||||
TagName = "myth",
|
.BindAttribute(attribute =>
|
||||||
TypeName = "mythTagHelper",
|
attribute
|
||||||
AssemblyName = "SomeAssembly",
|
.Name("bound")
|
||||||
Attributes = new[]
|
.PropertyName("Bound")
|
||||||
{
|
.TypeName(typeof(bool).FullName))
|
||||||
new TagHelperAttributeDescriptor
|
.BindAttribute(attribute =>
|
||||||
{
|
attribute
|
||||||
Name = "bound",
|
.Name("name")
|
||||||
PropertyName = "Bound",
|
.PropertyName("Name")
|
||||||
TypeName = typeof(bool).FullName
|
.TypeName(typeof(string).FullName))
|
||||||
},
|
.Build()
|
||||||
new TagHelperAttributeDescriptor
|
};
|
||||||
{
|
var descriptorProvider = new TagHelperDescriptorProvider(null, descriptors);
|
||||||
Name = "name",
|
|
||||||
PropertyName = "Name",
|
|
||||||
TypeName = typeof(string).FullName,
|
|
||||||
IsStringProperty = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
var descriptorProvider = new TagHelperDescriptorProvider(descriptors);
|
|
||||||
|
|
||||||
// Act & Assert
|
// Act & Assert
|
||||||
EvaluateData(descriptorProvider, documentContent, (MarkupBlock)expectedOutput, (RazorError[])expectedErrors);
|
EvaluateData(descriptorProvider, documentContent, (MarkupBlock)expectedOutput, (RazorError[])expectedErrors);
|
||||||
|
|
@ -3002,7 +2970,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
new[]
|
new[]
|
||||||
{
|
{
|
||||||
new RazorError(
|
new RazorError(
|
||||||
string.Format(errorFormat, "int-dictionary", "input", typeof(IDictionary<string, int>).FullName),
|
string.Format(errorFormat, "int-dictionary", "input", typeof(IDictionary<string, int>).Namespace + ".IDictionary<System.String, System.Int32>"),
|
||||||
absoluteIndex: 7,
|
absoluteIndex: 7,
|
||||||
lineIndex: 0,
|
lineIndex: 0,
|
||||||
columnIndex: 7,
|
columnIndex: 7,
|
||||||
|
|
@ -3022,7 +2990,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
new[]
|
new[]
|
||||||
{
|
{
|
||||||
new RazorError(
|
new RazorError(
|
||||||
string.Format(errorFormat, "string-dictionary", "input", typeof(IDictionary<string, string>).FullName),
|
string.Format(errorFormat, "string-dictionary", "input", typeof(IDictionary<string, string>).Namespace + ".IDictionary<System.String, System.String>"),
|
||||||
absoluteIndex: 7,
|
absoluteIndex: 7,
|
||||||
lineIndex: 0,
|
lineIndex: 0,
|
||||||
columnIndex: 7,
|
columnIndex: 7,
|
||||||
|
|
@ -3911,126 +3879,63 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var descriptors = new TagHelperDescriptor[]
|
var descriptors = new TagHelperDescriptor[]
|
||||||
{
|
{
|
||||||
new TagHelperDescriptor
|
ITagHelperDescriptorBuilder.Create("InputTagHelper1", "SomeAssembly")
|
||||||
{
|
.TagMatchingRule(rule =>
|
||||||
TagName = "input",
|
rule
|
||||||
TypeName = "InputTagHelper1",
|
.RequireTagName("input")
|
||||||
AssemblyName = "SomeAssembly",
|
.RequireAttribute(attribute => attribute.Name("unbound-required")))
|
||||||
Attributes = new[]
|
.TagMatchingRule(rule =>
|
||||||
{
|
rule
|
||||||
new TagHelperAttributeDescriptor
|
.RequireTagName("input")
|
||||||
{
|
.RequireAttribute(attribute => attribute.Name("bound-required-string")))
|
||||||
Name = "bound-required-string",
|
.BindAttribute(attribute =>
|
||||||
PropertyName = "BoundRequiredString",
|
attribute
|
||||||
TypeName = typeof(string).FullName,
|
.Name("bound-required-string")
|
||||||
IsStringProperty = true
|
.PropertyName("BoundRequiredString")
|
||||||
}
|
.TypeName(typeof(string).FullName))
|
||||||
},
|
.Build(),
|
||||||
RequiredAttributes = new[]
|
ITagHelperDescriptorBuilder.Create("InputTagHelper2", "SomeAssembly")
|
||||||
{
|
.TagMatchingRule(rule =>
|
||||||
new TagHelperRequiredAttributeDescriptor { Name = "unbound-required" }
|
rule
|
||||||
}
|
.RequireTagName("input")
|
||||||
},
|
.RequireAttribute(attribute => attribute.Name("bound-required-int")))
|
||||||
new TagHelperDescriptor
|
.BindAttribute(attribute =>
|
||||||
{
|
attribute
|
||||||
TagName = "input",
|
.Name("bound-required-int")
|
||||||
TypeName = "InputTagHelper1",
|
.PropertyName("BoundRequiredInt")
|
||||||
AssemblyName = "SomeAssembly",
|
.TypeName(typeof(int).FullName))
|
||||||
Attributes = new[]
|
.Build(),
|
||||||
{
|
ITagHelperDescriptorBuilder.Create("InputTagHelper3", "SomeAssembly")
|
||||||
new TagHelperAttributeDescriptor
|
.TagMatchingRule(rule => rule.RequireTagName("input"))
|
||||||
{
|
.BindAttribute(attribute =>
|
||||||
Name = "bound-required-string",
|
attribute
|
||||||
PropertyName = "BoundRequiredString",
|
.Name("int-dictionary")
|
||||||
TypeName = typeof(string).FullName,
|
.PropertyName("DictionaryOfIntProperty")
|
||||||
IsStringProperty = true
|
.TypeName(typeof(IDictionary<string, int>).Namespace + ".IDictionary<System.String, System.Int32>")
|
||||||
}
|
.AsDictionary("int-prefix-", typeof(int).FullName))
|
||||||
},
|
.BindAttribute(attribute =>
|
||||||
RequiredAttributes = new[]
|
attribute
|
||||||
{
|
.Name("string-dictionary")
|
||||||
new TagHelperRequiredAttributeDescriptor { Name = "bound-required-string" }
|
.PropertyName("DictionaryOfStringProperty")
|
||||||
}
|
.TypeName(typeof(IDictionary<string, string>).Namespace + ".IDictionary<System.String, System.String>")
|
||||||
},
|
.AsDictionary("string-prefix-", typeof(string).FullName))
|
||||||
new TagHelperDescriptor
|
.Build(),
|
||||||
{
|
ITagHelperDescriptorBuilder.Create("PTagHelper", "SomeAssembly")
|
||||||
TagName = "input",
|
.TagMatchingRule(rule => rule.RequireTagName("p"))
|
||||||
TypeName = "InputTagHelper2",
|
.BindAttribute(attribute =>
|
||||||
AssemblyName = "SomeAssembly",
|
attribute
|
||||||
Attributes = new[]
|
.Name("bound-string")
|
||||||
{
|
.PropertyName("BoundRequiredString")
|
||||||
new TagHelperAttributeDescriptor
|
.TypeName(typeof(string).FullName))
|
||||||
{
|
.BindAttribute(attribute =>
|
||||||
Name = "bound-required-int",
|
attribute
|
||||||
PropertyName = "BoundRequiredInt",
|
.Name("bound-int")
|
||||||
TypeName = typeof(int).FullName
|
.PropertyName("BoundRequiredString")
|
||||||
}
|
.TypeName(typeof(int).FullName))
|
||||||
},
|
.Build(),
|
||||||
RequiredAttributes = new[]
|
};
|
||||||
{
|
var descriptorProvider = new TagHelperDescriptorProvider(null, descriptors);
|
||||||
new TagHelperRequiredAttributeDescriptor { Name = "bound-required-int" }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
new TagHelperDescriptor
|
|
||||||
{
|
|
||||||
TagName = "input",
|
|
||||||
TypeName = "InputTagHelper3",
|
|
||||||
AssemblyName = "SomeAssembly",
|
|
||||||
Attributes = new[]
|
|
||||||
{
|
|
||||||
new TagHelperAttributeDescriptor
|
|
||||||
{
|
|
||||||
Name = "int-dictionary",
|
|
||||||
PropertyName ="DictionaryOfIntProperty",
|
|
||||||
TypeName = typeof(IDictionary<string, int>).FullName
|
|
||||||
},
|
|
||||||
new TagHelperAttributeDescriptor
|
|
||||||
{
|
|
||||||
Name = "string-dictionary",
|
|
||||||
PropertyName = "DictionaryOfStringProperty",
|
|
||||||
TypeName = typeof(IDictionary<string, string>).FullName
|
|
||||||
},
|
|
||||||
new TagHelperAttributeDescriptor
|
|
||||||
{
|
|
||||||
Name = "int-prefix-",
|
|
||||||
PropertyName = "DictionaryOfIntProperty",
|
|
||||||
TypeName = typeof(int).FullName,
|
|
||||||
IsIndexer = true
|
|
||||||
},
|
|
||||||
new TagHelperAttributeDescriptor
|
|
||||||
{
|
|
||||||
Name = "string-prefix-",
|
|
||||||
PropertyName = "DictionaryOfStringProperty",
|
|
||||||
TypeName = typeof(string).FullName,
|
|
||||||
IsIndexer = true,
|
|
||||||
IsStringProperty = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
new TagHelperDescriptor
|
|
||||||
{
|
|
||||||
TagName = "p",
|
|
||||||
TypeName = "PTagHelper",
|
|
||||||
AssemblyName = "SomeAssembly",
|
|
||||||
Attributes = new[]
|
|
||||||
{
|
|
||||||
new TagHelperAttributeDescriptor
|
|
||||||
{
|
|
||||||
Name = "bound-string",
|
|
||||||
PropertyName = "BoundRequiredString",
|
|
||||||
TypeName = typeof(string).FullName,
|
|
||||||
IsStringProperty = true
|
|
||||||
},
|
|
||||||
new TagHelperAttributeDescriptor
|
|
||||||
{
|
|
||||||
Name = "bound-int",
|
|
||||||
PropertyName = "BoundRequiredString",
|
|
||||||
TypeName = typeof(int).FullName
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
var descriptorProvider = new TagHelperDescriptorProvider(descriptors);
|
|
||||||
|
|
||||||
// Act & Assert
|
// Act & Assert
|
||||||
EvaluateData(descriptorProvider, documentContent, (MarkupBlock)expectedOutput, (RazorError[])expectedErrors);
|
EvaluateData(descriptorProvider, documentContent, (MarkupBlock)expectedOutput, (RazorError[])expectedErrors);
|
||||||
|
|
|
||||||
|
|
@ -14,27 +14,22 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
var strongPParent = new TagHelperDescriptor
|
var strongPDivParent = ITagHelperDescriptorBuilder.Create("StrongTagHelper", "SomeAssembly")
|
||||||
{
|
.TagMatchingRule(rule =>
|
||||||
TagName = "strong",
|
rule
|
||||||
TypeName = "StrongTagHelper",
|
.RequireTagName("strong")
|
||||||
AssemblyName = "SomeAssembly",
|
.RequireParentTag("p"))
|
||||||
RequiredParent = "p",
|
.TagMatchingRule(rule =>
|
||||||
};
|
rule
|
||||||
var strongDivParent = new TagHelperDescriptor
|
.RequireTagName("strong")
|
||||||
{
|
.RequireParentTag("div"))
|
||||||
TagName = "strong",
|
.Build();
|
||||||
TypeName = "StrongTagHelper",
|
var catchAllPParent = ITagHelperDescriptorBuilder.Create("CatchAllTagHelper", "SomeAssembly")
|
||||||
AssemblyName = "SomeAssembly",
|
.TagMatchingRule(rule =>
|
||||||
RequiredParent = "div",
|
rule
|
||||||
};
|
.RequireTagName("*")
|
||||||
var catchAllPParent = new TagHelperDescriptor
|
.RequireParentTag("p"))
|
||||||
{
|
.Build();
|
||||||
TagName = "*",
|
|
||||||
TypeName = "CatchAllTagHelper",
|
|
||||||
AssemblyName = "SomeAssembly",
|
|
||||||
RequiredParent = "p",
|
|
||||||
};
|
|
||||||
|
|
||||||
return new TheoryData<
|
return new TheoryData<
|
||||||
string, // tagName
|
string, // tagName
|
||||||
|
|
@ -45,25 +40,25 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
{
|
{
|
||||||
"strong",
|
"strong",
|
||||||
"p",
|
"p",
|
||||||
new[] { strongPParent, strongDivParent },
|
new[] { strongPDivParent },
|
||||||
new[] { strongPParent }
|
new[] { strongPDivParent }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"strong",
|
"strong",
|
||||||
"div",
|
"div",
|
||||||
new[] { strongPParent, strongDivParent, catchAllPParent },
|
new[] { strongPDivParent, catchAllPParent },
|
||||||
new[] { strongDivParent }
|
new[] { strongPDivParent }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"strong",
|
"strong",
|
||||||
"p",
|
"p",
|
||||||
new[] { strongPParent, strongDivParent, catchAllPParent },
|
new[] { strongPDivParent, catchAllPParent },
|
||||||
new[] { strongPParent, catchAllPParent }
|
new[] { strongPDivParent, catchAllPParent }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"custom",
|
"custom",
|
||||||
"p",
|
"p",
|
||||||
new[] { strongPParent, strongDivParent, catchAllPParent },
|
new[] { strongPDivParent, catchAllPParent },
|
||||||
new[] { catchAllPParent }
|
new[] { catchAllPParent }
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
@ -72,93 +67,73 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[MemberData(nameof(RequiredParentData))]
|
[MemberData(nameof(RequiredParentData))]
|
||||||
public void GetDescriptors_ReturnsDescriptorsParentTags(
|
public void GetTagHelperBinding_ReturnsBindingResultWithDescriptorsParentTags(
|
||||||
string tagName,
|
string tagName,
|
||||||
string parentTagName,
|
string parentTagName,
|
||||||
object availableDescriptors,
|
object availableDescriptors,
|
||||||
object expectedDescriptors)
|
object expectedDescriptors)
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var provider = new TagHelperDescriptorProvider((IEnumerable<TagHelperDescriptor>)availableDescriptors);
|
var provider = new TagHelperDescriptorProvider(null, (IEnumerable<TagHelperDescriptor>)availableDescriptors);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var resolvedDescriptors = provider.GetDescriptors(
|
var bindingResult = provider.GetTagHelperBinding(
|
||||||
tagName,
|
tagName,
|
||||||
attributes: Enumerable.Empty<KeyValuePair<string, string>>(),
|
attributes: Enumerable.Empty<KeyValuePair<string, string>>(),
|
||||||
parentTagName: parentTagName);
|
parentTagName: parentTagName);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal((IEnumerable<TagHelperDescriptor>)expectedDescriptors, resolvedDescriptors, CaseSensitiveTagHelperDescriptorComparer.Default);
|
Assert.Equal((IEnumerable<TagHelperDescriptor>)expectedDescriptors, bindingResult.Descriptors, TagHelperDescriptorComparer.CaseSensitive);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static TheoryData RequiredAttributeData
|
public static TheoryData RequiredAttributeData
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
var divDescriptor = new TagHelperDescriptor
|
var divDescriptor = ITagHelperDescriptorBuilder.Create("DivTagHelper", "SomeAssembly")
|
||||||
{
|
.TagMatchingRule(rule =>
|
||||||
TagName = "div",
|
rule
|
||||||
TypeName = "DivTagHelper",
|
.RequireTagName("div")
|
||||||
AssemblyName = "SomeAssembly",
|
.RequireAttribute(attribute => attribute.Name("style")))
|
||||||
RequiredAttributes = new[] { new TagHelperRequiredAttributeDescriptor { Name = "style" } }
|
.Build();
|
||||||
};
|
var inputDescriptor = ITagHelperDescriptorBuilder.Create("InputTagHelper", "SomeAssembly")
|
||||||
var inputDescriptor = new TagHelperDescriptor
|
.TagMatchingRule(rule =>
|
||||||
{
|
rule
|
||||||
TagName = "input",
|
.RequireTagName("input")
|
||||||
TypeName = "InputTagHelper",
|
.RequireAttribute(attribute => attribute.Name("class"))
|
||||||
AssemblyName = "SomeAssembly",
|
.RequireAttribute(attribute => attribute.Name("style")))
|
||||||
RequiredAttributes = new[]
|
.Build();
|
||||||
{
|
var inputWildcardPrefixDescriptor = ITagHelperDescriptorBuilder.Create("InputWildCardAttribute", "SomeAssembly")
|
||||||
new TagHelperRequiredAttributeDescriptor { Name = "class" },
|
.TagMatchingRule(rule =>
|
||||||
new TagHelperRequiredAttributeDescriptor { Name = "style" }
|
rule
|
||||||
}
|
.RequireTagName("input")
|
||||||
};
|
.RequireAttribute(attribute =>
|
||||||
var inputWildcardPrefixDescriptor = new TagHelperDescriptor
|
attribute
|
||||||
{
|
.Name("nodashprefix")
|
||||||
TagName = "input",
|
.NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.PrefixMatch)))
|
||||||
TypeName = "InputWildCardAttribute",
|
.Build();
|
||||||
AssemblyName = "SomeAssembly",
|
var catchAllDescriptor = ITagHelperDescriptorBuilder.Create("CatchAllTagHelper", "SomeAssembly")
|
||||||
RequiredAttributes = new[]
|
.TagMatchingRule(rule =>
|
||||||
{
|
rule
|
||||||
new TagHelperRequiredAttributeDescriptor
|
.RequireTagName(TagHelperDescriptorProvider.ElementCatchAllTarget)
|
||||||
{
|
.RequireAttribute(attribute => attribute.Name("class")))
|
||||||
Name = "nodashprefix",
|
.Build();
|
||||||
NameComparison = TagHelperRequiredAttributeNameComparison.PrefixMatch,
|
var catchAllDescriptor2 = ITagHelperDescriptorBuilder.Create("CatchAllTagHelper2", "SomeAssembly")
|
||||||
}
|
.TagMatchingRule(rule =>
|
||||||
}
|
rule
|
||||||
};
|
.RequireTagName(TagHelperDescriptorProvider.ElementCatchAllTarget)
|
||||||
var catchAllDescriptor = new TagHelperDescriptor
|
.RequireAttribute(attribute => attribute.Name("custom"))
|
||||||
{
|
.RequireAttribute(attribute => attribute.Name("class")))
|
||||||
TagName = TagHelperDescriptorProvider.ElementCatchAllTarget,
|
.Build();
|
||||||
TypeName = "CatchAllTagHelper",
|
var catchAllWildcardPrefixDescriptor = ITagHelperDescriptorBuilder.Create("CatchAllWildCardAttribute", "SomeAssembly")
|
||||||
AssemblyName = "SomeAssembly",
|
.TagMatchingRule(rule =>
|
||||||
RequiredAttributes = new[] { new TagHelperRequiredAttributeDescriptor { Name = "class" } }
|
rule
|
||||||
};
|
.RequireTagName(TagHelperDescriptorProvider.ElementCatchAllTarget)
|
||||||
var catchAllDescriptor2 = new TagHelperDescriptor
|
.RequireAttribute(attribute =>
|
||||||
{
|
attribute
|
||||||
TagName = TagHelperDescriptorProvider.ElementCatchAllTarget,
|
.Name("prefix-")
|
||||||
TypeName = "CatchAllTagHelper2",
|
.NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.PrefixMatch)))
|
||||||
AssemblyName = "SomeAssembly",
|
.Build();
|
||||||
RequiredAttributes = new[]
|
|
||||||
{
|
|
||||||
new TagHelperRequiredAttributeDescriptor { Name = "custom" },
|
|
||||||
new TagHelperRequiredAttributeDescriptor { Name = "class" }
|
|
||||||
}
|
|
||||||
};
|
|
||||||
var catchAllWildcardPrefixDescriptor = new TagHelperDescriptor
|
|
||||||
{
|
|
||||||
TagName = TagHelperDescriptorProvider.ElementCatchAllTarget,
|
|
||||||
TypeName = "CatchAllWildCardAttribute",
|
|
||||||
AssemblyName = "SomeAssembly",
|
|
||||||
RequiredAttributes = new[]
|
|
||||||
{
|
|
||||||
new TagHelperRequiredAttributeDescriptor
|
|
||||||
{
|
|
||||||
Name = "prefix-",
|
|
||||||
NameComparison = TagHelperRequiredAttributeNameComparison.PrefixMatch,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
var defaultAvailableDescriptors =
|
var defaultAvailableDescriptors =
|
||||||
new[] { divDescriptor, inputDescriptor, catchAllDescriptor, catchAllDescriptor2 };
|
new[] { divDescriptor, inputDescriptor, catchAllDescriptor, catchAllDescriptor2 };
|
||||||
var defaultWildcardDescriptors =
|
var defaultWildcardDescriptors =
|
||||||
|
|
@ -176,7 +151,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
"div",
|
"div",
|
||||||
new[] { kvp("custom") },
|
new[] { kvp("custom") },
|
||||||
defaultAvailableDescriptors,
|
defaultAvailableDescriptors,
|
||||||
Enumerable.Empty<TagHelperDescriptor>()
|
null
|
||||||
},
|
},
|
||||||
{ "div", new[] { kvp("style") }, defaultAvailableDescriptors, new[] { divDescriptor } },
|
{ "div", new[] { kvp("style") }, defaultAvailableDescriptors, new[] { divDescriptor } },
|
||||||
{ "div", new[] { kvp("class") }, defaultAvailableDescriptors, new[] { catchAllDescriptor } },
|
{ "div", new[] { kvp("class") }, defaultAvailableDescriptors, new[] { catchAllDescriptor } },
|
||||||
|
|
@ -214,19 +189,19 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
"input",
|
"input",
|
||||||
new[] { kvp("prefixABCnodashprefix") },
|
new[] { kvp("prefixABCnodashprefix") },
|
||||||
defaultWildcardDescriptors,
|
defaultWildcardDescriptors,
|
||||||
Enumerable.Empty<TagHelperDescriptor>()
|
null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"input",
|
"input",
|
||||||
new[] { kvp("prefix-") },
|
new[] { kvp("prefix-") },
|
||||||
defaultWildcardDescriptors,
|
defaultWildcardDescriptors,
|
||||||
Enumerable.Empty<TagHelperDescriptor>()
|
null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"input",
|
"input",
|
||||||
new[] { kvp("nodashprefix") },
|
new[] { kvp("nodashprefix") },
|
||||||
defaultWildcardDescriptors,
|
defaultWildcardDescriptors,
|
||||||
Enumerable.Empty<TagHelperDescriptor>()
|
null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"input",
|
"input",
|
||||||
|
|
@ -258,292 +233,222 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[MemberData(nameof(RequiredAttributeData))]
|
[MemberData(nameof(RequiredAttributeData))]
|
||||||
public void GetDescriptors_ReturnsDescriptorsWithRequiredAttributes(
|
public void GetTagHelperBinding_ReturnsBindingResultDescriptorsWithRequiredAttributes(
|
||||||
string tagName,
|
string tagName,
|
||||||
IEnumerable<KeyValuePair<string, string>> providedAttributes,
|
IEnumerable<KeyValuePair<string, string>> providedAttributes,
|
||||||
object availableDescriptors,
|
object availableDescriptors,
|
||||||
object expectedDescriptors)
|
object expectedDescriptors)
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var provider = new TagHelperDescriptorProvider((IEnumerable<TagHelperDescriptor>)availableDescriptors);
|
var provider = new TagHelperDescriptorProvider(null, (IEnumerable<TagHelperDescriptor>)availableDescriptors);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var resolvedDescriptors = provider.GetDescriptors(tagName, providedAttributes, parentTagName: "p").ToArray();
|
var bindingResult = provider.GetTagHelperBinding(tagName, providedAttributes, parentTagName: "p");
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal((IEnumerable<TagHelperDescriptor>)expectedDescriptors, resolvedDescriptors, CaseSensitiveTagHelperDescriptorComparer.Default);
|
Assert.Equal((IEnumerable<TagHelperDescriptor>)expectedDescriptors, bindingResult?.Descriptors, TagHelperDescriptorComparer.CaseSensitive);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void GetDescriptors_ReturnsEmptyDescriptorsWithPrefixAsTagName()
|
public void GetTagHelperBinding_ReturnsNullBindingResultPrefixAsTagName()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var catchAllDescriptor = CreatePrefixedDescriptor(
|
var catchAllDescriptor = ITagHelperDescriptorBuilder.Create("foo1", "SomeAssembly")
|
||||||
"th",
|
.TagMatchingRule(rule => rule.RequireTagName(TagHelperDescriptorProvider.ElementCatchAllTarget))
|
||||||
TagHelperDescriptorProvider.ElementCatchAllTarget,
|
.Build();
|
||||||
"foo1");
|
|
||||||
var descriptors = new[] { catchAllDescriptor };
|
var descriptors = new[] { catchAllDescriptor };
|
||||||
var provider = new TagHelperDescriptorProvider(descriptors);
|
var provider = new TagHelperDescriptorProvider("th", descriptors);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var resolvedDescriptors = provider.GetDescriptors(
|
var bindingResult = provider.GetTagHelperBinding(
|
||||||
tagName: "th",
|
tagName: "th",
|
||||||
attributes: Enumerable.Empty<KeyValuePair<string, string>>(),
|
attributes: Enumerable.Empty<KeyValuePair<string, string>>(),
|
||||||
parentTagName: "p");
|
parentTagName: "p");
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Empty(resolvedDescriptors);
|
Assert.Null(bindingResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void GetDescriptors_DeduplicatesTagHelpersByTypeName()
|
public void GetTagHelperBinding_ReturnsBindingResultCatchAllDescriptorsForPrefixedTags()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var descriptors = new[]
|
var catchAllDescriptor = ITagHelperDescriptorBuilder.Create("foo1", "SomeAssembly")
|
||||||
{
|
.TagMatchingRule(rule => rule.RequireTagName(TagHelperDescriptorProvider.ElementCatchAllTarget))
|
||||||
new TagHelperDescriptor
|
.Build();
|
||||||
{
|
|
||||||
AssemblyName = "TestAssembly",
|
|
||||||
TagName = "form",
|
|
||||||
TypeName = "TestFormTagHelper",
|
|
||||||
RequiredAttributes = new List<TagHelperRequiredAttributeDescriptor>()
|
|
||||||
{
|
|
||||||
new TagHelperRequiredAttributeDescriptor()
|
|
||||||
{
|
|
||||||
Name = "a",
|
|
||||||
NameComparison = TagHelperRequiredAttributeNameComparison.FullMatch
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
new TagHelperDescriptor
|
|
||||||
{
|
|
||||||
AssemblyName = "TestAssembly",
|
|
||||||
TagName = "form",
|
|
||||||
TypeName = "TestFormTagHelper",
|
|
||||||
RequiredAttributes = new List<TagHelperRequiredAttributeDescriptor>()
|
|
||||||
{
|
|
||||||
new TagHelperRequiredAttributeDescriptor()
|
|
||||||
{
|
|
||||||
Name = "b",
|
|
||||||
NameComparison = TagHelperRequiredAttributeNameComparison.FullMatch
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
var provider = new TagHelperDescriptorProvider(descriptors);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var resolvedDescriptors = provider.GetDescriptors(
|
|
||||||
tagName: "form",
|
|
||||||
attributes: new List<KeyValuePair<string, string>>()
|
|
||||||
{
|
|
||||||
new KeyValuePair<string, string>("a", "hi" ),
|
|
||||||
new KeyValuePair<string, string>("b", "there"),
|
|
||||||
},
|
|
||||||
parentTagName: "p");
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Same(descriptors[0], Assert.Single(resolvedDescriptors));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GetDescriptors_OnlyUnderstandsSinglePrefix()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var divDescriptor = CreatePrefixedDescriptor("th:", "div", "foo1");
|
|
||||||
var spanDescriptor = CreatePrefixedDescriptor("th2:", "span", "foo2");
|
|
||||||
var descriptors = new[] { divDescriptor, spanDescriptor };
|
|
||||||
var provider = new TagHelperDescriptorProvider(descriptors);
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var retrievedDescriptorsDiv = provider.GetDescriptors(
|
|
||||||
tagName: "th:div",
|
|
||||||
attributes: Enumerable.Empty<KeyValuePair<string, string>>(),
|
|
||||||
parentTagName: "p");
|
|
||||||
var retrievedDescriptorsSpan = provider.GetDescriptors(
|
|
||||||
tagName: "th2:span",
|
|
||||||
attributes: Enumerable.Empty<KeyValuePair<string, string>>(),
|
|
||||||
parentTagName: "p");
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
var descriptor = Assert.Single(retrievedDescriptorsDiv);
|
|
||||||
Assert.Same(divDescriptor, descriptor);
|
|
||||||
Assert.Empty(retrievedDescriptorsSpan);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void GetDescriptors_ReturnsCatchAllDescriptorsForPrefixedTags()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var catchAllDescriptor = CreatePrefixedDescriptor("th:", TagHelperDescriptorProvider.ElementCatchAllTarget, "foo1");
|
|
||||||
var descriptors = new[] { catchAllDescriptor };
|
var descriptors = new[] { catchAllDescriptor };
|
||||||
var provider = new TagHelperDescriptorProvider(descriptors);
|
var provider = new TagHelperDescriptorProvider("th:", descriptors);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var retrievedDescriptorsDiv = provider.GetDescriptors(
|
var bindingResultDiv = provider.GetTagHelperBinding(
|
||||||
tagName: "th:div",
|
tagName: "th:div",
|
||||||
attributes: Enumerable.Empty<KeyValuePair<string, string>>(),
|
attributes: Enumerable.Empty<KeyValuePair<string, string>>(),
|
||||||
parentTagName: "p");
|
parentTagName: "p");
|
||||||
var retrievedDescriptorsSpan = provider.GetDescriptors(
|
var bindingResultSpan = provider.GetTagHelperBinding(
|
||||||
tagName: "th:span",
|
tagName: "th:span",
|
||||||
attributes: Enumerable.Empty<KeyValuePair<string, string>>(),
|
attributes: Enumerable.Empty<KeyValuePair<string, string>>(),
|
||||||
parentTagName: "p");
|
parentTagName: "p");
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var descriptor = Assert.Single(retrievedDescriptorsDiv);
|
var descriptor = Assert.Single(bindingResultDiv.Descriptors);
|
||||||
Assert.Same(catchAllDescriptor, descriptor);
|
Assert.Same(catchAllDescriptor, descriptor);
|
||||||
descriptor = Assert.Single(retrievedDescriptorsSpan);
|
descriptor = Assert.Single(bindingResultSpan.Descriptors);
|
||||||
Assert.Same(catchAllDescriptor, descriptor);
|
Assert.Same(catchAllDescriptor, descriptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void GetDescriptors_ReturnsDescriptorsForPrefixedTags()
|
public void GetTagHelperBinding_ReturnsBindingResultDescriptorsForPrefixedTags()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var divDescriptor = CreatePrefixedDescriptor("th:", "div", "foo1");
|
var divDescriptor = ITagHelperDescriptorBuilder.Create("foo1", "SomeAssembly")
|
||||||
|
.TagMatchingRule(rule => rule.RequireTagName("div"))
|
||||||
|
.Build();
|
||||||
var descriptors = new[] { divDescriptor };
|
var descriptors = new[] { divDescriptor };
|
||||||
var provider = new TagHelperDescriptorProvider(descriptors);
|
var provider = new TagHelperDescriptorProvider("th:", descriptors);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var retrievedDescriptors = provider.GetDescriptors(
|
var bindingResult = provider.GetTagHelperBinding(
|
||||||
tagName: "th:div",
|
tagName: "th:div",
|
||||||
attributes: Enumerable.Empty<KeyValuePair<string, string>>(),
|
attributes: Enumerable.Empty<KeyValuePair<string, string>>(),
|
||||||
parentTagName: "p");
|
parentTagName: "p");
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var descriptor = Assert.Single(retrievedDescriptors);
|
var descriptor = Assert.Single(bindingResult.Descriptors);
|
||||||
Assert.Same(divDescriptor, descriptor);
|
Assert.Same(divDescriptor, descriptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData("*")]
|
[InlineData("*")]
|
||||||
[InlineData("div")]
|
[InlineData("div")]
|
||||||
public void GetDescriptors_ReturnsNothingForUnprefixedTags(string tagName)
|
public void GetTagHelperBinding_ReturnsNullForUnprefixedTags(string tagName)
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var divDescriptor = CreatePrefixedDescriptor("th:", tagName, "foo1");
|
var divDescriptor = ITagHelperDescriptorBuilder.Create("foo1", "SomeAssembly")
|
||||||
|
.TagMatchingRule(rule => rule.RequireTagName(tagName))
|
||||||
|
.Build();
|
||||||
var descriptors = new[] { divDescriptor };
|
var descriptors = new[] { divDescriptor };
|
||||||
var provider = new TagHelperDescriptorProvider(descriptors);
|
var provider = new TagHelperDescriptorProvider("th:", descriptors);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var retrievedDescriptorsDiv = provider.GetDescriptors(
|
var bindingResult = provider.GetTagHelperBinding(
|
||||||
tagName: "div",
|
tagName: "div",
|
||||||
attributes: Enumerable.Empty<KeyValuePair<string, string>>(),
|
attributes: Enumerable.Empty<KeyValuePair<string, string>>(),
|
||||||
parentTagName: "p");
|
parentTagName: "p");
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Empty(retrievedDescriptorsDiv);
|
Assert.Null(bindingResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void GetDescriptors_ReturnsNothingForUnregisteredTags()
|
public void GetDescriptors_ReturnsNothingForUnregisteredTags()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var divDescriptor = new TagHelperDescriptor
|
var divDescriptor = ITagHelperDescriptorBuilder.Create("foo1", "SomeAssembly")
|
||||||
{
|
.TagMatchingRule(rule => rule.RequireTagName("div"))
|
||||||
TagName = "div",
|
.Build();
|
||||||
TypeName = "foo1",
|
var spanDescriptor = ITagHelperDescriptorBuilder.Create("foo2", "SomeAssembly")
|
||||||
AssemblyName = "SomeAssembly",
|
.TagMatchingRule(rule => rule.RequireTagName("span"))
|
||||||
};
|
.Build();
|
||||||
var spanDescriptor = new TagHelperDescriptor
|
|
||||||
{
|
|
||||||
TagName = "span",
|
|
||||||
TypeName = "foo2",
|
|
||||||
AssemblyName = "SomeAssembly",
|
|
||||||
};
|
|
||||||
var descriptors = new TagHelperDescriptor[] { divDescriptor, spanDescriptor };
|
var descriptors = new TagHelperDescriptor[] { divDescriptor, spanDescriptor };
|
||||||
var provider = new TagHelperDescriptorProvider(descriptors);
|
var provider = new TagHelperDescriptorProvider(null, descriptors);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var retrievedDescriptors = provider.GetDescriptors(
|
var tagHelperBinding = provider.GetTagHelperBinding(
|
||||||
tagName: "foo",
|
tagName: "foo",
|
||||||
attributes: Enumerable.Empty<KeyValuePair<string, string>>(),
|
attributes: Enumerable.Empty<KeyValuePair<string, string>>(),
|
||||||
parentTagName: "p");
|
parentTagName: "p");
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Empty(retrievedDescriptors);
|
Assert.Null(tagHelperBinding);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void GetDescriptors_ReturnsCatchAllsWithEveryTagName()
|
public void GetDescriptors_ReturnsCatchAllsWithEveryTagName()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var divDescriptor = new TagHelperDescriptor
|
var divDescriptor = ITagHelperDescriptorBuilder.Create("foo1", "SomeAssembly")
|
||||||
{
|
.TagMatchingRule(rule => rule.RequireTagName("div"))
|
||||||
TagName = "div",
|
.Build();
|
||||||
TypeName = "foo1",
|
var spanDescriptor = ITagHelperDescriptorBuilder.Create("foo2", "SomeAssembly")
|
||||||
AssemblyName = "SomeAssembly",
|
.TagMatchingRule(rule => rule.RequireTagName("span"))
|
||||||
};
|
.Build();
|
||||||
var spanDescriptor = new TagHelperDescriptor
|
var catchAllDescriptor = ITagHelperDescriptorBuilder.Create("foo3", "SomeAssembly")
|
||||||
{
|
.TagMatchingRule(rule => rule.RequireTagName(TagHelperDescriptorProvider.ElementCatchAllTarget))
|
||||||
TagName = "span",
|
.Build();
|
||||||
TypeName = "foo2",
|
|
||||||
AssemblyName = "SomeAssembly",
|
|
||||||
};
|
|
||||||
var catchAllDescriptor = new TagHelperDescriptor
|
|
||||||
{
|
|
||||||
TagName = TagHelperDescriptorProvider.ElementCatchAllTarget,
|
|
||||||
TypeName = "foo3",
|
|
||||||
AssemblyName = "SomeAssembly",
|
|
||||||
};
|
|
||||||
var descriptors = new TagHelperDescriptor[] { divDescriptor, spanDescriptor, catchAllDescriptor };
|
var descriptors = new TagHelperDescriptor[] { divDescriptor, spanDescriptor, catchAllDescriptor };
|
||||||
var provider = new TagHelperDescriptorProvider(descriptors);
|
var provider = new TagHelperDescriptorProvider(null, descriptors);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var divDescriptors = provider.GetDescriptors(
|
var divBinding = provider.GetTagHelperBinding(
|
||||||
tagName: "div",
|
tagName: "div",
|
||||||
attributes: Enumerable.Empty<KeyValuePair<string, string>>(),
|
attributes: Enumerable.Empty<KeyValuePair<string, string>>(),
|
||||||
parentTagName: "p");
|
parentTagName: "p");
|
||||||
var spanDescriptors = provider.GetDescriptors(
|
var spanBinding = provider.GetTagHelperBinding(
|
||||||
tagName: "span",
|
tagName: "span",
|
||||||
attributes: Enumerable.Empty<KeyValuePair<string, string>>(),
|
attributes: Enumerable.Empty<KeyValuePair<string, string>>(),
|
||||||
parentTagName: "p");
|
parentTagName: "p");
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
// For divs
|
// For divs
|
||||||
Assert.Equal(2, divDescriptors.Count());
|
Assert.Equal(2, divBinding.Descriptors.Count());
|
||||||
Assert.Contains(divDescriptor, divDescriptors);
|
Assert.Contains(divDescriptor, divBinding.Descriptors);
|
||||||
Assert.Contains(catchAllDescriptor, divDescriptors);
|
Assert.Contains(catchAllDescriptor, divBinding.Descriptors);
|
||||||
|
|
||||||
// For spans
|
// For spans
|
||||||
Assert.Equal(2, spanDescriptors.Count());
|
Assert.Equal(2, spanBinding.Descriptors.Count());
|
||||||
Assert.Contains(spanDescriptor, spanDescriptors);
|
Assert.Contains(spanDescriptor, spanBinding.Descriptors);
|
||||||
Assert.Contains(catchAllDescriptor, spanDescriptors);
|
Assert.Contains(catchAllDescriptor, spanBinding.Descriptors);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void GetDescriptors_DuplicateDescriptorsAreNotPartOfTagHelperDescriptorPool()
|
public void GetDescriptors_DuplicateDescriptorsAreNotPartOfTagHelperDescriptorPool()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var divDescriptor = new TagHelperDescriptor
|
var divDescriptor = ITagHelperDescriptorBuilder.Create("foo1", "SomeAssembly")
|
||||||
{
|
.TagMatchingRule(rule => rule.RequireTagName("div"))
|
||||||
TagName = "div",
|
.Build();
|
||||||
TypeName = "foo1",
|
|
||||||
AssemblyName = "SomeAssembly",
|
|
||||||
};
|
|
||||||
var descriptors = new TagHelperDescriptor[] { divDescriptor, divDescriptor };
|
var descriptors = new TagHelperDescriptor[] { divDescriptor, divDescriptor };
|
||||||
var provider = new TagHelperDescriptorProvider(descriptors);
|
var provider = new TagHelperDescriptorProvider(null, descriptors);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var retrievedDescriptors = provider.GetDescriptors(
|
var bindingResult = provider.GetTagHelperBinding(
|
||||||
tagName: "div",
|
tagName: "div",
|
||||||
attributes: Enumerable.Empty<KeyValuePair<string, string>>(),
|
attributes: Enumerable.Empty<KeyValuePair<string, string>>(),
|
||||||
parentTagName: "p");
|
parentTagName: "p");
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
var descriptor = Assert.Single(retrievedDescriptors);
|
var descriptor = Assert.Single(bindingResult.Descriptors);
|
||||||
Assert.Same(divDescriptor, descriptor);
|
Assert.Same(divDescriptor, descriptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static TagHelperDescriptor CreatePrefixedDescriptor(string prefix, string tagName, string typeName)
|
[Fact]
|
||||||
|
public void GetTagHelperBinding_DescriptorWithMultipleRules_CorrectlySelectsMatchingRules()
|
||||||
{
|
{
|
||||||
return new TagHelperDescriptor
|
// Arrange
|
||||||
{
|
var multiRuleDescriptor = ITagHelperDescriptorBuilder.Create("foo", "SomeAssembly")
|
||||||
Prefix = prefix,
|
.TagMatchingRule(rule => rule
|
||||||
TagName = tagName,
|
.RequireTagName(TagHelperDescriptorProvider.ElementCatchAllTarget)
|
||||||
TypeName = typeName,
|
.RequireParentTag("body"))
|
||||||
AssemblyName = "SomeAssembly"
|
.TagMatchingRule(rule => rule
|
||||||
};
|
.RequireTagName("div"))
|
||||||
|
.TagMatchingRule(rule => rule
|
||||||
|
.RequireTagName("span"))
|
||||||
|
.Build();
|
||||||
|
var descriptors = new TagHelperDescriptor[] { multiRuleDescriptor };
|
||||||
|
var provider = new TagHelperDescriptorProvider(null, descriptors);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var binding = provider.GetTagHelperBinding(
|
||||||
|
tagName: "div",
|
||||||
|
attributes: Enumerable.Empty<KeyValuePair<string, string>>(),
|
||||||
|
parentTagName: "p");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var boundDescriptor = Assert.Single(binding.Descriptors);
|
||||||
|
Assert.Same(multiRuleDescriptor, boundDescriptor);
|
||||||
|
var boundRules = binding.GetBoundRules(boundDescriptor);
|
||||||
|
var boundRule = Assert.Single(boundRules);
|
||||||
|
Assert.Equal("div", boundRule.TagName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,47 +0,0 @@
|
||||||
// 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.Extensions.Internal;
|
|
||||||
using Xunit;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
|
||||||
{
|
|
||||||
internal class TagHelperDesignTimeDescriptorComparer : IEqualityComparer<TagHelperDesignTimeDescriptor>
|
|
||||||
{
|
|
||||||
public static readonly TagHelperDesignTimeDescriptorComparer Default =
|
|
||||||
new TagHelperDesignTimeDescriptorComparer();
|
|
||||||
|
|
||||||
private TagHelperDesignTimeDescriptorComparer()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Equals(TagHelperDesignTimeDescriptor descriptorX, TagHelperDesignTimeDescriptor descriptorY)
|
|
||||||
{
|
|
||||||
if (descriptorX == descriptorY)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Assert.NotNull(descriptorX);
|
|
||||||
Assert.NotNull(descriptorY);
|
|
||||||
Assert.Equal(descriptorX.Summary, descriptorY.Summary, StringComparer.Ordinal);
|
|
||||||
Assert.Equal(descriptorX.Remarks, descriptorY.Remarks, StringComparer.Ordinal);
|
|
||||||
Assert.Equal(descriptorX.OutputElementHint, descriptorY.OutputElementHint, StringComparer.Ordinal);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int GetHashCode(TagHelperDesignTimeDescriptor descriptor)
|
|
||||||
{
|
|
||||||
var hashCodeCombiner = HashCodeCombiner.Start();
|
|
||||||
|
|
||||||
hashCodeCombiner.Add(descriptor.Summary, StringComparer.Ordinal);
|
|
||||||
hashCodeCombiner.Add(descriptor.Remarks, StringComparer.Ordinal);
|
|
||||||
hashCodeCombiner.Add(descriptor.OutputElementHint, StringComparer.Ordinal);
|
|
||||||
|
|
||||||
return hashCodeCombiner;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,12 +1,8 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// 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.Collections.Generic;
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
|
||||||
using Microsoft.AspNetCore.Razor.Evolution;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
{
|
{
|
||||||
|
|
@ -41,27 +37,25 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||||
|
|
||||||
foreach (var tagName in tagNames)
|
foreach (var tagName in tagNames)
|
||||||
{
|
{
|
||||||
descriptors.Add(
|
var descriptor = ITagHelperDescriptorBuilder.Create(tagName + "taghelper", "SomeAssembly")
|
||||||
new TagHelperDescriptor
|
.TagMatchingRule(rule => rule.RequireTagName(tagName))
|
||||||
{
|
.Build();
|
||||||
TagName = tagName,
|
descriptors.Add(descriptor);
|
||||||
TypeName = tagName + "taghelper",
|
|
||||||
AssemblyName = "SomeAssembly"
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return new TagHelperDescriptorProvider(descriptors);
|
return new TagHelperDescriptorProvider(null, descriptors);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void EvaluateData(
|
internal void EvaluateData(
|
||||||
TagHelperDescriptorProvider provider,
|
TagHelperDescriptorProvider provider,
|
||||||
string documentContent,
|
string documentContent,
|
||||||
MarkupBlock expectedOutput,
|
MarkupBlock expectedOutput,
|
||||||
IEnumerable<RazorError> expectedErrors)
|
IEnumerable<RazorError> expectedErrors,
|
||||||
|
string tagHelperPrefix = null)
|
||||||
{
|
{
|
||||||
var syntaxTree = ParseDocument(documentContent);
|
var syntaxTree = ParseDocument(documentContent);
|
||||||
var errorSink = new ErrorSink();
|
var errorSink = new ErrorSink();
|
||||||
var parseTreeRewriter = new TagHelperParseTreeRewriter(provider);
|
var parseTreeRewriter = new TagHelperParseTreeRewriter(tagHelperPrefix, provider);
|
||||||
var actualTree = parseTreeRewriter.Rewrite(syntaxTree.Root, errorSink);
|
var actualTree = parseTreeRewriter.Rewrite(syntaxTree.Root, errorSink);
|
||||||
|
|
||||||
var allErrors = syntaxTree.Diagnostics.Concat(errorSink.Errors.Select(error => RazorDiagnostic.Create(error)));
|
var allErrors = syntaxTree.Diagnostics.Concat(errorSink.Errors.Select(error => RazorDiagnostic.Create(error)));
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ using Microsoft.AspNetCore.Razor.Evolution.Legacy;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Moq;
|
using Moq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Razor.Evolution
|
namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
{
|
{
|
||||||
|
|
@ -20,16 +21,14 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
{
|
{
|
||||||
builder.AddTagHelpers(new[]
|
builder.AddTagHelpers(new[]
|
||||||
{
|
{
|
||||||
new TagHelperDescriptor
|
CreateTagHelperDescriptor(
|
||||||
{
|
tagName: "form",
|
||||||
AssemblyName = "TestAssembly",
|
typeName: null,
|
||||||
TagName = "form",
|
assemblyName: "TestAssembly"),
|
||||||
},
|
CreateTagHelperDescriptor(
|
||||||
new TagHelperDescriptor
|
tagName: "input",
|
||||||
{
|
typeName: null,
|
||||||
AssemblyName = "TestAssembly",
|
assemblyName: "TestAssembly"),
|
||||||
TagName = "input",
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -59,39 +58,25 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
public void Execute_DirectiveWithoutQuotes_RewritesTagHelpers_TagHelperMatchesElementTwice()
|
public void Execute_DirectiveWithoutQuotes_RewritesTagHelpers_TagHelperMatchesElementTwice()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
|
var descriptor = CreateTagHelperDescriptor(
|
||||||
|
tagName: "form",
|
||||||
|
typeName: "TestFormTagHelper",
|
||||||
|
assemblyName: "TestAssembly",
|
||||||
|
ruleBuilders: new Action<TagMatchingRuleBuilder>[]
|
||||||
|
{
|
||||||
|
ruleBuilder => ruleBuilder
|
||||||
|
.RequireAttribute(attribute => attribute
|
||||||
|
.Name("a")
|
||||||
|
.NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.FullMatch)),
|
||||||
|
ruleBuilder => ruleBuilder
|
||||||
|
.RequireAttribute(attribute => attribute
|
||||||
|
.Name("b")
|
||||||
|
.NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.FullMatch)),
|
||||||
|
});
|
||||||
|
|
||||||
var engine = RazorEngine.Create(builder =>
|
var engine = RazorEngine.Create(builder =>
|
||||||
{
|
{
|
||||||
builder.AddTagHelpers(new[]
|
builder.AddTagHelpers(new[] { descriptor });
|
||||||
{
|
|
||||||
new TagHelperDescriptor
|
|
||||||
{
|
|
||||||
AssemblyName = "TestAssembly",
|
|
||||||
TagName = "form",
|
|
||||||
TypeName = "TestFormTagHelper",
|
|
||||||
RequiredAttributes = new List<TagHelperRequiredAttributeDescriptor>()
|
|
||||||
{
|
|
||||||
new TagHelperRequiredAttributeDescriptor()
|
|
||||||
{
|
|
||||||
Name = "a",
|
|
||||||
NameComparison = TagHelperRequiredAttributeNameComparison.FullMatch
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
new TagHelperDescriptor
|
|
||||||
{
|
|
||||||
AssemblyName = "TestAssembly",
|
|
||||||
TagName = "form",
|
|
||||||
TypeName = "TestFormTagHelper",
|
|
||||||
RequiredAttributes = new List<TagHelperRequiredAttributeDescriptor>()
|
|
||||||
{
|
|
||||||
new TagHelperRequiredAttributeDescriptor()
|
|
||||||
{
|
|
||||||
Name = "b",
|
|
||||||
NameComparison = TagHelperRequiredAttributeNameComparison.FullMatch
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
var pass = new TagHelperBinderSyntaxTreePass()
|
var pass = new TagHelperBinderSyntaxTreePass()
|
||||||
|
|
@ -117,46 +102,32 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
|
|
||||||
var formTagHelper = Assert.IsType<TagHelperBlock>(rewrittenTree.Root.Children[2]);
|
var formTagHelper = Assert.IsType<TagHelperBlock>(rewrittenTree.Root.Children[2]);
|
||||||
Assert.Equal("form", formTagHelper.TagName);
|
Assert.Equal("form", formTagHelper.TagName);
|
||||||
Assert.Single(formTagHelper.Descriptors);
|
Assert.Equal(2, formTagHelper.Binding.GetBoundRules(descriptor).Count());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Execute_DirectiveWithQuotes_RewritesTagHelpers_TagHelperMatchesElementTwice()
|
public void Execute_DirectiveWithQuotes_RewritesTagHelpers_TagHelperMatchesElementTwice()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
|
var descriptor = CreateTagHelperDescriptor(
|
||||||
|
tagName: "form",
|
||||||
|
typeName: "TestFormTagHelper",
|
||||||
|
assemblyName: "TestAssembly",
|
||||||
|
ruleBuilders: new Action<TagMatchingRuleBuilder>[]
|
||||||
|
{
|
||||||
|
ruleBuilder => ruleBuilder
|
||||||
|
.RequireAttribute(attribute => attribute
|
||||||
|
.Name("a")
|
||||||
|
.NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.FullMatch)),
|
||||||
|
ruleBuilder => ruleBuilder
|
||||||
|
.RequireAttribute(attribute => attribute
|
||||||
|
.Name("b")
|
||||||
|
.NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.FullMatch)),
|
||||||
|
});
|
||||||
|
|
||||||
var engine = RazorEngine.Create(builder =>
|
var engine = RazorEngine.Create(builder =>
|
||||||
{
|
{
|
||||||
builder.AddTagHelpers(new[]
|
builder.AddTagHelpers(new[] { descriptor });
|
||||||
{
|
|
||||||
new TagHelperDescriptor
|
|
||||||
{
|
|
||||||
AssemblyName = "TestAssembly",
|
|
||||||
TagName = "form",
|
|
||||||
TypeName = "TestFormTagHelper",
|
|
||||||
RequiredAttributes = new List<TagHelperRequiredAttributeDescriptor>()
|
|
||||||
{
|
|
||||||
new TagHelperRequiredAttributeDescriptor()
|
|
||||||
{
|
|
||||||
Name = "a",
|
|
||||||
NameComparison = TagHelperRequiredAttributeNameComparison.FullMatch
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
new TagHelperDescriptor
|
|
||||||
{
|
|
||||||
AssemblyName = "TestAssembly",
|
|
||||||
TagName = "form",
|
|
||||||
TypeName = "TestFormTagHelper",
|
|
||||||
RequiredAttributes = new List<TagHelperRequiredAttributeDescriptor>()
|
|
||||||
{
|
|
||||||
new TagHelperRequiredAttributeDescriptor()
|
|
||||||
{
|
|
||||||
Name = "b",
|
|
||||||
NameComparison = TagHelperRequiredAttributeNameComparison.FullMatch
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
var pass = new TagHelperBinderSyntaxTreePass()
|
var pass = new TagHelperBinderSyntaxTreePass()
|
||||||
|
|
@ -182,7 +153,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
|
|
||||||
var formTagHelper = Assert.IsType<TagHelperBlock>(rewrittenTree.Root.Children[2]);
|
var formTagHelper = Assert.IsType<TagHelperBlock>(rewrittenTree.Root.Children[2]);
|
||||||
Assert.Equal("form", formTagHelper.TagName);
|
Assert.Equal("form", formTagHelper.TagName);
|
||||||
Assert.Single(formTagHelper.Descriptors);
|
Assert.Equal(2, formTagHelper.Binding.GetBoundRules(descriptor).Count());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -298,7 +269,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
var resolverError = RazorDiagnostic.Create(new RazorError("Test error", new SourceLocation(19, 1, 17), length: 12));
|
var resolverError = RazorDiagnostic.Create(new RazorError("Test error", new SourceLocation(19, 1, 17), length: 12));
|
||||||
var engine = RazorEngine.Create(builder =>
|
var engine = RazorEngine.Create(builder =>
|
||||||
{
|
{
|
||||||
var resolver = new ErrorLoggingTagHelperDescriptorResolver(resolverError);
|
var resolver = new ErrorLoggingTagHelperDescriptorResolver(resolverError, tagName: "test");
|
||||||
builder.Features.Add(Mock.Of<ITagHelperFeature>(f => f.Resolver == resolver));
|
builder.Features.Add(Mock.Of<ITagHelperFeature>(f => f.Resolver == resolver));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -327,6 +298,36 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
Assert.Equal(new[] { initialError, resolverError }, outputTree.Diagnostics);
|
Assert.Equal(new[] { initialError, resolverError }, outputTree.Diagnostics);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Execute_CombinesDiagnosticsFromTagHelperDescriptor()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var resolverError = RazorDiagnostic.Create(new RazorError("Test error", new SourceLocation(19, 1, 17), length: 12));
|
||||||
|
var engine = RazorEngine.Create(builder =>
|
||||||
|
{
|
||||||
|
var resolver = new ErrorLoggingTagHelperDescriptorResolver(resolverError, tagName: null);
|
||||||
|
builder.Features.Add(Mock.Of<ITagHelperFeature>(f => f.Resolver == resolver));
|
||||||
|
});
|
||||||
|
|
||||||
|
var descriptorError = RazorDiagnosticFactory.CreateTagHelper_InvalidTargetedTagNameNullOrWhitespace();
|
||||||
|
|
||||||
|
var pass = new TagHelperBinderSyntaxTreePass()
|
||||||
|
{
|
||||||
|
Engine = engine,
|
||||||
|
};
|
||||||
|
|
||||||
|
var sourceDocument = CreateTestSourceDocument();
|
||||||
|
var codeDocument = RazorCodeDocument.Create(sourceDocument);
|
||||||
|
var originalTree = RazorSyntaxTree.Parse(sourceDocument);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var outputTree = pass.Execute(codeDocument, originalTree);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Empty(originalTree.Diagnostics);
|
||||||
|
Assert.Equal(new[] { resolverError, descriptorError }, outputTree.Diagnostics);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Execute_CombinesErrorsOnRewritingErrors()
|
public void Execute_CombinesErrorsOnRewritingErrors()
|
||||||
{
|
{
|
||||||
|
|
@ -335,16 +336,14 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
{
|
{
|
||||||
builder.AddTagHelpers(new[]
|
builder.AddTagHelpers(new[]
|
||||||
{
|
{
|
||||||
new TagHelperDescriptor
|
CreateTagHelperDescriptor(
|
||||||
{
|
tagName: "form",
|
||||||
TagName = "form",
|
typeName: null,
|
||||||
AssemblyName= "TestAssembly",
|
assemblyName: "TestAssembly"),
|
||||||
},
|
CreateTagHelperDescriptor(
|
||||||
new TagHelperDescriptor
|
tagName: "input",
|
||||||
{
|
typeName: null,
|
||||||
TagName = "input",
|
assemblyName: "TestAssembly"),
|
||||||
AssemblyName= "TestAssembly",
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -634,12 +633,10 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return new TagHelperDescriptor
|
return CreateTagHelperDescriptor(
|
||||||
{
|
tagName: "valid_plain",
|
||||||
TagName = "valid_plain",
|
typeName: "Microsoft.AspNetCore.Razor.TagHelpers.ValidPlainTagHelper",
|
||||||
TypeName = "Microsoft.AspNetCore.Razor.TagHelpers.ValidPlainTagHelper",
|
assemblyName: AssemblyA);
|
||||||
AssemblyName = AssemblyA,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -647,48 +644,34 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return new TagHelperDescriptor
|
return CreateTagHelperDescriptor(
|
||||||
{
|
tagName: "valid_inherited",
|
||||||
TagName = "valid_inherited",
|
typeName: "Microsoft.AspNetCore.Razor.TagHelpers.ValidInheritedTagHelper",
|
||||||
TypeName = "Microsoft.AspNetCore.Razor.TagHelpers.ValidInheritedTagHelper",
|
assemblyName: AssemblyA);
|
||||||
AssemblyName = AssemblyA
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static TagHelperDescriptor[] AllTagHelpers => new[]
|
|
||||||
{
|
|
||||||
Valid_PlainTagHelperDescriptor,
|
|
||||||
Valid_InheritedTagHelperDescriptor,
|
|
||||||
String_TagHelperDescriptor
|
|
||||||
};
|
|
||||||
|
|
||||||
private static TagHelperDescriptor String_TagHelperDescriptor
|
private static TagHelperDescriptor String_TagHelperDescriptor
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
// We're treating 'string' as a TagHelper so we can test TagHelpers in multiple assemblies without
|
// We're treating 'string' as a TagHelper so we can test TagHelpers in multiple assemblies without
|
||||||
// building a separate assembly with a single TagHelper.
|
// building a separate assembly with a single TagHelper.
|
||||||
return new TagHelperDescriptor
|
return CreateTagHelperDescriptor(
|
||||||
{
|
tagName: "string",
|
||||||
TagName = "string",
|
typeName: "System.String",
|
||||||
TypeName = "System.String",
|
assemblyName: AssemblyB);
|
||||||
AssemblyName = AssemblyB,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static TheoryData ProcessDirectives_TagHelperPrefixData
|
public static TheoryData ProcessTagHelperPrefixData
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return new TheoryData<
|
// directiveDescriptors, expected prefix
|
||||||
IEnumerable<TagHelperDescriptor>, // tagHelpers
|
return new TheoryData<IEnumerable<TagHelperDirectiveDescriptor>, string>
|
||||||
IEnumerable<TagHelperDirectiveDescriptor>, // directiveDescriptors
|
|
||||||
IEnumerable<TagHelperDescriptor>> // expectedDescriptors
|
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
AllTagHelpers,
|
|
||||||
new []
|
new []
|
||||||
{
|
{
|
||||||
CreateTagHelperDirectiveDescriptor("", TagHelperDirectiveType.TagHelperPrefix),
|
CreateTagHelperDirectiveDescriptor("", TagHelperDirectiveType.TagHelperPrefix),
|
||||||
|
|
@ -696,10 +679,9 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
"Microsoft.AspNetCore.Razor.TagHelpers.ValidPlain*, " + AssemblyA,
|
"Microsoft.AspNetCore.Razor.TagHelpers.ValidPlain*, " + AssemblyA,
|
||||||
TagHelperDirectiveType.AddTagHelper),
|
TagHelperDirectiveType.AddTagHelper),
|
||||||
},
|
},
|
||||||
new [] { Valid_PlainTagHelperDescriptor }
|
null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
AllTagHelpers,
|
|
||||||
new []
|
new []
|
||||||
{
|
{
|
||||||
CreateTagHelperDirectiveDescriptor("th:", TagHelperDirectiveType.TagHelperPrefix),
|
CreateTagHelperDirectiveDescriptor("th:", TagHelperDirectiveType.TagHelperPrefix),
|
||||||
|
|
@ -707,23 +689,17 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
"Microsoft.AspNetCore.Razor.TagHelpers.ValidPlain*, " + AssemblyA,
|
"Microsoft.AspNetCore.Razor.TagHelpers.ValidPlain*, " + AssemblyA,
|
||||||
TagHelperDirectiveType.AddTagHelper),
|
TagHelperDirectiveType.AddTagHelper),
|
||||||
},
|
},
|
||||||
new [] { CreatePrefixedValidPlainDescriptor("th:") }
|
"th:"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
AllTagHelpers,
|
|
||||||
new []
|
new []
|
||||||
{
|
{
|
||||||
CreateTagHelperDirectiveDescriptor("*, " + AssemblyA, TagHelperDirectiveType.AddTagHelper),
|
CreateTagHelperDirectiveDescriptor("*, " + AssemblyA, TagHelperDirectiveType.AddTagHelper),
|
||||||
CreateTagHelperDirectiveDescriptor("th:", TagHelperDirectiveType.TagHelperPrefix)
|
CreateTagHelperDirectiveDescriptor("th:", TagHelperDirectiveType.TagHelperPrefix)
|
||||||
},
|
},
|
||||||
new []
|
"th:"
|
||||||
{
|
|
||||||
CreatePrefixedValidPlainDescriptor("th:"),
|
|
||||||
CreatePrefixedValidInheritedDescriptor("th:")
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
AllTagHelpers,
|
|
||||||
new []
|
new []
|
||||||
{
|
{
|
||||||
CreateTagHelperDirectiveDescriptor("th-", TagHelperDirectiveType.TagHelperPrefix),
|
CreateTagHelperDirectiveDescriptor("th-", TagHelperDirectiveType.TagHelperPrefix),
|
||||||
|
|
@ -734,14 +710,9 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
"Microsoft.AspNetCore.Razor.TagHelpers.ValidInherited*, " + AssemblyA,
|
"Microsoft.AspNetCore.Razor.TagHelpers.ValidInherited*, " + AssemblyA,
|
||||||
TagHelperDirectiveType.AddTagHelper)
|
TagHelperDirectiveType.AddTagHelper)
|
||||||
},
|
},
|
||||||
new []
|
"th-"
|
||||||
{
|
|
||||||
CreatePrefixedValidPlainDescriptor("th-"),
|
|
||||||
CreatePrefixedValidInheritedDescriptor("th-")
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
AllTagHelpers,
|
|
||||||
new []
|
new []
|
||||||
{
|
{
|
||||||
CreateTagHelperDirectiveDescriptor("", TagHelperDirectiveType.TagHelperPrefix),
|
CreateTagHelperDirectiveDescriptor("", TagHelperDirectiveType.TagHelperPrefix),
|
||||||
|
|
@ -752,10 +723,9 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
"Microsoft.AspNetCore.Razor.TagHelpers.ValidInherited*, " + AssemblyA,
|
"Microsoft.AspNetCore.Razor.TagHelpers.ValidInherited*, " + AssemblyA,
|
||||||
TagHelperDirectiveType.AddTagHelper)
|
TagHelperDirectiveType.AddTagHelper)
|
||||||
},
|
},
|
||||||
new [] { Valid_PlainTagHelperDescriptor, Valid_InheritedTagHelperDescriptor }
|
null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
AllTagHelpers,
|
|
||||||
new []
|
new []
|
||||||
{
|
{
|
||||||
CreateTagHelperDirectiveDescriptor("th", TagHelperDirectiveType.TagHelperPrefix),
|
CreateTagHelperDirectiveDescriptor("th", TagHelperDirectiveType.TagHelperPrefix),
|
||||||
|
|
@ -766,15 +736,9 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
"*, " + AssemblyB,
|
"*, " + AssemblyB,
|
||||||
TagHelperDirectiveType.AddTagHelper),
|
TagHelperDirectiveType.AddTagHelper),
|
||||||
},
|
},
|
||||||
new []
|
"th"
|
||||||
{
|
|
||||||
CreatePrefixedValidPlainDescriptor("th"),
|
|
||||||
CreatePrefixedValidInheritedDescriptor("th"),
|
|
||||||
CreatePrefixedStringDescriptor("th")
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
AllTagHelpers,
|
|
||||||
new []
|
new []
|
||||||
{
|
{
|
||||||
CreateTagHelperDirectiveDescriptor(
|
CreateTagHelperDirectiveDescriptor(
|
||||||
|
|
@ -785,44 +749,30 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
"*, " + AssemblyB,
|
"*, " + AssemblyB,
|
||||||
TagHelperDirectiveType.AddTagHelper),
|
TagHelperDirectiveType.AddTagHelper),
|
||||||
},
|
},
|
||||||
new []
|
"th:-"
|
||||||
{
|
|
||||||
CreatePrefixedValidPlainDescriptor("th:-"),
|
|
||||||
CreatePrefixedValidInheritedDescriptor("th:-"),
|
|
||||||
CreatePrefixedStringDescriptor("th:-")
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[MemberData(nameof(ProcessDirectives_TagHelperPrefixData))]
|
[MemberData(nameof(ProcessTagHelperPrefixData))]
|
||||||
public void ProcessDirectives_AppliesDirectives_WithTagHelperPrefix(
|
public void ProcessTagHelperPrefix_ParsesPrefixFromDirectives_SetsOnCodeDocument(
|
||||||
object tagHelpers,
|
|
||||||
object directiveDescriptors,
|
object directiveDescriptors,
|
||||||
object expectedDescriptors)
|
string expectedPrefix)
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var errorSink = new ErrorSink();
|
var errorSink = new ErrorSink();
|
||||||
var pass = new TagHelperBinderSyntaxTreePass();
|
var pass = new TagHelperBinderSyntaxTreePass();
|
||||||
|
var document = RazorCodeDocument.Create(new DefaultRazorSourceDocument("Test content", encoding: Encoding.UTF8, fileName: "TestFile"));
|
||||||
var expected = (IEnumerable<TagHelperDescriptor>)expectedDescriptors;
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var results = pass.ProcessDirectives(
|
var prefix = pass.ProcessTagHelperPrefix(((IEnumerable<TagHelperDirectiveDescriptor>)directiveDescriptors).ToList(), document, errorSink);
|
||||||
((IEnumerable<TagHelperDirectiveDescriptor>)directiveDescriptors).ToArray(),
|
|
||||||
((IEnumerable<TagHelperDescriptor>)tagHelpers).ToArray(),
|
|
||||||
errorSink);
|
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Empty(errorSink.Errors);
|
Assert.Empty(errorSink.Errors);
|
||||||
Assert.Equal(expected.Count(), results.Count());
|
Assert.Equal(expectedPrefix, prefix);
|
||||||
|
Assert.Equal(expectedPrefix, document.GetTagHelperPrefix());
|
||||||
foreach (var expectedDescriptor in expected)
|
|
||||||
{
|
|
||||||
Assert.Contains(expectedDescriptor, results, TagHelperDescriptorComparer.Default);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static TheoryData ProcessDirectivesData
|
public static TheoryData ProcessDirectivesData
|
||||||
|
|
@ -874,7 +824,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
new []
|
new []
|
||||||
{
|
{
|
||||||
CreateTagHelperDirectiveDescriptor(
|
CreateTagHelperDirectiveDescriptor(
|
||||||
Valid_PlainTagHelperDescriptor.TypeName + ", " + AssemblyA,
|
Valid_PlainTagHelperDescriptor.Name + ", " + AssemblyA,
|
||||||
TagHelperDirectiveType.AddTagHelper),
|
TagHelperDirectiveType.AddTagHelper),
|
||||||
CreateTagHelperDirectiveDescriptor("*, " + AssemblyA, TagHelperDirectiveType.AddTagHelper)
|
CreateTagHelperDirectiveDescriptor("*, " + AssemblyA, TagHelperDirectiveType.AddTagHelper)
|
||||||
},
|
},
|
||||||
|
|
@ -886,7 +836,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
{
|
{
|
||||||
CreateTagHelperDirectiveDescriptor("*, " + AssemblyA, TagHelperDirectiveType.AddTagHelper),
|
CreateTagHelperDirectiveDescriptor("*, " + AssemblyA, TagHelperDirectiveType.AddTagHelper),
|
||||||
CreateTagHelperDirectiveDescriptor(
|
CreateTagHelperDirectiveDescriptor(
|
||||||
Valid_PlainTagHelperDescriptor.TypeName + ", " + AssemblyA,
|
Valid_PlainTagHelperDescriptor.Name + ", " + AssemblyA,
|
||||||
TagHelperDirectiveType.RemoveTagHelper)
|
TagHelperDirectiveType.RemoveTagHelper)
|
||||||
},
|
},
|
||||||
new [] { Valid_InheritedTagHelperDescriptor }
|
new [] { Valid_InheritedTagHelperDescriptor }
|
||||||
|
|
@ -897,7 +847,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
{
|
{
|
||||||
CreateTagHelperDirectiveDescriptor("*, " + AssemblyA, TagHelperDirectiveType.AddTagHelper),
|
CreateTagHelperDirectiveDescriptor("*, " + AssemblyA, TagHelperDirectiveType.AddTagHelper),
|
||||||
CreateTagHelperDirectiveDescriptor(
|
CreateTagHelperDirectiveDescriptor(
|
||||||
Valid_PlainTagHelperDescriptor.TypeName + ", " + AssemblyA,
|
Valid_PlainTagHelperDescriptor.Name + ", " + AssemblyA,
|
||||||
TagHelperDirectiveType.RemoveTagHelper),
|
TagHelperDirectiveType.RemoveTagHelper),
|
||||||
CreateTagHelperDirectiveDescriptor("*, " + AssemblyA, TagHelperDirectiveType.AddTagHelper)
|
CreateTagHelperDirectiveDescriptor("*, " + AssemblyA, TagHelperDirectiveType.AddTagHelper)
|
||||||
},
|
},
|
||||||
|
|
@ -967,7 +917,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
new []
|
new []
|
||||||
{
|
{
|
||||||
CreateTagHelperDirectiveDescriptor("*, " + AssemblyA, TagHelperDirectiveType.AddTagHelper),
|
CreateTagHelperDirectiveDescriptor("*, " + AssemblyA, TagHelperDirectiveType.AddTagHelper),
|
||||||
CreateTagHelperDirectiveDescriptor("System." + String_TagHelperDescriptor.TypeName + ", " + AssemblyB, TagHelperDirectiveType.AddTagHelper)
|
CreateTagHelperDirectiveDescriptor("System." + String_TagHelperDescriptor.Name + ", " + AssemblyB, TagHelperDirectiveType.AddTagHelper)
|
||||||
},
|
},
|
||||||
new [] { Valid_PlainTagHelperDescriptor }
|
new [] { Valid_PlainTagHelperDescriptor }
|
||||||
},
|
},
|
||||||
|
|
@ -992,7 +942,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
CreateTagHelperDirectiveDescriptor(
|
CreateTagHelperDirectiveDescriptor(
|
||||||
"?Microsoft*, " + AssemblyA, TagHelperDirectiveType.RemoveTagHelper),
|
"?Microsoft*, " + AssemblyA, TagHelperDirectiveType.RemoveTagHelper),
|
||||||
CreateTagHelperDirectiveDescriptor(
|
CreateTagHelperDirectiveDescriptor(
|
||||||
"System." + String_TagHelperDescriptor.TypeName + ", " + AssemblyB, TagHelperDirectiveType.RemoveTagHelper)
|
"System." + String_TagHelperDescriptor.Name + ", " + AssemblyB, TagHelperDirectiveType.RemoveTagHelper)
|
||||||
},
|
},
|
||||||
new []
|
new []
|
||||||
{
|
{
|
||||||
|
|
@ -1012,7 +962,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
CreateTagHelperDirectiveDescriptor(
|
CreateTagHelperDirectiveDescriptor(
|
||||||
"TagHelper*, " + AssemblyA, TagHelperDirectiveType.RemoveTagHelper),
|
"TagHelper*, " + AssemblyA, TagHelperDirectiveType.RemoveTagHelper),
|
||||||
CreateTagHelperDirectiveDescriptor(
|
CreateTagHelperDirectiveDescriptor(
|
||||||
"System." + String_TagHelperDescriptor.TypeName + ", " + AssemblyB, TagHelperDirectiveType.RemoveTagHelper)
|
"System." + String_TagHelperDescriptor.Name + ", " + AssemblyB, TagHelperDirectiveType.RemoveTagHelper)
|
||||||
},
|
},
|
||||||
new []
|
new []
|
||||||
{
|
{
|
||||||
|
|
@ -1081,8 +1031,8 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
new []
|
new []
|
||||||
{
|
{
|
||||||
CreateTagHelperDirectiveDescriptor("*, " + AssemblyA, TagHelperDirectiveType.AddTagHelper),
|
CreateTagHelperDirectiveDescriptor("*, " + AssemblyA, TagHelperDirectiveType.AddTagHelper),
|
||||||
CreateTagHelperDirectiveDescriptor(Valid_PlainTagHelperDescriptor.TypeName + ", " + AssemblyA, TagHelperDirectiveType.RemoveTagHelper),
|
CreateTagHelperDirectiveDescriptor(Valid_PlainTagHelperDescriptor.Name + ", " + AssemblyA, TagHelperDirectiveType.RemoveTagHelper),
|
||||||
CreateTagHelperDirectiveDescriptor(Valid_InheritedTagHelperDescriptor.TypeName + ", " + AssemblyA, TagHelperDirectiveType.RemoveTagHelper),
|
CreateTagHelperDirectiveDescriptor(Valid_InheritedTagHelperDescriptor.Name + ", " + AssemblyA, TagHelperDirectiveType.RemoveTagHelper),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -1111,9 +1061,9 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
{
|
{
|
||||||
CreateTagHelperDirectiveDescriptor("*, " + AssemblyA, TagHelperDirectiveType.AddTagHelper),
|
CreateTagHelperDirectiveDescriptor("*, " + AssemblyA, TagHelperDirectiveType.AddTagHelper),
|
||||||
CreateTagHelperDirectiveDescriptor("*, " + AssemblyB, TagHelperDirectiveType.AddTagHelper),
|
CreateTagHelperDirectiveDescriptor("*, " + AssemblyB, TagHelperDirectiveType.AddTagHelper),
|
||||||
CreateTagHelperDirectiveDescriptor(Valid_PlainTagHelperDescriptor.TypeName + ", " + AssemblyA, TagHelperDirectiveType.RemoveTagHelper),
|
CreateTagHelperDirectiveDescriptor(Valid_PlainTagHelperDescriptor.Name + ", " + AssemblyA, TagHelperDirectiveType.RemoveTagHelper),
|
||||||
CreateTagHelperDirectiveDescriptor(Valid_InheritedTagHelperDescriptor.TypeName + ", " + AssemblyA, TagHelperDirectiveType.RemoveTagHelper),
|
CreateTagHelperDirectiveDescriptor(Valid_InheritedTagHelperDescriptor.Name + ", " + AssemblyA, TagHelperDirectiveType.RemoveTagHelper),
|
||||||
CreateTagHelperDirectiveDescriptor(String_TagHelperDescriptor.TypeName + ", " + AssemblyB, TagHelperDirectiveType.RemoveTagHelper)
|
CreateTagHelperDirectiveDescriptor(String_TagHelperDescriptor.Name + ", " + AssemblyB, TagHelperDirectiveType.RemoveTagHelper)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -1121,7 +1071,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
new []
|
new []
|
||||||
{
|
{
|
||||||
CreateTagHelperDirectiveDescriptor("*, " + AssemblyA, TagHelperDirectiveType.RemoveTagHelper),
|
CreateTagHelperDirectiveDescriptor("*, " + AssemblyA, TagHelperDirectiveType.RemoveTagHelper),
|
||||||
CreateTagHelperDirectiveDescriptor(Valid_PlainTagHelperDescriptor.TypeName + ", " + AssemblyA, TagHelperDirectiveType.RemoveTagHelper),
|
CreateTagHelperDirectiveDescriptor(Valid_PlainTagHelperDescriptor.Name + ", " + AssemblyA, TagHelperDirectiveType.RemoveTagHelper),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -1143,8 +1093,8 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
new []
|
new []
|
||||||
{
|
{
|
||||||
CreateTagHelperDirectiveDescriptor("Mic*, " + AssemblyA, TagHelperDirectiveType.AddTagHelper),
|
CreateTagHelperDirectiveDescriptor("Mic*, " + AssemblyA, TagHelperDirectiveType.AddTagHelper),
|
||||||
CreateTagHelperDirectiveDescriptor(Valid_PlainTagHelperDescriptor.TypeName + ", " + AssemblyA, TagHelperDirectiveType.RemoveTagHelper),
|
CreateTagHelperDirectiveDescriptor(Valid_PlainTagHelperDescriptor.Name + ", " + AssemblyA, TagHelperDirectiveType.RemoveTagHelper),
|
||||||
CreateTagHelperDirectiveDescriptor(Valid_InheritedTagHelperDescriptor.TypeName + ", " + AssemblyA, TagHelperDirectiveType.RemoveTagHelper)
|
CreateTagHelperDirectiveDescriptor(Valid_InheritedTagHelperDescriptor.Name + ", " + AssemblyA, TagHelperDirectiveType.RemoveTagHelper)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -1220,7 +1170,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
var assemblyName = Valid_PlainTagHelperDescriptor.AssemblyName;
|
var assemblyName = Valid_PlainTagHelperDescriptor.AssemblyName;
|
||||||
var typeName = Valid_PlainTagHelperDescriptor.TypeName;
|
var typeName = Valid_PlainTagHelperDescriptor.Name;
|
||||||
return new TheoryData<string>
|
return new TheoryData<string>
|
||||||
{
|
{
|
||||||
$"{typeName},{assemblyName}",
|
$"{typeName},{assemblyName}",
|
||||||
|
|
@ -1246,12 +1196,12 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
|
|
||||||
var directives = new[]
|
var directives = new[]
|
||||||
{
|
{
|
||||||
new TagHelperDirectiveDescriptor()
|
new TagHelperDirectiveDescriptor()
|
||||||
{
|
{
|
||||||
DirectiveText = directiveText,
|
DirectiveText = directiveText,
|
||||||
DirectiveType = TagHelperDirectiveType.AddTagHelper,
|
DirectiveType = TagHelperDirectiveType.AddTagHelper,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var results = pass.ProcessDirectives(
|
var results = pass.ProcessDirectives(
|
||||||
|
|
@ -1311,26 +1261,29 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
|
|
||||||
private static TagHelperDescriptor CreatePrefixedValidPlainDescriptor(string prefix)
|
private static TagHelperDescriptor CreatePrefixedValidPlainDescriptor(string prefix)
|
||||||
{
|
{
|
||||||
return new TagHelperDescriptor(Valid_PlainTagHelperDescriptor)
|
return Valid_PlainTagHelperDescriptor;
|
||||||
{
|
//return new TagHelperDescriptor(Valid_PlainTagHelperDescriptor)
|
||||||
Prefix = prefix,
|
//{
|
||||||
};
|
// Prefix = prefix,
|
||||||
|
//};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static TagHelperDescriptor CreatePrefixedValidInheritedDescriptor(string prefix)
|
private static TagHelperDescriptor CreatePrefixedValidInheritedDescriptor(string prefix)
|
||||||
{
|
{
|
||||||
return new TagHelperDescriptor(Valid_InheritedTagHelperDescriptor)
|
return Valid_InheritedTagHelperDescriptor;
|
||||||
{
|
//return new TagHelperDescriptor(Valid_InheritedTagHelperDescriptor)
|
||||||
Prefix = prefix,
|
//{
|
||||||
};
|
// Prefix = prefix,
|
||||||
|
//};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static TagHelperDescriptor CreatePrefixedStringDescriptor(string prefix)
|
private static TagHelperDescriptor CreatePrefixedStringDescriptor(string prefix)
|
||||||
{
|
{
|
||||||
return new TagHelperDescriptor(String_TagHelperDescriptor)
|
return String_TagHelperDescriptor;
|
||||||
{
|
//return new TagHelperDescriptor(String_TagHelperDescriptor)
|
||||||
Prefix = prefix,
|
//{
|
||||||
};
|
// Prefix = prefix,
|
||||||
|
//};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static TagHelperDirectiveDescriptor CreateTagHelperDirectiveDescriptor(
|
private static TagHelperDirectiveDescriptor CreateTagHelperDirectiveDescriptor(
|
||||||
|
|
@ -1357,20 +1310,62 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
return sourceDocument;
|
return sourceDocument;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static TagHelperDescriptor CreateTagHelperDescriptor(
|
||||||
|
string tagName,
|
||||||
|
string typeName,
|
||||||
|
string assemblyName,
|
||||||
|
IEnumerable<Action<ITagHelperBoundAttributeDescriptorBuilder>> attributes = null,
|
||||||
|
IEnumerable<Action<TagMatchingRuleBuilder>> ruleBuilders = null)
|
||||||
|
{
|
||||||
|
var builder = ITagHelperDescriptorBuilder.Create(typeName, assemblyName);
|
||||||
|
|
||||||
|
if (attributes != null)
|
||||||
|
{
|
||||||
|
foreach (var attributeBuilder in attributes)
|
||||||
|
{
|
||||||
|
builder.BindAttribute(attributeBuilder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ruleBuilders != null)
|
||||||
|
{
|
||||||
|
foreach (var ruleBuilder in ruleBuilders)
|
||||||
|
{
|
||||||
|
builder.TagMatchingRule(innerRuleBuilder => {
|
||||||
|
innerRuleBuilder.RequireTagName(tagName);
|
||||||
|
ruleBuilder(innerRuleBuilder);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
builder.TagMatchingRule(ruleBuilder => ruleBuilder.RequireTagName(tagName));
|
||||||
|
}
|
||||||
|
|
||||||
|
var descriptor = builder.Build();
|
||||||
|
|
||||||
|
return descriptor;
|
||||||
|
}
|
||||||
|
|
||||||
private class ErrorLoggingTagHelperDescriptorResolver : ITagHelperDescriptorResolver
|
private class ErrorLoggingTagHelperDescriptorResolver : ITagHelperDescriptorResolver
|
||||||
{
|
{
|
||||||
private readonly RazorDiagnostic _error;
|
private readonly RazorDiagnostic _error;
|
||||||
|
private readonly string _tagName;
|
||||||
|
|
||||||
public ErrorLoggingTagHelperDescriptorResolver(RazorDiagnostic error)
|
public ErrorLoggingTagHelperDescriptorResolver(RazorDiagnostic error, string tagName = null)
|
||||||
{
|
{
|
||||||
_error = error;
|
_error = error;
|
||||||
|
_tagName = tagName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<TagHelperDescriptor> Resolve(IList<RazorDiagnostic> errors)
|
public IEnumerable<TagHelperDescriptor> Resolve(IList<RazorDiagnostic> errors)
|
||||||
{
|
{
|
||||||
errors.Add(_error);
|
errors.Add(_error);
|
||||||
|
|
||||||
return new[] { new TagHelperDescriptor() { AssemblyName = "TestAssembly" } };
|
return new[] { CreateTagHelperDescriptor(
|
||||||
|
tagName: _tagName,
|
||||||
|
typeName: null,
|
||||||
|
assemblyName: "TestAssembly") };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,557 +0,0 @@
|
||||||
// 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;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
using Xunit;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Razor.Evolution
|
|
||||||
{
|
|
||||||
public class TagHelperDescriptorTest
|
|
||||||
{
|
|
||||||
[Fact]
|
|
||||||
public void Constructor_CorrectlyCreatesCopy()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var descriptor = new TagHelperDescriptor
|
|
||||||
{
|
|
||||||
Prefix = "prefix",
|
|
||||||
TagName = "tag-name",
|
|
||||||
TypeName = "TypeName",
|
|
||||||
AssemblyName = "AsssemblyName",
|
|
||||||
Attributes = new List<TagHelperAttributeDescriptor>
|
|
||||||
{
|
|
||||||
new TagHelperAttributeDescriptor
|
|
||||||
{
|
|
||||||
Name = "test-attribute",
|
|
||||||
PropertyName = "TestAttribute",
|
|
||||||
TypeName = "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
RequiredAttributes = new List<TagHelperRequiredAttributeDescriptor>
|
|
||||||
{
|
|
||||||
new TagHelperRequiredAttributeDescriptor
|
|
||||||
{
|
|
||||||
Name = "test-required-attribute"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
AllowedChildren = new[] { "child" },
|
|
||||||
RequiredParent = "required parent",
|
|
||||||
TagStructure = TagStructure.NormalOrSelfClosing,
|
|
||||||
DesignTimeDescriptor = new TagHelperDesignTimeDescriptor()
|
|
||||||
};
|
|
||||||
|
|
||||||
descriptor.PropertyBag.Add("foo", "bar");
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var copyDescriptor = new TagHelperDescriptor(descriptor);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Equal(descriptor, copyDescriptor, CaseSensitiveTagHelperDescriptorComparer.Default);
|
|
||||||
Assert.Same(descriptor.Attributes, copyDescriptor.Attributes);
|
|
||||||
Assert.Same(descriptor.RequiredAttributes, copyDescriptor.RequiredAttributes);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void TagHelperDescriptor_CanBeSerialized()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var descriptor = new TagHelperDescriptor
|
|
||||||
{
|
|
||||||
Prefix = "prefix:",
|
|
||||||
TagName = "tag name",
|
|
||||||
TypeName = "type name",
|
|
||||||
AssemblyName = "assembly name",
|
|
||||||
RequiredAttributes = new[]
|
|
||||||
{
|
|
||||||
new TagHelperRequiredAttributeDescriptor
|
|
||||||
{
|
|
||||||
Name = "required attribute one",
|
|
||||||
NameComparison = TagHelperRequiredAttributeNameComparison.PrefixMatch
|
|
||||||
},
|
|
||||||
new TagHelperRequiredAttributeDescriptor
|
|
||||||
{
|
|
||||||
Name = "required attribute two",
|
|
||||||
NameComparison = TagHelperRequiredAttributeNameComparison.FullMatch,
|
|
||||||
Value = "something",
|
|
||||||
ValueComparison = TagHelperRequiredAttributeValueComparison.PrefixMatch,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
AllowedChildren = new[] { "allowed child one" },
|
|
||||||
RequiredParent = "parent name",
|
|
||||||
DesignTimeDescriptor = new TagHelperDesignTimeDescriptor
|
|
||||||
{
|
|
||||||
Summary = "usage summary",
|
|
||||||
Remarks = "usage remarks",
|
|
||||||
OutputElementHint = "some-tag"
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
var expectedSerializedDescriptor =
|
|
||||||
$"{{\"{ nameof(TagHelperDescriptor.Prefix) }\":\"prefix:\"," +
|
|
||||||
$"\"{ nameof(TagHelperDescriptor.TagName) }\":\"tag name\"," +
|
|
||||||
$"\"{ nameof(TagHelperDescriptor.FullTagName) }\":\"prefix:tag name\"," +
|
|
||||||
$"\"{ nameof(TagHelperDescriptor.TypeName) }\":\"type name\"," +
|
|
||||||
$"\"{ nameof(TagHelperDescriptor.AssemblyName) }\":\"assembly name\"," +
|
|
||||||
$"\"{ nameof(TagHelperDescriptor.Attributes) }\":[]," +
|
|
||||||
$"\"{ nameof(TagHelperDescriptor.RequiredAttributes) }\":" +
|
|
||||||
$"[{{\"{ nameof(TagHelperRequiredAttributeDescriptor.Name)}\":\"required attribute one\"," +
|
|
||||||
$"\"{ nameof(TagHelperRequiredAttributeDescriptor.NameComparison) }\":1," +
|
|
||||||
$"\"{ nameof(TagHelperRequiredAttributeDescriptor.Value) }\":null," +
|
|
||||||
$"\"{ nameof(TagHelperRequiredAttributeDescriptor.ValueComparison) }\":0}}," +
|
|
||||||
$"{{\"{ nameof(TagHelperRequiredAttributeDescriptor.Name)}\":\"required attribute two\"," +
|
|
||||||
$"\"{ nameof(TagHelperRequiredAttributeDescriptor.NameComparison) }\":0," +
|
|
||||||
$"\"{ nameof(TagHelperRequiredAttributeDescriptor.Value) }\":\"something\"," +
|
|
||||||
$"\"{ nameof(TagHelperRequiredAttributeDescriptor.ValueComparison) }\":2}}]," +
|
|
||||||
$"\"{ nameof(TagHelperDescriptor.AllowedChildren) }\":[\"allowed child one\"]," +
|
|
||||||
$"\"{ nameof(TagHelperDescriptor.RequiredParent) }\":\"parent name\"," +
|
|
||||||
$"\"{ nameof(TagHelperDescriptor.TagStructure) }\":0," +
|
|
||||||
$"\"{ nameof(TagHelperDescriptor.DesignTimeDescriptor) }\":{{" +
|
|
||||||
$"\"{ nameof(TagHelperDesignTimeDescriptor.Summary) }\":\"usage summary\"," +
|
|
||||||
$"\"{ nameof(TagHelperDesignTimeDescriptor.Remarks) }\":\"usage remarks\"," +
|
|
||||||
$"\"{ nameof(TagHelperDesignTimeDescriptor.OutputElementHint) }\":\"some-tag\"}}," +
|
|
||||||
$"\"{ nameof(TagHelperDescriptor.PropertyBag) }\":{{}}}}";
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var serializedDescriptor = JsonConvert.SerializeObject(descriptor);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Equal(expectedSerializedDescriptor, serializedDescriptor, StringComparer.Ordinal);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void TagHelperDescriptor_WithAttributes_CanBeSerialized()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var descriptor = new TagHelperDescriptor
|
|
||||||
{
|
|
||||||
Prefix = "prefix:",
|
|
||||||
TagName = "tag name",
|
|
||||||
TypeName = "type name",
|
|
||||||
AssemblyName = "assembly name",
|
|
||||||
Attributes = new[]
|
|
||||||
{
|
|
||||||
new TagHelperAttributeDescriptor
|
|
||||||
{
|
|
||||||
Name = "attribute one",
|
|
||||||
PropertyName = "property name",
|
|
||||||
TypeName = "property type name",
|
|
||||||
IsEnum = true,
|
|
||||||
},
|
|
||||||
new TagHelperAttributeDescriptor
|
|
||||||
{
|
|
||||||
Name = "attribute two",
|
|
||||||
PropertyName = "property name",
|
|
||||||
TypeName = typeof(string).FullName,
|
|
||||||
IsStringProperty = true
|
|
||||||
},
|
|
||||||
},
|
|
||||||
TagStructure = TagStructure.NormalOrSelfClosing
|
|
||||||
};
|
|
||||||
|
|
||||||
var expectedSerializedDescriptor =
|
|
||||||
$"{{\"{ nameof(TagHelperDescriptor.Prefix) }\":\"prefix:\"," +
|
|
||||||
$"\"{ nameof(TagHelperDescriptor.TagName) }\":\"tag name\"," +
|
|
||||||
$"\"{ nameof(TagHelperDescriptor.FullTagName) }\":\"prefix:tag name\"," +
|
|
||||||
$"\"{ nameof(TagHelperDescriptor.TypeName) }\":\"type name\"," +
|
|
||||||
$"\"{ nameof(TagHelperDescriptor.AssemblyName) }\":\"assembly name\"," +
|
|
||||||
$"\"{ nameof(TagHelperDescriptor.Attributes) }\":[" +
|
|
||||||
$"{{\"{ nameof(TagHelperAttributeDescriptor.IsIndexer) }\":false," +
|
|
||||||
$"\"{ nameof(TagHelperAttributeDescriptor.IsEnum) }\":true," +
|
|
||||||
$"\"{ nameof(TagHelperAttributeDescriptor.IsStringProperty) }\":false," +
|
|
||||||
$"\"{ nameof(TagHelperAttributeDescriptor.Name) }\":\"attribute one\"," +
|
|
||||||
$"\"{ nameof(TagHelperAttributeDescriptor.PropertyName) }\":\"property name\"," +
|
|
||||||
$"\"{ nameof(TagHelperAttributeDescriptor.TypeName) }\":\"property type name\"," +
|
|
||||||
$"\"{ nameof(TagHelperAttributeDescriptor.DesignTimeDescriptor) }\":null}}," +
|
|
||||||
$"{{\"{ nameof(TagHelperAttributeDescriptor.IsIndexer) }\":false," +
|
|
||||||
$"\"{ nameof(TagHelperAttributeDescriptor.IsEnum) }\":false," +
|
|
||||||
$"\"{ nameof(TagHelperAttributeDescriptor.IsStringProperty) }\":true," +
|
|
||||||
$"\"{ nameof(TagHelperAttributeDescriptor.Name) }\":\"attribute two\"," +
|
|
||||||
$"\"{ nameof(TagHelperAttributeDescriptor.PropertyName) }\":\"property name\"," +
|
|
||||||
$"\"{ nameof(TagHelperAttributeDescriptor.TypeName) }\":\"{ typeof(string).FullName }\"," +
|
|
||||||
$"\"{ nameof(TagHelperAttributeDescriptor.DesignTimeDescriptor) }\":null}}]," +
|
|
||||||
$"\"{ nameof(TagHelperDescriptor.RequiredAttributes) }\":[]," +
|
|
||||||
$"\"{ nameof(TagHelperDescriptor.AllowedChildren) }\":null," +
|
|
||||||
$"\"{ nameof(TagHelperDescriptor.RequiredParent) }\":null," +
|
|
||||||
$"\"{ nameof(TagHelperDescriptor.TagStructure) }\":1," +
|
|
||||||
$"\"{ nameof(TagHelperDescriptor.DesignTimeDescriptor) }\":null," +
|
|
||||||
$"\"{ nameof(TagHelperDescriptor.PropertyBag) }\":{{}}}}";
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var serializedDescriptor = JsonConvert.SerializeObject(descriptor);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Equal(expectedSerializedDescriptor, serializedDescriptor, StringComparer.Ordinal);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void TagHelperDescriptor_WithIndexerAttributes_CanBeSerialized()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var descriptor = new TagHelperDescriptor
|
|
||||||
{
|
|
||||||
Prefix = "prefix:",
|
|
||||||
TagName = "tag name",
|
|
||||||
TypeName = "type name",
|
|
||||||
AssemblyName = "assembly name",
|
|
||||||
Attributes = new[]
|
|
||||||
{
|
|
||||||
new TagHelperAttributeDescriptor
|
|
||||||
{
|
|
||||||
Name = "attribute one",
|
|
||||||
PropertyName = "property name",
|
|
||||||
TypeName = "property type name",
|
|
||||||
IsIndexer = true,
|
|
||||||
IsEnum = true,
|
|
||||||
},
|
|
||||||
new TagHelperAttributeDescriptor
|
|
||||||
{
|
|
||||||
Name = "attribute two",
|
|
||||||
PropertyName = "property name",
|
|
||||||
TypeName = typeof(string).FullName,
|
|
||||||
IsIndexer = true,
|
|
||||||
IsEnum = false,
|
|
||||||
IsStringProperty = true
|
|
||||||
},
|
|
||||||
},
|
|
||||||
AllowedChildren = new[] { "allowed child one", "allowed child two" },
|
|
||||||
RequiredParent = "parent name"
|
|
||||||
};
|
|
||||||
|
|
||||||
var expectedSerializedDescriptor =
|
|
||||||
$"{{\"{ nameof(TagHelperDescriptor.Prefix) }\":\"prefix:\"," +
|
|
||||||
$"\"{ nameof(TagHelperDescriptor.TagName) }\":\"tag name\"," +
|
|
||||||
$"\"{ nameof(TagHelperDescriptor.FullTagName) }\":\"prefix:tag name\"," +
|
|
||||||
$"\"{ nameof(TagHelperDescriptor.TypeName) }\":\"type name\"," +
|
|
||||||
$"\"{ nameof(TagHelperDescriptor.AssemblyName) }\":\"assembly name\"," +
|
|
||||||
$"\"{ nameof(TagHelperDescriptor.Attributes) }\":[" +
|
|
||||||
$"{{\"{ nameof(TagHelperAttributeDescriptor.IsIndexer) }\":true," +
|
|
||||||
$"\"{ nameof(TagHelperAttributeDescriptor.IsEnum) }\":true," +
|
|
||||||
$"\"{ nameof(TagHelperAttributeDescriptor.IsStringProperty) }\":false," +
|
|
||||||
$"\"{ nameof(TagHelperAttributeDescriptor.Name) }\":\"attribute one\"," +
|
|
||||||
$"\"{ nameof(TagHelperAttributeDescriptor.PropertyName) }\":\"property name\"," +
|
|
||||||
$"\"{ nameof(TagHelperAttributeDescriptor.TypeName) }\":\"property type name\"," +
|
|
||||||
$"\"{ nameof(TagHelperAttributeDescriptor.DesignTimeDescriptor) }\":null}}," +
|
|
||||||
$"{{\"{ nameof(TagHelperAttributeDescriptor.IsIndexer) }\":true," +
|
|
||||||
$"\"{ nameof(TagHelperAttributeDescriptor.IsEnum) }\":false," +
|
|
||||||
$"\"{ nameof(TagHelperAttributeDescriptor.IsStringProperty) }\":true," +
|
|
||||||
$"\"{ nameof(TagHelperAttributeDescriptor.Name) }\":\"attribute two\"," +
|
|
||||||
$"\"{ nameof(TagHelperAttributeDescriptor.PropertyName) }\":\"property name\"," +
|
|
||||||
$"\"{ nameof(TagHelperAttributeDescriptor.TypeName) }\":\"{ typeof(string).FullName }\"," +
|
|
||||||
$"\"{ nameof(TagHelperAttributeDescriptor.DesignTimeDescriptor) }\":null}}]," +
|
|
||||||
$"\"{ nameof(TagHelperDescriptor.RequiredAttributes) }\":[]," +
|
|
||||||
$"\"{ nameof(TagHelperDescriptor.AllowedChildren) }\":[\"allowed child one\",\"allowed child two\"]," +
|
|
||||||
$"\"{ nameof(TagHelperDescriptor.RequiredParent) }\":\"parent name\"," +
|
|
||||||
$"\"{ nameof(TagHelperDescriptor.TagStructure) }\":0," +
|
|
||||||
$"\"{ nameof(TagHelperDescriptor.DesignTimeDescriptor) }\":null," +
|
|
||||||
$"\"{ nameof(TagHelperDescriptor.PropertyBag) }\":{{}}}}";
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var serializedDescriptor = JsonConvert.SerializeObject(descriptor);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Equal(expectedSerializedDescriptor, serializedDescriptor, StringComparer.Ordinal);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void TagHelperDescriptor_WithPropertyBagElements_CanBeSerialized()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var descriptor = new TagHelperDescriptor
|
|
||||||
{
|
|
||||||
Prefix = "prefix:",
|
|
||||||
TagName = "tag name",
|
|
||||||
TypeName = "type name",
|
|
||||||
AssemblyName = "assembly name"
|
|
||||||
};
|
|
||||||
|
|
||||||
descriptor.PropertyBag.Add("key one", "value one");
|
|
||||||
descriptor.PropertyBag.Add("key two", "value two");
|
|
||||||
|
|
||||||
var expectedSerializedDescriptor =
|
|
||||||
$"{{\"{ nameof(TagHelperDescriptor.Prefix) }\":\"prefix:\"," +
|
|
||||||
$"\"{ nameof(TagHelperDescriptor.TagName) }\":\"tag name\"," +
|
|
||||||
$"\"{ nameof(TagHelperDescriptor.FullTagName) }\":\"prefix:tag name\"," +
|
|
||||||
$"\"{ nameof(TagHelperDescriptor.TypeName) }\":\"type name\"," +
|
|
||||||
$"\"{ nameof(TagHelperDescriptor.AssemblyName) }\":\"assembly name\"," +
|
|
||||||
$"\"{ nameof(TagHelperDescriptor.Attributes) }\":[]," +
|
|
||||||
$"\"{ nameof(TagHelperDescriptor.RequiredAttributes) }\":[]," +
|
|
||||||
$"\"{ nameof(TagHelperDescriptor.AllowedChildren) }\":null," +
|
|
||||||
$"\"{ nameof(TagHelperDescriptor.RequiredParent) }\":null," +
|
|
||||||
$"\"{ nameof(TagHelperDescriptor.TagStructure) }\":0," +
|
|
||||||
$"\"{ nameof(TagHelperDescriptor.DesignTimeDescriptor) }\":null," +
|
|
||||||
$"\"{ nameof(TagHelperDescriptor.PropertyBag) }\":" +
|
|
||||||
"{\"key one\":\"value one\",\"key two\":\"value two\"}}";
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var serializedDescriptor = JsonConvert.SerializeObject(descriptor);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Equal(expectedSerializedDescriptor, serializedDescriptor);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void TagHelperDescriptor_CanBeDeserialized()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var serializedDescriptor =
|
|
||||||
$"{{\"{nameof(TagHelperDescriptor.Prefix)}\":\"prefix:\"," +
|
|
||||||
$"\"{nameof(TagHelperDescriptor.TagName)}\":\"tag name\"," +
|
|
||||||
$"\"{nameof(TagHelperDescriptor.FullTagName)}\":\"prefix:tag name\"," +
|
|
||||||
$"\"{nameof(TagHelperDescriptor.TypeName)}\":\"type name\"," +
|
|
||||||
$"\"{nameof(TagHelperDescriptor.AssemblyName)}\":\"assembly name\"," +
|
|
||||||
$"\"{nameof(TagHelperDescriptor.Attributes)}\":[]," +
|
|
||||||
$"\"{ nameof(TagHelperDescriptor.RequiredAttributes) }\":" +
|
|
||||||
$"[{{\"{ nameof(TagHelperRequiredAttributeDescriptor.Name)}\":\"required attribute one\"," +
|
|
||||||
$"\"{ nameof(TagHelperRequiredAttributeDescriptor.NameComparison) }\":1," +
|
|
||||||
$"\"{ nameof(TagHelperRequiredAttributeDescriptor.Value) }\":null," +
|
|
||||||
$"\"{ nameof(TagHelperRequiredAttributeDescriptor.ValueComparison) }\":0}}," +
|
|
||||||
$"{{\"{ nameof(TagHelperRequiredAttributeDescriptor.Name)}\":\"required attribute two\"," +
|
|
||||||
$"\"{ nameof(TagHelperRequiredAttributeDescriptor.NameComparison) }\":0," +
|
|
||||||
$"\"{ nameof(TagHelperRequiredAttributeDescriptor.Value) }\":\"something\"," +
|
|
||||||
$"\"{ nameof(TagHelperRequiredAttributeDescriptor.ValueComparison) }\":2}}]," +
|
|
||||||
$"\"{ nameof(TagHelperDescriptor.AllowedChildren) }\":[\"allowed child one\",\"allowed child two\"]," +
|
|
||||||
$"\"{ nameof(TagHelperDescriptor.RequiredParent) }\":\"parent name\"," +
|
|
||||||
$"\"{nameof(TagHelperDescriptor.TagStructure)}\":2," +
|
|
||||||
$"\"{ nameof(TagHelperDescriptor.DesignTimeDescriptor) }\":{{" +
|
|
||||||
$"\"{ nameof(TagHelperDesignTimeDescriptor.Summary) }\":\"usage summary\"," +
|
|
||||||
$"\"{ nameof(TagHelperDesignTimeDescriptor.Remarks) }\":\"usage remarks\"," +
|
|
||||||
$"\"{ nameof(TagHelperDesignTimeDescriptor.OutputElementHint) }\":\"some-tag\"}}}}";
|
|
||||||
var expectedDescriptor = new TagHelperDescriptor
|
|
||||||
{
|
|
||||||
Prefix = "prefix:",
|
|
||||||
TagName = "tag name",
|
|
||||||
TypeName = "type name",
|
|
||||||
AssemblyName = "assembly name",
|
|
||||||
RequiredAttributes = new[]
|
|
||||||
{
|
|
||||||
new TagHelperRequiredAttributeDescriptor
|
|
||||||
{
|
|
||||||
Name = "required attribute one",
|
|
||||||
NameComparison = TagHelperRequiredAttributeNameComparison.PrefixMatch
|
|
||||||
},
|
|
||||||
new TagHelperRequiredAttributeDescriptor
|
|
||||||
{
|
|
||||||
Name = "required attribute two",
|
|
||||||
NameComparison = TagHelperRequiredAttributeNameComparison.FullMatch,
|
|
||||||
Value = "something",
|
|
||||||
ValueComparison = TagHelperRequiredAttributeValueComparison.PrefixMatch,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
AllowedChildren = new[] { "allowed child one", "allowed child two" },
|
|
||||||
RequiredParent = "parent name",
|
|
||||||
DesignTimeDescriptor = new TagHelperDesignTimeDescriptor
|
|
||||||
{
|
|
||||||
Summary = "usage summary",
|
|
||||||
Remarks = "usage remarks",
|
|
||||||
OutputElementHint = "some-tag"
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var descriptor = JsonConvert.DeserializeObject<TagHelperDescriptor>(serializedDescriptor);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.NotNull(descriptor);
|
|
||||||
Assert.Equal(expectedDescriptor.Prefix, descriptor.Prefix, StringComparer.Ordinal);
|
|
||||||
Assert.Equal(expectedDescriptor.TagName, descriptor.TagName, StringComparer.Ordinal);
|
|
||||||
Assert.Equal(expectedDescriptor.FullTagName, descriptor.FullTagName, StringComparer.Ordinal);
|
|
||||||
Assert.Equal(expectedDescriptor.TypeName, descriptor.TypeName, StringComparer.Ordinal);
|
|
||||||
Assert.Equal(expectedDescriptor.AssemblyName, descriptor.AssemblyName, StringComparer.Ordinal);
|
|
||||||
Assert.Empty(descriptor.Attributes);
|
|
||||||
Assert.Equal(expectedDescriptor.RequiredAttributes, descriptor.RequiredAttributes, TagHelperRequiredAttributeDescriptorComparer.Default);
|
|
||||||
Assert.Equal(
|
|
||||||
expectedDescriptor.DesignTimeDescriptor,
|
|
||||||
descriptor.DesignTimeDescriptor,
|
|
||||||
TagHelperDesignTimeDescriptorComparer.Default);
|
|
||||||
Assert.Empty(descriptor.PropertyBag);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void TagHelperDescriptor_WithAttributes_CanBeDeserialized()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var serializedDescriptor =
|
|
||||||
$"{{\"{ nameof(TagHelperDescriptor.Prefix) }\":\"prefix:\"," +
|
|
||||||
$"\"{ nameof(TagHelperDescriptor.TagName) }\":\"tag name\"," +
|
|
||||||
$"\"{ nameof(TagHelperDescriptor.FullTagName) }\":\"prefix:tag name\"," +
|
|
||||||
$"\"{ nameof(TagHelperDescriptor.TypeName) }\":\"type name\"," +
|
|
||||||
$"\"{ nameof(TagHelperDescriptor.AssemblyName) }\":\"assembly name\"," +
|
|
||||||
$"\"{ nameof(TagHelperDescriptor.Attributes) }\":[" +
|
|
||||||
$"{{\"{ nameof(TagHelperAttributeDescriptor.IsIndexer) }\":false," +
|
|
||||||
$"\"{ nameof(TagHelperAttributeDescriptor.IsEnum) }\":true," +
|
|
||||||
$"\"{ nameof(TagHelperAttributeDescriptor.IsStringProperty) }\":false," +
|
|
||||||
$"\"{ nameof(TagHelperAttributeDescriptor.Name) }\":\"attribute one\"," +
|
|
||||||
$"\"{ nameof(TagHelperAttributeDescriptor.PropertyName) }\":\"property name\"," +
|
|
||||||
$"\"{ nameof(TagHelperAttributeDescriptor.TypeName) }\":\"property type name\"," +
|
|
||||||
$"\"{ nameof(TagHelperAttributeDescriptor.DesignTimeDescriptor) }\":null}}," +
|
|
||||||
$"{{\"{ nameof(TagHelperAttributeDescriptor.IsIndexer) }\":false," +
|
|
||||||
$"\"{ nameof(TagHelperAttributeDescriptor.IsEnum) }\":false," +
|
|
||||||
$"\"{ nameof(TagHelperAttributeDescriptor.IsStringProperty) }\":true," +
|
|
||||||
$"\"{ nameof(TagHelperAttributeDescriptor.Name) }\":\"attribute two\"," +
|
|
||||||
$"\"{ nameof(TagHelperAttributeDescriptor.PropertyName) }\":\"property name\"," +
|
|
||||||
$"\"{ nameof(TagHelperAttributeDescriptor.TypeName) }\":\"{ typeof(string).FullName }\"," +
|
|
||||||
$"\"{ nameof(TagHelperAttributeDescriptor.DesignTimeDescriptor) }\":null}}]," +
|
|
||||||
$"\"{ nameof(TagHelperDescriptor.RequiredAttributes) }\":[]," +
|
|
||||||
$"\"{ nameof(TagHelperDescriptor.AllowedChildren) }\":null," +
|
|
||||||
$"\"{ nameof(TagHelperDescriptor.RequiredParent) }\":null," +
|
|
||||||
$"\"{nameof(TagHelperDescriptor.TagStructure)}\":0," +
|
|
||||||
$"\"{ nameof(TagHelperDescriptor.DesignTimeDescriptor) }\":null}}";
|
|
||||||
var expectedDescriptor = new TagHelperDescriptor
|
|
||||||
{
|
|
||||||
Prefix = "prefix:",
|
|
||||||
TagName = "tag name",
|
|
||||||
TypeName = "type name",
|
|
||||||
AssemblyName = "assembly name",
|
|
||||||
Attributes = new[]
|
|
||||||
{
|
|
||||||
new TagHelperAttributeDescriptor
|
|
||||||
{
|
|
||||||
Name = "attribute one",
|
|
||||||
PropertyName = "property name",
|
|
||||||
TypeName = "property type name",
|
|
||||||
IsEnum = true,
|
|
||||||
},
|
|
||||||
new TagHelperAttributeDescriptor
|
|
||||||
{
|
|
||||||
Name = "attribute two",
|
|
||||||
PropertyName = "property name",
|
|
||||||
TypeName = typeof(string).FullName,
|
|
||||||
IsEnum = false,
|
|
||||||
IsStringProperty = true
|
|
||||||
},
|
|
||||||
},
|
|
||||||
AllowedChildren = new[] { "allowed child one", "allowed child two" }
|
|
||||||
};
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var descriptor = JsonConvert.DeserializeObject<TagHelperDescriptor>(serializedDescriptor);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.NotNull(descriptor);
|
|
||||||
Assert.Equal(expectedDescriptor.Prefix, descriptor.Prefix, StringComparer.Ordinal);
|
|
||||||
Assert.Equal(expectedDescriptor.TagName, descriptor.TagName, StringComparer.Ordinal);
|
|
||||||
Assert.Equal(expectedDescriptor.FullTagName, descriptor.FullTagName, StringComparer.Ordinal);
|
|
||||||
Assert.Equal(expectedDescriptor.TypeName, descriptor.TypeName, StringComparer.Ordinal);
|
|
||||||
Assert.Equal(expectedDescriptor.AssemblyName, descriptor.AssemblyName, StringComparer.Ordinal);
|
|
||||||
Assert.Equal(expectedDescriptor.Attributes, descriptor.Attributes, TagHelperAttributeDescriptorComparer.Default);
|
|
||||||
Assert.Empty(descriptor.RequiredAttributes);
|
|
||||||
Assert.Empty(descriptor.PropertyBag);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void TagHelperDescriptor_WithIndexerAttributes_CanBeDeserialized()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var serializedDescriptor =
|
|
||||||
$"{{\"{ nameof(TagHelperDescriptor.Prefix) }\":\"prefix:\"," +
|
|
||||||
$"\"{ nameof(TagHelperDescriptor.TagName) }\":\"tag name\"," +
|
|
||||||
$"\"{ nameof(TagHelperDescriptor.FullTagName) }\":\"prefix:tag name\"," +
|
|
||||||
$"\"{ nameof(TagHelperDescriptor.TypeName) }\":\"type name\"," +
|
|
||||||
$"\"{ nameof(TagHelperDescriptor.AssemblyName) }\":\"assembly name\"," +
|
|
||||||
$"\"{ nameof(TagHelperDescriptor.Attributes) }\":[" +
|
|
||||||
$"{{\"{ nameof(TagHelperAttributeDescriptor.IsIndexer) }\":true," +
|
|
||||||
$"\"{ nameof(TagHelperAttributeDescriptor.IsEnum) }\":true," +
|
|
||||||
$"\"{ nameof(TagHelperAttributeDescriptor.IsStringProperty) }\":false," +
|
|
||||||
$"\"{ nameof(TagHelperAttributeDescriptor.Name) }\":\"attribute one\"," +
|
|
||||||
$"\"{ nameof(TagHelperAttributeDescriptor.PropertyName) }\":\"property name\"," +
|
|
||||||
$"\"{ nameof(TagHelperAttributeDescriptor.TypeName) }\":\"property type name\"," +
|
|
||||||
$"\"{ nameof(TagHelperAttributeDescriptor.DesignTimeDescriptor) }\":null}}," +
|
|
||||||
$"{{\"{ nameof(TagHelperAttributeDescriptor.IsIndexer) }\":true," +
|
|
||||||
$"\"{ nameof(TagHelperAttributeDescriptor.IsEnum) }\":false," +
|
|
||||||
$"\"{ nameof(TagHelperAttributeDescriptor.IsStringProperty) }\":true," +
|
|
||||||
$"\"{ nameof(TagHelperAttributeDescriptor.Name) }\":\"attribute two\"," +
|
|
||||||
$"\"{ nameof(TagHelperAttributeDescriptor.PropertyName) }\":\"property name\"," +
|
|
||||||
$"\"{ nameof(TagHelperAttributeDescriptor.TypeName) }\":\"{ typeof(string).FullName }\"," +
|
|
||||||
$"\"{ nameof(TagHelperAttributeDescriptor.DesignTimeDescriptor) }\":null}}]," +
|
|
||||||
$"\"{ nameof(TagHelperDescriptor.RequiredAttributes) }\":[]," +
|
|
||||||
$"\"{ nameof(TagHelperDescriptor.AllowedChildren) }\":null," +
|
|
||||||
$"\"{ nameof(TagHelperDescriptor.RequiredParent) }\":null," +
|
|
||||||
$"\"{nameof(TagHelperDescriptor.TagStructure)}\":1," +
|
|
||||||
$"\"{ nameof(TagHelperDescriptor.DesignTimeDescriptor) }\":null," +
|
|
||||||
$"\"{ nameof(TagHelperDescriptor.PropertyBag) }\":{{}}}}";
|
|
||||||
|
|
||||||
var expectedDescriptor = new TagHelperDescriptor
|
|
||||||
{
|
|
||||||
Prefix = "prefix:",
|
|
||||||
TagName = "tag name",
|
|
||||||
TypeName = "type name",
|
|
||||||
AssemblyName = "assembly name",
|
|
||||||
Attributes = new[]
|
|
||||||
{
|
|
||||||
new TagHelperAttributeDescriptor
|
|
||||||
{
|
|
||||||
Name = "attribute one",
|
|
||||||
PropertyName = "property name",
|
|
||||||
TypeName = "property type name",
|
|
||||||
IsIndexer = true,
|
|
||||||
IsEnum = true,
|
|
||||||
},
|
|
||||||
new TagHelperAttributeDescriptor
|
|
||||||
{
|
|
||||||
Name = "attribute two",
|
|
||||||
PropertyName = "property name",
|
|
||||||
TypeName = typeof(string).FullName,
|
|
||||||
IsIndexer = true,
|
|
||||||
IsEnum = false,
|
|
||||||
IsStringProperty = true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
TagStructure = TagStructure.NormalOrSelfClosing
|
|
||||||
};
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var descriptor = JsonConvert.DeserializeObject<TagHelperDescriptor>(serializedDescriptor);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.NotNull(descriptor);
|
|
||||||
Assert.Equal(expectedDescriptor.Prefix, descriptor.Prefix, StringComparer.Ordinal);
|
|
||||||
Assert.Equal(expectedDescriptor.TagName, descriptor.TagName, StringComparer.Ordinal);
|
|
||||||
Assert.Equal(expectedDescriptor.FullTagName, descriptor.FullTagName, StringComparer.Ordinal);
|
|
||||||
Assert.Equal(expectedDescriptor.TypeName, descriptor.TypeName, StringComparer.Ordinal);
|
|
||||||
Assert.Equal(expectedDescriptor.AssemblyName, descriptor.AssemblyName, StringComparer.Ordinal);
|
|
||||||
Assert.Equal(expectedDescriptor.Attributes, descriptor.Attributes, TagHelperAttributeDescriptorComparer.Default);
|
|
||||||
Assert.Empty(descriptor.RequiredAttributes);
|
|
||||||
Assert.Empty(descriptor.PropertyBag);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void TagHelperDescriptor_WithPropertyBagElements_CanBeDeserialized()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var serializedDescriptor =
|
|
||||||
$"{{\"{nameof(TagHelperDescriptor.Prefix)}\":\"prefix:\"," +
|
|
||||||
$"\"{nameof(TagHelperDescriptor.TagName)}\":\"tag name\"," +
|
|
||||||
$"\"{nameof(TagHelperDescriptor.TypeName)}\":\"type name\"," +
|
|
||||||
$"\"{nameof(TagHelperDescriptor.AssemblyName)}\":\"assembly name\"," +
|
|
||||||
$"\"{ nameof(TagHelperDescriptor.PropertyBag) }\":" +
|
|
||||||
"{\"key one\":\"value one\",\"key two\":\"value two\"}}";
|
|
||||||
var expectedDescriptor = new TagHelperDescriptor
|
|
||||||
{
|
|
||||||
Prefix = "prefix:",
|
|
||||||
TagName = "tag name",
|
|
||||||
TypeName = "type name",
|
|
||||||
AssemblyName = "assembly name"
|
|
||||||
};
|
|
||||||
|
|
||||||
expectedDescriptor.PropertyBag.Add("key one", "value one");
|
|
||||||
expectedDescriptor.PropertyBag.Add("key two", "value two");
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var descriptor = JsonConvert.DeserializeObject<TagHelperDescriptor>(serializedDescriptor);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.NotNull(descriptor);
|
|
||||||
Assert.Equal(expectedDescriptor.Prefix, descriptor.Prefix, StringComparer.Ordinal);
|
|
||||||
Assert.Equal(expectedDescriptor.TagName, descriptor.TagName, StringComparer.Ordinal);
|
|
||||||
Assert.Equal(expectedDescriptor.TypeName, descriptor.TypeName, StringComparer.Ordinal);
|
|
||||||
Assert.Equal(expectedDescriptor.AssemblyName, descriptor.AssemblyName, StringComparer.Ordinal);
|
|
||||||
Assert.Empty(descriptor.Attributes);
|
|
||||||
Assert.Empty(descriptor.RequiredAttributes);
|
|
||||||
Assert.Equal(expectedDescriptor.PropertyBag["key one"], descriptor.PropertyBag["key one"]);
|
|
||||||
Assert.Equal(expectedDescriptor.PropertyBag["key two"], descriptor.PropertyBag["key two"]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -9,144 +9,127 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
// requiredAttributeDescriptor, attributeName, attributeValue, expectedResult
|
// requiredAttributeDescriptor, attributeName, attributeValue, expectedResult
|
||||||
return new TheoryData<TagHelperRequiredAttributeDescriptor, string, string, bool>
|
return new TheoryData<RequiredAttributeDescriptor, string, string, bool>
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
new TagHelperRequiredAttributeDescriptor
|
RequiredAttributeDescriptorBuilder.Create().Name("key").Build(),
|
||||||
{
|
|
||||||
Name = "key"
|
|
||||||
},
|
|
||||||
"KeY",
|
"KeY",
|
||||||
"value",
|
"value",
|
||||||
true
|
true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
new TagHelperRequiredAttributeDescriptor
|
RequiredAttributeDescriptorBuilder.Create().Name("key").Build(),
|
||||||
{
|
|
||||||
Name = "key"
|
|
||||||
},
|
|
||||||
"keys",
|
"keys",
|
||||||
"value",
|
"value",
|
||||||
false
|
false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
new TagHelperRequiredAttributeDescriptor
|
RequiredAttributeDescriptorBuilder.Create()
|
||||||
{
|
.Name("route-")
|
||||||
Name = "route-",
|
.NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.PrefixMatch)
|
||||||
NameComparison = TagHelperRequiredAttributeNameComparison.PrefixMatch,
|
.Build(),
|
||||||
},
|
|
||||||
"ROUTE-area",
|
"ROUTE-area",
|
||||||
"manage",
|
"manage",
|
||||||
true
|
true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
new TagHelperRequiredAttributeDescriptor
|
RequiredAttributeDescriptorBuilder.Create()
|
||||||
{
|
.Name("route-")
|
||||||
Name = "route-",
|
.NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.PrefixMatch)
|
||||||
NameComparison = TagHelperRequiredAttributeNameComparison.PrefixMatch,
|
.Build(),
|
||||||
},
|
|
||||||
"routearea",
|
"routearea",
|
||||||
"manage",
|
"manage",
|
||||||
false
|
false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
new TagHelperRequiredAttributeDescriptor
|
RequiredAttributeDescriptorBuilder.Create()
|
||||||
{
|
.Name("route-")
|
||||||
Name = "route-",
|
.NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.PrefixMatch)
|
||||||
NameComparison = TagHelperRequiredAttributeNameComparison.PrefixMatch,
|
.Build(),
|
||||||
},
|
|
||||||
"route-",
|
"route-",
|
||||||
"manage",
|
"manage",
|
||||||
false
|
false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
new TagHelperRequiredAttributeDescriptor
|
RequiredAttributeDescriptorBuilder.Create()
|
||||||
{
|
.Name("key")
|
||||||
Name = "key",
|
.NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.FullMatch)
|
||||||
NameComparison = TagHelperRequiredAttributeNameComparison.FullMatch,
|
.Build(),
|
||||||
},
|
|
||||||
"KeY",
|
"KeY",
|
||||||
"value",
|
"value",
|
||||||
true
|
true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
new TagHelperRequiredAttributeDescriptor
|
RequiredAttributeDescriptorBuilder.Create()
|
||||||
{
|
.Name("key")
|
||||||
Name = "key",
|
.NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.FullMatch)
|
||||||
NameComparison = TagHelperRequiredAttributeNameComparison.FullMatch,
|
.Build(),
|
||||||
},
|
|
||||||
"keys",
|
"keys",
|
||||||
"value",
|
"value",
|
||||||
false
|
false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
new TagHelperRequiredAttributeDescriptor
|
RequiredAttributeDescriptorBuilder.Create()
|
||||||
{
|
.Name("key")
|
||||||
Name = "key",
|
.NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.FullMatch)
|
||||||
NameComparison = TagHelperRequiredAttributeNameComparison.FullMatch,
|
.Value("value")
|
||||||
Value = "value",
|
.ValueComparisonMode(RequiredAttributeDescriptor.ValueComparisonMode.FullMatch)
|
||||||
ValueComparison = TagHelperRequiredAttributeValueComparison.FullMatch,
|
.Build(),
|
||||||
},
|
|
||||||
"key",
|
"key",
|
||||||
"value",
|
"value",
|
||||||
true
|
true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
new TagHelperRequiredAttributeDescriptor
|
RequiredAttributeDescriptorBuilder.Create()
|
||||||
{
|
.Name("key")
|
||||||
Name = "key",
|
.NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.FullMatch)
|
||||||
NameComparison = TagHelperRequiredAttributeNameComparison.FullMatch,
|
.Value("value")
|
||||||
Value = "value",
|
.ValueComparisonMode(RequiredAttributeDescriptor.ValueComparisonMode.FullMatch)
|
||||||
ValueComparison = TagHelperRequiredAttributeValueComparison.FullMatch,
|
.Build(),
|
||||||
},
|
|
||||||
"key",
|
"key",
|
||||||
"Value",
|
"Value",
|
||||||
false
|
false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
new TagHelperRequiredAttributeDescriptor
|
RequiredAttributeDescriptorBuilder.Create()
|
||||||
{
|
.Name("class")
|
||||||
Name = "class",
|
.NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.FullMatch)
|
||||||
NameComparison = TagHelperRequiredAttributeNameComparison.FullMatch,
|
.Value("btn")
|
||||||
Value = "btn",
|
.ValueComparisonMode(RequiredAttributeDescriptor.ValueComparisonMode.PrefixMatch)
|
||||||
ValueComparison = TagHelperRequiredAttributeValueComparison.PrefixMatch,
|
.Build(),
|
||||||
},
|
|
||||||
"class",
|
"class",
|
||||||
"btn btn-success",
|
"btn btn-success",
|
||||||
true
|
true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
new TagHelperRequiredAttributeDescriptor
|
RequiredAttributeDescriptorBuilder.Create()
|
||||||
{
|
.Name("class")
|
||||||
Name = "class",
|
.NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.FullMatch)
|
||||||
NameComparison = TagHelperRequiredAttributeNameComparison.FullMatch,
|
.Value("btn")
|
||||||
Value = "btn",
|
.ValueComparisonMode(RequiredAttributeDescriptor.ValueComparisonMode.PrefixMatch)
|
||||||
ValueComparison = TagHelperRequiredAttributeValueComparison.PrefixMatch,
|
.Build(),
|
||||||
},
|
|
||||||
"class",
|
"class",
|
||||||
"BTN btn-success",
|
"BTN btn-success",
|
||||||
false
|
false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
new TagHelperRequiredAttributeDescriptor
|
RequiredAttributeDescriptorBuilder.Create()
|
||||||
{
|
.Name("href")
|
||||||
Name = "href",
|
.NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.FullMatch)
|
||||||
NameComparison = TagHelperRequiredAttributeNameComparison.FullMatch,
|
.Value("#navigate")
|
||||||
Value = "#navigate",
|
.ValueComparisonMode(RequiredAttributeDescriptor.ValueComparisonMode.SuffixMatch)
|
||||||
ValueComparison = TagHelperRequiredAttributeValueComparison.SuffixMatch,
|
.Build(),
|
||||||
},
|
|
||||||
"href",
|
"href",
|
||||||
"/home/index#navigate",
|
"/home/index#navigate",
|
||||||
true
|
true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
new TagHelperRequiredAttributeDescriptor
|
RequiredAttributeDescriptorBuilder.Create()
|
||||||
{
|
.Name("href")
|
||||||
Name = "href",
|
.NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.FullMatch)
|
||||||
NameComparison = TagHelperRequiredAttributeNameComparison.FullMatch,
|
.Value("#navigate")
|
||||||
Value = "#navigate",
|
.ValueComparisonMode(RequiredAttributeDescriptor.ValueComparisonMode.SuffixMatch)
|
||||||
ValueComparison = TagHelperRequiredAttributeValueComparison.SuffixMatch,
|
.Build(),
|
||||||
},
|
|
||||||
"href",
|
"href",
|
||||||
"/home/index#NAVigate",
|
"/home/index#NAVigate",
|
||||||
false
|
false
|
||||||
|
|
@ -164,7 +147,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
||||||
bool expectedResult)
|
bool expectedResult)
|
||||||
{
|
{
|
||||||
// Act
|
// Act
|
||||||
var result = ((TagHelperRequiredAttributeDescriptor)requiredAttributeDescriptor).IsMatch(attributeName, attributeValue);
|
var result = ((RequiredAttributeDescriptor)requiredAttributeDescriptor).IsMatch(attributeName, attributeValue);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal(expectedResult, result);
|
Assert.Equal(expectedResult, result);
|
||||||
|
|
|
||||||
|
|
@ -1,97 +0,0 @@
|
||||||
// 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.Linq;
|
|
||||||
using Microsoft.AspNetCore.Razor.Evolution;
|
|
||||||
using Microsoft.AspNetCore.Razor.Evolution.Legacy;
|
|
||||||
using Microsoft.Extensions.Internal;
|
|
||||||
using Xunit;
|
|
||||||
|
|
||||||
namespace Microsoft.CodeAnalysis.Razor.Workspaces.Test.Comparers
|
|
||||||
{
|
|
||||||
internal class CaseSensitiveTagHelperDescriptorComparer : TagHelperDescriptorComparer
|
|
||||||
{
|
|
||||||
public new static readonly CaseSensitiveTagHelperDescriptorComparer Default =
|
|
||||||
new CaseSensitiveTagHelperDescriptorComparer();
|
|
||||||
|
|
||||||
private CaseSensitiveTagHelperDescriptorComparer()
|
|
||||||
: base()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool Equals(TagHelperDescriptor descriptorX, TagHelperDescriptor descriptorY)
|
|
||||||
{
|
|
||||||
if (descriptorX == descriptorY)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Assert.True(base.Equals(descriptorX, descriptorY));
|
|
||||||
|
|
||||||
// Normal comparer doesn't care about the case, required attribute order, allowed children order,
|
|
||||||
// attributes or prefixes. In tests we do.
|
|
||||||
Assert.Equal(descriptorX.TagName, descriptorY.TagName, StringComparer.Ordinal);
|
|
||||||
Assert.Equal(descriptorX.Prefix, descriptorY.Prefix, StringComparer.Ordinal);
|
|
||||||
Assert.Equal(
|
|
||||||
descriptorX.RequiredAttributes,
|
|
||||||
descriptorY.RequiredAttributes,
|
|
||||||
CaseSensitiveTagHelperRequiredAttributeDescriptorComparer.Default);
|
|
||||||
Assert.Equal(descriptorX.RequiredParent, descriptorY.RequiredParent, StringComparer.Ordinal);
|
|
||||||
|
|
||||||
if (descriptorX.AllowedChildren != descriptorY.AllowedChildren)
|
|
||||||
{
|
|
||||||
Assert.Equal(descriptorX.AllowedChildren, descriptorY.AllowedChildren, StringComparer.Ordinal);
|
|
||||||
}
|
|
||||||
|
|
||||||
Assert.Equal(
|
|
||||||
descriptorX.Attributes,
|
|
||||||
descriptorY.Attributes,
|
|
||||||
TagHelperAttributeDescriptorComparer.Default);
|
|
||||||
Assert.Equal(
|
|
||||||
descriptorX.DesignTimeDescriptor,
|
|
||||||
descriptorY.DesignTimeDescriptor,
|
|
||||||
TagHelperDesignTimeDescriptorComparer.Default);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int GetHashCode(TagHelperDescriptor descriptor)
|
|
||||||
{
|
|
||||||
var hashCodeCombiner = HashCodeCombiner.Start();
|
|
||||||
hashCodeCombiner.Add(base.GetHashCode(descriptor));
|
|
||||||
hashCodeCombiner.Add(descriptor.TagName, StringComparer.Ordinal);
|
|
||||||
hashCodeCombiner.Add(descriptor.Prefix, StringComparer.Ordinal);
|
|
||||||
|
|
||||||
if (descriptor.DesignTimeDescriptor != null)
|
|
||||||
{
|
|
||||||
hashCodeCombiner.Add(
|
|
||||||
TagHelperDesignTimeDescriptorComparer.Default.GetHashCode(descriptor.DesignTimeDescriptor));
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var requiredAttribute in descriptor.RequiredAttributes.OrderBy(attribute => attribute.Name))
|
|
||||||
{
|
|
||||||
hashCodeCombiner.Add(
|
|
||||||
CaseSensitiveTagHelperRequiredAttributeDescriptorComparer.Default.GetHashCode(requiredAttribute));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (descriptor.AllowedChildren != null)
|
|
||||||
{
|
|
||||||
foreach (var child in descriptor.AllowedChildren.OrderBy(child => child))
|
|
||||||
{
|
|
||||||
hashCodeCombiner.Add(child, StringComparer.Ordinal);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var orderedAttributeHashCodes = descriptor.Attributes
|
|
||||||
.Select(attribute => TagHelperAttributeDescriptorComparer.Default.GetHashCode(attribute))
|
|
||||||
.OrderBy(hashcode => hashcode);
|
|
||||||
foreach (var attributeHashCode in orderedAttributeHashCodes)
|
|
||||||
{
|
|
||||||
hashCodeCombiner.Add(attributeHashCode);
|
|
||||||
}
|
|
||||||
|
|
||||||
return hashCodeCombiner.CombinedHash;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,44 +0,0 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using Microsoft.AspNetCore.Razor.Evolution;
|
|
||||||
using Microsoft.AspNetCore.Razor.Evolution.Legacy;
|
|
||||||
using Microsoft.Extensions.Internal;
|
|
||||||
using Xunit;
|
|
||||||
|
|
||||||
namespace Microsoft.CodeAnalysis.Razor.Workspaces.Test.Comparers
|
|
||||||
{
|
|
||||||
internal class CaseSensitiveTagHelperRequiredAttributeDescriptorComparer : TagHelperRequiredAttributeDescriptorComparer
|
|
||||||
{
|
|
||||||
public new static readonly CaseSensitiveTagHelperRequiredAttributeDescriptorComparer Default =
|
|
||||||
new CaseSensitiveTagHelperRequiredAttributeDescriptorComparer();
|
|
||||||
|
|
||||||
private CaseSensitiveTagHelperRequiredAttributeDescriptorComparer()
|
|
||||||
: base()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool Equals(TagHelperRequiredAttributeDescriptor descriptorX, TagHelperRequiredAttributeDescriptor descriptorY)
|
|
||||||
{
|
|
||||||
if (descriptorX == descriptorY)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Assert.True(base.Equals(descriptorX, descriptorY));
|
|
||||||
Assert.Equal(descriptorX.Name, descriptorY.Name, StringComparer.Ordinal);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int GetHashCode(TagHelperRequiredAttributeDescriptor descriptor)
|
|
||||||
{
|
|
||||||
var hashCodeCombiner = HashCodeCombiner.Start();
|
|
||||||
hashCodeCombiner.Add(base.GetHashCode(descriptor));
|
|
||||||
hashCodeCombiner.Add(descriptor.Name, StringComparer.Ordinal);
|
|
||||||
|
|
||||||
return hashCodeCombiner.CombinedHash;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,57 +0,0 @@
|
||||||
// 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;
|
|
||||||
using Microsoft.Extensions.Internal;
|
|
||||||
using Xunit;
|
|
||||||
|
|
||||||
namespace Microsoft.CodeAnalysis.Razor.Workspaces.Test.Comparers
|
|
||||||
{
|
|
||||||
internal class TagHelperAttributeDescriptorComparer : IEqualityComparer<TagHelperAttributeDescriptor>
|
|
||||||
{
|
|
||||||
public static readonly TagHelperAttributeDescriptorComparer Default =
|
|
||||||
new TagHelperAttributeDescriptorComparer();
|
|
||||||
|
|
||||||
private TagHelperAttributeDescriptorComparer()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Equals(TagHelperAttributeDescriptor descriptorX, TagHelperAttributeDescriptor descriptorY)
|
|
||||||
{
|
|
||||||
if (descriptorX == descriptorY)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Assert.NotNull(descriptorX);
|
|
||||||
Assert.NotNull(descriptorY);
|
|
||||||
Assert.Equal(descriptorX.IsIndexer, descriptorY.IsIndexer);
|
|
||||||
Assert.Equal(descriptorX.Name, descriptorY.Name, StringComparer.Ordinal);
|
|
||||||
Assert.Equal(descriptorX.PropertyName, descriptorY.PropertyName, StringComparer.Ordinal);
|
|
||||||
Assert.Equal(descriptorX.TypeName, descriptorY.TypeName, StringComparer.Ordinal);
|
|
||||||
Assert.Equal(descriptorX.IsEnum, descriptorY.IsEnum);
|
|
||||||
Assert.Equal(descriptorX.IsStringProperty, descriptorY.IsStringProperty);
|
|
||||||
|
|
||||||
return TagHelperAttributeDesignTimeDescriptorComparer.Default.Equals(
|
|
||||||
descriptorX.DesignTimeDescriptor,
|
|
||||||
descriptorY.DesignTimeDescriptor);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int GetHashCode(TagHelperAttributeDescriptor descriptor)
|
|
||||||
{
|
|
||||||
var hashCodeCombiner = HashCodeCombiner.Start();
|
|
||||||
hashCodeCombiner.Add(descriptor.IsIndexer);
|
|
||||||
hashCodeCombiner.Add(descriptor.Name, StringComparer.Ordinal);
|
|
||||||
hashCodeCombiner.Add(descriptor.PropertyName, StringComparer.Ordinal);
|
|
||||||
hashCodeCombiner.Add(descriptor.TypeName, StringComparer.Ordinal);
|
|
||||||
hashCodeCombiner.Add(descriptor.IsEnum);
|
|
||||||
hashCodeCombiner.Add(descriptor.IsStringProperty);
|
|
||||||
hashCodeCombiner.Add(TagHelperAttributeDesignTimeDescriptorComparer.Default.GetHashCode(
|
|
||||||
descriptor.DesignTimeDescriptor));
|
|
||||||
|
|
||||||
return hashCodeCombiner;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,48 +0,0 @@
|
||||||
// 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;
|
|
||||||
using Microsoft.Extensions.Internal;
|
|
||||||
using Xunit;
|
|
||||||
|
|
||||||
namespace Microsoft.CodeAnalysis.Razor.Workspaces.Test.Comparers
|
|
||||||
{
|
|
||||||
internal class TagHelperAttributeDesignTimeDescriptorComparer :
|
|
||||||
IEqualityComparer<TagHelperAttributeDesignTimeDescriptor>
|
|
||||||
{
|
|
||||||
public static readonly TagHelperAttributeDesignTimeDescriptorComparer Default =
|
|
||||||
new TagHelperAttributeDesignTimeDescriptorComparer();
|
|
||||||
|
|
||||||
private TagHelperAttributeDesignTimeDescriptorComparer()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Equals(
|
|
||||||
TagHelperAttributeDesignTimeDescriptor descriptorX,
|
|
||||||
TagHelperAttributeDesignTimeDescriptor descriptorY)
|
|
||||||
{
|
|
||||||
if (descriptorX == descriptorY)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Assert.NotNull(descriptorX);
|
|
||||||
Assert.NotNull(descriptorY);
|
|
||||||
Assert.Equal(descriptorX.Summary, descriptorY.Summary, StringComparer.Ordinal);
|
|
||||||
Assert.Equal(descriptorX.Remarks, descriptorY.Remarks, StringComparer.Ordinal);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int GetHashCode(TagHelperAttributeDesignTimeDescriptor descriptor)
|
|
||||||
{
|
|
||||||
var hashCodeCombiner = HashCodeCombiner.Start();
|
|
||||||
hashCodeCombiner.Add(descriptor.Summary, StringComparer.Ordinal);
|
|
||||||
hashCodeCombiner.Add(descriptor.Remarks, StringComparer.Ordinal);
|
|
||||||
|
|
||||||
return hashCodeCombiner;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,48 +0,0 @@
|
||||||
// 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;
|
|
||||||
using Microsoft.Extensions.Internal;
|
|
||||||
using Xunit;
|
|
||||||
|
|
||||||
namespace Microsoft.CodeAnalysis.Razor.Workspaces.Test.Comparers
|
|
||||||
{
|
|
||||||
internal class TagHelperDesignTimeDescriptorComparer : IEqualityComparer<TagHelperDesignTimeDescriptor>
|
|
||||||
{
|
|
||||||
public static readonly TagHelperDesignTimeDescriptorComparer Default =
|
|
||||||
new TagHelperDesignTimeDescriptorComparer();
|
|
||||||
|
|
||||||
private TagHelperDesignTimeDescriptorComparer()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Equals(TagHelperDesignTimeDescriptor descriptorX, TagHelperDesignTimeDescriptor descriptorY)
|
|
||||||
{
|
|
||||||
if (descriptorX == descriptorY)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Assert.NotNull(descriptorX);
|
|
||||||
Assert.NotNull(descriptorY);
|
|
||||||
Assert.Equal(descriptorX.Summary, descriptorY.Summary, StringComparer.Ordinal);
|
|
||||||
Assert.Equal(descriptorX.Remarks, descriptorY.Remarks, StringComparer.Ordinal);
|
|
||||||
Assert.Equal(descriptorX.OutputElementHint, descriptorY.OutputElementHint, StringComparer.Ordinal);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int GetHashCode(TagHelperDesignTimeDescriptor descriptor)
|
|
||||||
{
|
|
||||||
var hashCodeCombiner = HashCodeCombiner.Start();
|
|
||||||
|
|
||||||
hashCodeCombiner.Add(descriptor.Summary, StringComparer.Ordinal);
|
|
||||||
hashCodeCombiner.Add(descriptor.Remarks, StringComparer.Ordinal);
|
|
||||||
hashCodeCombiner.Add(descriptor.OutputElementHint, StringComparer.Ordinal);
|
|
||||||
|
|
||||||
return hashCodeCombiner;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -424,7 +424,7 @@ namespace Microsoft.CodeAnalysis.Razor.Workspaces.Test
|
||||||
[HtmlTargetElement("a")]
|
[HtmlTargetElement("a")]
|
||||||
[HtmlTargetElement("p")]
|
[HtmlTargetElement("p")]
|
||||||
[OutputElementHint("div")]
|
[OutputElementHint("div")]
|
||||||
public class MulitpleDescriptorTagHelperWithOutputElementHint : TagHelper
|
public class MultipleDescriptorTagHelperWithOutputElementHint : TagHelper
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,8 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// 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;
|
|
||||||
using Microsoft.AspNetCore.Razor.TagHelpers;
|
using Microsoft.AspNetCore.Razor.TagHelpers;
|
||||||
using Microsoft.CodeAnalysis.Razor.Workspaces.Test;
|
using System.Collections.Generic;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.CodeAnalysis.Razor.Workspaces
|
namespace Microsoft.CodeAnalysis.Razor.Workspaces
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,13 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using Microsoft.CodeAnalysis.CSharp;
|
||||||
|
using Microsoft.Extensions.DependencyModel;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using Microsoft.CodeAnalysis.CSharp;
|
|
||||||
using Microsoft.Extensions.DependencyModel;
|
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.CodeAnalysis.Razor
|
namespace Microsoft.CodeAnalysis.Razor
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,11 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Razor.Evolution;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using Microsoft.AspNetCore.Razor.Evolution;
|
|
||||||
using Microsoft.CodeAnalysis.Razor.Workspaces.Test;
|
|
||||||
using Microsoft.CodeAnalysis.Razor.Workspaces.Test.Comparers;
|
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
using Microsoft.AspNetCore.Razor.Evolution.Legacy;
|
||||||
|
|
||||||
namespace Microsoft.CodeAnalysis.Razor.Workspaces
|
namespace Microsoft.CodeAnalysis.Razor.Workspaces
|
||||||
{
|
{
|
||||||
|
|
@ -19,45 +18,32 @@ namespace Microsoft.CodeAnalysis.Razor.Workspaces
|
||||||
var testCompilation = TestCompilation.Create();
|
var testCompilation = TestCompilation.Create();
|
||||||
var viewComponent = testCompilation.GetTypeByMetadataName(typeof(StringParameterViewComponent).FullName);
|
var viewComponent = testCompilation.GetTypeByMetadataName(typeof(StringParameterViewComponent).FullName);
|
||||||
var factory = new ViewComponentTagHelperDescriptorFactory(testCompilation);
|
var factory = new ViewComponentTagHelperDescriptorFactory(testCompilation);
|
||||||
var expectedDescriptor = new TagHelperDescriptor
|
var expectedDescriptor = ITagHelperDescriptorBuilder.Create(
|
||||||
{
|
"__Generated__StringParameterViewComponentTagHelper",
|
||||||
TagName = "vc:string-parameter",
|
typeof(StringParameterViewComponent).GetTypeInfo().Assembly.GetName().Name)
|
||||||
TypeName = "__Generated__StringParameterViewComponentTagHelper",
|
.TagMatchingRule(rule =>
|
||||||
AssemblyName = typeof(StringParameterViewComponent).GetTypeInfo().Assembly.GetName().Name,
|
rule
|
||||||
Attributes = new List<TagHelperAttributeDescriptor>
|
.RequireTagName("vc:string-parameter")
|
||||||
{
|
.RequireAttribute(attribute => attribute.Name("foo"))
|
||||||
new TagHelperAttributeDescriptor
|
.RequireAttribute(attribute => attribute.Name("bar")))
|
||||||
{
|
.BindAttribute(attribute =>
|
||||||
Name = "foo",
|
attribute
|
||||||
PropertyName = "foo",
|
.Name("foo")
|
||||||
TypeName = typeof(string).FullName
|
.PropertyName("foo")
|
||||||
},
|
.TypeName(typeof(string).FullName))
|
||||||
new TagHelperAttributeDescriptor
|
.BindAttribute(attribute =>
|
||||||
{
|
attribute
|
||||||
Name = "bar",
|
.Name("bar")
|
||||||
PropertyName = "bar",
|
.PropertyName("bar")
|
||||||
TypeName = typeof(string).FullName
|
.TypeName(typeof(string).FullName))
|
||||||
}
|
.AddMetadata(ViewComponentTypes.ViewComponentNameKey, "StringParameter")
|
||||||
},
|
.Build();
|
||||||
RequiredAttributes = new List<TagHelperRequiredAttributeDescriptor>
|
|
||||||
{
|
|
||||||
new TagHelperRequiredAttributeDescriptor
|
|
||||||
{
|
|
||||||
Name = "foo"
|
|
||||||
},
|
|
||||||
new TagHelperRequiredAttributeDescriptor
|
|
||||||
{
|
|
||||||
Name = "bar"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
expectedDescriptor.PropertyBag.Add(ViewComponentTypes.ViewComponentNameKey, "StringParameter");
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var descriptor = factory.CreateDescriptor(viewComponent);
|
var descriptor = factory.CreateDescriptor(viewComponent);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal(expectedDescriptor, descriptor, CaseSensitiveTagHelperDescriptorComparer.Default);
|
Assert.Equal(expectedDescriptor, descriptor, TagHelperDescriptorComparer.CaseSensitive);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -67,60 +53,39 @@ namespace Microsoft.CodeAnalysis.Razor.Workspaces
|
||||||
var testCompilation = TestCompilation.Create();
|
var testCompilation = TestCompilation.Create();
|
||||||
var viewComponent = testCompilation.GetTypeByMetadataName(typeof(VariousParameterViewComponent).FullName);
|
var viewComponent = testCompilation.GetTypeByMetadataName(typeof(VariousParameterViewComponent).FullName);
|
||||||
var factory = new ViewComponentTagHelperDescriptorFactory(testCompilation);
|
var factory = new ViewComponentTagHelperDescriptorFactory(testCompilation);
|
||||||
var expectedDescriptor = new TagHelperDescriptor
|
var expectedDescriptor = ITagHelperDescriptorBuilder.Create(
|
||||||
{
|
"__Generated__VariousParameterViewComponentTagHelper",
|
||||||
TagName = "vc:various-parameter",
|
typeof(VariousParameterViewComponent).GetTypeInfo().Assembly.GetName().Name)
|
||||||
TypeName = "__Generated__VariousParameterViewComponentTagHelper",
|
.TagMatchingRule(rule =>
|
||||||
AssemblyName = typeof(VariousParameterViewComponent).GetTypeInfo().Assembly.GetName().Name,
|
rule
|
||||||
Attributes = new List<TagHelperAttributeDescriptor>
|
.RequireTagName("vc:various-parameter")
|
||||||
{
|
.RequireAttribute(attribute => attribute.Name("test-enum"))
|
||||||
new TagHelperAttributeDescriptor
|
.RequireAttribute(attribute => attribute.Name("test-string"))
|
||||||
{
|
.RequireAttribute(attribute => attribute.Name("baz")))
|
||||||
Name = "test-enum",
|
.BindAttribute(attribute =>
|
||||||
PropertyName = "testEnum",
|
attribute
|
||||||
TypeName = typeof(VariousParameterViewComponent).FullName + "." + nameof(VariousParameterViewComponent.TestEnum),
|
.Name("test-enum")
|
||||||
IsEnum = true
|
.PropertyName("testEnum")
|
||||||
},
|
.TypeName(typeof(VariousParameterViewComponent).FullName + "." + nameof(VariousParameterViewComponent.TestEnum))
|
||||||
|
.AsEnum())
|
||||||
new TagHelperAttributeDescriptor
|
.BindAttribute(attribute =>
|
||||||
{
|
attribute
|
||||||
Name = "test-string",
|
.Name("test-string")
|
||||||
PropertyName = "testString",
|
.PropertyName("testString")
|
||||||
TypeName = typeof(string).FullName
|
.TypeName(typeof(string).FullName))
|
||||||
},
|
.BindAttribute(attribute =>
|
||||||
|
attribute
|
||||||
new TagHelperAttributeDescriptor
|
.Name("baz")
|
||||||
{
|
.PropertyName("baz")
|
||||||
Name = "baz",
|
.TypeName(typeof(int).FullName))
|
||||||
PropertyName = "baz",
|
.AddMetadata(ViewComponentTypes.ViewComponentNameKey, "VariousParameter")
|
||||||
TypeName = typeof(int).FullName
|
.Build();
|
||||||
}
|
|
||||||
},
|
|
||||||
RequiredAttributes = new List<TagHelperRequiredAttributeDescriptor>
|
|
||||||
{
|
|
||||||
new TagHelperRequiredAttributeDescriptor
|
|
||||||
{
|
|
||||||
Name = "test-enum"
|
|
||||||
},
|
|
||||||
|
|
||||||
new TagHelperRequiredAttributeDescriptor
|
|
||||||
{
|
|
||||||
Name = "test-string"
|
|
||||||
},
|
|
||||||
|
|
||||||
new TagHelperRequiredAttributeDescriptor
|
|
||||||
{
|
|
||||||
Name = "baz"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
expectedDescriptor.PropertyBag.Add(ViewComponentTypes.ViewComponentNameKey, "VariousParameter");
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var descriptor = factory.CreateDescriptor(viewComponent);
|
var descriptor = factory.CreateDescriptor(viewComponent);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal(expectedDescriptor, descriptor, CaseSensitiveTagHelperDescriptorComparer.Default);
|
Assert.Equal(expectedDescriptor, descriptor, TagHelperDescriptorComparer.CaseSensitive);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
@ -130,50 +95,32 @@ namespace Microsoft.CodeAnalysis.Razor.Workspaces
|
||||||
var testCompilation = TestCompilation.Create();
|
var testCompilation = TestCompilation.Create();
|
||||||
var viewComponent = testCompilation.GetTypeByMetadataName(typeof(GenericParameterViewComponent).FullName);
|
var viewComponent = testCompilation.GetTypeByMetadataName(typeof(GenericParameterViewComponent).FullName);
|
||||||
var factory = new ViewComponentTagHelperDescriptorFactory(testCompilation);
|
var factory = new ViewComponentTagHelperDescriptorFactory(testCompilation);
|
||||||
var expectedDescriptor = new TagHelperDescriptor
|
var expectedDescriptor = ITagHelperDescriptorBuilder.Create(
|
||||||
{
|
"__Generated__GenericParameterViewComponentTagHelper",
|
||||||
TagName = "vc:generic-parameter",
|
typeof(GenericParameterViewComponent).GetTypeInfo().Assembly.GetName().Name)
|
||||||
TypeName = "__Generated__GenericParameterViewComponentTagHelper",
|
.TagMatchingRule(rule =>
|
||||||
AssemblyName = typeof(GenericParameterViewComponent).GetTypeInfo().Assembly.GetName().Name,
|
rule
|
||||||
Attributes = new List<TagHelperAttributeDescriptor>
|
.RequireTagName("vc:generic-parameter")
|
||||||
{
|
.RequireAttribute(attribute => attribute.Name("foo")))
|
||||||
new TagHelperAttributeDescriptor
|
.BindAttribute(attribute =>
|
||||||
{
|
attribute
|
||||||
Name = "foo",
|
.Name("foo")
|
||||||
PropertyName = "Foo",
|
.PropertyName("Foo")
|
||||||
TypeName = "System.Collections.Generic.List<System.String>"
|
.TypeName("System.Collections.Generic.List<System.String>"))
|
||||||
},
|
.BindAttribute(attribute =>
|
||||||
|
attribute
|
||||||
new TagHelperAttributeDescriptor
|
.Name("bar")
|
||||||
{
|
.PropertyName("Bar")
|
||||||
Name = "bar",
|
.TypeName("System.Collections.Generic.Dictionary<System.String, System.Int32>")
|
||||||
PropertyName = "Bar",
|
.AsDictionary("bar-", typeof(int).FullName))
|
||||||
TypeName = "System.Collections.Generic.Dictionary<System.String, System.Int32>"
|
.AddMetadata(ViewComponentTypes.ViewComponentNameKey, "GenericParameter")
|
||||||
},
|
.Build();
|
||||||
|
|
||||||
new TagHelperAttributeDescriptor
|
|
||||||
{
|
|
||||||
Name = "bar-",
|
|
||||||
PropertyName = "Bar",
|
|
||||||
TypeName = typeof(int).FullName,
|
|
||||||
IsIndexer = true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
RequiredAttributes = new List<TagHelperRequiredAttributeDescriptor>
|
|
||||||
{
|
|
||||||
new TagHelperRequiredAttributeDescriptor
|
|
||||||
{
|
|
||||||
Name = "foo"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
expectedDescriptor.PropertyBag.Add(ViewComponentTypes.ViewComponentNameKey, "GenericParameter");
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var descriptor = factory.CreateDescriptor(viewComponent);
|
var descriptor = factory.CreateDescriptor(viewComponent);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
Assert.Equal(expectedDescriptor, descriptor, CaseSensitiveTagHelperDescriptorComparer.Default);
|
Assert.Equal(expectedDescriptor, descriptor, TagHelperDescriptorComparer.CaseSensitive);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,188 @@
|
||||||
|
// 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;
|
||||||
|
using Microsoft.AspNetCore.Razor.Evolution.Legacy;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Microsoft.VisualStudio.LanguageServices.Razor
|
||||||
|
{
|
||||||
|
public class TagHelperDescriptorSerializationTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void TagHelperDescriptor_RoundTripsProperly()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var expectedDescriptor = CreateTagHelperDescriptor(
|
||||||
|
tagName: "tag-name",
|
||||||
|
typeName: "type name",
|
||||||
|
assemblyName: "assembly name",
|
||||||
|
attributes: new Action<ITagHelperBoundAttributeDescriptorBuilder>[]
|
||||||
|
{
|
||||||
|
builder => builder
|
||||||
|
.Name("test-attribute")
|
||||||
|
.PropertyName("TestAttribute")
|
||||||
|
.TypeName("string"),
|
||||||
|
},
|
||||||
|
ruleBuilders: new Action<TagMatchingRuleBuilder>[]
|
||||||
|
{
|
||||||
|
builder => builder
|
||||||
|
.RequireAttribute(attribute => attribute
|
||||||
|
.Name("required-attribute-one")
|
||||||
|
.NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.PrefixMatch))
|
||||||
|
.RequireAttribute(attribute => attribute
|
||||||
|
.Name("required-attribute-two")
|
||||||
|
.NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.FullMatch)
|
||||||
|
.Value("something")
|
||||||
|
.ValueComparisonMode(RequiredAttributeDescriptor.ValueComparisonMode.PrefixMatch))
|
||||||
|
.RequireParentTag("parent-name")
|
||||||
|
.RequireTagStructure(TagStructure.WithoutEndTag),
|
||||||
|
},
|
||||||
|
configureAction: builder =>
|
||||||
|
{
|
||||||
|
builder.AllowChildTag("allowed-child-one");
|
||||||
|
builder.AddMetadata("foo", "bar");
|
||||||
|
});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var serializedDescriptor = JsonConvert.SerializeObject(expectedDescriptor, TagHelperDescriptorJsonConverter.Instance, RazorDiagnosticJsonConverter.Instance);
|
||||||
|
var descriptor = JsonConvert.DeserializeObject<TagHelperDescriptor>(serializedDescriptor, TagHelperDescriptorJsonConverter.Instance, RazorDiagnosticJsonConverter.Instance);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(expectedDescriptor, descriptor, TagHelperDescriptorComparer.Default);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void TagHelperDescriptor_WithDiagnostic_RoundTripsProperly()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var expectedDescriptor = CreateTagHelperDescriptor(
|
||||||
|
tagName: "tag-name",
|
||||||
|
typeName: "type name",
|
||||||
|
assemblyName: "assembly name",
|
||||||
|
attributes: new Action<ITagHelperBoundAttributeDescriptorBuilder>[]
|
||||||
|
{
|
||||||
|
builder => builder
|
||||||
|
.Name("test-attribute")
|
||||||
|
.PropertyName("TestAttribute")
|
||||||
|
.TypeName("string"),
|
||||||
|
},
|
||||||
|
ruleBuilders: new Action<TagMatchingRuleBuilder>[]
|
||||||
|
{
|
||||||
|
builder => builder
|
||||||
|
.RequireAttribute(attribute => attribute
|
||||||
|
.Name("required-attribute-one")
|
||||||
|
.NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.PrefixMatch))
|
||||||
|
.RequireAttribute(attribute => attribute
|
||||||
|
.Name("required-attribute-two")
|
||||||
|
.NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.FullMatch)
|
||||||
|
.Value("something")
|
||||||
|
.ValueComparisonMode(RequiredAttributeDescriptor.ValueComparisonMode.PrefixMatch))
|
||||||
|
.RequireParentTag("parent-name"),
|
||||||
|
},
|
||||||
|
configureAction: builder =>
|
||||||
|
{
|
||||||
|
builder.AllowChildTag("allowed-child-one")
|
||||||
|
.AddMetadata("foo", "bar")
|
||||||
|
.AddDiagnostic(RazorDiagnostic.Create(
|
||||||
|
new RazorDiagnosticDescriptor("id", () => "Test Message 1", RazorDiagnosticSeverity.Error), new SourceSpan(null, 10, 20, 30, 40)))
|
||||||
|
.AddDiagnostic(RazorDiagnostic.Create(new RazorError("Test Message 2", 10, 20, 30, 40)));
|
||||||
|
});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var serializedDescriptor = JsonConvert.SerializeObject(expectedDescriptor, TagHelperDescriptorJsonConverter.Instance, RazorDiagnosticJsonConverter.Instance);
|
||||||
|
var descriptor = JsonConvert.DeserializeObject<TagHelperDescriptor>(serializedDescriptor, TagHelperDescriptorJsonConverter.Instance, RazorDiagnosticJsonConverter.Instance);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(expectedDescriptor, descriptor, TagHelperDescriptorComparer.Default);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void TagHelperDescriptor_WithIndexerAttributes_RoundTripsProperly()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var expectedDescriptor = CreateTagHelperDescriptor(
|
||||||
|
tagName: "tag-name",
|
||||||
|
typeName: "type name",
|
||||||
|
assemblyName: "assembly name",
|
||||||
|
attributes: new Action<ITagHelperBoundAttributeDescriptorBuilder>[]
|
||||||
|
{
|
||||||
|
builder => builder
|
||||||
|
.Name("test-attribute")
|
||||||
|
.PropertyName("TestAttribute")
|
||||||
|
.TypeName("SomeEnum")
|
||||||
|
.AsEnum()
|
||||||
|
.Documentation("Summary"),
|
||||||
|
builder => builder
|
||||||
|
.Name("test-attribute2")
|
||||||
|
.PropertyName("TestAttribute2")
|
||||||
|
.TypeName("SomeDictionary")
|
||||||
|
.AsDictionary("dict-prefix-", "string"),
|
||||||
|
},
|
||||||
|
ruleBuilders: new Action<TagMatchingRuleBuilder>[]
|
||||||
|
{
|
||||||
|
builder => builder
|
||||||
|
.RequireAttribute(attribute => attribute
|
||||||
|
.Name("required-attribute-one")
|
||||||
|
.NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.PrefixMatch))
|
||||||
|
},
|
||||||
|
configureAction: builder =>
|
||||||
|
{
|
||||||
|
builder
|
||||||
|
.AllowChildTag("allowed-child-one")
|
||||||
|
.AddMetadata("foo", "bar")
|
||||||
|
.TagOutputHint("Hint");
|
||||||
|
});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var serializedDescriptor = JsonConvert.SerializeObject(expectedDescriptor, TagHelperDescriptorJsonConverter.Instance, RazorDiagnosticJsonConverter.Instance);
|
||||||
|
var descriptor = JsonConvert.DeserializeObject<TagHelperDescriptor>(serializedDescriptor, TagHelperDescriptorJsonConverter.Instance, RazorDiagnosticJsonConverter.Instance);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.Equal(expectedDescriptor, descriptor, TagHelperDescriptorComparer.Default);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TagHelperDescriptor CreateTagHelperDescriptor(
|
||||||
|
string tagName,
|
||||||
|
string typeName,
|
||||||
|
string assemblyName,
|
||||||
|
IEnumerable<Action<ITagHelperBoundAttributeDescriptorBuilder>> attributes = null,
|
||||||
|
IEnumerable<Action<TagMatchingRuleBuilder>> ruleBuilders = null,
|
||||||
|
Action<ITagHelperDescriptorBuilder> configureAction = null)
|
||||||
|
{
|
||||||
|
var builder = ITagHelperDescriptorBuilder.Create(typeName, assemblyName);
|
||||||
|
|
||||||
|
if (attributes != null)
|
||||||
|
{
|
||||||
|
foreach (var attributeBuilder in attributes)
|
||||||
|
{
|
||||||
|
builder.BindAttribute(attributeBuilder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ruleBuilders != null)
|
||||||
|
{
|
||||||
|
foreach (var ruleBuilder in ruleBuilders)
|
||||||
|
{
|
||||||
|
builder.TagMatchingRule(innerRuleBuilder => {
|
||||||
|
innerRuleBuilder.RequireTagName(tagName);
|
||||||
|
ruleBuilder(innerRuleBuilder);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
builder.TagMatchingRule(ruleBuilder => ruleBuilder.RequireTagName(tagName));
|
||||||
|
}
|
||||||
|
|
||||||
|
configureAction?.Invoke(builder);
|
||||||
|
|
||||||
|
var descriptor = builder.Build();
|
||||||
|
|
||||||
|
return descriptor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
#if RAZOR_EXTENSION_DEVELOPER_MODE
|
#if RAZOR_EXTENSION_DEVELOPER_MODE
|
||||||
|
using System.Linq;
|
||||||
using Microsoft.AspNetCore.Razor.Evolution;
|
using Microsoft.AspNetCore.Razor.Evolution;
|
||||||
|
|
||||||
namespace Microsoft.VisualStudio.RazorExtension.RazorInfo
|
namespace Microsoft.VisualStudio.RazorExtension.RazorInfo
|
||||||
|
|
@ -17,9 +18,9 @@ namespace Microsoft.VisualStudio.RazorExtension.RazorInfo
|
||||||
|
|
||||||
public string AssemblyName => _descriptor.AssemblyName;
|
public string AssemblyName => _descriptor.AssemblyName;
|
||||||
|
|
||||||
public string TargetElement => _descriptor.TagName;
|
public string TargetElement => string.Join(", ", _descriptor.TagMatchingRules.Select(rule => rule.TagName));
|
||||||
|
|
||||||
public string TypeName => _descriptor.TypeName;
|
public string TypeName => _descriptor.Metadata[ITagHelperDescriptorBuilder.TypeNameKey];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
Loading…
Reference in New Issue