File: Language\Components\ComponentReferenceCaptureLoweringPass.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.Threading;
using Microsoft.AspNetCore.Razor.Language.Intermediate;
 
namespace Microsoft.AspNetCore.Razor.Language.Components;
 
internal sealed class ComponentReferenceCaptureLoweringPass : ComponentIntermediateNodePassBase, IRazorOptimizationPass
{
    // Run after component lowering pass
    public override int Order => 50;
 
    protected override void ExecuteCore(
        RazorCodeDocument codeDocument,
        DocumentIntermediateNode documentNode,
        CancellationToken cancellationToken)
    {
        if (!IsComponentDocument(documentNode))
        {
            return;
        }
 
        var namespaceNode = documentNode.FindPrimaryNamespace();
        var classNode = documentNode.FindPrimaryClass();
        if (namespaceNode == null || classNode == null)
        {
            // Nothing to do, bail. We can't function without the standard structure.
            return;
        }
 
        var references = documentNode.FindDescendantReferences<TagHelperDirectiveAttributeIntermediateNode>();
 
        foreach (var reference in references)
        {
            if (reference.Node.TagHelper.Kind == TagHelperKind.Ref)
            {
                RewriteUsage(reference);
            }
        }
    }
 
    private static void RewriteUsage(IntermediateNodeReference<TagHelperDirectiveAttributeIntermediateNode> reference)
    {
        var (node, parent) = reference;
 
        // If we can't get a non-empty attribute name, do nothing because there will
        // already be a diagnostic for empty values
        var identifierToken = DetermineIdentifierToken(node);
        if (identifierToken is null)
        {
            return;
        }
 
        // Determine whether this is an element capture or a component capture, and
        // if applicable the type name that will appear in the resulting capture code
        var referenceCapture = parent as ComponentIntermediateNode is { Component: { } componentTagHelper }
            ? new ReferenceCaptureIntermediateNode(identifierToken, componentTagHelper.TypeName)
            : new ReferenceCaptureIntermediateNode(identifierToken);
 
        reference.Replace(referenceCapture);
    }
 
    private static IntermediateToken? DetermineIdentifierToken(TagHelperDirectiveAttributeIntermediateNode attributeNode)
    {
        var foundToken = attributeNode.Children switch
        {
            [IntermediateToken token] => token,
            [CSharpExpressionIntermediateNode { Children: [IntermediateToken token] }] => token,
            _ => null,
        };
 
        if (foundToken is null)
        {
            return null;
        }
 
        return !foundToken.Content.IsNullOrWhiteSpace()
            ? foundToken
            : null;
    }
}