Adopt more of Razor Exensibility

Removes some workarounds and uses Razor extensibility in a few more
places.

_ViewImports now works in VS
This commit is contained in:
Ryan Nowak 2018-02-19 16:16:44 -08:00 committed by Steve Sanderson
parent 39c5aee9a1
commit 57a04fb178
23 changed files with 461 additions and 193 deletions

View File

@ -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));

View File

@ -31,6 +31,14 @@
<None Remove="tools\**" />
</ItemGroup>
<ItemGroup>
<!-- This is a newer version than is in the shared framework, so copy it -->
<Content Include="..\Microsoft.AspNetCore.Blazor.Razor.Extensions\bin\$(Configuration)\netstandard2.0\Microsoft.AspNetCore.Razor.Language.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Visible>false</Visible>
</Content>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Microsoft.AspNetCore.Blazor.Browser.JS\Microsoft.AspNetCore.Blazor.Browser.JS.csproj" />
<ProjectReference Include="..\Microsoft.AspNetCore.Blazor.Mono\Microsoft.AspNetCore.Blazor.Mono.csproj" />

View File

@ -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";
}
}
}

View File

@ -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
/// </summary>
internal class BlazorCodeTarget : CodeTarget
{
private readonly RazorCodeGenerationOptions _options;
public BlazorCodeTarget(RazorCodeGenerationOptions options, IEnumerable<ICodeTargetExtension> 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<TExtension>() => null;
public override TExtension GetExtension<TExtension>()
{
for (var i = 0; i < Extensions.Length; i++)
{
var match = Extensions[i] as TExtension;
if (match != null)
{
return match;
}
}
public override bool HasExtension<TExtension>() => false;
return null;
}
public override bool HasExtension<TExtension>()
{
for (var i = 0; i < Extensions.Length; i++)
{
var match = Extensions[i] as TExtension;
if (match != null)
{
return true;
}
}
return false;
}
}
}

View File

@ -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);
}
}

View File

@ -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<RazorExtension>());
}
}

View File

@ -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<IImportProjectFeature>().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;
}
}
}
}

View File

@ -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<RazorProjectItem> GetImports(RazorProjectItem projectItem)
{
if (projectItem == null)
{
throw new ArgumentNullException(nameof(projectItem));
}
var imports = new List<RazorProjectItem>()
{
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<RazorProjectItem> 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);
}
}
}

View File

@ -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)

View File

@ -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
{
/// <summary>
/// A <see cref="RazorEngine"/> phase that builds the C# document corresponding to
/// a <see cref="RazorCodeDocument"/> for a Blazor component.
/// </summary>
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);
}
/// <summary>
/// Creates <see cref="DocumentWriter"/> instances that are configured to use
/// <see cref="BlazorCodeTarget"/>.
/// </summary>
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();
}
}
}

View File

@ -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
{
/// <summary>
/// Wraps <see cref="RazorEngine"/>, configuring it to compile Blazor components.
/// </summary>
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<IRazorCSharpLoweringPhase>().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];
});
});
}
}
}

View File

@ -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
/// </summary>
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<RazorProjectItem> GetImportItems(RazorProjectItem projectItem)
{
if (projectItem == null)
{
throw new System.ArgumentNullException(nameof(projectItem));
}
return _feature.GetHierarchicalImports(Project, projectItem);
}
private static RazorSourceDocument GetDefaultImports()

View File

@ -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<IRazorTargetExtensionFeature>();
_targetExtensions = feature.FirstOrDefault()?.TargetExtensions.ToArray() ?? EmptyExtensionArray;
}
private static readonly ICodeTargetExtension[] EmptyExtensionArray = new ICodeTargetExtension[0];
private ICodeTargetExtension[] _targetExtensions;
#endregion
}
}

View File

@ -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);

View File

@ -4,6 +4,9 @@
<TargetFrameworks>netstandard2.0</TargetFrameworks>
<TargetFrameworks Condition="'$(OS)' == 'Windows_NT'">$(TargetFrameworks);net461</TargetFrameworks>
<RootNamespace>Microsoft.AspNetCore.Blazor.Razor</RootNamespace>
<!-- Copy package references to output, needed so the build project can find them -->
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup>
<ItemGroup>

View File

@ -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))]

View File

@ -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";
}
}

View File

@ -15,8 +15,6 @@ namespace Microsoft.AspNetCore.Blazor.Razor
/// </summary>
internal class ScopeStack
{
private const string _renderFragmentTypeName = "Microsoft.AspNetCore.Blazor.RenderFragment";
private readonly Stack<ScopeEntry> _stack = new Stack<ScopeEntry>();
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);
}

View File

@ -22,9 +22,14 @@ namespace Microsoft.AspNetCore.Blazor.Razor
// Captures: MyApp.Namespace.ISomeType<T1, T2>
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)

View File

@ -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)

View File

@ -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

View File

@ -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
/// <summary>
/// Provides methods for building a collection of <see cref="RenderTreeFrame"/> entries.

View File

@ -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<RazorProjectEngineBuilder> configure)
{
return RazorProjectEngine.Create(configuration, fileSystem, b =>
{
configure?.Invoke(b);
new BlazorExtensionInitializer().Initialize(b);
});
}
}
}