File: Mvc\ViewComponentTagHelperTargetExtension.cs
Web Access
Project: src\src\Razor\src\Compiler\Microsoft.CodeAnalysis.Razor.Compiler\src\Microsoft.CodeAnalysis.Razor.Compiler.csproj (Microsoft.CodeAnalysis.Razor.Compiler)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.Collections.Immutable;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.Language.CodeGeneration;
using Microsoft.AspNetCore.Razor.Language.Intermediate;
 
namespace Microsoft.AspNetCore.Mvc.Razor.Extensions;
 
internal sealed class ViewComponentTagHelperTargetExtension : ViewComponentTagHelperTargetExtensionBase
{
    private ImmutableArray<MethodParameter> _processInvokeAsyncArgsMethodParameters;
 
    private ImmutableArray<MethodParameter> ProcessInvokeAsyncArgsMethodParameters
    {
        get
        {
            if (_processInvokeAsyncArgsMethodParameters.IsDefault)
            {
                ImmutableInterlocked.InterlockedInitialize(ref _processInvokeAsyncArgsMethodParameters,
                    [new(TagHelperContextVariableName, ViewComponentsApi.TagHelperContext.FullTypeName)]);
            }
 
            return _processInvokeAsyncArgsMethodParameters;
        }
    }
 
    protected override string TagHelperContentVariableName => "__helperContent";
    protected override string TagHelperContextVariableName => "__context";
    protected override string TagHelperOutputVariableName => "__output";
    protected override string ViewComponentHelperVariableName => "__helper";
 
    protected override void WriteAdditionalMembers(CodeRenderingContext context, ViewComponentTagHelperIntermediateNode node)
    {
        // We pre-process the arguments passed to `InvokeAsync` to ensure that the
        // provided markup attributes (in kebab-case) are matched to the associated
        // properties in the VCTH class.
        WriteProcessInvokeAsyncArgsMethodString(context.CodeWriter, node.TagHelper);
    }
 
    private void WriteProcessInvokeAsyncArgsMethodString(CodeWriter writer, TagHelperDescriptor tagHelper)
    {
        const string DictionaryType = "Dictionary<string, object>";
 
        using (writer.BuildMethodDeclaration(
            CommonModifiers.Private,
            DictionaryType,
            ViewComponentsApi.ProcessInvokeAsyncArgsMethodName,
            ProcessInvokeAsyncArgsMethodParameters))
        {
            writer.WriteStartAssignment($"{DictionaryType} args")
                .WriteStartNewObject(DictionaryType)
                .WriteEndMethodInvocation();
 
            foreach (var attribute in tagHelper.BoundAttributes)
            {
                var attributeName = attribute.Name;
                var parameterName = attribute.PropertyName;
 
                writer.WriteLine($"if (__context.AllAttributes.ContainsName(\"{attributeName}\"))");
                using (writer.BuildScope())
                {
                    writer.WriteLine($"args[nameof({parameterName})] = {parameterName};");
                }
            }
 
            writer.WriteLine("return args;");
        }
    }
 
    protected override ImmutableArray<string> GetInvokeArguments(TagHelperDescriptor tagHelper)
    {
        var viewComponentName = tagHelper.ViewComponentName;
 
        return [$"\"{viewComponentName}\"", $"{ViewComponentsApi.ProcessInvokeAsyncArgsMethodName}({TagHelperContextVariableName})"];
    }
}