Implement Components with new HTML parser
This change ports the existing Blazor component compiler features to
work on top of the Razor compiler. These new features are on once Razor
Language version is set to V3 or higher.
The tests and code for the old Blazor compiler were added to this repo
in previous changes. This change adds the necessary enabling features,
and gets the tests working.
Code generation for tests tests is identical with a few exceptions.
Design time directives now include line mappings (as they should). The
infrastructure seems to prefer windows style line-endings whereas the
Blazor code did the opposite. I plan to leave this alone unless it's a
problem for testing on *nix.
IR Generation will be cosmetically different due to different node names
and formatting. Structure should generally be the same.
----
In order to implement this support, I had to add the following core
features.
Configuring passes/engine-features based on the langauge version. This
required revisiting a *lot* of old tests and forcing them to use a
version appropriate Razor engine. A lot of our tests for legacy things
were using new versions of the language :(.
Configuring passes/engine-features to switch on/off depending on the
document kind.
Configuring directives depending on the file-kind. Directives are a
parser feature, so this decision needs to be made before the IR phase.
Directives that are specific to a file kind will override directives
that are not - this allows components to customize templates for
instance.
Adding functions/inherits/implements directive as mainline features that
are always on in V3. I'm not quite motivated to do this yet for
typeparam, but we might end up there. Remember that directives are
reserved words in Razor.
\n\nCommit migrated from a04addb86f
This commit is contained in:
parent
45ad1c0ae8
commit
0c1c05efc6
|
|
@ -21,6 +21,12 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X
|
|||
throw new ArgumentNullException(nameof(projectItem));
|
||||
}
|
||||
|
||||
// Don't add MVC imports for a component - this shouldn't happen for v1, but just in case.
|
||||
if (string.Equals(projectItem.FileKind, FileKinds.Component, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return Array.Empty<RazorProjectItem>();
|
||||
}
|
||||
|
||||
var imports = new List<RazorProjectItem>();
|
||||
AddDefaultDirectivesImport(imports);
|
||||
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X
|
|||
builder.Features.Add(new ModelExpressionPass());
|
||||
builder.Features.Add(new MvcViewDocumentClassifierPass());
|
||||
|
||||
builder.SetImportFeature(new MvcImportProjectFeature());
|
||||
builder.Features.Add(new MvcImportProjectFeature());
|
||||
}
|
||||
|
||||
public static void RegisterViewComponentTagHelpers(RazorProjectEngineBuilder builder)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
// 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.Text;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.AspNetCore.Razor.Language.Intermediate;
|
||||
|
|
@ -173,7 +174,8 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X
|
|||
|
||||
private RazorEngine CreateEngine()
|
||||
{
|
||||
return RazorProjectEngine.Create(b =>
|
||||
var configuration = RazorConfiguration.Create(RazorLanguageVersion.Version_1_1, "test", Array.Empty<RazorExtension>());
|
||||
return RazorProjectEngine.Create(configuration, RazorProjectFileSystem.Empty, b =>
|
||||
{
|
||||
// Notice we're not registering the InjectDirective.Pass here so we can run it on demand.
|
||||
b.AddDirective(InjectDirective.Directive);
|
||||
|
|
|
|||
|
|
@ -11,8 +11,10 @@ using Xunit;
|
|||
|
||||
namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X
|
||||
{
|
||||
public class ModelDirectiveTest
|
||||
public class ModelDirectiveTest : RazorProjectEngineTestBase
|
||||
{
|
||||
protected override RazorLanguageVersion Version => RazorLanguageVersion.Version_1_1;
|
||||
|
||||
[Fact]
|
||||
public void ModelDirective_GetModelType_GetsTypeFromFirstWellFormedDirective()
|
||||
{
|
||||
|
|
@ -23,7 +25,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X
|
|||
@model
|
||||
");
|
||||
|
||||
var engine = CreateEngine();
|
||||
var engine = CreateRuntimeEngine();
|
||||
|
||||
var irDocument = CreateIRDocument(engine, codeDocument);
|
||||
|
||||
|
|
@ -40,7 +42,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X
|
|||
// Arrange
|
||||
var codeDocument = CreateDocument(@" ");
|
||||
|
||||
var engine = CreateEngine();
|
||||
var engine = CreateRuntimeEngine();
|
||||
|
||||
var irDocument = CreateIRDocument(engine, codeDocument);
|
||||
|
||||
|
|
@ -60,7 +62,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X
|
|||
@model Type1
|
||||
");
|
||||
|
||||
var engine = CreateEngine();
|
||||
var engine = CreateRuntimeEngine();
|
||||
var pass = new ModelDirective.Pass()
|
||||
{
|
||||
Engine = engine,
|
||||
|
|
@ -87,7 +89,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X
|
|||
@model Type2
|
||||
");
|
||||
|
||||
var engine = CreateEngine();
|
||||
var engine = CreateRuntimeEngine();
|
||||
var pass = new ModelDirective.Pass()
|
||||
{
|
||||
Engine = engine,
|
||||
|
|
@ -113,7 +115,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X
|
|||
@model Type1
|
||||
");
|
||||
|
||||
var engine = CreateEngine();
|
||||
var engine = CreateRuntimeEngine();
|
||||
var pass = new ModelDirective.Pass()
|
||||
{
|
||||
Engine = engine,
|
||||
|
|
@ -138,7 +140,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X
|
|||
@inherits BaseType<TModel>
|
||||
");
|
||||
|
||||
var engine = CreateEngine();
|
||||
var engine = CreateRuntimeEngine();
|
||||
var pass = new ModelDirective.Pass()
|
||||
{
|
||||
Engine = engine,
|
||||
|
|
@ -234,7 +236,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X
|
|||
return visitor.Node;
|
||||
}
|
||||
|
||||
private RazorEngine CreateEngine()
|
||||
private RazorEngine CreateRuntimeEngine()
|
||||
{
|
||||
return CreateEngineCore();
|
||||
}
|
||||
|
|
@ -246,7 +248,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X
|
|||
|
||||
private RazorEngine CreateEngineCore(bool designTime = false)
|
||||
{
|
||||
return RazorProjectEngine.Create(b =>
|
||||
return CreateProjectEngine(b =>
|
||||
{
|
||||
// Notice we're not registering the ModelDirective.Pass here so we can run it on demand.
|
||||
b.AddDirective(ModelDirective.Directive);
|
||||
|
|
|
|||
|
|
@ -8,8 +8,10 @@ using Xunit;
|
|||
|
||||
namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X
|
||||
{
|
||||
public class MvcRazorTemplateEngineTest
|
||||
public class MvcRazorTemplateEngineTest : RazorProjectEngineTestBase
|
||||
{
|
||||
protected override RazorLanguageVersion Version => RazorLanguageVersion.Version_1_1;
|
||||
|
||||
[Fact]
|
||||
public void GetDefaultImports_IncludesDefaultImports()
|
||||
{
|
||||
|
|
@ -25,7 +27,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X
|
|||
"@using Microsoft.AspNetCore.Mvc.ViewFeatures",
|
||||
};
|
||||
var mvcRazorTemplateEngine = new MvcRazorTemplateEngine(
|
||||
RazorProjectEngine.Create().Engine,
|
||||
CreateProjectEngine().Engine,
|
||||
new TestRazorProjectFileSystem());
|
||||
|
||||
// Act
|
||||
|
|
@ -51,7 +53,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X
|
|||
"@inject global::Microsoft.AspNetCore.Mvc.ViewFeatures.IModelExpressionProvider ModelExpressionProvider",
|
||||
};
|
||||
var mvcRazorTemplateEngine = new MvcRazorTemplateEngine(
|
||||
RazorProjectEngine.Create().Engine,
|
||||
CreateProjectEngine().Engine,
|
||||
new TestRazorProjectFileSystem());
|
||||
|
||||
// Act
|
||||
|
|
@ -69,7 +71,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X
|
|||
{
|
||||
// Arrange
|
||||
var mvcRazorTemplateEngine = new MvcRazorTemplateEngine(
|
||||
RazorProjectEngine.Create().Engine,
|
||||
CreateProjectEngine().Engine,
|
||||
new TestRazorProjectFileSystem());
|
||||
|
||||
// Act
|
||||
|
|
|
|||
|
|
@ -7,8 +7,10 @@ using Xunit;
|
|||
|
||||
namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X
|
||||
{
|
||||
public class MvcViewDocumentClassifierPassTest
|
||||
public class MvcViewDocumentClassifierPassTest : RazorProjectEngineTestBase
|
||||
{
|
||||
protected override RazorLanguageVersion Version => RazorLanguageVersion.Version_1_1;
|
||||
|
||||
[Fact]
|
||||
public void MvcViewDocumentClassifierPass_SetsDocumentKind()
|
||||
{
|
||||
|
|
@ -217,8 +219,6 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X
|
|||
Assert.Equal(new[] { "public", "async", "override" }, visitor.Method.Modifiers);
|
||||
}
|
||||
|
||||
private static RazorProjectEngine CreateProjectEngine() => RazorProjectEngine.Create();
|
||||
|
||||
private static DocumentIntermediateNode CreateIRDocument(RazorProjectEngine engine, RazorCodeDocument codeDocument)
|
||||
{
|
||||
for (var i = 0; i < engine.Phases.Count; i++)
|
||||
|
|
|
|||
|
|
@ -21,6 +21,12 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X
|
|||
throw new ArgumentNullException(nameof(projectItem));
|
||||
}
|
||||
|
||||
// Don't add MVC imports for a component - this shouldn't happen for v2, but just in case.
|
||||
if (string.Equals(projectItem.FileKind, FileKinds.Component, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return Array.Empty<RazorProjectItem>();
|
||||
}
|
||||
|
||||
var imports = new List<RazorProjectItem>();
|
||||
AddDefaultDirectivesImport(imports);
|
||||
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X
|
|||
throw new ArgumentNullException(nameof(builder));
|
||||
}
|
||||
|
||||
FunctionsDirective.Register(builder);
|
||||
InjectDirective.Register(builder);
|
||||
ModelDirective.Register(builder);
|
||||
NamespaceDirective.Register(builder);
|
||||
|
|
@ -42,7 +43,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X
|
|||
builder.Features.Add(new AssemblyAttributeInjectionPass());
|
||||
builder.Features.Add(new InstrumentationPass());
|
||||
|
||||
builder.SetImportFeature(new MvcImportProjectFeature());
|
||||
builder.Features.Add(new MvcImportProjectFeature());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X
|
|||
public static readonly string RouteTemplateKey = "RouteTemplate";
|
||||
|
||||
private static readonly RazorProjectEngine LeadingDirectiveParsingEngine = RazorProjectEngine.Create(
|
||||
RazorConfiguration.Default,
|
||||
RazorConfiguration.Create(RazorLanguageVersion.Version_2_1, "leading-directive-parser", Array.Empty<RazorExtension>()),
|
||||
RazorProjectFileSystem.Create("/"),
|
||||
builder =>
|
||||
{
|
||||
|
|
|
|||
|
|
@ -7,8 +7,10 @@ using Xunit;
|
|||
|
||||
namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X
|
||||
{
|
||||
public class AssemblyAttributeInjectionPassTest
|
||||
public class AssemblyAttributeInjectionPassTest : RazorProjectEngineTestBase
|
||||
{
|
||||
protected override RazorLanguageVersion Version => RazorLanguageVersion.Version_2_1;
|
||||
|
||||
[Fact]
|
||||
public void Execute_NoOps_IfNamespaceNodeIsMissing()
|
||||
{
|
||||
|
|
@ -20,7 +22,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X
|
|||
|
||||
var pass = new AssemblyAttributeInjectionPass
|
||||
{
|
||||
Engine = RazorProjectEngine.Create().Engine,
|
||||
Engine = CreateProjectEngine().Engine,
|
||||
};
|
||||
|
||||
// Act
|
||||
|
|
@ -45,7 +47,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X
|
|||
|
||||
var pass = new AssemblyAttributeInjectionPass
|
||||
{
|
||||
Engine = RazorProjectEngine.Create().Engine,
|
||||
Engine = CreateProjectEngine().Engine,
|
||||
};
|
||||
|
||||
// Act
|
||||
|
|
@ -71,7 +73,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X
|
|||
|
||||
var pass = new AssemblyAttributeInjectionPass
|
||||
{
|
||||
Engine = RazorProjectEngine.Create().Engine,
|
||||
Engine = CreateProjectEngine().Engine,
|
||||
};
|
||||
|
||||
// Act
|
||||
|
|
@ -112,7 +114,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X
|
|||
|
||||
var pass = new AssemblyAttributeInjectionPass
|
||||
{
|
||||
Engine = RazorProjectEngine.Create().Engine,
|
||||
Engine = CreateProjectEngine().Engine,
|
||||
};
|
||||
|
||||
// Act
|
||||
|
|
@ -147,7 +149,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X
|
|||
|
||||
var pass = new AssemblyAttributeInjectionPass
|
||||
{
|
||||
Engine = RazorProjectEngine.Create().Engine,
|
||||
Engine = CreateProjectEngine().Engine,
|
||||
};
|
||||
|
||||
// Act
|
||||
|
|
@ -190,7 +192,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X
|
|||
|
||||
var pass = new AssemblyAttributeInjectionPass
|
||||
{
|
||||
Engine = RazorProjectEngine.Create().Engine,
|
||||
Engine = CreateProjectEngine().Engine,
|
||||
};
|
||||
|
||||
var source = TestRazorSourceDocument.Create("test", new RazorSourceDocumentProperties(filePath: null, relativePath: "/Views/Index.cshtml"));
|
||||
|
|
@ -237,7 +239,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X
|
|||
|
||||
var pass = new AssemblyAttributeInjectionPass
|
||||
{
|
||||
Engine = RazorProjectEngine.Create().Engine,
|
||||
Engine = CreateProjectEngine().Engine,
|
||||
};
|
||||
|
||||
var source = TestRazorSourceDocument.Create("test", new RazorSourceDocumentProperties(filePath: null, relativePath: "/Views/Index.cshtml"));
|
||||
|
|
@ -290,7 +292,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X
|
|||
|
||||
var pass = new AssemblyAttributeInjectionPass
|
||||
{
|
||||
Engine = RazorProjectEngine.Create().Engine,
|
||||
Engine = CreateProjectEngine().Engine,
|
||||
};
|
||||
|
||||
var source = TestRazorSourceDocument.Create("test", new RazorSourceDocumentProperties(filePath: null, relativePath: "\\test\\\"Index.cshtml"));
|
||||
|
|
@ -349,7 +351,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X
|
|||
|
||||
var pass = new AssemblyAttributeInjectionPass
|
||||
{
|
||||
Engine = RazorProjectEngine.Create().Engine,
|
||||
Engine = CreateProjectEngine().Engine,
|
||||
};
|
||||
|
||||
var source = TestRazorSourceDocument.Create("test", new RazorSourceDocumentProperties(filePath: null, relativePath: "/Views/Index.cshtml"));
|
||||
|
|
@ -404,7 +406,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X
|
|||
|
||||
var pass = new AssemblyAttributeInjectionPass
|
||||
{
|
||||
Engine = RazorProjectEngine.Create().Engine,
|
||||
Engine = CreateProjectEngine().Engine,
|
||||
};
|
||||
|
||||
var source = TestRazorSourceDocument.Create("test", new RazorSourceDocumentProperties(filePath: null, relativePath: "test\\\"Index.cshtml"));
|
||||
|
|
|
|||
|
|
@ -6,12 +6,28 @@ using System.Collections.Generic;
|
|||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.AspNetCore.Razor.Language.Extensions;
|
||||
using Microsoft.AspNetCore.Razor.Language.IntegrationTests;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X.IntegrationTests
|
||||
{
|
||||
public class InstrumentationPassIntegrationTest : IntegrationTestBase
|
||||
{
|
||||
private readonly static CSharpCompilation DefaultBaseCompilation = MvcShim.BaseCompilation.WithAssemblyName("AppCode");
|
||||
|
||||
public InstrumentationPassIntegrationTest()
|
||||
: base(generateBaselines: null)
|
||||
{
|
||||
Configuration = RazorConfiguration.Create(
|
||||
RazorLanguageVersion.Version_2_0,
|
||||
"MVC-2.1",
|
||||
new[] { new AssemblyExtension("MVC-2.1", typeof(ExtensionInitializer).Assembly) });
|
||||
}
|
||||
|
||||
protected override CSharpCompilation BaseCompilation => DefaultBaseCompilation;
|
||||
|
||||
protected override RazorConfiguration Configuration { get; }
|
||||
|
||||
[Fact]
|
||||
public void BasicTest()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.AspNetCore.Razor.Language.Extensions;
|
||||
|
|
@ -11,8 +9,10 @@ using Xunit;
|
|||
|
||||
namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X
|
||||
{
|
||||
public class ModelDirectiveTest
|
||||
public class ModelDirectiveTest : RazorProjectEngineTestBase
|
||||
{
|
||||
protected override RazorLanguageVersion Version => RazorLanguageVersion.Version_2_1;
|
||||
|
||||
[Fact]
|
||||
public void ModelDirective_GetModelType_GetsTypeFromFirstWellFormedDirective()
|
||||
{
|
||||
|
|
@ -23,7 +23,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X
|
|||
@model
|
||||
");
|
||||
|
||||
var engine = CreateEngine();
|
||||
var engine = CreateRuntimeEngine();
|
||||
|
||||
var irDocument = CreateIRDocument(engine, codeDocument);
|
||||
|
||||
|
|
@ -40,7 +40,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X
|
|||
// Arrange
|
||||
var codeDocument = CreateDocument(@" ");
|
||||
|
||||
var engine = CreateEngine();
|
||||
var engine = CreateRuntimeEngine();
|
||||
|
||||
var irDocument = CreateIRDocument(engine, codeDocument);
|
||||
|
||||
|
|
@ -60,7 +60,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X
|
|||
@model Type1
|
||||
");
|
||||
|
||||
var engine = CreateEngine();
|
||||
var engine = CreateRuntimeEngine();
|
||||
var pass = new ModelDirective.Pass()
|
||||
{
|
||||
Engine = engine,
|
||||
|
|
@ -87,7 +87,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X
|
|||
@model Type2
|
||||
");
|
||||
|
||||
var engine = CreateEngine();
|
||||
var engine = CreateRuntimeEngine();
|
||||
var pass = new ModelDirective.Pass()
|
||||
{
|
||||
Engine = engine,
|
||||
|
|
@ -113,7 +113,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X
|
|||
@model Type1
|
||||
");
|
||||
|
||||
var engine = CreateEngine();
|
||||
var engine = CreateRuntimeEngine();
|
||||
var pass = new ModelDirective.Pass()
|
||||
{
|
||||
Engine = engine,
|
||||
|
|
@ -138,7 +138,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X
|
|||
@inherits BaseType<TModel>
|
||||
");
|
||||
|
||||
var engine = CreateEngine();
|
||||
var engine = CreateRuntimeEngine();
|
||||
var pass = new ModelDirective.Pass()
|
||||
{
|
||||
Engine = engine,
|
||||
|
|
@ -234,7 +234,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X
|
|||
return visitor.Node;
|
||||
}
|
||||
|
||||
private RazorEngine CreateEngine()
|
||||
private RazorEngine CreateRuntimeEngine()
|
||||
{
|
||||
return CreateEngineCore();
|
||||
}
|
||||
|
|
@ -246,7 +246,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X
|
|||
|
||||
private RazorEngine CreateEngineCore(bool designTime = false)
|
||||
{
|
||||
return RazorProjectEngine.Create(b =>
|
||||
return CreateProjectEngine(b =>
|
||||
{
|
||||
// Notice we're not registering the ModelDirective.Pass here so we can run it on demand.
|
||||
b.AddDirective(ModelDirective.Directive);
|
||||
|
|
|
|||
|
|
@ -7,8 +7,10 @@ using Xunit;
|
|||
|
||||
namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X
|
||||
{
|
||||
public class MvcViewDocumentClassifierPassTest
|
||||
public class MvcViewDocumentClassifierPassTest : RazorProjectEngineTestBase
|
||||
{
|
||||
protected override RazorLanguageVersion Version => RazorLanguageVersion.Version_2_1;
|
||||
|
||||
[Fact]
|
||||
public void MvcViewDocumentClassifierPass_SetsDocumentKind()
|
||||
{
|
||||
|
|
@ -217,8 +219,6 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X
|
|||
Assert.Equal(new[] { "public", "async", "override" }, visitor.Method.Modifiers);
|
||||
}
|
||||
|
||||
private static RazorProjectEngine CreateProjectEngine() => RazorProjectEngine.Create();
|
||||
|
||||
private static DocumentIntermediateNode CreateIRDocument(RazorProjectEngine projectEngine, RazorCodeDocument codeDocument)
|
||||
{
|
||||
for (var i = 0; i < projectEngine.Phases.Count; i++)
|
||||
|
|
|
|||
|
|
@ -9,8 +9,10 @@ using Xunit;
|
|||
|
||||
namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X
|
||||
{
|
||||
public class RazorPageDocumentClassifierPassTest
|
||||
public class RazorPageDocumentClassifierPassTest : RazorProjectEngineTestBase
|
||||
{
|
||||
protected override RazorLanguageVersion Version => RazorLanguageVersion.Version_2_1;
|
||||
|
||||
[Fact]
|
||||
public void RazorPageDocumentClassifierPass_LogsErrorForImportedPageDirectives()
|
||||
{
|
||||
|
|
@ -20,7 +22,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X
|
|||
var importDocument = RazorSourceDocument.Create("@page", "import.cshtml");
|
||||
var sourceDocument = RazorSourceDocument.Create("<p>Hello World</p>", "main.cshtml");
|
||||
var codeDocument = RazorCodeDocument.Create(sourceDocument, new[] { importDocument });
|
||||
var engine = CreateEngine();
|
||||
var engine = CreateProjectEngine().Engine;
|
||||
var irDocument = CreateIRDocument(engine, codeDocument);
|
||||
var pass = new RazorPageDocumentClassifierPass
|
||||
{
|
||||
|
|
@ -55,7 +57,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X
|
|||
";
|
||||
var codeDocument = RazorCodeDocument.Create(RazorSourceDocument.Create(content, "Test.cshtml"));
|
||||
|
||||
var engine = CreateEngine();
|
||||
var engine = CreateProjectEngine().Engine;
|
||||
var irDocument = CreateIRDocument(engine, codeDocument);
|
||||
var pass = new RazorPageDocumentClassifierPass
|
||||
{
|
||||
|
|
@ -85,7 +87,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X
|
|||
";
|
||||
var codeDocument = RazorCodeDocument.Create(RazorSourceDocument.Create(content, "Test.cshtml"));
|
||||
|
||||
var engine = CreateEngine();
|
||||
var engine = CreateProjectEngine().Engine;
|
||||
var irDocument = CreateIRDocument(engine, codeDocument);
|
||||
var pass = new RazorPageDocumentClassifierPass
|
||||
{
|
||||
|
|
@ -109,7 +111,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X
|
|||
// Arrange
|
||||
var codeDocument = RazorCodeDocument.Create(RazorSourceDocument.Create("@page", "Test.cshtml"));
|
||||
|
||||
var engine = CreateEngine();
|
||||
var engine = CreateProjectEngine().Engine;
|
||||
var irDocument = CreateIRDocument(engine, codeDocument);
|
||||
var pass = new RazorPageDocumentClassifierPass
|
||||
{
|
||||
|
|
@ -129,7 +131,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X
|
|||
// Arrange
|
||||
var codeDocument = RazorCodeDocument.Create(RazorSourceDocument.Create("@page", "Test.cshtml"));
|
||||
|
||||
var engine = CreateEngine();
|
||||
var engine = CreateProjectEngine().Engine;
|
||||
var irDocument = CreateIRDocument(engine, codeDocument);
|
||||
irDocument.DocumentKind = "some-value";
|
||||
var pass = new RazorPageDocumentClassifierPass
|
||||
|
|
@ -150,7 +152,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X
|
|||
// Arrange
|
||||
var codeDocument = RazorCodeDocument.Create(RazorSourceDocument.Create("@page+1", "Test.cshtml"));
|
||||
|
||||
var engine = CreateEngine();
|
||||
var engine = CreateProjectEngine().Engine;
|
||||
var irDocument = CreateIRDocument(engine, codeDocument);
|
||||
irDocument.DocumentKind = "some-value";
|
||||
var pass = new RazorPageDocumentClassifierPass
|
||||
|
|
@ -171,7 +173,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X
|
|||
// Arrange
|
||||
var codeDocument = RazorCodeDocument.Create(RazorSourceDocument.Create("@page", "Test.cshtml"));
|
||||
|
||||
var engine = CreateEngine();
|
||||
var engine = CreateProjectEngine().Engine;
|
||||
var irDocument = CreateIRDocument(engine, codeDocument);
|
||||
var pass = new RazorPageDocumentClassifierPass
|
||||
{
|
||||
|
|
@ -194,7 +196,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X
|
|||
var properties = new RazorSourceDocumentProperties(filePath: "ignored", relativePath: "Test.cshtml");
|
||||
var codeDocument = RazorCodeDocument.Create(RazorSourceDocument.Create("@page", properties));
|
||||
|
||||
var engine = CreateEngine();
|
||||
var engine = CreateProjectEngine().Engine;
|
||||
var irDocument = CreateIRDocument(engine, codeDocument);
|
||||
var pass = new RazorPageDocumentClassifierPass
|
||||
{
|
||||
|
|
@ -219,7 +221,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X
|
|||
var properties = new RazorSourceDocumentProperties(filePath: null, relativePath: null);
|
||||
var codeDocument = RazorCodeDocument.Create(RazorSourceDocument.Create("@page", properties));
|
||||
|
||||
var engine = CreateEngine();
|
||||
var engine = CreateProjectEngine().Engine;
|
||||
var irDocument = CreateIRDocument(engine, codeDocument);
|
||||
var pass = new RazorPageDocumentClassifierPass
|
||||
{
|
||||
|
|
@ -246,7 +248,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X
|
|||
var properties = new RazorSourceDocumentProperties(filePath: "ignored", relativePath: relativePath);
|
||||
var codeDocument = RazorCodeDocument.Create(RazorSourceDocument.Create("@page", properties));
|
||||
|
||||
var engine = CreateEngine();
|
||||
var engine = CreateProjectEngine().Engine;
|
||||
var irDocument = CreateIRDocument(engine, codeDocument);
|
||||
var pass = new RazorPageDocumentClassifierPass
|
||||
{
|
||||
|
|
@ -269,7 +271,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X
|
|||
var properties = new RazorSourceDocumentProperties(filePath: @"x::\application\Views\Home\Index.cshtml", relativePath: null);
|
||||
var codeDocument = RazorCodeDocument.Create(RazorSourceDocument.Create("@page", properties));
|
||||
|
||||
var engine = CreateEngine();
|
||||
var engine = CreateProjectEngine().Engine;
|
||||
var irDocument = CreateIRDocument(engine, codeDocument);
|
||||
var pass = new RazorPageDocumentClassifierPass
|
||||
{
|
||||
|
|
@ -292,7 +294,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X
|
|||
var properties = new RazorSourceDocumentProperties(filePath: @"x:\Test.cshtml", relativePath: "path.with+invalid-chars");
|
||||
var codeDocument = RazorCodeDocument.Create(RazorSourceDocument.Create("@page", properties));
|
||||
|
||||
var engine = CreateEngine();
|
||||
var engine = CreateProjectEngine().Engine;
|
||||
var irDocument = CreateIRDocument(engine, codeDocument);
|
||||
var pass = new RazorPageDocumentClassifierPass
|
||||
{
|
||||
|
|
@ -314,7 +316,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X
|
|||
// Arrange
|
||||
var codeDocument = RazorCodeDocument.Create(RazorSourceDocument.Create("@page", "Test.cshtml"));
|
||||
|
||||
var engine = CreateEngine();
|
||||
var engine = CreateProjectEngine().Engine;
|
||||
var irDocument = CreateIRDocument(engine, codeDocument);
|
||||
var pass = new RazorPageDocumentClassifierPass
|
||||
{
|
||||
|
|
@ -339,7 +341,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X
|
|||
var properties = new RazorSourceDocumentProperties(filePath: "ignored", relativePath: "Test.cshtml");
|
||||
var codeDocument = RazorCodeDocument.Create(RazorSourceDocument.Create("@page \"some-route\"", properties));
|
||||
|
||||
var engine = CreateEngine();
|
||||
var engine = CreateProjectEngine().Engine;
|
||||
var irDocument = CreateIRDocument(engine, codeDocument);
|
||||
var pass = new RazorPageDocumentClassifierPass
|
||||
{
|
||||
|
|
@ -357,14 +359,11 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X
|
|||
Assert.Equal("some-route", attributeNode.Value);
|
||||
}
|
||||
|
||||
private static RazorEngine CreateEngine()
|
||||
protected override void ConfigureProjectEngine(RazorProjectEngineBuilder builder)
|
||||
{
|
||||
return RazorProjectEngine.Create(b =>
|
||||
{
|
||||
PageDirective.Register(b);
|
||||
}).Engine;
|
||||
PageDirective.Register(builder);
|
||||
}
|
||||
|
||||
|
||||
private static DocumentIntermediateNode CreateIRDocument(RazorEngine engine, RazorCodeDocument codeDocument)
|
||||
{
|
||||
for (var i = 0; i < engine.Phases.Count; i++)
|
||||
|
|
|
|||
|
|
@ -1,12 +1,20 @@
|
|||
#pragma checksum "TestFiles/IntegrationTests/InstrumentationPassIntegrationTest/BasicTest.cshtml" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "78993008d95836bec2b9175d4294bf7bd5f5f109"
|
||||
// <auto-generated/>
|
||||
#pragma warning disable 1591
|
||||
[assembly: global::Microsoft.AspNetCore.Razor.Hosting.RazorCompiledItemAttribute(typeof(Razor.Template), @"default", @"/TestFiles/IntegrationTests/InstrumentationPassIntegrationTest/BasicTest.cshtml")]
|
||||
namespace Razor
|
||||
[assembly: global::Microsoft.AspNetCore.Razor.Hosting.RazorCompiledItemAttribute(typeof(AspNetCore.TestFiles_IntegrationTests_InstrumentationPassIntegrationTest_BasicTest), @"mvc.1.0.view", @"/TestFiles/IntegrationTests/InstrumentationPassIntegrationTest/BasicTest.cshtml")]
|
||||
[assembly:global::Microsoft.AspNetCore.Mvc.Razor.Compilation.RazorViewAttribute(@"/TestFiles/IntegrationTests/InstrumentationPassIntegrationTest/BasicTest.cshtml", typeof(AspNetCore.TestFiles_IntegrationTests_InstrumentationPassIntegrationTest_BasicTest))]
|
||||
namespace AspNetCore
|
||||
{
|
||||
#line hidden
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
using Microsoft.AspNetCore.Mvc.ViewFeatures;
|
||||
[global::Microsoft.AspNetCore.Razor.Hosting.RazorSourceChecksumAttribute(@"SHA1", @"78993008d95836bec2b9175d4294bf7bd5f5f109", @"/TestFiles/IntegrationTests/InstrumentationPassIntegrationTest/BasicTest.cshtml")]
|
||||
public class Template
|
||||
public class TestFiles_IntegrationTests_InstrumentationPassIntegrationTest_BasicTest : global::Microsoft.AspNetCore.Mvc.Razor.RazorPage<dynamic>
|
||||
{
|
||||
private static readonly global::Microsoft.AspNetCore.Razor.TagHelpers.TagHelperAttribute __tagHelperAttribute_0 = new global::Microsoft.AspNetCore.Razor.TagHelpers.TagHelperAttribute("value", "Hello", global::Microsoft.AspNetCore.Razor.TagHelpers.HtmlAttributeValueStyle.DoubleQuotes);
|
||||
private static readonly global::Microsoft.AspNetCore.Razor.TagHelpers.TagHelperAttribute __tagHelperAttribute_1 = new global::Microsoft.AspNetCore.Razor.TagHelpers.TagHelperAttribute("type", new global::Microsoft.AspNetCore.Html.HtmlString("text"), global::Microsoft.AspNetCore.Razor.TagHelpers.HtmlAttributeValueStyle.SingleQuotes);
|
||||
|
|
@ -34,9 +42,12 @@ namespace Razor
|
|||
#pragma warning disable 1998
|
||||
public async override global::System.Threading.Tasks.Task ExecuteAsync()
|
||||
{
|
||||
BeginContext(31, 28, true);
|
||||
BeginContext(31, 28, true);
|
||||
WriteLiteral("<span someattr>Hola</span>\r\n");
|
||||
EndContext();
|
||||
EndContext();
|
||||
BeginContext(61, 7, false);
|
||||
BeginContext(61, 7, false);
|
||||
#line 3 "TestFiles/IntegrationTests/InstrumentationPassIntegrationTest/BasicTest.cshtml"
|
||||
Write("Hello");
|
||||
|
|
@ -44,14 +55,21 @@ Write("Hello");
|
|||
#line default
|
||||
#line hidden
|
||||
EndContext();
|
||||
EndContext();
|
||||
BeginContext(69, 2, true);
|
||||
BeginContext(69, 2, true);
|
||||
WriteLiteral("\r\n");
|
||||
EndContext();
|
||||
EndContext();
|
||||
BeginContext(71, 87, false);
|
||||
BeginContext(71, 87, false);
|
||||
__tagHelperExecutionContext = __tagHelperScopeManager.Begin("form", global::Microsoft.AspNetCore.Razor.TagHelpers.TagMode.StartTagAndEndTag, "test", async() => {
|
||||
BeginContext(91, 6, true);
|
||||
BeginContext(91, 6, true);
|
||||
WriteLiteral("\r\n ");
|
||||
EndContext();
|
||||
EndContext();
|
||||
BeginContext(97, 52, false);
|
||||
BeginContext(97, 52, false);
|
||||
__tagHelperExecutionContext = __tagHelperScopeManager.Begin("input", global::Microsoft.AspNetCore.Razor.TagHelpers.TagMode.SelfClosing, "test", async() => {
|
||||
}
|
||||
|
|
@ -75,9 +93,12 @@ __InputTagHelper.BarProp = DateTime.Now;
|
|||
Write(__tagHelperExecutionContext.Output);
|
||||
__tagHelperExecutionContext = __tagHelperScopeManager.End();
|
||||
EndContext();
|
||||
EndContext();
|
||||
BeginContext(149, 2, true);
|
||||
BeginContext(149, 2, true);
|
||||
WriteLiteral("\r\n");
|
||||
EndContext();
|
||||
EndContext();
|
||||
}
|
||||
);
|
||||
__FormTagHelper = CreateTagHelper<global::FormTagHelper>();
|
||||
|
|
@ -91,19 +112,27 @@ __InputTagHelper.BarProp = DateTime.Now;
|
|||
Write(__tagHelperExecutionContext.Output);
|
||||
__tagHelperExecutionContext = __tagHelperScopeManager.End();
|
||||
EndContext();
|
||||
EndContext();
|
||||
BeginContext(158, 31, true);
|
||||
BeginContext(158, 31, true);
|
||||
WriteLiteral("\r\n\r\n<span>Here is some content ");
|
||||
EndContext();
|
||||
EndContext();
|
||||
BeginContext(207, 9, true);
|
||||
BeginContext(207, 9, true);
|
||||
WriteLiteral("</span>\r\n");
|
||||
EndContext();
|
||||
EndContext();
|
||||
BeginContext(217, 29, false);
|
||||
BeginContext(217, 29, false);
|
||||
#line 9 "TestFiles/IntegrationTests/InstrumentationPassIntegrationTest/BasicTest.cshtml"
|
||||
Write(Foo(item => new Template(async(__razor_template_writer) => {
|
||||
Write(Foo(item => new global::Microsoft.AspNetCore.Mvc.Razor.HelperResult(async(__razor_template_writer) => {
|
||||
PushWriter(__razor_template_writer);
|
||||
BeginContext(222, 24, true);
|
||||
BeginContext(222, 24, true);
|
||||
WriteLiteral("<span>Hello world</span>");
|
||||
EndContext();
|
||||
EndContext();
|
||||
PopWriter();
|
||||
}
|
||||
)));
|
||||
|
|
@ -111,8 +140,19 @@ Write(Foo(item => new Template(async(__razor_template_writer) => {
|
|||
#line default
|
||||
#line hidden
|
||||
EndContext();
|
||||
EndContext();
|
||||
}
|
||||
#pragma warning restore 1998
|
||||
[global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute]
|
||||
public global::Microsoft.AspNetCore.Mvc.ViewFeatures.IModelExpressionProvider ModelExpressionProvider { get; private set; }
|
||||
[global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute]
|
||||
public global::Microsoft.AspNetCore.Mvc.IUrlHelper Url { get; private set; }
|
||||
[global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute]
|
||||
public global::Microsoft.AspNetCore.Mvc.IViewComponentHelper Component { get; private set; }
|
||||
[global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute]
|
||||
public global::Microsoft.AspNetCore.Mvc.Rendering.IJsonHelper Json { get; private set; }
|
||||
[global::Microsoft.AspNetCore.Mvc.Razor.Internal.RazorInjectAttribute]
|
||||
public global::Microsoft.AspNetCore.Mvc.Rendering.IHtmlHelper<dynamic> Html { get; private set; }
|
||||
}
|
||||
}
|
||||
#pragma warning restore 1591
|
||||
|
|
|
|||
|
|
@ -1,8 +1,17 @@
|
|||
Document -
|
||||
RazorCompiledItemAttribute -
|
||||
NamespaceDeclaration - - Razor
|
||||
CSharpCode -
|
||||
IntermediateToken - - CSharp - [assembly:global::Microsoft.AspNetCore.Mvc.Razor.Compilation.RazorViewAttribute(@"/TestFiles/IntegrationTests/InstrumentationPassIntegrationTest/BasicTest.cshtml", typeof(AspNetCore.TestFiles_IntegrationTests_InstrumentationPassIntegrationTest_BasicTest))]
|
||||
NamespaceDeclaration - - AspNetCore
|
||||
UsingDirective - (1:0,1 [14] ) - System
|
||||
UsingDirective - (16:1,1 [34] ) - System.Collections.Generic
|
||||
UsingDirective - (51:2,1 [19] ) - System.Linq
|
||||
UsingDirective - (71:3,1 [30] ) - System.Threading.Tasks
|
||||
UsingDirective - (102:4,1 [32] ) - Microsoft.AspNetCore.Mvc
|
||||
UsingDirective - (135:5,1 [42] ) - Microsoft.AspNetCore.Mvc.Rendering
|
||||
UsingDirective - (178:6,1 [45] ) - Microsoft.AspNetCore.Mvc.ViewFeatures
|
||||
RazorSourceChecksumAttribute -
|
||||
ClassDeclaration - - public - Template - -
|
||||
ClassDeclaration - - public - TestFiles_IntegrationTests_InstrumentationPassIntegrationTest_BasicTest - global::Microsoft.AspNetCore.Mvc.Razor.RazorPage<dynamic> -
|
||||
PreallocatedTagHelperPropertyValue - - __tagHelperAttribute_0 - value - Hello - HtmlAttributeValueStyle.DoubleQuotes
|
||||
PreallocatedTagHelperHtmlAttributeValue - - __tagHelperAttribute_1 - type - text - HtmlAttributeValueStyle.SingleQuotes
|
||||
PreallocatedTagHelperHtmlAttributeValue - - __tagHelperAttribute_2 - unbound - foo - HtmlAttributeValueStyle.DoubleQuotes
|
||||
|
|
@ -10,6 +19,8 @@ Document -
|
|||
FieldDeclaration - - private - global::FormTagHelper - __FormTagHelper
|
||||
FieldDeclaration - - private - global::InputTagHelper - __InputTagHelper
|
||||
MethodDeclaration - - public async override - global::System.Threading.Tasks.Task - ExecuteAsync
|
||||
CSharpCode -
|
||||
IntermediateToken - - CSharp - BeginContext(31, 28, true);
|
||||
CSharpCode -
|
||||
IntermediateToken - - CSharp - BeginContext(31, 28, true);
|
||||
HtmlContent - (31:1,0 [28] BasicTest.cshtml)
|
||||
|
|
@ -21,28 +32,46 @@ Document -
|
|||
IntermediateToken - (57:1,26 [2] BasicTest.cshtml) - Html - \n
|
||||
CSharpCode -
|
||||
IntermediateToken - - CSharp - EndContext();
|
||||
CSharpCode -
|
||||
IntermediateToken - - CSharp - EndContext();
|
||||
CSharpCode -
|
||||
IntermediateToken - - CSharp - BeginContext(61, 7, false);
|
||||
CSharpCode -
|
||||
IntermediateToken - - CSharp - BeginContext(61, 7, false);
|
||||
CSharpExpression - (61:2,2 [7] BasicTest.cshtml)
|
||||
IntermediateToken - (61:2,2 [7] BasicTest.cshtml) - CSharp - "Hello"
|
||||
CSharpCode -
|
||||
IntermediateToken - - CSharp - EndContext();
|
||||
CSharpCode -
|
||||
IntermediateToken - - CSharp - EndContext();
|
||||
CSharpCode -
|
||||
IntermediateToken - - CSharp - BeginContext(69, 2, true);
|
||||
CSharpCode -
|
||||
IntermediateToken - - CSharp - BeginContext(69, 2, true);
|
||||
HtmlContent - (69:2,10 [2] BasicTest.cshtml)
|
||||
IntermediateToken - (69:2,10 [2] BasicTest.cshtml) - Html - \n
|
||||
CSharpCode -
|
||||
IntermediateToken - - CSharp - EndContext();
|
||||
CSharpCode -
|
||||
IntermediateToken - - CSharp - EndContext();
|
||||
CSharpCode -
|
||||
IntermediateToken - - CSharp - BeginContext(71, 87, false);
|
||||
CSharpCode -
|
||||
IntermediateToken - - CSharp - BeginContext(71, 87, false);
|
||||
TagHelper - (71:3,0 [87] BasicTest.cshtml) - form - TagMode.StartTagAndEndTag
|
||||
DefaultTagHelperBody -
|
||||
CSharpCode -
|
||||
IntermediateToken - - CSharp - BeginContext(91, 6, true);
|
||||
CSharpCode -
|
||||
IntermediateToken - - CSharp - BeginContext(91, 6, true);
|
||||
HtmlContent - (91:3,20 [6] BasicTest.cshtml)
|
||||
IntermediateToken - (91:3,20 [6] BasicTest.cshtml) - Html - \n
|
||||
CSharpCode -
|
||||
IntermediateToken - - CSharp - EndContext();
|
||||
CSharpCode -
|
||||
IntermediateToken - - CSharp - EndContext();
|
||||
CSharpCode -
|
||||
IntermediateToken - - CSharp - BeginContext(97, 52, false);
|
||||
CSharpCode -
|
||||
IntermediateToken - - CSharp - BeginContext(97, 52, false);
|
||||
TagHelper - (97:4,4 [52] BasicTest.cshtml) - input - TagMode.SelfClosing
|
||||
|
|
@ -56,17 +85,27 @@ Document -
|
|||
DefaultTagHelperExecute -
|
||||
CSharpCode -
|
||||
IntermediateToken - - CSharp - EndContext();
|
||||
CSharpCode -
|
||||
IntermediateToken - - CSharp - EndContext();
|
||||
CSharpCode -
|
||||
IntermediateToken - - CSharp - BeginContext(149, 2, true);
|
||||
CSharpCode -
|
||||
IntermediateToken - - CSharp - BeginContext(149, 2, true);
|
||||
HtmlContent - (149:4,56 [2] BasicTest.cshtml)
|
||||
IntermediateToken - (149:4,56 [2] BasicTest.cshtml) - Html - \n
|
||||
CSharpCode -
|
||||
IntermediateToken - - CSharp - EndContext();
|
||||
CSharpCode -
|
||||
IntermediateToken - - CSharp - EndContext();
|
||||
DefaultTagHelperCreate - - FormTagHelper
|
||||
PreallocatedTagHelperHtmlAttribute - - __tagHelperAttribute_2
|
||||
DefaultTagHelperExecute -
|
||||
CSharpCode -
|
||||
IntermediateToken - - CSharp - EndContext();
|
||||
CSharpCode -
|
||||
IntermediateToken - - CSharp - EndContext();
|
||||
CSharpCode -
|
||||
IntermediateToken - - CSharp - BeginContext(158, 31, true);
|
||||
CSharpCode -
|
||||
IntermediateToken - - CSharp - BeginContext(158, 31, true);
|
||||
HtmlContent - (158:5,7 [31] BasicTest.cshtml)
|
||||
|
|
@ -76,6 +115,10 @@ Document -
|
|||
IntermediateToken - (168:7,6 [21] BasicTest.cshtml) - Html - Here is some content
|
||||
CSharpCode -
|
||||
IntermediateToken - - CSharp - EndContext();
|
||||
CSharpCode -
|
||||
IntermediateToken - - CSharp - EndContext();
|
||||
CSharpCode -
|
||||
IntermediateToken - - CSharp - BeginContext(207, 9, true);
|
||||
CSharpCode -
|
||||
IntermediateToken - - CSharp - BeginContext(207, 9, true);
|
||||
HtmlContent - (207:7,45 [9] BasicTest.cshtml)
|
||||
|
|
@ -83,11 +126,17 @@ Document -
|
|||
IntermediateToken - (214:7,52 [2] BasicTest.cshtml) - Html - \n
|
||||
CSharpCode -
|
||||
IntermediateToken - - CSharp - EndContext();
|
||||
CSharpCode -
|
||||
IntermediateToken - - CSharp - EndContext();
|
||||
CSharpCode -
|
||||
IntermediateToken - - CSharp - BeginContext(217, 29, false);
|
||||
CSharpCode -
|
||||
IntermediateToken - - CSharp - BeginContext(217, 29, false);
|
||||
CSharpExpression - (217:8,1 [29] BasicTest.cshtml)
|
||||
IntermediateToken - (217:8,1 [4] BasicTest.cshtml) - CSharp - Foo(
|
||||
Template - (222:8,6 [24] BasicTest.cshtml)
|
||||
CSharpCode -
|
||||
IntermediateToken - - CSharp - BeginContext(222, 24, true);
|
||||
CSharpCode -
|
||||
IntermediateToken - - CSharp - BeginContext(222, 24, true);
|
||||
HtmlContent - (222:8,6 [24] BasicTest.cshtml)
|
||||
|
|
@ -97,6 +146,15 @@ Document -
|
|||
IntermediateToken - (239:8,23 [7] BasicTest.cshtml) - Html - </span>
|
||||
CSharpCode -
|
||||
IntermediateToken - - CSharp - EndContext();
|
||||
CSharpCode -
|
||||
IntermediateToken - - CSharp - EndContext();
|
||||
IntermediateToken - (246:8,30 [1] BasicTest.cshtml) - CSharp - )
|
||||
CSharpCode -
|
||||
IntermediateToken - - CSharp - EndContext();
|
||||
CSharpCode -
|
||||
IntermediateToken - - CSharp - EndContext();
|
||||
Inject -
|
||||
Inject -
|
||||
Inject -
|
||||
Inject -
|
||||
Inject -
|
||||
|
|
|
|||
|
|
@ -21,6 +21,12 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
|
|||
throw new ArgumentNullException(nameof(projectItem));
|
||||
}
|
||||
|
||||
// Don't add MVC imports for a component
|
||||
if (string.Equals(projectItem.FileKind, FileKinds.Component, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return Array.Empty<RazorProjectItem>();
|
||||
}
|
||||
|
||||
var imports = new List<RazorProjectItem>();
|
||||
AddDefaultDirectivesImport(imports);
|
||||
|
||||
|
|
|
|||
|
|
@ -21,8 +21,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
|
|||
ModelDirective.Register(builder);
|
||||
NamespaceDirective.Register(builder);
|
||||
PageDirective.Register(builder);
|
||||
|
||||
InheritsDirective.Register(builder);
|
||||
|
||||
SectionDirective.Register(builder);
|
||||
|
||||
builder.Features.Add(new DefaultTagHelperDescriptorProvider());
|
||||
|
|
@ -40,7 +39,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
|
|||
builder.Features.Add(new RazorPageDocumentClassifierPass());
|
||||
builder.Features.Add(new MvcViewDocumentClassifierPass());
|
||||
|
||||
builder.SetImportFeature(new MvcImportProjectFeature());
|
||||
builder.Features.Add(new MvcImportProjectFeature());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
|
|||
public static readonly string RouteTemplateKey = "RouteTemplate";
|
||||
|
||||
private static readonly RazorProjectEngine LeadingDirectiveParsingEngine = RazorProjectEngine.Create(
|
||||
RazorConfiguration.Default,
|
||||
RazorConfiguration.Create(RazorLanguageVersion.Version_3_0, "leading-directive-parser", Array.Empty<RazorExtension>()),
|
||||
RazorProjectFileSystem.Create("/"),
|
||||
builder =>
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,15 +1,16 @@
|
|||
// 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.Text;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.AspNetCore.Razor.Language.Intermediate;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
|
||||
{
|
||||
public class InjectDirectiveTest
|
||||
public class InjectDirectiveTest : RazorProjectEngineTestBase
|
||||
{
|
||||
protected override RazorLanguageVersion Version => RazorLanguageVersion.Version_3_0;
|
||||
|
||||
[Fact]
|
||||
public void InjectDirectivePass_Execute_DefinesProperty()
|
||||
{
|
||||
|
|
@ -171,14 +172,11 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
|
|||
return visitor.Node;
|
||||
}
|
||||
|
||||
private RazorEngine CreateEngine()
|
||||
protected override void ConfigureProjectEngine(RazorProjectEngineBuilder builder)
|
||||
{
|
||||
return RazorProjectEngine.Create(b =>
|
||||
{
|
||||
// Notice we're not registering the InjectDirective.Pass here so we can run it on demand.
|
||||
b.AddDirective(InjectDirective.Directive);
|
||||
b.AddDirective(ModelDirective.Directive);
|
||||
}).Engine;
|
||||
// Notice we're not registering the InjectDirective.Pass here so we can run it on demand.
|
||||
builder.AddDirective(InjectDirective.Directive);
|
||||
builder.AddDirective(ModelDirective.Directive);
|
||||
}
|
||||
|
||||
private DocumentIntermediateNode CreateIRDocument(RazorEngine engine, RazorCodeDocument codeDocument)
|
||||
|
|
|
|||
|
|
@ -181,6 +181,20 @@ public class MyModel
|
|||
AssertCSharpDocumentMatchesBaseline(compiled.CodeDocument.GetCSharpDocument());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BasicComponent_Runtime()
|
||||
{
|
||||
// Arrange
|
||||
var projectItem = CreateProjectItemFromFile(fileKind: FileKinds.Component);
|
||||
|
||||
// Act
|
||||
var compiled = CompileToAssembly(projectItem, designTime: false);
|
||||
|
||||
// Assert
|
||||
AssertDocumentNodeMatchesBaseline(compiled.CodeDocument.GetDocumentIntermediateNode());
|
||||
AssertCSharpDocumentMatchesBaseline(compiled.CodeDocument.GetCSharpDocument());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Sections_Runtime()
|
||||
{
|
||||
|
|
@ -631,6 +645,21 @@ public class MyModel
|
|||
AssertSourceMappingsMatchBaseline(compiled.CodeDocument);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BasicComponent_DesignTime()
|
||||
{
|
||||
// Arrange
|
||||
var projectItem = CreateProjectItemFromFile(fileKind: FileKinds.Component);
|
||||
|
||||
// Act
|
||||
var compiled = CompileToAssembly(projectItem, designTime: true);
|
||||
|
||||
// Assert
|
||||
AssertDocumentNodeMatchesBaseline(compiled.CodeDocument.GetDocumentIntermediateNode());
|
||||
AssertCSharpDocumentMatchesBaseline(compiled.CodeDocument.GetCSharpDocument());
|
||||
AssertSourceMappingsMatchBaseline(compiled.CodeDocument);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Sections_DesignTime()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.AspNetCore.Razor.Language.Extensions;
|
||||
|
|
@ -11,8 +9,10 @@ using Xunit;
|
|||
|
||||
namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
|
||||
{
|
||||
public class ModelDirectiveTest
|
||||
public class ModelDirectiveTest : RazorProjectEngineTestBase
|
||||
{
|
||||
protected override RazorLanguageVersion Version => RazorLanguageVersion.Version_3_0;
|
||||
|
||||
[Fact]
|
||||
public void ModelDirective_GetModelType_GetsTypeFromFirstWellFormedDirective()
|
||||
{
|
||||
|
|
@ -23,7 +23,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
|
|||
@model
|
||||
");
|
||||
|
||||
var engine = CreateEngine();
|
||||
var engine = CreateRuntimeEngine();
|
||||
|
||||
var irDocument = CreateIRDocument(engine, codeDocument);
|
||||
|
||||
|
|
@ -40,7 +40,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
|
|||
// Arrange
|
||||
var codeDocument = CreateDocument(@" ");
|
||||
|
||||
var engine = CreateEngine();
|
||||
var engine = CreateRuntimeEngine();
|
||||
|
||||
var irDocument = CreateIRDocument(engine, codeDocument);
|
||||
|
||||
|
|
@ -60,7 +60,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
|
|||
@model Type1
|
||||
");
|
||||
|
||||
var engine = CreateEngine();
|
||||
var engine = CreateRuntimeEngine();
|
||||
var pass = new ModelDirective.Pass()
|
||||
{
|
||||
Engine = engine,
|
||||
|
|
@ -87,7 +87,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
|
|||
@model Type2
|
||||
");
|
||||
|
||||
var engine = CreateEngine();
|
||||
var engine = CreateRuntimeEngine();
|
||||
var pass = new ModelDirective.Pass()
|
||||
{
|
||||
Engine = engine,
|
||||
|
|
@ -113,7 +113,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
|
|||
@model Type1
|
||||
");
|
||||
|
||||
var engine = CreateEngine();
|
||||
var engine = CreateRuntimeEngine();
|
||||
var pass = new ModelDirective.Pass()
|
||||
{
|
||||
Engine = engine,
|
||||
|
|
@ -138,7 +138,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
|
|||
@inherits BaseType<TModel>
|
||||
");
|
||||
|
||||
var engine = CreateEngine();
|
||||
var engine = CreateRuntimeEngine();
|
||||
var pass = new ModelDirective.Pass()
|
||||
{
|
||||
Engine = engine,
|
||||
|
|
@ -234,9 +234,9 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
|
|||
return visitor.Node;
|
||||
}
|
||||
|
||||
private RazorEngine CreateEngine()
|
||||
private RazorEngine CreateRuntimeEngine()
|
||||
{
|
||||
return CreateEngineCore();
|
||||
return CreateEngineCore(designTime: false);
|
||||
}
|
||||
|
||||
private RazorEngine CreateDesignTimeEngine()
|
||||
|
|
@ -246,14 +246,11 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
|
|||
|
||||
private RazorEngine CreateEngineCore(bool designTime = false)
|
||||
{
|
||||
return RazorProjectEngine.Create(b =>
|
||||
return CreateProjectEngine(b =>
|
||||
{
|
||||
// Notice we're not registering the ModelDirective.Pass here so we can run it on demand.
|
||||
b.AddDirective(ModelDirective.Directive);
|
||||
|
||||
// There's some special interaction with the inherits directive
|
||||
InheritsDirective.Register(b);
|
||||
|
||||
b.Features.Add(new DesignTimeOptionsFeature(designTime));
|
||||
}).Engine;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,8 +8,10 @@ using Xunit;
|
|||
|
||||
namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
|
||||
{
|
||||
public class MvcRazorTemplateEngineTest
|
||||
public class MvcRazorTemplateEngineTest : RazorProjectEngineTestBase
|
||||
{
|
||||
protected override RazorLanguageVersion Version => RazorLanguageVersion.Version_3_0;
|
||||
|
||||
[Fact]
|
||||
public void GetDefaultImports_IncludesDefaultImports()
|
||||
{
|
||||
|
|
@ -24,8 +26,9 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
|
|||
"@using Microsoft.AspNetCore.Mvc.Rendering",
|
||||
"@using Microsoft.AspNetCore.Mvc.ViewFeatures",
|
||||
};
|
||||
|
||||
var mvcRazorTemplateEngine = new MvcRazorTemplateEngine(
|
||||
RazorProjectEngine.Create().Engine,
|
||||
CreateProjectEngine().Engine,
|
||||
new TestRazorProjectFileSystem());
|
||||
|
||||
// Act
|
||||
|
|
@ -51,7 +54,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
|
|||
"@inject global::Microsoft.AspNetCore.Mvc.ViewFeatures.IModelExpressionProvider ModelExpressionProvider",
|
||||
};
|
||||
var mvcRazorTemplateEngine = new MvcRazorTemplateEngine(
|
||||
RazorProjectEngine.Create().Engine,
|
||||
CreateProjectEngine().Engine,
|
||||
new TestRazorProjectFileSystem());
|
||||
|
||||
// Act
|
||||
|
|
@ -69,7 +72,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
|
|||
{
|
||||
// Arrange
|
||||
var mvcRazorTemplateEngine = new MvcRazorTemplateEngine(
|
||||
RazorProjectEngine.Create().Engine,
|
||||
CreateProjectEngine().Engine,
|
||||
new TestRazorProjectFileSystem());
|
||||
|
||||
// Act
|
||||
|
|
|
|||
|
|
@ -7,8 +7,10 @@ using Xunit;
|
|||
|
||||
namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
|
||||
{
|
||||
public class MvcViewDocumentClassifierPassTest
|
||||
public class MvcViewDocumentClassifierPassTest : RazorProjectEngineTestBase
|
||||
{
|
||||
protected override RazorLanguageVersion Version => RazorLanguageVersion.Version_3_0;
|
||||
|
||||
[Fact]
|
||||
public void MvcViewDocumentClassifierPass_SetsDocumentKind()
|
||||
{
|
||||
|
|
@ -217,8 +219,6 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
|
|||
Assert.Equal(new[] { "public", "async", "override" }, visitor.Method.Modifiers);
|
||||
}
|
||||
|
||||
private static RazorProjectEngine CreateProjectEngine() => RazorProjectEngine.Create();
|
||||
|
||||
private static DocumentIntermediateNode CreateIRDocument(RazorProjectEngine projectEngine, RazorCodeDocument codeDocument)
|
||||
{
|
||||
for (var i = 0; i < projectEngine.Phases.Count; i++)
|
||||
|
|
|
|||
|
|
@ -9,8 +9,10 @@ using Xunit;
|
|||
|
||||
namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
|
||||
{
|
||||
public class RazorPageDocumentClassifierPassTest
|
||||
public class RazorPageDocumentClassifierPassTest : RazorProjectEngineTestBase
|
||||
{
|
||||
protected override RazorLanguageVersion Version => RazorLanguageVersion.Version_3_0;
|
||||
|
||||
[Fact]
|
||||
public void RazorPageDocumentClassifierPass_LogsErrorForImportedPageDirectives()
|
||||
{
|
||||
|
|
@ -20,7 +22,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
|
|||
var importDocument = RazorSourceDocument.Create("@page", "import.cshtml");
|
||||
var sourceDocument = RazorSourceDocument.Create("<p>Hello World</p>", "main.cshtml");
|
||||
var codeDocument = RazorCodeDocument.Create(sourceDocument, new[] { importDocument });
|
||||
var engine = CreateEngine();
|
||||
var engine = CreateRuntimeEngine();
|
||||
var irDocument = CreateIRDocument(engine, codeDocument);
|
||||
var pass = new RazorPageDocumentClassifierPass
|
||||
{
|
||||
|
|
@ -55,7 +57,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
|
|||
";
|
||||
var codeDocument = RazorCodeDocument.Create(RazorSourceDocument.Create(content, "Test.cshtml"));
|
||||
|
||||
var engine = CreateEngine();
|
||||
var engine = CreateRuntimeEngine();
|
||||
var irDocument = CreateIRDocument(engine, codeDocument);
|
||||
var pass = new RazorPageDocumentClassifierPass
|
||||
{
|
||||
|
|
@ -85,7 +87,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
|
|||
";
|
||||
var codeDocument = RazorCodeDocument.Create(RazorSourceDocument.Create(content, "Test.cshtml"));
|
||||
|
||||
var engine = CreateEngine();
|
||||
var engine = CreateRuntimeEngine();
|
||||
var irDocument = CreateIRDocument(engine, codeDocument);
|
||||
var pass = new RazorPageDocumentClassifierPass
|
||||
{
|
||||
|
|
@ -109,7 +111,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
|
|||
// Arrange
|
||||
var codeDocument = RazorCodeDocument.Create(RazorSourceDocument.Create("@page", "Test.cshtml"));
|
||||
|
||||
var engine = CreateEngine();
|
||||
var engine = CreateRuntimeEngine();
|
||||
var irDocument = CreateIRDocument(engine, codeDocument);
|
||||
var pass = new RazorPageDocumentClassifierPass
|
||||
{
|
||||
|
|
@ -129,7 +131,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
|
|||
// Arrange
|
||||
var codeDocument = RazorCodeDocument.Create(RazorSourceDocument.Create("@page", "Test.cshtml"));
|
||||
|
||||
var engine = CreateEngine();
|
||||
var engine = CreateRuntimeEngine();
|
||||
var irDocument = CreateIRDocument(engine, codeDocument);
|
||||
irDocument.DocumentKind = "some-value";
|
||||
var pass = new RazorPageDocumentClassifierPass
|
||||
|
|
@ -150,7 +152,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
|
|||
// Arrange
|
||||
var codeDocument = RazorCodeDocument.Create(RazorSourceDocument.Create("@page+1", "Test.cshtml"));
|
||||
|
||||
var engine = CreateEngine();
|
||||
var engine = CreateRuntimeEngine();
|
||||
var irDocument = CreateIRDocument(engine, codeDocument);
|
||||
irDocument.DocumentKind = "some-value";
|
||||
var pass = new RazorPageDocumentClassifierPass
|
||||
|
|
@ -171,7 +173,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
|
|||
// Arrange
|
||||
var codeDocument = RazorCodeDocument.Create(RazorSourceDocument.Create("@page", "Test.cshtml"));
|
||||
|
||||
var engine = CreateEngine();
|
||||
var engine = CreateRuntimeEngine();
|
||||
var irDocument = CreateIRDocument(engine, codeDocument);
|
||||
var pass = new RazorPageDocumentClassifierPass
|
||||
{
|
||||
|
|
@ -194,7 +196,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
|
|||
var properties = new RazorSourceDocumentProperties(filePath: "ignored", relativePath: "Test.cshtml");
|
||||
var codeDocument = RazorCodeDocument.Create(RazorSourceDocument.Create("@page", properties));
|
||||
|
||||
var engine = CreateEngine();
|
||||
var engine = CreateRuntimeEngine();
|
||||
var irDocument = CreateIRDocument(engine, codeDocument);
|
||||
var pass = new RazorPageDocumentClassifierPass
|
||||
{
|
||||
|
|
@ -219,7 +221,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
|
|||
var properties = new RazorSourceDocumentProperties(filePath: null, relativePath: null);
|
||||
var codeDocument = RazorCodeDocument.Create(RazorSourceDocument.Create("@page", properties));
|
||||
|
||||
var engine = CreateEngine();
|
||||
var engine = CreateRuntimeEngine();
|
||||
var irDocument = CreateIRDocument(engine, codeDocument);
|
||||
var pass = new RazorPageDocumentClassifierPass
|
||||
{
|
||||
|
|
@ -246,7 +248,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
|
|||
var properties = new RazorSourceDocumentProperties(filePath: "ignored", relativePath: relativePath);
|
||||
var codeDocument = RazorCodeDocument.Create(RazorSourceDocument.Create("@page", properties));
|
||||
|
||||
var engine = CreateEngine();
|
||||
var engine = CreateRuntimeEngine();
|
||||
var irDocument = CreateIRDocument(engine, codeDocument);
|
||||
var pass = new RazorPageDocumentClassifierPass
|
||||
{
|
||||
|
|
@ -269,7 +271,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
|
|||
var properties = new RazorSourceDocumentProperties(filePath: @"x::\application\Views\Home\Index.cshtml", relativePath: null);
|
||||
var codeDocument = RazorCodeDocument.Create(RazorSourceDocument.Create("@page", properties));
|
||||
|
||||
var engine = CreateEngine();
|
||||
var engine = CreateRuntimeEngine();
|
||||
var irDocument = CreateIRDocument(engine, codeDocument);
|
||||
var pass = new RazorPageDocumentClassifierPass
|
||||
{
|
||||
|
|
@ -292,7 +294,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
|
|||
var properties = new RazorSourceDocumentProperties(filePath: @"x:\Test.cshtml", relativePath: "path.with+invalid-chars");
|
||||
var codeDocument = RazorCodeDocument.Create(RazorSourceDocument.Create("@page", properties));
|
||||
|
||||
var engine = CreateEngine();
|
||||
var engine = CreateRuntimeEngine();
|
||||
var irDocument = CreateIRDocument(engine, codeDocument);
|
||||
var pass = new RazorPageDocumentClassifierPass
|
||||
{
|
||||
|
|
@ -314,7 +316,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
|
|||
// Arrange
|
||||
var codeDocument = RazorCodeDocument.Create(RazorSourceDocument.Create("@page", "Test.cshtml"));
|
||||
|
||||
var engine = CreateEngine();
|
||||
var engine = CreateRuntimeEngine();
|
||||
var irDocument = CreateIRDocument(engine, codeDocument);
|
||||
var pass = new RazorPageDocumentClassifierPass
|
||||
{
|
||||
|
|
@ -339,7 +341,7 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
|
|||
var properties = new RazorSourceDocumentProperties(filePath: "ignored", relativePath: "Test.cshtml");
|
||||
var codeDocument = RazorCodeDocument.Create(RazorSourceDocument.Create("@page \"some-route\"", properties));
|
||||
|
||||
var engine = CreateEngine();
|
||||
var engine = CreateRuntimeEngine();
|
||||
var irDocument = CreateIRDocument(engine, codeDocument);
|
||||
var pass = new RazorPageDocumentClassifierPass
|
||||
{
|
||||
|
|
@ -357,9 +359,9 @@ namespace Microsoft.AspNetCore.Mvc.Razor.Extensions
|
|||
Assert.Equal("some-route", attributeNode.Value);
|
||||
}
|
||||
|
||||
private static RazorEngine CreateEngine()
|
||||
private RazorEngine CreateRuntimeEngine()
|
||||
{
|
||||
return RazorProjectEngine.Create(b =>
|
||||
return CreateProjectEngine(b =>
|
||||
{
|
||||
PageDirective.Register(b);
|
||||
}).Engine;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,9 @@
|
|||
@implements IDisposable
|
||||
<div class="@this.ToString()">
|
||||
Hello world
|
||||
@string.Format("{0}", "Hello")
|
||||
</div>
|
||||
|
||||
@functions {
|
||||
void IDisposable.Dispose(){ }
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
// <auto-generated/>
|
||||
#pragma warning disable 1591
|
||||
namespace AspNetCore
|
||||
{
|
||||
#line hidden
|
||||
using TModel = global::System.Object;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
public class BasicComponent : Microsoft.AspNetCore.Components.ComponentBase, IDisposable
|
||||
{
|
||||
#pragma warning disable 219
|
||||
private void __RazorDirectiveTokenHelpers__() {
|
||||
((System.Action)(() => {
|
||||
#line 1 "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent.cshtml"
|
||||
IDisposable __typeHelper = default(IDisposable);
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
}
|
||||
))();
|
||||
}
|
||||
#pragma warning restore 219
|
||||
#pragma warning disable 0414
|
||||
private static System.Object __o = null;
|
||||
#pragma warning restore 0414
|
||||
#pragma warning disable 1998
|
||||
protected override void BuildRenderTree(Microsoft.AspNetCore.Components.RenderTree.RenderTreeBuilder builder)
|
||||
{
|
||||
base.BuildRenderTree(builder);
|
||||
#line 2 "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent.cshtml"
|
||||
__o = this.ToString();
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
#line 4 "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent.cshtml"
|
||||
__o = string.Format("{0}", "Hello");
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
}
|
||||
#pragma warning restore 1998
|
||||
#line 7 "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent.cshtml"
|
||||
|
||||
void IDisposable.Dispose(){ }
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
}
|
||||
}
|
||||
#pragma warning restore 1591
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
Document -
|
||||
NamespaceDeclaration - - AspNetCore
|
||||
UsingDirective - - TModel = global::System.Object
|
||||
UsingDirective - (1:0,1 [12] ) - System
|
||||
UsingDirective - (16:1,1 [32] ) - System.Collections.Generic
|
||||
UsingDirective - (51:2,1 [17] ) - System.Linq
|
||||
UsingDirective - (71:3,1 [28] ) - System.Threading.Tasks
|
||||
UsingDirective - (102:4,1 [37] ) - Microsoft.AspNetCore.Components
|
||||
ClassDeclaration - - public - BasicComponent - Microsoft.AspNetCore.Components.ComponentBase - IDisposable
|
||||
DesignTimeDirective -
|
||||
DirectiveToken - (14:0,14 [36] ) - "*, Microsoft.AspNetCore.Components"
|
||||
DirectiveToken - (12:0,12 [11] BasicComponent.cshtml) - IDisposable
|
||||
CSharpCode -
|
||||
IntermediateToken - - CSharp - #pragma warning disable 0414
|
||||
CSharpCode -
|
||||
IntermediateToken - - CSharp - private static System.Object __o = null;
|
||||
CSharpCode -
|
||||
IntermediateToken - - CSharp - #pragma warning restore 0414
|
||||
MethodDeclaration - - protected override - void - BuildRenderTree
|
||||
CSharpCode -
|
||||
IntermediateToken - - CSharp - base.BuildRenderTree(builder);
|
||||
MarkupElement - (25:1,0 [91] BasicComponent.cshtml) - div
|
||||
HtmlAttribute - (29:1,4 [25] BasicComponent.cshtml) - class=" - "
|
||||
CSharpExpressionAttributeValue - (37:1,12 [16] BasicComponent.cshtml) -
|
||||
IntermediateToken - (38:1,13 [15] BasicComponent.cshtml) - CSharp - this.ToString()
|
||||
HtmlContent - (55:1,30 [23] BasicComponent.cshtml)
|
||||
IntermediateToken - (55:1,30 [23] BasicComponent.cshtml) - Html - \n Hello world\n
|
||||
CSharpExpression - (79:3,5 [29] BasicComponent.cshtml)
|
||||
IntermediateToken - (79:3,5 [29] BasicComponent.cshtml) - CSharp - string.Format("{0}", "Hello")
|
||||
HtmlContent - (108:3,34 [2] BasicComponent.cshtml)
|
||||
IntermediateToken - (108:3,34 [2] BasicComponent.cshtml) - Html - \n
|
||||
HtmlContent - (116:4,6 [4] BasicComponent.cshtml)
|
||||
IntermediateToken - (116:4,6 [4] BasicComponent.cshtml) - Html - \n\n
|
||||
HtmlContent - (170:8,1 [2] BasicComponent.cshtml)
|
||||
IntermediateToken - (170:8,1 [2] BasicComponent.cshtml) - Html - \n
|
||||
CSharpCode - (132:6,12 [37] BasicComponent.cshtml)
|
||||
IntermediateToken - (132:6,12 [37] BasicComponent.cshtml) - CSharp - \n void IDisposable.Dispose(){ }\n
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
Source Location: (12:0,12 [11] TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent.cshtml)
|
||||
|IDisposable|
|
||||
Generated Location: (618:17,0 [11] )
|
||||
|IDisposable|
|
||||
|
||||
Source Location: (38:1,13 [15] TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent.cshtml)
|
||||
|this.ToString()|
|
||||
Generated Location: (1214:33,13 [15] )
|
||||
|this.ToString()|
|
||||
|
||||
Source Location: (79:3,5 [29] TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent.cshtml)
|
||||
|string.Format("{0}", "Hello")|
|
||||
Generated Location: (1359:38,6 [29] )
|
||||
|string.Format("{0}", "Hello")|
|
||||
|
||||
Source Location: (132:6,12 [37] TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent.cshtml)
|
||||
|
|
||||
void IDisposable.Dispose(){ }
|
||||
|
|
||||
Generated Location: (1573:45,12 [37] )
|
||||
|
|
||||
void IDisposable.Dispose(){ }
|
||||
|
|
||||
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
#pragma checksum "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent.cshtml" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "d3c3d6059615673cb46fc4974164d61eabadb890"
|
||||
// <auto-generated/>
|
||||
#pragma warning disable 1591
|
||||
namespace AspNetCore
|
||||
{
|
||||
#line hidden
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
public class BasicComponent : Microsoft.AspNetCore.Components.ComponentBase, IDisposable
|
||||
{
|
||||
#pragma warning disable 1998
|
||||
protected override void BuildRenderTree(Microsoft.AspNetCore.Components.RenderTree.RenderTreeBuilder builder)
|
||||
{
|
||||
base.BuildRenderTree(builder);
|
||||
builder.OpenElement(0, "div");
|
||||
builder.AddAttribute(1, "class", this.ToString());
|
||||
builder.AddContent(2, "\r\n Hello world\r\n ");
|
||||
builder.AddContent(3, string.Format("{0}", "Hello"));
|
||||
builder.AddContent(4, "\r\n");
|
||||
builder.CloseElement();
|
||||
}
|
||||
#pragma warning restore 1998
|
||||
#line 7 "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent.cshtml"
|
||||
|
||||
void IDisposable.Dispose(){ }
|
||||
|
||||
#line default
|
||||
#line hidden
|
||||
}
|
||||
}
|
||||
#pragma warning restore 1591
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
Document -
|
||||
NamespaceDeclaration - - AspNetCore
|
||||
UsingDirective - (1:0,1 [14] ) - System
|
||||
UsingDirective - (16:1,1 [34] ) - System.Collections.Generic
|
||||
UsingDirective - (51:2,1 [19] ) - System.Linq
|
||||
UsingDirective - (71:3,1 [30] ) - System.Threading.Tasks
|
||||
UsingDirective - (102:4,1 [37] ) - Microsoft.AspNetCore.Components
|
||||
ClassDeclaration - - public - BasicComponent - Microsoft.AspNetCore.Components.ComponentBase - IDisposable
|
||||
MethodDeclaration - - protected override - void - BuildRenderTree
|
||||
CSharpCode -
|
||||
IntermediateToken - - CSharp - base.BuildRenderTree(builder);
|
||||
MarkupElement - (25:1,0 [91] BasicComponent.cshtml) - div
|
||||
HtmlAttribute - (29:1,4 [25] BasicComponent.cshtml) - class=" - "
|
||||
CSharpExpressionAttributeValue - (37:1,12 [16] BasicComponent.cshtml) -
|
||||
IntermediateToken - (38:1,13 [15] BasicComponent.cshtml) - CSharp - this.ToString()
|
||||
HtmlContent - (55:1,30 [23] BasicComponent.cshtml)
|
||||
IntermediateToken - (55:1,30 [19] BasicComponent.cshtml) - Html - \n Hello world\n
|
||||
IntermediateToken - (74:3,0 [4] BasicComponent.cshtml) - Html -
|
||||
CSharpExpression - (79:3,5 [29] BasicComponent.cshtml)
|
||||
IntermediateToken - (79:3,5 [29] BasicComponent.cshtml) - CSharp - string.Format("{0}", "Hello")
|
||||
HtmlContent - (108:3,34 [2] BasicComponent.cshtml)
|
||||
IntermediateToken - (108:3,34 [2] BasicComponent.cshtml) - Html - \n
|
||||
CSharpCode - (132:6,12 [37] BasicComponent.cshtml)
|
||||
IntermediateToken - (132:6,12 [37] BasicComponent.cshtml) - CSharp - \n void IDisposable.Dispose(){ }\n
|
||||
|
|
@ -257,6 +257,46 @@ namespace Microsoft.AspNetCore.Razor.Language.CodeGeneration
|
|||
VisitDefault(node);
|
||||
}
|
||||
|
||||
public override void VisitComponent(ComponentIntermediateNode node)
|
||||
{
|
||||
Context.NodeWriter.WriteComponent(Context, node);
|
||||
}
|
||||
|
||||
public override void VisitComponentAttribute(ComponentAttributeIntermediateNode node)
|
||||
{
|
||||
Context.NodeWriter.WriteComponentAttribute(Context, node);
|
||||
}
|
||||
|
||||
public override void VisitComponentChildContent(ComponentChildContentIntermediateNode node)
|
||||
{
|
||||
Context.NodeWriter.WriteComponentChildContent(Context, node);
|
||||
}
|
||||
|
||||
public override void VisitComponentTypeArgument(ComponentTypeArgumentIntermediateNode node)
|
||||
{
|
||||
Context.NodeWriter.WriteComponentTypeArgument(Context, node);
|
||||
}
|
||||
|
||||
public override void VisitComponentTypeInferenceMethod(ComponentTypeInferenceMethodIntermediateNode node)
|
||||
{
|
||||
Context.NodeWriter.WriteComponentTypeInferenceMethod(Context, node);
|
||||
}
|
||||
|
||||
public override void VisitMarkupElement(MarkupElementIntermediateNode node)
|
||||
{
|
||||
Context.NodeWriter.WriteMarkupElement(Context, node);
|
||||
}
|
||||
|
||||
public override void VisitMarkupBlock(MarkupBlockIntermediateNode node)
|
||||
{
|
||||
Context.NodeWriter.WriteMarkupBlock(Context, node);
|
||||
}
|
||||
|
||||
public override void VisitReferenceCapture(ReferenceCaptureIntermediateNode node)
|
||||
{
|
||||
Context.NodeWriter.WriteReferenceCapture(Context, node);
|
||||
}
|
||||
|
||||
public override void VisitDefault(IntermediateNode node)
|
||||
{
|
||||
Context.RenderChildren(node);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
// 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.Language.Intermediate;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.CodeGeneration
|
||||
|
|
@ -23,6 +24,46 @@ namespace Microsoft.AspNetCore.Razor.Language.CodeGeneration
|
|||
|
||||
public abstract void WriteCSharpCodeAttributeValue(CodeRenderingContext context, CSharpCodeAttributeValueIntermediateNode node);
|
||||
|
||||
public virtual void WriteComponent(CodeRenderingContext context, ComponentIntermediateNode node)
|
||||
{
|
||||
throw new NotSupportedException("This writer does not support components.");
|
||||
}
|
||||
|
||||
public virtual void WriteComponentAttribute(CodeRenderingContext context, ComponentAttributeIntermediateNode node)
|
||||
{
|
||||
throw new NotSupportedException("This writer does not support components.");
|
||||
}
|
||||
|
||||
public virtual void WriteComponentChildContent(CodeRenderingContext context, ComponentChildContentIntermediateNode node)
|
||||
{
|
||||
throw new NotSupportedException("This writer does not support components.");
|
||||
}
|
||||
|
||||
public virtual void WriteComponentTypeArgument(CodeRenderingContext context, ComponentTypeArgumentIntermediateNode node)
|
||||
{
|
||||
throw new NotSupportedException("This writer does not support components.");
|
||||
}
|
||||
|
||||
public virtual void WriteComponentTypeInferenceMethod(CodeRenderingContext context, ComponentTypeInferenceMethodIntermediateNode node)
|
||||
{
|
||||
throw new NotSupportedException("This writer does not support components.");
|
||||
}
|
||||
|
||||
public virtual void WriteMarkupElement(CodeRenderingContext context, MarkupElementIntermediateNode node)
|
||||
{
|
||||
throw new NotSupportedException("This writer does not support components.");
|
||||
}
|
||||
|
||||
public virtual void WriteMarkupBlock(CodeRenderingContext context, MarkupBlockIntermediateNode node)
|
||||
{
|
||||
throw new NotSupportedException("This writer does not support components.");
|
||||
}
|
||||
|
||||
public virtual void WriteReferenceCapture(CodeRenderingContext context, ReferenceCaptureIntermediateNode node)
|
||||
{
|
||||
throw new NotSupportedException("This writer does not support components.");
|
||||
}
|
||||
|
||||
public abstract void BeginWriterScope(CodeRenderingContext context, string writer);
|
||||
|
||||
public abstract void EndWriterScope(CodeRenderingContext context);
|
||||
|
|
|
|||
|
|
@ -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.AspNetCore.Razor.Language;
|
||||
using Microsoft.AspNetCore.Razor.Language.CodeGeneration;
|
||||
using Microsoft.AspNetCore.Razor.Language.Intermediate;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Components
|
||||
{
|
||||
internal class BlazorRazorCSharpLoweringPhase : RazorEnginePhaseBase, IRazorCSharpLoweringPhase
|
||||
{
|
||||
protected override void ExecuteCore(RazorCodeDocument codeDocument)
|
||||
{
|
||||
var documentNode = codeDocument.GetDocumentIntermediateNode();
|
||||
ThrowForMissingDocumentDependency(documentNode);
|
||||
#pragma warning disable CS0618
|
||||
var writer = new DocumentWriterWorkaround().Create(documentNode.Target, documentNode.Options);
|
||||
#pragma warning restore CS0618
|
||||
try
|
||||
{
|
||||
var cSharpDocument = writer.WriteDocument(codeDocument, documentNode);
|
||||
codeDocument.SetCSharpDocument(cSharpDocument);
|
||||
}
|
||||
catch (RazorCompilerException ex)
|
||||
{
|
||||
// Currently the Blazor code generation has some 'fatal errors' that can cause code generation
|
||||
// to fail completely. This class is here to make that implementation work gracefully.
|
||||
var cSharpDocument = RazorCSharpDocument.Create("", documentNode.Options, new[] { ex.Diagnostic });
|
||||
codeDocument.SetCSharpDocument(cSharpDocument);
|
||||
}
|
||||
}
|
||||
|
||||
private class DocumentWriterWorkaround : DocumentWriter
|
||||
{
|
||||
public override RazorCSharpDocument WriteDocument(RazorCodeDocument codeDocument, DocumentIntermediateNode documentNode)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,130 +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.Language;
|
||||
using Microsoft.AspNetCore.Razor.Language.Extensions;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes the Blazor extension.
|
||||
/// </summary>
|
||||
public class BlazorExtensionInitializer : RazorExtensionInitializer
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies the declaration configuration.
|
||||
/// </summary>
|
||||
public static readonly RazorConfiguration DeclarationConfiguration;
|
||||
|
||||
/// <summary>
|
||||
/// Specifies the default configuration.
|
||||
/// </summary>
|
||||
public static readonly RazorConfiguration DefaultConfiguration;
|
||||
|
||||
static BlazorExtensionInitializer()
|
||||
{
|
||||
// The configuration names here need to match what we put in the MSBuild configuration
|
||||
DeclarationConfiguration = RazorConfiguration.Create(
|
||||
RazorLanguageVersion.Experimental,
|
||||
"BlazorDeclaration-0.1",
|
||||
Array.Empty<RazorExtension>());
|
||||
|
||||
DefaultConfiguration = RazorConfiguration.Create(
|
||||
RazorLanguageVersion.Experimental,
|
||||
"Blazor-0.1",
|
||||
Array.Empty<RazorExtension>());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers the Blazor extension.
|
||||
/// </summary>
|
||||
/// <param name="builder">The <see cref="RazorProjectEngineBuilder"/>.</param>
|
||||
public static void Register(RazorProjectEngineBuilder builder)
|
||||
{
|
||||
if (builder == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(builder));
|
||||
}
|
||||
|
||||
FunctionsDirective.Register(builder);
|
||||
ImplementsDirective.Register(builder);
|
||||
InheritsDirective.Register(builder);
|
||||
InjectDirective.Register(builder);
|
||||
LayoutDirective.Register(builder);
|
||||
PageDirective.Register(builder);
|
||||
TypeParamDirective.Register(builder);
|
||||
|
||||
builder.Features.Remove(builder.Features.OfType<IImportProjectFeature>().Single());
|
||||
builder.Features.Add(new BlazorImportProjectFeature());
|
||||
|
||||
var index = builder.Phases.IndexOf(builder.Phases.OfType<IRazorCSharpLoweringPhase>().Single());
|
||||
builder.Phases[index] = new BlazorRazorCSharpLoweringPhase();
|
||||
|
||||
builder.Features.Add(new ConfigureBlazorCodeGenerationOptions());
|
||||
|
||||
builder.AddTargetExtension(new BlazorTemplateTargetExtension());
|
||||
|
||||
var isDeclarationOnlyCompile = builder.Configuration.ConfigurationName == DeclarationConfiguration.ConfigurationName;
|
||||
|
||||
// Blazor-specific passes, in order.
|
||||
if (!isDeclarationOnlyCompile)
|
||||
{
|
||||
// There's no benefit in this optimization during the declaration-only compile
|
||||
builder.Features.Add(new TrimWhitespacePass());
|
||||
}
|
||||
builder.Features.Add(new ComponentDocumentClassifierPass());
|
||||
builder.Features.Add(new ScriptTagPass());
|
||||
builder.Features.Add(new ComplexAttributeContentPass());
|
||||
builder.Features.Add(new ComponentLoweringPass());
|
||||
builder.Features.Add(new EventHandlerLoweringPass());
|
||||
builder.Features.Add(new ReferenceCaptureLoweringPass());
|
||||
builder.Features.Add(new BindLoweringPass());
|
||||
builder.Features.Add(new TemplateDiagnosticPass());
|
||||
builder.Features.Add(new GenericComponentPass());
|
||||
builder.Features.Add(new ChildContentDiagnosticPass());
|
||||
builder.Features.Add(new HtmlBlockPass());
|
||||
|
||||
if (isDeclarationOnlyCompile)
|
||||
{
|
||||
// This is for 'declaration only' processing. We don't want to try and emit any method bodies during
|
||||
// the design time build because we can't do it correctly until the set of components is known.
|
||||
builder.Features.Add(new EliminateMethodBodyPass());
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the Blazor extension.
|
||||
/// </summary>
|
||||
/// <param name="builder">The <see cref="RazorProjectEngineBuilder"/>.</param>
|
||||
public override void Initialize(RazorProjectEngineBuilder builder)
|
||||
{
|
||||
if (builder == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(builder));
|
||||
}
|
||||
|
||||
Register(builder);
|
||||
}
|
||||
|
||||
private class ConfigureBlazorCodeGenerationOptions : IConfigureRazorCodeGenerationOptionsFeature
|
||||
{
|
||||
public int Order => 0;
|
||||
|
||||
public RazorEngine Engine { get; set; }
|
||||
|
||||
public void Configure(RazorCodeGenerationOptionsBuilder options)
|
||||
{
|
||||
if (options == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(options));
|
||||
}
|
||||
|
||||
// These metadata attributes require a reference to the Razor.Runtime package which we don't
|
||||
// otherwise need.
|
||||
options.SuppressMetadataAttributes = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,647 +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 Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.AspNetCore.Razor.Language.CodeGeneration;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
|
||||
// Copied directly from https://github.com/aspnet/Razor/blob/ff40124594b58b17988d50841175430a4b73d1a9/src/Microsoft.AspNetCore.Razor.Language/CodeGeneration/CodeWriterExtensions.cs
|
||||
// (other than the namespace change) because it's internal
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Components
|
||||
{
|
||||
internal static class CodeWriterExtensions
|
||||
{
|
||||
private const string InstanceMethodFormat = "{0}.{1}";
|
||||
|
||||
private static readonly char[] CStyleStringLiteralEscapeChars =
|
||||
{
|
||||
'\r',
|
||||
'\t',
|
||||
'\"',
|
||||
'\'',
|
||||
'\\',
|
||||
'\0',
|
||||
'\n',
|
||||
'\u2028',
|
||||
'\u2029',
|
||||
};
|
||||
|
||||
public static bool IsAtBeginningOfLine(this CodeWriter writer)
|
||||
{
|
||||
return writer.Length == 0 || writer[writer.Length - 1] == '\n';
|
||||
}
|
||||
|
||||
public static CodeWriter WritePadding(this CodeWriter writer, int offset, SourceSpan? span, CodeRenderingContext context)
|
||||
{
|
||||
if (span == null)
|
||||
{
|
||||
return writer;
|
||||
}
|
||||
|
||||
var basePadding = CalculatePadding();
|
||||
var resolvedPadding = Math.Max(basePadding - offset, 0);
|
||||
|
||||
if (context.Options.IndentWithTabs)
|
||||
{
|
||||
// Avoid writing directly to the StringBuilder here, that will throw off the manual indexing
|
||||
// done by the base class.
|
||||
var tabs = resolvedPadding / context.Options.IndentSize;
|
||||
for (var i = 0; i < tabs; i++)
|
||||
{
|
||||
writer.Write("\t");
|
||||
}
|
||||
|
||||
var spaces = resolvedPadding % context.Options.IndentSize;
|
||||
for (var i = 0; i < spaces; i++)
|
||||
{
|
||||
writer.Write(" ");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (var i = 0; i < resolvedPadding; i++)
|
||||
{
|
||||
writer.Write(" ");
|
||||
}
|
||||
}
|
||||
|
||||
return writer;
|
||||
|
||||
int CalculatePadding()
|
||||
{
|
||||
var spaceCount = 0;
|
||||
for (var i = span.Value.AbsoluteIndex - 1; i >= 0; i--)
|
||||
{
|
||||
var @char = context.SourceDocument[i];
|
||||
if (@char == '\n' || @char == '\r')
|
||||
{
|
||||
break;
|
||||
}
|
||||
else if (@char == '\t')
|
||||
{
|
||||
spaceCount += context.Options.IndentSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
spaceCount++;
|
||||
}
|
||||
}
|
||||
|
||||
return spaceCount;
|
||||
}
|
||||
}
|
||||
|
||||
public static CodeWriter WriteVariableDeclaration(this CodeWriter writer, string type, string name, string value)
|
||||
{
|
||||
writer.Write(type).Write(" ").Write(name);
|
||||
if (!string.IsNullOrEmpty(value))
|
||||
{
|
||||
writer.Write(" = ").Write(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
writer.Write(" = null");
|
||||
}
|
||||
|
||||
writer.WriteLine(";");
|
||||
|
||||
return writer;
|
||||
}
|
||||
|
||||
public static CodeWriter WriteBooleanLiteral(this CodeWriter writer, bool value)
|
||||
{
|
||||
return writer.Write(value.ToString().ToLowerInvariant());
|
||||
}
|
||||
|
||||
public static CodeWriter WriteStartAssignment(this CodeWriter writer, string name)
|
||||
{
|
||||
return writer.Write(name).Write(" = ");
|
||||
}
|
||||
|
||||
public static CodeWriter WriteParameterSeparator(this CodeWriter writer)
|
||||
{
|
||||
return writer.Write(", ");
|
||||
}
|
||||
|
||||
public static CodeWriter WriteStartNewObject(this CodeWriter writer, string typeName)
|
||||
{
|
||||
return writer.Write("new ").Write(typeName).Write("(");
|
||||
}
|
||||
|
||||
public static CodeWriter WriteStringLiteral(this CodeWriter writer, string literal)
|
||||
{
|
||||
if (literal.Length >= 256 && literal.Length <= 1500 && literal.IndexOf('\0') == -1)
|
||||
{
|
||||
WriteVerbatimStringLiteral(writer, literal);
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteCStyleStringLiteral(writer, literal);
|
||||
}
|
||||
|
||||
return writer;
|
||||
}
|
||||
|
||||
public static CodeWriter WriteUsing(this CodeWriter writer, string name)
|
||||
{
|
||||
return WriteUsing(writer, name, endLine: true);
|
||||
}
|
||||
|
||||
public static CodeWriter WriteUsing(this CodeWriter writer, string name, bool endLine)
|
||||
{
|
||||
writer.Write("using ");
|
||||
writer.Write(name);
|
||||
|
||||
if (endLine)
|
||||
{
|
||||
writer.WriteLine(";");
|
||||
}
|
||||
|
||||
return writer;
|
||||
}
|
||||
|
||||
public static CodeWriter WriteLineNumberDirective(this CodeWriter writer, SourceSpan span)
|
||||
{
|
||||
if (writer.Length >= writer.NewLine.Length && !IsAtBeginningOfLine(writer))
|
||||
{
|
||||
writer.WriteLine();
|
||||
}
|
||||
|
||||
var lineNumberAsString = (span.LineIndex + 1).ToString(CultureInfo.InvariantCulture);
|
||||
return writer.Write("#line ").Write(lineNumberAsString).Write(" \"").Write(span.FilePath).WriteLine("\"");
|
||||
}
|
||||
|
||||
public static CodeWriter WriteStartMethodInvocation(this CodeWriter writer, string methodName)
|
||||
{
|
||||
writer.Write(methodName);
|
||||
|
||||
return writer.Write("(");
|
||||
}
|
||||
|
||||
public static CodeWriter WriteEndMethodInvocation(this CodeWriter writer)
|
||||
{
|
||||
return WriteEndMethodInvocation(writer, endLine: true);
|
||||
}
|
||||
|
||||
public static CodeWriter WriteEndMethodInvocation(this CodeWriter writer, bool endLine)
|
||||
{
|
||||
writer.Write(")");
|
||||
if (endLine)
|
||||
{
|
||||
writer.WriteLine(";");
|
||||
}
|
||||
|
||||
return writer;
|
||||
}
|
||||
|
||||
// Writes a method invocation for the given instance name.
|
||||
public static CodeWriter WriteInstanceMethodInvocation(
|
||||
this CodeWriter writer,
|
||||
string instanceName,
|
||||
string methodName,
|
||||
params string[] parameters)
|
||||
{
|
||||
if (instanceName == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(instanceName));
|
||||
}
|
||||
|
||||
if (methodName == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(methodName));
|
||||
}
|
||||
|
||||
return WriteInstanceMethodInvocation(writer, instanceName, methodName, endLine: true, parameters: parameters);
|
||||
}
|
||||
|
||||
// Writes a method invocation for the given instance name.
|
||||
public static CodeWriter WriteInstanceMethodInvocation(
|
||||
this CodeWriter writer,
|
||||
string instanceName,
|
||||
string methodName,
|
||||
bool endLine,
|
||||
params string[] parameters)
|
||||
{
|
||||
if (instanceName == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(instanceName));
|
||||
}
|
||||
|
||||
if (methodName == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(methodName));
|
||||
}
|
||||
|
||||
return WriteMethodInvocation(
|
||||
writer,
|
||||
string.Format(CultureInfo.InvariantCulture, InstanceMethodFormat, instanceName, methodName),
|
||||
endLine,
|
||||
parameters);
|
||||
}
|
||||
|
||||
public static CodeWriter WriteStartInstanceMethodInvocation(this CodeWriter writer, string instanceName, string methodName)
|
||||
{
|
||||
if (instanceName == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(instanceName));
|
||||
}
|
||||
|
||||
if (methodName == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(methodName));
|
||||
}
|
||||
|
||||
return WriteStartMethodInvocation(
|
||||
writer,
|
||||
string.Format(CultureInfo.InvariantCulture, InstanceMethodFormat, instanceName, methodName));
|
||||
}
|
||||
|
||||
public static CodeWriter WriteField(this CodeWriter writer, IList<string> modifiers, string typeName, string fieldName)
|
||||
{
|
||||
if (modifiers == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(modifiers));
|
||||
}
|
||||
|
||||
if (typeName == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(typeName));
|
||||
}
|
||||
|
||||
if (fieldName == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(fieldName));
|
||||
}
|
||||
|
||||
for (var i = 0; i < modifiers.Count; i++)
|
||||
{
|
||||
writer.Write(modifiers[i]);
|
||||
writer.Write(" ");
|
||||
}
|
||||
|
||||
writer.Write(typeName);
|
||||
writer.Write(" ");
|
||||
writer.Write(fieldName);
|
||||
writer.Write(";");
|
||||
writer.WriteLine();
|
||||
|
||||
return writer;
|
||||
}
|
||||
|
||||
public static CodeWriter WriteMethodInvocation(this CodeWriter writer, string methodName, params string[] parameters)
|
||||
{
|
||||
return WriteMethodInvocation(writer, methodName, endLine: true, parameters: parameters);
|
||||
}
|
||||
|
||||
public static CodeWriter WriteMethodInvocation(this CodeWriter writer, string methodName, bool endLine, params string[] parameters)
|
||||
{
|
||||
return
|
||||
WriteStartMethodInvocation(writer, methodName)
|
||||
.Write(string.Join(", ", parameters))
|
||||
.WriteEndMethodInvocation(endLine);
|
||||
}
|
||||
|
||||
public static CodeWriter WriteAutoPropertyDeclaration(this CodeWriter writer, IList<string> modifiers, string typeName, string propertyName)
|
||||
{
|
||||
if (modifiers == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(modifiers));
|
||||
}
|
||||
|
||||
if (typeName == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(typeName));
|
||||
}
|
||||
|
||||
if (propertyName == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(propertyName));
|
||||
}
|
||||
|
||||
for (var i = 0; i < modifiers.Count; i++)
|
||||
{
|
||||
writer.Write(modifiers[i]);
|
||||
writer.Write(" ");
|
||||
}
|
||||
|
||||
writer.Write(typeName);
|
||||
writer.Write(" ");
|
||||
writer.Write(propertyName);
|
||||
writer.Write(" { get; set; }");
|
||||
writer.WriteLine();
|
||||
|
||||
return writer;
|
||||
}
|
||||
|
||||
public static CSharpCodeWritingScope BuildScope(this CodeWriter writer)
|
||||
{
|
||||
return new CSharpCodeWritingScope(writer);
|
||||
}
|
||||
|
||||
public static CSharpCodeWritingScope BuildLambda(this CodeWriter writer, params string[] parameterNames)
|
||||
{
|
||||
return BuildLambda(writer, async: false, parameterNames: parameterNames);
|
||||
}
|
||||
|
||||
public static CSharpCodeWritingScope BuildAsyncLambda(this CodeWriter writer, params string[] parameterNames)
|
||||
{
|
||||
return BuildLambda(writer, async: true, parameterNames: parameterNames);
|
||||
}
|
||||
|
||||
private static CSharpCodeWritingScope BuildLambda(CodeWriter writer, bool async, string[] parameterNames)
|
||||
{
|
||||
if (async)
|
||||
{
|
||||
writer.Write("async");
|
||||
}
|
||||
|
||||
writer.Write("(").Write(string.Join(", ", parameterNames)).Write(") => ");
|
||||
|
||||
var scope = new CSharpCodeWritingScope(writer);
|
||||
|
||||
return scope;
|
||||
}
|
||||
|
||||
public static CSharpCodeWritingScope BuildNamespace(this CodeWriter writer, string name)
|
||||
{
|
||||
writer.Write("namespace ").WriteLine(name);
|
||||
|
||||
return new CSharpCodeWritingScope(writer);
|
||||
}
|
||||
|
||||
public static CSharpCodeWritingScope BuildClassDeclaration(
|
||||
this CodeWriter writer,
|
||||
IList<string> modifiers,
|
||||
string name,
|
||||
string baseType,
|
||||
IEnumerable<string> interfaces)
|
||||
{
|
||||
for (var i = 0; i < modifiers.Count; i++)
|
||||
{
|
||||
writer.Write(modifiers[i]);
|
||||
writer.Write(" ");
|
||||
}
|
||||
|
||||
writer.Write("class ");
|
||||
writer.Write(name);
|
||||
|
||||
var hasBaseType = !string.IsNullOrEmpty(baseType);
|
||||
var hasInterfaces = interfaces != null && interfaces.Count() > 0;
|
||||
|
||||
if (hasBaseType || hasInterfaces)
|
||||
{
|
||||
writer.Write(" : ");
|
||||
|
||||
if (hasBaseType)
|
||||
{
|
||||
writer.Write(baseType);
|
||||
|
||||
if (hasInterfaces)
|
||||
{
|
||||
WriteParameterSeparator(writer);
|
||||
}
|
||||
}
|
||||
|
||||
if (hasInterfaces)
|
||||
{
|
||||
writer.Write(string.Join(", ", interfaces));
|
||||
}
|
||||
}
|
||||
|
||||
writer.WriteLine();
|
||||
|
||||
return new CSharpCodeWritingScope(writer);
|
||||
}
|
||||
|
||||
public static CSharpCodeWritingScope BuildMethodDeclaration(
|
||||
this CodeWriter writer,
|
||||
string accessibility,
|
||||
string returnType,
|
||||
string name,
|
||||
IEnumerable<KeyValuePair<string, string>> parameters)
|
||||
{
|
||||
writer.Write(accessibility)
|
||||
.Write(" ")
|
||||
.Write(returnType)
|
||||
.Write(" ")
|
||||
.Write(name)
|
||||
.Write("(")
|
||||
.Write(string.Join(", ", parameters.Select(p => p.Key + " " + p.Value)))
|
||||
.WriteLine(")");
|
||||
|
||||
return new CSharpCodeWritingScope(writer);
|
||||
}
|
||||
|
||||
public static IDisposable BuildLinePragma(this CodeWriter writer, SourceSpan? span)
|
||||
{
|
||||
if (string.IsNullOrEmpty(span?.FilePath))
|
||||
{
|
||||
// Can't build a valid line pragma without a file path.
|
||||
return NullDisposable.Default;
|
||||
}
|
||||
|
||||
return new LinePragmaWriter(writer, span.Value);
|
||||
}
|
||||
|
||||
private static void WriteVerbatimStringLiteral(CodeWriter writer, string literal)
|
||||
{
|
||||
writer.Write("@\"");
|
||||
|
||||
// We need to suppress indenting during the writing of the string's content. A
|
||||
// verbatim string literal could contain newlines that don't get escaped.
|
||||
var indent = writer.CurrentIndent;
|
||||
writer.CurrentIndent = 0;
|
||||
|
||||
// We need to find the index of each '"' (double-quote) to escape it.
|
||||
var start = 0;
|
||||
int end;
|
||||
while ((end = literal.IndexOf('\"', start)) > -1)
|
||||
{
|
||||
writer.Write(literal, start, end - start);
|
||||
|
||||
writer.Write("\"\"");
|
||||
|
||||
start = end + 1;
|
||||
}
|
||||
|
||||
Debug.Assert(end == -1); // We've hit all of the double-quotes.
|
||||
|
||||
// Write the remainder after the last double-quote.
|
||||
writer.Write(literal, start, literal.Length - start);
|
||||
|
||||
writer.Write("\"");
|
||||
|
||||
writer.CurrentIndent = indent;
|
||||
}
|
||||
|
||||
private static void WriteCStyleStringLiteral(CodeWriter writer, string literal)
|
||||
{
|
||||
// From CSharpCodeGenerator.QuoteSnippetStringCStyle in CodeDOM
|
||||
writer.Write("\"");
|
||||
|
||||
// We need to find the index of each escapable character to escape it.
|
||||
var start = 0;
|
||||
int end;
|
||||
while ((end = literal.IndexOfAny(CStyleStringLiteralEscapeChars, start)) > -1)
|
||||
{
|
||||
writer.Write(literal, start, end - start);
|
||||
|
||||
switch (literal[end])
|
||||
{
|
||||
case '\r':
|
||||
writer.Write("\\r");
|
||||
break;
|
||||
case '\t':
|
||||
writer.Write("\\t");
|
||||
break;
|
||||
case '\"':
|
||||
writer.Write("\\\"");
|
||||
break;
|
||||
case '\'':
|
||||
writer.Write("\\\'");
|
||||
break;
|
||||
case '\\':
|
||||
writer.Write("\\\\");
|
||||
break;
|
||||
case '\0':
|
||||
writer.Write("\\\0");
|
||||
break;
|
||||
case '\n':
|
||||
writer.Write("\\n");
|
||||
break;
|
||||
case '\u2028':
|
||||
case '\u2029':
|
||||
writer.Write("\\u");
|
||||
writer.Write(((int)literal[end]).ToString("X4", CultureInfo.InvariantCulture));
|
||||
break;
|
||||
default:
|
||||
Debug.Assert(false, "Unknown escape character.");
|
||||
break;
|
||||
}
|
||||
|
||||
start = end + 1;
|
||||
}
|
||||
|
||||
Debug.Assert(end == -1); // We've hit all of chars that need escaping.
|
||||
|
||||
// Write the remainder after the last escaped char.
|
||||
writer.Write(literal, start, literal.Length - start);
|
||||
|
||||
writer.Write("\"");
|
||||
}
|
||||
|
||||
public struct CSharpCodeWritingScope : IDisposable
|
||||
{
|
||||
private CodeWriter _writer;
|
||||
private bool _autoSpace;
|
||||
private int _tabSize;
|
||||
private int _startIndent;
|
||||
|
||||
public CSharpCodeWritingScope(CodeWriter writer, int tabSize = 4, bool autoSpace = true)
|
||||
{
|
||||
_writer = writer;
|
||||
_autoSpace = autoSpace;
|
||||
_tabSize = tabSize;
|
||||
_startIndent = -1; // Set in WriteStartScope
|
||||
|
||||
WriteStartScope();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
WriteEndScope();
|
||||
}
|
||||
|
||||
private void WriteStartScope()
|
||||
{
|
||||
TryAutoSpace(" ");
|
||||
|
||||
_writer.WriteLine("{");
|
||||
_writer.CurrentIndent += _tabSize;
|
||||
_startIndent = _writer.CurrentIndent;
|
||||
}
|
||||
|
||||
private void WriteEndScope()
|
||||
{
|
||||
TryAutoSpace(_writer.NewLine);
|
||||
|
||||
// Ensure the scope hasn't been modified
|
||||
if (_writer.CurrentIndent == _startIndent)
|
||||
{
|
||||
_writer.CurrentIndent -= _tabSize;
|
||||
}
|
||||
|
||||
_writer.WriteLine("}");
|
||||
}
|
||||
|
||||
private void TryAutoSpace(string spaceCharacter)
|
||||
{
|
||||
if (_autoSpace &&
|
||||
_writer.Length > 0 &&
|
||||
!char.IsWhiteSpace(_writer[_writer.Length - 1]))
|
||||
{
|
||||
_writer.Write(spaceCharacter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class LinePragmaWriter : IDisposable
|
||||
{
|
||||
private readonly CodeWriter _writer;
|
||||
private readonly int _startIndent;
|
||||
|
||||
public LinePragmaWriter(CodeWriter writer, SourceSpan span)
|
||||
{
|
||||
if (writer == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(writer));
|
||||
}
|
||||
|
||||
_writer = writer;
|
||||
_startIndent = _writer.CurrentIndent;
|
||||
_writer.CurrentIndent = 0;
|
||||
WriteLineNumberDirective(writer, span);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
// Need to add an additional line at the end IF there wasn't one already written.
|
||||
// This is needed to work with the C# editor's handling of #line ...
|
||||
var endsWithNewline = _writer.Length > 0 && _writer[_writer.Length - 1] == '\n';
|
||||
|
||||
// Always write at least 1 empty line to potentially separate code from pragmas.
|
||||
_writer.WriteLine();
|
||||
|
||||
// Check if the previous empty line wasn't enough to separate code from pragmas.
|
||||
if (!endsWithNewline)
|
||||
{
|
||||
_writer.WriteLine();
|
||||
}
|
||||
|
||||
_writer
|
||||
.WriteLine("#line default")
|
||||
.WriteLine("#line hidden");
|
||||
|
||||
_writer.CurrentIndent = _startIndent;
|
||||
}
|
||||
}
|
||||
|
||||
private class NullDisposable : IDisposable
|
||||
{
|
||||
public static readonly NullDisposable Default = new NullDisposable();
|
||||
|
||||
private NullDisposable()
|
||||
{
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -8,13 +8,18 @@ using Microsoft.AspNetCore.Razor.Language.Intermediate;
|
|||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Components
|
||||
{
|
||||
internal class BindLoweringPass : IntermediateNodePassBase, IRazorOptimizationPass
|
||||
internal class ComponentBindLoweringPass : ComponentIntermediateNodePassBase, IRazorOptimizationPass
|
||||
{
|
||||
// Run after event handler pass
|
||||
public override int Order => 100;
|
||||
|
||||
protected override void ExecuteCore(RazorCodeDocument codeDocument, DocumentIntermediateNode documentNode)
|
||||
{
|
||||
if (!IsComponentDocument(documentNode))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var @namespace = documentNode.FindPrimaryNamespace();
|
||||
var @class = documentNode.FindPrimaryClass();
|
||||
if (@namespace == null || @class == null)
|
||||
|
|
@ -2,12 +2,11 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.AspNetCore.Razor.Language.Intermediate;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Components
|
||||
{
|
||||
internal class ChildContentDiagnosticPass : IntermediateNodePassBase, IRazorOptimizationPass
|
||||
internal class ComponentChildContentDiagnosticPass : ComponentIntermediateNodePassBase, IRazorOptimizationPass
|
||||
{
|
||||
// Runs after components/eventhandlers/ref/bind/templates. We want to validate every component
|
||||
// and it's usage of ChildContent.
|
||||
|
|
@ -15,6 +14,11 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
|
||||
protected override void ExecuteCore(RazorCodeDocument codeDocument, DocumentIntermediateNode documentNode)
|
||||
{
|
||||
if (!IsComponentDocument(documentNode))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var visitor = new Visitor();
|
||||
visitor.Visit(documentNode);
|
||||
}
|
||||
|
|
@ -3,29 +3,28 @@
|
|||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.AspNetCore.Razor.Language.CodeGeneration;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// Directs a <see cref="DocumentWriter"/> to use <see cref="BlazorRuntimeNodeWriter"/>.
|
||||
/// </summary>
|
||||
internal class BlazorCodeTarget : CodeTarget
|
||||
internal class ComponentCodeTarget : CodeTarget
|
||||
{
|
||||
private readonly RazorCodeGenerationOptions _options;
|
||||
|
||||
public BlazorCodeTarget(RazorCodeGenerationOptions options, IEnumerable<ICodeTargetExtension> extensions)
|
||||
public ComponentCodeTarget(RazorCodeGenerationOptions options, IEnumerable<ICodeTargetExtension> extensions)
|
||||
{
|
||||
_options = options;
|
||||
Extensions = extensions.ToArray();
|
||||
|
||||
// Components provide some built-in target extensions that don't apply to
|
||||
// legacy documents.
|
||||
Extensions = new[] { new ComponentTemplateTargetExtension(), }.Concat(extensions).ToArray();
|
||||
}
|
||||
|
||||
public ICodeTargetExtension[] Extensions { get; }
|
||||
|
||||
public override IntermediateNodeWriter CreateNodeWriter()
|
||||
{
|
||||
return _options.DesignTime ? (BlazorNodeWriter)new BlazorDesignTimeNodeWriter() : new BlazorRuntimeNodeWriter();
|
||||
return _options.DesignTime ? (IntermediateNodeWriter)new ComponentDesignTimeNodeWriter() : new ComponentRuntimeNodeWriter();
|
||||
}
|
||||
|
||||
public override TExtension GetExtension<TExtension>()
|
||||
|
|
@ -2,7 +2,6 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.AspNetCore.Razor.Language.Intermediate;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Components
|
||||
|
|
@ -12,13 +11,18 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
//
|
||||
// This is where a lot of the complexity in the Razor/TagHelpers model creeps in and we
|
||||
// might be able to avoid it if these features aren't needed.
|
||||
internal class ComplexAttributeContentPass : IntermediateNodePassBase, IRazorOptimizationPass
|
||||
internal class ComponentComplexAttributeContentPass : ComponentIntermediateNodePassBase, IRazorOptimizationPass
|
||||
{
|
||||
// Run before other Blazor passes
|
||||
// Run before other Component passes
|
||||
public override int Order => -1000;
|
||||
|
||||
protected override void ExecuteCore(RazorCodeDocument codeDocument, DocumentIntermediateNode documentNode)
|
||||
{
|
||||
if (!IsComponentDocument(documentNode))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var nodes = documentNode.FindDescendantNodes<TagHelperIntermediateNode>();
|
||||
for (var i = 0; i < nodes.Count; i++)
|
||||
{
|
||||
|
|
@ -11,13 +11,13 @@ using Microsoft.AspNetCore.Razor.Language.Intermediate;
|
|||
namespace Microsoft.AspNetCore.Razor.Language.Components
|
||||
{
|
||||
// Based on the DesignTimeNodeWriter from Razor repo.
|
||||
internal class BlazorDesignTimeNodeWriter : BlazorNodeWriter
|
||||
internal class ComponentDesignTimeNodeWriter : ComponentNodeWriter
|
||||
{
|
||||
private readonly ScopeStack _scopeStack = new ScopeStack();
|
||||
|
||||
private static readonly string DesignTimeVariable = "__o";
|
||||
|
||||
public override void WriteHtmlBlock(CodeRenderingContext context, MarkupBlockIntermediateNode node)
|
||||
public override void WriteMarkupBlock(CodeRenderingContext context, MarkupBlockIntermediateNode node)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
|
|
@ -32,7 +32,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
// Do nothing
|
||||
}
|
||||
|
||||
public override void WriteHtmlElement(CodeRenderingContext context, MarkupElementIntermediateNode node)
|
||||
public override void WriteMarkupElement(CodeRenderingContext context, MarkupElementIntermediateNode node)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
|
|
@ -309,7 +309,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
// Do nothing
|
||||
}
|
||||
|
||||
public override void BeginWriteAttribute(CodeWriter codeWriter, string key)
|
||||
protected override void BeginWriteAttribute(CodeWriter codeWriter, string key)
|
||||
{
|
||||
if (codeWriter == null)
|
||||
{
|
||||
|
|
@ -3,7 +3,9 @@
|
|||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Microsoft.AspNetCore.Razor.Language.CodeGeneration;
|
||||
using Microsoft.AspNetCore.Razor.Language.Intermediate;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Components
|
||||
|
|
@ -25,6 +27,11 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
return string.Equals(codeDocument.GetFileKind(), FileKinds.Component);
|
||||
}
|
||||
|
||||
protected override CodeTarget CreateTarget(RazorCodeDocument codeDocument, RazorCodeGenerationOptions options)
|
||||
{
|
||||
return new ComponentCodeTarget(options, TargetExtensions);
|
||||
}
|
||||
|
||||
protected override void OnDocumentStructureCreated(RazorCodeDocument codeDocument, NamespaceDeclarationIntermediateNode @namespace, ClassDeclarationIntermediateNode @class, MethodDeclarationIntermediateNode method)
|
||||
{
|
||||
base.OnDocumentStructureCreated(codeDocument, @namespace, @class, method);
|
||||
|
|
@ -45,7 +52,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
@namespace.Content = computedNamespace;
|
||||
|
||||
@class.ClassName = computedClass;
|
||||
@class.BaseType = $"global::{CodeGenerationConstants.ComponentBase.FullTypeName}";
|
||||
@class.BaseType = $"{CodeGenerationConstants.ComponentBase.FullTypeName}";
|
||||
var filePath = codeDocument.Source.RelativePath ?? codeDocument.Source.FilePath;
|
||||
if (string.IsNullOrEmpty(filePath))
|
||||
{
|
||||
|
|
@ -61,12 +68,24 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
|
||||
@class.Modifiers.Clear();
|
||||
@class.Modifiers.Add("public");
|
||||
@class.Modifiers.Add("sealed");
|
||||
|
||||
var documentNode = codeDocument.GetDocumentIntermediateNode();
|
||||
var typeParamReferences = documentNode.FindDirectiveReferences(ComponentTypeParamDirective.Directive);
|
||||
for (var i = 0; i < typeParamReferences.Count; i++)
|
||||
{
|
||||
var typeParamNode = (DirectiveIntermediateNode)typeParamReferences[i].Node;
|
||||
if (typeParamNode.HasDiagnostics)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
@class.TypeParameters.Add(new TypeParameter() { ParameterName = typeParamNode.Tokens.First().Content, });
|
||||
}
|
||||
|
||||
method.MethodName = CodeGenerationConstants.ComponentBase.BuildRenderTree;
|
||||
method.ReturnType = "void";
|
||||
method.Modifiers.Clear();
|
||||
method.Modifiers.Add("public");
|
||||
method.Modifiers.Add("protected");
|
||||
method.Modifiers.Add("override");
|
||||
|
||||
method.Parameters.Clear();
|
||||
|
|
|
|||
|
|
@ -8,12 +8,17 @@ using Microsoft.AspNetCore.Razor.Language.Intermediate;
|
|||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Components
|
||||
{
|
||||
internal class EventHandlerLoweringPass : IntermediateNodePassBase, IRazorOptimizationPass
|
||||
internal class ComponentEventHandlerLoweringPass : ComponentIntermediateNodePassBase, IRazorOptimizationPass
|
||||
{
|
||||
public override int Order => 50;
|
||||
|
||||
protected override void ExecuteCore(RazorCodeDocument codeDocument, DocumentIntermediateNode documentNode)
|
||||
{
|
||||
if (!IsComponentDocument(documentNode))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var @namespace = documentNode.FindPrimaryNamespace();
|
||||
var @class = documentNode.FindPrimaryClass();
|
||||
if (@namespace == null || @class == null)
|
||||
|
|
@ -1,22 +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.Language.Extensions;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Components
|
||||
{
|
||||
public static class ComponentExtensions
|
||||
{
|
||||
public static void Register(RazorProjectEngineBuilder builder)
|
||||
{
|
||||
if (builder == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(builder));
|
||||
}
|
||||
|
||||
FunctionsDirective.Register(builder);
|
||||
builder.Features.Add(new ComponentDocumentClassifierPass());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -11,7 +11,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
// 1. Adds diagnostics for missing generic type arguments
|
||||
// 2. Rewrites the type name of the component to substitute generic type arguments
|
||||
// 3. Rewrites the type names of parameters/child content to substitute generic type arguments
|
||||
internal class GenericComponentPass : IntermediateNodePassBase, IRazorOptimizationPass
|
||||
internal class ComponentGenericTypePass : ComponentIntermediateNodePassBase, IRazorOptimizationPass
|
||||
{
|
||||
private TypeNameFeature _typeNameFeature;
|
||||
|
||||
|
|
@ -19,27 +19,42 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
// and it's usage of ChildContent.
|
||||
public override int Order => 160;
|
||||
|
||||
protected override void OnInitialized()
|
||||
private TypeNameFeature TypeNameFeature
|
||||
{
|
||||
_typeNameFeature = GetRequiredFeature<TypeNameFeature>();
|
||||
get
|
||||
{
|
||||
// Doing lazy intialization here to avoid making things really complicated when we don't
|
||||
// need to exercise this code in tests.
|
||||
if (_typeNameFeature == null)
|
||||
{
|
||||
_typeNameFeature = GetRequiredFeature<TypeNameFeature>();
|
||||
}
|
||||
|
||||
return _typeNameFeature;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void ExecuteCore(RazorCodeDocument codeDocument, DocumentIntermediateNode documentNode)
|
||||
{
|
||||
var visitor = new Visitor(_typeNameFeature);
|
||||
if (!IsComponentDocument(documentNode))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var visitor = new Visitor(this);
|
||||
visitor.Visit(documentNode);
|
||||
}
|
||||
|
||||
private class Visitor : IntermediateNodeWalker
|
||||
{
|
||||
private readonly TypeNameFeature _typeNameFeature;
|
||||
private readonly ComponentGenericTypePass _pass;
|
||||
|
||||
// Incrementing ID for type inference method names
|
||||
private int _id;
|
||||
|
||||
public Visitor(TypeNameFeature typeNameFeature)
|
||||
public Visitor(ComponentGenericTypePass pass)
|
||||
{
|
||||
_typeNameFeature = typeNameFeature;
|
||||
_pass = pass;
|
||||
}
|
||||
|
||||
public override void VisitComponent(ComponentIntermediateNode node)
|
||||
|
|
@ -82,7 +97,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
if (ValidateTypeArguments(node, bindings))
|
||||
{
|
||||
var mappings = bindings.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.Content);
|
||||
RewriteTypeNames(_typeNameFeature.CreateGenericTypeRewriter(mappings), node);
|
||||
RewriteTypeNames(_pass.TypeNameFeature.CreateGenericTypeRewriter(mappings), node);
|
||||
}
|
||||
|
||||
return;
|
||||
|
|
@ -96,7 +111,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
|
||||
// Since we're generating code in a different namespace, we need to 'global qualify' all of the types
|
||||
// to avoid clashes with our generated code.
|
||||
RewriteTypeNames(_typeNameFeature.CreateGlobalQualifiedTypeNameRewriter(bindings.Keys), node);
|
||||
RewriteTypeNames(_pass.TypeNameFeature.CreateGlobalQualifiedTypeNameRewriter(bindings.Keys), node);
|
||||
|
||||
//
|
||||
// We need to verify that an argument was provided that 'covers' each type parameter.
|
||||
|
|
@ -122,7 +137,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
continue;
|
||||
}
|
||||
|
||||
var typeParameters = _typeNameFeature.ParseTypeParameters(attribute.TypeName);
|
||||
var typeParameters = _pass.TypeNameFeature.ParseTypeParameters(attribute.TypeName);
|
||||
if (typeParameters.Count == 0)
|
||||
{
|
||||
bindings.Remove(attribute.TypeName);
|
||||
|
|
@ -145,7 +160,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
// helpful as possible. So let's substitute 'object' for all of those type parameters, and add
|
||||
// an error.
|
||||
var mappings = bindings.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.Content);
|
||||
RewriteTypeNames(_typeNameFeature.CreateGenericTypeRewriter(mappings), node);
|
||||
RewriteTypeNames(_pass.TypeNameFeature.CreateGenericTypeRewriter(mappings), node);
|
||||
|
||||
node.Diagnostics.Add(ComponentDiagnosticFactory.Create_GenericComponentTypeInferenceUnderspecified(node.Source, node, node.Component.GetTypeParameters()));
|
||||
}
|
||||
|
|
@ -14,7 +14,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
//
|
||||
// Does not preserve insignificant details of the HTML, like tag closing style
|
||||
// or quote style.
|
||||
internal class HtmlBlockPass : IntermediateNodePassBase, IRazorOptimizationPass
|
||||
internal class ComponentHtmlBlockPass : ComponentIntermediateNodePassBase, IRazorOptimizationPass
|
||||
{
|
||||
// Runs LATE because we want to destroy structure.
|
||||
public override int Order => 10000;
|
||||
|
|
@ -23,6 +23,11 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
RazorCodeDocument codeDocument,
|
||||
DocumentIntermediateNode documentNode)
|
||||
{
|
||||
if (!IsComponentDocument(documentNode))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (documentNode.Options.DesignTime)
|
||||
{
|
||||
// Nothing to do during design time.
|
||||
|
|
@ -114,9 +119,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
// We would store both the div and a tag in a list, but make sure to visit
|
||||
// the div first. Then when we process the div (recursively), we would remove
|
||||
// the a from the list.
|
||||
private class FindHtmlTreeVisitor :
|
||||
IntermediateNodeWalker,
|
||||
IExtensionIntermediateNodeVisitor<MarkupElementIntermediateNode>
|
||||
private class FindHtmlTreeVisitor : IntermediateNodeWalker
|
||||
{
|
||||
private bool _foundNonHtml;
|
||||
|
||||
|
|
@ -129,7 +132,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
base.VisitDefault(node);
|
||||
}
|
||||
|
||||
public void VisitExtension(MarkupElementIntermediateNode node)
|
||||
public override void VisitMarkupElement(MarkupElementIntermediateNode node)
|
||||
{
|
||||
// We need to restore the state after processing this node.
|
||||
// We might have found a leaf-block of HTML, but that shouldn't
|
||||
|
|
@ -226,9 +229,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
}
|
||||
}
|
||||
|
||||
private class RewriteVisitor :
|
||||
IntermediateNodeWalker,
|
||||
IExtensionIntermediateNodeVisitor<MarkupElementIntermediateNode>
|
||||
private class RewriteVisitor : IntermediateNodeWalker
|
||||
{
|
||||
private readonly StringBuilder _encodingBuilder;
|
||||
|
||||
|
|
@ -243,7 +244,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
|
||||
public StringBuilder Builder { get; } = new StringBuilder();
|
||||
|
||||
public void VisitExtension(MarkupElementIntermediateNode node)
|
||||
public override void VisitMarkupElement(MarkupElementIntermediateNode node)
|
||||
{
|
||||
for (var i = 0; i < _trees.Count; i++)
|
||||
{
|
||||
|
|
@ -319,12 +320,24 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
|
||||
public override void VisitHtmlAttributeValue(HtmlAttributeValueIntermediateNode node)
|
||||
{
|
||||
Builder.Append(node.Children);
|
||||
for (var i = 0; i < node.Children.Count; i++)
|
||||
{
|
||||
if (node.Children[i] is IntermediateToken token)
|
||||
{
|
||||
Builder.Append(token.Content);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void VisitHtml(HtmlContentIntermediateNode node)
|
||||
{
|
||||
Builder.Append(node.Children);
|
||||
for (var i = 0; i < node.Children.Count; i++)
|
||||
{
|
||||
if (node.Children[i] is IntermediateToken token)
|
||||
{
|
||||
Builder.Append(token.Content);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -9,7 +9,7 @@ using System.Text;
|
|||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Components
|
||||
{
|
||||
internal class BlazorImportProjectFeature : IImportProjectFeature
|
||||
internal class ComponentImportProjectFeature : IImportProjectFeature
|
||||
{
|
||||
private const string ImportsFileName = "_ViewImports.cshtml";
|
||||
|
||||
|
|
@ -33,6 +33,12 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
throw new ArgumentNullException(nameof(projectItem));
|
||||
}
|
||||
|
||||
// Don't add Component imports for a non-component.
|
||||
if (!string.Equals(projectItem.FileKind, FileKinds.Component, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return Array.Empty<RazorProjectItem>();
|
||||
}
|
||||
|
||||
var imports = new List<RazorProjectItem>()
|
||||
{
|
||||
new VirtualProjectItem(DefaultUsingImportContent),
|
||||
|
|
@ -46,13 +52,15 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
// Avoiding the path-specific APIs here, we want to handle all styles of paths
|
||||
// on all platforms
|
||||
var trimLength = projectItem.FilePath.Length + (projectItem.FilePath.StartsWith("/") ? 0 : 1);
|
||||
var baseDirectory = projectItem.PhysicalPath.Substring(0, projectItem.PhysicalPath.Length - trimLength);
|
||||
|
||||
var lastSlash = baseDirectory.LastIndexOfAny(PathSeparators);
|
||||
var baseNamespace = lastSlash == -1 ? baseDirectory : baseDirectory.Substring(lastSlash + 1);
|
||||
if (!string.IsNullOrEmpty(baseNamespace))
|
||||
if (projectItem.PhysicalPath.Length > trimLength)
|
||||
{
|
||||
imports.Add(new VirtualProjectItem($@"@addTagHelper ""*, {baseNamespace}"""));
|
||||
var baseDirectory = projectItem.PhysicalPath.Substring(0, projectItem.PhysicalPath.Length - trimLength);
|
||||
var lastSlash = baseDirectory.LastIndexOfAny(PathSeparators);
|
||||
var baseNamespace = lastSlash == -1 ? baseDirectory : baseDirectory.Substring(lastSlash + 1);
|
||||
if (!string.IsNullOrEmpty(baseNamespace))
|
||||
{
|
||||
imports.Add(new VirtualProjectItem($@"@addTagHelper ""*, {baseNamespace}"""));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
// 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;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Components
|
||||
{
|
||||
// Much of the following is equivalent to Microsoft.AspNetCore.Mvc.Razor.Extensions's InjectDirective,
|
||||
// but this one outputs properties annotated for Blazor's property injector, plus it doesn't need to
|
||||
// support multiple CodeTargets.
|
||||
|
||||
internal class ComponentInjectDirective
|
||||
{
|
||||
public static readonly DirectiveDescriptor Directive = DirectiveDescriptor.CreateDirective(
|
||||
"inject",
|
||||
DirectiveKind.SingleLine,
|
||||
builder =>
|
||||
{
|
||||
builder.AddTypeToken("TypeName", "The type of the service to inject.");
|
||||
builder.AddMemberToken("PropertyName", "The name of the property.");
|
||||
builder.Usage = DirectiveUsage.FileScopedMultipleOccurring;
|
||||
builder.Description = "Inject a service from the application's service container into a property.";
|
||||
});
|
||||
|
||||
public static void Register(RazorProjectEngineBuilder builder)
|
||||
{
|
||||
if (builder == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(builder));
|
||||
}
|
||||
|
||||
builder.AddDirective(FileKinds.Component, Directive);
|
||||
builder.Features.Add(new ComponentInjectDirectivePass());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
// 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.Language.Intermediate;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Components
|
||||
{
|
||||
internal class ComponentInjectDirectivePass : IntermediateNodePassBase, IRazorDirectiveClassifierPass
|
||||
{
|
||||
protected override void ExecuteCore(
|
||||
RazorCodeDocument codeDocument,
|
||||
DocumentIntermediateNode documentNode)
|
||||
{
|
||||
var visitor = new Visitor();
|
||||
visitor.Visit(documentNode);
|
||||
|
||||
var properties = new HashSet<string>(StringComparer.Ordinal);
|
||||
var classNode = documentNode.FindPrimaryClass();
|
||||
|
||||
for (var i = visitor.Directives.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var directive = visitor.Directives[i];
|
||||
var tokens = directive.Tokens.ToArray();
|
||||
if (tokens.Length < 2)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var typeName = tokens[0].Content;
|
||||
var memberName = tokens[1].Content;
|
||||
|
||||
if (!properties.Add(memberName))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
classNode.Children.Add(new ComponentInjectIntermediateNode(typeName, memberName));
|
||||
}
|
||||
}
|
||||
|
||||
private class Visitor : IntermediateNodeWalker
|
||||
{
|
||||
public IList<DirectiveIntermediateNode> Directives { get; }
|
||||
= new List<DirectiveIntermediateNode>();
|
||||
|
||||
public override void VisitDirective(DirectiveIntermediateNode node)
|
||||
{
|
||||
if (node.Directive == ComponentInjectDirective.Directive)
|
||||
{
|
||||
Directives.Add(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
// 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.Language.CodeGeneration;
|
||||
using Microsoft.AspNetCore.Razor.Language.Intermediate;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Components
|
||||
{
|
||||
internal class ComponentInjectIntermediateNode : ExtensionIntermediateNode
|
||||
{
|
||||
private static readonly IList<string> _injectedPropertyModifiers = new[]
|
||||
{
|
||||
$"[global::{ComponentsApi.InjectAttribute.FullTypeName}]",
|
||||
"private" // Encapsulation is the default
|
||||
};
|
||||
|
||||
public ComponentInjectIntermediateNode(string typeName, string memberName)
|
||||
{
|
||||
TypeName = typeName;
|
||||
MemberName = memberName;
|
||||
}
|
||||
|
||||
public string TypeName { get; }
|
||||
|
||||
public string MemberName { get; }
|
||||
|
||||
public override IntermediateNodeCollection Children => IntermediateNodeCollection.ReadOnly;
|
||||
|
||||
|
||||
public override void Accept(IntermediateNodeVisitor visitor)
|
||||
{
|
||||
if (visitor == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(visitor));
|
||||
}
|
||||
|
||||
AcceptExtensionNode(this, visitor);
|
||||
}
|
||||
|
||||
public override void WriteNode(CodeTarget target, CodeRenderingContext context)
|
||||
{
|
||||
if (target == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(target));
|
||||
}
|
||||
|
||||
if (context == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
context.CodeWriter.WriteAutoPropertyDeclaration(
|
||||
_injectedPropertyModifiers,
|
||||
TypeName,
|
||||
MemberName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
// 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.Language.Intermediate;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Components
|
||||
{
|
||||
internal abstract class ComponentIntermediateNodePassBase : IntermediateNodePassBase
|
||||
{
|
||||
protected bool IsComponentDocument(DocumentIntermediateNode document)
|
||||
{
|
||||
if (document == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(document));
|
||||
}
|
||||
|
||||
return string.Equals(document.DocumentKind, ComponentDocumentClassifierPass.ComponentDocumentKind, StringComparison.Ordinal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,11 +2,10 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Components
|
||||
{
|
||||
internal static class LayoutDirective
|
||||
internal static class ComponentLayoutDirective
|
||||
{
|
||||
public static readonly DirectiveDescriptor Directive = DirectiveDescriptor.CreateDirective(
|
||||
"layout",
|
||||
|
|
@ -25,8 +24,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
throw new ArgumentNullException(nameof(builder));
|
||||
}
|
||||
|
||||
builder.AddDirective(Directive);
|
||||
builder.Features.Add(new LayoutDirectivePass());
|
||||
builder.AddDirective(FileKinds.Component, Directive);
|
||||
builder.Features.Add(new ComponentLayoutDirectivePass());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -7,7 +7,7 @@ using Microsoft.AspNetCore.Razor.Language.Intermediate;
|
|||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Components
|
||||
{
|
||||
internal class LayoutDirectivePass : IntermediateNodePassBase, IRazorDirectiveClassifierPass
|
||||
internal class ComponentLayoutDirectivePass : IntermediateNodePassBase, IRazorDirectiveClassifierPass
|
||||
{
|
||||
protected override void ExecuteCore(RazorCodeDocument codeDocument, DocumentIntermediateNode documentNode)
|
||||
{
|
||||
|
|
@ -18,7 +18,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
return;
|
||||
}
|
||||
|
||||
var directives = documentNode.FindDirectiveReferences(LayoutDirective.Directive);
|
||||
var directives = documentNode.FindDirectiveReferences(ComponentLayoutDirective.Directive);
|
||||
if (directives.Count == 0)
|
||||
{
|
||||
return;
|
||||
|
|
@ -7,13 +7,18 @@ using Microsoft.AspNetCore.Razor.Language.Intermediate;
|
|||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Components
|
||||
{
|
||||
internal class ComponentLoweringPass : IntermediateNodePassBase, IRazorOptimizationPass
|
||||
internal class ComponentLoweringPass : ComponentIntermediateNodePassBase, IRazorOptimizationPass
|
||||
{
|
||||
// This pass runs earlier than our other passes that 'lower' specific kinds of attributes.
|
||||
public override int Order => 0;
|
||||
|
||||
protected override void ExecuteCore(RazorCodeDocument codeDocument, DocumentIntermediateNode documentNode)
|
||||
{
|
||||
if (!IsComponentDocument(documentNode))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var @namespace = documentNode.FindPrimaryNamespace();
|
||||
var @class = documentNode.FindPrimaryClass();
|
||||
if (@namespace == null || @class == null)
|
||||
|
|
|
|||
|
|
@ -10,23 +10,9 @@ using Microsoft.AspNetCore.Razor.Language.Intermediate;
|
|||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Components
|
||||
{
|
||||
internal abstract class BlazorNodeWriter : IntermediateNodeWriter
|
||||
internal abstract class ComponentNodeWriter : IntermediateNodeWriter, ITemplateTargetExtension
|
||||
{
|
||||
public abstract void BeginWriteAttribute(CodeWriter codeWriter, string key);
|
||||
|
||||
public abstract void WriteComponent(CodeRenderingContext context, ComponentIntermediateNode node);
|
||||
|
||||
public abstract void WriteComponentAttribute(CodeRenderingContext context, ComponentAttributeIntermediateNode node);
|
||||
|
||||
public abstract void WriteComponentChildContent(CodeRenderingContext context, ComponentChildContentIntermediateNode node);
|
||||
|
||||
public abstract void WriteComponentTypeArgument(CodeRenderingContext context, ComponentTypeArgumentIntermediateNode node);
|
||||
|
||||
public abstract void WriteHtmlElement(CodeRenderingContext context, MarkupElementIntermediateNode node);
|
||||
|
||||
public abstract void WriteHtmlBlock(CodeRenderingContext context, MarkupBlockIntermediateNode node);
|
||||
|
||||
public abstract void WriteReferenceCapture(CodeRenderingContext context, ReferenceCaptureIntermediateNode node);
|
||||
protected abstract void BeginWriteAttribute(CodeWriter codeWriter, string key);
|
||||
|
||||
protected abstract void WriteReferenceCaptureInnards(CodeRenderingContext context, ReferenceCaptureIntermediateNode node, bool shouldTypeCheck);
|
||||
|
||||
|
|
@ -53,9 +39,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
return;
|
||||
}
|
||||
|
||||
|
||||
// Currently the same for design time and runtime
|
||||
public void WriteComponentTypeInferenceMethod(CodeRenderingContext context, ComponentTypeInferenceMethodIntermediateNode node)
|
||||
public override void WriteComponentTypeInferenceMethod(CodeRenderingContext context, ComponentTypeInferenceMethodIntermediateNode node)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
|
|
@ -2,12 +2,11 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.AspNetCore.Razor.Language.Intermediate;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Components
|
||||
{
|
||||
internal class PageDirective
|
||||
internal class ComponentPageDirective
|
||||
{
|
||||
public static readonly DirectiveDescriptor Directive = DirectiveDescriptor.CreateDirective(
|
||||
"page",
|
||||
|
|
@ -19,7 +18,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
builder.Description = ComponentResources.PageDirective_Description;
|
||||
});
|
||||
|
||||
private PageDirective(string routeTemplate, IntermediateNode directiveNode)
|
||||
private ComponentPageDirective(string routeTemplate, IntermediateNode directiveNode)
|
||||
{
|
||||
RouteTemplate = routeTemplate;
|
||||
DirectiveNode = directiveNode;
|
||||
|
|
@ -36,8 +35,8 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
throw new ArgumentNullException(nameof(builder));
|
||||
}
|
||||
|
||||
builder.AddDirective(Directive);
|
||||
builder.Features.Add(new PageDirectivePass());
|
||||
builder.AddDirective(FileKinds.Component, Directive);
|
||||
builder.Features.Add(new ComponentPageDirectivePass());
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
|
|
@ -3,13 +3,11 @@
|
|||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.AspNetCore.Razor.Language.CodeGeneration;
|
||||
using Microsoft.AspNetCore.Razor.Language.Intermediate;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Components
|
||||
{
|
||||
internal class PageDirectivePass : IntermediateNodePassBase, IRazorDirectiveClassifierPass
|
||||
internal class ComponentPageDirectivePass : IntermediateNodePassBase, IRazorDirectiveClassifierPass
|
||||
{
|
||||
protected override void ExecuteCore(RazorCodeDocument codeDocument, DocumentIntermediateNode documentNode)
|
||||
{
|
||||
|
|
@ -30,7 +28,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
return;
|
||||
}
|
||||
|
||||
var directives = documentNode.FindDirectiveReferences(PageDirective.Directive);
|
||||
var directives = documentNode.FindDirectiveReferences(ComponentPageDirective.Directive);
|
||||
if (directives.Count == 0)
|
||||
{
|
||||
return;
|
||||
|
|
@ -1,18 +1,22 @@
|
|||
// 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.Language;
|
||||
using Microsoft.AspNetCore.Razor.Language.Intermediate;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Components
|
||||
{
|
||||
internal class ReferenceCaptureLoweringPass : IntermediateNodePassBase, IRazorOptimizationPass
|
||||
internal class ComponentReferenceCaptureLoweringPass : ComponentIntermediateNodePassBase, IRazorOptimizationPass
|
||||
{
|
||||
// Run after component lowering pass
|
||||
public override int Order => 50;
|
||||
|
||||
protected override void ExecuteCore(RazorCodeDocument codeDocument, DocumentIntermediateNode documentNode)
|
||||
{
|
||||
if (!IsComponentDocument(documentNode))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var @namespace = documentNode.FindPrimaryNamespace();
|
||||
var @class = documentNode.FindPrimaryClass();
|
||||
if (@namespace == null || @class == null)
|
||||
|
|
@ -15,7 +15,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
/// <summary>
|
||||
/// Generates the C# code corresponding to Razor source document contents.
|
||||
/// </summary>
|
||||
internal class BlazorRuntimeNodeWriter : BlazorNodeWriter
|
||||
internal class ComponentRuntimeNodeWriter : ComponentNodeWriter
|
||||
{
|
||||
private readonly List<IntermediateToken> _currentAttributeValues = new List<IntermediateToken>();
|
||||
private readonly ScopeStack _scopeStack = new ScopeStack();
|
||||
|
|
@ -145,7 +145,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
}
|
||||
}
|
||||
|
||||
public override void WriteHtmlBlock(CodeRenderingContext context, MarkupBlockIntermediateNode node)
|
||||
public override void WriteMarkupBlock(CodeRenderingContext context, MarkupBlockIntermediateNode node)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
|
|
@ -165,7 +165,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
.WriteEndMethodInvocation();
|
||||
}
|
||||
|
||||
public override void WriteHtmlElement(CodeRenderingContext context, MarkupElementIntermediateNode node)
|
||||
public override void WriteMarkupElement(CodeRenderingContext context, MarkupElementIntermediateNode node)
|
||||
{
|
||||
if (context == null)
|
||||
{
|
||||
|
|
@ -641,7 +641,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void WriteAttribute(CodeWriter codeWriter, string key, IList<IntermediateToken> value)
|
||||
{
|
||||
BeginWriteAttribute(codeWriter, key);
|
||||
|
|
@ -649,7 +649,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
codeWriter.WriteEndMethodInvocation();
|
||||
}
|
||||
|
||||
public override void BeginWriteAttribute(CodeWriter codeWriter, string key)
|
||||
protected override void BeginWriteAttribute(CodeWriter codeWriter, string key)
|
||||
{
|
||||
codeWriter
|
||||
.WriteStartMethodInvocation($"{_scopeStack.BuilderVarName}.{nameof(ComponentsApi.RenderTreeBuilder.AddAttribute)}")
|
||||
|
|
@ -6,14 +6,14 @@ using Microsoft.AspNetCore.Razor.Language.Intermediate;
|
|||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Components
|
||||
{
|
||||
internal class ScriptTagPass : IntermediateNodePassBase, IRazorDocumentClassifierPass
|
||||
internal class ComponentScriptTagPass : ComponentIntermediateNodePassBase, IRazorDocumentClassifierPass
|
||||
{
|
||||
// Run as soon as possible after the Component rewrite pass
|
||||
public override int Order => ComponentDocumentClassifierPass.DefaultFeatureOrder + 2;
|
||||
public override int Order => 5;
|
||||
|
||||
protected override void ExecuteCore(RazorCodeDocument codeDocument, DocumentIntermediateNode documentNode)
|
||||
{
|
||||
if (documentNode.DocumentKind != ComponentDocumentClassifierPass.ComponentDocumentKind)
|
||||
if (!IsComponentDocument(documentNode))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
@ -22,9 +22,9 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
visitor.Visit(documentNode);
|
||||
}
|
||||
|
||||
private class Visitor : IntermediateNodeWalker, IExtensionIntermediateNodeVisitor<MarkupElementIntermediateNode>
|
||||
private class Visitor : IntermediateNodeWalker
|
||||
{
|
||||
public void VisitExtension(MarkupElementIntermediateNode node)
|
||||
public override void VisitMarkupElement(MarkupElementIntermediateNode node)
|
||||
{
|
||||
// Disallow <script> in components as per #552
|
||||
if (string.Equals(node.TagName, "script", StringComparison.OrdinalIgnoreCase))
|
||||
|
|
@ -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.AspNetCore.Razor.Language;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Razor.Language.Extensions;
|
||||
using Microsoft.AspNetCore.Razor.Language.Intermediate;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Components
|
||||
{
|
||||
internal class TemplateDiagnosticPass : IntermediateNodePassBase, IRazorOptimizationPass
|
||||
internal class ComponentTemplateDiagnosticPass : ComponentIntermediateNodePassBase, IRazorOptimizationPass
|
||||
{
|
||||
// Runs after components/eventhandlers/ref/bind. We need to check for templates in all of those
|
||||
// places.
|
||||
|
|
@ -16,6 +15,11 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
|
||||
protected override void ExecuteCore(RazorCodeDocument codeDocument, DocumentIntermediateNode documentNode)
|
||||
{
|
||||
if (!IsComponentDocument(documentNode))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var visitor = new Visitor();
|
||||
visitor.Visit(documentNode);
|
||||
|
||||
|
|
@ -6,11 +6,13 @@ using Microsoft.AspNetCore.Razor.Language.Extensions;
|
|||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Components
|
||||
{
|
||||
internal class BlazorTemplateTargetExtension : ITemplateTargetExtension
|
||||
internal class ComponentTemplateTargetExtension : ITemplateTargetExtension
|
||||
{
|
||||
public void WriteTemplate(CodeRenderingContext context, TemplateIntermediateNode node)
|
||||
{
|
||||
((BlazorNodeWriter)context.NodeWriter).WriteTemplate(context, node);
|
||||
// This is OK because this will only be plugged in by the component code target
|
||||
// not globally.
|
||||
((ComponentNodeWriter)context.NodeWriter).WriteTemplate(context, node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,11 +2,10 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Components
|
||||
{
|
||||
internal class TypeParamDirective
|
||||
internal class ComponentTypeParamDirective
|
||||
{
|
||||
public static readonly DirectiveDescriptor Directive = DirectiveDescriptor.CreateDirective(
|
||||
"typeparam",
|
||||
|
|
@ -25,7 +24,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
throw new ArgumentNullException(nameof(builder));
|
||||
}
|
||||
|
||||
builder.AddDirective(Directive);
|
||||
builder.AddDirective(FileKinds.Component, Directive);
|
||||
return builder;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,13 +1,12 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.AspNetCore.Razor.Language.Intermediate;
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Razor.Language.Intermediate;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Components
|
||||
{
|
||||
internal class TrimWhitespacePass : IntermediateNodePassBase, IRazorDirectiveClassifierPass
|
||||
internal class ComponentWhitespacePass : ComponentIntermediateNodePassBase, IRazorDirectiveClassifierPass
|
||||
{
|
||||
protected override void ExecuteCore(RazorCodeDocument codeDocument, DocumentIntermediateNode documentNode)
|
||||
{
|
||||
|
|
@ -21,15 +20,29 @@ namespace Microsoft.AspNetCore.Razor.Language.Components
|
|||
throw new ArgumentNullException(nameof(documentNode));
|
||||
}
|
||||
|
||||
// There's no benefit running the whitespace trimmer during design-time builds
|
||||
if (!documentNode.Options.DesignTime)
|
||||
if (!IsComponentDocument(documentNode))
|
||||
{
|
||||
var method = documentNode.FindPrimaryMethod();
|
||||
if (method != null)
|
||||
{
|
||||
RemoveContiguousWhitespace(method.Children, TraversalDirection.Forwards);
|
||||
RemoveContiguousWhitespace(method.Children, TraversalDirection.Backwards);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (documentNode.Options.SuppressPrimaryMethodBody)
|
||||
{
|
||||
// There's no benefit running the whitespace trimmer if we're not emitting
|
||||
// the method bodies.
|
||||
return;
|
||||
}
|
||||
|
||||
// There's no benefit running the whitespace trimmer during design-time builds
|
||||
if (documentNode.Options.DesignTime)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var method = documentNode.FindPrimaryMethod();
|
||||
if (method != null)
|
||||
{
|
||||
RemoveContiguousWhitespace(method.Children, TraversalDirection.Forwards);
|
||||
RemoveContiguousWhitespace(method.Children, TraversalDirection.Backwards);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,111 +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 System.Linq;
|
||||
using Microsoft.AspNetCore.Razor.Language.CodeGeneration;
|
||||
using Microsoft.AspNetCore.Razor.Language.Intermediate;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Components
|
||||
{
|
||||
// Much of the following is equivalent to Microsoft.AspNetCore.Mvc.Razor.Extensions's InjectDirective,
|
||||
// but this one outputs properties annotated for Blazor's property injector, plus it doesn't need to
|
||||
// support multiple CodeTargets.
|
||||
|
||||
internal class InjectDirective
|
||||
{
|
||||
public static readonly DirectiveDescriptor Directive = DirectiveDescriptor.CreateDirective(
|
||||
"inject",
|
||||
DirectiveKind.SingleLine,
|
||||
builder =>
|
||||
{
|
||||
builder.AddTypeToken("TypeName", "The type of the service to inject.");
|
||||
builder.AddMemberToken("PropertyName", "The name of the property.");
|
||||
builder.Usage = DirectiveUsage.FileScopedMultipleOccurring;
|
||||
builder.Description = "Inject a service from the application's service container into a property.";
|
||||
});
|
||||
|
||||
public static void Register(RazorProjectEngineBuilder builder)
|
||||
{
|
||||
builder.AddDirective(Directive);
|
||||
builder.Features.Add(new Pass());
|
||||
}
|
||||
|
||||
private class Pass : IntermediateNodePassBase, IRazorDirectiveClassifierPass
|
||||
{
|
||||
protected override void ExecuteCore(
|
||||
RazorCodeDocument codeDocument,
|
||||
DocumentIntermediateNode documentNode)
|
||||
{
|
||||
var visitor = new Visitor();
|
||||
visitor.Visit(documentNode);
|
||||
|
||||
var properties = new HashSet<string>(StringComparer.Ordinal);
|
||||
var classNode = documentNode.FindPrimaryClass();
|
||||
|
||||
for (var i = visitor.Directives.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var directive = visitor.Directives[i];
|
||||
var tokens = directive.Tokens.ToArray();
|
||||
if (tokens.Length < 2)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var typeName = tokens[0].Content;
|
||||
var memberName = tokens[1].Content;
|
||||
|
||||
if (!properties.Add(memberName))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
classNode.Children.Add(new InjectIntermediateNode(typeName, memberName));
|
||||
}
|
||||
}
|
||||
|
||||
private class Visitor : IntermediateNodeWalker
|
||||
{
|
||||
public IList<DirectiveIntermediateNode> Directives { get; }
|
||||
= new List<DirectiveIntermediateNode>();
|
||||
|
||||
public override void VisitDirective(DirectiveIntermediateNode node)
|
||||
{
|
||||
if (node.Directive == Directive)
|
||||
{
|
||||
Directives.Add(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class InjectIntermediateNode : ExtensionIntermediateNode
|
||||
{
|
||||
private static readonly IList<string> _injectedPropertyModifiers = new[]
|
||||
{
|
||||
$"[global::{ComponentsApi.InjectAttribute.FullTypeName}]",
|
||||
"private" // Encapsulation is the default
|
||||
};
|
||||
|
||||
public string TypeName { get; }
|
||||
public string MemberName { get; }
|
||||
public override IntermediateNodeCollection Children => IntermediateNodeCollection.ReadOnly;
|
||||
|
||||
public InjectIntermediateNode(string typeName, string memberName)
|
||||
{
|
||||
TypeName = typeName;
|
||||
MemberName = memberName;
|
||||
}
|
||||
|
||||
public override void Accept(IntermediateNodeVisitor visitor)
|
||||
=> AcceptExtensionNode(this, visitor);
|
||||
|
||||
public override void WriteNode(CodeTarget target, CodeRenderingContext context)
|
||||
=> context.CodeWriter.WriteAutoPropertyDeclaration(
|
||||
_injectedPropertyModifiers,
|
||||
TypeName,
|
||||
MemberName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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.
|
||||
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using System;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a fatal error during the transformation of a Blazor component from
|
||||
/// Razor source code to C# source code.
|
||||
/// </summary>
|
||||
public class RazorCompilerException : Exception
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructs an instance of <see cref="RazorCompilerException"/>.
|
||||
/// </summary>
|
||||
/// <param name="diagnostic"></param>
|
||||
public RazorCompilerException(RazorDiagnostic diagnostic)
|
||||
{
|
||||
Diagnostic = diagnostic;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the diagnostic value.
|
||||
/// </summary>
|
||||
public RazorDiagnostic Diagnostic { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -9,12 +9,15 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
{
|
||||
private bool _designTime;
|
||||
|
||||
public DefaultRazorCodeGenerationOptionsBuilder(RazorConfiguration configuration)
|
||||
public DefaultRazorCodeGenerationOptionsBuilder(RazorConfiguration configuration, string fileKind)
|
||||
{
|
||||
if (configuration == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(configuration));
|
||||
}
|
||||
|
||||
Configuration = configuration;
|
||||
FileKind = fileKind;
|
||||
}
|
||||
|
||||
public DefaultRazorCodeGenerationOptionsBuilder(bool designTime)
|
||||
|
|
@ -26,6 +29,8 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
|
||||
public override bool DesignTime => _designTime;
|
||||
|
||||
public override string FileKind { get; }
|
||||
|
||||
public override int IndentSize { get; set; } = 4;
|
||||
|
||||
public override bool IndentWithTabs { get; set; }
|
||||
|
|
|
|||
|
|
@ -15,9 +15,9 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
_configureOptions = ProjectEngine.EngineFeatures.OfType<IConfigureRazorCodeGenerationOptionsFeature>().ToArray();
|
||||
}
|
||||
|
||||
public RazorCodeGenerationOptions Create(Action<RazorCodeGenerationOptionsBuilder> configure)
|
||||
public RazorCodeGenerationOptions Create(string fileKind, Action<RazorCodeGenerationOptionsBuilder> configure)
|
||||
{
|
||||
var builder = new DefaultRazorCodeGenerationOptionsBuilder(ProjectEngine.Configuration);
|
||||
var builder = new DefaultRazorCodeGenerationOptionsBuilder(ProjectEngine.Configuration, fileKind);
|
||||
configure?.Invoke(builder);
|
||||
|
||||
for (var i = 0; i < _configureOptions.Length; i++)
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language
|
||||
{
|
||||
|
|
@ -10,6 +11,8 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
{
|
||||
public ICollection<DirectiveDescriptor> Directives { get; } = new List<DirectiveDescriptor>();
|
||||
|
||||
public IDictionary<string, ICollection<DirectiveDescriptor>> DirectivesByFileKind { get; } = new Dictionary<string, ICollection<DirectiveDescriptor>>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
public int Order => 100;
|
||||
|
||||
void IConfigureRazorParserOptionsFeature.Configure(RazorParserOptionsBuilder options)
|
||||
|
|
@ -25,6 +28,21 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
{
|
||||
options.Directives.Add(directive);
|
||||
}
|
||||
|
||||
if (options.FileKind != null && DirectivesByFileKind.TryGetValue(options.FileKind, out var directives))
|
||||
{
|
||||
foreach (var directive in directives)
|
||||
{
|
||||
// Replace any non-file-kind-specific directives
|
||||
var replaces = options.Directives.Where(d => string.Equals(d.Directive, directive.Directive, StringComparison.Ordinal)).ToArray();
|
||||
foreach (var replace in replaces)
|
||||
{
|
||||
options.Directives.Remove(replace);
|
||||
}
|
||||
|
||||
options.Directives.Add(directive);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,32 +35,34 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
|
||||
document.Options = codeDocument.GetCodeGenerationOptions() ?? _optionsFeature.GetOptions();
|
||||
|
||||
IReadOnlyList<UsingReference> importedUsings = Array.Empty<UsingReference>();
|
||||
|
||||
// The import documents should be inserted logically before the main document.
|
||||
var imports = codeDocument.GetImportSyntaxTrees();
|
||||
if (imports != null)
|
||||
{
|
||||
var importsVisitor = new ImportsVisitor(document, builder, syntaxTree.Options.FeatureFlags);
|
||||
var importedUsings = ImportDirectives(document, builder, syntaxTree.Options, imports);
|
||||
|
||||
for (var j = 0; j < imports.Count; j++)
|
||||
// Lower the main document, appending after the imported directives.
|
||||
//
|
||||
// We need to decide up front if this document is a "component" file. This will affect how
|
||||
// lowering behaves.
|
||||
LoweringVisitor visitor;
|
||||
if (string.Equals(FileKinds.Component, codeDocument.GetFileKind(), StringComparison.OrdinalIgnoreCase) &&
|
||||
syntaxTree.Options.FeatureFlags.AllowComponentFileKind)
|
||||
{
|
||||
visitor = new ComponentFileKindVisitor(document, builder, syntaxTree.Options.FeatureFlags)
|
||||
{
|
||||
var import = imports[j];
|
||||
SourceDocument = syntaxTree.Source,
|
||||
};
|
||||
|
||||
importsVisitor.SourceDocument = import.Source;
|
||||
importsVisitor.Visit(import.Root);
|
||||
}
|
||||
|
||||
importedUsings = importsVisitor.Usings;
|
||||
visitor.Visit(syntaxTree.Root);
|
||||
}
|
||||
|
||||
var tagHelperPrefix = tagHelperContext?.Prefix;
|
||||
var visitor = new MainSourceVisitor(document, builder, tagHelperPrefix, syntaxTree.Options.FeatureFlags)
|
||||
else
|
||||
{
|
||||
SourceDocument = syntaxTree.Source,
|
||||
};
|
||||
visitor = new LegacyFileKindVisitor(document, builder, tagHelperContext?.Prefix, syntaxTree.Options.FeatureFlags)
|
||||
{
|
||||
SourceDocument = syntaxTree.Source,
|
||||
};
|
||||
|
||||
visitor.Visit(syntaxTree.Root);
|
||||
visitor.Visit(syntaxTree.Root);
|
||||
}
|
||||
|
||||
// 1. Prioritize non-imported usings over imported ones.
|
||||
// 2. Don't import usings that already exist in primary document.
|
||||
|
|
@ -76,7 +78,6 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
|
||||
// In each lowering piece above, namespaces were tracked. We render them here to ensure every
|
||||
// lowering action has a chance to add a source location to a namespace. Ultimately, closest wins.
|
||||
|
||||
var i = 0;
|
||||
foreach (var reference in usingReferences)
|
||||
{
|
||||
|
|
@ -89,7 +90,7 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
builder.Insert(i++, @using);
|
||||
}
|
||||
|
||||
ImportDirectives(document);
|
||||
PostProcessImportedDirectives(document);
|
||||
|
||||
// The document should contain all errors that currently exist in the system. This involves
|
||||
// adding the errors from the primary and imported syntax trees.
|
||||
|
|
@ -113,15 +114,36 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
codeDocument.SetDocumentIntermediateNode(document);
|
||||
}
|
||||
|
||||
private void ImportDirectives(DocumentIntermediateNode document)
|
||||
private IReadOnlyList<UsingReference> ImportDirectives(
|
||||
DocumentIntermediateNode document,
|
||||
IntermediateNodeBuilder builder,
|
||||
RazorParserOptions options,
|
||||
IReadOnlyList<RazorSyntaxTree> imports)
|
||||
{
|
||||
var visitor = new DirectiveVisitor();
|
||||
visitor.VisitDocument(document);
|
||||
|
||||
var seenDirectives = new HashSet<DirectiveDescriptor>();
|
||||
for (var i = visitor.Directives.Count - 1; i >= 0; i--)
|
||||
if (imports == null)
|
||||
{
|
||||
var reference = visitor.Directives[i];
|
||||
return Array.Empty<UsingReference>();
|
||||
}
|
||||
|
||||
var importsVisitor = new ImportsVisitor(document, builder, options.FeatureFlags);
|
||||
for (var i = 0; i < imports.Count; i++)
|
||||
{
|
||||
var import = imports[i];
|
||||
|
||||
importsVisitor.SourceDocument = import.Source;
|
||||
importsVisitor.Visit(import.Root);
|
||||
}
|
||||
|
||||
return importsVisitor.Usings;
|
||||
}
|
||||
|
||||
private void PostProcessImportedDirectives(DocumentIntermediateNode document)
|
||||
{
|
||||
var directives = document.FindDescendantReferences<DirectiveIntermediateNode>();
|
||||
var seenDirectives = new HashSet<DirectiveDescriptor>();
|
||||
for (var i = directives.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var reference = directives[i];
|
||||
var directive = (DirectiveIntermediateNode)reference.Node;
|
||||
var descriptor = directive.Directive;
|
||||
var seenDirective = !seenDirectives.Add(descriptor);
|
||||
|
|
@ -134,24 +156,29 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
switch (descriptor.Kind)
|
||||
{
|
||||
case DirectiveKind.SingleLine:
|
||||
if (seenDirective && descriptor.Usage == DirectiveUsage.FileScopedSinglyOccurring)
|
||||
{
|
||||
// This directive has been overridden, it should be removed from the document.
|
||||
if (seenDirective && descriptor.Usage == DirectiveUsage.FileScopedSinglyOccurring)
|
||||
{
|
||||
// This directive has been overridden, it should be removed from the document.
|
||||
break;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
case DirectiveKind.RazorBlock:
|
||||
case DirectiveKind.CodeBlock:
|
||||
{
|
||||
if (descriptor.Usage == DirectiveUsage.FileScopedSinglyOccurring)
|
||||
{
|
||||
// A block directive cannot be imported.
|
||||
document.Diagnostics.Add(
|
||||
RazorDiagnosticFactory.CreateDirective_BlockDirectiveCannotBeImported(descriptor.Directive));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
continue;
|
||||
case DirectiveKind.RazorBlock:
|
||||
case DirectiveKind.CodeBlock:
|
||||
if (descriptor.Usage == DirectiveUsage.FileScopedSinglyOccurring)
|
||||
{
|
||||
// A block directive cannot be imported.
|
||||
|
||||
document.Diagnostics.Add(
|
||||
RazorDiagnosticFactory.CreateDirective_BlockDirectiveCannotBeImported(descriptor.Directive));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new InvalidOperationException(Resources.FormatUnexpectedDirectiveKind(typeof(DirectiveKind).FullName));
|
||||
}
|
||||
|
|
@ -406,12 +433,13 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
}
|
||||
}
|
||||
|
||||
private class MainSourceVisitor : LoweringVisitor
|
||||
// Lowers a document using *html-as-text* and Tag Helpers
|
||||
private class LegacyFileKindVisitor : LoweringVisitor
|
||||
{
|
||||
private readonly HashSet<string> _renderedBoundAttributeNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
private readonly string _tagHelperPrefix;
|
||||
|
||||
public MainSourceVisitor(DocumentIntermediateNode document, IntermediateNodeBuilder builder, string tagHelperPrefix, RazorParserFeatureFlags featureFlags)
|
||||
public LegacyFileKindVisitor(DocumentIntermediateNode document, IntermediateNodeBuilder builder, string tagHelperPrefix, RazorParserFeatureFlags featureFlags)
|
||||
: base(document, builder, featureFlags)
|
||||
{
|
||||
_tagHelperPrefix = tagHelperPrefix;
|
||||
|
|
@ -1057,6 +1085,665 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
}
|
||||
}
|
||||
|
||||
// Lowers a document using *html-as-nodes* and Components
|
||||
private class ComponentFileKindVisitor : LoweringVisitor
|
||||
{
|
||||
private readonly HashSet<string> _renderedBoundAttributeNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
public ComponentFileKindVisitor(
|
||||
DocumentIntermediateNode document,
|
||||
IntermediateNodeBuilder builder,
|
||||
RazorParserFeatureFlags featureFlags)
|
||||
: base(document, builder, featureFlags)
|
||||
{
|
||||
}
|
||||
|
||||
public override void DefaultVisit(SyntaxNode node)
|
||||
{
|
||||
base.DefaultVisit(node);
|
||||
}
|
||||
|
||||
public override void VisitMarkupElement(MarkupElementSyntax node)
|
||||
{
|
||||
_builder.Push(new MarkupElementIntermediateNode()
|
||||
{
|
||||
Source = BuildSourceSpanFromNode(node),
|
||||
|
||||
// Could be empty while the tag is being typed in.
|
||||
TagName = node.StartTag?.GetTagName() ?? node.EndTag?.GetTagName() ?? string.Empty,
|
||||
});
|
||||
|
||||
base.VisitMarkupElement(node);
|
||||
|
||||
_builder.Pop();
|
||||
}
|
||||
|
||||
public override void VisitMarkupStartTag(MarkupStartTagSyntax node)
|
||||
{
|
||||
// We want to skip over the other misc tokens that make up a start tag, and
|
||||
// just process the attributes.
|
||||
//
|
||||
// Visit the attributes
|
||||
foreach (var block in node.Children)
|
||||
{
|
||||
if (block is MarkupAttributeBlockSyntax attribute)
|
||||
{
|
||||
VisitMarkupAttributeBlock(attribute);
|
||||
}
|
||||
else if (block is MarkupMinimizedAttributeBlockSyntax minimized)
|
||||
{
|
||||
VisitMarkupMinimizedAttributeBlock(minimized);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void VisitMarkupEndTag(MarkupEndTagSyntax node)
|
||||
{
|
||||
// We want to skip over the other misc tokens that make up a start tag, and
|
||||
// just process the attributes.
|
||||
//
|
||||
// Nothing to do here
|
||||
}
|
||||
|
||||
// Example
|
||||
// <input` checked="hello-world @false"`/>
|
||||
// Name=checked
|
||||
// Prefix= checked="
|
||||
// Suffix="
|
||||
public override void VisitMarkupAttributeBlock(MarkupAttributeBlockSyntax node)
|
||||
{
|
||||
// For now we're using HtmlAttributeIntermediateNode for these so we're still
|
||||
// building Prefix and Suffix, even though we don't really use them. If we
|
||||
// end up using another node type in the future this can be simplified quite
|
||||
// a lot.
|
||||
var prefixTokens = MergeLiterals(
|
||||
node.NamePrefix?.LiteralTokens,
|
||||
node.Name.LiteralTokens,
|
||||
node.NameSuffix?.LiteralTokens,
|
||||
node.EqualsToken == null ? new SyntaxList<SyntaxToken>() : new SyntaxList<SyntaxToken>(node.EqualsToken),
|
||||
node.ValuePrefix?.LiteralTokens);
|
||||
var prefix = (MarkupTextLiteralSyntax)SyntaxFactory.MarkupTextLiteral(prefixTokens).Green.CreateRed(node, node.NamePrefix?.Position ?? node.Name.Position);
|
||||
|
||||
var name = node.Name.GetContent();
|
||||
_builder.Push(new HtmlAttributeIntermediateNode()
|
||||
{
|
||||
AttributeName = node.Name.GetContent(),
|
||||
Prefix = prefix.GetContent(),
|
||||
Suffix = node.ValueSuffix?.GetContent() ?? string.Empty,
|
||||
Source = BuildSourceSpanFromNode(node),
|
||||
});
|
||||
|
||||
Visit(node.Value);
|
||||
|
||||
_builder.Pop();
|
||||
}
|
||||
|
||||
public override void VisitMarkupMinimizedAttributeBlock(MarkupMinimizedAttributeBlockSyntax node)
|
||||
{
|
||||
var prefixTokens = MergeLiterals(node.NamePrefix?.LiteralTokens, node.Name.LiteralTokens);
|
||||
var prefix = (MarkupTextLiteralSyntax)SyntaxFactory.MarkupTextLiteral(prefixTokens).Green.CreateRed(node, node.NamePrefix?.Position ?? node.Name.Position);
|
||||
|
||||
var name = node.Name.GetContent();
|
||||
_builder.Add(new HtmlAttributeIntermediateNode()
|
||||
{
|
||||
AttributeName = node.Name.GetContent(),
|
||||
Prefix = prefix.GetContent(),
|
||||
Suffix = null,
|
||||
Source = BuildSourceSpanFromNode(node),
|
||||
});
|
||||
}
|
||||
|
||||
// Example
|
||||
// <input checked="hello-world `@false`"/>
|
||||
// Prefix= (space)
|
||||
// Children will contain a token for @false.
|
||||
public override void VisitMarkupDynamicAttributeValue(MarkupDynamicAttributeValueSyntax node)
|
||||
{
|
||||
var containsExpression = false;
|
||||
var descendantNodes = node.DescendantNodes(n =>
|
||||
{
|
||||
// Don't go into sub block. They may contain expressions but we only care about the top level.
|
||||
return !(n.Parent is CSharpCodeBlockSyntax);
|
||||
});
|
||||
foreach (var child in descendantNodes)
|
||||
{
|
||||
if (child is CSharpImplicitExpressionSyntax || child is CSharpExplicitExpressionSyntax)
|
||||
{
|
||||
containsExpression = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (containsExpression)
|
||||
{
|
||||
_builder.Push(new CSharpExpressionAttributeValueIntermediateNode()
|
||||
{
|
||||
Prefix = node.Prefix?.GetContent() ?? string.Empty,
|
||||
Source = BuildSourceSpanFromNode(node),
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
_builder.Push(new CSharpCodeAttributeValueIntermediateNode()
|
||||
{
|
||||
Prefix = node.Prefix?.GetContent() ?? string.Empty,
|
||||
Source = BuildSourceSpanFromNode(node),
|
||||
});
|
||||
}
|
||||
|
||||
Visit(node.Value);
|
||||
|
||||
_builder.Pop();
|
||||
}
|
||||
|
||||
public override void VisitMarkupLiteralAttributeValue(MarkupLiteralAttributeValueSyntax node)
|
||||
{
|
||||
_builder.Push(new HtmlAttributeValueIntermediateNode()
|
||||
{
|
||||
Prefix = node.Prefix?.GetContent() ?? string.Empty,
|
||||
Source = BuildSourceSpanFromNode(node),
|
||||
});
|
||||
|
||||
_builder.Add(new IntermediateToken()
|
||||
{
|
||||
Content = node.Value?.GetContent() ?? string.Empty,
|
||||
Kind = TokenKind.Html,
|
||||
Source = BuildSourceSpanFromNode(node.Value)
|
||||
});
|
||||
|
||||
_builder.Pop();
|
||||
}
|
||||
|
||||
public override void VisitMarkupTextLiteral(MarkupTextLiteralSyntax node)
|
||||
{
|
||||
var context = node.GetSpanContext();
|
||||
if (context != null && context.ChunkGenerator == SpanChunkGenerator.Null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (node.LiteralTokens.Count == 1)
|
||||
{
|
||||
var token = node.LiteralTokens[0];
|
||||
if (token != null &&
|
||||
token.Kind == SyntaxKind.Marker &&
|
||||
token.Content.Length == 0)
|
||||
{
|
||||
// We don't want to create IR nodes for marker tokens.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Combine chunks of HTML literal text if possible.
|
||||
var source = BuildSourceSpanFromNode(node);
|
||||
var currentChildren = _builder.Current.Children;
|
||||
if (currentChildren.Count > 0 &&
|
||||
currentChildren[currentChildren.Count - 1] is HtmlContentIntermediateNode existingHtmlContent)
|
||||
{
|
||||
if (existingHtmlContent.Source == null && source == null)
|
||||
{
|
||||
Combine(existingHtmlContent, node);
|
||||
return;
|
||||
}
|
||||
|
||||
if (source != null &&
|
||||
existingHtmlContent.Source != null &&
|
||||
existingHtmlContent.Source.Value.FilePath == source.Value.FilePath &&
|
||||
existingHtmlContent.Source.Value.AbsoluteIndex + existingHtmlContent.Source.Value.Length == source.Value.AbsoluteIndex)
|
||||
{
|
||||
Combine(existingHtmlContent, node);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_builder.Add(new HtmlContentIntermediateNode()
|
||||
{
|
||||
Source = source,
|
||||
Children =
|
||||
{
|
||||
new IntermediateToken()
|
||||
{
|
||||
Content = node.GetContent(),
|
||||
Kind = TokenKind.Html,
|
||||
Source = source,
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public override void VisitCSharpTemplateBlock(CSharpTemplateBlockSyntax node)
|
||||
{
|
||||
var templateNode = new TemplateIntermediateNode();
|
||||
_builder.Push(templateNode);
|
||||
|
||||
base.VisitCSharpTemplateBlock(node);
|
||||
|
||||
_builder.Pop();
|
||||
|
||||
if (templateNode.Children.Count > 0)
|
||||
{
|
||||
var sourceRangeStart = templateNode
|
||||
.Children
|
||||
.FirstOrDefault(child => child.Source != null)
|
||||
?.Source;
|
||||
|
||||
if (sourceRangeStart != null)
|
||||
{
|
||||
var contentLength = templateNode.Children.Sum(child => child.Source?.Length ?? 0);
|
||||
|
||||
templateNode.Source = new SourceSpan(
|
||||
sourceRangeStart.Value.FilePath ?? SourceDocument.FilePath,
|
||||
sourceRangeStart.Value.AbsoluteIndex,
|
||||
sourceRangeStart.Value.LineIndex,
|
||||
sourceRangeStart.Value.CharacterIndex,
|
||||
contentLength);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CSharp expressions are broken up into blocks and spans because Razor allows Razor comments
|
||||
// inside an expression.
|
||||
// Ex:
|
||||
// @DateTime.@*This is a comment*@Now
|
||||
//
|
||||
// We need to capture this in the IR so that we can give each piece the correct source mappings
|
||||
public override void VisitCSharpExplicitExpression(CSharpExplicitExpressionSyntax node)
|
||||
{
|
||||
if (_builder.Current is CSharpExpressionAttributeValueIntermediateNode)
|
||||
{
|
||||
base.VisitCSharpExplicitExpression(node);
|
||||
return;
|
||||
}
|
||||
|
||||
var expressionNode = new CSharpExpressionIntermediateNode();
|
||||
|
||||
_builder.Push(expressionNode);
|
||||
|
||||
base.VisitCSharpExplicitExpression(node);
|
||||
|
||||
_builder.Pop();
|
||||
|
||||
if (expressionNode.Children.Count > 0)
|
||||
{
|
||||
var sourceRangeStart = expressionNode
|
||||
.Children
|
||||
.FirstOrDefault(child => child.Source != null)
|
||||
?.Source;
|
||||
|
||||
if (sourceRangeStart != null)
|
||||
{
|
||||
var contentLength = expressionNode.Children.Sum(child => child.Source?.Length ?? 0);
|
||||
|
||||
expressionNode.Source = new SourceSpan(
|
||||
sourceRangeStart.Value.FilePath ?? SourceDocument.FilePath,
|
||||
sourceRangeStart.Value.AbsoluteIndex,
|
||||
sourceRangeStart.Value.LineIndex,
|
||||
sourceRangeStart.Value.CharacterIndex,
|
||||
contentLength);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void VisitCSharpImplicitExpression(CSharpImplicitExpressionSyntax node)
|
||||
{
|
||||
if (_builder.Current is CSharpExpressionAttributeValueIntermediateNode)
|
||||
{
|
||||
base.VisitCSharpImplicitExpression(node);
|
||||
return;
|
||||
}
|
||||
|
||||
var expressionNode = new CSharpExpressionIntermediateNode();
|
||||
|
||||
_builder.Push(expressionNode);
|
||||
|
||||
base.VisitCSharpImplicitExpression(node);
|
||||
|
||||
_builder.Pop();
|
||||
|
||||
if (expressionNode.Children.Count > 0)
|
||||
{
|
||||
var sourceRangeStart = expressionNode
|
||||
.Children
|
||||
.FirstOrDefault(child => child.Source != null)
|
||||
?.Source;
|
||||
|
||||
if (sourceRangeStart != null)
|
||||
{
|
||||
var contentLength = expressionNode.Children.Sum(child => child.Source?.Length ?? 0);
|
||||
|
||||
expressionNode.Source = new SourceSpan(
|
||||
sourceRangeStart.Value.FilePath ?? SourceDocument.FilePath,
|
||||
sourceRangeStart.Value.AbsoluteIndex,
|
||||
sourceRangeStart.Value.LineIndex,
|
||||
sourceRangeStart.Value.CharacterIndex,
|
||||
contentLength);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void VisitCSharpExpressionLiteral(CSharpExpressionLiteralSyntax node)
|
||||
{
|
||||
if (_builder.Current is TagHelperHtmlAttributeIntermediateNode)
|
||||
{
|
||||
// If we are top level in a tag helper HTML attribute, we want to be rendered as markup.
|
||||
// This case happens for duplicate non-string bound attributes. They would be initially be categorized as
|
||||
// CSharp but since they are duplicate, they should just be markup.
|
||||
var markupLiteral = SyntaxFactory.MarkupTextLiteral(node.LiteralTokens).Green.CreateRed(node.Parent, node.Position);
|
||||
Visit(markupLiteral);
|
||||
return;
|
||||
}
|
||||
|
||||
_builder.Add(new IntermediateToken()
|
||||
{
|
||||
Content = node.GetContent(),
|
||||
Kind = TokenKind.CSharp,
|
||||
Source = BuildSourceSpanFromNode(node),
|
||||
});
|
||||
|
||||
base.VisitCSharpExpressionLiteral(node);
|
||||
}
|
||||
|
||||
public override void VisitCSharpStatementLiteral(CSharpStatementLiteralSyntax node)
|
||||
{
|
||||
var context = node.GetSpanContext();
|
||||
if (context == null || context.ChunkGenerator is StatementChunkGenerator)
|
||||
{
|
||||
var isAttributeValue = _builder.Current is CSharpCodeAttributeValueIntermediateNode;
|
||||
|
||||
if (!isAttributeValue)
|
||||
{
|
||||
var statementNode = new CSharpCodeIntermediateNode()
|
||||
{
|
||||
Source = BuildSourceSpanFromNode(node)
|
||||
};
|
||||
_builder.Push(statementNode);
|
||||
}
|
||||
|
||||
_builder.Add(new IntermediateToken()
|
||||
{
|
||||
Content = node.GetContent(),
|
||||
Kind = TokenKind.CSharp,
|
||||
Source = BuildSourceSpanFromNode(node),
|
||||
});
|
||||
|
||||
if (!isAttributeValue)
|
||||
{
|
||||
_builder.Pop();
|
||||
}
|
||||
}
|
||||
|
||||
base.VisitCSharpStatementLiteral(node);
|
||||
}
|
||||
|
||||
public override void VisitMarkupTagHelperElement(MarkupTagHelperElementSyntax node)
|
||||
{
|
||||
var info = node.TagHelperInfo;
|
||||
var tagName = info.TagName;
|
||||
var tagHelperNode = new TagHelperIntermediateNode()
|
||||
{
|
||||
TagName = tagName,
|
||||
TagMode = info.TagMode,
|
||||
Source = BuildSourceSpanFromNode(node)
|
||||
};
|
||||
|
||||
foreach (var tagHelper in info.BindingResult.Descriptors)
|
||||
{
|
||||
tagHelperNode.TagHelpers.Add(tagHelper);
|
||||
}
|
||||
|
||||
_builder.Push(tagHelperNode);
|
||||
|
||||
_builder.Push(new TagHelperBodyIntermediateNode());
|
||||
|
||||
foreach (var item in node.Body)
|
||||
{
|
||||
Visit(item);
|
||||
}
|
||||
|
||||
_builder.Pop(); // Pop InitializeTagHelperStructureIntermediateNode
|
||||
|
||||
Visit(node.StartTag);
|
||||
|
||||
_builder.Pop(); // Pop TagHelperIntermediateNode
|
||||
|
||||
// No need to visit the end tag because we don't write any IR for it.
|
||||
|
||||
// We don't want to track attributes from a previous tag helper element.
|
||||
_renderedBoundAttributeNames.Clear();
|
||||
}
|
||||
|
||||
public override void VisitMarkupTagHelperStartTag(MarkupTagHelperStartTagSyntax node)
|
||||
{
|
||||
foreach (var child in node.Children)
|
||||
{
|
||||
if (child is MarkupTagHelperAttributeSyntax || child is MarkupMinimizedTagHelperAttributeSyntax)
|
||||
{
|
||||
Visit(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void VisitMarkupMinimizedTagHelperAttribute(MarkupMinimizedTagHelperAttributeSyntax node)
|
||||
{
|
||||
if (!_featureFlags.AllowMinimizedBooleanTagHelperAttributes)
|
||||
{
|
||||
// Minimized attributes are not valid for non-boolean bound attributes. TagHelperBlockRewriter
|
||||
// has already logged an error if it was a non-boolean bound attribute; so we can skip.
|
||||
return;
|
||||
}
|
||||
|
||||
var element = node.FirstAncestorOrSelf<MarkupTagHelperElementSyntax>();
|
||||
var descriptors = element.TagHelperInfo.BindingResult.Descriptors;
|
||||
var attributeName = node.Name.GetContent();
|
||||
var associatedDescriptors = descriptors.Where(descriptor =>
|
||||
descriptor.BoundAttributes.Any(attributeDescriptor => TagHelperMatchingConventions.CanSatisfyBoundAttribute(attributeName, attributeDescriptor)));
|
||||
|
||||
if (associatedDescriptors.Any() && _renderedBoundAttributeNames.Add(attributeName))
|
||||
{
|
||||
foreach (var associatedDescriptor in associatedDescriptors)
|
||||
{
|
||||
var associatedAttributeDescriptor = associatedDescriptor.BoundAttributes.First(a =>
|
||||
{
|
||||
return TagHelperMatchingConventions.CanSatisfyBoundAttribute(attributeName, a);
|
||||
});
|
||||
|
||||
var expectsBooleanValue = associatedAttributeDescriptor.ExpectsBooleanValue(attributeName);
|
||||
|
||||
if (!expectsBooleanValue)
|
||||
{
|
||||
// We do not allow minimized non-boolean bound attributes.
|
||||
return;
|
||||
}
|
||||
|
||||
var setTagHelperProperty = new TagHelperPropertyIntermediateNode()
|
||||
{
|
||||
AttributeName = attributeName,
|
||||
BoundAttribute = associatedAttributeDescriptor,
|
||||
TagHelper = associatedDescriptor,
|
||||
AttributeStructure = node.TagHelperAttributeInfo.AttributeStructure,
|
||||
Source = null,
|
||||
IsIndexerNameMatch = TagHelperMatchingConventions.SatisfiesBoundAttributeIndexer(attributeName, associatedAttributeDescriptor),
|
||||
};
|
||||
|
||||
_builder.Add(setTagHelperProperty);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var addHtmlAttribute = new TagHelperHtmlAttributeIntermediateNode()
|
||||
{
|
||||
AttributeName = attributeName,
|
||||
AttributeStructure = node.TagHelperAttributeInfo.AttributeStructure
|
||||
};
|
||||
|
||||
_builder.Add(addHtmlAttribute);
|
||||
}
|
||||
}
|
||||
|
||||
public override void VisitMarkupTagHelperAttribute(MarkupTagHelperAttributeSyntax node)
|
||||
{
|
||||
var element = node.FirstAncestorOrSelf<MarkupTagHelperElementSyntax>();
|
||||
var descriptors = element.TagHelperInfo.BindingResult.Descriptors;
|
||||
var attributeName = node.Name.GetContent();
|
||||
var attributeValueNode = node.Value;
|
||||
var associatedDescriptors = descriptors.Where(descriptor =>
|
||||
descriptor.BoundAttributes.Any(attributeDescriptor => TagHelperMatchingConventions.CanSatisfyBoundAttribute(attributeName, attributeDescriptor)));
|
||||
|
||||
if (associatedDescriptors.Any() && _renderedBoundAttributeNames.Add(attributeName))
|
||||
{
|
||||
foreach (var associatedDescriptor in associatedDescriptors)
|
||||
{
|
||||
var associatedAttributeDescriptor = associatedDescriptor.BoundAttributes.First(a =>
|
||||
{
|
||||
return TagHelperMatchingConventions.CanSatisfyBoundAttribute(attributeName, a);
|
||||
});
|
||||
|
||||
var setTagHelperProperty = new TagHelperPropertyIntermediateNode()
|
||||
{
|
||||
AttributeName = attributeName,
|
||||
BoundAttribute = associatedAttributeDescriptor,
|
||||
TagHelper = associatedDescriptor,
|
||||
AttributeStructure = node.TagHelperAttributeInfo.AttributeStructure,
|
||||
Source = BuildSourceSpanFromNode(attributeValueNode),
|
||||
IsIndexerNameMatch = TagHelperMatchingConventions.SatisfiesBoundAttributeIndexer(attributeName, associatedAttributeDescriptor),
|
||||
};
|
||||
|
||||
_builder.Push(setTagHelperProperty);
|
||||
VisitAttributeValue(attributeValueNode);
|
||||
_builder.Pop();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var addHtmlAttribute = new TagHelperHtmlAttributeIntermediateNode()
|
||||
{
|
||||
AttributeName = attributeName,
|
||||
AttributeStructure = node.TagHelperAttributeInfo.AttributeStructure
|
||||
};
|
||||
|
||||
_builder.Push(addHtmlAttribute);
|
||||
VisitAttributeValue(attributeValueNode);
|
||||
_builder.Pop();
|
||||
}
|
||||
}
|
||||
|
||||
private void VisitAttributeValue(SyntaxNode node)
|
||||
{
|
||||
if (node == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
IReadOnlyList<SyntaxNode> children = node.ChildNodes();
|
||||
var position = node.Position;
|
||||
if (children.First() is MarkupBlockSyntax markupBlock &&
|
||||
markupBlock.Children.Count == 2 &&
|
||||
markupBlock.Children[0] is MarkupTextLiteralSyntax &&
|
||||
markupBlock.Children[1] is MarkupEphemeralTextLiteralSyntax)
|
||||
{
|
||||
// This is a special case when we have an attribute like attr="@@foo".
|
||||
// In this case, we want the foo to be written out as HtmlContent and not HtmlAttributeValue.
|
||||
Visit(markupBlock);
|
||||
children = children.Skip(1).ToList();
|
||||
position = children.Count > 0 ? children[0].Position : position;
|
||||
}
|
||||
|
||||
if (children.All(c => c is MarkupLiteralAttributeValueSyntax))
|
||||
{
|
||||
var literalAttributeValueNodes = children.Cast<MarkupLiteralAttributeValueSyntax>().ToArray();
|
||||
var valueTokens = SyntaxListBuilder<SyntaxToken>.Create();
|
||||
for (var i = 0; i < literalAttributeValueNodes.Length; i++)
|
||||
{
|
||||
var mergedValue = MergeAttributeValue(literalAttributeValueNodes[i]);
|
||||
valueTokens.AddRange(mergedValue.LiteralTokens);
|
||||
}
|
||||
var rewritten = SyntaxFactory.MarkupTextLiteral(valueTokens.ToList()).Green.CreateRed(node.Parent, position);
|
||||
Visit(rewritten);
|
||||
}
|
||||
else if (children.All(c => c is MarkupTextLiteralSyntax))
|
||||
{
|
||||
var builder = SyntaxListBuilder<SyntaxToken>.Create();
|
||||
var markupLiteralArray = children.Cast<MarkupTextLiteralSyntax>();
|
||||
foreach (var literal in markupLiteralArray)
|
||||
{
|
||||
builder.AddRange(literal.LiteralTokens);
|
||||
}
|
||||
var rewritten = SyntaxFactory.MarkupTextLiteral(builder.ToList()).Green.CreateRed(node.Parent, position);
|
||||
Visit(rewritten);
|
||||
}
|
||||
else if (children.All(c => c is CSharpExpressionLiteralSyntax))
|
||||
{
|
||||
var builder = SyntaxListBuilder<SyntaxToken>.Create();
|
||||
var expressionLiteralArray = children.Cast<CSharpExpressionLiteralSyntax>();
|
||||
SpanContext context = null;
|
||||
foreach (var literal in expressionLiteralArray)
|
||||
{
|
||||
context = literal.GetSpanContext();
|
||||
builder.AddRange(literal.LiteralTokens);
|
||||
}
|
||||
var rewritten = SyntaxFactory.CSharpExpressionLiteral(builder.ToList()).Green.CreateRed(node.Parent, position);
|
||||
rewritten = context != null ? rewritten.WithSpanContext(context) : rewritten;
|
||||
Visit(rewritten);
|
||||
}
|
||||
else
|
||||
{
|
||||
Visit(node);
|
||||
}
|
||||
}
|
||||
|
||||
private MarkupTextLiteralSyntax MergeAttributeValue(MarkupLiteralAttributeValueSyntax node)
|
||||
{
|
||||
var valueTokens = MergeLiterals(node.Prefix?.LiteralTokens, node.Value?.LiteralTokens);
|
||||
var rewritten = node.Prefix?.Update(valueTokens) ?? node.Value?.Update(valueTokens);
|
||||
rewritten = (MarkupTextLiteralSyntax)rewritten?.Green.CreateRed(node, node.Position);
|
||||
var originalContext = rewritten.GetSpanContext();
|
||||
if (originalContext != null)
|
||||
{
|
||||
rewritten = rewritten.WithSpanContext(new SpanContext(new MarkupChunkGenerator(), originalContext.EditHandler));
|
||||
}
|
||||
|
||||
return rewritten;
|
||||
}
|
||||
|
||||
private void Combine(HtmlContentIntermediateNode node, SyntaxNode item)
|
||||
{
|
||||
node.Children.Add(new IntermediateToken()
|
||||
{
|
||||
Content = item.GetContent(),
|
||||
Kind = TokenKind.Html,
|
||||
Source = BuildSourceSpanFromNode(item),
|
||||
});
|
||||
|
||||
if (node.Source != null)
|
||||
{
|
||||
Debug.Assert(node.Source.Value.FilePath != null);
|
||||
|
||||
node.Source = new SourceSpan(
|
||||
node.Source.Value.FilePath,
|
||||
node.Source.Value.AbsoluteIndex,
|
||||
node.Source.Value.LineIndex,
|
||||
node.Source.Value.CharacterIndex,
|
||||
node.Source.Value.Length + item.FullWidth);
|
||||
}
|
||||
}
|
||||
|
||||
private SyntaxList<SyntaxToken> MergeLiterals(params SyntaxList<SyntaxToken>?[] literals)
|
||||
{
|
||||
var builder = SyntaxListBuilder<SyntaxToken>.Create();
|
||||
for (var i = 0; i < literals.Length; i++)
|
||||
{
|
||||
var literal = literals[i];
|
||||
if (!literal.HasValue)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
builder.AddRange(literal.Value);
|
||||
}
|
||||
|
||||
return builder.ToList();
|
||||
}
|
||||
}
|
||||
|
||||
private class ImportsVisitor : LoweringVisitor
|
||||
{
|
||||
public ImportsVisitor(DocumentIntermediateNode document, IntermediateNodeBuilder builder, RazorParserFeatureFlags featureFlags)
|
||||
|
|
@ -1099,18 +1786,6 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
}
|
||||
}
|
||||
|
||||
private class DirectiveVisitor : IntermediateNodeWalker
|
||||
{
|
||||
public List<IntermediateNodeReference> Directives = new List<IntermediateNodeReference>();
|
||||
|
||||
public override void VisitDirective(DirectiveIntermediateNode node)
|
||||
{
|
||||
Directives.Add(new IntermediateNodeReference(Parent, node));
|
||||
|
||||
base.VisitDirective(node);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsMalformed(IEnumerable<RazorDiagnostic> diagnostics)
|
||||
=> diagnostics.Any(diagnostic => diagnostic.Severity == RazorDiagnosticSeverity.Error);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
{
|
||||
private bool _designTime;
|
||||
|
||||
public DefaultRazorParserOptionsBuilder(RazorConfiguration configuration)
|
||||
public DefaultRazorParserOptionsBuilder(RazorConfiguration configuration, string fileKind)
|
||||
{
|
||||
if (configuration == null)
|
||||
{
|
||||
|
|
@ -20,6 +20,7 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
|
||||
Configuration = configuration;
|
||||
LanguageVersion = configuration.LanguageVersion;
|
||||
FileKind = fileKind;
|
||||
}
|
||||
|
||||
public DefaultRazorParserOptionsBuilder(bool designTime, RazorLanguageVersion version)
|
||||
|
|
@ -34,6 +35,8 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
|
||||
public override ICollection<DirectiveDescriptor> Directives { get; } = new List<DirectiveDescriptor>();
|
||||
|
||||
public override string FileKind { get; }
|
||||
|
||||
public override bool ParseLeadingDirectives { get; set; }
|
||||
|
||||
public override RazorLanguageVersion LanguageVersion { get; }
|
||||
|
|
|
|||
|
|
@ -15,9 +15,9 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
_configureOptions = ProjectEngine.EngineFeatures.OfType<IConfigureRazorParserOptionsFeature>().ToArray();
|
||||
}
|
||||
|
||||
public RazorParserOptions Create(Action<RazorParserOptionsBuilder> configure)
|
||||
public RazorParserOptions Create(string fileKind, Action<RazorParserOptionsBuilder> configure)
|
||||
{
|
||||
var builder = new DefaultRazorParserOptionsBuilder(ProjectEngine.Configuration);
|
||||
var builder = new DefaultRazorParserOptionsBuilder(ProjectEngine.Configuration, fileKind);
|
||||
configure?.Invoke(builder);
|
||||
|
||||
for (var i = 0; i < _configureOptions.Length; i++)
|
||||
|
|
|
|||
|
|
@ -62,24 +62,55 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
throw new ArgumentNullException(nameof(projectItem));
|
||||
}
|
||||
|
||||
var sourceDocument = RazorSourceDocument.ReadFrom(projectItem);
|
||||
|
||||
var importFeature = GetRequiredFeature<IImportProjectFeature>();
|
||||
var importItems = importFeature.GetImports(projectItem);
|
||||
var importSourceDocuments = GetImportSourceDocuments(importItems);
|
||||
|
||||
return CreateCodeDocumentCore(sourceDocument, projectItem.FileKind, importSourceDocuments, tagHelpers: null);
|
||||
return CreateCodeDocumentCore(projectItem, configureParser: null, configureCodeGeneration: null);
|
||||
}
|
||||
|
||||
internal override RazorCodeDocument CreateCodeDocumentCore(RazorSourceDocument sourceDocument, string fileKind, IReadOnlyList<RazorSourceDocument> importSourceDocuments, IReadOnlyList<TagHelperDescriptor> tagHelpers)
|
||||
protected RazorCodeDocument CreateCodeDocumentCore(
|
||||
RazorProjectItem projectItem,
|
||||
Action<RazorParserOptionsBuilder> configureParser,
|
||||
Action<RazorCodeGenerationOptionsBuilder> configureCodeGeneration)
|
||||
{
|
||||
if (projectItem == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectItem));
|
||||
}
|
||||
|
||||
var sourceDocument = RazorSourceDocument.ReadFrom(projectItem);
|
||||
|
||||
var importItems = new List<RazorProjectItem>();
|
||||
var features = ProjectFeatures.OfType<IImportProjectFeature>();
|
||||
foreach (var feature in features)
|
||||
{
|
||||
importItems.AddRange(feature.GetImports(projectItem));
|
||||
}
|
||||
|
||||
var importSourceDocuments = GetImportSourceDocuments(importItems);
|
||||
return CreateCodeDocumentCore(sourceDocument, projectItem.FileKind, importSourceDocuments, tagHelpers: null, configureParser, configureCodeGeneration);
|
||||
}
|
||||
|
||||
protected RazorCodeDocument CreateCodeDocumentCore(
|
||||
RazorSourceDocument sourceDocument,
|
||||
string fileKind,
|
||||
IReadOnlyList<RazorSourceDocument> importSourceDocuments,
|
||||
IReadOnlyList<TagHelperDescriptor> tagHelpers,
|
||||
Action<RazorParserOptionsBuilder> configureParser,
|
||||
Action<RazorCodeGenerationOptionsBuilder> configureCodeGeneration)
|
||||
{
|
||||
if (sourceDocument == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(sourceDocument));
|
||||
}
|
||||
|
||||
var parserOptions = GetRequiredFeature<IRazorParserOptionsFactoryProjectFeature>().Create(ConfigureParserOptions);
|
||||
var codeGenerationOptions = GetRequiredFeature<IRazorCodeGenerationOptionsFactoryProjectFeature>().Create(ConfigureCodeGenerationOptions);
|
||||
var parserOptions = GetRequiredFeature<IRazorParserOptionsFactoryProjectFeature>().Create(fileKind, builder =>
|
||||
{
|
||||
ConfigureParserOptions(builder);
|
||||
configureParser?.Invoke(builder);
|
||||
});
|
||||
var codeGenerationOptions = GetRequiredFeature<IRazorCodeGenerationOptionsFactoryProjectFeature>().Create(fileKind, builder =>
|
||||
{
|
||||
ConfigureCodeGenerationOptions(builder);
|
||||
configureCodeGeneration?.Invoke(builder);
|
||||
});
|
||||
|
||||
var codeDocument = RazorCodeDocument.Create(sourceDocument, importSourceDocuments, parserOptions, codeGenerationOptions);
|
||||
codeDocument.SetTagHelpers(tagHelpers);
|
||||
|
|
@ -99,24 +130,55 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
throw new ArgumentNullException(nameof(projectItem));
|
||||
}
|
||||
|
||||
var sourceDocument = RazorSourceDocument.ReadFrom(projectItem);
|
||||
|
||||
var importFeature = GetRequiredFeature<IImportProjectFeature>();
|
||||
var importItems = importFeature.GetImports(projectItem);
|
||||
var importSourceDocuments = GetImportSourceDocuments(importItems, suppressExceptions: true);
|
||||
|
||||
return CreateCodeDocumentDesignTimeCore(sourceDocument, projectItem.FileKind, importSourceDocuments, tagHelpers: null);
|
||||
return CreateCodeDocumentDesignTimeCore(projectItem, configureParser: null, configureCodeGeneration: null);
|
||||
}
|
||||
|
||||
internal override RazorCodeDocument CreateCodeDocumentDesignTimeCore(RazorSourceDocument sourceDocument, string fileKind, IReadOnlyList<RazorSourceDocument> importSourceDocuments, IReadOnlyList<TagHelperDescriptor> tagHelpers)
|
||||
protected RazorCodeDocument CreateCodeDocumentDesignTimeCore(
|
||||
RazorProjectItem projectItem,
|
||||
Action<RazorParserOptionsBuilder> configureParser,
|
||||
Action<RazorCodeGenerationOptionsBuilder> configureCodeGeneration)
|
||||
{
|
||||
if (projectItem == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectItem));
|
||||
}
|
||||
|
||||
var sourceDocument = RazorSourceDocument.ReadFrom(projectItem);
|
||||
|
||||
var importItems = new List<RazorProjectItem>();
|
||||
var features = ProjectFeatures.OfType<IImportProjectFeature>();
|
||||
foreach (var feature in features)
|
||||
{
|
||||
importItems.AddRange(feature.GetImports(projectItem));
|
||||
}
|
||||
|
||||
var importSourceDocuments = GetImportSourceDocuments(importItems, suppressExceptions: true);
|
||||
return CreateCodeDocumentDesignTimeCore(sourceDocument, projectItem.FileKind, importSourceDocuments, tagHelpers: null, configureParser, configureCodeGeneration);
|
||||
}
|
||||
|
||||
protected RazorCodeDocument CreateCodeDocumentDesignTimeCore(
|
||||
RazorSourceDocument sourceDocument,
|
||||
string fileKind,
|
||||
IReadOnlyList<RazorSourceDocument> importSourceDocuments,
|
||||
IReadOnlyList<TagHelperDescriptor> tagHelpers,
|
||||
Action<RazorParserOptionsBuilder> configureParser,
|
||||
Action<RazorCodeGenerationOptionsBuilder> configureCodeGeneration)
|
||||
{
|
||||
if (sourceDocument == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(sourceDocument));
|
||||
}
|
||||
|
||||
var parserOptions = GetRequiredFeature<IRazorParserOptionsFactoryProjectFeature>().Create(ConfigureDesignTimeParserOptions);
|
||||
var codeGenerationOptions = GetRequiredFeature<IRazorCodeGenerationOptionsFactoryProjectFeature>().Create(ConfigureDesignTimeCodeGenerationOptions);
|
||||
var parserOptions = GetRequiredFeature<IRazorParserOptionsFactoryProjectFeature>().Create(fileKind, builder =>
|
||||
{
|
||||
ConfigureDesignTimeParserOptions(builder);
|
||||
configureParser?.Invoke(builder);
|
||||
});
|
||||
var codeGenerationOptions = GetRequiredFeature<IRazorCodeGenerationOptionsFactoryProjectFeature>().Create(fileKind, builder =>
|
||||
{
|
||||
ConfigureDesignTimeCodeGenerationOptions(builder);
|
||||
configureCodeGeneration?.Invoke(builder);
|
||||
});
|
||||
|
||||
var codeDocument = RazorCodeDocument.Create(sourceDocument, importSourceDocuments, parserOptions, codeGenerationOptions);
|
||||
codeDocument.SetTagHelpers(tagHelpers);
|
||||
|
|
@ -129,6 +191,62 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
return codeDocument;
|
||||
}
|
||||
|
||||
public override RazorCodeDocument Process(RazorSourceDocument source, string fileKind, IReadOnlyList<RazorSourceDocument> importSources, IReadOnlyList<TagHelperDescriptor> tagHelpers)
|
||||
{
|
||||
if (source == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(source));
|
||||
}
|
||||
|
||||
var codeDocument = CreateCodeDocumentCore(source, fileKind, importSources, tagHelpers, configureParser: null, configureCodeGeneration: null);
|
||||
ProcessCore(codeDocument);
|
||||
return codeDocument;
|
||||
}
|
||||
|
||||
public override RazorCodeDocument ProcessDeclarationOnly(RazorProjectItem projectItem)
|
||||
{
|
||||
if (projectItem == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(projectItem));
|
||||
}
|
||||
|
||||
var codeDocument = CreateCodeDocumentCore(projectItem, configureParser: null, configureCodeGeneration: (builder) =>
|
||||
{
|
||||
builder.SuppressPrimaryMethodBody = true;
|
||||
});
|
||||
|
||||
ProcessCore(codeDocument);
|
||||
return codeDocument;
|
||||
}
|
||||
|
||||
public override RazorCodeDocument ProcessDeclarationOnly(RazorSourceDocument source, string fileKind, IReadOnlyList<RazorSourceDocument> importSources, IReadOnlyList<TagHelperDescriptor> tagHelpers)
|
||||
{
|
||||
if (source == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(source));
|
||||
}
|
||||
|
||||
var codeDocument = CreateCodeDocumentCore(source, fileKind, importSources, tagHelpers, configureParser: null, configureCodeGeneration: (builder) =>
|
||||
{
|
||||
builder.SuppressPrimaryMethodBody = true;
|
||||
});
|
||||
|
||||
ProcessCore(codeDocument);
|
||||
return codeDocument;
|
||||
}
|
||||
|
||||
public override RazorCodeDocument ProcessDesignTime(RazorSourceDocument source, string fileKind, IReadOnlyList<RazorSourceDocument> importSources, IReadOnlyList<TagHelperDescriptor> tagHelpers)
|
||||
{
|
||||
if (source == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(source));
|
||||
}
|
||||
|
||||
var codeDocument = CreateCodeDocumentDesignTimeCore(source, fileKind, importSources, tagHelpers, configureParser: null, configureCodeGeneration: null);
|
||||
ProcessCore(codeDocument);
|
||||
return codeDocument;
|
||||
}
|
||||
|
||||
protected override void ProcessCore(RazorCodeDocument codeDocument)
|
||||
{
|
||||
if (codeDocument == null)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
// 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.Language.CodeGeneration;
|
||||
|
|
@ -10,15 +11,14 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
{
|
||||
public abstract class DocumentClassifierPassBase : IntermediateNodePassBase, IRazorDocumentClassifierPass
|
||||
{
|
||||
private static readonly ICodeTargetExtension[] EmptyExtensionArray = new ICodeTargetExtension[0];
|
||||
private ICodeTargetExtension[] _targetExtensions;
|
||||
|
||||
protected abstract string DocumentKind { get; }
|
||||
|
||||
protected IReadOnlyList<ICodeTargetExtension> TargetExtensions { get; private set; }
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
var feature = Engine.Features.OfType<IRazorTargetExtensionFeature>();
|
||||
_targetExtensions = feature.FirstOrDefault()?.TargetExtensions.ToArray() ?? EmptyExtensionArray;
|
||||
TargetExtensions = feature.FirstOrDefault()?.TargetExtensions.ToArray() ?? Array.Empty<ICodeTargetExtension>();
|
||||
}
|
||||
|
||||
protected sealed override void ExecuteCore(RazorCodeDocument codeDocument, DocumentIntermediateNode documentNode)
|
||||
|
|
@ -35,6 +35,10 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
|
||||
documentNode.DocumentKind = DocumentKind;
|
||||
documentNode.Target = CreateTarget(codeDocument, documentNode.Options);
|
||||
if (documentNode.Target == null)
|
||||
{
|
||||
throw new InvalidOperationException($"{nameof(CreateTarget)} must return a non-null {nameof(CodeTarget)}.");
|
||||
}
|
||||
|
||||
Rewrite(codeDocument, documentNode);
|
||||
}
|
||||
|
|
@ -80,13 +84,14 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
|
||||
protected abstract bool IsMatch(RazorCodeDocument codeDocument, DocumentIntermediateNode documentNode);
|
||||
|
||||
private CodeTarget CreateTarget(RazorCodeDocument codeDocument, RazorCodeGenerationOptions options)
|
||||
// virtual to allow replacing the code target wholesale.
|
||||
protected virtual CodeTarget CreateTarget(RazorCodeDocument codeDocument, RazorCodeGenerationOptions options)
|
||||
{
|
||||
return CodeTarget.CreateDefault(codeDocument, options, (builder) =>
|
||||
{
|
||||
for (var i = 0; i < _targetExtensions.Length; i++)
|
||||
for (var i = 0; i < TargetExtensions.Count; i++)
|
||||
{
|
||||
builder.TargetExtensions.Add(_targetExtensions[i]);
|
||||
builder.TargetExtensions.Add(TargetExtensions[i]);
|
||||
}
|
||||
|
||||
ConfigureTarget(builder);
|
||||
|
|
|
|||
|
|
@ -2,9 +2,8 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Components
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Extensions
|
||||
{
|
||||
internal static class ImplementsDirective
|
||||
{
|
||||
|
|
@ -3,11 +3,9 @@
|
|||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Razor.Language;
|
||||
using Microsoft.AspNetCore.Razor.Language.Extensions;
|
||||
using Microsoft.AspNetCore.Razor.Language.Intermediate;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Components
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Extensions
|
||||
{
|
||||
internal class ImplementsDirectivePass : IntermediateNodePassBase, IRazorDirectiveClassifierPass
|
||||
{
|
||||
|
|
@ -1,8 +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;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using Microsoft.AspNetCore.Razor.Language.Components;
|
||||
using Microsoft.AspNetCore.Razor.Language.Intermediate;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Extensions
|
||||
|
|
@ -26,6 +28,12 @@ namespace Microsoft.AspNetCore.Razor.Language.Extensions
|
|||
return;
|
||||
}
|
||||
|
||||
if (string.Equals(documentNode.DocumentKind, ComponentDocumentClassifierPass.ComponentDocumentKind, StringComparison.Ordinal))
|
||||
{
|
||||
// Metadata attributes are not used for components.
|
||||
return;
|
||||
}
|
||||
|
||||
// We need to be able to compute the data we need for the [RazorCompiledItem] attribute - that includes
|
||||
// a full type name, and a document kind, and optionally an identifier.
|
||||
//
|
||||
|
|
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language
|
||||
{
|
||||
class FileKindDirectiveFeature
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
namespace Microsoft.AspNetCore.Razor.Language
|
||||
{
|
||||
internal static class FileKinds
|
||||
public static class FileKinds
|
||||
{
|
||||
public static readonly string Component = "component";
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,6 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
{
|
||||
internal interface IRazorCodeGenerationOptionsFactoryProjectFeature : IRazorProjectEngineFeature
|
||||
{
|
||||
RazorCodeGenerationOptions Create(Action<RazorCodeGenerationOptionsBuilder> configure);
|
||||
RazorCodeGenerationOptions Create(string fileKind, Action<RazorCodeGenerationOptionsBuilder> configure);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,6 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
{
|
||||
internal interface IRazorParserOptionsFactoryProjectFeature : IRazorProjectEngineFeature
|
||||
{
|
||||
RazorParserOptions Create(Action<RazorParserOptionsBuilder> configure);
|
||||
RazorParserOptions Create(string fileKind, Action<RazorParserOptionsBuilder> configure);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Intermediate
|
|||
// An optional type inference node. This will be populated (and point to a different part of the tree)
|
||||
// if this component call site requires type inference.
|
||||
public ComponentTypeInferenceMethodIntermediateNode TypeInferenceNode { get; set; }
|
||||
|
||||
|
||||
public string TypeName { get; set; }
|
||||
|
||||
public override void Accept(IntermediateNodeVisitor visitor)
|
||||
|
|
|
|||
|
|
@ -154,6 +154,16 @@ namespace Microsoft.AspNetCore.Razor.Language.Intermediate
|
|||
VisitDefault(node);
|
||||
}
|
||||
|
||||
public virtual void VisitMarkupElement(MarkupElementIntermediateNode node)
|
||||
{
|
||||
VisitDefault(node);
|
||||
}
|
||||
|
||||
public virtual void VisitMarkupBlock(MarkupBlockIntermediateNode node)
|
||||
{
|
||||
VisitDefault(node);
|
||||
}
|
||||
|
||||
public virtual void VisitReferenceCapture(ReferenceCaptureIntermediateNode node)
|
||||
{
|
||||
VisitDefault(node);
|
||||
|
|
|
|||
|
|
@ -2,14 +2,10 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using Microsoft.AspNetCore.Razor.Language.CodeGeneration;
|
||||
using Microsoft.AspNetCore.Razor.Language.Components;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Intermediate
|
||||
{
|
||||
[DebuggerDisplay("{DebuggerDisplay,nq}")]
|
||||
internal class MarkupBlockIntermediateNode : ExtensionIntermediateNode
|
||||
public sealed class MarkupBlockIntermediateNode : IntermediateNode
|
||||
{
|
||||
public override IntermediateNodeCollection Children { get; } = new IntermediateNodeCollection();
|
||||
|
||||
|
|
@ -22,25 +18,17 @@ namespace Microsoft.AspNetCore.Razor.Language.Intermediate
|
|||
throw new ArgumentNullException(nameof(visitor));
|
||||
}
|
||||
|
||||
AcceptExtensionNode<MarkupBlockIntermediateNode>(this, visitor);
|
||||
visitor.VisitMarkupBlock(this);
|
||||
}
|
||||
|
||||
public override void WriteNode(CodeTarget target, CodeRenderingContext context)
|
||||
public override void FormatNode(IntermediateNodeFormatter formatter)
|
||||
{
|
||||
if (target == null)
|
||||
if (formatter == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(target));
|
||||
throw new ArgumentNullException(nameof(formatter));
|
||||
}
|
||||
|
||||
if (context == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
var writer = (BlazorNodeWriter)context.NodeWriter;
|
||||
writer.WriteHtmlBlock(context, this);
|
||||
formatter.WriteContent(Content);
|
||||
}
|
||||
|
||||
private string DebuggerDisplay => Content;
|
||||
}
|
||||
}
|
||||
|
|
@ -5,14 +5,10 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Microsoft.AspNetCore.Razor.Language.CodeGeneration;
|
||||
using Microsoft.AspNetCore.Razor.Language.Components;
|
||||
|
||||
namespace Microsoft.AspNetCore.Razor.Language.Intermediate
|
||||
{
|
||||
[DebuggerDisplay("{DebuggerDisplay,nq}")]
|
||||
internal class MarkupElementIntermediateNode : ExtensionIntermediateNode
|
||||
public sealed class MarkupElementIntermediateNode : IntermediateNode
|
||||
{
|
||||
public IEnumerable<HtmlAttributeIntermediateNode> Attributes => Children.OfType<HtmlAttributeIntermediateNode>();
|
||||
|
||||
|
|
@ -36,56 +32,19 @@ namespace Microsoft.AspNetCore.Razor.Language.Intermediate
|
|||
throw new ArgumentNullException(nameof(visitor));
|
||||
}
|
||||
|
||||
AcceptExtensionNode<MarkupElementIntermediateNode>(this, visitor);
|
||||
visitor.VisitMarkupElement(this);
|
||||
}
|
||||
|
||||
public override void WriteNode(CodeTarget target, CodeRenderingContext context)
|
||||
public override void FormatNode(IntermediateNodeFormatter formatter)
|
||||
{
|
||||
if (target == null)
|
||||
if (formatter == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(target));
|
||||
throw new ArgumentNullException(nameof(formatter));
|
||||
}
|
||||
|
||||
if (context == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
formatter.WriteContent(TagName);
|
||||
|
||||
var writer = (BlazorNodeWriter)context.NodeWriter;
|
||||
writer.WriteHtmlElement(context, this);
|
||||
}
|
||||
|
||||
private string DebuggerDisplay
|
||||
{
|
||||
get
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
builder.Append("Element: ");
|
||||
builder.Append("<");
|
||||
builder.Append(TagName);
|
||||
|
||||
foreach (var attribute in Attributes)
|
||||
{
|
||||
builder.Append(" ");
|
||||
builder.Append(attribute.AttributeName);
|
||||
builder.Append("=\"...\"");
|
||||
}
|
||||
|
||||
foreach (var capture in Captures)
|
||||
{
|
||||
builder.Append(" ");
|
||||
builder.Append("ref");
|
||||
builder.Append("=\"...\"");
|
||||
}
|
||||
|
||||
builder.Append(">");
|
||||
builder.Append(Body.Any() ? "..." : string.Empty);
|
||||
builder.Append("</");
|
||||
builder.Append(TagName);
|
||||
builder.Append(">");
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
formatter.WriteProperty(nameof(TagName), TagName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -7,7 +7,7 @@ using System.Collections.Generic;
|
|||
|
||||
namespace Microsoft.AspNetCore.Razor.Language
|
||||
{
|
||||
public sealed class ItemCollection : ICollection<KeyValuePair<object, object>>
|
||||
public sealed class ItemCollection : ICollection<KeyValuePair<object, object>>, ICollection
|
||||
{
|
||||
private readonly Dictionary<object, object> _inner;
|
||||
|
||||
|
|
@ -43,9 +43,9 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
|
||||
public bool IsReadOnly => _inner != null;
|
||||
|
||||
int ICollection<KeyValuePair<object, object>>.Count => throw new NotImplementedException();
|
||||
bool ICollection.IsSynchronized => ((ICollection)_inner).IsSynchronized;
|
||||
|
||||
bool ICollection<KeyValuePair<object, object>>.IsReadOnly => throw new NotImplementedException();
|
||||
object ICollection.SyncRoot => ((ICollection)_inner).SyncRoot;
|
||||
|
||||
public void Add(KeyValuePair<object, object> item)
|
||||
{
|
||||
|
|
@ -120,5 +120,10 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
|
||||
return ((ICollection<KeyValuePair<object, object>>)_inner).Remove(item);
|
||||
}
|
||||
|
||||
void ICollection.CopyTo(Array array, int index)
|
||||
{
|
||||
((ICollection)_inner).CopyTo(array, index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
|
||||
public abstract bool DesignTime { get; }
|
||||
|
||||
public virtual string FileKind => null;
|
||||
|
||||
public abstract int IndentSize { get; set; }
|
||||
|
||||
public abstract bool IndentWithTabs { get; set; }
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
{
|
||||
var allowMinimizedBooleanTagHelperAttributes = false;
|
||||
var allowHtmlCommentsInTagHelpers = false;
|
||||
var allowComponentFileKind = false;
|
||||
var experimental_AllowConditionalDataDashAttributes = false;
|
||||
|
||||
if (version.CompareTo(RazorLanguageVersion.Version_2_1) >= 0)
|
||||
|
|
@ -18,6 +19,12 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
allowHtmlCommentsInTagHelpers = true;
|
||||
}
|
||||
|
||||
if (version.CompareTo(RazorLanguageVersion.Version_3_0) >= 0)
|
||||
{
|
||||
// Added in 3.0
|
||||
allowComponentFileKind = true;
|
||||
}
|
||||
|
||||
if (version.CompareTo(RazorLanguageVersion.Experimental) >= 0)
|
||||
{
|
||||
experimental_AllowConditionalDataDashAttributes = true;
|
||||
|
|
@ -26,6 +33,7 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
return new DefaultRazorParserFeatureFlags(
|
||||
allowMinimizedBooleanTagHelperAttributes,
|
||||
allowHtmlCommentsInTagHelpers,
|
||||
allowComponentFileKind,
|
||||
experimental_AllowConditionalDataDashAttributes);
|
||||
}
|
||||
|
||||
|
|
@ -33,6 +41,8 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
|
||||
public abstract bool AllowHtmlCommentsInTagHelpers { get; }
|
||||
|
||||
public abstract bool AllowComponentFileKind { get; }
|
||||
|
||||
public abstract bool EXPERIMENTAL_AllowConditionalDataDashAttributes { get; }
|
||||
|
||||
private class DefaultRazorParserFeatureFlags : RazorParserFeatureFlags
|
||||
|
|
@ -40,10 +50,12 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
public DefaultRazorParserFeatureFlags(
|
||||
bool allowMinimizedBooleanTagHelperAttributes,
|
||||
bool allowHtmlCommentsInTagHelpers,
|
||||
bool allowComponentFileKind,
|
||||
bool experimental_AllowConditionalDataDashAttributes)
|
||||
{
|
||||
AllowMinimizedBooleanTagHelperAttributes = allowMinimizedBooleanTagHelperAttributes;
|
||||
AllowHtmlCommentsInTagHelpers = allowHtmlCommentsInTagHelpers;
|
||||
AllowComponentFileKind = allowComponentFileKind;
|
||||
EXPERIMENTAL_AllowConditionalDataDashAttributes = experimental_AllowConditionalDataDashAttributes;
|
||||
}
|
||||
|
||||
|
|
@ -51,6 +63,8 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
|
||||
public override bool AllowHtmlCommentsInTagHelpers { get; }
|
||||
|
||||
public override bool AllowComponentFileKind { get; }
|
||||
|
||||
public override bool EXPERIMENTAL_AllowConditionalDataDashAttributes { get; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
|
||||
public abstract ICollection<DirectiveDescriptor> Directives { get; }
|
||||
|
||||
public virtual string FileKind => null;
|
||||
|
||||
public abstract bool ParseLeadingDirectives { get; set; }
|
||||
|
||||
public virtual RazorLanguageVersion LanguageVersion { get; }
|
||||
|
|
|
|||
|
|
@ -35,18 +35,26 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
return codeDocument;
|
||||
}
|
||||
|
||||
internal virtual RazorCodeDocument Process(RazorSourceDocument source, string fileKind, IReadOnlyList<RazorSourceDocument> importSources, IReadOnlyList<TagHelperDescriptor> tagHelpers)
|
||||
public virtual RazorCodeDocument Process(RazorSourceDocument source, string fileKind, IReadOnlyList<RazorSourceDocument> importSources, IReadOnlyList<TagHelperDescriptor> tagHelpers)
|
||||
{
|
||||
if (source == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(source));
|
||||
}
|
||||
|
||||
var codeDocument = CreateCodeDocumentCore(source, fileKind, importSources, tagHelpers);
|
||||
ProcessCore(codeDocument);
|
||||
return codeDocument;
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public virtual RazorCodeDocument ProcessDeclarationOnly(RazorProjectItem projectItem)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public virtual RazorCodeDocument ProcessDeclarationOnly(RazorSourceDocument source, string fileKind, IReadOnlyList<RazorSourceDocument> importSources, IReadOnlyList<TagHelperDescriptor> tagHelpers)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public virtual RazorCodeDocument ProcessDesignTime(RazorSourceDocument source, string fileKind, IReadOnlyList<RazorSourceDocument> importSources, IReadOnlyList<TagHelperDescriptor> tagHelpers)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public virtual RazorCodeDocument ProcessDesignTime(RazorProjectItem projectItem)
|
||||
{
|
||||
if (projectItem == null)
|
||||
|
|
@ -59,42 +67,10 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
return codeDocument;
|
||||
}
|
||||
|
||||
internal virtual RazorCodeDocument ProcessDesignTime(RazorSourceDocument source, string fileKind, IReadOnlyList<RazorSourceDocument> importSources, IReadOnlyList<TagHelperDescriptor> tagHelpers)
|
||||
{
|
||||
if (source == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(source));
|
||||
}
|
||||
|
||||
var codeDocument = CreateCodeDocumentDesignTimeCore(source, fileKind, importSources, tagHelpers);
|
||||
ProcessCore(codeDocument);
|
||||
return codeDocument;
|
||||
}
|
||||
|
||||
protected abstract RazorCodeDocument CreateCodeDocumentCore(RazorProjectItem projectItem);
|
||||
|
||||
internal virtual RazorCodeDocument CreateCodeDocumentCore(RazorSourceDocument source, string fileKind, IReadOnlyList<RazorSourceDocument> importSources, IReadOnlyList<TagHelperDescriptor> tagHelpers)
|
||||
{
|
||||
var codeDocument = RazorCodeDocument.Create(source, importSources);
|
||||
if (fileKind != null)
|
||||
{
|
||||
codeDocument.SetFileKind(fileKind);
|
||||
}
|
||||
return codeDocument;
|
||||
}
|
||||
|
||||
protected abstract RazorCodeDocument CreateCodeDocumentDesignTimeCore(RazorProjectItem projectItem);
|
||||
|
||||
internal virtual RazorCodeDocument CreateCodeDocumentDesignTimeCore(RazorSourceDocument source, string fileKind, IReadOnlyList<RazorSourceDocument> importSources, IReadOnlyList<TagHelperDescriptor> tagHelpers)
|
||||
{
|
||||
var codeDocument = RazorCodeDocument.Create(source, importSources);
|
||||
if (fileKind != null)
|
||||
{
|
||||
codeDocument.SetFileKind(fileKind);
|
||||
}
|
||||
return codeDocument;
|
||||
}
|
||||
|
||||
protected abstract void ProcessCore(RazorCodeDocument codeDocument);
|
||||
|
||||
internal static RazorProjectEngine CreateEmpty(Action<RazorProjectEngineBuilder> configure = null)
|
||||
|
|
@ -106,8 +82,6 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
return builder.Build();
|
||||
}
|
||||
|
||||
internal static RazorProjectEngine Create() => Create(configure: null);
|
||||
|
||||
internal static RazorProjectEngine Create(Action<RazorProjectEngineBuilder> configure) => Create(RazorConfiguration.Default, RazorProjectFileSystem.Empty, configure);
|
||||
|
||||
public static RazorProjectEngine Create(RazorConfiguration configuration, RazorProjectFileSystem fileSystem) => Create(configuration, fileSystem, configure: null);
|
||||
|
|
@ -136,7 +110,16 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
// This allows extensions to rely on default features, and customizations to override choices made by
|
||||
// extensions.
|
||||
AddDefaultPhases(builder.Phases);
|
||||
AddDefaultsFeatures(builder.Features);
|
||||
AddDefaultFeatures(builder.Features);
|
||||
|
||||
if (configuration.LanguageVersion.CompareTo(RazorLanguageVersion.Version_3_0) >= 0)
|
||||
{
|
||||
FunctionsDirective.Register(builder);
|
||||
ImplementsDirective.Register(builder);
|
||||
InheritsDirective.Register(builder);
|
||||
|
||||
AddComponentFeatures(builder);
|
||||
}
|
||||
|
||||
LoadExtensions(builder, configuration.Extensions);
|
||||
|
||||
|
|
@ -157,7 +140,7 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
phases.Add(new DefaultRazorCSharpLoweringPhase());
|
||||
}
|
||||
|
||||
private static void AddDefaultsFeatures(ICollection<IRazorFeature> features)
|
||||
private static void AddDefaultFeatures(ICollection<IRazorFeature> features)
|
||||
{
|
||||
features.Add(new DefaultImportProjectFeature());
|
||||
|
||||
|
|
@ -221,6 +204,36 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
});
|
||||
}
|
||||
|
||||
private static void AddComponentFeatures(RazorProjectEngineBuilder builder)
|
||||
{
|
||||
// Project Engine Features
|
||||
builder.Features.Add(new ComponentImportProjectFeature());
|
||||
|
||||
// Directives (conditional on file kind)
|
||||
ComponentInjectDirective.Register(builder);
|
||||
ComponentLayoutDirective.Register(builder);
|
||||
ComponentPageDirective.Register(builder);
|
||||
ComponentTypeParamDirective.Register(builder);
|
||||
|
||||
// Document Classifier
|
||||
builder.Features.Add(new ComponentDocumentClassifierPass());
|
||||
|
||||
// Directive Classifier
|
||||
builder.Features.Add(new ComponentWhitespacePass());
|
||||
|
||||
// Optimization
|
||||
builder.Features.Add(new ComponentComplexAttributeContentPass());
|
||||
builder.Features.Add(new ComponentLoweringPass());
|
||||
builder.Features.Add(new ComponentScriptTagPass());
|
||||
builder.Features.Add(new ComponentEventHandlerLoweringPass());
|
||||
builder.Features.Add(new ComponentReferenceCaptureLoweringPass());
|
||||
builder.Features.Add(new ComponentBindLoweringPass());
|
||||
builder.Features.Add(new ComponentTemplateDiagnosticPass());
|
||||
builder.Features.Add(new ComponentGenericTypePass());
|
||||
builder.Features.Add(new ComponentChildContentDiagnosticPass());
|
||||
builder.Features.Add(new ComponentHtmlBlockPass());
|
||||
}
|
||||
|
||||
private static void LoadExtensions(RazorProjectEngineBuilder builder, IReadOnlyList<RazorExtension> extensions)
|
||||
{
|
||||
for (var i = 0; i < extensions.Count; i++)
|
||||
|
|
@ -234,9 +247,6 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
initializer?.Initialize(builder);
|
||||
}
|
||||
}
|
||||
|
||||
// Default extensions.
|
||||
ComponentExtensions.Register(builder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -145,6 +145,43 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the specified <see cref="DirectiveDescriptor"/> for the provided file kind.
|
||||
/// </summary>
|
||||
/// <param name="builder">The <see cref="RazorProjectEngineBuilder"/>.</param>
|
||||
/// <param name="fileKind">The file kind, for which to register the directive. See <see cref="FileKinds"/>.</param>
|
||||
/// <param name="directive">The <see cref="DirectiveDescriptor"/> to add.</param>
|
||||
/// <returns>The <see cref="RazorProjectEngineBuilder"/>.</returns>
|
||||
public static RazorProjectEngineBuilder AddDirective(this RazorProjectEngineBuilder builder, string fileKind, DirectiveDescriptor directive)
|
||||
{
|
||||
if (builder == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(builder));
|
||||
}
|
||||
|
||||
if (fileKind == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(fileKind));
|
||||
}
|
||||
|
||||
if (directive == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(directive));
|
||||
}
|
||||
|
||||
var directiveFeature = GetDirectiveFeature(builder);
|
||||
|
||||
if (!directiveFeature.DirectivesByFileKind.TryGetValue(fileKind, out var directives))
|
||||
{
|
||||
directives = new List<DirectiveDescriptor>();
|
||||
directiveFeature.DirectivesByFileKind.Add(fileKind, directives);
|
||||
}
|
||||
|
||||
directives.Add(directive);
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the provided <see cref="RazorProjectItem" />s as imports to all project items processed
|
||||
/// by the <see cref="RazorProjectEngine"/>.
|
||||
|
|
@ -158,17 +195,15 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
{
|
||||
throw new ArgumentNullException(nameof(builder));
|
||||
}
|
||||
|
||||
var existingImportFeature = builder.Features.OfType<IImportProjectFeature>().First();
|
||||
var testImportFeature = new AdditionalImportsProjectFeature(existingImportFeature, imports);
|
||||
builder.SetImportFeature(testImportFeature);
|
||||
|
||||
builder.Features.Add(new AdditionalImportsProjectFeature(imports));
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
private static IRazorDirectiveFeature GetDirectiveFeature(RazorProjectEngineBuilder builder)
|
||||
private static DefaultRazorDirectiveFeature GetDirectiveFeature(RazorProjectEngineBuilder builder)
|
||||
{
|
||||
var directiveFeature = builder.Features.OfType<IRazorDirectiveFeature>().FirstOrDefault();
|
||||
var directiveFeature = builder.Features.OfType<DefaultRazorDirectiveFeature>().FirstOrDefault();
|
||||
if (directiveFeature == null)
|
||||
{
|
||||
directiveFeature = new DefaultRazorDirectiveFeature();
|
||||
|
|
@ -204,31 +239,16 @@ namespace Microsoft.AspNetCore.Razor.Language
|
|||
|
||||
private class AdditionalImportsProjectFeature : RazorProjectEngineFeatureBase, IImportProjectFeature
|
||||
{
|
||||
private readonly IImportProjectFeature _existingImportFeature;
|
||||
private readonly IEnumerable<RazorProjectItem> _imports;
|
||||
private readonly IReadOnlyList<RazorProjectItem> _imports;
|
||||
|
||||
public override RazorProjectEngine ProjectEngine
|
||||
public AdditionalImportsProjectFeature(params string[] imports)
|
||||
{
|
||||
get => base.ProjectEngine;
|
||||
set
|
||||
{
|
||||
_existingImportFeature.ProjectEngine = value;
|
||||
base.ProjectEngine = value;
|
||||
}
|
||||
}
|
||||
|
||||
public AdditionalImportsProjectFeature(IImportProjectFeature existingImportFeature, params string[] imports)
|
||||
{
|
||||
_existingImportFeature = existingImportFeature;
|
||||
_imports = imports.Select(import => new InMemoryProjectItem(import));
|
||||
_imports = imports.Select(import => new InMemoryProjectItem(import)).ToArray();
|
||||
}
|
||||
|
||||
public IReadOnlyList<RazorProjectItem> GetImports(RazorProjectItem projectItem)
|
||||
{
|
||||
var imports = _existingImportFeature.GetImports(projectItem).ToList();
|
||||
imports.AddRange(_imports);
|
||||
|
||||
return imports;
|
||||
return _imports;
|
||||
}
|
||||
|
||||
private class InMemoryProjectItem : RazorProjectItem
|
||||
|
|
|
|||
|
|
@ -248,7 +248,7 @@ namespace Microsoft.CodeAnalysis.Razor
|
|||
var formatName = entry.Suffix == null ? "Format_" + entry.ValueAttribute : "Format_" + entry.Suffix;
|
||||
var formatAttributeName = entry.Suffix == null ? "format-" + entry.ValueAttribute : "format-" + entry.Suffix;
|
||||
|
||||
var builder = TagHelperDescriptorBuilder.Create(BlazorMetadata.Bind.TagHelperKind, name, entry.Assembly);
|
||||
var builder = TagHelperDescriptorBuilder.Create(BlazorMetadata.Bind.TagHelperKind, name, ComponentsApi.AssemblyName);
|
||||
builder.Documentation = string.Format(
|
||||
ComponentResources.BindTagHelper_Element_Documentation,
|
||||
entry.ValueAttribute,
|
||||
|
|
|
|||
|
|
@ -104,7 +104,7 @@ namespace Microsoft.CodeAnalysis.Razor
|
|||
{
|
||||
var entry = data[i];
|
||||
|
||||
var builder = TagHelperDescriptorBuilder.Create(BlazorMetadata.EventHandler.TagHelperKind, entry.Attribute, entry.Assembly);
|
||||
var builder = TagHelperDescriptorBuilder.Create(BlazorMetadata.EventHandler.TagHelperKind, entry.Attribute, ComponentsApi.AssemblyName);
|
||||
builder.Documentation = string.Format(
|
||||
ComponentResources.EventHandlerTagHelper_Documentation,
|
||||
entry.Attribute,
|
||||
|
|
|
|||
|
|
@ -4,11 +4,13 @@
|
|||
using System.Runtime.CompilerServices;
|
||||
[assembly: InternalsVisibleTo("rzls, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
[assembly: InternalsVisibleTo("rzc, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc.Razor.Extensions.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Razor.Language.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Razor.LanguageServer.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Razor.Test.Common, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
[assembly: InternalsVisibleTo("Microsoft.VisualStudio.LiveShare.Razor, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
[assembly: InternalsVisibleTo("Microsoft.VisualStudio.LiveShare.Razor.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
[assembly: InternalsVisibleTo("Microsoft.CodeAnalysis.Razor.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Mvc.Razor.Extensions.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
[assembly: InternalsVisibleTo("Microsoft.CodeAnalysis.Razor.Workspaces, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
[assembly: InternalsVisibleTo("Microsoft.CodeAnalysis.Razor.Workspaces.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
[assembly: InternalsVisibleTo("Microsoft.CodeAnalysis.Remote.Razor, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
|
|
|
|||
|
|
@ -32,7 +32,9 @@ namespace Microsoft.CodeAnalysis.Razor
|
|||
protected static TagHelperDescriptor[] ExcludeBuiltInComponents(TagHelperDescriptorProviderContext context)
|
||||
{
|
||||
return context.Results
|
||||
.Where(c => c.AssemblyName == "TestAssembly")
|
||||
.Where(c => c.GetTypeName() != "Microsoft.AspNetCore.Components.Bind")
|
||||
.Where(c => c.GetTypeName() != "Microsoft.AspNetCore.Components.BindAttributes")
|
||||
.Where(c => c.GetTypeName() != "Microsoft.AspNetCore.Components.EventHandlers")
|
||||
.OrderBy(c => c.Name)
|
||||
.ToArray();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -222,7 +222,7 @@ namespace Test
|
|||
bind.Documentation);
|
||||
|
||||
// These are all trivially derived from the assembly/namespace/type name
|
||||
Assert.Equal("TestAssembly", bind.AssemblyName);
|
||||
Assert.Equal("Microsoft.AspNetCore.Components", bind.AssemblyName);
|
||||
Assert.Equal("Bind", bind.Name);
|
||||
Assert.Equal("Test.BindAttributes", bind.DisplayName);
|
||||
Assert.Equal("Test.BindAttributes", bind.GetTypeName());
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ namespace Test
|
|||
item.Documentation);
|
||||
|
||||
// These are all trivially derived from the assembly/namespace/type name
|
||||
Assert.Equal("TestAssembly", item.AssemblyName);
|
||||
Assert.Equal("Microsoft.AspNetCore.Components", item.AssemblyName);
|
||||
Assert.Equal("onclick", item.Name);
|
||||
Assert.Equal("Test.EventHandlers", item.DisplayName);
|
||||
Assert.Equal("Test.EventHandlers", item.GetTypeName());
|
||||
|
|
|
|||
|
|
@ -0,0 +1,344 @@
|
|||
// 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.Runtime.InteropServices;
|
||||
|
||||
namespace Microsoft.AspNetCore.Components.RenderTree
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an entry in a tree of user interface (UI) items.
|
||||
/// </summary>
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
public readonly struct RenderTreeFrame
|
||||
{
|
||||
// Note that the struct layout has to be valid in both 32-bit and 64-bit runtime platforms,
|
||||
// which means that all reference-type fields need to take up 8 bytes (except for the last
|
||||
// one, which will be sized as either 4 or 8 bytes depending on the runtime platform).
|
||||
// This is not optimal for the Mono-WebAssembly case because that's always 32-bit so the
|
||||
// reference-type fields could be reduced to 4 bytes each. We could use ifdefs to have
|
||||
// different fields offsets for the 32 and 64 bit compile targets, but then we'd have the
|
||||
// complexity of needing different binaries when loaded into Mono-WASM vs desktop.
|
||||
// Eventually we might stop using this shared memory interop altogether (and would have to
|
||||
// if running as a web worker) so for now to keep things simple, treat reference types as
|
||||
// 8 bytes here.
|
||||
|
||||
// --------------------------------------------------------------------------------
|
||||
// Common
|
||||
// --------------------------------------------------------------------------------
|
||||
|
||||
/// <summary>
|
||||
/// Gets the sequence number of the frame. Sequence numbers indicate the relative source
|
||||
/// positions of the instructions that inserted the frames. Sequence numbers are only
|
||||
/// comparable within the same sequence (typically, the same source method).
|
||||
/// </summary>
|
||||
[FieldOffset(0)] public readonly int Sequence;
|
||||
|
||||
/// <summary>
|
||||
/// Describes the type of this frame.
|
||||
/// </summary>
|
||||
[FieldOffset(4)] public readonly RenderTreeFrameType FrameType;
|
||||
|
||||
// --------------------------------------------------------------------------------
|
||||
// RenderTreeFrameType.Element
|
||||
// --------------------------------------------------------------------------------
|
||||
|
||||
/// <summary>
|
||||
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Element"/>
|
||||
/// gets the number of frames in the subtree for which this frame is the root.
|
||||
/// The value is zero if the frame has not yet been closed.
|
||||
/// </summary>
|
||||
[FieldOffset(8)] public readonly int ElementSubtreeLength;
|
||||
|
||||
/// <summary>
|
||||
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Element"/>,
|
||||
/// gets a name representing the type of the element. Otherwise, the value is undefined.
|
||||
/// </summary>
|
||||
[FieldOffset(16)] public readonly string ElementName;
|
||||
|
||||
// --------------------------------------------------------------------------------
|
||||
// RenderTreeFrameType.Text
|
||||
// --------------------------------------------------------------------------------
|
||||
|
||||
/// <summary>
|
||||
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Text"/>,
|
||||
/// gets the content of the text frame. Otherwise, the value is undefined.
|
||||
/// </summary>
|
||||
[FieldOffset(16)] public readonly string TextContent;
|
||||
|
||||
// --------------------------------------------------------------------------------
|
||||
// RenderTreeFrameType.Attribute
|
||||
// --------------------------------------------------------------------------------
|
||||
|
||||
/// <summary>
|
||||
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Attribute"/>
|
||||
/// gets the ID of the corresponding event handler, if any.
|
||||
/// </summary>
|
||||
[FieldOffset(8)] public readonly int AttributeEventHandlerId;
|
||||
|
||||
/// <summary>
|
||||
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Attribute"/>,
|
||||
/// gets the attribute name. Otherwise, the value is undefined.
|
||||
/// </summary>
|
||||
[FieldOffset(16)] public readonly string AttributeName;
|
||||
|
||||
/// <summary>
|
||||
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Attribute"/>,
|
||||
/// gets the attribute value. Otherwise, the value is undefined.
|
||||
/// </summary>
|
||||
[FieldOffset(24)] public readonly object AttributeValue;
|
||||
|
||||
// --------------------------------------------------------------------------------
|
||||
// RenderTreeFrameType.Component
|
||||
// --------------------------------------------------------------------------------
|
||||
|
||||
/// <summary>
|
||||
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Component"/>
|
||||
/// gets the number of frames in the subtree for which this frame is the root.
|
||||
/// The value is zero if the frame has not yet been closed.
|
||||
/// </summary>
|
||||
[FieldOffset(8)] public readonly int ComponentSubtreeLength;
|
||||
|
||||
/// <summary>
|
||||
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Component"/>,
|
||||
/// gets the child component instance identifier.
|
||||
/// </summary>
|
||||
[FieldOffset(12)] public readonly int ComponentId;
|
||||
|
||||
/// <summary>
|
||||
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Component"/>,
|
||||
/// gets the type of the child component.
|
||||
/// </summary>
|
||||
[FieldOffset(16)] public readonly Type ComponentType;
|
||||
|
||||
/// <summary>
|
||||
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Component"/>,
|
||||
/// gets the child component instance. Otherwise, the value is undefined.
|
||||
/// </summary>
|
||||
public IComponent Component => null;
|
||||
|
||||
// --------------------------------------------------------------------------------
|
||||
// RenderTreeFrameType.Region
|
||||
// --------------------------------------------------------------------------------
|
||||
|
||||
/// <summary>
|
||||
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Region"/>
|
||||
/// gets the number of frames in the subtree for which this frame is the root.
|
||||
/// The value is zero if the frame has not yet been closed.
|
||||
/// </summary>
|
||||
[FieldOffset(8)] public readonly int RegionSubtreeLength;
|
||||
|
||||
// --------------------------------------------------------------------------------
|
||||
// RenderTreeFrameType.ElementReferenceCapture
|
||||
// --------------------------------------------------------------------------------
|
||||
|
||||
/// <summary>
|
||||
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.ElementReferenceCapture"/>,
|
||||
/// gets the ID of the reference capture. Otherwise, the value is undefined.
|
||||
/// </summary>
|
||||
[FieldOffset(16)] public readonly string ElementReferenceCaptureId;
|
||||
|
||||
/// <summary>
|
||||
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.ElementReferenceCapture"/>,
|
||||
/// gets the action that writes the reference to its target. Otherwise, the value is undefined.
|
||||
/// </summary>
|
||||
[FieldOffset(24)] public readonly Action<ElementRef> ElementReferenceCaptureAction;
|
||||
|
||||
// --------------------------------------------------------------------------------
|
||||
// RenderTreeFrameType.ComponentReferenceCapture
|
||||
// --------------------------------------------------------------------------------
|
||||
|
||||
/// <summary>
|
||||
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.ComponentReferenceCapture"/>,
|
||||
/// gets the index of the parent frame representing the component being captured. Otherwise, the value is undefined.
|
||||
/// WARNING: This index can only be used in the context of the frame's original render tree. If the frame is
|
||||
/// copied elsewhere, such as to the ReferenceFrames buffer of a RenderTreeDiff, then the index will
|
||||
/// not relate to entries in that other buffer.
|
||||
/// Currently there's no scenario where this matters, but if there was, we could change all of the subtree
|
||||
/// initialization logic in RenderTreeDiffBuilder to walk the frames hierarchically, then it would know
|
||||
/// the parent index at the point where it wants to initialize the ComponentReferenceCapture frame.
|
||||
/// </summary>
|
||||
[FieldOffset(8)] public readonly int ComponentReferenceCaptureParentFrameIndex;
|
||||
|
||||
/// <summary>
|
||||
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.ComponentReferenceCapture"/>,
|
||||
/// gets the action that writes the reference to its target. Otherwise, the value is undefined.
|
||||
/// </summary>
|
||||
[FieldOffset(16)] public readonly Action<object> ComponentReferenceCaptureAction;
|
||||
|
||||
// --------------------------------------------------------------------------------
|
||||
// RenderTreeFrameType.Markup
|
||||
// --------------------------------------------------------------------------------
|
||||
|
||||
/// <summary>
|
||||
/// If the <see cref="FrameType"/> property equals <see cref="RenderTreeFrameType.Markup"/>,
|
||||
/// gets the content of the markup frame. Otherwise, the value is undefined.
|
||||
/// </summary>
|
||||
[FieldOffset(16)] public readonly string MarkupContent;
|
||||
|
||||
private RenderTreeFrame(int sequence, string elementName, int elementSubtreeLength)
|
||||
: this()
|
||||
{
|
||||
FrameType = RenderTreeFrameType.Element;
|
||||
Sequence = sequence;
|
||||
ElementName = elementName;
|
||||
ElementSubtreeLength = elementSubtreeLength;
|
||||
}
|
||||
|
||||
private RenderTreeFrame(int sequence, Type componentType, int componentSubtreeLength)
|
||||
: this()
|
||||
{
|
||||
FrameType = RenderTreeFrameType.Component;
|
||||
Sequence = sequence;
|
||||
ComponentType = componentType;
|
||||
ComponentSubtreeLength = componentSubtreeLength;
|
||||
}
|
||||
|
||||
private RenderTreeFrame(int sequence, string textContent)
|
||||
: this()
|
||||
{
|
||||
FrameType = RenderTreeFrameType.Text;
|
||||
Sequence = sequence;
|
||||
TextContent = textContent;
|
||||
}
|
||||
|
||||
private RenderTreeFrame(int sequence, string attributeName, object attributeValue)
|
||||
: this()
|
||||
{
|
||||
FrameType = RenderTreeFrameType.Attribute;
|
||||
Sequence = sequence;
|
||||
AttributeName = attributeName;
|
||||
AttributeValue = attributeValue;
|
||||
}
|
||||
|
||||
private RenderTreeFrame(int sequence, string attributeName, object attributeValue, int eventHandlerId)
|
||||
: this()
|
||||
{
|
||||
FrameType = RenderTreeFrameType.Attribute;
|
||||
Sequence = sequence;
|
||||
AttributeName = attributeName;
|
||||
AttributeValue = attributeValue;
|
||||
AttributeEventHandlerId = eventHandlerId;
|
||||
}
|
||||
|
||||
private RenderTreeFrame(int sequence, int regionSubtreeLength)
|
||||
: this()
|
||||
{
|
||||
FrameType = RenderTreeFrameType.Region;
|
||||
Sequence = sequence;
|
||||
RegionSubtreeLength = regionSubtreeLength;
|
||||
}
|
||||
|
||||
private RenderTreeFrame(int sequence, Action<ElementRef> elementReferenceCaptureAction, string elementReferenceCaptureId)
|
||||
: this()
|
||||
{
|
||||
FrameType = RenderTreeFrameType.ElementReferenceCapture;
|
||||
Sequence = sequence;
|
||||
ElementReferenceCaptureAction = elementReferenceCaptureAction;
|
||||
ElementReferenceCaptureId = elementReferenceCaptureId;
|
||||
}
|
||||
|
||||
private RenderTreeFrame(int sequence, Action<object> componentReferenceCaptureAction, int parentFrameIndex)
|
||||
: this()
|
||||
{
|
||||
FrameType = RenderTreeFrameType.ComponentReferenceCapture;
|
||||
Sequence = sequence;
|
||||
ComponentReferenceCaptureAction = componentReferenceCaptureAction;
|
||||
ComponentReferenceCaptureParentFrameIndex = parentFrameIndex;
|
||||
}
|
||||
|
||||
// If we need further constructors whose signatures clash with the patterns above,
|
||||
// we can add extra args to this general-purpose one.
|
||||
private RenderTreeFrame(int sequence, RenderTreeFrameType frameType, string markupContent)
|
||||
: this()
|
||||
{
|
||||
FrameType = frameType;
|
||||
Sequence = sequence;
|
||||
MarkupContent = markupContent;
|
||||
}
|
||||
|
||||
internal static RenderTreeFrame Element(int sequence, string elementName)
|
||||
=> new RenderTreeFrame(sequence, elementName: elementName, elementSubtreeLength: 0);
|
||||
|
||||
internal static RenderTreeFrame Text(int sequence, string textContent)
|
||||
=> new RenderTreeFrame(sequence, textContent: textContent);
|
||||
|
||||
internal static RenderTreeFrame Markup(int sequence, string markupContent)
|
||||
=> new RenderTreeFrame(sequence, RenderTreeFrameType.Markup, markupContent);
|
||||
|
||||
internal static RenderTreeFrame Attribute(int sequence, string name, MulticastDelegate value)
|
||||
=> new RenderTreeFrame(sequence, attributeName: name, attributeValue: value);
|
||||
|
||||
internal static RenderTreeFrame Attribute(int sequence, string name, object value)
|
||||
=> new RenderTreeFrame(sequence, attributeName: name, attributeValue: value);
|
||||
|
||||
internal static RenderTreeFrame ChildComponent(int sequence, Type componentType)
|
||||
=> new RenderTreeFrame(sequence, componentType, 0);
|
||||
|
||||
internal static RenderTreeFrame PlaceholderChildComponentWithSubtreeLength(int subtreeLength)
|
||||
=> new RenderTreeFrame(0, typeof(IComponent), subtreeLength);
|
||||
|
||||
internal static RenderTreeFrame Region(int sequence)
|
||||
=> new RenderTreeFrame(sequence, regionSubtreeLength: 0);
|
||||
|
||||
internal static RenderTreeFrame ElementReferenceCapture(int sequence, Action<ElementRef> elementReferenceCaptureAction)
|
||||
=> new RenderTreeFrame(sequence, elementReferenceCaptureAction: elementReferenceCaptureAction, elementReferenceCaptureId: null);
|
||||
|
||||
internal static RenderTreeFrame ComponentReferenceCapture(int sequence, Action<object> componentReferenceCaptureAction, int parentFrameIndex)
|
||||
=> new RenderTreeFrame(sequence, componentReferenceCaptureAction: componentReferenceCaptureAction, parentFrameIndex: parentFrameIndex);
|
||||
|
||||
internal RenderTreeFrame WithElementSubtreeLength(int elementSubtreeLength)
|
||||
=> new RenderTreeFrame(Sequence, elementName: ElementName, elementSubtreeLength: elementSubtreeLength);
|
||||
|
||||
internal RenderTreeFrame WithComponentSubtreeLength(int componentSubtreeLength)
|
||||
=> new RenderTreeFrame(Sequence, componentType: ComponentType, componentSubtreeLength: componentSubtreeLength);
|
||||
|
||||
internal RenderTreeFrame WithAttributeSequence(int sequence)
|
||||
=> new RenderTreeFrame(sequence, attributeName: AttributeName, attributeValue: AttributeValue);
|
||||
|
||||
internal RenderTreeFrame WithAttributeEventHandlerId(int eventHandlerId)
|
||||
=> new RenderTreeFrame(Sequence, AttributeName, AttributeValue, eventHandlerId);
|
||||
|
||||
internal RenderTreeFrame WithRegionSubtreeLength(int regionSubtreeLength)
|
||||
=> new RenderTreeFrame(Sequence, regionSubtreeLength: regionSubtreeLength);
|
||||
|
||||
internal RenderTreeFrame WithElementReferenceCaptureId(string elementReferenceCaptureId)
|
||||
=> new RenderTreeFrame(Sequence, ElementReferenceCaptureAction, elementReferenceCaptureId);
|
||||
|
||||
/// <inheritdoc />
|
||||
// Just to be nice for debugging and unit tests.
|
||||
public override string ToString()
|
||||
{
|
||||
switch (FrameType)
|
||||
{
|
||||
case RenderTreeFrameType.Attribute:
|
||||
return $"Attribute: (seq={Sequence}, id={AttributeEventHandlerId}) '{AttributeName}'='{AttributeValue}'";
|
||||
|
||||
case RenderTreeFrameType.Component:
|
||||
return $"Component: (seq={Sequence}, len={ComponentSubtreeLength}) {ComponentType}";
|
||||
|
||||
case RenderTreeFrameType.Element:
|
||||
return $"Element: (seq={Sequence}, len={ElementSubtreeLength}) {ElementName}";
|
||||
|
||||
case RenderTreeFrameType.Region:
|
||||
return $"Region: (seq={Sequence}, len={RegionSubtreeLength})";
|
||||
|
||||
case RenderTreeFrameType.Text:
|
||||
return $"Text: (seq={Sequence}, len=n/a) {EscapeNewlines(TextContent)}";
|
||||
|
||||
case RenderTreeFrameType.Markup:
|
||||
return $"Markup: (seq={Sequence}, len=n/a) {EscapeNewlines(TextContent)}";
|
||||
|
||||
case RenderTreeFrameType.ElementReferenceCapture:
|
||||
return $"ElementReferenceCapture: (seq={Sequence}, len=n/a) {ElementReferenceCaptureAction}";
|
||||
}
|
||||
|
||||
return base.ToString();
|
||||
}
|
||||
|
||||
private static string EscapeNewlines(string text)
|
||||
{
|
||||
return text.Replace("\n", "\\n").Replace("\r\n", "\\r\\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
// 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.Components.RenderTree
|
||||
{
|
||||
/// <summary>
|
||||
/// Describes the type of a <see cref="RenderTreeFrame"/>.
|
||||
/// </summary>
|
||||
public enum RenderTreeFrameType : int
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a container for other frames.
|
||||
/// </summary>
|
||||
Element = 1,
|
||||
|
||||
/// <summary>
|
||||
/// Represents text content.
|
||||
/// </summary>
|
||||
Text = 2,
|
||||
|
||||
/// <summary>
|
||||
/// Represents a key-value pair associated with another <see cref="RenderTreeFrame"/>.
|
||||
/// </summary>
|
||||
Attribute = 3,
|
||||
|
||||
/// <summary>
|
||||
/// Represents a child component.
|
||||
/// </summary>
|
||||
Component = 4,
|
||||
|
||||
/// <summary>
|
||||
/// Defines the boundary around range of sibling frames that should be treated as an
|
||||
/// unsplittable group for the purposes of diffing. This is typically used when appending
|
||||
/// a tree fragment generated by external code, because the sequence numbers in that tree
|
||||
/// fragment are not comparable to sequence numbers outside it.
|
||||
/// </summary>
|
||||
Region = 5,
|
||||
|
||||
/// <summary>
|
||||
/// Represents an instruction to capture or update a reference to the parent element.
|
||||
/// </summary>
|
||||
ElementReferenceCapture = 6,
|
||||
|
||||
/// <summary>
|
||||
/// Represents an instruction to capture or update a reference to the parent component.
|
||||
/// </summary>
|
||||
ComponentReferenceCapture = 7,
|
||||
|
||||
/// <summary>
|
||||
/// Represents a block of markup content.
|
||||
/// </summary>
|
||||
Markup = 8,
|
||||
}
|
||||
}
|
||||
|
|
@ -164,7 +164,7 @@ namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests
|
|||
return projectItem;
|
||||
}
|
||||
|
||||
protected RazorProjectItem CreateProjectItemFromFile(string filePath = null)
|
||||
protected RazorProjectItem CreateProjectItemFromFile(string filePath = null, string fileKind = null)
|
||||
{
|
||||
if (FileName == null)
|
||||
{
|
||||
|
|
@ -201,7 +201,8 @@ namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests
|
|||
basePath: workingDirectory,
|
||||
filePath: filePath,
|
||||
physicalPath: fullPath,
|
||||
relativePhysicalPath: sourceFileName)
|
||||
relativePhysicalPath: sourceFileName,
|
||||
fileKind: fileKind)
|
||||
{
|
||||
Content = fileContent,
|
||||
};
|
||||
|
|
@ -321,10 +322,14 @@ namespace Microsoft.AspNetCore.Razor.Language.IntegrationTests
|
|||
});
|
||||
}
|
||||
|
||||
// Decorate the import feature so we can normalize line endings.
|
||||
var importFeature = b.Features.OfType<IImportProjectFeature>().FirstOrDefault();
|
||||
b.Features.Add(new NormalizedDefaultImportFeature(importFeature, LineEnding));
|
||||
b.Features.Remove(importFeature);
|
||||
b.Features.Add(new DefaultTypeNameFeature());
|
||||
|
||||
// Decorate each import feature so we can normalize line endings.
|
||||
foreach (var feature in b.Features.OfType<IImportProjectFeature>().ToArray())
|
||||
{
|
||||
b.Features.Remove(feature);
|
||||
b.Features.Add(new NormalizedDefaultImportFeature(feature, LineEnding));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue