File: Mvc\CreateNewOnMetadataUpdateAttributePass.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.Linq;
using System.Threading;
using Microsoft.AspNetCore.Razor.Language;
using Microsoft.AspNetCore.Razor.Language.CodeGeneration;
using Microsoft.AspNetCore.Razor.Language.Extensions;
using Microsoft.AspNetCore.Razor.Language.Intermediate;
 
namespace Microsoft.AspNetCore.Mvc.Razor.Extensions;
 
/// <summary>
/// Applies attributes required for hot reload.
/// </summary>
internal sealed class CreateNewOnMetadataUpdateAttributePass : IntermediateNodePassBase, IRazorOptimizationPass
{
    protected override void ExecuteCore(
        RazorCodeDocument codeDocument,
        DocumentIntermediateNode documentNode,
        CancellationToken cancellationToken)
    {
        if (documentNode.DocumentKind != RazorPageDocumentClassifierPass.RazorPageDocumentKind &&
            documentNode.DocumentKind != MvcViewDocumentClassifierPass.MvcViewDocumentKind)
        {
            // Not a MVC file. Skip.
            return;
        }
 
        var @namespace = documentNode.FindPrimaryNamespace();
        var @class = documentNode.FindPrimaryClass();
 
        if (@namespace == null || @class == null)
        {
            // Nothing to do, bail. We can't function without the standard structure.
            return;
        }
 
        var classIndex = @namespace.Children.IndexOf(@class);
        if (classIndex == -1)
        {
            return;
        }
 
        var identifierFeature = Engine.GetFeatures<IMetadataIdentifierFeature>().First();
        var identifier = identifierFeature.GetIdentifier(codeDocument, codeDocument.Source);
 
        var metadataAttributeNode = new CreateNewOnMetadataUpdateAttributeIntermediateNode();
        // Metadata attributes need to be inserted right before the class declaration.
        @namespace.Children.Insert(classIndex, metadataAttributeNode);
 
        // [global:Microsoft.AspNetCore.Razor.Hosting.RazorCompiledItemMetadataAttribute("Identifier", "/Views/Home/Index.cshtml")]
        @namespace.Children.Insert(classIndex, new RazorCompiledItemMetadataAttributeIntermediateNode
        {
            Key = "Identifier",
            Value = identifier,
        });
    }
 
    internal sealed class CreateNewOnMetadataUpdateAttributeIntermediateNode : ExtensionIntermediateNode
    {
        private const string CreateNewOnMetadataUpdateAttributeName = "global::System.Runtime.CompilerServices.CreateNewOnMetadataUpdateAttribute";
 
        public override IntermediateNodeCollection Children => IntermediateNodeCollection.ReadOnly;
 
        public override void Accept(IntermediateNodeVisitor visitor)
        {
            AcceptExtensionNode(this, visitor);
        }
 
        public override void WriteNode(CodeTarget target, CodeRenderingContext context)
        {
            // [global::System.Runtime.CompilerServices.CreateNewOnMetadataUpdateAttribute]
            context.CodeWriter
                .Write("[")
                .Write(CreateNewOnMetadataUpdateAttributeName)
                .WriteLine("]");
        }
    }
}