File: UpdatePackageStaticWebAssets.cs
Web Access
Project: src\sdk\src\StaticWebAssetsSdk\Tasks\Microsoft.NET.Sdk.StaticWebAssets.Tasks.csproj (Microsoft.NET.Sdk.StaticWebAssets.Tasks)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#nullable disable

using Microsoft.AspNetCore.StaticWebAssets.Tasks.Utils;
using Microsoft.Build.Framework;

namespace Microsoft.AspNetCore.StaticWebAssets.Tasks;

public class UpdatePackageStaticWebAssets : Task
{
    [Required]
    public ITaskItem[] Assets { get; set; }

    public string IntermediateOutputPath { get; set; }

    public string ProjectPackageId { get; set; }

    public string ProjectBasePath { get; set; }

    [Output]
    public ITaskItem[] UpdatedAssets { get; set; }

    [Output]
    public ITaskItem[] OriginalAssets { get; set; }

    [Output]
    public ITaskItem[] RemappedEndpoints { get; set; }

    [Output]
    public ITaskItem[] OriginalFrameworkEndpoints { get; set; }

    public ITaskItem[] Endpoints { get; set; }

    public override bool Execute()
    {
        try
        {
            var originalAssets = new List<ITaskItem>();
            var updatedAssets = new List<ITaskItem>();
            var assetMapping = new Dictionary<string, (string NewIdentity, string OldBasePath)>(OSPath.PathComparer);

            for (var i = 0; i < Assets.Length; i++)
            {
                var candidate = Assets[i];
                var sourceType = candidate.GetMetadata(nameof(StaticWebAsset.SourceType));

                if (StaticWebAsset.SourceTypes.IsPackage(sourceType))
                {
                    originalAssets.Add(candidate);
                    updatedAssets.Add(StaticWebAsset.FromV1TaskItem(candidate).ToTaskItem());
                }
                else if (StaticWebAsset.SourceTypes.IsFramework(sourceType))
                {
                    originalAssets.Add(candidate);
                    var asset = StaticWebAsset.FromV1TaskItem(candidate);
                    var (transformed, oldPath, oldBasePath) = StaticWebAsset.MaterializeFrameworkAsset(
                        asset, IntermediateOutputPath, ProjectPackageId, ProjectBasePath, Log);
                    if (transformed != null)
                    {
                        updatedAssets.Add(transformed.ToTaskItem());
                        assetMapping[oldPath] = (transformed.Identity, oldBasePath);
                    }
                }
            }

            OriginalAssets = [.. originalAssets];
            UpdatedAssets = [.. updatedAssets];

            if (Endpoints != null && assetMapping.Count > 0)
            {
                RemapEndpoints(assetMapping);
            }
        }
        catch (Exception ex)
        {
            Log.LogError(ex.ToString());
        }

        return !Log.HasLoggedErrors;
    }

    private void RemapEndpoints(Dictionary<string, (string NewIdentity, string OldBasePath)> assetMapping)
    {
        var remappedEndpoints = new List<StaticWebAssetEndpoint>();
        var originalEndpointItems = new List<ITaskItem>();
        var parsedEndpoints = StaticWebAssetEndpoint.FromItemGroup(Endpoints);

        var endpointsByRoute = new Dictionary<string, List<(StaticWebAssetEndpoint Parsed, int OriginalIndex)>>(StringComparer.Ordinal);
        for (var i = 0; i < parsedEndpoints.Length; i++)
        {
            var endpoint = parsedEndpoints[i];
            if (!endpointsByRoute.TryGetValue(endpoint.Route, out var group))
            {
                group = [];
                endpointsByRoute[endpoint.Route] = group;
            }
            group.Add((endpoint, i));
        }

        var routeSegments = new List<PathTokenizer.Segment>();
        var basePathSegments = new List<PathTokenizer.Segment>();

        foreach (var kvp in endpointsByRoute)
        {
            var group = kvp.Value;
            var groupNeedsRemapping = false;
            foreach (var (endpoint, _) in group)
            {
                if (!string.IsNullOrEmpty(endpoint.AssetFile) && assetMapping.ContainsKey(endpoint.AssetFile))
                {
                    groupNeedsRemapping = true;
                    break;
                }
            }

            if (groupNeedsRemapping)
            {
                foreach (var (endpoint, originalIndex) in group)
                {
                    // Capture the original endpoint task item for removal.
                    originalEndpointItems.Add(Endpoints[originalIndex]);

                    if (!string.IsNullOrEmpty(endpoint.AssetFile) && assetMapping.TryGetValue(endpoint.AssetFile, out var info))
                    {
                        var oldAssetFile = endpoint.AssetFile;
                        endpoint.AssetFile = info.NewIdentity;

                        StaticWebAssetEndpoint.RemapEndpointRoute(endpoint, info.OldBasePath, ProjectBasePath, routeSegments, basePathSegments);

                        Log.LogMessage(MessageImportance.Low, "Remapped endpoint route from '{0}' to '{1}', AssetFile from '{2}' to '{3}'.",
                            kvp.Key, endpoint.Route, oldAssetFile, info.NewIdentity);
                    }
                    remappedEndpoints.Add(endpoint);
                }
            }
        }

        OriginalFrameworkEndpoints = [.. originalEndpointItems];
        RemappedEndpoints = StaticWebAssetEndpoint.ToTaskItems(remappedEndpoints);
    }

}