public IXamlAstNode Transform()

in src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/GroupTransformers/XamlMergeResourceGroupTransformer.cs [13:175]


    public IXamlAstNode Transform(AstGroupTransformationContext context, IXamlAstNode node)
    {
        var resourceDictionaryType = context.GetAvaloniaTypes().ResourceDictionary;
        if (node is not XamlObjectInitializationNode resourceDictionaryNode
            || resourceDictionaryNode.Type != resourceDictionaryType
            || resourceDictionaryNode.Manipulation is not XamlManipulationGroupNode resourceDictionaryManipulation)
        {
            return node;
        }

        var mergeResourceIncludeType = context.GetAvaloniaTypes().MergeResourceInclude;
        var mergeSourceNodes = new List<XamlPropertyAssignmentNode>();
        var shouldExit = false; // if any manipulation node has an error, we should stop processing further.
        foreach (var manipulationNode in resourceDictionaryManipulation.Children.ToArray())
        {
            void ProcessXamlPropertyAssignmentNode(XamlManipulationGroupNode parent, XamlPropertyAssignmentNode assignmentNode)
            {
                if (assignmentNode.Property.Name == "MergedDictionaries"
                    && assignmentNode.Values.FirstOrDefault() is XamlValueWithManipulationNode valueNode)
                {
                    if (valueNode.Type.GetClrType() == mergeResourceIncludeType)
                    {
                        if (valueNode.Manipulation is XamlObjectInitializationNode objectInitialization
                            && objectInitialization.Manipulation is XamlPropertyAssignmentNode sourceAssignmentNode)
                        {
                            parent.Children.Remove(assignmentNode);
                            mergeSourceNodes.Add(sourceAssignmentNode);
                        }
                        else
                        {
                            shouldExit = true;
                            context.ReportTransformError(
                                "Invalid MergeResourceInclude node found. Make sure that Source property is set.",
                                valueNode);
                        }
                    }
                    else if (mergeSourceNodes.Any())
                    {
                        shouldExit = true;
                        context.ReportTransformError(
                            "MergeResourceInclude should always be included last when mixing with other dictionaries inside of the ResourceDictionary.MergedDictionaries.",
                            valueNode);
                    }
                }
            }
            
            if (manipulationNode is XamlPropertyAssignmentNode singleValueAssignment)
            {
                ProcessXamlPropertyAssignmentNode(resourceDictionaryManipulation, singleValueAssignment);
            }
            else if (manipulationNode is XamlManipulationGroupNode groupNodeValues)
            {
                foreach (var groupNodeValue in groupNodeValues.Children.OfType<XamlPropertyAssignmentNode>().ToArray())
                {
                    ProcessXamlPropertyAssignmentNode(groupNodeValues, groupNodeValue);
                }
            }
        }

        if (shouldExit || !mergeSourceNodes.Any())
        {
            return node;
        }
        
        var manipulationGroup = new List<IXamlAstManipulationNode>();
        foreach (var sourceNode in mergeSourceNodes)
        {
            var (originalAssetPath, propertyNode) =
                AvaloniaXamlIncludeTransformer.ResolveSourceFromXamlInclude(context, "MergeResourceInclude", sourceNode, true);
            if (originalAssetPath is null)
            {
                return context.ReportTransformError(
                    $"Node MergeResourceInclude is unable to resolve \"{originalAssetPath}\" path.", propertyNode, node);
            }

            var targetDocument = context.Documents.FirstOrDefault(d =>
                string.Equals(d.Uri, originalAssetPath, StringComparison.InvariantCultureIgnoreCase));
            if (targetDocument?.XamlDocument.Root is not XamlValueWithManipulationNode targetDocumentRoot)
            {
                return context.ReportTransformError(
                    $"Node MergeResourceInclude is unable to resolve \"{originalAssetPath}\" path.", propertyNode, node);
            }

            var singleRootObject = ((XamlManipulationGroupNode)targetDocumentRoot.Manipulation!)
                .Children.OfType<XamlObjectInitializationNode>().Single();
            if (singleRootObject.Type != resourceDictionaryType)
            {
                return context.ReportTransformError(
                    "MergeResourceInclude can only include another ResourceDictionary", propertyNode, node);
            }
            
            manipulationGroup.Add(singleRootObject.Manipulation);

            if (targetDocument.Usage == XamlDocumentUsage.Unknown)
            {
                targetDocument.Usage = XamlDocumentUsage.Merged;
            }
        }
        
        // Order of resources is defined by ResourceDictionary.TryGetResource.
        // It is read by following priority:
        // - own resources.
        // - own theme dictionaries.
        // - merged dictionaries.
        // We need to maintain this order when we inject "compiled merged" resources.
        // Doing this by injecting merged dictionaries in the beginning, so it can be overwritten by "own resources".  
        // MergedDictionaries are read first, so we need ot inject our merged values in the beginning.
        var children = resourceDictionaryManipulation.Children;
        children.InsertRange(0, manipulationGroup);

        // Flatten resource assignments.
        for (var i = 0; i < children.Count; i++)
        {
            if (children[i] is XamlManipulationGroupNode group)
            {
                children.RemoveAt(i);
                children.AddRange(group.Children);
                i--; // step back, so new items can be reiterated.
            }
        }

        // Merge "ThemeDictionaries" as well.
        for (var i = children.Count - 1; i >= 0; i--)
        {
            if (children[i] is XamlPropertyAssignmentNode assignmentNode
                && assignmentNode.Property.Name == "ThemeDictionaries"
                && assignmentNode.Values.Count == 2
                && assignmentNode.Values[0] is {} key
                && assignmentNode.Values[1] is XamlValueWithManipulationNode
                {
                    Manipulation: XamlObjectInitializationNode
                    {
                        Manipulation: XamlManipulationGroupNode valueGroup
                    }
                })
            {
                for (var j = i - 1; j >= 0; j--)
                {
                    if (children[j] is XamlPropertyAssignmentNode sameKeyPrevAssignmentNode
                        && sameKeyPrevAssignmentNode.Property.Name == "ThemeDictionaries"
                        && sameKeyPrevAssignmentNode.Values.Count == 2
                        && sameKeyPrevAssignmentNode.Values[1] is XamlValueWithManipulationNode
                        {
                            Manipulation: XamlObjectInitializationNode
                            {
                                Manipulation: XamlManipulationGroupNode sameKeyPrevValueGroup
                            }
                        }
                        && ThemeVariantNodeEquals(context, key, sameKeyPrevAssignmentNode.Values[0]))
                    {
                        sameKeyPrevValueGroup.Children.AddRange(valueGroup.Children);
                        FixEnsureCapacityNodes(context, sameKeyPrevValueGroup);
                        children.RemoveAt(i);
                        break;
                    }
                }
            }
        }

        FixEnsureCapacityNodes(context, resourceDictionaryManipulation);

        return node;
    }