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 tagHelperRenderingContext = Context.TagHelperRenderingContext;
|
||||
var propertyValueAccessor = GetTagHelperPropertyAccessor(tagHelperVariableName, node.AttributeName, node.Descriptor);
|
||||
var propertyValueAccessor = GetTagHelperPropertyAccessor(node.IsIndexerNameMatch, tagHelperVariableName, node.AttributeName, node.Descriptor);
|
||||
|
||||
string previousValueAccessor;
|
||||
if (tagHelperRenderingContext.RenderedBoundAttributes.TryGetValue(node.AttributeName, out previousValueAccessor))
|
||||
|
|
@ -208,7 +208,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration
|
|||
tagHelperRenderingContext.RenderedBoundAttributes[node.AttributeName] = propertyValueAccessor;
|
||||
}
|
||||
|
||||
if (node.Descriptor.IsStringProperty)
|
||||
if (node.Descriptor.IsStringProperty || (node.IsIndexerNameMatch && node.Descriptor.IsIndexerStringProperty))
|
||||
{
|
||||
VisitDefault(node);
|
||||
|
||||
|
|
|
|||
|
|
@ -83,15 +83,16 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration
|
|||
protected static string GetTagHelperVariableName(string tagHelperTypeName) => "__" + tagHelperTypeName.Replace('.', '_');
|
||||
|
||||
protected static string GetTagHelperPropertyAccessor(
|
||||
bool isIndexerNameMatch,
|
||||
string tagHelperVariableName,
|
||||
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}\"]";
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -373,7 +373,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration
|
|||
public override void VisitSetPreallocatedTagHelperProperty(SetPreallocatedTagHelperPropertyIRNode node)
|
||||
{
|
||||
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 */;
|
||||
Context.Writer
|
||||
.WriteStartAssignment(propertyValueAccessor)
|
||||
|
|
@ -391,17 +391,18 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration
|
|||
{
|
||||
var tagHelperVariableName = GetTagHelperVariableName(node.TagHelperTypeName);
|
||||
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.
|
||||
if (node.Descriptor.IsIndexer &&
|
||||
tagHelperRenderingContext.VerifiedPropertyDictionaries.Add(node.Descriptor.PropertyName))
|
||||
if (node.IsIndexerNameMatch &&
|
||||
tagHelperRenderingContext.VerifiedPropertyDictionaries.Add(propertyName))
|
||||
{
|
||||
// Throw a reasonable Exception at runtime if the dictionary property is null.
|
||||
Context.Writer
|
||||
.Write("if (")
|
||||
.Write(tagHelperVariableName)
|
||||
.Write(".")
|
||||
.Write(node.Descriptor.PropertyName)
|
||||
.Write(propertyName)
|
||||
.WriteLine(" == null)");
|
||||
using (Context.Writer.BuildScope())
|
||||
{
|
||||
|
|
@ -415,13 +416,13 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration
|
|||
.WriteParameterSeparator()
|
||||
.WriteStringLiteral(node.TagHelperTypeName)
|
||||
.WriteParameterSeparator()
|
||||
.WriteStringLiteral(node.Descriptor.PropertyName)
|
||||
.WriteStringLiteral(propertyName)
|
||||
.WriteEndMethodInvocation(endLine: false) // End of method call
|
||||
.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;
|
||||
if (tagHelperRenderingContext.RenderedBoundAttributes.TryGetValue(node.AttributeName, out previousValueAccessor))
|
||||
|
|
@ -438,7 +439,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration
|
|||
tagHelperRenderingContext.RenderedBoundAttributes[node.AttributeName] = propertyValueAccessor;
|
||||
}
|
||||
|
||||
if (node.Descriptor.IsStringProperty)
|
||||
if (node.Descriptor.IsStringProperty || (node.IsIndexerNameMatch && node.Descriptor.IsIndexerStringProperty))
|
||||
{
|
||||
Context.Writer.WriteMethodInvocation("BeginWriteTagHelperAttribute" /* ORIGINAL: BeginWriteTagHelperAttributeMethodName */);
|
||||
|
||||
|
|
@ -683,8 +684,11 @@ namespace Microsoft.AspNetCore.Razor.Evolution.CodeGeneration
|
|||
else if (node is TemplateIRNode)
|
||||
{
|
||||
var attributeValueNode = (SetTagHelperPropertyIRNode)node.Parent;
|
||||
var expectedTypeName = attributeValueNode.IsIndexerNameMatch ?
|
||||
attributeValueNode.Descriptor.IndexerTypeName :
|
||||
attributeValueNode.Descriptor.TypeName;
|
||||
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),
|
||||
documentLocation.Length);
|
||||
Context.Diagnostics.Add(RazorDiagnostic.Create(error));
|
||||
|
|
|
|||
|
|
@ -18,8 +18,8 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
ThrowForMissingDependency(syntaxTree);
|
||||
|
||||
var builder = RazorIRBuilder.Document();
|
||||
|
||||
var document = (DocumentIRNode)builder.Current;
|
||||
|
||||
document.Options = syntaxTree.Options;
|
||||
|
||||
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,
|
||||
};
|
||||
|
|
@ -220,10 +221,12 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
private class MainSourceVisitor : LoweringVisitor
|
||||
{
|
||||
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)
|
||||
{
|
||||
_tagHelperPrefix = tagHelperPrefix;
|
||||
}
|
||||
|
||||
public override void VisitDirectiveToken(DirectiveTokenChunkGenerator chunkGenerator, Span span)
|
||||
|
|
@ -467,9 +470,9 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
});
|
||||
|
||||
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()
|
||||
|
|
@ -482,8 +485,8 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
|
||||
_builder.Pop(); // Pop InitializeTagHelperStructureIRNode
|
||||
|
||||
AddTagHelperCreation(tagHelperBlock.Descriptors);
|
||||
AddTagHelperAttributes(tagHelperBlock.Attributes, tagHelperBlock.Descriptors);
|
||||
AddTagHelperCreation(tagHelperBlock.Binding);
|
||||
AddTagHelperAttributes(tagHelperBlock.Attributes, tagHelperBlock.Binding);
|
||||
AddExecuteTagHelpers();
|
||||
|
||||
_builder.Pop(); // Pop TagHelperIRNode
|
||||
|
|
@ -497,19 +500,22 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
_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)
|
||||
{
|
||||
var typeName = descriptor.Metadata[ITagHelperDescriptorBuilder.TypeNameKey];
|
||||
var createTagHelper = new CreateTagHelperIRNode()
|
||||
{
|
||||
TagHelperTypeName = descriptor.TypeName,
|
||||
TagHelperTypeName = typeName,
|
||||
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);
|
||||
foreach (var attribute in attributes)
|
||||
{
|
||||
var attributeValueNode = attribute.Value;
|
||||
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))
|
||||
{
|
||||
|
|
@ -537,16 +544,21 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
|
||||
foreach (var associatedDescriptor in associatedDescriptors)
|
||||
{
|
||||
var associatedAttributeDescriptor = associatedDescriptor.Attributes.First(
|
||||
attributeDescriptor => attributeDescriptor.IsNameMatch(attribute.Name));
|
||||
var associatedAttributeDescriptor = associatedDescriptor.BoundAttributes.First(
|
||||
attributeDescriptor => attributeDescriptor.CanMatchName(attribute.Name));
|
||||
var tagHelperTypeName = associatedDescriptor.Metadata[ITagHelperDescriptorBuilder.TypeNameKey];
|
||||
var attributePropertyName = associatedAttributeDescriptor.Metadata[ITagHelperBoundAttributeDescriptorBuilder.PropertyNameKey];
|
||||
|
||||
var setTagHelperProperty = new SetTagHelperPropertyIRNode()
|
||||
{
|
||||
PropertyName = associatedAttributeDescriptor.PropertyName,
|
||||
PropertyName = attributePropertyName,
|
||||
AttributeName = attribute.Name,
|
||||
TagHelperTypeName = associatedDescriptor.TypeName,
|
||||
TagHelperTypeName = tagHelperTypeName,
|
||||
Descriptor = associatedAttributeDescriptor,
|
||||
Binding = tagHelperBinding,
|
||||
ValueStyle = attribute.ValueStyle,
|
||||
Source = BuildSourceSpanFromNode(attributeValueNode)
|
||||
Source = BuildSourceSpanFromNode(attributeValueNode),
|
||||
IsIndexerNameMatch = associatedAttributeDescriptor.IsIndexerNameMatch(attribute.Name),
|
||||
};
|
||||
|
||||
_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.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Razor.Evolution.Legacy;
|
||||
|
||||
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.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Razor.Evolution.Legacy;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate
|
||||
{
|
||||
|
|
@ -21,7 +22,11 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate
|
|||
|
||||
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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -23,7 +23,11 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Intermediate
|
|||
|
||||
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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
TagName = source.TagName;
|
||||
Descriptors = source.Descriptors;
|
||||
Binding = source.BindingResult;
|
||||
Attributes = new List<TagHelperAttributeNode>(source.Attributes);
|
||||
_start = source.Start;
|
||||
TagMode = source.TagMode;
|
||||
|
|
@ -61,9 +61,9 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
|||
public TagMode TagMode { get; }
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="TagHelperDescriptor"/>s for the HTML element.
|
||||
/// <see cref="TagHelperDescriptor"/> bindings for the HTML element.
|
||||
/// </summary>
|
||||
public IEnumerable<TagHelperDescriptor> Descriptors { get; }
|
||||
public TagHelperBinding Binding { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The HTML attributes.
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
|||
: base(original)
|
||||
{
|
||||
TagName = original.TagName;
|
||||
Descriptors = original.Descriptors;
|
||||
BindingResult = original.Binding;
|
||||
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="start">Starting location 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
|
||||
/// tag.</param>
|
||||
/// <param name="bindingResult"></param>
|
||||
public TagHelperBlockBuilder(
|
||||
string tagName,
|
||||
TagMode tagMode,
|
||||
SourceLocation start,
|
||||
IList<TagHelperAttributeNode> attributes,
|
||||
IEnumerable<TagHelperDescriptor> descriptors)
|
||||
TagHelperBinding bindingResult)
|
||||
{
|
||||
TagName = tagName;
|
||||
TagMode = tagMode;
|
||||
Start = start;
|
||||
Descriptors = descriptors;
|
||||
BindingResult = bindingResult;
|
||||
Attributes = new List<TagHelperAttributeNode>(attributes);
|
||||
Type = BlockType.Tag;
|
||||
ChunkGenerator = new TagHelperChunkGenerator(descriptors);
|
||||
ChunkGenerator = new TagHelperChunkGenerator();
|
||||
}
|
||||
|
||||
// Internal for testing
|
||||
|
|
@ -60,7 +59,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
|||
TagMode = tagMode;
|
||||
Attributes = attributes;
|
||||
Type = BlockType.Tag;
|
||||
ChunkGenerator = new TagHelperChunkGenerator(tagHelperDescriptors: null);
|
||||
ChunkGenerator = new TagHelperChunkGenerator();
|
||||
|
||||
// Children is IList, no AddRange
|
||||
foreach (var child in children)
|
||||
|
|
@ -89,7 +88,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
|||
/// <summary>
|
||||
/// <see cref="TagHelperDescriptor"/>s for the HTML element.
|
||||
/// </summary>
|
||||
public IEnumerable<TagHelperDescriptor> Descriptors { get; }
|
||||
public TagHelperBinding BindingResult { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The HTML attributes.
|
||||
|
|
|
|||
|
|
@ -17,28 +17,24 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
|||
string tagName,
|
||||
bool validStructure,
|
||||
Block tag,
|
||||
IEnumerable<TagHelperDescriptor> descriptors,
|
||||
TagHelperBinding bindingResult,
|
||||
ErrorSink errorSink)
|
||||
{
|
||||
// There will always be at least one child for the '<'.
|
||||
var start = tag.Children.First().Start;
|
||||
var attributes = GetTagAttributes(tagName, validStructure, tag, descriptors, errorSink);
|
||||
var tagMode = GetTagMode(tagName, tag, descriptors, errorSink);
|
||||
var attributes = GetTagAttributes(tagName, validStructure, tag, bindingResult, 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(
|
||||
string tagName,
|
||||
bool validStructure,
|
||||
Block tagBlock,
|
||||
IEnumerable<TagHelperDescriptor> descriptors,
|
||||
TagHelperBinding bindingResult,
|
||||
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>();
|
||||
|
||||
// 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;
|
||||
if (child.IsBlock)
|
||||
{
|
||||
result = TryParseBlock(tagName, (Block)child, descriptors, errorSink);
|
||||
result = TryParseBlock(tagName, (Block)child, bindingResult.Descriptors, errorSink);
|
||||
}
|
||||
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.
|
||||
|
|
@ -77,7 +73,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
|||
LegacyResources.FormatRewriterError_EmptyTagHelperBoundAttribute(
|
||||
result.AttributeName,
|
||||
tagName,
|
||||
GetPropertyType(result.AttributeName, descriptors)),
|
||||
GetPropertyType(result.AttributeName, bindingResult.Descriptors)),
|
||||
result.AttributeName.Length);
|
||||
}
|
||||
|
||||
|
|
@ -118,7 +114,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
|||
private static TagMode GetTagMode(
|
||||
string tagName,
|
||||
Block beginTagBlock,
|
||||
IEnumerable<TagHelperDescriptor> descriptors,
|
||||
TagHelperBinding bindingResult,
|
||||
ErrorSink errorSink)
|
||||
{
|
||||
var childSpan = beginTagBlock.FindLastDescendentSpan();
|
||||
|
|
@ -129,12 +125,15 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
|||
return TagMode.SelfClosing;
|
||||
}
|
||||
|
||||
var baseDescriptor = descriptors.FirstOrDefault(
|
||||
descriptor => descriptor.TagStructure != TagStructure.Unspecified);
|
||||
var resolvedTagStructure = baseDescriptor?.TagStructure ?? TagStructure.Unspecified;
|
||||
if (resolvedTagStructure == TagStructure.WithoutEndTag)
|
||||
foreach (var descriptor in bindingResult.Descriptors)
|
||||
{
|
||||
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;
|
||||
|
|
@ -656,8 +655,16 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
|||
private static string GetPropertyType(string name, IEnumerable<TagHelperDescriptor> 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.
|
||||
|
|
@ -665,10 +672,12 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
|||
{
|
||||
var firstBoundAttribute = FindFirstBoundAttribute(name, descriptors);
|
||||
var isBoundAttribute = firstBoundAttribute != null;
|
||||
var isBoundNonStringAttribute = isBoundAttribute && !firstBoundAttribute.IsStringProperty;
|
||||
var isBoundNonStringAttribute = isBoundAttribute &&
|
||||
!(firstBoundAttribute.IsStringProperty ||
|
||||
(firstBoundAttribute.IsIndexerNameMatch(name) && firstBoundAttribute.IsIndexerStringProperty));
|
||||
var isMissingDictionaryKey = isBoundAttribute &&
|
||||
firstBoundAttribute.IsIndexer &&
|
||||
name.Length == firstBoundAttribute.Name.Length;
|
||||
firstBoundAttribute.IndexerNamePrefix != null &&
|
||||
name.Length == firstBoundAttribute.IndexerNamePrefix.Length;
|
||||
|
||||
return new TryParseResult
|
||||
{
|
||||
|
|
@ -680,15 +689,13 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
|||
}
|
||||
|
||||
// Finds first TagHelperAttributeDescriptor matching given name.
|
||||
private static TagHelperAttributeDescriptor FindFirstBoundAttribute(
|
||||
private static BoundAttributeDescriptor FindFirstBoundAttribute(
|
||||
string name,
|
||||
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
|
||||
.SelectMany(descriptor => descriptor.Attributes)
|
||||
.FirstOrDefault(attributeDescriptor => attributeDescriptor.IsNameMatch(name));
|
||||
.SelectMany(descriptor => descriptor.BoundAttributes)
|
||||
.FirstOrDefault(attributeDescriptor => attributeDescriptor.CanMatchName(name));
|
||||
|
||||
return firstBoundAttribute;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,25 +1,10 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||
{
|
||||
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)
|
||||
{
|
||||
//var tagHelperBlock = target as TagHelperBlock;
|
||||
|
|
|
|||
|
|
@ -8,10 +8,6 @@ using Microsoft.Extensions.Internal;
|
|||
|
||||
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>
|
||||
{
|
||||
/// <summary>
|
||||
|
|
@ -20,27 +16,34 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
|||
public static readonly TagHelperDescriptorComparer Default = new TagHelperDescriptorComparer();
|
||||
|
||||
/// <summary>
|
||||
/// An instance of <see cref="TagHelperDescriptorComparer"/> that only compares
|
||||
/// <see cref="TagHelperDescriptor.TypeName"/>.
|
||||
/// A default instance of the <see cref="TagHelperDescriptorComparer"/> that does case-sensitive comparison.
|
||||
/// </summary>
|
||||
public static readonly TagHelperDescriptorComparer TypeName = new TypeNameTagHelperDescriptorComparer();
|
||||
internal static readonly TagHelperDescriptorComparer CaseSensitive =
|
||||
new TagHelperDescriptorComparer(caseSensitive: true);
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new <see cref="TagHelperDescriptorComparer"/> instance.
|
||||
/// </summary>
|
||||
protected TagHelperDescriptorComparer()
|
||||
private readonly StringComparer _stringComparer;
|
||||
private readonly StringComparison _stringComparison;
|
||||
private readonly BoundAttributeDescriptorComparer _boundAttributeComparer;
|
||||
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)
|
||||
{
|
||||
if (descriptorX == descriptorY)
|
||||
|
|
@ -49,28 +52,30 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
|||
}
|
||||
|
||||
return descriptorX != null &&
|
||||
string.Equals(descriptorX.TypeName, descriptorY.TypeName, StringComparison.Ordinal) &&
|
||||
string.Equals(descriptorX.TagName, descriptorY.TagName, StringComparison.OrdinalIgnoreCase) &&
|
||||
string.Equals(descriptorX.Kind, descriptorY.Kind, StringComparison.Ordinal) &&
|
||||
string.Equals(descriptorX.AssemblyName, descriptorY.AssemblyName, StringComparison.Ordinal) &&
|
||||
string.Equals(
|
||||
descriptorX.RequiredParent,
|
||||
descriptorY.RequiredParent,
|
||||
StringComparison.OrdinalIgnoreCase) &&
|
||||
Enumerable.SequenceEqual(
|
||||
descriptorX.RequiredAttributes.OrderBy(attribute => attribute.Name, StringComparer.OrdinalIgnoreCase),
|
||||
descriptorY.RequiredAttributes.OrderBy(attribute => attribute.Name, StringComparer.OrdinalIgnoreCase),
|
||||
TagHelperRequiredAttributeDescriptorComparer.Default) &&
|
||||
(descriptorX.AllowedChildren == descriptorY.AllowedChildren ||
|
||||
(descriptorX.AllowedChildren != null &&
|
||||
descriptorY.AllowedChildren != null &&
|
||||
descriptorX.BoundAttributes.OrderBy(attribute => attribute.Name, _stringComparer),
|
||||
descriptorY.BoundAttributes.OrderBy(attribute => attribute.Name, _stringComparer),
|
||||
_boundAttributeComparer) &&
|
||||
Enumerable.SequenceEqual(
|
||||
descriptorX.AllowedChildren.OrderBy(child => child, StringComparer.OrdinalIgnoreCase),
|
||||
descriptorY.AllowedChildren.OrderBy(child => child, StringComparer.OrdinalIgnoreCase),
|
||||
StringComparer.OrdinalIgnoreCase))) &&
|
||||
descriptorX.TagStructure == descriptorY.TagStructure &&
|
||||
descriptorX.TagMatchingRules.OrderBy(rule => rule.TagName, _stringComparer),
|
||||
descriptorY.TagMatchingRules.OrderBy(rule => rule.TagName, _stringComparer),
|
||||
_tagMatchingRuleComparer) &&
|
||||
(descriptorX.AllowedChildTags == descriptorY.AllowedChildTags ||
|
||||
(descriptorX.AllowedChildTags != null &&
|
||||
descriptorY.AllowedChildTags != null &&
|
||||
Enumerable.SequenceEqual(
|
||||
descriptorX.PropertyBag.OrderBy(propertyX => propertyX.Key, StringComparer.Ordinal),
|
||||
descriptorY.PropertyBag.OrderBy(propertyY => propertyY.Key, StringComparer.Ordinal));
|
||||
descriptorX.AllowedChildTags.OrderBy(child => child, _stringComparer),
|
||||
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 />
|
||||
|
|
@ -82,59 +87,35 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
|||
}
|
||||
|
||||
var hashCodeCombiner = HashCodeCombiner.Start();
|
||||
hashCodeCombiner.Add(descriptor.TypeName, StringComparer.Ordinal);
|
||||
hashCodeCombiner.Add(descriptor.TagName, StringComparer.OrdinalIgnoreCase);
|
||||
hashCodeCombiner.Add(descriptor.Kind);
|
||||
hashCodeCombiner.Add(descriptor.AssemblyName, StringComparer.Ordinal);
|
||||
hashCodeCombiner.Add(descriptor.RequiredParent, StringComparer.OrdinalIgnoreCase);
|
||||
hashCodeCombiner.Add(descriptor.TagStructure);
|
||||
|
||||
var attributes = descriptor.RequiredAttributes.OrderBy(
|
||||
attribute => attribute.Name,
|
||||
StringComparer.OrdinalIgnoreCase);
|
||||
foreach (var attribute in attributes)
|
||||
var boundAttributes = descriptor.BoundAttributes.OrderBy(attribute => attribute.Name, _stringComparer);
|
||||
foreach (var attribute in boundAttributes)
|
||||
{
|
||||
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)
|
||||
{
|
||||
hashCodeCombiner.Add(child, StringComparer.OrdinalIgnoreCase);
|
||||
hashCodeCombiner.Add(child, _stringComparer);
|
||||
}
|
||||
}
|
||||
|
||||
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 = "*";
|
||||
|
||||
private IDictionary<string, HashSet<TagHelperDescriptor>> _registrations;
|
||||
private string _tagHelperPrefix;
|
||||
private readonly string _tagHelperPrefix;
|
||||
|
||||
/// <summary>
|
||||
/// Instantiates a new instance of the <see cref="TagHelperDescriptorProvider"/>.
|
||||
/// </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>
|
||||
public TagHelperDescriptorProvider(IEnumerable<TagHelperDescriptor> descriptors)
|
||||
public TagHelperDescriptorProvider(string tagHelperPrefix, IEnumerable<TagHelperDescriptor> descriptors)
|
||||
{
|
||||
_tagHelperPrefix = tagHelperPrefix;
|
||||
_registrations = new Dictionary<string, HashSet<TagHelperDescriptor>>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
// 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"/>.
|
||||
/// Will return an empty <see cref="Enumerable" /> if no <see cref="TagHelperDescriptor"/>s are
|
||||
/// found.</returns>
|
||||
public IEnumerable<TagHelperDescriptor> GetDescriptors(
|
||||
public TagHelperBinding GetTagHelperBinding(
|
||||
string tagName,
|
||||
IEnumerable<KeyValuePair<string, string>> attributes,
|
||||
string parentTagName)
|
||||
|
|
@ -52,14 +54,13 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
|||
!tagName.StartsWith(_tagHelperPrefix, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
// 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;
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
|
@ -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
|
||||
// with all the catch-all descriptors.
|
||||
HashSet<TagHelperDescriptor> matchingDescriptors;
|
||||
if (_registrations.TryGetValue(tagName, out matchingDescriptors))
|
||||
if (_registrations.TryGetValue(tagName, out HashSet<TagHelperDescriptor> matchingDescriptors))
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (HasRequiredAttributes(descriptor, attributes) &&
|
||||
HasRequiredParentTag(descriptor, parentTagName))
|
||||
var applicableRules = descriptor.TagMatchingRules.Where(
|
||||
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(
|
||||
TagHelperDescriptor descriptor,
|
||||
private bool MatchesRule(
|
||||
TagMatchingRule rule,
|
||||
IEnumerable<KeyValuePair<string, string>> tagAttributes,
|
||||
string tagNameWithoutPrefix,
|
||||
string parentTagName)
|
||||
{
|
||||
return descriptor.RequiredParent == null ||
|
||||
string.Equals(parentTagName, descriptor.RequiredParent, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
// Verify tag name
|
||||
if (rule.TagName != ElementCatchAllTarget &&
|
||||
rule.TagName != null &&
|
||||
!string.Equals(tagNameWithoutPrefix, rule.TagName, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool HasRequiredAttributes(
|
||||
TagHelperDescriptor descriptor,
|
||||
IEnumerable<KeyValuePair<string, string>> attributes)
|
||||
{
|
||||
return descriptor.RequiredAttributes.All(
|
||||
requiredAttribute => attributes.Any(
|
||||
attribute => requiredAttribute.IsMatch(attribute.Key, attribute.Value)));
|
||||
// Verify parent tag
|
||||
if (rule.ParentTag != null && !string.Equals(parentTagName, rule.ParentTag, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!rule.Attributes.All(
|
||||
requiredAttribute => tagAttributes.Any(
|
||||
attribute => requiredAttribute.IsMatch(attribute.Key, attribute.Value))))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void Register(TagHelperDescriptor descriptor)
|
||||
{
|
||||
HashSet<TagHelperDescriptor> descriptorSet;
|
||||
|
||||
if (_tagHelperPrefix == null)
|
||||
foreach (var rule in descriptor.TagMatchingRules)
|
||||
{
|
||||
_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"
|
||||
};
|
||||
|
||||
private readonly string _tagHelperPrefix;
|
||||
private readonly List<KeyValuePair<string, string>> _htmlAttributeTracker;
|
||||
private readonly StringBuilder _attributeValueBuilder;
|
||||
private readonly TagHelperDescriptorProvider _provider;
|
||||
|
|
@ -45,8 +46,9 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
|||
private BlockBuilder _currentBlock;
|
||||
private string _currentParentTagName;
|
||||
|
||||
public TagHelperParseTreeRewriter(TagHelperDescriptorProvider provider)
|
||||
public TagHelperParseTreeRewriter(string tagHelperPrefix, TagHelperDescriptorProvider provider)
|
||||
{
|
||||
_tagHelperPrefix = tagHelperPrefix;
|
||||
_provider = provider;
|
||||
_trackerStack = new Stack<TagBlockTracker>();
|
||||
_blockStack = new Stack<BlockBuilder>();
|
||||
|
|
@ -172,7 +174,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
|||
return false;
|
||||
}
|
||||
|
||||
var descriptors = Enumerable.Empty<TagHelperDescriptor>();
|
||||
TagHelperBinding tagHelperBinding;
|
||||
|
||||
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.
|
||||
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 (!descriptors.Any())
|
||||
if (tagHelperBinding == null)
|
||||
{
|
||||
// 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
|
||||
|
|
@ -206,7 +208,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
|||
}
|
||||
|
||||
ValidateParentAllowsTagHelper(tagName, tagBlock, errorSink);
|
||||
ValidateDescriptors(descriptors, tagName, tagBlock, errorSink);
|
||||
ValidateBinding(tagHelperBinding, tagName, tagBlock, errorSink);
|
||||
|
||||
// We're in a start TagHelper block.
|
||||
var validTagStructure = ValidateTagSyntax(tagName, tagBlock, errorSink);
|
||||
|
|
@ -215,7 +217,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
|||
tagName,
|
||||
validTagStructure,
|
||||
tagBlock,
|
||||
descriptors,
|
||||
tagHelperBinding,
|
||||
errorSink);
|
||||
|
||||
// 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
|
||||
{
|
||||
descriptors = _provider.GetDescriptors(
|
||||
tagHelperBinding = _provider.GetTagHelperBinding(
|
||||
tagName,
|
||||
attributes: Enumerable.Empty<KeyValuePair<string, string>>(),
|
||||
parentTagName: _currentParentTagName);
|
||||
|
||||
// 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.
|
||||
if (!descriptors.Any())
|
||||
if (tagHelperBinding == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var invalidDescriptor = descriptors.FirstOrDefault(
|
||||
descriptor => descriptor.TagStructure == TagStructure.WithoutEndTag);
|
||||
if (invalidDescriptor != null)
|
||||
foreach (var descriptor in tagHelperBinding.Descriptors)
|
||||
{
|
||||
// End tag TagHelper that states it shouldn't have an end tag.
|
||||
errorSink.OnError(
|
||||
SourceLocationTracker.Advance(tagBlock.Start, "</"),
|
||||
LegacyResources.FormatTagHelperParseTreeRewriter_EndTagTagHelperMustNotHaveAnEndTag(
|
||||
tagName,
|
||||
invalidDescriptor.TypeName,
|
||||
invalidDescriptor.TagStructure),
|
||||
tagName.Length);
|
||||
var boundRules = tagHelperBinding.GetBoundRules(descriptor);
|
||||
var invalidRule = boundRules.FirstOrDefault(rule => rule.TagStructure == TagStructure.WithoutEndTag);
|
||||
|
||||
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
|
||||
|
|
@ -463,7 +471,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
|||
return false;
|
||||
}
|
||||
|
||||
return _currentTagHelperTracker.AllowedChildren != null;
|
||||
return _currentTagHelperTracker.AllowedChildren != null && _currentTagHelperTracker.AllowedChildren.Count > 0;
|
||||
}
|
||||
|
||||
private void ValidateParentAllowsContent(Span child, ErrorSink errorSink)
|
||||
|
|
@ -536,8 +544,8 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
|||
errorSink.OnError(errorStart, errorMessage, tagName.Length);
|
||||
}
|
||||
|
||||
private static void ValidateDescriptors(
|
||||
IEnumerable<TagHelperDescriptor> descriptors,
|
||||
private static void ValidateBinding(
|
||||
TagHelperBinding bindingResult,
|
||||
string tagName,
|
||||
Block tagBlock,
|
||||
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
|
||||
// multiple descriptors that expect different TagStructures (other than TagStructure.Unspecified).
|
||||
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 (baseDescriptor != null && baseDescriptor.TagStructure != descriptor.TagStructure)
|
||||
if (rule.TagStructure != TagStructure.Unspecified)
|
||||
{
|
||||
errorSink.OnError(
|
||||
tagBlock.Start,
|
||||
LegacyResources.FormatTagHelperParseTreeRewriter_InconsistentTagStructure(
|
||||
baseDescriptor.TypeName,
|
||||
descriptor.TypeName,
|
||||
tagName,
|
||||
nameof(TagHelperDescriptor.TagStructure)),
|
||||
tagBlock.Length);
|
||||
}
|
||||
// Can't have a set of TagHelpers that expect different structures.
|
||||
if (baseStructure.HasValue && baseStructure != rule.TagStructure)
|
||||
{
|
||||
var baseDescriptorTypeName = baseDescriptor.Metadata[ITagHelperDescriptorBuilder.TypeNameKey];
|
||||
var descriptorTypeName = descriptor.Metadata[ITagHelperDescriptorBuilder.TypeNameKey];
|
||||
errorSink.OnError(
|
||||
tagBlock.Start,
|
||||
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)
|
||||
{
|
||||
_currentTagHelperTracker = new TagHelperBlockTracker(builder);
|
||||
_currentTagHelperTracker = new TagHelperBlockTracker(_tagHelperPrefix, builder);
|
||||
PushTrackerStack(_currentTagHelperTracker);
|
||||
|
||||
TrackBlock(builder);
|
||||
|
|
@ -830,19 +846,22 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
|||
|
||||
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)
|
||||
{
|
||||
_tagHelperPrefix = tagHelperPrefix;
|
||||
Builder = builder;
|
||||
|
||||
if (Builder.Descriptors.Any(descriptor => descriptor.AllowedChildren != null))
|
||||
if (Builder.BindingResult.Descriptors.Any(descriptor => descriptor.AllowedChildTags != null))
|
||||
{
|
||||
AllowedChildren = Builder.Descriptors
|
||||
.Where(descriptor => descriptor.AllowedChildren != null)
|
||||
.SelectMany(descriptor => descriptor.AllowedChildren)
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase);
|
||||
AllowedChildren = Builder.BindingResult.Descriptors
|
||||
.Where(descriptor => descriptor.AllowedChildTags != null)
|
||||
.SelectMany(descriptor => descriptor.AllowedChildTags)
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||
.ToList();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -850,18 +869,17 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
|||
|
||||
public uint OpenMatchingTags { get; set; }
|
||||
|
||||
public IEnumerable<string> AllowedChildren { get; }
|
||||
public IReadOnlyList<string> AllowedChildren { get; }
|
||||
|
||||
public IEnumerable<string> PrefixedAllowedChildren
|
||||
public IReadOnlyList<string> PrefixedAllowedChildren
|
||||
{
|
||||
get
|
||||
{
|
||||
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 => prefix + allowedChild);
|
||||
_prefixedAllowedChildren = AllowedChildren.Select(allowedChild => _tagHelperPrefix + allowedChild).ToList();
|
||||
}
|
||||
|
||||
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.Remote.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("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")]
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -15,225 +15,379 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
/// </summary>
|
||||
internal static string ArgumentCannotBeNullOrEmpty
|
||||
{
|
||||
get { return GetString("ArgumentCannotBeNullOrEmpty"); }
|
||||
get => GetString("ArgumentCannotBeNullOrEmpty");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Value cannot be null or an empty string.
|
||||
/// </summary>
|
||||
internal static string FormatArgumentCannotBeNullOrEmpty()
|
||||
{
|
||||
return GetString("ArgumentCannotBeNullOrEmpty");
|
||||
}
|
||||
=> GetString("ArgumentCannotBeNullOrEmpty");
|
||||
|
||||
/// <summary>
|
||||
/// The '{0}' feature requires a '{1}' provided by the '{2}'.
|
||||
/// </summary>
|
||||
internal static string FeatureDependencyMissing
|
||||
{
|
||||
get { return GetString("FeatureDependencyMissing"); }
|
||||
get => GetString("FeatureDependencyMissing");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The '{0}' feature requires a '{1}' provided by the '{2}'.
|
||||
/// </summary>
|
||||
internal static string FormatFeatureDependencyMissing(object p0, object p1, object p2)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("FeatureDependencyMissing"), p0, p1, p2);
|
||||
}
|
||||
=> string.Format(CultureInfo.CurrentCulture, GetString("FeatureDependencyMissing"), p0, p1, p2);
|
||||
|
||||
/// <summary>
|
||||
/// Invalid tag helper directive look up text '{0}'. The correct look up text format is: "typeName, assemblyName".
|
||||
/// </summary>
|
||||
internal static string InvalidTagHelperLookupText
|
||||
{
|
||||
get { return GetString("InvalidTagHelperLookupText"); }
|
||||
get => GetString("InvalidTagHelperLookupText");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invalid tag helper directive look up text '{0}'. The correct look up text format is: "typeName, assemblyName".
|
||||
/// </summary>
|
||||
internal static string FormatInvalidTagHelperLookupText(object p0)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("InvalidTagHelperLookupText"), p0);
|
||||
}
|
||||
=> string.Format(CultureInfo.CurrentCulture, GetString("InvalidTagHelperLookupText"), p0);
|
||||
|
||||
/// <summary>
|
||||
/// Invalid tag helper directive '{0}' value. '{1}' is not allowed in prefix '{2}'.
|
||||
/// </summary>
|
||||
internal static string InvalidTagHelperPrefixValue
|
||||
{
|
||||
get { return GetString("InvalidTagHelperPrefixValue"); }
|
||||
get => GetString("InvalidTagHelperPrefixValue");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invalid tag helper directive '{0}' value. '{1}' is not allowed in prefix '{2}'.
|
||||
/// </summary>
|
||||
internal static string FormatInvalidTagHelperPrefixValue(object p0, object p1, object p2)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("InvalidTagHelperPrefixValue"), p0, p1, p2);
|
||||
}
|
||||
=> string.Format(CultureInfo.CurrentCulture, GetString("InvalidTagHelperPrefixValue"), p0, p1, p2);
|
||||
|
||||
/// <summary>
|
||||
/// The '{0}' operation is not valid when the builder is empty.
|
||||
/// </summary>
|
||||
internal static string IRBuilder_PopInvalid
|
||||
{
|
||||
get { return GetString("IRBuilder_PopInvalid"); }
|
||||
get => GetString("IRBuilder_PopInvalid");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The '{0}' operation is not valid when the builder is empty.
|
||||
/// </summary>
|
||||
internal static string FormatIRBuilder_PopInvalid(object p0)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("IRBuilder_PopInvalid"), p0);
|
||||
}
|
||||
=> string.Format(CultureInfo.CurrentCulture, GetString("IRBuilder_PopInvalid"), p0);
|
||||
|
||||
/// <summary>
|
||||
/// The specified encoding '{0}' does not match the content's encoding '{1}'.
|
||||
/// </summary>
|
||||
internal static string MismatchedContentEncoding
|
||||
{
|
||||
get { return GetString("MismatchedContentEncoding"); }
|
||||
get => GetString("MismatchedContentEncoding");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The specified encoding '{0}' does not match the content's encoding '{1}'.
|
||||
/// </summary>
|
||||
internal static string FormatMismatchedContentEncoding(object p0, object p1)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("MismatchedContentEncoding"), p0, p1);
|
||||
}
|
||||
=> string.Format(CultureInfo.CurrentCulture, GetString("MismatchedContentEncoding"), p0, p1);
|
||||
|
||||
/// <summary>
|
||||
/// The '{0}' phase requires a '{1}' provided by the '{2}'.
|
||||
/// </summary>
|
||||
internal static string PhaseDependencyMissing
|
||||
{
|
||||
get { return GetString("PhaseDependencyMissing"); }
|
||||
get => GetString("PhaseDependencyMissing");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The '{0}' phase requires a '{1}' provided by the '{2}'.
|
||||
/// </summary>
|
||||
internal static string FormatPhaseDependencyMissing(object p0, object p1, object p2)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("PhaseDependencyMissing"), p0, p1, p2);
|
||||
}
|
||||
=> string.Format(CultureInfo.CurrentCulture, GetString("PhaseDependencyMissing"), p0, p1, p2);
|
||||
|
||||
/// <summary>
|
||||
/// The phase must be initialized by setting the '{0}' property.
|
||||
/// </summary>
|
||||
internal static string PhaseMustBeInitialized
|
||||
{
|
||||
get { return GetString("PhaseMustBeInitialized"); }
|
||||
get => GetString("PhaseMustBeInitialized");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The phase must be initialized by setting the '{0}' property.
|
||||
/// </summary>
|
||||
internal static string FormatPhaseMustBeInitialized(object p0)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("PhaseMustBeInitialized"), p0);
|
||||
}
|
||||
=> string.Format(CultureInfo.CurrentCulture, GetString("PhaseMustBeInitialized"), p0);
|
||||
|
||||
/// <summary>
|
||||
/// Tag helper directive assembly name cannot be null or empty.
|
||||
/// </summary>
|
||||
internal static string TagHelperAssemblyNameCannotBeEmptyOrNull
|
||||
{
|
||||
get { return GetString("TagHelperAssemblyNameCannotBeEmptyOrNull"); }
|
||||
get => GetString("TagHelperAssemblyNameCannotBeEmptyOrNull");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tag helper directive assembly name cannot be null or empty.
|
||||
/// </summary>
|
||||
internal static string FormatTagHelperAssemblyNameCannotBeEmptyOrNull()
|
||||
{
|
||||
return GetString("TagHelperAssemblyNameCannotBeEmptyOrNull");
|
||||
}
|
||||
=> GetString("TagHelperAssemblyNameCannotBeEmptyOrNull");
|
||||
|
||||
/// <summary>
|
||||
/// The assembly '{0}' could not be resolved or contains no tag helpers.
|
||||
/// </summary>
|
||||
internal static string TagHelperAssemblyCouldNotBeResolved
|
||||
{
|
||||
get { return GetString("TagHelperAssemblyCouldNotBeResolved"); }
|
||||
get => GetString("TagHelperAssemblyCouldNotBeResolved");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The assembly '{0}' could not be resolved or contains no tag helpers.
|
||||
/// </summary>
|
||||
internal static string FormatTagHelperAssemblyCouldNotBeResolved(object p0)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperAssemblyCouldNotBeResolved"), p0);
|
||||
}
|
||||
=> string.Format(CultureInfo.CurrentCulture, GetString("TagHelperAssemblyCouldNotBeResolved"), p0);
|
||||
|
||||
/// <summary>
|
||||
/// Path must begin with a forward slash '/'.
|
||||
/// </summary>
|
||||
internal static string RazorProject_PathMustStartWithForwardSlash
|
||||
{
|
||||
get { return GetString("RazorProject_PathMustStartWithForwardSlash"); }
|
||||
get => GetString("RazorProject_PathMustStartWithForwardSlash");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Path must begin with a forward slash '/'.
|
||||
/// </summary>
|
||||
internal static string FormatRazorProject_PathMustStartWithForwardSlash()
|
||||
{
|
||||
return GetString("RazorProject_PathMustStartWithForwardSlash");
|
||||
}
|
||||
=> GetString("RazorProject_PathMustStartWithForwardSlash");
|
||||
|
||||
/// <summary>
|
||||
/// The method '{0}' has already been invoked.
|
||||
/// </summary>
|
||||
internal static string DirectiveDescriptor_BeginOptionalsAlreadyInvoked
|
||||
{
|
||||
get { return GetString("DirectiveDescriptor_BeginOptionalsAlreadyInvoked"); }
|
||||
get => GetString("DirectiveDescriptor_BeginOptionalsAlreadyInvoked");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The method '{0}' has already been invoked.
|
||||
/// </summary>
|
||||
internal static string FormatDirectiveDescriptor_BeginOptionalsAlreadyInvoked(object p0)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("DirectiveDescriptor_BeginOptionalsAlreadyInvoked"), p0);
|
||||
}
|
||||
=> string.Format(CultureInfo.CurrentCulture, GetString("DirectiveDescriptor_BeginOptionalsAlreadyInvoked"), p0);
|
||||
|
||||
/// <summary>
|
||||
/// The document of kind '{0}' does not have a '{1}'. The document classifier must set a value for '{2}'.
|
||||
/// </summary>
|
||||
internal static string DocumentMissingTarget
|
||||
{
|
||||
get { return GetString("DocumentMissingTarget"); }
|
||||
get => GetString("DocumentMissingTarget");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The document of kind '{0}' does not have a '{1}'. The document classifier must set a value for '{2}'.
|
||||
/// </summary>
|
||||
internal static string FormatDocumentMissingTarget(object p0, object p1, object p2)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("DocumentMissingTarget"), p0, p1, p2);
|
||||
}
|
||||
=> string.Format(CultureInfo.CurrentCulture, GetString("DocumentMissingTarget"), p0, p1, p2);
|
||||
|
||||
/// <summary>
|
||||
/// The item '{0}' could not be found.
|
||||
/// </summary>
|
||||
internal static string RazorTemplateEngine_ItemCouldNotBeFound
|
||||
{
|
||||
get { return GetString("RazorTemplateEngine_ItemCouldNotBeFound"); }
|
||||
get => GetString("RazorTemplateEngine_ItemCouldNotBeFound");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The item '{0}' could not be found.
|
||||
/// </summary>
|
||||
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)
|
||||
{
|
||||
var value = _resourceManager.GetString(name);
|
||||
|
|
|
|||
|
|
@ -9,6 +9,28 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
{
|
||||
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)
|
||||
{
|
||||
if (document == null)
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ using System.Diagnostics;
|
|||
|
||||
namespace Microsoft.AspNetCore.Razor.Evolution
|
||||
{
|
||||
[DebuggerDisplay("{Error} {Id}: {GetMessageFormat()}")]
|
||||
[DebuggerDisplay("Error {Id}: {GetMessageFormat()}")]
|
||||
public sealed class RazorDiagnosticDescriptor : IEquatable<RazorDiagnosticDescriptor>
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (!node.Descriptor.IsStringProperty ||
|
||||
if (!(node.Descriptor.IsStringProperty || (node.IsIndexerNameMatch && node.Descriptor.IsIndexerStringProperty)) ||
|
||||
node.Children.Count != 1 ||
|
||||
!(node.Children.First() is HtmlContentIRNode))
|
||||
{
|
||||
|
|
@ -139,7 +139,9 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
TagHelperTypeName = node.TagHelperTypeName,
|
||||
PropertyName = node.PropertyName,
|
||||
Descriptor = node.Descriptor,
|
||||
Parent = node.Parent
|
||||
Binding = node.Binding,
|
||||
Parent = node.Parent,
|
||||
IsIndexerNameMatch = node.IsIndexerNameMatch,
|
||||
};
|
||||
|
||||
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">
|
||||
<value>The item '{0}' could not be found.</value>
|
||||
</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>
|
||||
|
|
@ -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(syntaxTree.Root);
|
||||
|
||||
var errorList = new List<RazorDiagnostic>();
|
||||
|
|
@ -52,6 +52,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
var directives = visitor.Directives;
|
||||
descriptors = ProcessDirectives(directives, descriptors, errorSink);
|
||||
|
||||
var tagHelperPrefix = ProcessTagHelperPrefix(directives, codeDocument, errorSink);
|
||||
var root = syntaxTree.Root;
|
||||
|
||||
if (descriptors.Count == 0)
|
||||
|
|
@ -64,20 +65,51 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
}
|
||||
else
|
||||
{
|
||||
var descriptorProvider = new TagHelperDescriptorProvider(descriptors);
|
||||
var rewriter = new TagHelperParseTreeRewriter(descriptorProvider);
|
||||
var descriptorProvider = new TagHelperDescriptorProvider(tagHelperPrefix, descriptors);
|
||||
var rewriter = new TagHelperParseTreeRewriter(tagHelperPrefix, descriptorProvider);
|
||||
root = rewriter.Rewrite(root, errorSink);
|
||||
}
|
||||
|
||||
// Temporary code while we're still using legacy diagnostics in the SyntaxTree.
|
||||
errorList.AddRange(errorSink.Errors.Select(error => RazorDiagnostic.Create(error)));
|
||||
|
||||
errorList.AddRange(descriptors.SelectMany(d => d.GetAllDiagnostics()));
|
||||
|
||||
var diagnostics = CombineErrors(syntaxTree.Diagnostics, errorList);
|
||||
|
||||
var newSyntaxTree = RazorSyntaxTree.Create(root, syntaxTree.Source, diagnostics, syntaxTree.Options);
|
||||
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(
|
||||
IReadOnlyList<TagHelperDirectiveDescriptor> directives,
|
||||
IReadOnlyList<TagHelperDescriptor> tagHelpers,
|
||||
|
|
@ -85,9 +117,6 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
{
|
||||
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++)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
var prefix = prefixDirective?.DirectiveText;
|
||||
if (prefix != null && !IsValidTagHelperPrefix(prefix, prefixDirective.Location, errorSink))
|
||||
{
|
||||
prefix = null;
|
||||
}
|
||||
|
||||
return PrefixDescriptors(prefix, matches);
|
||||
return matches.ToArray();
|
||||
}
|
||||
|
||||
private bool AssemblyContainsTagHelpers(string assemblyName, IReadOnlyList<TagHelperDescriptor> tagHelpers)
|
||||
|
|
@ -254,21 +270,6 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
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)
|
||||
{
|
||||
if (!string.Equals(descriptor.AssemblyName, lookupInfo.AssemblyName, StringComparison.Ordinal))
|
||||
|
|
@ -276,6 +277,14 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
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.Length == 1)
|
||||
|
|
@ -286,10 +295,10 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -1,251 +1,64 @@
|
|||
// 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
|
||||
{
|
||||
/// <summary>
|
||||
/// A metadata class describing a tag helper.
|
||||
/// </summary>
|
||||
public class TagHelperDescriptor
|
||||
public abstract class TagHelperDescriptor
|
||||
{
|
||||
private string _prefix = string.Empty;
|
||||
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>();
|
||||
private IEnumerable<RazorDiagnostic> _allDiagnostics;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="TagHelperDescriptor"/>.
|
||||
/// </summary>
|
||||
public TagHelperDescriptor()
|
||||
protected TagHelperDescriptor(string kind)
|
||||
{
|
||||
Kind = kind;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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;
|
||||
public string Kind { get; }
|
||||
|
||||
foreach (var property in descriptor.PropertyBag)
|
||||
{
|
||||
PropertyBag.Add(property.Key, property.Value);
|
||||
}
|
||||
}
|
||||
public string Name { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Text used as a required prefix when matching HTML start and end tags in the Razor source to available
|
||||
/// tag helpers.
|
||||
/// </summary>
|
||||
public string Prefix
|
||||
public IEnumerable<TagMatchingRule> TagMatchingRules { get; protected set; }
|
||||
|
||||
public string AssemblyName { get; protected set; }
|
||||
|
||||
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
|
||||
{
|
||||
return _prefix;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(value));
|
||||
}
|
||||
var allDiagnostics = GetAllDiagnostics();
|
||||
var anyErrors = allDiagnostics.Any(diagnostic => diagnostic.Severity == RazorDiagnosticSeverity.Error);
|
||||
|
||||
_prefix = value;
|
||||
return anyErrors;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The tag name that the tag helper should target.
|
||||
/// </summary>
|
||||
public string TagName
|
||||
public virtual IEnumerable<RazorDiagnostic> GetAllDiagnostics()
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <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;
|
||||
}
|
||||
return _allDiagnostics;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
public virtual bool Equals(TagHelperDescriptor descriptorX, TagHelperDescriptor descriptorY)
|
||||
{
|
||||
if (descriptorX == descriptorY)
|
||||
if (object.ReferenceEquals(descriptorX, descriptorY))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (descriptorX == null ^ descriptorY == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return descriptorX != null &&
|
||||
string.Equals(descriptorX.TypeName, descriptorY.TypeName, StringComparison.Ordinal) &&
|
||||
string.Equals(descriptorX.TagName, descriptorY.TagName, StringComparison.OrdinalIgnoreCase) &&
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ namespace Microsoft.AspNetCore.Razor.Parser.TagHelpers
|
|||
public TagMode TagMode { get; }
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="TagHelperDescriptor"/>s for the HTML element.
|
||||
/// <see cref="TagHelperDescriptor"/> bindings for the HTML element.
|
||||
/// </summary>
|
||||
public IEnumerable<TagHelperDescriptor> Descriptors { get; }
|
||||
|
||||
|
|
|
|||
|
|
@ -21,9 +21,10 @@ namespace Microsoft.CodeAnalysis.Razor
|
|||
public override TagHelperResolutionResult GetTagHelpers(Compilation compilation, IEnumerable<string> assemblyNameFilters)
|
||||
{
|
||||
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);
|
||||
|
||||
var diagnostics = new List<RazorDiagnostic>();
|
||||
|
|
@ -37,7 +38,7 @@ namespace Microsoft.CodeAnalysis.Razor
|
|||
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 visitor = TagHelperTypeVisitor.Create(compilation, types);
|
||||
|
|
@ -50,8 +51,12 @@ namespace Microsoft.CodeAnalysis.Razor
|
|||
{
|
||||
if (assemblyNameFilters.Contains(type.ContainingAssembly.Identity.Name))
|
||||
{
|
||||
var descriptors = factory.CreateDescriptors(type, errors);
|
||||
results.AddRange(descriptors);
|
||||
var descriptor = factory.CreateDescriptor(type);
|
||||
|
||||
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
|
||||
= 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>
|
||||
/// Could not find matching ']' for required attribute '{0}'.
|
||||
/// </summary>
|
||||
internal static string TagHelperDescriptorFactory_CouldNotFindMatchingEndBrace
|
||||
{
|
||||
get { return GetString("TagHelperDescriptorFactory_CouldNotFindMatchingEndBrace"); }
|
||||
get => GetString("TagHelperDescriptorFactory_CouldNotFindMatchingEndBrace");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Could not find matching ']' for required attribute '{0}'.
|
||||
/// </summary>
|
||||
internal static string FormatTagHelperDescriptorFactory_CouldNotFindMatchingEndBrace(object 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);
|
||||
}
|
||||
=> string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_CouldNotFindMatchingEndBrace"), p0);
|
||||
|
||||
/// <summary>
|
||||
/// Invalid tag helper bound property '{0}.{1}'. '{2}.{3}' must be null unless property type implements '{4}'.
|
||||
/// </summary>
|
||||
internal static string TagHelperDescriptorFactory_InvalidAttributePrefixNotNull
|
||||
{
|
||||
get { return GetString("TagHelperDescriptorFactory_InvalidAttributePrefixNotNull"); }
|
||||
get => GetString("TagHelperDescriptorFactory_InvalidAttributePrefixNotNull");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invalid tag helper bound property '{0}.{1}'. '{2}.{3}' must be null unless property type implements '{4}'.
|
||||
/// </summary>
|
||||
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>
|
||||
/// 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>
|
||||
internal static string TagHelperDescriptorFactory_InvalidAttributePrefixNull
|
||||
{
|
||||
get { return GetString("TagHelperDescriptorFactory_InvalidAttributePrefixNull"); }
|
||||
get => GetString("TagHelperDescriptorFactory_InvalidAttributePrefixNull");
|
||||
}
|
||||
|
||||
/// <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}'.
|
||||
/// </summary>
|
||||
internal static string FormatTagHelperDescriptorFactory_InvalidAttributePrefixNull(object p0, object p1, object p2, object p3, object p4)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidAttributePrefixNull"), p0, p1, p2, p3, p4);
|
||||
}
|
||||
=> string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidAttributePrefixNull"), p0, p1, p2, p3, p4);
|
||||
|
||||
/// <summary>
|
||||
/// Invalid required attribute character '{0}' in required attribute '{1}'. Separate required attributes with commas.
|
||||
/// </summary>
|
||||
internal static string TagHelperDescriptorFactory_InvalidRequiredAttributeCharacter
|
||||
{
|
||||
get { return GetString("TagHelperDescriptorFactory_InvalidRequiredAttributeCharacter"); }
|
||||
get => GetString("TagHelperDescriptorFactory_InvalidRequiredAttributeCharacter");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invalid required attribute character '{0}' in required attribute '{1}'. Separate required attributes with commas.
|
||||
/// </summary>
|
||||
internal static string FormatTagHelperDescriptorFactory_InvalidRequiredAttributeCharacter(object p0, object p1)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidRequiredAttributeCharacter"), p0, p1);
|
||||
}
|
||||
=> string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidRequiredAttributeCharacter"), p0, p1);
|
||||
|
||||
/// <summary>
|
||||
/// Required attribute '{0}' has mismatched quotes '{1}' around value.
|
||||
/// </summary>
|
||||
internal static string TagHelperDescriptorFactory_InvalidRequiredAttributeMismatchedQuotes
|
||||
{
|
||||
get { return GetString("TagHelperDescriptorFactory_InvalidRequiredAttributeMismatchedQuotes"); }
|
||||
get => GetString("TagHelperDescriptorFactory_InvalidRequiredAttributeMismatchedQuotes");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Required attribute '{0}' has mismatched quotes '{1}' around value.
|
||||
/// </summary>
|
||||
internal static string FormatTagHelperDescriptorFactory_InvalidRequiredAttributeMismatchedQuotes(object p0, object p1)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidRequiredAttributeMismatchedQuotes"), p0, p1);
|
||||
}
|
||||
=> string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidRequiredAttributeMismatchedQuotes"), p0, p1);
|
||||
|
||||
/// <summary>
|
||||
/// Invalid character '{0}' in required attribute '{1}'. Expected supported CSS operator or ']'.
|
||||
/// </summary>
|
||||
internal static string TagHelperDescriptorFactory_InvalidRequiredAttributeOperator
|
||||
{
|
||||
get { return GetString("TagHelperDescriptorFactory_InvalidRequiredAttributeOperator"); }
|
||||
get => GetString("TagHelperDescriptorFactory_InvalidRequiredAttributeOperator");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invalid character '{0}' in required attribute '{1}'. Expected supported CSS operator or ']'.
|
||||
/// </summary>
|
||||
internal static string FormatTagHelperDescriptorFactory_InvalidRequiredAttributeOperator(object p0, object 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");
|
||||
}
|
||||
=> string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidRequiredAttributeOperator"), p0, p1);
|
||||
|
||||
/// <summary>
|
||||
/// Required attribute '{0}' has a partial CSS operator. '{1}' must be followed by an equals.
|
||||
/// </summary>
|
||||
internal static string TagHelperDescriptorFactory_PartialRequiredAttributeOperator
|
||||
{
|
||||
get { return GetString("TagHelperDescriptorFactory_PartialRequiredAttributeOperator"); }
|
||||
get => GetString("TagHelperDescriptorFactory_PartialRequiredAttributeOperator");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Required attribute '{0}' has a partial CSS operator. '{1}' must be followed by an equals.
|
||||
/// </summary>
|
||||
internal static string FormatTagHelperDescriptorFactory_PartialRequiredAttributeOperator(object p0, object 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");
|
||||
}
|
||||
=> string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_PartialRequiredAttributeOperator"), p0, p1);
|
||||
|
||||
private static string GetString(string name, params string[] formatterNames)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -15,80 +15,70 @@ namespace Microsoft.CodeAnalysis.Razor
|
|||
/// </summary>
|
||||
internal static string ViewComponent_AmbiguousMethods
|
||||
{
|
||||
get { return GetString("ViewComponent_AmbiguousMethods"); }
|
||||
get => GetString("ViewComponent_AmbiguousMethods");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// View component '{0}' must have exactly one public method named '{1}' or '{2}'.
|
||||
/// </summary>
|
||||
internal static string FormatViewComponent_AmbiguousMethods(object p0, object p1, object p2)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("ViewComponent_AmbiguousMethods"), p0, p1, p2);
|
||||
}
|
||||
=> string.Format(CultureInfo.CurrentCulture, GetString("ViewComponent_AmbiguousMethods"), p0, p1, p2);
|
||||
|
||||
/// <summary>
|
||||
/// Method '{0}' of view component '{1}' should be declared to return {2}&lt;T&gt;.
|
||||
/// </summary>
|
||||
internal static string ViewComponent_AsyncMethod_ShouldReturnTask
|
||||
{
|
||||
get { return GetString("ViewComponent_AsyncMethod_ShouldReturnTask"); }
|
||||
get => GetString("ViewComponent_AsyncMethod_ShouldReturnTask");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Method '{0}' of view component '{1}' should be declared to return {2}&lt;T&gt;.
|
||||
/// </summary>
|
||||
internal static string FormatViewComponent_AsyncMethod_ShouldReturnTask(object p0, object p1, object p2)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("ViewComponent_AsyncMethod_ShouldReturnTask"), p0, p1, p2);
|
||||
}
|
||||
=> string.Format(CultureInfo.CurrentCulture, GetString("ViewComponent_AsyncMethod_ShouldReturnTask"), p0, p1, p2);
|
||||
|
||||
/// <summary>
|
||||
/// Could not find an '{0}' or '{1}' method for the view component '{2}'.
|
||||
/// </summary>
|
||||
internal static string ViewComponent_CannotFindMethod
|
||||
{
|
||||
get { return GetString("ViewComponent_CannotFindMethod"); }
|
||||
get => GetString("ViewComponent_CannotFindMethod");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Could not find an '{0}' or '{1}' method for the view component '{2}'.
|
||||
/// </summary>
|
||||
internal static string FormatViewComponent_CannotFindMethod(object p0, object p1, object p2)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("ViewComponent_CannotFindMethod"), p0, p1, p2);
|
||||
}
|
||||
=> string.Format(CultureInfo.CurrentCulture, GetString("ViewComponent_CannotFindMethod"), p0, p1, p2);
|
||||
|
||||
/// <summary>
|
||||
/// Method '{0}' of view component '{1}' cannot return a {2}.
|
||||
/// </summary>
|
||||
internal static string ViewComponent_SyncMethod_CannotReturnTask
|
||||
{
|
||||
get { return GetString("ViewComponent_SyncMethod_CannotReturnTask"); }
|
||||
get => GetString("ViewComponent_SyncMethod_CannotReturnTask");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Method '{0}' of view component '{1}' cannot return a {2}.
|
||||
/// </summary>
|
||||
internal static string FormatViewComponent_SyncMethod_CannotReturnTask(object p0, object p1, object p2)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("ViewComponent_SyncMethod_CannotReturnTask"), p0, p1, p2);
|
||||
}
|
||||
=> string.Format(CultureInfo.CurrentCulture, GetString("ViewComponent_SyncMethod_CannotReturnTask"), p0, p1, p2);
|
||||
|
||||
/// <summary>
|
||||
/// Method '{0}' of view component '{1}' should be declared to return a value.
|
||||
/// </summary>
|
||||
internal static string ViewComponent_SyncMethod_ShouldReturnValue
|
||||
{
|
||||
get { return GetString("ViewComponent_SyncMethod_ShouldReturnValue"); }
|
||||
get => GetString("ViewComponent_SyncMethod_ShouldReturnValue");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Method '{0}' of view component '{1}' should be declared to return a value.
|
||||
/// </summary>
|
||||
internal static string FormatViewComponent_SyncMethod_ShouldReturnValue(object p0, object p1)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("ViewComponent_SyncMethod_ShouldReturnValue"), p0, p1);
|
||||
}
|
||||
=> string.Format(CultureInfo.CurrentCulture, GetString("ViewComponent_SyncMethod_ShouldReturnValue"), p0, p1);
|
||||
|
||||
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">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</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">
|
||||
<value>Could not find matching ']' for required attribute '{0}'.</value>
|
||||
</data>
|
||||
<data name="TagHelperDescriptorFactory_InvalidAttributeNameAttribute" 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>
|
||||
<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_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>
|
||||
</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">
|
||||
<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>
|
||||
|
|
@ -165,25 +138,7 @@
|
|||
<data name="TagHelperDescriptorFactory_InvalidRequiredAttributeOperator" xml:space="preserve">
|
||||
<value>Invalid character '{0}' in required attribute '{1}'. Expected supported CSS operator or ']'.</value>
|
||||
</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">
|
||||
<value>Required attribute '{0}' has a partial CSS operator. '{1}' must be followed by an equals.</value>
|
||||
</data>
|
||||
<data name="TagHelperDescriptorFactory_Prefix" xml:space="preserve">
|
||||
<value>prefix</value>
|
||||
</data>
|
||||
<data name="TagHelperDescriptorFactory_Tag" xml:space="preserve">
|
||||
<value>Tag</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
@ -32,8 +32,11 @@ namespace Microsoft.CodeAnalysis.Razor
|
|||
|
||||
foreach (var type in types)
|
||||
{
|
||||
var descriptors = factory.CreateDescriptors(type, errors);
|
||||
results.AddRange(descriptors);
|
||||
var descriptor = factory.CreateDescriptor(type);
|
||||
if (descriptor != null)
|
||||
{
|
||||
results.Add(descriptor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -52,7 +55,10 @@ namespace Microsoft.CodeAnalysis.Razor
|
|||
{
|
||||
var descriptor = factory.CreateDescriptor(type);
|
||||
|
||||
results.Add(descriptor);
|
||||
if (descriptor != null)
|
||||
{
|
||||
results.Add(descriptor);
|
||||
}
|
||||
}
|
||||
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.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
|
@ -36,66 +35,69 @@ namespace Microsoft.CodeAnalysis.Razor
|
|||
var shortName = GetShortName(type);
|
||||
var tagName = $"vc:{DefaultTagHelperDescriptorFactory.ToHtmlCase(shortName)}";
|
||||
var typeName = $"__Generated__{shortName}ViewComponentTagHelper";
|
||||
|
||||
var descriptor = new TagHelperDescriptor
|
||||
var descriptorBuilder = ITagHelperDescriptorBuilder.Create(typeName, assemblyName);
|
||||
var methodParameters = GetInvokeMethodParameters(type);
|
||||
descriptorBuilder.TagMatchingRule(ruleBuilder =>
|
||||
{
|
||||
TagName = tagName,
|
||||
TypeName = typeName,
|
||||
AssemblyName = assemblyName
|
||||
};
|
||||
ruleBuilder.RequireTagName(tagName);
|
||||
AddRequiredAttributes(methodParameters, ruleBuilder);
|
||||
});
|
||||
|
||||
SetAttributeDescriptors(type, descriptor);
|
||||
AddBoundAttributes(methodParameters, descriptorBuilder);
|
||||
|
||||
descriptor.PropertyBag.Add(ViewComponentTypes.ViewComponentNameKey, shortName);
|
||||
descriptorBuilder.AddMetadata(ViewComponentTypes.ViewComponentNameKey, shortName);
|
||||
|
||||
var descriptor = descriptorBuilder.Build();
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
private void SetAttributeDescriptors(INamedTypeSymbol type, TagHelperDescriptor descriptor)
|
||||
private void AddRequiredAttributes(ImmutableArray<IParameterSymbol> methodParameters, TagMatchingRuleBuilder builder)
|
||||
{
|
||||
var methodParameters = GetInvokeMethodParameters(type);
|
||||
var attributeDescriptors = new List<TagHelperAttributeDescriptor>();
|
||||
var indexerDescriptors = new List<TagHelperAttributeDescriptor>();
|
||||
var requiredAttributeDescriptors = new List<TagHelperRequiredAttributeDescriptor>();
|
||||
foreach (var parameter in methodParameters)
|
||||
{
|
||||
if (GetIndexerValueTypeName(parameter) == null)
|
||||
{
|
||||
// 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)
|
||||
{
|
||||
var lowerKebabName = DefaultTagHelperDescriptorFactory.ToHtmlCase(parameter.Name);
|
||||
var typeName = parameter.Type.ToDisplayString(FullNameTypeDisplayFormat);
|
||||
var attributeDescriptor = new TagHelperAttributeDescriptor
|
||||
builder.BindAttribute(attributeBuilder =>
|
||||
{
|
||||
Name = lowerKebabName,
|
||||
PropertyName = parameter.Name,
|
||||
TypeName = typeName
|
||||
};
|
||||
attributeBuilder
|
||||
.Name(lowerKebabName)
|
||||
.PropertyName(parameter.Name)
|
||||
.TypeName(typeName);
|
||||
|
||||
attributeDescriptor.IsEnum = 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
|
||||
if (parameter.Type.TypeKind == TypeKind.Enum)
|
||||
{
|
||||
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;
|
||||
if ((parameter.Type as INamedTypeSymbol)?.ConstructedFrom == _iDictionarySymbol)
|
||||
|
|
@ -117,16 +119,9 @@ namespace Microsoft.CodeAnalysis.Razor
|
|||
}
|
||||
|
||||
var type = dictionaryType.TypeArguments[1];
|
||||
var descriptor = new TagHelperAttributeDescriptor
|
||||
{
|
||||
Name = name + "-",
|
||||
PropertyName = parameter.Name,
|
||||
TypeName = type.ToDisplayString(FullNameTypeDisplayFormat),
|
||||
IsEnum = type.TypeKind == TypeKind.Enum,
|
||||
IsIndexer = true
|
||||
};
|
||||
var typeName = type.ToDisplayString(FullNameTypeDisplayFormat);
|
||||
|
||||
return descriptor;
|
||||
return typeName;
|
||||
}
|
||||
|
||||
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
|
||||
// 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);
|
||||
AddXmlDocumentation(compilation, result.Descriptors);
|
||||
return result;
|
||||
var documentedTagHelpers = GetDocumentedTagHelpers(compilation, result.Descriptors);
|
||||
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++)
|
||||
{
|
||||
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)
|
||||
{
|
||||
var tagHelperBuilder = ShallowCopy(typeName, tagHelper);
|
||||
var xml = symbol.GetDocumentationCommentXml();
|
||||
|
||||
if (!string.IsNullOrEmpty(xml))
|
||||
{
|
||||
var documentation = new XmlMemberDocumentation(xml);
|
||||
tagHelper.DesignTimeDescriptor.Summary = documentation.GetSummary();
|
||||
tagHelper.DesignTimeDescriptor.Remarks = documentation.GetRemarks();
|
||||
tagHelperBuilder.Documentation(xml);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
xml = attributeSymbol.GetDocumentationCommentXml();
|
||||
if (!string.IsNullOrEmpty(xml))
|
||||
{
|
||||
var documentation = new XmlMemberDocumentation(xml);
|
||||
attribute.DesignTimeDescriptor.Summary = documentation.GetSummary();
|
||||
attribute.DesignTimeDescriptor.Remarks = documentation.GetRemarks();
|
||||
var attributeBuilder = ShallowCopy(typeName, resolvedAttribute);
|
||||
attributeBuilder.Documentation(xml);
|
||||
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()
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@
|
|||
<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.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.AspNetCore.Razor.Evolution\Microsoft.AspNetCore.Razor.Evolution.csproj" />
|
||||
</ItemGroup>
|
||||
|
|
|
|||
|
|
@ -10,21 +10,33 @@ namespace Microsoft.VisualStudio.LanguageServices.Razor
|
|||
private static readonly ResourceManager _resourceManager
|
||||
= 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>
|
||||
/// An unexpected exception occurred when invoking '{0}.{1}' on the Razor language service.
|
||||
/// </summary>
|
||||
internal static string UnexpectedException
|
||||
{
|
||||
get { return GetString("UnexpectedException"); }
|
||||
get => GetString("UnexpectedException");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An unexpected exception occurred when invoking '{0}.{1}' on the Razor language service.
|
||||
/// </summary>
|
||||
internal static string FormatUnexpectedException(object p0, object p1)
|
||||
{
|
||||
return string.Format(CultureInfo.CurrentCulture, GetString("UnexpectedException"), p0, p1);
|
||||
}
|
||||
=> string.Format(CultureInfo.CurrentCulture, GetString("UnexpectedException"), p0, p1);
|
||||
|
||||
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">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</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">
|
||||
<value>An unexpected exception occurred when invoking '{0}.{1}' on the Razor language service.</value>
|
||||
</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];
|
||||
phase.Execute(codeDocument);
|
||||
|
||||
|
||||
if (phase is IRazorDocumentClassifierPhase)
|
||||
{
|
||||
break;
|
||||
|
|
@ -168,4 +168,4 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
return irDocument;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -174,12 +174,10 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
<span val=""@Hello World""></span>");
|
||||
var tagHelpers = new[]
|
||||
{
|
||||
new TagHelperDescriptor
|
||||
{
|
||||
TagName = "span",
|
||||
TypeName = "SpanTagHelper",
|
||||
AssemblyName = "TestAssembly",
|
||||
}
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "span",
|
||||
typeName: "SpanTagHelper",
|
||||
assemblyName: "TestAssembly")
|
||||
};
|
||||
|
||||
// Act
|
||||
|
|
@ -221,12 +219,10 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
<cool:span val=""@Hello World""></cool:span>");
|
||||
var tagHelpers = new[]
|
||||
{
|
||||
new TagHelperDescriptor
|
||||
{
|
||||
TagName = "span",
|
||||
TypeName = "SpanTagHelper",
|
||||
AssemblyName = "TestAssembly",
|
||||
}
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "span",
|
||||
typeName: "SpanTagHelper",
|
||||
assemblyName: "TestAssembly")
|
||||
};
|
||||
|
||||
// Act
|
||||
|
|
@ -273,12 +269,10 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
}");
|
||||
var tagHelpers = new[]
|
||||
{
|
||||
new TagHelperDescriptor
|
||||
{
|
||||
TagName = "span",
|
||||
TypeName = "SpanTagHelper",
|
||||
AssemblyName = "TestAssembly",
|
||||
}
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "span",
|
||||
typeName: "SpanTagHelper",
|
||||
assemblyName: "TestAssembly")
|
||||
};
|
||||
|
||||
// Act
|
||||
|
|
@ -299,7 +293,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
n,
|
||||
c1 => DirectiveToken(DirectiveTokenKind.Member, "test", c1),
|
||||
c1 => Html(Environment.NewLine, c1),
|
||||
c1 =>
|
||||
c1 =>
|
||||
{
|
||||
var tagHelperNode = Assert.IsType<TagHelperIRNode>(c1);
|
||||
Children(
|
||||
|
|
@ -324,24 +318,23 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
// Arrange
|
||||
var codeDocument = TestRazorCodeDocument.Create(@"@addTagHelper *, TestAssembly
|
||||
<input bound='foo' />");
|
||||
var descriptor = new TagHelperDescriptor
|
||||
var tagHelpers = new[]
|
||||
{
|
||||
TagName = "input",
|
||||
TypeName = "InputTagHelper",
|
||||
AssemblyName = "TestAssembly",
|
||||
Attributes = new[]
|
||||
{
|
||||
new TagHelperAttributeDescriptor
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "input",
|
||||
typeName: "InputTagHelper",
|
||||
assemblyName: "TestAssembly",
|
||||
attributes: new Action<ITagHelperBoundAttributeDescriptorBuilder>[]
|
||||
{
|
||||
Name = "bound",
|
||||
PropertyName = "FooProp",
|
||||
TypeName = "System.String"
|
||||
}
|
||||
}
|
||||
builder => builder
|
||||
.Name("bound")
|
||||
.PropertyName("FooProp")
|
||||
.TypeName("System.String"),
|
||||
})
|
||||
};
|
||||
|
||||
// Act
|
||||
var irDocument = Lower(codeDocument, tagHelpers: new[] { descriptor });
|
||||
var irDocument = Lower(codeDocument, tagHelpers: tagHelpers);
|
||||
|
||||
// Assert
|
||||
Children(
|
||||
|
|
@ -484,5 +477,28 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
|
||||
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()
|
||||
{
|
||||
// Arrange, Act & Assert
|
||||
RunRuntimeTagHelpersTest(TestTagHelperDescriptors.PrefixedPAndInputTagHelperDescriptors);
|
||||
RunRuntimeTagHelpersTest(TestTagHelperDescriptors.DefaultPAndInputTagHelperDescriptors, tagHelperPrefix: "THS");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -690,13 +690,6 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
|
|||
RunRuntimeTagHelpersTest(TestTagHelperDescriptors.DefaultPAndInputTagHelperDescriptors);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DuplicateTargetTagHelper_Runtime()
|
||||
{
|
||||
// Arrange, Act & Assert
|
||||
RunRuntimeTagHelpersTest(TestTagHelperDescriptors.DuplicateTargetTagHelperDescriptors);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EmptyAttributeTagHelpers_Runtime()
|
||||
{
|
||||
|
|
@ -1480,7 +1473,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
|
|||
public void BasicTagHelpers_Prefixed_DesignTime()
|
||||
{
|
||||
// Arrange, Act & Assert
|
||||
RunDesignTimeTagHelpersTest(TestTagHelperDescriptors.PrefixedPAndInputTagHelperDescriptors);
|
||||
RunDesignTimeTagHelpersTest(TestTagHelperDescriptors.DefaultPAndInputTagHelperDescriptors, tagHelperPrefix: "THS");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -1490,13 +1483,6 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
|
|||
RunDesignTimeTagHelpersTest(TestTagHelperDescriptors.DefaultPAndInputTagHelperDescriptors);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DuplicateTargetTagHelper_DesignTime()
|
||||
{
|
||||
// Arrange, Act & Assert
|
||||
RunDesignTimeTagHelpersTest(TestTagHelperDescriptors.DuplicateTargetTagHelperDescriptors);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EmptyAttributeTagHelpers_DesignTime()
|
||||
{
|
||||
|
|
@ -1616,7 +1602,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
|
|||
return codeDocument;
|
||||
}
|
||||
|
||||
private void RunRuntimeTagHelpersTest(IEnumerable<TagHelperDescriptor> descriptors)
|
||||
private void RunRuntimeTagHelpersTest(IEnumerable<TagHelperDescriptor> descriptors, string tagHelperPrefix = null)
|
||||
{
|
||||
// Arrange
|
||||
var engine = RazorEngine.Create(
|
||||
|
|
@ -1626,6 +1612,10 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
|
|||
builder.AddTagHelpers(descriptors);
|
||||
});
|
||||
var document = CreateCodeDocument();
|
||||
if (tagHelperPrefix != null)
|
||||
{
|
||||
document.SetTagHelperPrefix(tagHelperPrefix);
|
||||
}
|
||||
|
||||
// Act
|
||||
engine.Process(document);
|
||||
|
|
@ -1635,7 +1625,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
|
|||
AssertCSharpDocumentMatchesBaseline(document.GetCSharpDocument());
|
||||
}
|
||||
|
||||
private void RunDesignTimeTagHelpersTest(IEnumerable<TagHelperDescriptor> descriptors)
|
||||
private void RunDesignTimeTagHelpersTest(IEnumerable<TagHelperDescriptor> descriptors, string tagHelperPrefix = null)
|
||||
{
|
||||
// Arrange
|
||||
var engine = RazorEngine.CreateDesignTime(
|
||||
|
|
@ -1645,6 +1635,10 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
|
|||
builder.AddTagHelpers(descriptors);
|
||||
});
|
||||
var document = CreateCodeDocument();
|
||||
if (tagHelperPrefix != null)
|
||||
{
|
||||
document.SetTagHelperPrefix(tagHelperPrefix);
|
||||
}
|
||||
|
||||
// Act
|
||||
engine.Process(document);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
|
||||
|
|
@ -13,39 +15,29 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
|
|||
// Arrange
|
||||
var descriptors = new[]
|
||||
{
|
||||
new TagHelperDescriptor
|
||||
{
|
||||
TagName = "p",
|
||||
TypeName = "PTagHelper",
|
||||
AssemblyName = "TestAssembly",
|
||||
},
|
||||
new TagHelperDescriptor
|
||||
{
|
||||
TagName = "form",
|
||||
TypeName = "FormTagHelper",
|
||||
AssemblyName = "TestAssembly",
|
||||
},
|
||||
new TagHelperDescriptor
|
||||
{
|
||||
TagName = "input",
|
||||
TypeName = "InputTagHelper",
|
||||
AssemblyName = "TestAssembly",
|
||||
Attributes = new[]
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "p",
|
||||
typeName: "PTagHelper",
|
||||
assemblyName: "TestAssembly"),
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "form",
|
||||
typeName: "FormTagHelper",
|
||||
assemblyName: "TestAssembly"),
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "input",
|
||||
typeName: "InputTagHelper",
|
||||
assemblyName: "TestAssembly",
|
||||
attributes: new Action<ITagHelperBoundAttributeDescriptorBuilder>[]
|
||||
{
|
||||
new TagHelperAttributeDescriptor
|
||||
{
|
||||
Name = "value",
|
||||
PropertyName = "FooProp",
|
||||
TypeName = "System.String" // Gets preallocated
|
||||
},
|
||||
new TagHelperAttributeDescriptor
|
||||
{
|
||||
Name = "date",
|
||||
PropertyName = "BarProp",
|
||||
TypeName = "System.DateTime" // Doesn't get preallocated
|
||||
}
|
||||
}
|
||||
}
|
||||
builder => builder
|
||||
.Name("value")
|
||||
.PropertyName("FooProp")
|
||||
.TypeName("System.String"), // Gets preallocated
|
||||
builder => builder
|
||||
.Name("date")
|
||||
.PropertyName("BarProp")
|
||||
.TypeName("System.DateTime"), // Doesn't get preallocated
|
||||
})
|
||||
};
|
||||
|
||||
var engine = RazorEngine.Create(b =>
|
||||
|
|
@ -53,7 +45,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
|
|||
b.AddTagHelpers(descriptors);
|
||||
b.Features.Add(new DefaultInstrumentationPass());
|
||||
});
|
||||
|
||||
|
||||
var document = CreateCodeDocument();
|
||||
|
||||
// Act
|
||||
|
|
@ -66,5 +58,28 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
|
|||
AssertCSharpDocumentMatchesBaseline(csharpDocument);
|
||||
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.
|
||||
// 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;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
|
||||
|
|
@ -13,12 +15,10 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
|
|||
// Arrange
|
||||
var descriptors = new[]
|
||||
{
|
||||
new TagHelperDescriptor
|
||||
{
|
||||
TagName = "input",
|
||||
TypeName = "InputTagHelper",
|
||||
AssemblyName = "TestAssembly",
|
||||
}
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "input",
|
||||
typeName: "InputTagHelper",
|
||||
assemblyName: "TestAssembly")
|
||||
};
|
||||
|
||||
var engine = RazorEngine.Create(builder => builder.AddTagHelpers(descriptors));
|
||||
|
|
@ -37,18 +37,17 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
|
|||
// Arrange
|
||||
var descriptors = new[]
|
||||
{
|
||||
new TagHelperDescriptor
|
||||
{
|
||||
TagName = "input",
|
||||
TypeName = "InputTagHelper",
|
||||
AssemblyName = "TestAssembly",
|
||||
Attributes = new[] { new TagHelperAttributeDescriptor
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "input",
|
||||
typeName: "InputTagHelper",
|
||||
assemblyName: "TestAssembly",
|
||||
attributes: new Action<ITagHelperBoundAttributeDescriptorBuilder>[]
|
||||
{
|
||||
Name = "bound",
|
||||
PropertyName = "FooProp",
|
||||
TypeName = "System.String"
|
||||
} }
|
||||
}
|
||||
builder => builder
|
||||
.Name("bound")
|
||||
.PropertyName("FooProp")
|
||||
.TypeName("System.String"),
|
||||
})
|
||||
};
|
||||
|
||||
var engine = RazorEngine.Create(builder => builder.AddTagHelpers(descriptors));
|
||||
|
|
@ -67,30 +66,25 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
|
|||
// Arrange
|
||||
var descriptors = new[]
|
||||
{
|
||||
new TagHelperDescriptor
|
||||
{
|
||||
TagName = "p",
|
||||
TypeName = "PTagHelper",
|
||||
AssemblyName = "TestAssembly",
|
||||
},
|
||||
new TagHelperDescriptor
|
||||
{
|
||||
TagName = "form",
|
||||
TypeName = "FormTagHelper",
|
||||
AssemblyName = "TestAssembly",
|
||||
},
|
||||
new TagHelperDescriptor
|
||||
{
|
||||
TagName = "input",
|
||||
TypeName = "InputTagHelper",
|
||||
AssemblyName = "TestAssembly",
|
||||
Attributes = new[] { new TagHelperAttributeDescriptor
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "p",
|
||||
typeName: "PTagHelper",
|
||||
assemblyName: "TestAssembly"),
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "form",
|
||||
typeName: "FormTagHelper",
|
||||
assemblyName: "TestAssembly"),
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "input",
|
||||
typeName: "InputTagHelper",
|
||||
assemblyName: "TestAssembly",
|
||||
attributes: new Action<ITagHelperBoundAttributeDescriptorBuilder>[]
|
||||
{
|
||||
Name = "value",
|
||||
PropertyName = "FooProp",
|
||||
TypeName = "System.String"
|
||||
} }
|
||||
}
|
||||
builder => builder
|
||||
.Name("value")
|
||||
.PropertyName("FooProp")
|
||||
.TypeName("System.String"),
|
||||
})
|
||||
};
|
||||
|
||||
var engine = RazorEngine.Create(builder => builder.AddTagHelpers(descriptors));
|
||||
|
|
@ -102,5 +96,28 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
|
|||
// Assert
|
||||
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
|
||||
{
|
||||
internal static IEnumerable<TagHelperDescriptor> DefaultPAndInputTagHelperDescriptors { get; }
|
||||
= BuildPAndInputTagHelperDescriptors(prefix: string.Empty);
|
||||
internal static IEnumerable<TagHelperDescriptor> PrefixedPAndInputTagHelperDescriptors { get; }
|
||||
= BuildPAndInputTagHelperDescriptors(prefix: "THS");
|
||||
|
||||
internal static IEnumerable<TagHelperDescriptor> SimpleTagHelperDescriptors
|
||||
{
|
||||
get
|
||||
{
|
||||
return new[]
|
||||
{
|
||||
new TagHelperDescriptor
|
||||
{
|
||||
TagName = "span",
|
||||
TypeName = "SpanTagHelper",
|
||||
AssemblyName = "TestAssembly",
|
||||
},
|
||||
new TagHelperDescriptor
|
||||
{
|
||||
TagName = "div",
|
||||
TypeName = "DivTagHelper",
|
||||
AssemblyName = "TestAssembly",
|
||||
},
|
||||
new TagHelperDescriptor
|
||||
{
|
||||
TagName = "input",
|
||||
TypeName = "InputTagHelper",
|
||||
AssemblyName = "TestAssembly",
|
||||
Attributes = new[]
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "span",
|
||||
typeName: "SpanTagHelper",
|
||||
assemblyName: "TestAssembly"),
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "div",
|
||||
typeName: "DivTagHelper",
|
||||
assemblyName: "TestAssembly"),
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "input",
|
||||
typeName: "InputTagHelper",
|
||||
assemblyName: "TestAssembly",
|
||||
attributes: new Action<ITagHelperBoundAttributeDescriptorBuilder>[]
|
||||
{
|
||||
new TagHelperAttributeDescriptor
|
||||
{
|
||||
Name = "value",
|
||||
PropertyName = "FooProp",
|
||||
TypeName = "System.String"
|
||||
},
|
||||
new TagHelperAttributeDescriptor
|
||||
{
|
||||
Name = "bound",
|
||||
PropertyName = "BoundProp",
|
||||
TypeName = "System.String"
|
||||
}
|
||||
}
|
||||
}
|
||||
builder => builder
|
||||
.Name("value")
|
||||
.PropertyName("FooProp")
|
||||
.TypeName("System.String"),
|
||||
builder => builder
|
||||
.Name("bound")
|
||||
.PropertyName("BoundProp")
|
||||
.TypeName("System.String"),
|
||||
})
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -66,113 +51,93 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
|
|||
|
||||
return new[]
|
||||
{
|
||||
new TagHelperDescriptor
|
||||
{
|
||||
TagName = "a",
|
||||
TypeName = "TestNamespace.ATagHelper",
|
||||
AssemblyName = "TestAssembly",
|
||||
RequiredAttributes = new[]
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "a",
|
||||
typeName: "TestNamespace.ATagHelper",
|
||||
assemblyName: "TestAssembly",
|
||||
ruleBuilders: new Action<TagMatchingRuleBuilder>[]
|
||||
{
|
||||
new TagHelperRequiredAttributeDescriptor
|
||||
{
|
||||
Name = "href",
|
||||
NameComparison = TagHelperRequiredAttributeNameComparison.FullMatch,
|
||||
Value = "~/",
|
||||
ValueComparison = TagHelperRequiredAttributeValueComparison.FullMatch,
|
||||
}
|
||||
},
|
||||
},
|
||||
new TagHelperDescriptor
|
||||
{
|
||||
TagName = "a",
|
||||
TypeName = "TestNamespace.ATagHelperMultipleSelectors",
|
||||
AssemblyName = "TestAssembly",
|
||||
RequiredAttributes = new[]
|
||||
builder => builder
|
||||
.RequireAttribute(attribute => attribute
|
||||
.Name("href")
|
||||
.NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.FullMatch)
|
||||
.Value("~/")
|
||||
.ValueComparisonMode(RequiredAttributeDescriptor.ValueComparisonMode.FullMatch)),
|
||||
}),
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "a",
|
||||
typeName: "TestNamespace.ATagHelperMultipleSelectors",
|
||||
assemblyName: "TestAssembly",
|
||||
ruleBuilders: new Action<TagMatchingRuleBuilder>[]
|
||||
{
|
||||
new TagHelperRequiredAttributeDescriptor
|
||||
{
|
||||
Name = "href",
|
||||
NameComparison = TagHelperRequiredAttributeNameComparison.FullMatch,
|
||||
Value = "~/",
|
||||
ValueComparison = TagHelperRequiredAttributeValueComparison.PrefixMatch,
|
||||
},
|
||||
new TagHelperRequiredAttributeDescriptor
|
||||
{
|
||||
Name = "href",
|
||||
NameComparison = TagHelperRequiredAttributeNameComparison.FullMatch,
|
||||
Value = "?hello=world",
|
||||
ValueComparison = TagHelperRequiredAttributeValueComparison.SuffixMatch,
|
||||
}
|
||||
},
|
||||
},
|
||||
new TagHelperDescriptor
|
||||
{
|
||||
TagName = "input",
|
||||
TypeName = "TestNamespace.InputTagHelper",
|
||||
AssemblyName = "TestAssembly",
|
||||
Attributes = new TagHelperAttributeDescriptor[]
|
||||
builder => builder
|
||||
.RequireAttribute(attribute => attribute
|
||||
.Name("href")
|
||||
.NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.FullMatch)
|
||||
.Value("~/")
|
||||
.ValueComparisonMode(RequiredAttributeDescriptor.ValueComparisonMode.PrefixMatch))
|
||||
.RequireAttribute(attribute => attribute
|
||||
.Name("href")
|
||||
.NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.FullMatch)
|
||||
.Value("?hello=world")
|
||||
.ValueComparisonMode(RequiredAttributeDescriptor.ValueComparisonMode.SuffixMatch)),
|
||||
}),
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "input",
|
||||
typeName: "TestNamespace.InputTagHelper",
|
||||
assemblyName: "TestAssembly",
|
||||
attributes: new Action<ITagHelperBoundAttributeDescriptorBuilder>[]
|
||||
{
|
||||
new TagHelperAttributeDescriptor("type", inputTypePropertyInfo),
|
||||
builder => BuildBoundAttributeDescriptorFromPropertyInfo(builder, "type", inputTypePropertyInfo),
|
||||
},
|
||||
RequiredAttributes = new[]
|
||||
ruleBuilders: new Action<TagMatchingRuleBuilder>[]
|
||||
{
|
||||
new TagHelperRequiredAttributeDescriptor
|
||||
{
|
||||
Name = "type",
|
||||
NameComparison = TagHelperRequiredAttributeNameComparison.FullMatch,
|
||||
Value = "text",
|
||||
ValueComparison = TagHelperRequiredAttributeValueComparison.FullMatch,
|
||||
}
|
||||
},
|
||||
},
|
||||
new TagHelperDescriptor
|
||||
{
|
||||
TagName = "input",
|
||||
TypeName = "TestNamespace.InputTagHelper2",
|
||||
AssemblyName = "TestAssembly",
|
||||
Attributes = new TagHelperAttributeDescriptor[]
|
||||
builder => builder
|
||||
.RequireAttribute(attribute => attribute
|
||||
.Name("type")
|
||||
.NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.FullMatch)
|
||||
.Value("text")
|
||||
.ValueComparisonMode(RequiredAttributeDescriptor.ValueComparisonMode.FullMatch)),
|
||||
}),
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "input",
|
||||
typeName: "TestNamespace.InputTagHelper2",
|
||||
assemblyName: "TestAssembly",
|
||||
attributes: new Action<ITagHelperBoundAttributeDescriptorBuilder>[]
|
||||
{
|
||||
new TagHelperAttributeDescriptor("type", inputTypePropertyInfo),
|
||||
builder => BuildBoundAttributeDescriptorFromPropertyInfo(builder, "type", inputTypePropertyInfo),
|
||||
},
|
||||
RequiredAttributes = new[]
|
||||
ruleBuilders: new Action<TagMatchingRuleBuilder>[]
|
||||
{
|
||||
new TagHelperRequiredAttributeDescriptor
|
||||
{
|
||||
Name = "ty",
|
||||
NameComparison = TagHelperRequiredAttributeNameComparison.PrefixMatch,
|
||||
}
|
||||
},
|
||||
},
|
||||
new TagHelperDescriptor
|
||||
{
|
||||
TagName = "*",
|
||||
TypeName = "TestNamespace.CatchAllTagHelper",
|
||||
AssemblyName = "TestAssembly",
|
||||
RequiredAttributes = new[]
|
||||
builder => builder
|
||||
.RequireAttribute(attribute => attribute
|
||||
.Name("ty")
|
||||
.NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.PrefixMatch)),
|
||||
}),
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "*",
|
||||
typeName: "TestNamespace.CatchAllTagHelper",
|
||||
assemblyName: "TestAssembly",
|
||||
ruleBuilders: new Action<TagMatchingRuleBuilder>[]
|
||||
{
|
||||
new TagHelperRequiredAttributeDescriptor
|
||||
{
|
||||
Name = "href",
|
||||
NameComparison = TagHelperRequiredAttributeNameComparison.FullMatch,
|
||||
Value = "~/",
|
||||
ValueComparison = TagHelperRequiredAttributeValueComparison.PrefixMatch,
|
||||
}
|
||||
},
|
||||
},
|
||||
new TagHelperDescriptor
|
||||
{
|
||||
TagName = "*",
|
||||
TypeName = "TestNamespace.CatchAllTagHelper2",
|
||||
AssemblyName = "TestAssembly",
|
||||
RequiredAttributes = new[]
|
||||
builder => builder
|
||||
.RequireAttribute(attribute => attribute
|
||||
.Name("href")
|
||||
.NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.FullMatch)
|
||||
.Value("~/")
|
||||
.ValueComparisonMode(RequiredAttributeDescriptor.ValueComparisonMode.PrefixMatch)),
|
||||
}),
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "*",
|
||||
typeName: "TestNamespace.CatchAllTagHelper2",
|
||||
assemblyName: "TestAssembly",
|
||||
ruleBuilders: new Action<TagMatchingRuleBuilder>[]
|
||||
{
|
||||
new TagHelperRequiredAttributeDescriptor
|
||||
{
|
||||
Name = "type",
|
||||
NameComparison = TagHelperRequiredAttributeNameComparison.FullMatch,
|
||||
}
|
||||
},
|
||||
}
|
||||
builder => builder
|
||||
.RequireAttribute(attribute => attribute
|
||||
.Name("type")
|
||||
.NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.FullMatch)),
|
||||
}),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -183,38 +148,30 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
|
|||
{
|
||||
return new[]
|
||||
{
|
||||
new TagHelperDescriptor
|
||||
{
|
||||
TagName = "*",
|
||||
TypeName = "TestNamespace.CatchAllTagHelper",
|
||||
AssemblyName = "TestAssembly",
|
||||
Attributes = new[]
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "*",
|
||||
typeName: "TestNamespace.CatchAllTagHelper",
|
||||
assemblyName: "TestAssembly",
|
||||
attributes: new Action<ITagHelperBoundAttributeDescriptorBuilder>[]
|
||||
{
|
||||
new TagHelperAttributeDescriptor
|
||||
{
|
||||
Name = "catch-all",
|
||||
PropertyName = "CatchAll",
|
||||
IsEnum = true,
|
||||
TypeName = typeof(MyEnum).FullName
|
||||
},
|
||||
}
|
||||
},
|
||||
new TagHelperDescriptor
|
||||
{
|
||||
TagName = "input",
|
||||
TypeName = "TestNamespace.InputTagHelper",
|
||||
AssemblyName = "TestAssembly",
|
||||
Attributes = new[]
|
||||
builder => builder
|
||||
.Name("catch-all")
|
||||
.PropertyName("CatchAll")
|
||||
.AsEnum()
|
||||
.TypeName(typeof(MyEnum).FullName),
|
||||
}),
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "input",
|
||||
typeName: "TestNamespace.InputTagHelper",
|
||||
assemblyName: "TestAssembly",
|
||||
attributes: new Action<ITagHelperBoundAttributeDescriptorBuilder>[]
|
||||
{
|
||||
new TagHelperAttributeDescriptor
|
||||
{
|
||||
Name = "value",
|
||||
PropertyName = "Value",
|
||||
IsEnum = true,
|
||||
TypeName = typeof(MyEnum).FullName
|
||||
},
|
||||
}
|
||||
},
|
||||
builder => builder
|
||||
.Name("value")
|
||||
.PropertyName("Value")
|
||||
.AsEnum()
|
||||
.TypeName(typeof(MyEnum).FullName),
|
||||
}),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -225,52 +182,41 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
|
|||
{
|
||||
return new[]
|
||||
{
|
||||
new TagHelperDescriptor
|
||||
{
|
||||
TagName = "*",
|
||||
TypeName = "TestNamespace.CatchAllTagHelper",
|
||||
AssemblyName = "TestAssembly",
|
||||
Attributes = new[]
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "*",
|
||||
typeName: "TestNamespace.CatchAllTagHelper",
|
||||
assemblyName: "TestAssembly",
|
||||
attributes: new Action<ITagHelperBoundAttributeDescriptorBuilder>[]
|
||||
{
|
||||
new TagHelperAttributeDescriptor
|
||||
{
|
||||
Name = "[item]",
|
||||
PropertyName = "ListItems",
|
||||
TypeName = typeof(List<string>).FullName
|
||||
},
|
||||
new TagHelperAttributeDescriptor
|
||||
{
|
||||
Name = "[(item)]",
|
||||
PropertyName = "ArrayItems",
|
||||
TypeName = typeof(string[]).FullName
|
||||
},
|
||||
new TagHelperAttributeDescriptor
|
||||
{
|
||||
Name = "(click)",
|
||||
PropertyName = "Event1",
|
||||
TypeName = typeof(Action).FullName
|
||||
},
|
||||
new TagHelperAttributeDescriptor
|
||||
{
|
||||
Name = "(^click)",
|
||||
PropertyName = "Event2",
|
||||
TypeName = typeof(Action).FullName
|
||||
},
|
||||
new TagHelperAttributeDescriptor
|
||||
{
|
||||
Name = "*something",
|
||||
PropertyName = "StringProperty1",
|
||||
TypeName = typeof(string).FullName
|
||||
},
|
||||
new TagHelperAttributeDescriptor
|
||||
{
|
||||
Name = "#local",
|
||||
PropertyName = "StringProperty2",
|
||||
TypeName = typeof(string).FullName
|
||||
},
|
||||
builder => builder
|
||||
.Name("[item]")
|
||||
.PropertyName("ListItems")
|
||||
.TypeName(typeof(List<string>).FullName),
|
||||
builder => builder
|
||||
.Name("[(item)]")
|
||||
.PropertyName("ArrayItems")
|
||||
.TypeName(typeof(string[]).FullName),
|
||||
builder => builder
|
||||
.Name("(click)")
|
||||
.PropertyName("Event1")
|
||||
.TypeName(typeof(Action).FullName),
|
||||
builder => builder
|
||||
.Name("(^click)")
|
||||
.PropertyName("Event2")
|
||||
.TypeName(typeof(Action).FullName),
|
||||
builder => builder
|
||||
.Name("*something")
|
||||
.PropertyName("StringProperty1")
|
||||
.TypeName(typeof(string).FullName),
|
||||
builder => builder
|
||||
.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[]
|
||||
{
|
||||
new TagHelperDescriptor
|
||||
{
|
||||
TagName = "*",
|
||||
TypeName = "TestNamespace.CatchAllTagHelper",
|
||||
AssemblyName = "TestAssembly",
|
||||
Attributes = new[]
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "*",
|
||||
typeName: "TestNamespace.CatchAllTagHelper",
|
||||
assemblyName: "TestAssembly",
|
||||
attributes: new Action<ITagHelperBoundAttributeDescriptorBuilder>[]
|
||||
{
|
||||
new TagHelperAttributeDescriptor
|
||||
{
|
||||
Name = "catchall-bound-string",
|
||||
PropertyName = "BoundRequiredString",
|
||||
TypeName = typeof(string).FullName,
|
||||
IsStringProperty = true
|
||||
}
|
||||
builder => builder
|
||||
.Name("catchall-bound-string")
|
||||
.PropertyName("BoundRequiredString")
|
||||
.TypeName(typeof(string).FullName),
|
||||
},
|
||||
RequiredAttributes = new[]
|
||||
ruleBuilders: new Action<TagMatchingRuleBuilder>[]
|
||||
{
|
||||
new TagHelperRequiredAttributeDescriptor { Name = "catchall-unbound-required" }
|
||||
},
|
||||
},
|
||||
new TagHelperDescriptor
|
||||
{
|
||||
TagName = "input",
|
||||
TypeName = "TestNamespace.InputTagHelper",
|
||||
AssemblyName = "TestAssembly",
|
||||
Attributes = new[]
|
||||
builder => builder.RequireAttribute(attribute => attribute.Name("catchall-unbound-required")),
|
||||
}),
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "input",
|
||||
typeName: "TestNamespace.InputTagHelper",
|
||||
assemblyName: "TestAssembly",
|
||||
attributes: new Action<ITagHelperBoundAttributeDescriptorBuilder>[]
|
||||
{
|
||||
new TagHelperAttributeDescriptor
|
||||
{
|
||||
Name = "input-bound-required-string",
|
||||
PropertyName = "BoundRequiredString",
|
||||
TypeName = typeof(string).FullName,
|
||||
IsStringProperty = true
|
||||
},
|
||||
new TagHelperAttributeDescriptor
|
||||
{
|
||||
Name = "input-bound-string",
|
||||
PropertyName = "BoundString",
|
||||
TypeName = typeof(string).FullName,
|
||||
IsStringProperty = true
|
||||
}
|
||||
builder => builder
|
||||
.Name("input-bound-required-string")
|
||||
.PropertyName("BoundRequiredString")
|
||||
.TypeName(typeof(string).FullName),
|
||||
builder => builder
|
||||
.Name("input-bound-string")
|
||||
.PropertyName("BoundString")
|
||||
.TypeName(typeof(string).FullName),
|
||||
},
|
||||
RequiredAttributes = new[]
|
||||
ruleBuilders: new Action<TagMatchingRuleBuilder>[]
|
||||
{
|
||||
new TagHelperRequiredAttributeDescriptor { Name = "input-bound-required-string" },
|
||||
new TagHelperRequiredAttributeDescriptor { Name = "input-unbound-required" }
|
||||
},
|
||||
}
|
||||
builder => builder
|
||||
.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[]
|
||||
{
|
||||
new TagHelperDescriptor
|
||||
{
|
||||
TagName = "input",
|
||||
TypeName = "TestNamespace.InputTagHelper",
|
||||
AssemblyName = "TestAssembly",
|
||||
Attributes = new[]
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "input",
|
||||
typeName: "TestNamespace.InputTagHelper",
|
||||
assemblyName: "TestAssembly",
|
||||
attributes: new Action<ITagHelperBoundAttributeDescriptorBuilder>[]
|
||||
{
|
||||
new TagHelperAttributeDescriptor
|
||||
{
|
||||
Name = "bound",
|
||||
PropertyName = "Bound",
|
||||
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" } },
|
||||
}
|
||||
builder => builder
|
||||
.Name("bound")
|
||||
.PropertyName("Bound")
|
||||
.TypeName(typeof(string).FullName)
|
||||
}),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -427,47 +296,49 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
|
|||
var inputCheckedPropertyInfo = typeof(TestType).GetProperty("Checked");
|
||||
return new[]
|
||||
{
|
||||
new TagHelperDescriptor
|
||||
{
|
||||
TagName = "p",
|
||||
TypeName = "TestNamespace.PTagHelper",
|
||||
AssemblyName = "TestAssembly",
|
||||
RequiredAttributes = new[] { new TagHelperRequiredAttributeDescriptor { Name = "class" } },
|
||||
},
|
||||
new TagHelperDescriptor
|
||||
{
|
||||
TagName = "input",
|
||||
TypeName = "TestNamespace.InputTagHelper",
|
||||
AssemblyName = "TestAssembly",
|
||||
Attributes = new TagHelperAttributeDescriptor[]
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "p",
|
||||
typeName: "TestNamespace.PTagHelper",
|
||||
assemblyName: "TestAssembly",
|
||||
ruleBuilders: new Action<TagMatchingRuleBuilder>[]
|
||||
{
|
||||
new TagHelperAttributeDescriptor("type", inputTypePropertyInfo)
|
||||
},
|
||||
RequiredAttributes = new[] { new TagHelperRequiredAttributeDescriptor { Name = "type" } },
|
||||
},
|
||||
new TagHelperDescriptor
|
||||
{
|
||||
TagName = "input",
|
||||
TypeName = "TestNamespace.InputTagHelper2",
|
||||
AssemblyName = "TestAssembly",
|
||||
Attributes = new TagHelperAttributeDescriptor[]
|
||||
builder => builder.RequireAttribute(attribute => attribute.Name("class")),
|
||||
}),
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "input",
|
||||
typeName: "TestNamespace.InputTagHelper",
|
||||
assemblyName: "TestAssembly",
|
||||
attributes: new Action<ITagHelperBoundAttributeDescriptorBuilder>[]
|
||||
{
|
||||
new TagHelperAttributeDescriptor("type", inputTypePropertyInfo),
|
||||
new TagHelperAttributeDescriptor("checked", inputCheckedPropertyInfo)
|
||||
builder => BuildBoundAttributeDescriptorFromPropertyInfo(builder, "type", inputTypePropertyInfo),
|
||||
},
|
||||
RequiredAttributes = new[]
|
||||
ruleBuilders: new Action<TagMatchingRuleBuilder>[]
|
||||
{
|
||||
new TagHelperRequiredAttributeDescriptor { Name = "type" },
|
||||
new TagHelperRequiredAttributeDescriptor { Name = "checked" }
|
||||
builder => builder.RequireAttribute(attribute => attribute.Name("type")),
|
||||
}),
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "input",
|
||||
typeName: "TestNamespace.InputTagHelper2",
|
||||
assemblyName: "TestAssembly",
|
||||
attributes: new Action<ITagHelperBoundAttributeDescriptorBuilder>[]
|
||||
{
|
||||
builder => BuildBoundAttributeDescriptorFromPropertyInfo(builder, "type", inputTypePropertyInfo),
|
||||
builder => BuildBoundAttributeDescriptorFromPropertyInfo(builder, "checked", inputCheckedPropertyInfo),
|
||||
},
|
||||
},
|
||||
new TagHelperDescriptor
|
||||
{
|
||||
TagName = "*",
|
||||
TypeName = "TestNamespace.CatchAllTagHelper",
|
||||
AssemblyName = "TestAssembly",
|
||||
RequiredAttributes = new[] { new TagHelperRequiredAttributeDescriptor { Name = "catchAll" } },
|
||||
}
|
||||
ruleBuilders: new Action<TagMatchingRuleBuilder>[]
|
||||
{
|
||||
builder => builder
|
||||
.RequireAttribute(attribute => attribute.Name("type"))
|
||||
.RequireAttribute(attribute => attribute.Name("checked")),
|
||||
}),
|
||||
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[]
|
||||
{
|
||||
new TagHelperDescriptor
|
||||
{
|
||||
TagName = "input",
|
||||
TypeName = "TestNamespace.InputTagHelper1",
|
||||
AssemblyName = "TestAssembly",
|
||||
Attributes = new[]
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "input",
|
||||
typeName: "TestNamespace.InputTagHelper1",
|
||||
assemblyName: "TestAssembly",
|
||||
attributes: new Action<ITagHelperBoundAttributeDescriptorBuilder>[]
|
||||
{
|
||||
new TagHelperAttributeDescriptor
|
||||
{
|
||||
Name = "int-prefix-grabber",
|
||||
PropertyName = "IntProperty",
|
||||
TypeName = typeof(int).FullName
|
||||
},
|
||||
new TagHelperAttributeDescriptor
|
||||
{
|
||||
Name = "int-dictionary",
|
||||
PropertyName = "IntDictionaryProperty",
|
||||
TypeName = typeof(IDictionary<string, int>).FullName
|
||||
},
|
||||
new TagHelperAttributeDescriptor
|
||||
{
|
||||
Name = "string-dictionary",
|
||||
PropertyName = "StringDictionaryProperty",
|
||||
TypeName = "Namespace.DictionaryWithoutParameterlessConstructor<string, string>"
|
||||
},
|
||||
new TagHelperAttributeDescriptor
|
||||
{
|
||||
Name = "string-prefix-grabber",
|
||||
PropertyName = "StringProperty",
|
||||
TypeName = typeof(string).FullName,
|
||||
IsStringProperty = true
|
||||
},
|
||||
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[]
|
||||
builder => builder
|
||||
.Name("int-prefix-grabber")
|
||||
.PropertyName("IntProperty")
|
||||
.TypeName(typeof(int).FullName),
|
||||
builder => builder
|
||||
.Name("int-dictionary")
|
||||
.PropertyName("IntDictionaryProperty")
|
||||
.TypeName(typeof(IDictionary<string, int>).FullName)
|
||||
.AsDictionary("int-prefix-", typeof(int).FullName),
|
||||
builder => builder
|
||||
.Name("string-prefix-grabber")
|
||||
.PropertyName("StringProperty")
|
||||
.TypeName(typeof(string).FullName),
|
||||
builder => builder
|
||||
.Name("string-dictionary")
|
||||
.PropertyName("StringDictionaryProperty")
|
||||
.TypeName("Namespace.DictionaryWithoutParameterlessConstructor<string, string>")
|
||||
.AsDictionary("string-prefix-", typeof(string).FullName),
|
||||
}),
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "input",
|
||||
typeName: "TestNamespace.InputTagHelper2",
|
||||
assemblyName: "TestAssembly",
|
||||
attributes: new Action<ITagHelperBoundAttributeDescriptorBuilder>[]
|
||||
{
|
||||
new TagHelperAttributeDescriptor
|
||||
{
|
||||
Name = "int-dictionary",
|
||||
PropertyName = "IntDictionaryProperty",
|
||||
TypeName = typeof(int).FullName
|
||||
},
|
||||
new TagHelperAttributeDescriptor
|
||||
{
|
||||
Name = "string-dictionary",
|
||||
PropertyName = "StringDictionaryProperty",
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
builder => builder
|
||||
.Name("int-dictionary")
|
||||
.PropertyName("IntDictionaryProperty")
|
||||
.TypeName(typeof(int).FullName)
|
||||
.AsDictionary("int-prefix-", typeof(int).FullName),
|
||||
builder => builder
|
||||
.Name("string-dictionary")
|
||||
.PropertyName("StringDictionaryProperty")
|
||||
.TypeName("Namespace.DictionaryWithoutParameterlessConstructor<string, string>")
|
||||
.AsDictionary("string-prefix-", typeof(string).FullName),
|
||||
}),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -574,71 +402,120 @@ namespace Microsoft.AspNetCore.Razor.Evolution.IntegrationTests
|
|||
var propertyInfo = typeof(TestType).GetProperty("BoundProperty");
|
||||
return new[]
|
||||
{
|
||||
new TagHelperDescriptor
|
||||
{
|
||||
TagName = "MyTagHelper",
|
||||
TypeName = "TestNamespace.MyTagHelper",
|
||||
AssemblyName = "TestAssembly",
|
||||
Attributes = new []
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "MyTagHelper",
|
||||
typeName: "TestNamespace.MyTagHelper",
|
||||
assemblyName: "TestAssembly",
|
||||
attributes: new Action<ITagHelperBoundAttributeDescriptorBuilder>[]
|
||||
{
|
||||
new TagHelperAttributeDescriptor("BoundProperty", propertyInfo)
|
||||
}
|
||||
},
|
||||
new TagHelperDescriptor
|
||||
{
|
||||
TagName = "NestedTagHelper",
|
||||
TypeName = "TestNamespace.NestedTagHelper",
|
||||
AssemblyName = "TestAssembly"
|
||||
}
|
||||
builder => BuildBoundAttributeDescriptorFromPropertyInfo(builder, "BoundProperty", propertyInfo),
|
||||
}),
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "NestedTagHelper",
|
||||
typeName: "TestNamespace.NestedTagHelper",
|
||||
assemblyName: "TestAssembly"),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<TagHelperDescriptor> BuildPAndInputTagHelperDescriptors(string prefix)
|
||||
internal static IEnumerable<TagHelperDescriptor> DefaultPAndInputTagHelperDescriptors
|
||||
{
|
||||
var pAgePropertyInfo = typeof(TestType).GetProperty("Age");
|
||||
var inputTypePropertyInfo = typeof(TestType).GetProperty("Type");
|
||||
var checkedPropertyInfo = typeof(TestType).GetProperty("Checked");
|
||||
|
||||
return new[]
|
||||
get
|
||||
{
|
||||
new TagHelperDescriptor
|
||||
var pAgePropertyInfo = typeof(TestType).GetProperty("Age");
|
||||
var inputTypePropertyInfo = typeof(TestType).GetProperty("Type");
|
||||
var checkedPropertyInfo = typeof(TestType).GetProperty("Checked");
|
||||
|
||||
return new[]
|
||||
{
|
||||
Prefix = prefix,
|
||||
TagName = "p",
|
||||
TypeName = "TestNamespace.PTagHelper",
|
||||
AssemblyName = "TestAssembly",
|
||||
Attributes = new TagHelperAttributeDescriptor[]
|
||||
{
|
||||
new TagHelperAttributeDescriptor("age", pAgePropertyInfo)
|
||||
},
|
||||
TagStructure = TagStructure.NormalOrSelfClosing
|
||||
},
|
||||
new TagHelperDescriptor
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "p",
|
||||
typeName: "TestNamespace.PTagHelper",
|
||||
assemblyName: "TestAssembly",
|
||||
attributes: new Action<ITagHelperBoundAttributeDescriptorBuilder>[]
|
||||
{
|
||||
builder => BuildBoundAttributeDescriptorFromPropertyInfo(builder, "age", pAgePropertyInfo),
|
||||
},
|
||||
ruleBuilders: new Action<TagMatchingRuleBuilder>[]
|
||||
{
|
||||
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,
|
||||
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)
|
||||
},
|
||||
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 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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
var descriptors = new[]
|
||||
{
|
||||
new TagHelperDescriptor
|
||||
{
|
||||
TagName = "p",
|
||||
TypeName = "PTagHelper"
|
||||
},
|
||||
ITagHelperDescriptorBuilder.Create("PTagHelper", "TestAssembly")
|
||||
.TagMatchingRule(rule => rule.RequireTagName("p"))
|
||||
.Build()
|
||||
};
|
||||
|
||||
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
|
||||
var descriptors = new[]
|
||||
{
|
||||
new TagHelperDescriptor
|
||||
{
|
||||
TagName = "p",
|
||||
TypeName = "PTagHelper",
|
||||
AssemblyName = "Test",
|
||||
Attributes = new[]
|
||||
{
|
||||
new TagHelperAttributeDescriptor
|
||||
{
|
||||
Name = "obj-attr",
|
||||
TypeName = typeof(object).FullName,
|
||||
PropertyName = "ObjectAttribute",
|
||||
},
|
||||
new TagHelperAttributeDescriptor
|
||||
{
|
||||
Name = "str-attr",
|
||||
TypeName = typeof(string).FullName,
|
||||
PropertyName = "StringAttribute",
|
||||
},
|
||||
}
|
||||
},
|
||||
ITagHelperDescriptorBuilder.Create("PTagHelper", "Test")
|
||||
.TagMatchingRule(rule => rule.RequireTagName("p"))
|
||||
.BindAttribute(attribute => attribute
|
||||
.Name("obj-attr")
|
||||
.TypeName(typeof(object).FullName)
|
||||
.PropertyName("ObjectAttribute"))
|
||||
.BindAttribute(attribute => attribute
|
||||
.Name("str-attr")
|
||||
.TypeName(typeof(string).FullName)
|
||||
.PropertyName("StringAttribute"))
|
||||
.Build()
|
||||
};
|
||||
|
||||
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
|
||||
var descriptors = new[]
|
||||
{
|
||||
new TagHelperDescriptor
|
||||
{
|
||||
TagName = "*",
|
||||
TypeName = "CatchAllTagHelper",
|
||||
AssemblyName = "SomeAssembly",
|
||||
Attributes = new[]
|
||||
{
|
||||
new TagHelperAttributeDescriptor
|
||||
{
|
||||
Name = "[item]",
|
||||
PropertyName = "ListItems",
|
||||
TypeName = typeof(List<string>).FullName
|
||||
},
|
||||
new TagHelperAttributeDescriptor
|
||||
{
|
||||
Name = "[(item)]",
|
||||
PropertyName = "ArrayItems",
|
||||
TypeName = typeof(string[]).FullName
|
||||
},
|
||||
new TagHelperAttributeDescriptor
|
||||
{
|
||||
Name = "(click)",
|
||||
PropertyName = "Event1",
|
||||
TypeName = typeof(Action).FullName
|
||||
},
|
||||
new TagHelperAttributeDescriptor
|
||||
{
|
||||
Name = "(^click)",
|
||||
PropertyName = "Event2",
|
||||
TypeName = typeof(Action).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" } },
|
||||
},
|
||||
ITagHelperDescriptorBuilder.Create("CatchAllTagHelper", "SomeAssembly")
|
||||
.TagMatchingRule(rule =>
|
||||
rule
|
||||
.RequireTagName("*")
|
||||
.RequireAttribute(attribute => attribute.Name("bound")))
|
||||
.BindAttribute(attribute =>
|
||||
attribute
|
||||
.Name("[item]")
|
||||
.PropertyName("ListItems")
|
||||
.TypeName(typeof(List<string>).Namespace + "List<System.String>"))
|
||||
.BindAttribute(attribute =>
|
||||
attribute
|
||||
.Name("[(item)]")
|
||||
.PropertyName("ArrayItems")
|
||||
.TypeName(typeof(string[]).Namespace + "System.String[]"))
|
||||
.BindAttribute(attribute =>
|
||||
attribute
|
||||
.Name("(click)")
|
||||
.PropertyName("Event1")
|
||||
.TypeName(typeof(Action).FullName))
|
||||
.BindAttribute(attribute =>
|
||||
attribute
|
||||
.Name("(^click)")
|
||||
.PropertyName("Event2")
|
||||
.TypeName(typeof(Action).FullName))
|
||||
.BindAttribute(attribute =>
|
||||
attribute
|
||||
.Name("*something")
|
||||
.PropertyName("StringProperty1")
|
||||
.TypeName(typeof(string).FullName))
|
||||
.BindAttribute(attribute =>
|
||||
attribute
|
||||
.Name("#local")
|
||||
.PropertyName("StringProperty2")
|
||||
.TypeName(typeof(string).FullName))
|
||||
.Build()
|
||||
};
|
||||
var descriptorProvider = new TagHelperDescriptorProvider(descriptors);
|
||||
var descriptorProvider = new TagHelperDescriptorProvider(null, descriptors);
|
||||
|
||||
// Act & Assert
|
||||
EvaluateData(descriptorProvider, documentContent, (MarkupBlock)expectedOutput, expectedErrors: new RazorError[0]);
|
||||
|
|
@ -225,16 +215,15 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
|||
{
|
||||
// Arrange
|
||||
var descriptors = new TagHelperDescriptor[]
|
||||
{
|
||||
new TagHelperDescriptor
|
||||
{
|
||||
TagName = "input",
|
||||
TypeName = "InputTagHelper",
|
||||
AssemblyName = "SomeAssembly",
|
||||
TagStructure = TagStructure.WithoutEndTag,
|
||||
}
|
||||
};
|
||||
var descriptorProvider = new TagHelperDescriptorProvider(descriptors);
|
||||
{
|
||||
ITagHelperDescriptorBuilder.Create("InputTagHelper", "SomeAssembly")
|
||||
.TagMatchingRule(rule =>
|
||||
rule
|
||||
.RequireTagName("input")
|
||||
.RequireTagStructure(TagStructure.WithoutEndTag))
|
||||
.Build()
|
||||
};
|
||||
var descriptorProvider = new TagHelperDescriptorProvider(null, descriptors);
|
||||
|
||||
// Act & Assert
|
||||
EvaluateData(descriptorProvider, documentContent, (MarkupBlock)expectedOutput, expectedErrors: new RazorError[0]);
|
||||
|
|
@ -317,31 +306,29 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
|||
[MemberData(nameof(TagStructureCompatibilityData))]
|
||||
public void Rewrite_AllowsCompatibleTagStructures(
|
||||
string documentContent,
|
||||
int structure1,
|
||||
int structure2,
|
||||
TagStructure structure1,
|
||||
TagStructure structure2,
|
||||
object expectedOutput)
|
||||
{
|
||||
// Arrange
|
||||
var factory = new SpanFactory();
|
||||
var blockFactory = new BlockFactory(factory);
|
||||
var descriptors = new TagHelperDescriptor[]
|
||||
{
|
||||
new TagHelperDescriptor
|
||||
{
|
||||
TagName = "input",
|
||||
TypeName = "InputTagHelper1",
|
||||
AssemblyName = "SomeAssembly",
|
||||
TagStructure = (TagStructure)structure1
|
||||
},
|
||||
new TagHelperDescriptor
|
||||
{
|
||||
TagName = "input",
|
||||
TypeName = "InputTagHelper2",
|
||||
AssemblyName = "SomeAssembly",
|
||||
TagStructure = (TagStructure)structure2
|
||||
}
|
||||
};
|
||||
var descriptorProvider = new TagHelperDescriptorProvider(descriptors);
|
||||
{
|
||||
ITagHelperDescriptorBuilder.Create("InputTagHelper1", "SomeAssembly")
|
||||
.TagMatchingRule(rule =>
|
||||
rule
|
||||
.RequireTagName("input")
|
||||
.RequireTagStructure(structure1))
|
||||
.Build(),
|
||||
ITagHelperDescriptorBuilder.Create("InputTagHelper2", "SomeAssembly")
|
||||
.TagMatchingRule(rule =>
|
||||
rule
|
||||
.RequireTagName("input")
|
||||
.RequireTagStructure(structure2))
|
||||
.Build()
|
||||
};
|
||||
var descriptorProvider = new TagHelperDescriptorProvider(null, descriptors);
|
||||
|
||||
// Act & Assert
|
||||
EvaluateData(descriptorProvider, documentContent, (MarkupBlock)expectedOutput, expectedErrors: new RazorError[0]);
|
||||
|
|
@ -1199,43 +1186,33 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
|||
|
||||
[Theory]
|
||||
[MemberData(nameof(CodeTagHelperAttributesData))]
|
||||
public void TagHelperParseTreeRewriter_CreatesMarkupCodeSpansForNonStringTagHelperAttributes(
|
||||
public void Rewrite_CreatesMarkupCodeSpansForNonStringTagHelperAttributes(
|
||||
string documentContent,
|
||||
object expectedOutput)
|
||||
{
|
||||
// Arrange
|
||||
var descriptors = new TagHelperDescriptor[]
|
||||
{
|
||||
new TagHelperDescriptor
|
||||
{
|
||||
TagName = "person",
|
||||
TypeName = "PersonTagHelper",
|
||||
AssemblyName = "personAssembly",
|
||||
Attributes = new[]
|
||||
{
|
||||
new TagHelperAttributeDescriptor
|
||||
{
|
||||
Name = "age",
|
||||
PropertyName = "Age",
|
||||
TypeName = typeof(int).FullName
|
||||
},
|
||||
new TagHelperAttributeDescriptor
|
||||
{
|
||||
Name = "birthday",
|
||||
PropertyName = "BirthDay",
|
||||
TypeName = typeof(DateTime).FullName
|
||||
},
|
||||
new TagHelperAttributeDescriptor
|
||||
{
|
||||
Name = "name",
|
||||
PropertyName = "Name",
|
||||
TypeName = typeof(string).FullName,
|
||||
IsStringProperty = true
|
||||
}
|
||||
}
|
||||
}
|
||||
ITagHelperDescriptorBuilder.Create("PersonTagHelper", "personAssembly")
|
||||
.TagMatchingRule(rule => rule.RequireTagName("person"))
|
||||
.BindAttribute(attribute =>
|
||||
attribute
|
||||
.Name("age")
|
||||
.PropertyName("Age")
|
||||
.TypeName(typeof(int).FullName))
|
||||
.BindAttribute(attribute =>
|
||||
attribute
|
||||
.Name("birthday")
|
||||
.PropertyName("BirthDay")
|
||||
.TypeName(typeof(DateTime).FullName))
|
||||
.BindAttribute(attribute =>
|
||||
attribute
|
||||
.Name("name")
|
||||
.PropertyName("Name")
|
||||
.TypeName(typeof(string).FullName))
|
||||
.Build()
|
||||
};
|
||||
var providerContext = new TagHelperDescriptorProvider(descriptors);
|
||||
var providerContext = new TagHelperDescriptorProvider(null, descriptors);
|
||||
|
||||
// Act & Assert
|
||||
EvaluateData(providerContext,
|
||||
|
|
@ -2253,31 +2230,22 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
|||
{
|
||||
// Arrange
|
||||
var descriptors = new TagHelperDescriptor[]
|
||||
{
|
||||
new TagHelperDescriptor
|
||||
{
|
||||
TagName = "myth",
|
||||
TypeName = "mythTagHelper",
|
||||
AssemblyName = "SomeAssembly",
|
||||
Attributes = new[]
|
||||
{
|
||||
new TagHelperAttributeDescriptor
|
||||
{
|
||||
Name = "bound",
|
||||
PropertyName = "Bound",
|
||||
TypeName = typeof(bool).FullName
|
||||
},
|
||||
new TagHelperAttributeDescriptor
|
||||
{
|
||||
Name = "name",
|
||||
PropertyName = "Name",
|
||||
TypeName = typeof(string).FullName,
|
||||
IsStringProperty = true
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
var descriptorProvider = new TagHelperDescriptorProvider(descriptors);
|
||||
{
|
||||
ITagHelperDescriptorBuilder.Create("mythTagHelper", "SomeAssembly")
|
||||
.TagMatchingRule(rule => rule.RequireTagName("myth"))
|
||||
.BindAttribute(attribute =>
|
||||
attribute
|
||||
.Name("bound")
|
||||
.PropertyName("Bound")
|
||||
.TypeName(typeof(bool).FullName))
|
||||
.BindAttribute(attribute =>
|
||||
attribute
|
||||
.Name("name")
|
||||
.PropertyName("Name")
|
||||
.TypeName(typeof(string).FullName))
|
||||
.Build()
|
||||
};
|
||||
var descriptorProvider = new TagHelperDescriptorProvider(null, descriptors);
|
||||
|
||||
// Act & Assert
|
||||
EvaluateData(descriptorProvider, documentContent, (MarkupBlock)expectedOutput, (RazorError[])expectedErrors);
|
||||
|
|
@ -3002,7 +2970,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
|||
new[]
|
||||
{
|
||||
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,
|
||||
lineIndex: 0,
|
||||
columnIndex: 7,
|
||||
|
|
@ -3022,7 +2990,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
|||
new[]
|
||||
{
|
||||
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,
|
||||
lineIndex: 0,
|
||||
columnIndex: 7,
|
||||
|
|
@ -3911,126 +3879,63 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
|||
{
|
||||
// Arrange
|
||||
var descriptors = new TagHelperDescriptor[]
|
||||
{
|
||||
new TagHelperDescriptor
|
||||
{
|
||||
TagName = "input",
|
||||
TypeName = "InputTagHelper1",
|
||||
AssemblyName = "SomeAssembly",
|
||||
Attributes = new[]
|
||||
{
|
||||
new TagHelperAttributeDescriptor
|
||||
{
|
||||
Name = "bound-required-string",
|
||||
PropertyName = "BoundRequiredString",
|
||||
TypeName = typeof(string).FullName,
|
||||
IsStringProperty = true
|
||||
}
|
||||
},
|
||||
RequiredAttributes = new[]
|
||||
{
|
||||
new TagHelperRequiredAttributeDescriptor { Name = "unbound-required" }
|
||||
}
|
||||
},
|
||||
new TagHelperDescriptor
|
||||
{
|
||||
TagName = "input",
|
||||
TypeName = "InputTagHelper1",
|
||||
AssemblyName = "SomeAssembly",
|
||||
Attributes = new[]
|
||||
{
|
||||
new TagHelperAttributeDescriptor
|
||||
{
|
||||
Name = "bound-required-string",
|
||||
PropertyName = "BoundRequiredString",
|
||||
TypeName = typeof(string).FullName,
|
||||
IsStringProperty = true
|
||||
}
|
||||
},
|
||||
RequiredAttributes = new[]
|
||||
{
|
||||
new TagHelperRequiredAttributeDescriptor { Name = "bound-required-string" }
|
||||
}
|
||||
},
|
||||
new TagHelperDescriptor
|
||||
{
|
||||
TagName = "input",
|
||||
TypeName = "InputTagHelper2",
|
||||
AssemblyName = "SomeAssembly",
|
||||
Attributes = new[]
|
||||
{
|
||||
new TagHelperAttributeDescriptor
|
||||
{
|
||||
Name = "bound-required-int",
|
||||
PropertyName = "BoundRequiredInt",
|
||||
TypeName = typeof(int).FullName
|
||||
}
|
||||
},
|
||||
RequiredAttributes = new[]
|
||||
{
|
||||
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);
|
||||
{
|
||||
ITagHelperDescriptorBuilder.Create("InputTagHelper1", "SomeAssembly")
|
||||
.TagMatchingRule(rule =>
|
||||
rule
|
||||
.RequireTagName("input")
|
||||
.RequireAttribute(attribute => attribute.Name("unbound-required")))
|
||||
.TagMatchingRule(rule =>
|
||||
rule
|
||||
.RequireTagName("input")
|
||||
.RequireAttribute(attribute => attribute.Name("bound-required-string")))
|
||||
.BindAttribute(attribute =>
|
||||
attribute
|
||||
.Name("bound-required-string")
|
||||
.PropertyName("BoundRequiredString")
|
||||
.TypeName(typeof(string).FullName))
|
||||
.Build(),
|
||||
ITagHelperDescriptorBuilder.Create("InputTagHelper2", "SomeAssembly")
|
||||
.TagMatchingRule(rule =>
|
||||
rule
|
||||
.RequireTagName("input")
|
||||
.RequireAttribute(attribute => attribute.Name("bound-required-int")))
|
||||
.BindAttribute(attribute =>
|
||||
attribute
|
||||
.Name("bound-required-int")
|
||||
.PropertyName("BoundRequiredInt")
|
||||
.TypeName(typeof(int).FullName))
|
||||
.Build(),
|
||||
ITagHelperDescriptorBuilder.Create("InputTagHelper3", "SomeAssembly")
|
||||
.TagMatchingRule(rule => rule.RequireTagName("input"))
|
||||
.BindAttribute(attribute =>
|
||||
attribute
|
||||
.Name("int-dictionary")
|
||||
.PropertyName("DictionaryOfIntProperty")
|
||||
.TypeName(typeof(IDictionary<string, int>).Namespace + ".IDictionary<System.String, System.Int32>")
|
||||
.AsDictionary("int-prefix-", typeof(int).FullName))
|
||||
.BindAttribute(attribute =>
|
||||
attribute
|
||||
.Name("string-dictionary")
|
||||
.PropertyName("DictionaryOfStringProperty")
|
||||
.TypeName(typeof(IDictionary<string, string>).Namespace + ".IDictionary<System.String, System.String>")
|
||||
.AsDictionary("string-prefix-", typeof(string).FullName))
|
||||
.Build(),
|
||||
ITagHelperDescriptorBuilder.Create("PTagHelper", "SomeAssembly")
|
||||
.TagMatchingRule(rule => rule.RequireTagName("p"))
|
||||
.BindAttribute(attribute =>
|
||||
attribute
|
||||
.Name("bound-string")
|
||||
.PropertyName("BoundRequiredString")
|
||||
.TypeName(typeof(string).FullName))
|
||||
.BindAttribute(attribute =>
|
||||
attribute
|
||||
.Name("bound-int")
|
||||
.PropertyName("BoundRequiredString")
|
||||
.TypeName(typeof(int).FullName))
|
||||
.Build(),
|
||||
};
|
||||
var descriptorProvider = new TagHelperDescriptorProvider(null, descriptors);
|
||||
|
||||
// Act & Assert
|
||||
EvaluateData(descriptorProvider, documentContent, (MarkupBlock)expectedOutput, (RazorError[])expectedErrors);
|
||||
|
|
|
|||
|
|
@ -14,27 +14,22 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
|||
{
|
||||
get
|
||||
{
|
||||
var strongPParent = new TagHelperDescriptor
|
||||
{
|
||||
TagName = "strong",
|
||||
TypeName = "StrongTagHelper",
|
||||
AssemblyName = "SomeAssembly",
|
||||
RequiredParent = "p",
|
||||
};
|
||||
var strongDivParent = new TagHelperDescriptor
|
||||
{
|
||||
TagName = "strong",
|
||||
TypeName = "StrongTagHelper",
|
||||
AssemblyName = "SomeAssembly",
|
||||
RequiredParent = "div",
|
||||
};
|
||||
var catchAllPParent = new TagHelperDescriptor
|
||||
{
|
||||
TagName = "*",
|
||||
TypeName = "CatchAllTagHelper",
|
||||
AssemblyName = "SomeAssembly",
|
||||
RequiredParent = "p",
|
||||
};
|
||||
var strongPDivParent = ITagHelperDescriptorBuilder.Create("StrongTagHelper", "SomeAssembly")
|
||||
.TagMatchingRule(rule =>
|
||||
rule
|
||||
.RequireTagName("strong")
|
||||
.RequireParentTag("p"))
|
||||
.TagMatchingRule(rule =>
|
||||
rule
|
||||
.RequireTagName("strong")
|
||||
.RequireParentTag("div"))
|
||||
.Build();
|
||||
var catchAllPParent = ITagHelperDescriptorBuilder.Create("CatchAllTagHelper", "SomeAssembly")
|
||||
.TagMatchingRule(rule =>
|
||||
rule
|
||||
.RequireTagName("*")
|
||||
.RequireParentTag("p"))
|
||||
.Build();
|
||||
|
||||
return new TheoryData<
|
||||
string, // tagName
|
||||
|
|
@ -45,25 +40,25 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
|||
{
|
||||
"strong",
|
||||
"p",
|
||||
new[] { strongPParent, strongDivParent },
|
||||
new[] { strongPParent }
|
||||
new[] { strongPDivParent },
|
||||
new[] { strongPDivParent }
|
||||
},
|
||||
{
|
||||
"strong",
|
||||
"div",
|
||||
new[] { strongPParent, strongDivParent, catchAllPParent },
|
||||
new[] { strongDivParent }
|
||||
new[] { strongPDivParent, catchAllPParent },
|
||||
new[] { strongPDivParent }
|
||||
},
|
||||
{
|
||||
"strong",
|
||||
"p",
|
||||
new[] { strongPParent, strongDivParent, catchAllPParent },
|
||||
new[] { strongPParent, catchAllPParent }
|
||||
new[] { strongPDivParent, catchAllPParent },
|
||||
new[] { strongPDivParent, catchAllPParent }
|
||||
},
|
||||
{
|
||||
"custom",
|
||||
"p",
|
||||
new[] { strongPParent, strongDivParent, catchAllPParent },
|
||||
new[] { strongPDivParent, catchAllPParent },
|
||||
new[] { catchAllPParent }
|
||||
},
|
||||
};
|
||||
|
|
@ -72,93 +67,73 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
|||
|
||||
[Theory]
|
||||
[MemberData(nameof(RequiredParentData))]
|
||||
public void GetDescriptors_ReturnsDescriptorsParentTags(
|
||||
public void GetTagHelperBinding_ReturnsBindingResultWithDescriptorsParentTags(
|
||||
string tagName,
|
||||
string parentTagName,
|
||||
object availableDescriptors,
|
||||
object expectedDescriptors)
|
||||
{
|
||||
// Arrange
|
||||
var provider = new TagHelperDescriptorProvider((IEnumerable<TagHelperDescriptor>)availableDescriptors);
|
||||
var provider = new TagHelperDescriptorProvider(null, (IEnumerable<TagHelperDescriptor>)availableDescriptors);
|
||||
|
||||
// Act
|
||||
var resolvedDescriptors = provider.GetDescriptors(
|
||||
var bindingResult = provider.GetTagHelperBinding(
|
||||
tagName,
|
||||
attributes: Enumerable.Empty<KeyValuePair<string, string>>(),
|
||||
parentTagName: parentTagName);
|
||||
|
||||
// Assert
|
||||
Assert.Equal((IEnumerable<TagHelperDescriptor>)expectedDescriptors, resolvedDescriptors, CaseSensitiveTagHelperDescriptorComparer.Default);
|
||||
Assert.Equal((IEnumerable<TagHelperDescriptor>)expectedDescriptors, bindingResult.Descriptors, TagHelperDescriptorComparer.CaseSensitive);
|
||||
}
|
||||
|
||||
public static TheoryData RequiredAttributeData
|
||||
{
|
||||
get
|
||||
{
|
||||
var divDescriptor = new TagHelperDescriptor
|
||||
{
|
||||
TagName = "div",
|
||||
TypeName = "DivTagHelper",
|
||||
AssemblyName = "SomeAssembly",
|
||||
RequiredAttributes = new[] { new TagHelperRequiredAttributeDescriptor { Name = "style" } }
|
||||
};
|
||||
var inputDescriptor = new TagHelperDescriptor
|
||||
{
|
||||
TagName = "input",
|
||||
TypeName = "InputTagHelper",
|
||||
AssemblyName = "SomeAssembly",
|
||||
RequiredAttributes = new[]
|
||||
{
|
||||
new TagHelperRequiredAttributeDescriptor { Name = "class" },
|
||||
new TagHelperRequiredAttributeDescriptor { Name = "style" }
|
||||
}
|
||||
};
|
||||
var inputWildcardPrefixDescriptor = new TagHelperDescriptor
|
||||
{
|
||||
TagName = "input",
|
||||
TypeName = "InputWildCardAttribute",
|
||||
AssemblyName = "SomeAssembly",
|
||||
RequiredAttributes = new[]
|
||||
{
|
||||
new TagHelperRequiredAttributeDescriptor
|
||||
{
|
||||
Name = "nodashprefix",
|
||||
NameComparison = TagHelperRequiredAttributeNameComparison.PrefixMatch,
|
||||
}
|
||||
}
|
||||
};
|
||||
var catchAllDescriptor = new TagHelperDescriptor
|
||||
{
|
||||
TagName = TagHelperDescriptorProvider.ElementCatchAllTarget,
|
||||
TypeName = "CatchAllTagHelper",
|
||||
AssemblyName = "SomeAssembly",
|
||||
RequiredAttributes = new[] { new TagHelperRequiredAttributeDescriptor { Name = "class" } }
|
||||
};
|
||||
var catchAllDescriptor2 = new TagHelperDescriptor
|
||||
{
|
||||
TagName = TagHelperDescriptorProvider.ElementCatchAllTarget,
|
||||
TypeName = "CatchAllTagHelper2",
|
||||
AssemblyName = "SomeAssembly",
|
||||
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 divDescriptor = ITagHelperDescriptorBuilder.Create("DivTagHelper", "SomeAssembly")
|
||||
.TagMatchingRule(rule =>
|
||||
rule
|
||||
.RequireTagName("div")
|
||||
.RequireAttribute(attribute => attribute.Name("style")))
|
||||
.Build();
|
||||
var inputDescriptor = ITagHelperDescriptorBuilder.Create("InputTagHelper", "SomeAssembly")
|
||||
.TagMatchingRule(rule =>
|
||||
rule
|
||||
.RequireTagName("input")
|
||||
.RequireAttribute(attribute => attribute.Name("class"))
|
||||
.RequireAttribute(attribute => attribute.Name("style")))
|
||||
.Build();
|
||||
var inputWildcardPrefixDescriptor = ITagHelperDescriptorBuilder.Create("InputWildCardAttribute", "SomeAssembly")
|
||||
.TagMatchingRule(rule =>
|
||||
rule
|
||||
.RequireTagName("input")
|
||||
.RequireAttribute(attribute =>
|
||||
attribute
|
||||
.Name("nodashprefix")
|
||||
.NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.PrefixMatch)))
|
||||
.Build();
|
||||
var catchAllDescriptor = ITagHelperDescriptorBuilder.Create("CatchAllTagHelper", "SomeAssembly")
|
||||
.TagMatchingRule(rule =>
|
||||
rule
|
||||
.RequireTagName(TagHelperDescriptorProvider.ElementCatchAllTarget)
|
||||
.RequireAttribute(attribute => attribute.Name("class")))
|
||||
.Build();
|
||||
var catchAllDescriptor2 = ITagHelperDescriptorBuilder.Create("CatchAllTagHelper2", "SomeAssembly")
|
||||
.TagMatchingRule(rule =>
|
||||
rule
|
||||
.RequireTagName(TagHelperDescriptorProvider.ElementCatchAllTarget)
|
||||
.RequireAttribute(attribute => attribute.Name("custom"))
|
||||
.RequireAttribute(attribute => attribute.Name("class")))
|
||||
.Build();
|
||||
var catchAllWildcardPrefixDescriptor = ITagHelperDescriptorBuilder.Create("CatchAllWildCardAttribute", "SomeAssembly")
|
||||
.TagMatchingRule(rule =>
|
||||
rule
|
||||
.RequireTagName(TagHelperDescriptorProvider.ElementCatchAllTarget)
|
||||
.RequireAttribute(attribute =>
|
||||
attribute
|
||||
.Name("prefix-")
|
||||
.NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.PrefixMatch)))
|
||||
.Build();
|
||||
var defaultAvailableDescriptors =
|
||||
new[] { divDescriptor, inputDescriptor, catchAllDescriptor, catchAllDescriptor2 };
|
||||
var defaultWildcardDescriptors =
|
||||
|
|
@ -176,7 +151,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
|||
"div",
|
||||
new[] { kvp("custom") },
|
||||
defaultAvailableDescriptors,
|
||||
Enumerable.Empty<TagHelperDescriptor>()
|
||||
null
|
||||
},
|
||||
{ "div", new[] { kvp("style") }, defaultAvailableDescriptors, new[] { divDescriptor } },
|
||||
{ "div", new[] { kvp("class") }, defaultAvailableDescriptors, new[] { catchAllDescriptor } },
|
||||
|
|
@ -214,19 +189,19 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
|||
"input",
|
||||
new[] { kvp("prefixABCnodashprefix") },
|
||||
defaultWildcardDescriptors,
|
||||
Enumerable.Empty<TagHelperDescriptor>()
|
||||
null
|
||||
},
|
||||
{
|
||||
"input",
|
||||
new[] { kvp("prefix-") },
|
||||
defaultWildcardDescriptors,
|
||||
Enumerable.Empty<TagHelperDescriptor>()
|
||||
null
|
||||
},
|
||||
{
|
||||
"input",
|
||||
new[] { kvp("nodashprefix") },
|
||||
defaultWildcardDescriptors,
|
||||
Enumerable.Empty<TagHelperDescriptor>()
|
||||
null
|
||||
},
|
||||
{
|
||||
"input",
|
||||
|
|
@ -258,292 +233,222 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
|||
|
||||
[Theory]
|
||||
[MemberData(nameof(RequiredAttributeData))]
|
||||
public void GetDescriptors_ReturnsDescriptorsWithRequiredAttributes(
|
||||
public void GetTagHelperBinding_ReturnsBindingResultDescriptorsWithRequiredAttributes(
|
||||
string tagName,
|
||||
IEnumerable<KeyValuePair<string, string>> providedAttributes,
|
||||
object availableDescriptors,
|
||||
object expectedDescriptors)
|
||||
{
|
||||
// Arrange
|
||||
var provider = new TagHelperDescriptorProvider((IEnumerable<TagHelperDescriptor>)availableDescriptors);
|
||||
var provider = new TagHelperDescriptorProvider(null, (IEnumerable<TagHelperDescriptor>)availableDescriptors);
|
||||
|
||||
// Act
|
||||
var resolvedDescriptors = provider.GetDescriptors(tagName, providedAttributes, parentTagName: "p").ToArray();
|
||||
var bindingResult = provider.GetTagHelperBinding(tagName, providedAttributes, parentTagName: "p");
|
||||
|
||||
// Assert
|
||||
Assert.Equal((IEnumerable<TagHelperDescriptor>)expectedDescriptors, resolvedDescriptors, CaseSensitiveTagHelperDescriptorComparer.Default);
|
||||
Assert.Equal((IEnumerable<TagHelperDescriptor>)expectedDescriptors, bindingResult?.Descriptors, TagHelperDescriptorComparer.CaseSensitive);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetDescriptors_ReturnsEmptyDescriptorsWithPrefixAsTagName()
|
||||
public void GetTagHelperBinding_ReturnsNullBindingResultPrefixAsTagName()
|
||||
{
|
||||
// Arrange
|
||||
var catchAllDescriptor = CreatePrefixedDescriptor(
|
||||
"th",
|
||||
TagHelperDescriptorProvider.ElementCatchAllTarget,
|
||||
"foo1");
|
||||
var catchAllDescriptor = ITagHelperDescriptorBuilder.Create("foo1", "SomeAssembly")
|
||||
.TagMatchingRule(rule => rule.RequireTagName(TagHelperDescriptorProvider.ElementCatchAllTarget))
|
||||
.Build();
|
||||
var descriptors = new[] { catchAllDescriptor };
|
||||
var provider = new TagHelperDescriptorProvider(descriptors);
|
||||
var provider = new TagHelperDescriptorProvider("th", descriptors);
|
||||
|
||||
// Act
|
||||
var resolvedDescriptors = provider.GetDescriptors(
|
||||
var bindingResult = provider.GetTagHelperBinding(
|
||||
tagName: "th",
|
||||
attributes: Enumerable.Empty<KeyValuePair<string, string>>(),
|
||||
parentTagName: "p");
|
||||
|
||||
// Assert
|
||||
Assert.Empty(resolvedDescriptors);
|
||||
Assert.Null(bindingResult);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetDescriptors_DeduplicatesTagHelpersByTypeName()
|
||||
public void GetTagHelperBinding_ReturnsBindingResultCatchAllDescriptorsForPrefixedTags()
|
||||
{
|
||||
// Arrange
|
||||
var descriptors = new[]
|
||||
{
|
||||
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 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 catchAllDescriptor = ITagHelperDescriptorBuilder.Create("foo1", "SomeAssembly")
|
||||
.TagMatchingRule(rule => rule.RequireTagName(TagHelperDescriptorProvider.ElementCatchAllTarget))
|
||||
.Build();
|
||||
var descriptors = new[] { catchAllDescriptor };
|
||||
var provider = new TagHelperDescriptorProvider(descriptors);
|
||||
var provider = new TagHelperDescriptorProvider("th:", descriptors);
|
||||
|
||||
// Act
|
||||
var retrievedDescriptorsDiv = provider.GetDescriptors(
|
||||
var bindingResultDiv = provider.GetTagHelperBinding(
|
||||
tagName: "th:div",
|
||||
attributes: Enumerable.Empty<KeyValuePair<string, string>>(),
|
||||
parentTagName: "p");
|
||||
var retrievedDescriptorsSpan = provider.GetDescriptors(
|
||||
var bindingResultSpan = provider.GetTagHelperBinding(
|
||||
tagName: "th:span",
|
||||
attributes: Enumerable.Empty<KeyValuePair<string, string>>(),
|
||||
parentTagName: "p");
|
||||
|
||||
// Assert
|
||||
var descriptor = Assert.Single(retrievedDescriptorsDiv);
|
||||
var descriptor = Assert.Single(bindingResultDiv.Descriptors);
|
||||
Assert.Same(catchAllDescriptor, descriptor);
|
||||
descriptor = Assert.Single(retrievedDescriptorsSpan);
|
||||
descriptor = Assert.Single(bindingResultSpan.Descriptors);
|
||||
Assert.Same(catchAllDescriptor, descriptor);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetDescriptors_ReturnsDescriptorsForPrefixedTags()
|
||||
public void GetTagHelperBinding_ReturnsBindingResultDescriptorsForPrefixedTags()
|
||||
{
|
||||
// Arrange
|
||||
var divDescriptor = CreatePrefixedDescriptor("th:", "div", "foo1");
|
||||
var divDescriptor = ITagHelperDescriptorBuilder.Create("foo1", "SomeAssembly")
|
||||
.TagMatchingRule(rule => rule.RequireTagName("div"))
|
||||
.Build();
|
||||
var descriptors = new[] { divDescriptor };
|
||||
var provider = new TagHelperDescriptorProvider(descriptors);
|
||||
var provider = new TagHelperDescriptorProvider("th:", descriptors);
|
||||
|
||||
// Act
|
||||
var retrievedDescriptors = provider.GetDescriptors(
|
||||
var bindingResult = provider.GetTagHelperBinding(
|
||||
tagName: "th:div",
|
||||
attributes: Enumerable.Empty<KeyValuePair<string, string>>(),
|
||||
parentTagName: "p");
|
||||
|
||||
// Assert
|
||||
var descriptor = Assert.Single(retrievedDescriptors);
|
||||
var descriptor = Assert.Single(bindingResult.Descriptors);
|
||||
Assert.Same(divDescriptor, descriptor);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("*")]
|
||||
[InlineData("div")]
|
||||
public void GetDescriptors_ReturnsNothingForUnprefixedTags(string tagName)
|
||||
public void GetTagHelperBinding_ReturnsNullForUnprefixedTags(string tagName)
|
||||
{
|
||||
// Arrange
|
||||
var divDescriptor = CreatePrefixedDescriptor("th:", tagName, "foo1");
|
||||
var divDescriptor = ITagHelperDescriptorBuilder.Create("foo1", "SomeAssembly")
|
||||
.TagMatchingRule(rule => rule.RequireTagName(tagName))
|
||||
.Build();
|
||||
var descriptors = new[] { divDescriptor };
|
||||
var provider = new TagHelperDescriptorProvider(descriptors);
|
||||
var provider = new TagHelperDescriptorProvider("th:", descriptors);
|
||||
|
||||
// Act
|
||||
var retrievedDescriptorsDiv = provider.GetDescriptors(
|
||||
var bindingResult = provider.GetTagHelperBinding(
|
||||
tagName: "div",
|
||||
attributes: Enumerable.Empty<KeyValuePair<string, string>>(),
|
||||
parentTagName: "p");
|
||||
|
||||
// Assert
|
||||
Assert.Empty(retrievedDescriptorsDiv);
|
||||
Assert.Null(bindingResult);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetDescriptors_ReturnsNothingForUnregisteredTags()
|
||||
{
|
||||
// Arrange
|
||||
var divDescriptor = new TagHelperDescriptor
|
||||
{
|
||||
TagName = "div",
|
||||
TypeName = "foo1",
|
||||
AssemblyName = "SomeAssembly",
|
||||
};
|
||||
var spanDescriptor = new TagHelperDescriptor
|
||||
{
|
||||
TagName = "span",
|
||||
TypeName = "foo2",
|
||||
AssemblyName = "SomeAssembly",
|
||||
};
|
||||
var divDescriptor = ITagHelperDescriptorBuilder.Create("foo1", "SomeAssembly")
|
||||
.TagMatchingRule(rule => rule.RequireTagName("div"))
|
||||
.Build();
|
||||
var spanDescriptor = ITagHelperDescriptorBuilder.Create("foo2", "SomeAssembly")
|
||||
.TagMatchingRule(rule => rule.RequireTagName("span"))
|
||||
.Build();
|
||||
var descriptors = new TagHelperDescriptor[] { divDescriptor, spanDescriptor };
|
||||
var provider = new TagHelperDescriptorProvider(descriptors);
|
||||
var provider = new TagHelperDescriptorProvider(null, descriptors);
|
||||
|
||||
// Act
|
||||
var retrievedDescriptors = provider.GetDescriptors(
|
||||
var tagHelperBinding = provider.GetTagHelperBinding(
|
||||
tagName: "foo",
|
||||
attributes: Enumerable.Empty<KeyValuePair<string, string>>(),
|
||||
parentTagName: "p");
|
||||
|
||||
// Assert
|
||||
Assert.Empty(retrievedDescriptors);
|
||||
Assert.Null(tagHelperBinding);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetDescriptors_ReturnsCatchAllsWithEveryTagName()
|
||||
{
|
||||
// Arrange
|
||||
var divDescriptor = new TagHelperDescriptor
|
||||
{
|
||||
TagName = "div",
|
||||
TypeName = "foo1",
|
||||
AssemblyName = "SomeAssembly",
|
||||
};
|
||||
var spanDescriptor = new TagHelperDescriptor
|
||||
{
|
||||
TagName = "span",
|
||||
TypeName = "foo2",
|
||||
AssemblyName = "SomeAssembly",
|
||||
};
|
||||
var catchAllDescriptor = new TagHelperDescriptor
|
||||
{
|
||||
TagName = TagHelperDescriptorProvider.ElementCatchAllTarget,
|
||||
TypeName = "foo3",
|
||||
AssemblyName = "SomeAssembly",
|
||||
};
|
||||
var divDescriptor = ITagHelperDescriptorBuilder.Create("foo1", "SomeAssembly")
|
||||
.TagMatchingRule(rule => rule.RequireTagName("div"))
|
||||
.Build();
|
||||
var spanDescriptor = ITagHelperDescriptorBuilder.Create("foo2", "SomeAssembly")
|
||||
.TagMatchingRule(rule => rule.RequireTagName("span"))
|
||||
.Build();
|
||||
var catchAllDescriptor = ITagHelperDescriptorBuilder.Create("foo3", "SomeAssembly")
|
||||
.TagMatchingRule(rule => rule.RequireTagName(TagHelperDescriptorProvider.ElementCatchAllTarget))
|
||||
.Build();
|
||||
var descriptors = new TagHelperDescriptor[] { divDescriptor, spanDescriptor, catchAllDescriptor };
|
||||
var provider = new TagHelperDescriptorProvider(descriptors);
|
||||
var provider = new TagHelperDescriptorProvider(null, descriptors);
|
||||
|
||||
// Act
|
||||
var divDescriptors = provider.GetDescriptors(
|
||||
var divBinding = provider.GetTagHelperBinding(
|
||||
tagName: "div",
|
||||
attributes: Enumerable.Empty<KeyValuePair<string, string>>(),
|
||||
parentTagName: "p");
|
||||
var spanDescriptors = provider.GetDescriptors(
|
||||
var spanBinding = provider.GetTagHelperBinding(
|
||||
tagName: "span",
|
||||
attributes: Enumerable.Empty<KeyValuePair<string, string>>(),
|
||||
parentTagName: "p");
|
||||
|
||||
// Assert
|
||||
// For divs
|
||||
Assert.Equal(2, divDescriptors.Count());
|
||||
Assert.Contains(divDescriptor, divDescriptors);
|
||||
Assert.Contains(catchAllDescriptor, divDescriptors);
|
||||
Assert.Equal(2, divBinding.Descriptors.Count());
|
||||
Assert.Contains(divDescriptor, divBinding.Descriptors);
|
||||
Assert.Contains(catchAllDescriptor, divBinding.Descriptors);
|
||||
|
||||
// For spans
|
||||
Assert.Equal(2, spanDescriptors.Count());
|
||||
Assert.Contains(spanDescriptor, spanDescriptors);
|
||||
Assert.Contains(catchAllDescriptor, spanDescriptors);
|
||||
Assert.Equal(2, spanBinding.Descriptors.Count());
|
||||
Assert.Contains(spanDescriptor, spanBinding.Descriptors);
|
||||
Assert.Contains(catchAllDescriptor, spanBinding.Descriptors);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetDescriptors_DuplicateDescriptorsAreNotPartOfTagHelperDescriptorPool()
|
||||
{
|
||||
// Arrange
|
||||
var divDescriptor = new TagHelperDescriptor
|
||||
{
|
||||
TagName = "div",
|
||||
TypeName = "foo1",
|
||||
AssemblyName = "SomeAssembly",
|
||||
};
|
||||
var divDescriptor = ITagHelperDescriptorBuilder.Create("foo1", "SomeAssembly")
|
||||
.TagMatchingRule(rule => rule.RequireTagName("div"))
|
||||
.Build();
|
||||
var descriptors = new TagHelperDescriptor[] { divDescriptor, divDescriptor };
|
||||
var provider = new TagHelperDescriptorProvider(descriptors);
|
||||
var provider = new TagHelperDescriptorProvider(null, descriptors);
|
||||
|
||||
// Act
|
||||
var retrievedDescriptors = provider.GetDescriptors(
|
||||
var bindingResult = provider.GetTagHelperBinding(
|
||||
tagName: "div",
|
||||
attributes: Enumerable.Empty<KeyValuePair<string, string>>(),
|
||||
parentTagName: "p");
|
||||
|
||||
// Assert
|
||||
var descriptor = Assert.Single(retrievedDescriptors);
|
||||
var descriptor = Assert.Single(bindingResult.Descriptors);
|
||||
Assert.Same(divDescriptor, descriptor);
|
||||
}
|
||||
|
||||
private static TagHelperDescriptor CreatePrefixedDescriptor(string prefix, string tagName, string typeName)
|
||||
[Fact]
|
||||
public void GetTagHelperBinding_DescriptorWithMultipleRules_CorrectlySelectsMatchingRules()
|
||||
{
|
||||
return new TagHelperDescriptor
|
||||
{
|
||||
Prefix = prefix,
|
||||
TagName = tagName,
|
||||
TypeName = typeName,
|
||||
AssemblyName = "SomeAssembly"
|
||||
};
|
||||
// Arrange
|
||||
var multiRuleDescriptor = ITagHelperDescriptorBuilder.Create("foo", "SomeAssembly")
|
||||
.TagMatchingRule(rule => rule
|
||||
.RequireTagName(TagHelperDescriptorProvider.ElementCatchAllTarget)
|
||||
.RequireParentTag("body"))
|
||||
.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.
|
||||
// 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.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Microsoft.AspNetCore.Razor.Evolution;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
||||
{
|
||||
|
|
@ -41,27 +37,25 @@ namespace Microsoft.AspNetCore.Razor.Evolution.Legacy
|
|||
|
||||
foreach (var tagName in tagNames)
|
||||
{
|
||||
descriptors.Add(
|
||||
new TagHelperDescriptor
|
||||
{
|
||||
TagName = tagName,
|
||||
TypeName = tagName + "taghelper",
|
||||
AssemblyName = "SomeAssembly"
|
||||
});
|
||||
var descriptor = ITagHelperDescriptorBuilder.Create(tagName + "taghelper", "SomeAssembly")
|
||||
.TagMatchingRule(rule => rule.RequireTagName(tagName))
|
||||
.Build();
|
||||
descriptors.Add(descriptor);
|
||||
}
|
||||
|
||||
return new TagHelperDescriptorProvider(descriptors);
|
||||
return new TagHelperDescriptorProvider(null, descriptors);
|
||||
}
|
||||
|
||||
internal void EvaluateData(
|
||||
TagHelperDescriptorProvider provider,
|
||||
string documentContent,
|
||||
MarkupBlock expectedOutput,
|
||||
IEnumerable<RazorError> expectedErrors)
|
||||
IEnumerable<RazorError> expectedErrors,
|
||||
string tagHelperPrefix = null)
|
||||
{
|
||||
var syntaxTree = ParseDocument(documentContent);
|
||||
var errorSink = new ErrorSink();
|
||||
var parseTreeRewriter = new TagHelperParseTreeRewriter(provider);
|
||||
var parseTreeRewriter = new TagHelperParseTreeRewriter(tagHelperPrefix, provider);
|
||||
var actualTree = parseTreeRewriter.Rewrite(syntaxTree.Root, errorSink);
|
||||
|
||||
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 System.Linq;
|
||||
using Moq;
|
||||
using System.Text;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Evolution
|
||||
{
|
||||
|
|
@ -20,16 +21,14 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
{
|
||||
builder.AddTagHelpers(new[]
|
||||
{
|
||||
new TagHelperDescriptor
|
||||
{
|
||||
AssemblyName = "TestAssembly",
|
||||
TagName = "form",
|
||||
},
|
||||
new TagHelperDescriptor
|
||||
{
|
||||
AssemblyName = "TestAssembly",
|
||||
TagName = "input",
|
||||
}
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "form",
|
||||
typeName: null,
|
||||
assemblyName: "TestAssembly"),
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "input",
|
||||
typeName: null,
|
||||
assemblyName: "TestAssembly"),
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -59,39 +58,25 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
public void Execute_DirectiveWithoutQuotes_RewritesTagHelpers_TagHelperMatchesElementTwice()
|
||||
{
|
||||
// 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 =>
|
||||
{
|
||||
builder.AddTagHelpers(new[]
|
||||
{
|
||||
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
|
||||
}
|
||||
},
|
||||
}
|
||||
});
|
||||
builder.AddTagHelpers(new[] { descriptor });
|
||||
});
|
||||
|
||||
var pass = new TagHelperBinderSyntaxTreePass()
|
||||
|
|
@ -117,46 +102,32 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
|
||||
var formTagHelper = Assert.IsType<TagHelperBlock>(rewrittenTree.Root.Children[2]);
|
||||
Assert.Equal("form", formTagHelper.TagName);
|
||||
Assert.Single(formTagHelper.Descriptors);
|
||||
Assert.Equal(2, formTagHelper.Binding.GetBoundRules(descriptor).Count());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Execute_DirectiveWithQuotes_RewritesTagHelpers_TagHelperMatchesElementTwice()
|
||||
{
|
||||
// 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 =>
|
||||
{
|
||||
builder.AddTagHelpers(new[]
|
||||
{
|
||||
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
|
||||
}
|
||||
},
|
||||
}
|
||||
});
|
||||
builder.AddTagHelpers(new[] { descriptor });
|
||||
});
|
||||
|
||||
var pass = new TagHelperBinderSyntaxTreePass()
|
||||
|
|
@ -182,7 +153,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
|
||||
var formTagHelper = Assert.IsType<TagHelperBlock>(rewrittenTree.Root.Children[2]);
|
||||
Assert.Equal("form", formTagHelper.TagName);
|
||||
Assert.Single(formTagHelper.Descriptors);
|
||||
Assert.Equal(2, formTagHelper.Binding.GetBoundRules(descriptor).Count());
|
||||
}
|
||||
|
||||
[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 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));
|
||||
});
|
||||
|
||||
|
|
@ -327,6 +298,36 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
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]
|
||||
public void Execute_CombinesErrorsOnRewritingErrors()
|
||||
{
|
||||
|
|
@ -335,16 +336,14 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
{
|
||||
builder.AddTagHelpers(new[]
|
||||
{
|
||||
new TagHelperDescriptor
|
||||
{
|
||||
TagName = "form",
|
||||
AssemblyName= "TestAssembly",
|
||||
},
|
||||
new TagHelperDescriptor
|
||||
{
|
||||
TagName = "input",
|
||||
AssemblyName= "TestAssembly",
|
||||
}
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "form",
|
||||
typeName: null,
|
||||
assemblyName: "TestAssembly"),
|
||||
CreateTagHelperDescriptor(
|
||||
tagName: "input",
|
||||
typeName: null,
|
||||
assemblyName: "TestAssembly"),
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -634,12 +633,10 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
{
|
||||
get
|
||||
{
|
||||
return new TagHelperDescriptor
|
||||
{
|
||||
TagName = "valid_plain",
|
||||
TypeName = "Microsoft.AspNetCore.Razor.TagHelpers.ValidPlainTagHelper",
|
||||
AssemblyName = AssemblyA,
|
||||
};
|
||||
return CreateTagHelperDescriptor(
|
||||
tagName: "valid_plain",
|
||||
typeName: "Microsoft.AspNetCore.Razor.TagHelpers.ValidPlainTagHelper",
|
||||
assemblyName: AssemblyA);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -647,48 +644,34 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
{
|
||||
get
|
||||
{
|
||||
return new TagHelperDescriptor
|
||||
{
|
||||
TagName = "valid_inherited",
|
||||
TypeName = "Microsoft.AspNetCore.Razor.TagHelpers.ValidInheritedTagHelper",
|
||||
AssemblyName = AssemblyA
|
||||
};
|
||||
return CreateTagHelperDescriptor(
|
||||
tagName: "valid_inherited",
|
||||
typeName: "Microsoft.AspNetCore.Razor.TagHelpers.ValidInheritedTagHelper",
|
||||
assemblyName: AssemblyA);
|
||||
}
|
||||
}
|
||||
|
||||
private static TagHelperDescriptor[] AllTagHelpers => new[]
|
||||
{
|
||||
Valid_PlainTagHelperDescriptor,
|
||||
Valid_InheritedTagHelperDescriptor,
|
||||
String_TagHelperDescriptor
|
||||
};
|
||||
|
||||
private static TagHelperDescriptor String_TagHelperDescriptor
|
||||
{
|
||||
get
|
||||
{
|
||||
// We're treating 'string' as a TagHelper so we can test TagHelpers in multiple assemblies without
|
||||
// building a separate assembly with a single TagHelper.
|
||||
return new TagHelperDescriptor
|
||||
{
|
||||
TagName = "string",
|
||||
TypeName = "System.String",
|
||||
AssemblyName = AssemblyB,
|
||||
};
|
||||
return CreateTagHelperDescriptor(
|
||||
tagName: "string",
|
||||
typeName: "System.String",
|
||||
assemblyName: AssemblyB);
|
||||
}
|
||||
}
|
||||
|
||||
public static TheoryData ProcessDirectives_TagHelperPrefixData
|
||||
public static TheoryData ProcessTagHelperPrefixData
|
||||
{
|
||||
get
|
||||
{
|
||||
return new TheoryData<
|
||||
IEnumerable<TagHelperDescriptor>, // tagHelpers
|
||||
IEnumerable<TagHelperDirectiveDescriptor>, // directiveDescriptors
|
||||
IEnumerable<TagHelperDescriptor>> // expectedDescriptors
|
||||
// directiveDescriptors, expected prefix
|
||||
return new TheoryData<IEnumerable<TagHelperDirectiveDescriptor>, string>
|
||||
{
|
||||
{
|
||||
AllTagHelpers,
|
||||
new []
|
||||
{
|
||||
CreateTagHelperDirectiveDescriptor("", TagHelperDirectiveType.TagHelperPrefix),
|
||||
|
|
@ -696,10 +679,9 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
"Microsoft.AspNetCore.Razor.TagHelpers.ValidPlain*, " + AssemblyA,
|
||||
TagHelperDirectiveType.AddTagHelper),
|
||||
},
|
||||
new [] { Valid_PlainTagHelperDescriptor }
|
||||
null
|
||||
},
|
||||
{
|
||||
AllTagHelpers,
|
||||
new []
|
||||
{
|
||||
CreateTagHelperDirectiveDescriptor("th:", TagHelperDirectiveType.TagHelperPrefix),
|
||||
|
|
@ -707,23 +689,17 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
"Microsoft.AspNetCore.Razor.TagHelpers.ValidPlain*, " + AssemblyA,
|
||||
TagHelperDirectiveType.AddTagHelper),
|
||||
},
|
||||
new [] { CreatePrefixedValidPlainDescriptor("th:") }
|
||||
"th:"
|
||||
},
|
||||
{
|
||||
AllTagHelpers,
|
||||
new []
|
||||
{
|
||||
CreateTagHelperDirectiveDescriptor("*, " + AssemblyA, TagHelperDirectiveType.AddTagHelper),
|
||||
CreateTagHelperDirectiveDescriptor("th:", TagHelperDirectiveType.TagHelperPrefix)
|
||||
},
|
||||
new []
|
||||
{
|
||||
CreatePrefixedValidPlainDescriptor("th:"),
|
||||
CreatePrefixedValidInheritedDescriptor("th:")
|
||||
}
|
||||
"th:"
|
||||
},
|
||||
{
|
||||
AllTagHelpers,
|
||||
new []
|
||||
{
|
||||
CreateTagHelperDirectiveDescriptor("th-", TagHelperDirectiveType.TagHelperPrefix),
|
||||
|
|
@ -734,14 +710,9 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
"Microsoft.AspNetCore.Razor.TagHelpers.ValidInherited*, " + AssemblyA,
|
||||
TagHelperDirectiveType.AddTagHelper)
|
||||
},
|
||||
new []
|
||||
{
|
||||
CreatePrefixedValidPlainDescriptor("th-"),
|
||||
CreatePrefixedValidInheritedDescriptor("th-")
|
||||
}
|
||||
"th-"
|
||||
},
|
||||
{
|
||||
AllTagHelpers,
|
||||
new []
|
||||
{
|
||||
CreateTagHelperDirectiveDescriptor("", TagHelperDirectiveType.TagHelperPrefix),
|
||||
|
|
@ -752,10 +723,9 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
"Microsoft.AspNetCore.Razor.TagHelpers.ValidInherited*, " + AssemblyA,
|
||||
TagHelperDirectiveType.AddTagHelper)
|
||||
},
|
||||
new [] { Valid_PlainTagHelperDescriptor, Valid_InheritedTagHelperDescriptor }
|
||||
null
|
||||
},
|
||||
{
|
||||
AllTagHelpers,
|
||||
new []
|
||||
{
|
||||
CreateTagHelperDirectiveDescriptor("th", TagHelperDirectiveType.TagHelperPrefix),
|
||||
|
|
@ -766,15 +736,9 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
"*, " + AssemblyB,
|
||||
TagHelperDirectiveType.AddTagHelper),
|
||||
},
|
||||
new []
|
||||
{
|
||||
CreatePrefixedValidPlainDescriptor("th"),
|
||||
CreatePrefixedValidInheritedDescriptor("th"),
|
||||
CreatePrefixedStringDescriptor("th")
|
||||
}
|
||||
"th"
|
||||
},
|
||||
{
|
||||
AllTagHelpers,
|
||||
new []
|
||||
{
|
||||
CreateTagHelperDirectiveDescriptor(
|
||||
|
|
@ -785,44 +749,30 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
"*, " + AssemblyB,
|
||||
TagHelperDirectiveType.AddTagHelper),
|
||||
},
|
||||
new []
|
||||
{
|
||||
CreatePrefixedValidPlainDescriptor("th:-"),
|
||||
CreatePrefixedValidInheritedDescriptor("th:-"),
|
||||
CreatePrefixedStringDescriptor("th:-")
|
||||
}
|
||||
"th:-"
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(ProcessDirectives_TagHelperPrefixData))]
|
||||
public void ProcessDirectives_AppliesDirectives_WithTagHelperPrefix(
|
||||
object tagHelpers,
|
||||
[MemberData(nameof(ProcessTagHelperPrefixData))]
|
||||
public void ProcessTagHelperPrefix_ParsesPrefixFromDirectives_SetsOnCodeDocument(
|
||||
object directiveDescriptors,
|
||||
object expectedDescriptors)
|
||||
string expectedPrefix)
|
||||
{
|
||||
// Arrange
|
||||
var errorSink = new ErrorSink();
|
||||
var pass = new TagHelperBinderSyntaxTreePass();
|
||||
|
||||
var expected = (IEnumerable<TagHelperDescriptor>)expectedDescriptors;
|
||||
var document = RazorCodeDocument.Create(new DefaultRazorSourceDocument("Test content", encoding: Encoding.UTF8, fileName: "TestFile"));
|
||||
|
||||
// Act
|
||||
var results = pass.ProcessDirectives(
|
||||
((IEnumerable<TagHelperDirectiveDescriptor>)directiveDescriptors).ToArray(),
|
||||
((IEnumerable<TagHelperDescriptor>)tagHelpers).ToArray(),
|
||||
errorSink);
|
||||
var prefix = pass.ProcessTagHelperPrefix(((IEnumerable<TagHelperDirectiveDescriptor>)directiveDescriptors).ToList(), document, errorSink);
|
||||
|
||||
// Assert
|
||||
Assert.Empty(errorSink.Errors);
|
||||
Assert.Equal(expected.Count(), results.Count());
|
||||
|
||||
foreach (var expectedDescriptor in expected)
|
||||
{
|
||||
Assert.Contains(expectedDescriptor, results, TagHelperDescriptorComparer.Default);
|
||||
}
|
||||
Assert.Equal(expectedPrefix, prefix);
|
||||
Assert.Equal(expectedPrefix, document.GetTagHelperPrefix());
|
||||
}
|
||||
|
||||
public static TheoryData ProcessDirectivesData
|
||||
|
|
@ -874,7 +824,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
new []
|
||||
{
|
||||
CreateTagHelperDirectiveDescriptor(
|
||||
Valid_PlainTagHelperDescriptor.TypeName + ", " + AssemblyA,
|
||||
Valid_PlainTagHelperDescriptor.Name + ", " + AssemblyA,
|
||||
TagHelperDirectiveType.AddTagHelper),
|
||||
CreateTagHelperDirectiveDescriptor("*, " + AssemblyA, TagHelperDirectiveType.AddTagHelper)
|
||||
},
|
||||
|
|
@ -886,7 +836,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
{
|
||||
CreateTagHelperDirectiveDescriptor("*, " + AssemblyA, TagHelperDirectiveType.AddTagHelper),
|
||||
CreateTagHelperDirectiveDescriptor(
|
||||
Valid_PlainTagHelperDescriptor.TypeName + ", " + AssemblyA,
|
||||
Valid_PlainTagHelperDescriptor.Name + ", " + AssemblyA,
|
||||
TagHelperDirectiveType.RemoveTagHelper)
|
||||
},
|
||||
new [] { Valid_InheritedTagHelperDescriptor }
|
||||
|
|
@ -897,7 +847,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
{
|
||||
CreateTagHelperDirectiveDescriptor("*, " + AssemblyA, TagHelperDirectiveType.AddTagHelper),
|
||||
CreateTagHelperDirectiveDescriptor(
|
||||
Valid_PlainTagHelperDescriptor.TypeName + ", " + AssemblyA,
|
||||
Valid_PlainTagHelperDescriptor.Name + ", " + AssemblyA,
|
||||
TagHelperDirectiveType.RemoveTagHelper),
|
||||
CreateTagHelperDirectiveDescriptor("*, " + AssemblyA, TagHelperDirectiveType.AddTagHelper)
|
||||
},
|
||||
|
|
@ -967,7 +917,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
new []
|
||||
{
|
||||
CreateTagHelperDirectiveDescriptor("*, " + AssemblyA, TagHelperDirectiveType.AddTagHelper),
|
||||
CreateTagHelperDirectiveDescriptor("System." + String_TagHelperDescriptor.TypeName + ", " + AssemblyB, TagHelperDirectiveType.AddTagHelper)
|
||||
CreateTagHelperDirectiveDescriptor("System." + String_TagHelperDescriptor.Name + ", " + AssemblyB, TagHelperDirectiveType.AddTagHelper)
|
||||
},
|
||||
new [] { Valid_PlainTagHelperDescriptor }
|
||||
},
|
||||
|
|
@ -992,7 +942,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
CreateTagHelperDirectiveDescriptor(
|
||||
"?Microsoft*, " + AssemblyA, TagHelperDirectiveType.RemoveTagHelper),
|
||||
CreateTagHelperDirectiveDescriptor(
|
||||
"System." + String_TagHelperDescriptor.TypeName + ", " + AssemblyB, TagHelperDirectiveType.RemoveTagHelper)
|
||||
"System." + String_TagHelperDescriptor.Name + ", " + AssemblyB, TagHelperDirectiveType.RemoveTagHelper)
|
||||
},
|
||||
new []
|
||||
{
|
||||
|
|
@ -1012,7 +962,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
CreateTagHelperDirectiveDescriptor(
|
||||
"TagHelper*, " + AssemblyA, TagHelperDirectiveType.RemoveTagHelper),
|
||||
CreateTagHelperDirectiveDescriptor(
|
||||
"System." + String_TagHelperDescriptor.TypeName + ", " + AssemblyB, TagHelperDirectiveType.RemoveTagHelper)
|
||||
"System." + String_TagHelperDescriptor.Name + ", " + AssemblyB, TagHelperDirectiveType.RemoveTagHelper)
|
||||
},
|
||||
new []
|
||||
{
|
||||
|
|
@ -1081,8 +1031,8 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
new []
|
||||
{
|
||||
CreateTagHelperDirectiveDescriptor("*, " + AssemblyA, TagHelperDirectiveType.AddTagHelper),
|
||||
CreateTagHelperDirectiveDescriptor(Valid_PlainTagHelperDescriptor.TypeName + ", " + AssemblyA, TagHelperDirectiveType.RemoveTagHelper),
|
||||
CreateTagHelperDirectiveDescriptor(Valid_InheritedTagHelperDescriptor.TypeName + ", " + AssemblyA, TagHelperDirectiveType.RemoveTagHelper),
|
||||
CreateTagHelperDirectiveDescriptor(Valid_PlainTagHelperDescriptor.Name + ", " + AssemblyA, TagHelperDirectiveType.RemoveTagHelper),
|
||||
CreateTagHelperDirectiveDescriptor(Valid_InheritedTagHelperDescriptor.Name + ", " + AssemblyA, TagHelperDirectiveType.RemoveTagHelper),
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -1111,9 +1061,9 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
{
|
||||
CreateTagHelperDirectiveDescriptor("*, " + AssemblyA, TagHelperDirectiveType.AddTagHelper),
|
||||
CreateTagHelperDirectiveDescriptor("*, " + AssemblyB, TagHelperDirectiveType.AddTagHelper),
|
||||
CreateTagHelperDirectiveDescriptor(Valid_PlainTagHelperDescriptor.TypeName + ", " + AssemblyA, TagHelperDirectiveType.RemoveTagHelper),
|
||||
CreateTagHelperDirectiveDescriptor(Valid_InheritedTagHelperDescriptor.TypeName + ", " + AssemblyA, TagHelperDirectiveType.RemoveTagHelper),
|
||||
CreateTagHelperDirectiveDescriptor(String_TagHelperDescriptor.TypeName + ", " + AssemblyB, TagHelperDirectiveType.RemoveTagHelper)
|
||||
CreateTagHelperDirectiveDescriptor(Valid_PlainTagHelperDescriptor.Name + ", " + AssemblyA, TagHelperDirectiveType.RemoveTagHelper),
|
||||
CreateTagHelperDirectiveDescriptor(Valid_InheritedTagHelperDescriptor.Name + ", " + AssemblyA, TagHelperDirectiveType.RemoveTagHelper),
|
||||
CreateTagHelperDirectiveDescriptor(String_TagHelperDescriptor.Name + ", " + AssemblyB, TagHelperDirectiveType.RemoveTagHelper)
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -1121,7 +1071,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
new []
|
||||
{
|
||||
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 []
|
||||
{
|
||||
CreateTagHelperDirectiveDescriptor("Mic*, " + AssemblyA, TagHelperDirectiveType.AddTagHelper),
|
||||
CreateTagHelperDirectiveDescriptor(Valid_PlainTagHelperDescriptor.TypeName + ", " + AssemblyA, TagHelperDirectiveType.RemoveTagHelper),
|
||||
CreateTagHelperDirectiveDescriptor(Valid_InheritedTagHelperDescriptor.TypeName + ", " + AssemblyA, TagHelperDirectiveType.RemoveTagHelper)
|
||||
CreateTagHelperDirectiveDescriptor(Valid_PlainTagHelperDescriptor.Name + ", " + AssemblyA, TagHelperDirectiveType.RemoveTagHelper),
|
||||
CreateTagHelperDirectiveDescriptor(Valid_InheritedTagHelperDescriptor.Name + ", " + AssemblyA, TagHelperDirectiveType.RemoveTagHelper)
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -1220,7 +1170,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
get
|
||||
{
|
||||
var assemblyName = Valid_PlainTagHelperDescriptor.AssemblyName;
|
||||
var typeName = Valid_PlainTagHelperDescriptor.TypeName;
|
||||
var typeName = Valid_PlainTagHelperDescriptor.Name;
|
||||
return new TheoryData<string>
|
||||
{
|
||||
$"{typeName},{assemblyName}",
|
||||
|
|
@ -1246,12 +1196,12 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
|
||||
var directives = new[]
|
||||
{
|
||||
new TagHelperDirectiveDescriptor()
|
||||
{
|
||||
DirectiveText = directiveText,
|
||||
DirectiveType = TagHelperDirectiveType.AddTagHelper,
|
||||
}
|
||||
};
|
||||
new TagHelperDirectiveDescriptor()
|
||||
{
|
||||
DirectiveText = directiveText,
|
||||
DirectiveType = TagHelperDirectiveType.AddTagHelper,
|
||||
}
|
||||
};
|
||||
|
||||
// Act
|
||||
var results = pass.ProcessDirectives(
|
||||
|
|
@ -1311,26 +1261,29 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
|
||||
private static TagHelperDescriptor CreatePrefixedValidPlainDescriptor(string prefix)
|
||||
{
|
||||
return new TagHelperDescriptor(Valid_PlainTagHelperDescriptor)
|
||||
{
|
||||
Prefix = prefix,
|
||||
};
|
||||
return Valid_PlainTagHelperDescriptor;
|
||||
//return new TagHelperDescriptor(Valid_PlainTagHelperDescriptor)
|
||||
//{
|
||||
// Prefix = prefix,
|
||||
//};
|
||||
}
|
||||
|
||||
private static TagHelperDescriptor CreatePrefixedValidInheritedDescriptor(string prefix)
|
||||
{
|
||||
return new TagHelperDescriptor(Valid_InheritedTagHelperDescriptor)
|
||||
{
|
||||
Prefix = prefix,
|
||||
};
|
||||
return Valid_InheritedTagHelperDescriptor;
|
||||
//return new TagHelperDescriptor(Valid_InheritedTagHelperDescriptor)
|
||||
//{
|
||||
// Prefix = prefix,
|
||||
//};
|
||||
}
|
||||
|
||||
private static TagHelperDescriptor CreatePrefixedStringDescriptor(string prefix)
|
||||
{
|
||||
return new TagHelperDescriptor(String_TagHelperDescriptor)
|
||||
{
|
||||
Prefix = prefix,
|
||||
};
|
||||
return String_TagHelperDescriptor;
|
||||
//return new TagHelperDescriptor(String_TagHelperDescriptor)
|
||||
//{
|
||||
// Prefix = prefix,
|
||||
//};
|
||||
}
|
||||
|
||||
private static TagHelperDirectiveDescriptor CreateTagHelperDirectiveDescriptor(
|
||||
|
|
@ -1357,20 +1310,62 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
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 readonly RazorDiagnostic _error;
|
||||
private readonly string _tagName;
|
||||
|
||||
public ErrorLoggingTagHelperDescriptorResolver(RazorDiagnostic error)
|
||||
public ErrorLoggingTagHelperDescriptorResolver(RazorDiagnostic error, string tagName = null)
|
||||
{
|
||||
_error = error;
|
||||
_tagName = tagName;
|
||||
}
|
||||
|
||||
public IEnumerable<TagHelperDescriptor> Resolve(IList<RazorDiagnostic> errors)
|
||||
{
|
||||
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
|
||||
{
|
||||
// requiredAttributeDescriptor, attributeName, attributeValue, expectedResult
|
||||
return new TheoryData<TagHelperRequiredAttributeDescriptor, string, string, bool>
|
||||
return new TheoryData<RequiredAttributeDescriptor, string, string, bool>
|
||||
{
|
||||
{
|
||||
new TagHelperRequiredAttributeDescriptor
|
||||
{
|
||||
Name = "key"
|
||||
},
|
||||
RequiredAttributeDescriptorBuilder.Create().Name("key").Build(),
|
||||
"KeY",
|
||||
"value",
|
||||
true
|
||||
},
|
||||
{
|
||||
new TagHelperRequiredAttributeDescriptor
|
||||
{
|
||||
Name = "key"
|
||||
},
|
||||
RequiredAttributeDescriptorBuilder.Create().Name("key").Build(),
|
||||
"keys",
|
||||
"value",
|
||||
false
|
||||
},
|
||||
{
|
||||
new TagHelperRequiredAttributeDescriptor
|
||||
{
|
||||
Name = "route-",
|
||||
NameComparison = TagHelperRequiredAttributeNameComparison.PrefixMatch,
|
||||
},
|
||||
RequiredAttributeDescriptorBuilder.Create()
|
||||
.Name("route-")
|
||||
.NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.PrefixMatch)
|
||||
.Build(),
|
||||
"ROUTE-area",
|
||||
"manage",
|
||||
true
|
||||
},
|
||||
{
|
||||
new TagHelperRequiredAttributeDescriptor
|
||||
{
|
||||
Name = "route-",
|
||||
NameComparison = TagHelperRequiredAttributeNameComparison.PrefixMatch,
|
||||
},
|
||||
RequiredAttributeDescriptorBuilder.Create()
|
||||
.Name("route-")
|
||||
.NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.PrefixMatch)
|
||||
.Build(),
|
||||
"routearea",
|
||||
"manage",
|
||||
false
|
||||
},
|
||||
{
|
||||
new TagHelperRequiredAttributeDescriptor
|
||||
{
|
||||
Name = "route-",
|
||||
NameComparison = TagHelperRequiredAttributeNameComparison.PrefixMatch,
|
||||
},
|
||||
RequiredAttributeDescriptorBuilder.Create()
|
||||
.Name("route-")
|
||||
.NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.PrefixMatch)
|
||||
.Build(),
|
||||
"route-",
|
||||
"manage",
|
||||
false
|
||||
},
|
||||
{
|
||||
new TagHelperRequiredAttributeDescriptor
|
||||
{
|
||||
Name = "key",
|
||||
NameComparison = TagHelperRequiredAttributeNameComparison.FullMatch,
|
||||
},
|
||||
RequiredAttributeDescriptorBuilder.Create()
|
||||
.Name("key")
|
||||
.NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.FullMatch)
|
||||
.Build(),
|
||||
"KeY",
|
||||
"value",
|
||||
true
|
||||
},
|
||||
{
|
||||
new TagHelperRequiredAttributeDescriptor
|
||||
{
|
||||
Name = "key",
|
||||
NameComparison = TagHelperRequiredAttributeNameComparison.FullMatch,
|
||||
},
|
||||
RequiredAttributeDescriptorBuilder.Create()
|
||||
.Name("key")
|
||||
.NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.FullMatch)
|
||||
.Build(),
|
||||
"keys",
|
||||
"value",
|
||||
false
|
||||
},
|
||||
{
|
||||
new TagHelperRequiredAttributeDescriptor
|
||||
{
|
||||
Name = "key",
|
||||
NameComparison = TagHelperRequiredAttributeNameComparison.FullMatch,
|
||||
Value = "value",
|
||||
ValueComparison = TagHelperRequiredAttributeValueComparison.FullMatch,
|
||||
},
|
||||
RequiredAttributeDescriptorBuilder.Create()
|
||||
.Name("key")
|
||||
.NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.FullMatch)
|
||||
.Value("value")
|
||||
.ValueComparisonMode(RequiredAttributeDescriptor.ValueComparisonMode.FullMatch)
|
||||
.Build(),
|
||||
"key",
|
||||
"value",
|
||||
true
|
||||
},
|
||||
{
|
||||
new TagHelperRequiredAttributeDescriptor
|
||||
{
|
||||
Name = "key",
|
||||
NameComparison = TagHelperRequiredAttributeNameComparison.FullMatch,
|
||||
Value = "value",
|
||||
ValueComparison = TagHelperRequiredAttributeValueComparison.FullMatch,
|
||||
},
|
||||
RequiredAttributeDescriptorBuilder.Create()
|
||||
.Name("key")
|
||||
.NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.FullMatch)
|
||||
.Value("value")
|
||||
.ValueComparisonMode(RequiredAttributeDescriptor.ValueComparisonMode.FullMatch)
|
||||
.Build(),
|
||||
"key",
|
||||
"Value",
|
||||
false
|
||||
},
|
||||
{
|
||||
new TagHelperRequiredAttributeDescriptor
|
||||
{
|
||||
Name = "class",
|
||||
NameComparison = TagHelperRequiredAttributeNameComparison.FullMatch,
|
||||
Value = "btn",
|
||||
ValueComparison = TagHelperRequiredAttributeValueComparison.PrefixMatch,
|
||||
},
|
||||
RequiredAttributeDescriptorBuilder.Create()
|
||||
.Name("class")
|
||||
.NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.FullMatch)
|
||||
.Value("btn")
|
||||
.ValueComparisonMode(RequiredAttributeDescriptor.ValueComparisonMode.PrefixMatch)
|
||||
.Build(),
|
||||
"class",
|
||||
"btn btn-success",
|
||||
true
|
||||
},
|
||||
{
|
||||
new TagHelperRequiredAttributeDescriptor
|
||||
{
|
||||
Name = "class",
|
||||
NameComparison = TagHelperRequiredAttributeNameComparison.FullMatch,
|
||||
Value = "btn",
|
||||
ValueComparison = TagHelperRequiredAttributeValueComparison.PrefixMatch,
|
||||
},
|
||||
RequiredAttributeDescriptorBuilder.Create()
|
||||
.Name("class")
|
||||
.NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.FullMatch)
|
||||
.Value("btn")
|
||||
.ValueComparisonMode(RequiredAttributeDescriptor.ValueComparisonMode.PrefixMatch)
|
||||
.Build(),
|
||||
"class",
|
||||
"BTN btn-success",
|
||||
false
|
||||
},
|
||||
{
|
||||
new TagHelperRequiredAttributeDescriptor
|
||||
{
|
||||
Name = "href",
|
||||
NameComparison = TagHelperRequiredAttributeNameComparison.FullMatch,
|
||||
Value = "#navigate",
|
||||
ValueComparison = TagHelperRequiredAttributeValueComparison.SuffixMatch,
|
||||
},
|
||||
RequiredAttributeDescriptorBuilder.Create()
|
||||
.Name("href")
|
||||
.NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.FullMatch)
|
||||
.Value("#navigate")
|
||||
.ValueComparisonMode(RequiredAttributeDescriptor.ValueComparisonMode.SuffixMatch)
|
||||
.Build(),
|
||||
"href",
|
||||
"/home/index#navigate",
|
||||
true
|
||||
},
|
||||
{
|
||||
new TagHelperRequiredAttributeDescriptor
|
||||
{
|
||||
Name = "href",
|
||||
NameComparison = TagHelperRequiredAttributeNameComparison.FullMatch,
|
||||
Value = "#navigate",
|
||||
ValueComparison = TagHelperRequiredAttributeValueComparison.SuffixMatch,
|
||||
},
|
||||
RequiredAttributeDescriptorBuilder.Create()
|
||||
.Name("href")
|
||||
.NameComparisonMode(RequiredAttributeDescriptor.NameComparisonMode.FullMatch)
|
||||
.Value("#navigate")
|
||||
.ValueComparisonMode(RequiredAttributeDescriptor.ValueComparisonMode.SuffixMatch)
|
||||
.Build(),
|
||||
"href",
|
||||
"/home/index#NAVigate",
|
||||
false
|
||||
|
|
@ -164,7 +147,7 @@ namespace Microsoft.AspNetCore.Razor.Evolution
|
|||
bool expectedResult)
|
||||
{
|
||||
// Act
|
||||
var result = ((TagHelperRequiredAttributeDescriptor)requiredAttributeDescriptor).IsMatch(attributeName, attributeValue);
|
||||
var result = ((RequiredAttributeDescriptor)requiredAttributeDescriptor).IsMatch(attributeName, attributeValue);
|
||||
|
||||
// Assert
|
||||
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("p")]
|
||||
[OutputElementHint("div")]
|
||||
public class MulitpleDescriptorTagHelperWithOutputElementHint : TagHelper
|
||||
public class MultipleDescriptorTagHelperWithOutputElementHint : TagHelper
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,11 +1,8 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Razor.TagHelpers;
|
||||
using Microsoft.CodeAnalysis.Razor.Workspaces.Test;
|
||||
using System.Collections.Generic;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.Workspaces
|
||||
|
|
|
|||
|
|
@ -1,14 +1,13 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.Extensions.DependencyModel;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.Extensions.DependencyModel;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor
|
||||
|
|
|
|||
|
|
@ -1,12 +1,11 @@
|
|||
// 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;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNetCore.Razor.Evolution;
|
||||
using Microsoft.CodeAnalysis.Razor.Workspaces.Test;
|
||||
using Microsoft.CodeAnalysis.Razor.Workspaces.Test.Comparers;
|
||||
using Xunit;
|
||||
using Microsoft.AspNetCore.Razor.Evolution.Legacy;
|
||||
|
||||
namespace Microsoft.CodeAnalysis.Razor.Workspaces
|
||||
{
|
||||
|
|
@ -19,45 +18,32 @@ namespace Microsoft.CodeAnalysis.Razor.Workspaces
|
|||
var testCompilation = TestCompilation.Create();
|
||||
var viewComponent = testCompilation.GetTypeByMetadataName(typeof(StringParameterViewComponent).FullName);
|
||||
var factory = new ViewComponentTagHelperDescriptorFactory(testCompilation);
|
||||
var expectedDescriptor = new TagHelperDescriptor
|
||||
{
|
||||
TagName = "vc:string-parameter",
|
||||
TypeName = "__Generated__StringParameterViewComponentTagHelper",
|
||||
AssemblyName = typeof(StringParameterViewComponent).GetTypeInfo().Assembly.GetName().Name,
|
||||
Attributes = new List<TagHelperAttributeDescriptor>
|
||||
{
|
||||
new TagHelperAttributeDescriptor
|
||||
{
|
||||
Name = "foo",
|
||||
PropertyName = "foo",
|
||||
TypeName = typeof(string).FullName
|
||||
},
|
||||
new TagHelperAttributeDescriptor
|
||||
{
|
||||
Name = "bar",
|
||||
PropertyName = "bar",
|
||||
TypeName = typeof(string).FullName
|
||||
}
|
||||
},
|
||||
RequiredAttributes = new List<TagHelperRequiredAttributeDescriptor>
|
||||
{
|
||||
new TagHelperRequiredAttributeDescriptor
|
||||
{
|
||||
Name = "foo"
|
||||
},
|
||||
new TagHelperRequiredAttributeDescriptor
|
||||
{
|
||||
Name = "bar"
|
||||
}
|
||||
}
|
||||
};
|
||||
expectedDescriptor.PropertyBag.Add(ViewComponentTypes.ViewComponentNameKey, "StringParameter");
|
||||
var expectedDescriptor = ITagHelperDescriptorBuilder.Create(
|
||||
"__Generated__StringParameterViewComponentTagHelper",
|
||||
typeof(StringParameterViewComponent).GetTypeInfo().Assembly.GetName().Name)
|
||||
.TagMatchingRule(rule =>
|
||||
rule
|
||||
.RequireTagName("vc:string-parameter")
|
||||
.RequireAttribute(attribute => attribute.Name("foo"))
|
||||
.RequireAttribute(attribute => attribute.Name("bar")))
|
||||
.BindAttribute(attribute =>
|
||||
attribute
|
||||
.Name("foo")
|
||||
.PropertyName("foo")
|
||||
.TypeName(typeof(string).FullName))
|
||||
.BindAttribute(attribute =>
|
||||
attribute
|
||||
.Name("bar")
|
||||
.PropertyName("bar")
|
||||
.TypeName(typeof(string).FullName))
|
||||
.AddMetadata(ViewComponentTypes.ViewComponentNameKey, "StringParameter")
|
||||
.Build();
|
||||
|
||||
// Act
|
||||
var descriptor = factory.CreateDescriptor(viewComponent);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedDescriptor, descriptor, CaseSensitiveTagHelperDescriptorComparer.Default);
|
||||
Assert.Equal(expectedDescriptor, descriptor, TagHelperDescriptorComparer.CaseSensitive);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -67,60 +53,39 @@ namespace Microsoft.CodeAnalysis.Razor.Workspaces
|
|||
var testCompilation = TestCompilation.Create();
|
||||
var viewComponent = testCompilation.GetTypeByMetadataName(typeof(VariousParameterViewComponent).FullName);
|
||||
var factory = new ViewComponentTagHelperDescriptorFactory(testCompilation);
|
||||
var expectedDescriptor = new TagHelperDescriptor
|
||||
{
|
||||
TagName = "vc:various-parameter",
|
||||
TypeName = "__Generated__VariousParameterViewComponentTagHelper",
|
||||
AssemblyName = typeof(VariousParameterViewComponent).GetTypeInfo().Assembly.GetName().Name,
|
||||
Attributes = new List<TagHelperAttributeDescriptor>
|
||||
{
|
||||
new TagHelperAttributeDescriptor
|
||||
{
|
||||
Name = "test-enum",
|
||||
PropertyName = "testEnum",
|
||||
TypeName = typeof(VariousParameterViewComponent).FullName + "." + nameof(VariousParameterViewComponent.TestEnum),
|
||||
IsEnum = true
|
||||
},
|
||||
|
||||
new TagHelperAttributeDescriptor
|
||||
{
|
||||
Name = "test-string",
|
||||
PropertyName = "testString",
|
||||
TypeName = typeof(string).FullName
|
||||
},
|
||||
|
||||
new TagHelperAttributeDescriptor
|
||||
{
|
||||
Name = "baz",
|
||||
PropertyName = "baz",
|
||||
TypeName = typeof(int).FullName
|
||||
}
|
||||
},
|
||||
RequiredAttributes = new List<TagHelperRequiredAttributeDescriptor>
|
||||
{
|
||||
new TagHelperRequiredAttributeDescriptor
|
||||
{
|
||||
Name = "test-enum"
|
||||
},
|
||||
|
||||
new TagHelperRequiredAttributeDescriptor
|
||||
{
|
||||
Name = "test-string"
|
||||
},
|
||||
|
||||
new TagHelperRequiredAttributeDescriptor
|
||||
{
|
||||
Name = "baz"
|
||||
}
|
||||
}
|
||||
};
|
||||
expectedDescriptor.PropertyBag.Add(ViewComponentTypes.ViewComponentNameKey, "VariousParameter");
|
||||
var expectedDescriptor = ITagHelperDescriptorBuilder.Create(
|
||||
"__Generated__VariousParameterViewComponentTagHelper",
|
||||
typeof(VariousParameterViewComponent).GetTypeInfo().Assembly.GetName().Name)
|
||||
.TagMatchingRule(rule =>
|
||||
rule
|
||||
.RequireTagName("vc:various-parameter")
|
||||
.RequireAttribute(attribute => attribute.Name("test-enum"))
|
||||
.RequireAttribute(attribute => attribute.Name("test-string"))
|
||||
.RequireAttribute(attribute => attribute.Name("baz")))
|
||||
.BindAttribute(attribute =>
|
||||
attribute
|
||||
.Name("test-enum")
|
||||
.PropertyName("testEnum")
|
||||
.TypeName(typeof(VariousParameterViewComponent).FullName + "." + nameof(VariousParameterViewComponent.TestEnum))
|
||||
.AsEnum())
|
||||
.BindAttribute(attribute =>
|
||||
attribute
|
||||
.Name("test-string")
|
||||
.PropertyName("testString")
|
||||
.TypeName(typeof(string).FullName))
|
||||
.BindAttribute(attribute =>
|
||||
attribute
|
||||
.Name("baz")
|
||||
.PropertyName("baz")
|
||||
.TypeName(typeof(int).FullName))
|
||||
.AddMetadata(ViewComponentTypes.ViewComponentNameKey, "VariousParameter")
|
||||
.Build();
|
||||
|
||||
// Act
|
||||
var descriptor = factory.CreateDescriptor(viewComponent);
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedDescriptor, descriptor, CaseSensitiveTagHelperDescriptorComparer.Default);
|
||||
Assert.Equal(expectedDescriptor, descriptor, TagHelperDescriptorComparer.CaseSensitive);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -130,50 +95,32 @@ namespace Microsoft.CodeAnalysis.Razor.Workspaces
|
|||
var testCompilation = TestCompilation.Create();
|
||||
var viewComponent = testCompilation.GetTypeByMetadataName(typeof(GenericParameterViewComponent).FullName);
|
||||
var factory = new ViewComponentTagHelperDescriptorFactory(testCompilation);
|
||||
var expectedDescriptor = new TagHelperDescriptor
|
||||
{
|
||||
TagName = "vc:generic-parameter",
|
||||
TypeName = "__Generated__GenericParameterViewComponentTagHelper",
|
||||
AssemblyName = typeof(GenericParameterViewComponent).GetTypeInfo().Assembly.GetName().Name,
|
||||
Attributes = new List<TagHelperAttributeDescriptor>
|
||||
{
|
||||
new TagHelperAttributeDescriptor
|
||||
{
|
||||
Name = "foo",
|
||||
PropertyName = "Foo",
|
||||
TypeName = "System.Collections.Generic.List<System.String>"
|
||||
},
|
||||
|
||||
new TagHelperAttributeDescriptor
|
||||
{
|
||||
Name = "bar",
|
||||
PropertyName = "Bar",
|
||||
TypeName = "System.Collections.Generic.Dictionary<System.String, System.Int32>"
|
||||
},
|
||||
|
||||
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");
|
||||
var expectedDescriptor = ITagHelperDescriptorBuilder.Create(
|
||||
"__Generated__GenericParameterViewComponentTagHelper",
|
||||
typeof(GenericParameterViewComponent).GetTypeInfo().Assembly.GetName().Name)
|
||||
.TagMatchingRule(rule =>
|
||||
rule
|
||||
.RequireTagName("vc:generic-parameter")
|
||||
.RequireAttribute(attribute => attribute.Name("foo")))
|
||||
.BindAttribute(attribute =>
|
||||
attribute
|
||||
.Name("foo")
|
||||
.PropertyName("Foo")
|
||||
.TypeName("System.Collections.Generic.List<System.String>"))
|
||||
.BindAttribute(attribute =>
|
||||
attribute
|
||||
.Name("bar")
|
||||
.PropertyName("Bar")
|
||||
.TypeName("System.Collections.Generic.Dictionary<System.String, System.Int32>")
|
||||
.AsDictionary("bar-", typeof(int).FullName))
|
||||
.AddMetadata(ViewComponentTypes.ViewComponentNameKey, "GenericParameter")
|
||||
.Build();
|
||||
|
||||
// Act
|
||||
var descriptor = factory.CreateDescriptor(viewComponent);
|
||||
|
||||
// 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.
|
||||
|
||||
#if RAZOR_EXTENSION_DEVELOPER_MODE
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Razor.Evolution;
|
||||
|
||||
namespace Microsoft.VisualStudio.RazorExtension.RazorInfo
|
||||
|
|
@ -17,9 +18,9 @@ namespace Microsoft.VisualStudio.RazorExtension.RazorInfo
|
|||
|
||||
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
|
||||
Loading…
Reference in New Issue