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:
parent
39c5aee9a1
commit
57a04fb178
|
|
@ -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));
|
||||
|
|
|
|||
|
|
@ -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" />
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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>());
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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];
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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))]
|
||||
|
|
@ -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";
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue