File: BlazorWasmAppBuilder.cs
Web Access
Project: src\src\Aspire.Hosting.Blazor\Aspire.Hosting.Blazor.csproj (Aspire.Hosting.Blazor)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Text.Json;
using Microsoft.Extensions.Logging;
 
namespace Aspire.Hosting;
 
/// <summary>
/// Builds Blazor WASM app projects and discovers their static web asset manifest paths
/// by shelling out to the dotnet CLI.
/// </summary>
internal static class BlazorWasmAppBuilder
{
    /// <summary>
    /// Builds a single WASM app via <c>dotnet build</c>.
    /// </summary>
    public static async Task<bool> BuildAsync(string projectPath, ILogger logger, CancellationToken cancellationToken)
    {
        BlazorGatewayLog.BuildStarted(logger, projectPath);
        var result = await BlazorDotNetCliRunner.RunAsync(
            projectPath,
            "build",
            [],
            machineReadableOutput: false,
            cancellationToken).ConfigureAwait(false);
 
        if (!result.Started)
        {
            BlazorGatewayLog.ProcessStartFailed(
                logger,
                result.Command,
                projectPath,
                result.StartException?.Message ?? "Process.Start returned null.");
            return false;
        }
 
        if (result.ExitCode != 0)
        {
            BlazorGatewayLog.BuildFailed(logger, projectPath, result.StandardOutput, result.StandardError);
            return false;
        }
 
        BlazorGatewayLog.BuildSucceeded(logger, Path.GetFileNameWithoutExtension(projectPath));
        return true;
    }
 
    /// <summary>
    /// Invokes the built-in <c>ResolveStaticWebAssetsConfiguration</c> MSBuild target to
    /// discover the endpoints and development manifest file paths via <c>-getProperty</c>.
    /// </summary>
    public static async Task<(string endpointsManifest, string runtimeManifest)?> GetManifestPathsAsync(
        string projectPath, ILogger logger, CancellationToken cancellationToken)
    {
        var result = await BlazorDotNetCliRunner.RunAsync(
            projectPath,
            "msbuild",
            [
                "-t:ResolveStaticWebAssetsConfiguration",
                "-getProperty:StaticWebAssetEndpointsBuildManifestPath",
                "-getProperty:StaticWebAssetDevelopmentManifestPath",
                "-nologo"
            ],
            machineReadableOutput: true,
            cancellationToken).ConfigureAwait(false);
 
        if (!result.Started)
        {
            BlazorGatewayLog.ProcessStartFailed(
                logger,
                result.Command,
                projectPath,
                result.StartException?.Message ?? "Process.Start returned null.");
            return null;
        }
 
        if (result.ExitCode != 0)
        {
            BlazorGatewayLog.MsBuildTargetFailed(logger, projectPath, result.StandardOutput, result.StandardError);
            return null;
        }
 
        MSBuildPropertiesOutput? output;
        try
        {
            output = JsonSerializer.Deserialize(result.StandardOutput.Trim(), ManifestJsonContext.Default.MSBuildPropertiesOutput);
        }
        catch (JsonException ex)
        {
            BlazorGatewayLog.ManifestJsonParseFailed(logger, projectPath, ex);
            return null;
        }
 
        var props = output?.Properties;
 
        if (props == null
            || string.IsNullOrEmpty(props.StaticWebAssetEndpointsBuildManifestPath)
            || string.IsNullOrEmpty(props.StaticWebAssetDevelopmentManifestPath))
        {
            BlazorGatewayLog.IncompleteManifestPaths(logger,
                props?.StaticWebAssetEndpointsBuildManifestPath, props?.StaticWebAssetDevelopmentManifestPath);
            return null;
        }
 
        var projectDir = Path.GetDirectoryName(projectPath)!;
        var endpoints = Path.GetFullPath(Path.Combine(projectDir, props.StaticWebAssetEndpointsBuildManifestPath));
        var runtime = Path.GetFullPath(Path.Combine(projectDir, props.StaticWebAssetDevelopmentManifestPath));
 
        return (endpoints, runtime);
    }
}