diff --git a/src/Microsoft.AspNetCore.Blazor.Build/Core/RazorCompilation/RazorCompiler.cs b/src/Microsoft.AspNetCore.Blazor.Build/Core/RazorCompilation/RazorCompiler.cs
index ce3d4cb726..1fdb181f40 100644
--- a/src/Microsoft.AspNetCore.Blazor.Build/Core/RazorCompilation/RazorCompiler.cs
+++ b/src/Microsoft.AspNetCore.Blazor.Build/Core/RazorCompilation/RazorCompiler.cs
@@ -93,9 +93,9 @@ namespace Microsoft.AspNetCore.Blazor.Build.Core.RazorCompilation
// name and any public members. Don't need to actually emit all the RenderTreeBuilder
// invocations.
- var engine = new BlazorRazorEngine();
+ var engine = RazorEngine.Create(b => BlazorExtensionInitializer.Register(b));
var blazorTemplateEngine = new BlazorTemplateEngine(
- engine.Engine,
+ engine,
RazorProjectFileSystem.Create(inputRootPath));
var codeDoc = blazorTemplateEngine.CreateCodeDocument(
new BlazorProjectItem(inputRootPath, inputFilePath, inputFileContents));
diff --git a/src/Microsoft.AspNetCore.Blazor.Build/Microsoft.AspNetCore.Blazor.Build.csproj b/src/Microsoft.AspNetCore.Blazor.Build/Microsoft.AspNetCore.Blazor.Build.csproj
index ecdf6366d3..acaa68d5eb 100644
--- a/src/Microsoft.AspNetCore.Blazor.Build/Microsoft.AspNetCore.Blazor.Build.csproj
+++ b/src/Microsoft.AspNetCore.Blazor.Build/Microsoft.AspNetCore.Blazor.Build.csproj
@@ -31,6 +31,14 @@
+
+
+
+ PreserveNewest
+ false
+
+
+
diff --git a/src/Microsoft.AspNetCore.Blazor.Razor.Extensions/BlazorApi.cs b/src/Microsoft.AspNetCore.Blazor.Razor.Extensions/BlazorApi.cs
new file mode 100644
index 0000000000..ae9cfa0e47
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Blazor.Razor.Extensions/BlazorApi.cs
@@ -0,0 +1,52 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+namespace Microsoft.AspNetCore.Blazor.Razor
+{
+ // Constants for method names used in code-generation
+ // Keep these in sync with the actual definitions
+ internal static class BlazorApi
+ {
+ public static class BlazorComponent
+ {
+ public static readonly string FullTypeName = "Microsoft.AspNetCore.Blazor.Components.BlazorComponent";
+
+ public static readonly string BuildRenderTree = nameof(BuildRenderTree);
+ }
+
+ public static class RenderFragment
+ {
+ public static readonly string FullTypeName = "Microsoft.AspNetCore.Blazor.RenderFragment";
+ }
+
+ public static class RenderTreeBuilder
+ {
+ public static readonly string FullTypeName = "Microsoft.AspNetCore.Blazor.RenderTree.RenderTreeBuilder";
+
+ public static readonly string OpenElement = nameof(OpenElement);
+
+ public static readonly string CloseElement = nameof(CloseElement);
+
+ public static readonly string OpenComponent = nameof(OpenComponent);
+
+ public static readonly string CloseComponent = nameof(CloseElement);
+
+ public static readonly string AddContent = nameof(AddContent);
+
+ public static readonly string AddAttribute = nameof(AddAttribute);
+
+ public static readonly string Clear = nameof(Clear);
+
+ public static readonly string GetFrames = nameof(GetFrames);
+
+ public static readonly string ChildContent = nameof(ChildContent);
+ }
+
+ public static class BindMethods
+ {
+ public static readonly string GetValue = "Microsoft.AspNetCore.Blazor.Components.BindMethods.GetValue";
+
+ public static readonly string SetValue = "Microsoft.AspNetCore.Blazor.Components.BindMethods.SetValue";
+ }
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Blazor.Razor.Extensions/BlazorCodeTarget.cs b/src/Microsoft.AspNetCore.Blazor.Razor.Extensions/BlazorCodeTarget.cs
index c2f2557d6d..13d3e3ed41 100644
--- a/src/Microsoft.AspNetCore.Blazor.Razor.Extensions/BlazorCodeTarget.cs
+++ b/src/Microsoft.AspNetCore.Blazor.Razor.Extensions/BlazorCodeTarget.cs
@@ -1,7 +1,9 @@
// 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;
using Microsoft.AspNetCore.Razor.Language.CodeGeneration;
namespace Microsoft.AspNetCore.Blazor.Razor
@@ -11,11 +13,47 @@ namespace Microsoft.AspNetCore.Blazor.Razor
///
internal class BlazorCodeTarget : CodeTarget
{
+ private readonly RazorCodeGenerationOptions _options;
+
+ public BlazorCodeTarget(RazorCodeGenerationOptions options, IEnumerable extensions)
+ {
+ _options = options;
+ Extensions = extensions.ToArray();
+ }
+
+ public ICodeTargetExtension[] Extensions { get; }
+
public override IntermediateNodeWriter CreateNodeWriter()
- => new BlazorIntermediateNodeWriter();
+ {
+ return _options.DesignTime ? (IntermediateNodeWriter)new DesignTimeNodeWriter() : new BlazorIntermediateNodeWriter();
+ }
- public override TExtension GetExtension() => null;
+ public override TExtension GetExtension()
+ {
+ for (var i = 0; i < Extensions.Length; i++)
+ {
+ var match = Extensions[i] as TExtension;
+ if (match != null)
+ {
+ return match;
+ }
+ }
- public override bool HasExtension() => false;
+ return null;
+ }
+
+ public override bool HasExtension()
+ {
+ for (var i = 0; i < Extensions.Length; i++)
+ {
+ var match = Extensions[i] as TExtension;
+ if (match != null)
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
}
}
diff --git a/src/Microsoft.AspNetCore.Blazor.Razor.Extensions/BlazorComponent.cs b/src/Microsoft.AspNetCore.Blazor.Razor.Extensions/BlazorComponent.cs
deleted file mode 100644
index 634555a5e1..0000000000
--- a/src/Microsoft.AspNetCore.Blazor.Razor.Extensions/BlazorComponent.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-namespace Microsoft.AspNetCore.Blazor.Razor
-{
- internal static class BlazorComponent
- {
- public static readonly string FullTypeName = "Microsoft.AspNetCore.Blazor.Components.BlazorComponent";
-
- public static readonly string BuildRenderTree = nameof(BuildRenderTree);
- }
-}
diff --git a/src/Microsoft.AspNetCore.Blazor.Razor.Extensions/BlazorConfiguration.cs b/src/Microsoft.AspNetCore.Blazor.Razor.Extensions/BlazorConfiguration.cs
new file mode 100644
index 0000000000..4fcab4fcd0
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Blazor.Razor.Extensions/BlazorConfiguration.cs
@@ -0,0 +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;
+using Microsoft.AspNetCore.Razor.Language;
+
+namespace Microsoft.AspNetCore.Blazor.Razor
+{
+ public static class BlazorConfiguration
+ {
+ public static readonly RazorConfiguration Default = new RazorConfiguration(
+ RazorLanguageVersion.Version_2_1,
+ "Blazor-0.1",
+ Array.Empty());
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Blazor.Razor.Extensions/BlazorExtensionInitializer.cs b/src/Microsoft.AspNetCore.Blazor.Razor.Extensions/BlazorExtensionInitializer.cs
new file mode 100644
index 0000000000..8d0ba17784
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Blazor.Razor.Extensions/BlazorExtensionInitializer.cs
@@ -0,0 +1,83 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Linq;
+using Microsoft.AspNetCore.Razor.Language;
+using Microsoft.AspNetCore.Razor.Language.Extensions;
+
+namespace Microsoft.AspNetCore.Blazor.Razor
+{
+ public class BlazorExtensionInitializer : RazorExtensionInitializer
+ {
+ public static void Register(RazorProjectEngineBuilder builder)
+ {
+ if (builder == null)
+ {
+ throw new ArgumentNullException(nameof(builder));
+ }
+
+ FunctionsDirective.Register(builder);
+ InjectDirective.Register(builder);
+ InheritsDirective.Register(builder);
+ TemporaryLayoutPass.Register(builder);
+ TemporaryImplementsPass.Register(builder);
+
+ builder.Features.Remove(builder.Features.OfType().Single());
+ builder.Features.Add(new BlazorImportProjectFeature());
+
+ builder.Features.Add(new ConfigureBlazorCodeGenerationOptions());
+
+ builder.Features.Add(new ComponentDocumentClassifierPass());
+ }
+
+ // This is temporarily used to initialize a RazorEngine by the build tools until we get the features
+ // we need into the RazorProjectEngine (namespace).
+ public static void Register(IRazorEngineBuilder builder)
+ {
+ if (builder == null)
+ {
+ throw new ArgumentNullException(nameof(builder));
+ }
+
+ FunctionsDirective.Register(builder);
+ InjectDirective.Register(builder);
+ InheritsDirective.Register(builder);
+ TemporaryLayoutPass.Register(builder);
+ TemporaryImplementsPass.Register(builder);
+
+ builder.Features.Add(new ConfigureBlazorCodeGenerationOptions());
+
+ builder.Features.Add(new ComponentDocumentClassifierPass());
+ }
+
+ 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/Microsoft.AspNetCore.Blazor.Razor.Extensions/BlazorImportProjectFeature.cs b/src/Microsoft.AspNetCore.Blazor.Razor.Extensions/BlazorImportProjectFeature.cs
new file mode 100644
index 0000000000..d79794be42
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Blazor.Razor.Extensions/BlazorImportProjectFeature.cs
@@ -0,0 +1,79 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using Microsoft.AspNetCore.Razor.Language;
+
+namespace Microsoft.AspNetCore.Blazor.Razor
+{
+ internal class BlazorImportProjectFeature : IImportProjectFeature
+ {
+ private const string ImportsFileName = "_ViewImports.cshtml";
+
+ public RazorProjectItem DefaultImports => VirtualProjectItem.Instance;
+
+ public RazorProjectEngine ProjectEngine { get; set; }
+
+ public IReadOnlyList GetImports(RazorProjectItem projectItem)
+ {
+ if (projectItem == null)
+ {
+ throw new ArgumentNullException(nameof(projectItem));
+ }
+
+ var imports = new List()
+ {
+ VirtualProjectItem.Instance,
+ };
+
+ // We add hierarchical imports second so any default directive imports can be overridden.
+ imports.AddRange(GetHierarchicalImports(ProjectEngine.FileSystem, projectItem));
+
+ return imports;
+ }
+
+ // Temporary API until we fully convert to RazorProjectEngine
+ public IEnumerable GetHierarchicalImports(RazorProject project, RazorProjectItem projectItem)
+ {
+ // We want items in descending order. FindHierarchicalItems returns items in ascending order.
+ return project.FindHierarchicalItems(projectItem.FilePath, ImportsFileName).Reverse();
+ }
+
+ private class VirtualProjectItem : RazorProjectItem
+ {
+ private readonly byte[] _defaultImportBytes;
+
+ private VirtualProjectItem()
+ {
+ var preamble = Encoding.UTF8.GetPreamble();
+ var content = @"
+@using System
+@using System.Collections.Generic
+@using System.Linq
+@using System.Threading.Tasks
+";
+ var contentBytes = Encoding.UTF8.GetBytes(content);
+
+ _defaultImportBytes = new byte[preamble.Length + contentBytes.Length];
+ preamble.CopyTo(_defaultImportBytes, 0);
+ contentBytes.CopyTo(_defaultImportBytes, preamble.Length);
+ }
+
+ public override string BasePath => null;
+
+ public override string FilePath => null;
+
+ public override string PhysicalPath => null;
+
+ public override bool Exists => true;
+
+ public static VirtualProjectItem Instance { get; } = new VirtualProjectItem();
+
+ public override Stream Read() => new MemoryStream(_defaultImportBytes);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.AspNetCore.Blazor.Razor.Extensions/BlazorIntermediateNodeWriter.cs b/src/Microsoft.AspNetCore.Blazor.Razor.Extensions/BlazorIntermediateNodeWriter.cs
index 5f921bcfbe..5a6d7eb687 100644
--- a/src/Microsoft.AspNetCore.Blazor.Razor.Extensions/BlazorIntermediateNodeWriter.cs
+++ b/src/Microsoft.AspNetCore.Blazor.Razor.Extensions/BlazorIntermediateNodeWriter.cs
@@ -126,7 +126,7 @@ namespace Microsoft.AspNetCore.Blazor.Razor
// text to display
_scopeStack.IncrementCurrentScopeChildCount(context);
context.CodeWriter
- .WriteStartMethodInvocation($"{_scopeStack.BuilderVarName}.{nameof(RenderTreeBuilder.AddContent)}")
+ .WriteStartMethodInvocation($"{_scopeStack.BuilderVarName}.{nameof(BlazorApi.RenderTreeBuilder.AddContent)}")
.Write((_sourceSequence++).ToString())
.WriteParameterSeparator();
@@ -216,7 +216,7 @@ namespace Microsoft.AspNetCore.Blazor.Razor
// Text node
_scopeStack.IncrementCurrentScopeChildCount(context);
codeWriter
- .WriteStartMethodInvocation($"{_scopeStack.BuilderVarName}.{nameof(RenderTreeBuilder.AddContent)}")
+ .WriteStartMethodInvocation($"{_scopeStack.BuilderVarName}.{nameof(BlazorApi.RenderTreeBuilder.AddContent)}")
.Write((_sourceSequence++).ToString())
.WriteParameterSeparator()
.WriteStringLiteral(nextToken.Data)
@@ -237,14 +237,14 @@ namespace Microsoft.AspNetCore.Blazor.Razor
if (isComponent)
{
codeWriter
- .WriteStartMethodInvocation($"{_scopeStack.BuilderVarName}.{nameof(RenderTreeBuilder.OpenComponent)}<{componentTypeName}>")
+ .WriteStartMethodInvocation($"{_scopeStack.BuilderVarName}.{nameof(BlazorApi.RenderTreeBuilder.OpenComponent)}<{componentTypeName}>")
.Write((_sourceSequence++).ToString())
.WriteEndMethodInvocation();
}
else
{
codeWriter
- .WriteStartMethodInvocation($"{_scopeStack.BuilderVarName}.{nameof(RenderTreeBuilder.OpenElement)}")
+ .WriteStartMethodInvocation($"{_scopeStack.BuilderVarName}.{nameof(BlazorApi.RenderTreeBuilder.OpenElement)}")
.Write((_sourceSequence++).ToString())
.WriteParameterSeparator()
.WriteStringLiteral(nextTag.Data)
@@ -294,8 +294,8 @@ namespace Microsoft.AspNetCore.Blazor.Razor
isComponent: isComponent,
source: CalculateSourcePosition(node.Source, nextToken.Position));
var closeMethodName = isComponent
- ? nameof(RenderTreeBuilder.CloseComponent)
- : nameof(RenderTreeBuilder.CloseElement);
+ ? nameof(BlazorApi.RenderTreeBuilder.CloseComponent)
+ : nameof(BlazorApi.RenderTreeBuilder.CloseElement);
codeWriter
.WriteStartMethodInvocation($"{_scopeStack.BuilderVarName}.{closeMethodName}")
.WriteEndMethodInvocation();
@@ -334,7 +334,7 @@ namespace Microsoft.AspNetCore.Blazor.Razor
WriteAttribute(context.CodeWriter, "value", new IntermediateToken
{
Kind = TokenKind.CSharp,
- Content = $"{RenderTreeBuilder.BindMethodsGetValue}({valueParams})"
+ Content = $"{BlazorApi.BindMethods.GetValue}({valueParams})"
});
// [2] @onchange(BindSetValue(parsed => { X = parsed; }, X, Y, Z, ...))
@@ -350,7 +350,7 @@ namespace Microsoft.AspNetCore.Blazor.Razor
AttributeValue = new IntermediateToken
{
Kind = TokenKind.CSharp,
- Content = $"onchange({RenderTreeBuilder.BindMethodsSetValue}({parsedArgsJoined}))"
+ Content = $"onchange({BlazorApi.BindMethods.SetValue}({parsedArgsJoined}))"
}
};
WriteElementAttributeToken(context, tag, onChangeAttributeToken);
@@ -360,7 +360,7 @@ namespace Microsoft.AspNetCore.Blazor.Razor
// For any other attribute token (e.g., @onclick(...)), treat it as an expression
// that will evaluate as an attribute frame
context.CodeWriter
- .WriteStartMethodInvocation($"{_scopeStack.BuilderVarName}.{nameof(RenderTreeBuilder.AddAttribute)}")
+ .WriteStartMethodInvocation($"{_scopeStack.BuilderVarName}.{nameof(BlazorApi.RenderTreeBuilder.AddAttribute)}")
.Write((_sourceSequence++).ToString())
.WriteParameterSeparator()
.Write(token.AttributeValue.Content)
@@ -439,7 +439,7 @@ namespace Microsoft.AspNetCore.Blazor.Razor
public void BeginWriteAttribute(CodeWriter codeWriter, string key)
{
codeWriter
- .WriteStartMethodInvocation($"{_scopeStack.BuilderVarName}.{nameof(RenderTreeBuilder.AddAttribute)}")
+ .WriteStartMethodInvocation($"{_scopeStack.BuilderVarName}.{nameof(BlazorApi.RenderTreeBuilder.AddAttribute)}")
.Write((_sourceSequence++).ToString())
.WriteParameterSeparator()
.WriteStringLiteral(key)
diff --git a/src/Microsoft.AspNetCore.Blazor.Razor.Extensions/BlazorLoweringPhase.cs b/src/Microsoft.AspNetCore.Blazor.Razor.Extensions/BlazorLoweringPhase.cs
deleted file mode 100644
index be2583a099..0000000000
--- a/src/Microsoft.AspNetCore.Blazor.Razor.Extensions/BlazorLoweringPhase.cs
+++ /dev/null
@@ -1,75 +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.Blazor.Razor
-{
- ///
- /// A phase that builds the C# document corresponding to
- /// a for a Blazor component.
- ///
- internal class BlazorLoweringPhase : IRazorCSharpLoweringPhase
- {
- private readonly RazorCodeGenerationOptions _codegenOptions;
-
- public BlazorLoweringPhase(RazorCodeGenerationOptions codegenOptions)
- {
- _codegenOptions = codegenOptions
- ?? throw new ArgumentNullException(nameof(codegenOptions));
- }
-
- public RazorEngine Engine { get; set; }
-
- public void Execute(RazorCodeDocument codeDocument)
- {
- var writer = BlazorComponentDocumentWriter.Create(_codegenOptions);
- var documentNode = codeDocument.GetDocumentIntermediateNode();
- ConvertToBlazorPrimaryMethod(documentNode);
- var csharpDoc = writer.WriteDocument(codeDocument, documentNode);
- codeDocument.SetCSharpDocument(csharpDoc);
- }
-
- private void ConvertToBlazorPrimaryMethod(DocumentIntermediateNode documentNode)
- {
- // Replaces the default "ExecuteAsync" method with Blazor's "BuildRenderTree".
- // Note that DefaultDocumentWriter's VisitMethodDeclaration is hardcoded to
- // emit methods with no parameters, so there's no way of setting the parameters
- // from here. We inject the parameter later in RazorCompiler.
- var primaryMethod = documentNode.FindPrimaryMethod();
- primaryMethod.ReturnType = "void";
- primaryMethod.MethodName = BlazorComponent.BuildRenderTree;
- primaryMethod.Modifiers.Clear();
- primaryMethod.Modifiers.Add("protected");
- primaryMethod.Modifiers.Add("override");
-
- var line = new CSharpCodeIntermediateNode();
- line.Children.Add(new IntermediateToken
- {
- Kind = TokenKind.CSharp,
- Content = $"base.{primaryMethod.MethodName}(builder);" + Environment.NewLine
- });
- primaryMethod.Children.Insert(0, line);
- }
-
- ///
- /// Creates instances that are configured to use
- /// .
- ///
- private class BlazorComponentDocumentWriter : DocumentWriter
- {
- public static DocumentWriter Create(RazorCodeGenerationOptions options)
- => Instance.Create(new BlazorCodeTarget(), options);
-
- private static BlazorComponentDocumentWriter Instance
- = new BlazorComponentDocumentWriter();
-
- public override RazorCSharpDocument WriteDocument(
- RazorCodeDocument codeDocument, DocumentIntermediateNode documentNode)
- => throw new NotImplementedException();
- }
- }
-}
diff --git a/src/Microsoft.AspNetCore.Blazor.Razor.Extensions/BlazorRazorEngine.cs b/src/Microsoft.AspNetCore.Blazor.Razor.Extensions/BlazorRazorEngine.cs
deleted file mode 100644
index fab6f1136d..0000000000
--- a/src/Microsoft.AspNetCore.Blazor.Razor.Extensions/BlazorRazorEngine.cs
+++ /dev/null
@@ -1,46 +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.Linq;
-using Microsoft.AspNetCore.Razor.Language;
-using Microsoft.AspNetCore.Razor.Language.Extensions;
-
-namespace Microsoft.AspNetCore.Blazor.Razor
-{
- ///
- /// Wraps , configuring it to compile Blazor components.
- ///
- public class BlazorRazorEngine
- {
- private readonly RazorEngine _engine;
- private readonly RazorCodeGenerationOptions _codegenOptions;
-
- public RazorEngine Engine => _engine;
-
- public BlazorRazorEngine()
- {
- _codegenOptions = RazorCodeGenerationOptions.CreateDefault();
-
- _engine = RazorEngine.Create(configure =>
- {
- FunctionsDirective.Register(configure);
- InheritsDirective.Register(configure);
- InjectDirective.Register(configure);
- TemporaryLayoutPass.Register(configure);
- TemporaryImplementsPass.Register(configure);
-
- configure.SetBaseType(BlazorComponent.FullTypeName);
-
- configure.Phases.Remove(
- configure.Phases.OfType().Single());
- configure.Phases.Add(new BlazorLoweringPhase(_codegenOptions));
-
- configure.ConfigureClass((codeDoc, classNode) =>
- {
- configure.SetNamespace((string)codeDoc.Items[BlazorCodeDocItems.Namespace]);
- classNode.ClassName = (string)codeDoc.Items[BlazorCodeDocItems.ClassName];
- });
- });
- }
- }
-}
diff --git a/src/Microsoft.AspNetCore.Blazor.Razor.Extensions/BlazorTemplateEngine.cs b/src/Microsoft.AspNetCore.Blazor.Razor.Extensions/BlazorTemplateEngine.cs
index abba4ececf..da0450ec10 100644
--- a/src/Microsoft.AspNetCore.Blazor.Razor.Extensions/BlazorTemplateEngine.cs
+++ b/src/Microsoft.AspNetCore.Blazor.Razor.Extensions/BlazorTemplateEngine.cs
@@ -4,6 +4,7 @@
using System.IO;
using Microsoft.AspNetCore.Razor.Language;
using System.Text;
+using System.Collections.Generic;
namespace Microsoft.AspNetCore.Blazor.Razor
{
@@ -12,11 +13,26 @@ namespace Microsoft.AspNetCore.Blazor.Razor
///
public class BlazorTemplateEngine : RazorTemplateEngine
{
+ // We need to implement and register this feature for tooling support to work. Subclassing TemplateEngine
+ // doesn't work inside visual studio.
+ private readonly BlazorImportProjectFeature _feature;
+
public BlazorTemplateEngine(RazorEngine engine, RazorProject project)
: base(engine, project)
{
- Options.ImportsFileName = "_ViewImports.cshtml";
- Options.DefaultImports = GetDefaultImports();
+ _feature = new BlazorImportProjectFeature();
+
+ Options.DefaultImports = RazorSourceDocument.ReadFrom(_feature.DefaultImports);
+ }
+
+ public override IEnumerable GetImportItems(RazorProjectItem projectItem)
+ {
+ if (projectItem == null)
+ {
+ throw new System.ArgumentNullException(nameof(projectItem));
+ }
+
+ return _feature.GetHierarchicalImports(Project, projectItem);
}
private static RazorSourceDocument GetDefaultImports()
diff --git a/src/Microsoft.AspNetCore.Blazor.Razor.Extensions/ComponentDocumentClassifierPass.cs b/src/Microsoft.AspNetCore.Blazor.Razor.Extensions/ComponentDocumentClassifierPass.cs
new file mode 100644
index 0000000000..3307af5d87
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Blazor.Razor.Extensions/ComponentDocumentClassifierPass.cs
@@ -0,0 +1,94 @@
+// 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 Microsoft.AspNetCore.Razor.Language.Intermediate;
+using System;
+using System.IO;
+using System.Linq;
+
+namespace Microsoft.AspNetCore.Blazor.Razor
+{
+ internal class ComponentDocumentClassifierPass : DocumentClassifierPassBase, IRazorDocumentClassifierPass
+ {
+ protected override string DocumentKind => "Blazor.Component-0.1";
+
+ protected override bool IsMatch(RazorCodeDocument codeDocument, DocumentIntermediateNode documentNode)
+ {
+ // Treat everything as a component by default if Blazor is part of the configuration.
+ return true;
+ }
+
+ protected override void OnDocumentStructureCreated(
+ RazorCodeDocument codeDocument,
+ NamespaceDeclarationIntermediateNode @namespace,
+ ClassDeclarationIntermediateNode @class,
+ MethodDeclarationIntermediateNode method)
+ {
+ @namespace.Content = (string)codeDocument.Items[BlazorCodeDocItems.Namespace];
+ if (@namespace.Content == null)
+ {
+ @namespace.Content = "Blazor";
+ }
+
+ @class.BaseType = BlazorApi.BlazorComponent.FullTypeName;
+ @class.ClassName = (string)codeDocument.Items[BlazorCodeDocItems.ClassName];
+ if (@class.ClassName == null)
+ {
+ @class.ClassName = codeDocument.Source.FilePath == null ? null : Path.GetFileNameWithoutExtension(codeDocument.Source.FilePath);
+ }
+
+ if (@class.ClassName == null)
+ {
+ @class.ClassName = "__BlazorComponent";
+ }
+
+ @class.Modifiers.Clear();
+ @class.Modifiers.Add("public");
+
+ method.ReturnType = "void";
+ method.MethodName = BlazorApi.BlazorComponent.BuildRenderTree;
+ method.Modifiers.Clear();
+ method.Modifiers.Add("protected");
+ method.Modifiers.Add("override");
+
+ method.Parameters.Clear();
+ method.Parameters.Add(new MethodParameter()
+ {
+ ParameterName = "builder",
+ TypeName = BlazorApi.RenderTreeBuilder.FullTypeName,
+ });
+
+ // We need to call the 'base' method as the first statement.
+ var callBase = new CSharpCodeIntermediateNode();
+ callBase.Children.Add(new IntermediateToken
+ {
+ Kind = TokenKind.CSharp,
+ Content = $"base.{BlazorApi.BlazorComponent.BuildRenderTree}(builder);" + Environment.NewLine
+ });
+ method.Children.Insert(0, callBase);
+ }
+
+ #region Workaround
+ // This is a workaround for the fact that the base class doesn't provide good support
+ // for replacing the IntermediateNodeWriter when building the code target.
+ void IRazorDocumentClassifierPass.Execute(RazorCodeDocument codeDocument, DocumentIntermediateNode documentNode)
+ {
+ base.Execute(codeDocument, documentNode);
+ documentNode.Target = new BlazorCodeTarget(documentNode.Options, _targetExtensions);
+ }
+
+ protected override void OnInitialized()
+ {
+ base.OnInitialized();
+
+ var feature = Engine.Features.OfType();
+ _targetExtensions = feature.FirstOrDefault()?.TargetExtensions.ToArray() ?? EmptyExtensionArray;
+ }
+
+ private static readonly ICodeTargetExtension[] EmptyExtensionArray = new ICodeTargetExtension[0];
+ private ICodeTargetExtension[] _targetExtensions;
+ #endregion
+ }
+}
diff --git a/src/Microsoft.AspNetCore.Blazor.Razor.Extensions/InjectDirective.cs b/src/Microsoft.AspNetCore.Blazor.Razor.Extensions/InjectDirective.cs
index 610b85c88c..9d8b5dab4a 100644
--- a/src/Microsoft.AspNetCore.Blazor.Razor.Extensions/InjectDirective.cs
+++ b/src/Microsoft.AspNetCore.Blazor.Razor.Extensions/InjectDirective.cs
@@ -29,6 +29,12 @@ namespace Microsoft.AspNetCore.Blazor.Razor
builder.Description = "Inject a service from the application's service container into a property.";
});
+ public static void Register(RazorProjectEngineBuilder builder)
+ {
+ builder.AddDirective(Directive);
+ builder.Features.Add(new Pass());
+ }
+
public static void Register(IRazorEngineBuilder builder)
{
builder.AddDirective(Directive);
diff --git a/src/Microsoft.AspNetCore.Blazor.Razor.Extensions/Microsoft.AspNetCore.Blazor.Razor.Extensions.csproj b/src/Microsoft.AspNetCore.Blazor.Razor.Extensions/Microsoft.AspNetCore.Blazor.Razor.Extensions.csproj
index afcdc8d1dd..ded47d31b5 100644
--- a/src/Microsoft.AspNetCore.Blazor.Razor.Extensions/Microsoft.AspNetCore.Blazor.Razor.Extensions.csproj
+++ b/src/Microsoft.AspNetCore.Blazor.Razor.Extensions/Microsoft.AspNetCore.Blazor.Razor.Extensions.csproj
@@ -4,6 +4,9 @@
netstandard2.0
$(TargetFrameworks);net461
Microsoft.AspNetCore.Blazor.Razor
+
+
+ true
diff --git a/src/Microsoft.AspNetCore.Blazor.Razor.Extensions/Properties/AssemblyInfo.cs b/src/Microsoft.AspNetCore.Blazor.Razor.Extensions/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000000..4b642e6ea5
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Blazor.Razor.Extensions/Properties/AssemblyInfo.cs
@@ -0,0 +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 Microsoft.AspNetCore.Blazor.Razor;
+using Microsoft.AspNetCore.Razor.Language;
+
+[assembly: ProvideRazorExtensionInitializer("Blazor-0.1", typeof(BlazorExtensionInitializer))]
\ No newline at end of file
diff --git a/src/Microsoft.AspNetCore.Blazor.Razor.Extensions/RenderTreeBuilder.cs b/src/Microsoft.AspNetCore.Blazor.Razor.Extensions/RenderTreeBuilder.cs
deleted file mode 100644
index 398c5ad064..0000000000
--- a/src/Microsoft.AspNetCore.Blazor.Razor.Extensions/RenderTreeBuilder.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright (c) .NET Foundation. All rights reserved.
-// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-
-namespace Microsoft.AspNetCore.Blazor.Razor
-{
- // Constants for method names used in code-generation
- // Keep these in sync with the actual RenderTreeBuilder definitions
- internal static class RenderTreeBuilder
- {
- public static readonly string OpenElement = nameof(OpenElement);
-
- public static readonly string CloseElement = nameof(CloseElement);
-
- public static readonly string OpenComponent = nameof(OpenComponent);
-
- public static readonly string CloseComponent = nameof(CloseElement);
-
- public static readonly string AddContent = nameof(AddContent);
-
- public static readonly string AddAttribute = nameof(AddAttribute);
-
- public static readonly string Clear = nameof(Clear);
-
- public static readonly string GetFrames = nameof(GetFrames);
-
- public static readonly string ChildContent = nameof(ChildContent);
-
- public static readonly string BindMethodsGetValue = "Microsoft.AspNetCore.Blazor.Components.BindMethods.GetValue";
-
- public static readonly string BindMethodsSetValue = "Microsoft.AspNetCore.Blazor.Components.BindMethods.SetValue";
- }
-}
diff --git a/src/Microsoft.AspNetCore.Blazor.Razor.Extensions/ScopeStack.cs b/src/Microsoft.AspNetCore.Blazor.Razor.Extensions/ScopeStack.cs
index 4b8ce02301..79895409e1 100644
--- a/src/Microsoft.AspNetCore.Blazor.Razor.Extensions/ScopeStack.cs
+++ b/src/Microsoft.AspNetCore.Blazor.Razor.Extensions/ScopeStack.cs
@@ -15,8 +15,6 @@ namespace Microsoft.AspNetCore.Blazor.Razor
///
internal class ScopeStack
{
- private const string _renderFragmentTypeName = "Microsoft.AspNetCore.Blazor.RenderFragment";
-
private readonly Stack _stack = new Stack();
private int _builderVarNumber = 1;
@@ -72,9 +70,9 @@ namespace Microsoft.AspNetCore.Blazor.Razor
// When we're about to insert the first child into a component,
// it's time to open a new lambda
var blazorNodeWriter = (BlazorIntermediateNodeWriter)context.NodeWriter;
- blazorNodeWriter.BeginWriteAttribute(context.CodeWriter, RenderTreeBuilder.ChildContent);
+ blazorNodeWriter.BeginWriteAttribute(context.CodeWriter, BlazorApi.RenderTreeBuilder.ChildContent);
OffsetBuilderVarNumber(1);
- context.CodeWriter.Write($"({_renderFragmentTypeName})(");
+ context.CodeWriter.Write($"({BlazorApi.RenderFragment.FullTypeName})(");
currentScope.LambdaScope = context.CodeWriter.BuildLambda(BuilderVarName);
}
diff --git a/src/Microsoft.AspNetCore.Blazor.Razor.Extensions/Temporary/TemporaryImplementsPass.cs b/src/Microsoft.AspNetCore.Blazor.Razor.Extensions/Temporary/TemporaryImplementsPass.cs
index 8b1e50fb19..731961ef75 100644
--- a/src/Microsoft.AspNetCore.Blazor.Razor.Extensions/Temporary/TemporaryImplementsPass.cs
+++ b/src/Microsoft.AspNetCore.Blazor.Razor.Extensions/Temporary/TemporaryImplementsPass.cs
@@ -22,9 +22,14 @@ namespace Microsoft.AspNetCore.Blazor.Razor
// Captures: MyApp.Namespace.ISomeType
private const string ImplementsTokenPattern = @"\s*Implements\s*<(.+)\>\s*\(\s*\)\s*";
- public static void Register(IRazorEngineBuilder configuration)
+ public static void Register(IRazorEngineBuilder builder)
{
- configuration.Features.Add(new TemporaryImplementsPass());
+ builder.Features.Add(new TemporaryImplementsPass());
+ }
+
+ public static void Register(RazorProjectEngineBuilder builder)
+ {
+ builder.Features.Add(new TemporaryImplementsPass());
}
private TemporaryImplementsPass() : base(ImplementsTokenPattern)
diff --git a/src/Microsoft.AspNetCore.Blazor.Razor.Extensions/Temporary/TemporaryLayoutPass.cs b/src/Microsoft.AspNetCore.Blazor.Razor.Extensions/Temporary/TemporaryLayoutPass.cs
index bfc7f89c09..69ec91f3ed 100644
--- a/src/Microsoft.AspNetCore.Blazor.Razor.Extensions/Temporary/TemporaryLayoutPass.cs
+++ b/src/Microsoft.AspNetCore.Blazor.Razor.Extensions/Temporary/TemporaryLayoutPass.cs
@@ -25,9 +25,14 @@ namespace Microsoft.AspNetCore.Blazor.Razor
private const string LayoutAttributeTypeName
= "Microsoft.AspNetCore.Blazor.Layouts.LayoutAttribute";
- public static void Register(IRazorEngineBuilder configuration)
+ public static void Register(IRazorEngineBuilder builder)
{
- configuration.Features.Add(new TemporaryLayoutPass());
+ builder.Features.Add(new TemporaryLayoutPass());
+ }
+
+ public static void Register(RazorProjectEngineBuilder builder)
+ {
+ builder.Features.Add(new TemporaryLayoutPass());
}
private TemporaryLayoutPass() : base(LayoutTokenPattern)
diff --git a/src/Microsoft.AspNetCore.Blazor/Components/BlazorComponent.cs b/src/Microsoft.AspNetCore.Blazor/Components/BlazorComponent.cs
index c4b7989e14..1f947a25ed 100644
--- a/src/Microsoft.AspNetCore.Blazor/Components/BlazorComponent.cs
+++ b/src/Microsoft.AspNetCore.Blazor/Components/BlazorComponent.cs
@@ -10,7 +10,7 @@ namespace Microsoft.AspNetCore.Blazor.Components
// IMPORTANT
//
// Many of these names are used in code generation. Keep these in sync with the code generation code
- // See: src/Microsoft.AspNetCore.Blazor.Razor.Extensions/BlazorComponent.cs
+ // See: src/Microsoft.AspNetCore.Blazor.Razor.Extensions/BlazorApi.cs
// Most of the developer-facing component lifecycle concepts are encapsulated in this
// base class. The core Blazor rendering system doesn't know about them (it only knows
diff --git a/src/Microsoft.AspNetCore.Blazor/RenderTree/RenderTreeBuilder.cs b/src/Microsoft.AspNetCore.Blazor/RenderTree/RenderTreeBuilder.cs
index 4b40e9fe38..698f6ce51c 100644
--- a/src/Microsoft.AspNetCore.Blazor/RenderTree/RenderTreeBuilder.cs
+++ b/src/Microsoft.AspNetCore.Blazor/RenderTree/RenderTreeBuilder.cs
@@ -11,7 +11,7 @@ namespace Microsoft.AspNetCore.Blazor.RenderTree
// IMPORTANT
//
// Many of these names are used in code generation. Keep these in sync with the code generation code
- // See: src/Microsoft.AspNetCore.Blazor.Razor.Extensions/RenderTreeBuilder.cs
+ // See: src/Microsoft.AspNetCore.Blazor.Razor.Extensions/BlazorApi.cs
///
/// Provides methods for building a collection of entries.
diff --git a/tooling/Microsoft.VisualStudio.LanguageServices.Blazor/BlazorProjectEngineFactory.cs b/tooling/Microsoft.VisualStudio.LanguageServices.Blazor/BlazorProjectEngineFactory.cs
new file mode 100644
index 0000000000..2b283624db
--- /dev/null
+++ b/tooling/Microsoft.VisualStudio.LanguageServices.Blazor/BlazorProjectEngineFactory.cs
@@ -0,0 +1,23 @@
+// 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.Blazor.Razor;
+using Microsoft.AspNetCore.Razor.Language;
+using Microsoft.CodeAnalysis.Razor;
+
+namespace Microsoft.VisualStudio.LanguageServices.Blazor
+{
+ [ExportCustomProjectEngineFactory("Blazor-0.1", SupportsSerialization = false)]
+ internal class BlazorProjectEngineFactory : IProjectEngineFactory
+ {
+ public RazorProjectEngine Create(RazorConfiguration configuration, RazorProjectFileSystem fileSystem, Action configure)
+ {
+ return RazorProjectEngine.Create(configuration, fileSystem, b =>
+ {
+ configure?.Invoke(b);
+ new BlazorExtensionInitializer().Initialize(b);
+ });
+ }
+ }
+}