Remove old <c:Foo> syntax
This commit is contained in:
parent
d175b4d38a
commit
ffadeecb45
|
|
@ -11,19 +11,6 @@ namespace Microsoft.AspNetCore.Blazor.Razor
|
||||||
{
|
{
|
||||||
internal static class BlazorDiagnosticFactory
|
internal static class BlazorDiagnosticFactory
|
||||||
{
|
{
|
||||||
public static readonly RazorDiagnosticDescriptor InvalidComponentAttributeSyntax = new RazorDiagnosticDescriptor(
|
|
||||||
"BL9980",
|
|
||||||
() => "Wrong syntax for '{0}' on '{1}': As a temporary " +
|
|
||||||
$"limitation, component attributes must be expressed with C# syntax. For example, " +
|
|
||||||
$"SomeParam=@(\"Some value\") is allowed, but SomeParam=\"Some value\" is not.",
|
|
||||||
RazorDiagnosticSeverity.Error);
|
|
||||||
|
|
||||||
public static RazorDiagnostic Create_InvalidComponentAttributeSynx(TextPosition position, SourceSpan? span, string attributeName, string componentName)
|
|
||||||
{
|
|
||||||
span = CalculateSourcePosition(span, position);
|
|
||||||
return RazorDiagnostic.Create(InvalidComponentAttributeSyntax, span ?? SourceSpan.Undefined, attributeName, componentName);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static readonly RazorDiagnosticDescriptor UnexpectedClosingTag = new RazorDiagnosticDescriptor(
|
public static readonly RazorDiagnosticDescriptor UnexpectedClosingTag = new RazorDiagnosticDescriptor(
|
||||||
"BL9981",
|
"BL9981",
|
||||||
() => "Unexpected closing tag '{0}' with no matching start tag.",
|
() => "Unexpected closing tag '{0}' with no matching start tag.",
|
||||||
|
|
|
||||||
|
|
@ -219,38 +219,18 @@ namespace Microsoft.AspNetCore.Blazor.Razor
|
||||||
{
|
{
|
||||||
var nextTag = nextToken.AsTag();
|
var nextTag = nextToken.AsTag();
|
||||||
var tagNameOriginalCase = GetTagNameWithOriginalCase(originalHtmlContent, nextTag);
|
var tagNameOriginalCase = GetTagNameWithOriginalCase(originalHtmlContent, nextTag);
|
||||||
var isComponent = TryGetComponentTypeNameFromTagName(tagNameOriginalCase, out var componentTypeName);
|
|
||||||
|
|
||||||
if (nextToken.Type == HtmlTokenType.StartTag)
|
if (nextToken.Type == HtmlTokenType.StartTag)
|
||||||
{
|
{
|
||||||
_scopeStack.IncrementCurrentScopeChildCount(context);
|
_scopeStack.IncrementCurrentScopeChildCount(context);
|
||||||
if (isComponent)
|
|
||||||
{
|
|
||||||
codeWriter
|
|
||||||
.WriteStartMethodInvocation($"{_scopeStack.BuilderVarName}.{nameof(BlazorApi.RenderTreeBuilder.OpenComponent)}<{componentTypeName}>")
|
|
||||||
.Write((_sourceSequence++).ToString())
|
|
||||||
.WriteEndMethodInvocation();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
codeWriter
|
|
||||||
.WriteStartMethodInvocation($"{_scopeStack.BuilderVarName}.{nameof(BlazorApi.RenderTreeBuilder.OpenElement)}")
|
|
||||||
.Write((_sourceSequence++).ToString())
|
|
||||||
.WriteParameterSeparator()
|
|
||||||
.WriteStringLiteral(nextTag.Data)
|
|
||||||
.WriteEndMethodInvocation();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isComponent && nextTag.Attributes.Count > 0)
|
|
||||||
{
|
|
||||||
var diagnostic = BlazorDiagnosticFactory.Create_InvalidComponentAttributeSynx(
|
|
||||||
nextTag.Position,
|
|
||||||
node.Source,
|
|
||||||
nextTag.Attributes[0].Key,
|
|
||||||
tagNameOriginalCase);
|
|
||||||
throw new RazorCompilerException(diagnostic);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
codeWriter
|
||||||
|
.WriteStartMethodInvocation($"{_scopeStack.BuilderVarName}.{nameof(BlazorApi.RenderTreeBuilder.OpenElement)}")
|
||||||
|
.Write((_sourceSequence++).ToString())
|
||||||
|
.WriteParameterSeparator()
|
||||||
|
.WriteStringLiteral(nextTag.Data)
|
||||||
|
.WriteEndMethodInvocation();
|
||||||
|
|
||||||
foreach (var attribute in nextTag.Attributes)
|
foreach (var attribute in nextTag.Attributes)
|
||||||
{
|
{
|
||||||
WriteAttribute(codeWriter, attribute.Key, attribute.Value);
|
WriteAttribute(codeWriter, attribute.Key, attribute.Value);
|
||||||
|
|
@ -274,25 +254,20 @@ namespace Microsoft.AspNetCore.Blazor.Razor
|
||||||
_currentElementAttributeTokens.Clear();
|
_currentElementAttributeTokens.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
_scopeStack.OpenScope(
|
_scopeStack.OpenScope( tagName: nextTag.Data, isComponent: false);
|
||||||
tagName: isComponent ? tagNameOriginalCase : nextTag.Data,
|
|
||||||
isComponent: isComponent);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nextToken.Type == HtmlTokenType.EndTag
|
if (nextToken.Type == HtmlTokenType.EndTag
|
||||||
|| nextTag.IsSelfClosing
|
|| nextTag.IsSelfClosing
|
||||||
|| (!isComponent && htmlVoidElementsLookup.Contains(nextTag.Data)))
|
|| htmlVoidElementsLookup.Contains(nextTag.Data))
|
||||||
{
|
{
|
||||||
_scopeStack.CloseScope(
|
_scopeStack.CloseScope(
|
||||||
context: context,
|
context: context,
|
||||||
tagName: isComponent ? tagNameOriginalCase : nextTag.Data,
|
tagName: nextTag.Data,
|
||||||
isComponent: isComponent,
|
isComponent: false,
|
||||||
source: CalculateSourcePosition(node.Source, nextToken.Position));
|
source: CalculateSourcePosition(node.Source, nextToken.Position));
|
||||||
var closeMethodName = isComponent
|
|
||||||
? nameof(BlazorApi.RenderTreeBuilder.CloseComponent)
|
|
||||||
: nameof(BlazorApi.RenderTreeBuilder.CloseElement);
|
|
||||||
codeWriter
|
codeWriter
|
||||||
.WriteStartMethodInvocation($"{_scopeStack.BuilderVarName}.{closeMethodName}")
|
.WriteStartMethodInvocation($"{_scopeStack.BuilderVarName}.{BlazorApi.RenderTreeBuilder.CloseElement}")
|
||||||
.WriteEndMethodInvocation();
|
.WriteEndMethodInvocation();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
@ -559,32 +534,6 @@ namespace Microsoft.AspNetCore.Blazor.Razor
|
||||||
return document.Substring(tagToken.Position.Position + offset, tagToken.Name.Length);
|
return document.Substring(tagToken.Position.Position + offset, tagToken.Name.Length);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool TryGetComponentTypeNameFromTagName(string tagName, out string componentTypeName)
|
|
||||||
{
|
|
||||||
// Determine whether 'tagName' represents a Blazor component, and if so, return the
|
|
||||||
// name of the component's .NET type. The type name doesn't have to be fully-qualified,
|
|
||||||
// because it's up to the developer to put in whatever @using statements are required.
|
|
||||||
|
|
||||||
// TODO: Remove this temporary syntax and make the compiler smart enough to infer it
|
|
||||||
// directly. This could either work by having a configurable list of non-component tag names
|
|
||||||
// (which would default to all standard HTML elements, plus anything that contains a '-'
|
|
||||||
// character, since those are mandatory for custom HTML elements and prohibited for .NET
|
|
||||||
// type names), or better, could somehow know what .NET types are in scope at this point
|
|
||||||
// in the compilation and treat everything else as a non-component element.
|
|
||||||
|
|
||||||
const string temporaryPrefix = "c:";
|
|
||||||
if (tagName.StartsWith(temporaryPrefix, StringComparison.Ordinal))
|
|
||||||
{
|
|
||||||
componentTypeName = tagName.Substring(temporaryPrefix.Length);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
componentTypeName = null;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void WriteAttribute(CodeWriter codeWriter, string key, object value)
|
private void WriteAttribute(CodeWriter codeWriter, string key, object value)
|
||||||
{
|
{
|
||||||
BeginWriteAttribute(codeWriter, key);
|
BeginWriteAttribute(codeWriter, key);
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
<div class='container-fluid'>
|
<div class='container-fluid'>
|
||||||
<div class='row'>
|
<div class='row'>
|
||||||
<div class='col-sm-3'>
|
<div class='col-sm-3'>
|
||||||
<c:NavMenu />
|
<NavMenu />
|
||||||
</div>
|
</div>
|
||||||
<div class='col-sm-9'>
|
<div class='col-sm-9'>
|
||||||
@Body
|
@Body
|
||||||
|
|
|
||||||
|
|
@ -13,20 +13,19 @@
|
||||||
<div class='navbar-collapse collapse'>
|
<div class='navbar-collapse collapse'>
|
||||||
<ul class='nav navbar-nav'>
|
<ul class='nav navbar-nav'>
|
||||||
<li>
|
<li>
|
||||||
<!-- The 'c:' prefix and parens are workarounds for a Razor compilation issue and will be removed soon. -->
|
<NavLink href="/">
|
||||||
<c:NavLink href=@("/")>
|
|
||||||
<span class='glyphicon glyphicon-home'></span> Home
|
<span class='glyphicon glyphicon-home'></span> Home
|
||||||
</c:NavLink>
|
</NavLink>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<c:NavLink href=@("/counter")>
|
<NavLink href="/counter">
|
||||||
<span class='glyphicon glyphicon-education'></span> Counter
|
<span class='glyphicon glyphicon-education'></span> Counter
|
||||||
</c:NavLink>
|
</NavLink>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<c:NavLink href=@("/fetchdata")>
|
<NavLink href="/fetchdata">
|
||||||
<span class='glyphicon glyphicon-th-list'></span> Fetch data
|
<span class='glyphicon glyphicon-th-list'></span> Fetch data
|
||||||
</c:NavLink>
|
</NavLink>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
<div class='container-fluid'>
|
<div class='container-fluid'>
|
||||||
<div class='row'>
|
<div class='row'>
|
||||||
<div class='col-sm-3'>
|
<div class='col-sm-3'>
|
||||||
<c:NavMenu />
|
<NavMenu />
|
||||||
</div>
|
</div>
|
||||||
<div class='col-sm-9'>
|
<div class='col-sm-9'>
|
||||||
@Body
|
@Body
|
||||||
|
|
|
||||||
|
|
@ -13,20 +13,19 @@
|
||||||
<div class='navbar-collapse collapse'>
|
<div class='navbar-collapse collapse'>
|
||||||
<ul class='nav navbar-nav'>
|
<ul class='nav navbar-nav'>
|
||||||
<li>
|
<li>
|
||||||
<!-- The 'c:' prefix and parens are workarounds for a Razor compilation issue and will be removed soon. -->
|
<NavLink href="/">
|
||||||
<c:NavLink href=@("/")>
|
|
||||||
<span class='glyphicon glyphicon-home'></span> Home
|
<span class='glyphicon glyphicon-home'></span> Home
|
||||||
</c:NavLink>
|
</NavLink>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<c:NavLink href=@("/counter")>
|
<NavLink href="/counter">
|
||||||
<span class='glyphicon glyphicon-education'></span> Counter
|
<span class='glyphicon glyphicon-education'></span> Counter
|
||||||
</c:NavLink>
|
</NavLink>
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<c:NavLink href=@("/fetchdata")>
|
<NavLink href="/fetchdata">
|
||||||
<span class='glyphicon glyphicon-th-list'></span> Fetch data
|
<span class='glyphicon glyphicon-th-list'></span> Fetch data
|
||||||
</c:NavLink>
|
</NavLink>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -7,44 +7,6 @@ namespace Microsoft.AspNetCore.Blazor.Build.Test
|
||||||
{
|
{
|
||||||
public class DiagnosticRazorIntegrationTest : RazorIntegrationTestBase
|
public class DiagnosticRazorIntegrationTest : RazorIntegrationTestBase
|
||||||
{
|
{
|
||||||
[Fact]
|
|
||||||
public void TemporaryComponentSyntaxRejectsParametersExpressedAsPlainHtmlAttributes()
|
|
||||||
{
|
|
||||||
// This is a temporary syntax restriction. Currently you can write:
|
|
||||||
// <c:MyComponent MyParam=@("My value") />
|
|
||||||
// ... but are *not* allowed to write:
|
|
||||||
// <c:MyComponent MyParam="My value" />
|
|
||||||
// This is because until we get the improved taghelper-based tooling,
|
|
||||||
// we're using AngleSharp to parse the plain HTML attributes, and it
|
|
||||||
// suffers from limitations:
|
|
||||||
// * Loses the casing of attribute names (MyParam becomes myparam)
|
|
||||||
// * Doesn't recognize MyBool=true as an bool (becomes mybool="true"),
|
|
||||||
// plus equivalent for other primitives like enum values
|
|
||||||
// So to avoid people getting runtime errors, we're currently imposing
|
|
||||||
// the compile-time restriction that component params have to be given
|
|
||||||
// as C# expressions, e.g., MyBool=@true and MyString=@("Hello")
|
|
||||||
|
|
||||||
// Arrange/Act
|
|
||||||
var result = CompileToCSharp(
|
|
||||||
$"Line 1\n" +
|
|
||||||
$"Some text <c:MyComponent MyParam=\"My value\" />");
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
Assert.Collection(
|
|
||||||
result.Diagnostics,
|
|
||||||
item =>
|
|
||||||
{
|
|
||||||
Assert.Equal("BL9980", item.Id);
|
|
||||||
Assert.Equal(
|
|
||||||
$"Wrong syntax for 'myparam' on 'c:MyComponent': As a temporary " +
|
|
||||||
$"limitation, component attributes must be expressed with C# syntax. For " +
|
|
||||||
$"example, SomeParam=@(\"Some value\") is allowed, but SomeParam=\"Some value\" " +
|
|
||||||
$"is not.", item.GetMessage());
|
|
||||||
Assert.Equal(1, item.Span.LineIndex);
|
|
||||||
Assert.Equal(10, item.Span.CharacterIndex);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void RejectsEndTagWithNoStartTag()
|
public void RejectsEndTagWithNoStartTag()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue