Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 8 additions & 12 deletions src/BuiltInTools/dotnet-watch/Browser/BrowserConnector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ private static ProjectKey GetProjectKey(ProjectGraphNode projectNode)
ProcessSpec processSpec,
EnvironmentVariablesBuilder environmentBuilder,
ProjectOptions projectOptions,
HotReloadProfile profile,
CancellationToken cancellationToken)
{
BrowserRefreshServer? server;
Expand All @@ -75,7 +76,7 @@ private static ProjectKey GetProjectKey(ProjectGraphNode projectNode)
hasExistingServer = _servers.TryGetValue(key, out server);
if (!hasExistingServer)
{
server = IsServerSupported(projectNode) ? new BrowserRefreshServer(context.EnvironmentOptions, context.Reporter) : null;
server = IsServerSupported(projectNode, profile) ? new BrowserRefreshServer(context.EnvironmentOptions, context.Reporter) : null;
_servers.Add(key, server);
}
}
Expand Down Expand Up @@ -271,36 +272,31 @@ private bool CanLaunchBrowser(DotNetWatchContext context, ProjectGraphNode proje
return true;
}

public bool IsServerSupported(ProjectGraphNode projectNode)
public bool IsServerSupported(ProjectGraphNode projectNode, HotReloadProfile profile)
{
if (context.EnvironmentOptions.SuppressBrowserRefresh)
{
context.Reporter.Report(MessageDescriptor.SkippingConfiguringBrowserRefresh_SuppressedViaEnvironmentVariable.ToErrorWhen(profile.RequiresBrowserRefresh), EnvironmentVariables.SuppressBrowserRefresh);
return false;
}

if (!projectNode.IsNetCoreApp(minVersion: s_minimumSupportedVersion))
{
context.Reporter.Warn(
"Skipping configuring browser-refresh middleware since the target framework version is not supported." +
" For more information see 'https://aka.ms/dotnet/watch/unsupported-tfm'.");

context.Reporter.Report(MessageDescriptor.SkippingConfiguringBrowserRefresh_TargetFrameworkNotSupported.ToErrorWhen(profile.RequiresBrowserRefresh));
return false;
}

if (!IsWebApp(projectNode))
// We only want to enable browser refresh if this is a WebApp (ASP.NET Core / Blazor app).
if (!projectNode.IsWebApp())
{
context.Reporter.Verbose("Skipping configuring browser-refresh middleware since this is not a webapp.");
context.Reporter.Report(MessageDescriptor.SkippingConfiguringBrowserRefresh_NotWebApp.ToErrorWhen(profile.RequiresBrowserRefresh));
return false;
}

context.Reporter.Report(MessageDescriptor.ConfiguredToUseBrowserRefresh);
return true;
}

// We only want to enable browser refresh if this is a WebApp (ASP.NET Core / Blazor app).
private static bool IsWebApp(ProjectGraphNode projectNode)
=> projectNode.GetCapabilities().Any(value => value is "AspNetCore" or "WebAssembly");

private LaunchSettingsProfile GetLaunchProfile(ProjectOptions projectOptions)
{
var projectDirectory = Path.GetDirectoryName(projectOptions.ProjectPath);
Expand Down
2 changes: 1 addition & 1 deletion src/BuiltInTools/dotnet-watch/DotNetWatcher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public override async Task WatchAsync(CancellationToken shutdownCancellationToke
};

var browserRefreshServer = (projectRootNode != null)
? await browserConnector.GetOrCreateBrowserRefreshServerAsync(projectRootNode, processSpec, environmentBuilder, Context.RootProjectOptions, shutdownCancellationToken)
? await browserConnector.GetOrCreateBrowserRefreshServerAsync(projectRootNode, processSpec, environmentBuilder, Context.RootProjectOptions, HotReloadProfile.Default, shutdownCancellationToken)
: null;

environmentBuilder.SetProcessEnvironmentVariables(processSpec);
Expand Down
18 changes: 16 additions & 2 deletions src/BuiltInTools/dotnet-watch/HotReload/CompilationHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -102,13 +102,21 @@ public async ValueTask StartSessionAsync(CancellationToken cancellationToken)
_reporter.Report(MessageDescriptor.HotReloadSessionStarted);
}

private static DeltaApplier CreateDeltaApplier(HotReloadProfile profile, ProjectGraphNode project, BrowserRefreshServer? browserRefreshServer, IReporter processReporter)
=> profile switch
private DeltaApplier? CreateDeltaApplier(HotReloadProfile profile, ProjectGraphNode project, BrowserRefreshServer? browserRefreshServer, IReporter processReporter)
{
if (browserRefreshServer == null && profile.RequiresBrowserRefresh)
{
// error has been reported earlier
return null;
}

return profile switch
{
HotReloadProfile.BlazorWebAssembly => new BlazorWebAssemblyDeltaApplier(processReporter, browserRefreshServer!, project),
HotReloadProfile.BlazorHosted => new BlazorWebAssemblyHostedDeltaApplier(processReporter, browserRefreshServer!, project),
_ => new DefaultDeltaApplier(processReporter),
};
}

public async Task<RunningProject?> TrackRunningProjectAsync(
ProjectGraphNode projectNode,
Expand All @@ -125,6 +133,12 @@ private static DeltaApplier CreateDeltaApplier(HotReloadProfile profile, Project
var projectPath = projectNode.ProjectInstance.FullPath;

var deltaApplier = CreateDeltaApplier(profile, projectNode, browserRefreshServer, processReporter);
if (deltaApplier == null)
{
// error already reported
return null;
}

var processExitedSource = new CancellationTokenSource();
var processCommunicationCancellationSource = CancellationTokenSource.CreateLinkedTokenSource(processExitedSource.Token, cancellationToken);

Expand Down
32 changes: 20 additions & 12 deletions src/BuiltInTools/dotnet-watch/HotReload/HotReloadProfile.cs
Original file line number Diff line number Diff line change
@@ -1,20 +1,28 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace Microsoft.DotNet.Watch
namespace Microsoft.DotNet.Watch;

internal enum HotReloadProfile
{
internal enum HotReloadProfile
{
Default,
Default,

/// <summary>
/// Blazor WebAssembly app
/// </summary>
BlazorWebAssembly,

/// <summary>
/// Blazor WebAssembly app
/// </summary>
BlazorWebAssembly,
/// <summary>
/// Blazor WebAssembly app hosted by an ASP.NET Core app.
/// </summary>
BlazorHosted,
}

/// <summary>
/// Blazor WebAssembly app hosted by an ASP.NET Core app.
/// </summary>
BlazorHosted,
internal static class HotReloadProfileExtensions
{
extension(HotReloadProfile profile)
{
public bool RequiresBrowserRefresh
=> profile is HotReloadProfile.BlazorWebAssembly or HotReloadProfile.BlazorHosted;
}
}
58 changes: 31 additions & 27 deletions src/BuiltInTools/dotnet-watch/HotReload/HotReloadProfileReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,44 +11,48 @@ internal static class HotReloadProfileReader
{
public static HotReloadProfile InferHotReloadProfile(ProjectGraphNode projectNode, IReporter reporter)
{
var queue = new Queue<ProjectGraphNode>();
queue.Enqueue(projectNode);

ProjectInstance? aspnetCoreProject = null;
if (projectNode.IsWebApp())
{
var queue = new Queue<ProjectGraphNode>();
queue.Enqueue(projectNode);

var visited = new HashSet<ProjectGraphNode>();
ProjectInstance? aspnetCoreProject = null;

while (queue.Count > 0)
{
var currentNode = queue.Dequeue();
var projectCapability = currentNode.ProjectInstance.GetItems("ProjectCapability");
var visited = new HashSet<ProjectGraphNode>();

foreach (var item in projectCapability)
while (queue.Count > 0)
{
if (item.EvaluatedInclude == "AspNetCore")
{
aspnetCoreProject = currentNode.ProjectInstance;
break;
}
else if (item.EvaluatedInclude == "WebAssembly")
var currentNode = queue.Dequeue();
var projectCapability = currentNode.ProjectInstance.GetItems("ProjectCapability");

foreach (var item in projectCapability)
{
// We saw a previous project that was AspNetCore. This must he a blazor hosted app.
if (aspnetCoreProject is not null && aspnetCoreProject != currentNode.ProjectInstance)
if (item.EvaluatedInclude == "AspNetCore")
{
reporter.Verbose($"HotReloadProfile: BlazorHosted. {aspnetCoreProject.FullPath} references BlazorWebAssembly project {currentNode.ProjectInstance.FullPath}.", emoji: "🔥");
return HotReloadProfile.BlazorHosted;
aspnetCoreProject = currentNode.ProjectInstance;
break;
}

reporter.Verbose("HotReloadProfile: BlazorWebAssembly.", emoji: "🔥");
return HotReloadProfile.BlazorWebAssembly;
if (item.EvaluatedInclude == "WebAssembly")
{
// We saw a previous project that was AspNetCore. This must he a blazor hosted app.
if (aspnetCoreProject is not null && aspnetCoreProject != currentNode.ProjectInstance)
{
reporter.Verbose($"HotReloadProfile: BlazorHosted. {aspnetCoreProject.FullPath} references BlazorWebAssembly project {currentNode.ProjectInstance.FullPath}.", emoji: "🔥");
return HotReloadProfile.BlazorHosted;
}

reporter.Verbose("HotReloadProfile: BlazorWebAssembly.", emoji: "🔥");
return HotReloadProfile.BlazorWebAssembly;
}
}
}

foreach (var project in currentNode.ProjectReferences)
{
if (visited.Add(project))
foreach (var project in currentNode.ProjectReferences)
{
queue.Enqueue(project);
if (visited.Add(project))
{
queue.Enqueue(project);
}
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/BuiltInTools/dotnet-watch/HotReload/ProjectLauncher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ public EnvironmentOptions EnvironmentOptions
}
}

var browserRefreshServer = await browserConnector.GetOrCreateBrowserRefreshServerAsync(projectNode, processSpec, environmentBuilder, projectOptions, cancellationToken);
var browserRefreshServer = await browserConnector.GetOrCreateBrowserRefreshServerAsync(projectNode, processSpec, environmentBuilder, projectOptions, profile, cancellationToken);

var arguments = new List<string>()
{
Expand Down
Loading
Loading