File: HotReload\AppModels\WebApplicationAppModel.cs
Web Access
Project: ..\..\..\src\BuiltInTools\dotnet-watch\dotnet-watch.csproj (dotnet-watch)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Collections.Immutable;
using Microsoft.Build.Graph;
using Microsoft.DotNet.HotReload;
using Microsoft.Extensions.Logging;
 
namespace Microsoft.DotNet.Watch;
 
internal abstract class WebApplicationAppModel(DotNetWatchContext context) : HotReloadAppModel
{
    // This needs to be in sync with the version BrowserRefreshMiddleware is compiled against.
    private static readonly Version s_minimumSupportedVersion = Versions.Version6_0;
    private const string MiddlewareTargetFramework = "net6.0";
 
    public DotNetWatchContext Context => context;
 
    public abstract bool RequiresBrowserRefresh { get; }
 
    /// <summary>
    /// Project that's used for launching the application.
    /// </summary>
    public abstract ProjectGraphNode LaunchingProject { get; }
 
    protected abstract HotReloadClients CreateClients(ILogger clientLogger, ILogger agentLogger, BrowserRefreshServer? browserRefreshServer);
 
    public async sealed override ValueTask<HotReloadClients?> TryCreateClientsAsync(ILogger clientLogger, ILogger agentLogger, CancellationToken cancellationToken)
    {
        var browserRefreshServer = await context.BrowserRefreshServerFactory.GetOrCreateBrowserRefreshServerAsync(LaunchingProject, this, cancellationToken);
        if (RequiresBrowserRefresh && browserRefreshServer == null)
        {
            // Error has been reported
            return null;
        }
 
        return CreateClients(clientLogger, agentLogger, browserRefreshServer);
    }
 
    protected WebAssemblyHotReloadClient CreateWebAssemblyClient(ILogger clientLogger, ILogger agentLogger, BrowserRefreshServer browserRefreshServer, ProjectGraphNode clientProject)
    {
        var capabilities = clientProject.GetWebAssemblyCapabilities().ToImmutableArray();
        var targetFramework = clientProject.GetTargetFrameworkVersion() ?? throw new InvalidOperationException("Project doesn't define TargetFrameworkVersion");
 
        return new WebAssemblyHotReloadClient(clientLogger, agentLogger, browserRefreshServer, capabilities, targetFramework, context.EnvironmentOptions.TestFlags.HasFlag(TestFlags.MockBrowser));
    }
 
    private static string GetMiddlewareAssemblyPath()
        => GetInjectedAssemblyPath(MiddlewareTargetFramework, "Microsoft.AspNetCore.Watch.BrowserRefresh");
 
    public BrowserRefreshServer? TryCreateRefreshServer(ProjectGraphNode projectNode)
    {
        var logger = context.LoggerFactory.CreateLogger(BrowserRefreshServer.ServerLogComponentName, projectNode.GetDisplayName());
 
        if (IsServerSupported(projectNode, logger))
        {
            return new BrowserRefreshServer(
                logger,
                context.LoggerFactory,
                middlewareAssemblyPath: GetMiddlewareAssemblyPath(),
                dotnetPath: context.EnvironmentOptions.MuxerPath,
                autoReloadWebSocketHostName: context.EnvironmentOptions.AutoReloadWebSocketHostName,
                autoReloadWebSocketPort: context.EnvironmentOptions.AutoReloadWebSocketPort,
                suppressTimeouts: context.EnvironmentOptions.TestFlags != TestFlags.None);
        }
 
        return null;
    }
 
    public bool IsServerSupported(ProjectGraphNode projectNode, ILogger logger)
    {
        if (context.EnvironmentOptions.SuppressBrowserRefresh)
        {
            logger.Log(MessageDescriptor.SkippingConfiguringBrowserRefresh_SuppressedViaEnvironmentVariable.WithSeverityWhen(MessageSeverity.Error, RequiresBrowserRefresh), EnvironmentVariables.Names.SuppressBrowserRefresh);
            return false;
        }
 
        if (!projectNode.IsNetCoreApp(minVersion: s_minimumSupportedVersion))
        {
            logger.Log(MessageDescriptor.SkippingConfiguringBrowserRefresh_TargetFrameworkNotSupported.WithSeverityWhen(MessageSeverity.Error, RequiresBrowserRefresh));
            return false;
        }
 
        logger.Log(MessageDescriptor.ConfiguredToUseBrowserRefresh);
        return true;
    }
}