From 0c1c05efc6b4e08902caca52fc61de1c91417fc9 Mon Sep 17 00:00:00 2001 From: Ryan Nowak Date: Fri, 21 Dec 2018 12:35:48 -0800 Subject: [PATCH] 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 https://github.com/dotnet/aspnetcore-tooling/commit/a04addb86f88cf1321e27d0e9f5440585934cffc --- .../src/MvcImportProjectFeature.cs | 6 + .../src/RazorExtensions.cs | 2 +- .../test/InjectDirectiveTest.cs | 4 +- .../test/ModelDirectiveTest.cs | 20 +- .../test/MvcRazorTemplateEngineTest.cs | 10 +- .../test/MvcViewDocumentClassifierPassTest.cs | 6 +- .../src/MvcImportProjectFeature.cs | 6 + .../src/RazorExtensions.cs | 3 +- .../src/RazorPageDocumentClassifierPass.cs | 2 +- .../AssemblyAttributeInjectionPassTest.cs | 24 +- .../InstrumentationPassIntegrationTest.cs | 16 + .../test/ModelDirectiveTest.cs | 22 +- .../test/MvcViewDocumentClassifierPassTest.cs | 6 +- .../RazorPageDocumentClassifierPassTest.cs | 41 +- .../BasicTest.codegen.cs | 48 +- .../BasicTest.ir.txt | 62 +- .../src/MvcImportProjectFeature.cs | 6 + .../src/RazorExtensions.cs | 5 +- .../src/RazorPageDocumentClassifierPass.cs | 2 +- .../test/InjectDirectiveTest.cs | 16 +- .../CodeGenerationIntegrationTest.cs | 29 + .../test/ModelDirectiveTest.cs | 27 +- .../test/MvcRazorTemplateEngineTest.cs | 11 +- .../test/MvcViewDocumentClassifierPassTest.cs | 6 +- .../RazorPageDocumentClassifierPassTest.cs | 36 +- .../BasicComponent.cshtml | 9 + .../BasicComponent_DesignTime.codegen.cs | 53 ++ .../BasicComponent_DesignTime.ir.txt | 37 + .../BasicComponent_DesignTime.mappings.txt | 24 + .../BasicComponent_Runtime.codegen.cs | 34 + .../BasicComponent_Runtime.ir.txt | 24 + .../CodeGeneration/DefaultDocumentWriter.cs | 40 + .../CodeGeneration/IntermediateNodeWriter.cs | 41 + .../Components/BlazorCSharpLoweringPhase.cs | 42 - .../Components/BlazorExtensionInitializer.cs | 130 --- .../src/Components/CodeWriterExtensions.cs | 647 --------------- ...ngPass.cs => ComponentBindLoweringPass.cs} | 7 +- ...=> ComponentChildContentDiagnosticPass.cs} | 8 +- ...orCodeTarget.cs => ComponentCodeTarget.cs} | 15 +- ...> ComponentComplexAttributeContentPass.cs} | 10 +- ...er.cs => ComponentDesignTimeNodeWriter.cs} | 8 +- .../ComponentDocumentClassifierPass.cs | 25 +- ...s => ComponentEventHandlerLoweringPass.cs} | 7 +- .../src/Components/ComponentExtensions.cs | 22 - ...entPass.cs => ComponentGenericTypePass.cs} | 37 +- ...BlockPass.cs => ComponentHtmlBlockPass.cs} | 35 +- ...re.cs => ComponentImportProjectFeature.cs} | 22 +- .../Components/ComponentInjectDirective.cs | 36 + .../ComponentInjectDirectivePass.cs | 58 ++ .../ComponentInjectIntermediateNode.cs | 60 ++ .../ComponentIntermediateNodePassBase.cs | 21 + ...rective.cs => ComponentLayoutDirective.cs} | 7 +- ...ass.cs => ComponentLayoutDirectivePass.cs} | 4 +- .../src/Components/ComponentLoweringPass.cs | 7 +- ...orNodeWriter.cs => ComponentNodeWriter.cs} | 21 +- ...Directive.cs => ComponentPageDirective.cs} | 9 +- ...ePass.cs => ComponentPageDirectivePass.cs} | 6 +- ... ComponentReferenceCaptureLoweringPass.cs} | 8 +- ...riter.cs => ComponentRuntimeNodeWriter.cs} | 10 +- ...ptTagPass.cs => ComponentScriptTagPass.cs} | 10 +- ....cs => ComponentTemplateDiagnosticPass.cs} | 10 +- ...cs => ComponentTemplateTargetExtension.cs} | 6 +- ...tive.cs => ComponentTypeParamDirective.cs} | 5 +- ...pacePass.cs => ComponentWhitespacePass.cs} | 35 +- .../src/Components/InjectDirective.cs | 111 --- .../src/Components/RazorCompilerException.cs | 29 - ...efaultRazorCodeGenerationOptionsBuilder.cs | 7 +- ...eGenerationOptionsFactoryProjectFeature.cs | 4 +- .../src/DefaultRazorDirectiveFeature.cs | 18 + ...faultRazorIntermediateNodeLoweringPhase.cs | 783 ++++++++++++++++-- .../src/DefaultRazorParserOptionsBuilder.cs | 5 +- ...RazorParserOptionsFactoryProjectFeature.cs | 4 +- .../src/DefaultRazorProjectEngine.cs | 158 +++- .../src/DocumentClassifierPassBase.cs | 19 +- .../ImplementsDirective.cs | 3 +- .../ImplementsDirectivePass.cs | 4 +- .../src/Extensions/MetadataAttributePass.cs | 8 + .../src/FileKindDirectiveFeature.cs | 10 + .../src/FileKinds.cs | 2 +- ...eGenerationOptionsFactoryProjectFeature.cs | 2 +- ...RazorParserOptionsFactoryProjectFeature.cs | 2 +- .../Intermediate/ComponentIntermediateNode.cs | 2 +- .../Intermediate/IntermediateNodeVisitor.cs | 10 + .../MarkupBlockIntermediateNode.cs | 24 +- .../MarkupElementIntermediateNode.cs | 55 +- .../src/ItemCollection.cs | 11 +- .../src/RazorCodeGenerationOptionsBuilder.cs | 2 + .../src/RazorParserFeatureFlags.cs | 14 + .../src/RazorParserOptionsBuilder.cs | 2 + .../src/RazorProjectEngine.cs | 106 +-- .../RazorProjectEngineBuilderExtensions.cs | 70 +- .../src/BindTagHelperDescriptorProvider.cs | 2 +- ...EventHandlerTagHelperDescriptorProvider.cs | 2 +- .../src/Properties/AssemblyInfo.cs | 4 +- .../BaseTagHelperDescriptorProviderTest.cs | 4 +- .../BindTagHelperDescriptorProviderTest.cs | 2 +- ...tHandlerTagHelperDescriptorProviderTest.cs | 2 +- .../Components/RenderTreeFrame.cs | 344 ++++++++ .../Components/RenderTreeFrameType.cs | 54 ++ .../IntegrationTests/IntegrationTestBase.cs | 17 +- .../IntermediateNodeWriter.cs | 52 +- .../IntializeTestFileAttribute.cs | 11 +- .../RazorBaselineIntegrationTestBase.cs | 209 +++++ .../RazorIntegrationTestBase.cs | 470 +++++++++++ .../Language/RazorProjectEngineTestBase.cs | 34 + .../Language/TestFile.cs | 2 +- ...rosoft.AspNetCore.Razor.Test.Common.csproj | 1 + .../BindAttributes.cs | 26 + .../ComponentBase.cs | 2 +- .../EventHandlers.cs | 128 +++ .../MarkupString.cs | 36 + .../RenderTree/RenderTreeBuilder.cs | 94 ++- .../RouteAttribute.cs | 33 + .../RuntimeHelpers.cs | 20 + .../UIEventArgsRenderTreeBuilderExtensions.cs | 547 ++++++++++++ 115 files changed, 4089 insertions(+), 1478 deletions(-) create mode 100644 src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent.cshtml create mode 100644 src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent_DesignTime.codegen.cs create mode 100644 src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent_DesignTime.ir.txt create mode 100644 src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent_DesignTime.mappings.txt create mode 100644 src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent_Runtime.codegen.cs create mode 100644 src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent_Runtime.ir.txt delete mode 100644 src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/BlazorCSharpLoweringPhase.cs delete mode 100644 src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/BlazorExtensionInitializer.cs delete mode 100644 src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/CodeWriterExtensions.cs rename src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/{BindLoweringPass.cs => ComponentBindLoweringPass.cs} (99%) rename src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/{ChildContentDiagnosticPass.cs => ComponentChildContentDiagnosticPass.cs} (93%) rename src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/{BlazorCodeTarget.cs => ComponentCodeTarget.cs} (71%) rename src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/{ComplexAttributeContentPass.cs => ComponentComplexAttributeContentPass.cs} (94%) rename src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/{BlazorDesignTimeNodeWriter.cs => ComponentDesignTimeNodeWriter.cs} (98%) rename src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/{EventHandlerLoweringPass.cs => ComponentEventHandlerLoweringPass.cs} (97%) delete mode 100644 src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentExtensions.cs rename src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/{GenericComponentPass.cs => ComponentGenericTypePass.cs} (91%) rename src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/{HtmlBlockPass.cs => ComponentHtmlBlockPass.cs} (91%) rename src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/{BlazorImportProjectFeature.cs => ComponentImportProjectFeature.cs} (79%) create mode 100644 src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentInjectDirective.cs create mode 100644 src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentInjectDirectivePass.cs create mode 100644 src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentInjectIntermediateNode.cs create mode 100644 src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentIntermediateNodePassBase.cs rename src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/{LayoutDirective.cs => ComponentLayoutDirective.cs} (83%) rename src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/{LayoutDirectivePass.cs => ComponentLayoutDirectivePass.cs} (92%) rename src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/{BlazorNodeWriter.cs => ComponentNodeWriter.cs} (88%) rename src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/{PageDirective.cs => ComponentPageDirective.cs} (82%) rename src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/{PageDirectivePass.cs => ComponentPageDirectivePass.cs} (93%) rename src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/{ReferenceCaptureLoweringPass.cs => ComponentReferenceCaptureLoweringPass.cs} (93%) rename src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/{BlazorRuntimeNodeWriter.cs => ComponentRuntimeNodeWriter.cs} (98%) rename src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/{ScriptTagPass.cs => ComponentScriptTagPass.cs} (81%) rename src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/{TemplateDiagnosticPass.cs => ComponentTemplateDiagnosticPass.cs} (91%) rename src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/{BlazorTemplateTargetExtension.cs => ComponentTemplateTargetExtension.cs} (62%) rename src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/{TypeParamDirective.cs => ComponentTypeParamDirective.cs} (89%) rename src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/{TrimWhitespacePass.cs => ComponentWhitespacePass.cs} (80%) delete mode 100644 src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/InjectDirective.cs delete mode 100644 src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/RazorCompilerException.cs rename src/Razor/Microsoft.AspNetCore.Razor.Language/src/{Components => Extensions}/ImplementsDirective.cs (91%) rename src/Razor/Microsoft.AspNetCore.Razor.Language/src/{Components => Extensions}/ImplementsDirectivePass.cs (88%) create mode 100644 src/Razor/Microsoft.AspNetCore.Razor.Language/src/FileKindDirectiveFeature.cs create mode 100644 src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common/Components/RenderTreeFrame.cs create mode 100644 src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common/Components/RenderTreeFrameType.cs create mode 100644 src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common/Language/IntegrationTests/RazorBaselineIntegrationTestBase.cs create mode 100644 src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common/Language/IntegrationTests/RazorIntegrationTestBase.cs create mode 100644 src/Razor/test/Microsoft.AspNetCore.Razor.Test.Common/Language/RazorProjectEngineTestBase.cs create mode 100644 src/Razor/test/Microsoft.AspNetCore.Razor.Test.ComponentShim/Microsoft.AspNetCore.Components/BindAttributes.cs create mode 100644 src/Razor/test/Microsoft.AspNetCore.Razor.Test.ComponentShim/Microsoft.AspNetCore.Components/EventHandlers.cs create mode 100644 src/Razor/test/Microsoft.AspNetCore.Razor.Test.ComponentShim/Microsoft.AspNetCore.Components/MarkupString.cs create mode 100644 src/Razor/test/Microsoft.AspNetCore.Razor.Test.ComponentShim/Microsoft.AspNetCore.Components/RouteAttribute.cs create mode 100644 src/Razor/test/Microsoft.AspNetCore.Razor.Test.ComponentShim/Microsoft.AspNetCore.Components/RuntimeHelpers.cs create mode 100644 src/Razor/test/Microsoft.AspNetCore.Razor.Test.ComponentShim/Microsoft.AspNetCore.Components/UIEventArgsRenderTreeBuilderExtensions.cs diff --git a/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X/src/MvcImportProjectFeature.cs b/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X/src/MvcImportProjectFeature.cs index 254c57c1c4..e117a5298c 100644 --- a/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X/src/MvcImportProjectFeature.cs +++ b/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X/src/MvcImportProjectFeature.cs @@ -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(); + } + var imports = new List(); AddDefaultDirectivesImport(imports); diff --git a/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X/src/RazorExtensions.cs b/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X/src/RazorExtensions.cs index 55e8929457..f8cf7f5cb4 100644 --- a/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X/src/RazorExtensions.cs +++ b/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X/src/RazorExtensions.cs @@ -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) diff --git a/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X/test/InjectDirectiveTest.cs b/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X/test/InjectDirectiveTest.cs index a5579d645a..63560c0f1f 100644 --- a/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X/test/InjectDirectiveTest.cs +++ b/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X/test/InjectDirectiveTest.cs @@ -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()); + 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); diff --git a/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X/test/ModelDirectiveTest.cs b/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X/test/ModelDirectiveTest.cs index cc6cf53097..7a4c811dbd 100644 --- a/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X/test/ModelDirectiveTest.cs +++ b/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X/test/ModelDirectiveTest.cs @@ -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 "); - 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); diff --git a/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X/test/MvcRazorTemplateEngineTest.cs b/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X/test/MvcRazorTemplateEngineTest.cs index 45186848ae..eca2b8d49b 100644 --- a/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X/test/MvcRazorTemplateEngineTest.cs +++ b/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X/test/MvcRazorTemplateEngineTest.cs @@ -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 diff --git a/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X/test/MvcViewDocumentClassifierPassTest.cs b/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X/test/MvcViewDocumentClassifierPassTest.cs index 663aff4518..a0a3825267 100644 --- a/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X/test/MvcViewDocumentClassifierPassTest.cs +++ b/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version1_X/test/MvcViewDocumentClassifierPassTest.cs @@ -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++) diff --git a/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X/src/MvcImportProjectFeature.cs b/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X/src/MvcImportProjectFeature.cs index c25cb0d063..d7844d9eee 100644 --- a/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X/src/MvcImportProjectFeature.cs +++ b/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X/src/MvcImportProjectFeature.cs @@ -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(); + } + var imports = new List(); AddDefaultDirectivesImport(imports); diff --git a/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X/src/RazorExtensions.cs b/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X/src/RazorExtensions.cs index 69cf7e2055..0e52eb00d1 100644 --- a/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X/src/RazorExtensions.cs +++ b/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X/src/RazorExtensions.cs @@ -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()); } } } diff --git a/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X/src/RazorPageDocumentClassifierPass.cs b/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X/src/RazorPageDocumentClassifierPass.cs index a5de7d0783..b11912ff81 100644 --- a/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X/src/RazorPageDocumentClassifierPass.cs +++ b/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X/src/RazorPageDocumentClassifierPass.cs @@ -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()), RazorProjectFileSystem.Create("/"), builder => { diff --git a/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X/test/AssemblyAttributeInjectionPassTest.cs b/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X/test/AssemblyAttributeInjectionPassTest.cs index 4dbe6c72b4..29127a5cd3 100644 --- a/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X/test/AssemblyAttributeInjectionPassTest.cs +++ b/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X/test/AssemblyAttributeInjectionPassTest.cs @@ -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")); diff --git a/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X/test/IntegrationTests/InstrumentationPassIntegrationTest.cs b/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X/test/IntegrationTests/InstrumentationPassIntegrationTest.cs index 604100a335..58d6a46df0 100644 --- a/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X/test/IntegrationTests/InstrumentationPassIntegrationTest.cs +++ b/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X/test/IntegrationTests/InstrumentationPassIntegrationTest.cs @@ -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() { diff --git a/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X/test/ModelDirectiveTest.cs b/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X/test/ModelDirectiveTest.cs index eedc94d581..d01195906b 100644 --- a/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X/test/ModelDirectiveTest.cs +++ b/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X/test/ModelDirectiveTest.cs @@ -1,8 +1,6 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using 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 "); - 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); diff --git a/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X/test/MvcViewDocumentClassifierPassTest.cs b/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X/test/MvcViewDocumentClassifierPassTest.cs index 2d437c158d..455ed1ba21 100644 --- a/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X/test/MvcViewDocumentClassifierPassTest.cs +++ b/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X/test/MvcViewDocumentClassifierPassTest.cs @@ -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++) diff --git a/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X/test/RazorPageDocumentClassifierPassTest.cs b/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X/test/RazorPageDocumentClassifierPassTest.cs index 74acfa0c13..8e9f111579 100644 --- a/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X/test/RazorPageDocumentClassifierPassTest.cs +++ b/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X/test/RazorPageDocumentClassifierPassTest.cs @@ -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("

Hello World

", "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++) diff --git a/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X/test/TestFiles/IntegrationTests/InstrumentationPassIntegrationTest/BasicTest.codegen.cs b/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X/test/TestFiles/IntegrationTests/InstrumentationPassIntegrationTest/BasicTest.codegen.cs index 8c5b2e0f79..804cb2af89 100644 --- a/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X/test/TestFiles/IntegrationTests/InstrumentationPassIntegrationTest/BasicTest.codegen.cs +++ b/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X/test/TestFiles/IntegrationTests/InstrumentationPassIntegrationTest/BasicTest.codegen.cs @@ -1,12 +1,20 @@ #pragma checksum "TestFiles/IntegrationTests/InstrumentationPassIntegrationTest/BasicTest.cshtml" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "78993008d95836bec2b9175d4294bf7bd5f5f109" // #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 { 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("Hola\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(); @@ -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\nHere is some content "); EndContext(); + EndContext(); + BeginContext(207, 9, true); BeginContext(207, 9, true); WriteLiteral("\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("Hello world"); 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 Html { get; private set; } } } #pragma warning restore 1591 diff --git a/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X/test/TestFiles/IntegrationTests/InstrumentationPassIntegrationTest/BasicTest.ir.txt b/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X/test/TestFiles/IntegrationTests/InstrumentationPassIntegrationTest/BasicTest.ir.txt index 6ac51fb0e8..a54315062c 100644 --- a/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X/test/TestFiles/IntegrationTests/InstrumentationPassIntegrationTest/BasicTest.ir.txt +++ b/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions.Version2_X/test/TestFiles/IntegrationTests/InstrumentationPassIntegrationTest/BasicTest.ir.txt @@ -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 - 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 - 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 - diff --git a/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions/src/MvcImportProjectFeature.cs b/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions/src/MvcImportProjectFeature.cs index 5c6ee7361d..5a56cd19d0 100644 --- a/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions/src/MvcImportProjectFeature.cs +++ b/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions/src/MvcImportProjectFeature.cs @@ -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(); + } + var imports = new List(); AddDefaultDirectivesImport(imports); diff --git a/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions/src/RazorExtensions.cs b/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions/src/RazorExtensions.cs index 99e202724a..26e641d7aa 100644 --- a/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions/src/RazorExtensions.cs +++ b/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions/src/RazorExtensions.cs @@ -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()); } } } diff --git a/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions/src/RazorPageDocumentClassifierPass.cs b/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions/src/RazorPageDocumentClassifierPass.cs index c6c0df9345..96a05cfc22 100644 --- a/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions/src/RazorPageDocumentClassifierPass.cs +++ b/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions/src/RazorPageDocumentClassifierPass.cs @@ -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()), RazorProjectFileSystem.Create("/"), builder => { diff --git a/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/InjectDirectiveTest.cs b/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/InjectDirectiveTest.cs index 54a2fa4fd1..a935b23bfb 100644 --- a/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/InjectDirectiveTest.cs +++ b/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/InjectDirectiveTest.cs @@ -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) diff --git a/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/IntegrationTests/CodeGenerationIntegrationTest.cs b/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/IntegrationTests/CodeGenerationIntegrationTest.cs index 8273810ca3..05d14e1184 100644 --- a/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/IntegrationTests/CodeGenerationIntegrationTest.cs +++ b/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/IntegrationTests/CodeGenerationIntegrationTest.cs @@ -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() { diff --git a/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/ModelDirectiveTest.cs b/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/ModelDirectiveTest.cs index 425b1b41c6..881557aa4d 100644 --- a/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/ModelDirectiveTest.cs +++ b/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/ModelDirectiveTest.cs @@ -1,8 +1,6 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using 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 "); - 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; } diff --git a/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/MvcRazorTemplateEngineTest.cs b/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/MvcRazorTemplateEngineTest.cs index f6c61c42a6..c5a25d29aa 100644 --- a/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/MvcRazorTemplateEngineTest.cs +++ b/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/MvcRazorTemplateEngineTest.cs @@ -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 diff --git a/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/MvcViewDocumentClassifierPassTest.cs b/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/MvcViewDocumentClassifierPassTest.cs index d7c0ae6854..3cb503f9fc 100644 --- a/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/MvcViewDocumentClassifierPassTest.cs +++ b/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/MvcViewDocumentClassifierPassTest.cs @@ -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++) diff --git a/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/RazorPageDocumentClassifierPassTest.cs b/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/RazorPageDocumentClassifierPassTest.cs index a0cdbd7f80..286edb49bf 100644 --- a/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/RazorPageDocumentClassifierPassTest.cs +++ b/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/RazorPageDocumentClassifierPassTest.cs @@ -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("

Hello World

", "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; diff --git a/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent.cshtml b/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent.cshtml new file mode 100644 index 0000000000..85206b7b78 --- /dev/null +++ b/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent.cshtml @@ -0,0 +1,9 @@ +@implements IDisposable +
+ Hello world + @string.Format("{0}", "Hello") +
+ +@functions { + void IDisposable.Dispose(){ } +} diff --git a/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent_DesignTime.codegen.cs b/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent_DesignTime.codegen.cs new file mode 100644 index 0000000000..5fe69361a5 --- /dev/null +++ b/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent_DesignTime.codegen.cs @@ -0,0 +1,53 @@ +// +#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 diff --git a/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent_DesignTime.ir.txt b/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent_DesignTime.ir.txt new file mode 100644 index 0000000000..73d65846a2 --- /dev/null +++ b/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent_DesignTime.ir.txt @@ -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 diff --git a/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent_DesignTime.mappings.txt b/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent_DesignTime.mappings.txt new file mode 100644 index 0000000000..abc4f51e55 --- /dev/null +++ b/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent_DesignTime.mappings.txt @@ -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(){ } +| + diff --git a/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent_Runtime.codegen.cs b/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent_Runtime.codegen.cs new file mode 100644 index 0000000000..65fa29f408 --- /dev/null +++ b/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent_Runtime.codegen.cs @@ -0,0 +1,34 @@ +#pragma checksum "TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent.cshtml" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "d3c3d6059615673cb46fc4974164d61eabadb890" +// +#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 diff --git a/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent_Runtime.ir.txt b/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent_Runtime.ir.txt new file mode 100644 index 0000000000..58d2bd23dd --- /dev/null +++ b/src/Razor/Microsoft.AspNetCore.Mvc.Razor.Extensions/test/TestFiles/IntegrationTests/CodeGenerationIntegrationTest/BasicComponent_Runtime.ir.txt @@ -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 diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/CodeGeneration/DefaultDocumentWriter.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/CodeGeneration/DefaultDocumentWriter.cs index a47295143d..8a6865f7ed 100644 --- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/CodeGeneration/DefaultDocumentWriter.cs +++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/CodeGeneration/DefaultDocumentWriter.cs @@ -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); diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/CodeGeneration/IntermediateNodeWriter.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/CodeGeneration/IntermediateNodeWriter.cs index d8c636f064..c8a3c6659a 100644 --- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/CodeGeneration/IntermediateNodeWriter.cs +++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/CodeGeneration/IntermediateNodeWriter.cs @@ -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); diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/BlazorCSharpLoweringPhase.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/BlazorCSharpLoweringPhase.cs deleted file mode 100644 index fd9a026f13..0000000000 --- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/BlazorCSharpLoweringPhase.cs +++ /dev/null @@ -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(); - } - } - } -} \ No newline at end of file diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/BlazorExtensionInitializer.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/BlazorExtensionInitializer.cs deleted file mode 100644 index 6a03dc07e8..0000000000 --- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/BlazorExtensionInitializer.cs +++ /dev/null @@ -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 -{ - /// - /// Initializes the Blazor extension. - /// - public class BlazorExtensionInitializer : RazorExtensionInitializer - { - /// - /// Specifies the declaration configuration. - /// - public static readonly RazorConfiguration DeclarationConfiguration; - - /// - /// Specifies the default configuration. - /// - 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()); - - DefaultConfiguration = RazorConfiguration.Create( - RazorLanguageVersion.Experimental, - "Blazor-0.1", - Array.Empty()); - } - - /// - /// Registers the Blazor extension. - /// - /// The . - 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().Single()); - builder.Features.Add(new BlazorImportProjectFeature()); - - var index = builder.Phases.IndexOf(builder.Phases.OfType().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()); - } - } - - /// - /// Initializes the Blazor extension. - /// - /// The . - 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; - } - } - } -} diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/CodeWriterExtensions.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/CodeWriterExtensions.cs deleted file mode 100644 index e458d88d6a..0000000000 --- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/CodeWriterExtensions.cs +++ /dev/null @@ -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 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 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 modifiers, - string name, - string baseType, - IEnumerable 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> 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() - { - } - } - } -} diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/BindLoweringPass.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentBindLoweringPass.cs similarity index 99% rename from src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/BindLoweringPass.cs rename to src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentBindLoweringPass.cs index 34fdd2d26c..9e979b515f 100644 --- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/BindLoweringPass.cs +++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentBindLoweringPass.cs @@ -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) diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ChildContentDiagnosticPass.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentChildContentDiagnosticPass.cs similarity index 93% rename from src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ChildContentDiagnosticPass.cs rename to src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentChildContentDiagnosticPass.cs index c004e51bca..ea102fa9c9 100644 --- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ChildContentDiagnosticPass.cs +++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentChildContentDiagnosticPass.cs @@ -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); } diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/BlazorCodeTarget.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentCodeTarget.cs similarity index 71% rename from src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/BlazorCodeTarget.cs rename to src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentCodeTarget.cs index 1437e5432b..a106dd5d5d 100644 --- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/BlazorCodeTarget.cs +++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentCodeTarget.cs @@ -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 { - /// - /// Directs a to use . - /// - internal class BlazorCodeTarget : CodeTarget + internal class ComponentCodeTarget : CodeTarget { private readonly RazorCodeGenerationOptions _options; - public BlazorCodeTarget(RazorCodeGenerationOptions options, IEnumerable extensions) + public ComponentCodeTarget(RazorCodeGenerationOptions options, IEnumerable 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() diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComplexAttributeContentPass.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentComplexAttributeContentPass.cs similarity index 94% rename from src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComplexAttributeContentPass.cs rename to src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentComplexAttributeContentPass.cs index f500fedcb2..8b7c73d306 100644 --- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComplexAttributeContentPass.cs +++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentComplexAttributeContentPass.cs @@ -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(); for (var i = 0; i < nodes.Count; i++) { diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/BlazorDesignTimeNodeWriter.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentDesignTimeNodeWriter.cs similarity index 98% rename from src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/BlazorDesignTimeNodeWriter.cs rename to src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentDesignTimeNodeWriter.cs index 753c220e87..da3c03b2d8 100644 --- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/BlazorDesignTimeNodeWriter.cs +++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentDesignTimeNodeWriter.cs @@ -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) { diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentDocumentClassifierPass.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentDocumentClassifierPass.cs index dbf5bb100d..08b9dc7b03 100644 --- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentDocumentClassifierPass.cs +++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentDocumentClassifierPass.cs @@ -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(); diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/EventHandlerLoweringPass.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentEventHandlerLoweringPass.cs similarity index 97% rename from src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/EventHandlerLoweringPass.cs rename to src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentEventHandlerLoweringPass.cs index 4ee38ed992..7a731dabdd 100644 --- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/EventHandlerLoweringPass.cs +++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentEventHandlerLoweringPass.cs @@ -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) diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentExtensions.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentExtensions.cs deleted file mode 100644 index 6f7a2affa4..0000000000 --- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentExtensions.cs +++ /dev/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()); - } - } -} diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/GenericComponentPass.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentGenericTypePass.cs similarity index 91% rename from src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/GenericComponentPass.cs rename to src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentGenericTypePass.cs index f1932742a0..a3646ba767 100644 --- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/GenericComponentPass.cs +++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentGenericTypePass.cs @@ -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(); + 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(); + } + + 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())); } diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/HtmlBlockPass.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentHtmlBlockPass.cs similarity index 91% rename from src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/HtmlBlockPass.cs rename to src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentHtmlBlockPass.cs index e2cb256ff8..8327b33318 100644 --- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/HtmlBlockPass.cs +++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentHtmlBlockPass.cs @@ -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 + 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 + 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); + } + } } } } diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/BlazorImportProjectFeature.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentImportProjectFeature.cs similarity index 79% rename from src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/BlazorImportProjectFeature.cs rename to src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentImportProjectFeature.cs index a180d58f74..dea07160f1 100644 --- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/BlazorImportProjectFeature.cs +++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentImportProjectFeature.cs @@ -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(); + } + var imports = new List() { 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}""")); + } } } diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentInjectDirective.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentInjectDirective.cs new file mode 100644 index 0000000000..3c9923008b --- /dev/null +++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentInjectDirective.cs @@ -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()); + } + } +} diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentInjectDirectivePass.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentInjectDirectivePass.cs new file mode 100644 index 0000000000..6ad554b03a --- /dev/null +++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentInjectDirectivePass.cs @@ -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(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 Directives { get; } + = new List(); + + public override void VisitDirective(DirectiveIntermediateNode node) + { + if (node.Directive == ComponentInjectDirective.Directive) + { + Directives.Add(node); + } + } + } + } +} diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentInjectIntermediateNode.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentInjectIntermediateNode.cs new file mode 100644 index 0000000000..ee0363fd7e --- /dev/null +++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentInjectIntermediateNode.cs @@ -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 _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); + } + } +} diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentIntermediateNodePassBase.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentIntermediateNodePassBase.cs new file mode 100644 index 0000000000..5aa169ff4f --- /dev/null +++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentIntermediateNodePassBase.cs @@ -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); + } + } +} diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/LayoutDirective.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentLayoutDirective.cs similarity index 83% rename from src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/LayoutDirective.cs rename to src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentLayoutDirective.cs index 0d2d586260..c746b75d7b 100644 --- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/LayoutDirective.cs +++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentLayoutDirective.cs @@ -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()); } } } \ No newline at end of file diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/LayoutDirectivePass.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentLayoutDirectivePass.cs similarity index 92% rename from src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/LayoutDirectivePass.cs rename to src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentLayoutDirectivePass.cs index a22160570d..b1c36f3152 100644 --- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/LayoutDirectivePass.cs +++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentLayoutDirectivePass.cs @@ -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; diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentLoweringPass.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentLoweringPass.cs index c32df5738d..61fd26cad0 100644 --- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentLoweringPass.cs +++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentLoweringPass.cs @@ -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) diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/BlazorNodeWriter.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentNodeWriter.cs similarity index 88% rename from src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/BlazorNodeWriter.cs rename to src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentNodeWriter.cs index 256ffbee8f..0de9d18a89 100644 --- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/BlazorNodeWriter.cs +++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentNodeWriter.cs @@ -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) { diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/PageDirective.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentPageDirective.cs similarity index 82% rename from src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/PageDirective.cs rename to src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentPageDirective.cs index 08b5ec74b8..80ebd9ffca 100644 --- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/PageDirective.cs +++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentPageDirective.cs @@ -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; } } diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/PageDirectivePass.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentPageDirectivePass.cs similarity index 93% rename from src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/PageDirectivePass.cs rename to src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentPageDirectivePass.cs index 224b85b503..0fe0ac5e26 100644 --- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/PageDirectivePass.cs +++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentPageDirectivePass.cs @@ -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; diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ReferenceCaptureLoweringPass.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentReferenceCaptureLoweringPass.cs similarity index 93% rename from src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ReferenceCaptureLoweringPass.cs rename to src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentReferenceCaptureLoweringPass.cs index c5a9cbac02..aa66e07c8d 100644 --- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ReferenceCaptureLoweringPass.cs +++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentReferenceCaptureLoweringPass.cs @@ -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) diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/BlazorRuntimeNodeWriter.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentRuntimeNodeWriter.cs similarity index 98% rename from src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/BlazorRuntimeNodeWriter.cs rename to src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentRuntimeNodeWriter.cs index 4d7fc4db3c..8fcf48ec18 100644 --- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/BlazorRuntimeNodeWriter.cs +++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentRuntimeNodeWriter.cs @@ -15,7 +15,7 @@ namespace Microsoft.AspNetCore.Razor.Language.Components /// /// Generates the C# code corresponding to Razor source document contents. /// - internal class BlazorRuntimeNodeWriter : BlazorNodeWriter + internal class ComponentRuntimeNodeWriter : ComponentNodeWriter { private readonly List _currentAttributeValues = new List(); 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 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)}") diff --git a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ScriptTagPass.cs b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentScriptTagPass.cs similarity index 81% rename from src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ScriptTagPass.cs rename to src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentScriptTagPass.cs index 01086ec3ac..6fad5cfca5 100644 --- a/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ScriptTagPass.cs +++ b/src/Razor/Microsoft.AspNetCore.Razor.Language/src/Components/ComponentScriptTagPass.cs @@ -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 + private class Visitor : IntermediateNodeWalker { - public void VisitExtension(MarkupElementIntermediateNode node) + public override void VisitMarkupElement(MarkupElementIntermediateNode node) { // Disallow