Skip to content

Fix Hot Reload profile detection for web apps #48909

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
May 19, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
7 changes: 2 additions & 5 deletions src/BuiltInTools/dotnet-watch/Browser/BrowserConnector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,8 @@ public bool IsServerSupported(ProjectGraphNode projectNode)
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.");
return false;
Expand All @@ -297,10 +298,6 @@ public bool IsServerSupported(ProjectGraphNode projectNode)
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
4 changes: 2 additions & 2 deletions src/BuiltInTools/dotnet-watch/HotReload/CompilationHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,8 @@ public async ValueTask StartSessionAsync(CancellationToken cancellationToken)
private static DeltaApplier CreateDeltaApplier(HotReloadProfile profile, ProjectGraphNode project, BrowserRefreshServer? browserRefreshServer, IReporter processReporter)
=> profile switch
{
HotReloadProfile.BlazorWebAssembly => new BlazorWebAssemblyDeltaApplier(processReporter, browserRefreshServer!, project),
HotReloadProfile.BlazorHosted => new BlazorWebAssemblyHostedDeltaApplier(processReporter, browserRefreshServer!, project),
HotReloadProfile.BlazorWebAssembly => new BlazorWebAssemblyDeltaApplier(processReporter, browserRefreshServer ?? throw new InvalidOperationException(), project),
HotReloadProfile.BlazorHosted => new BlazorWebAssemblyHostedDeltaApplier(processReporter, browserRefreshServer ?? throw new InvalidOperationException(), project),
_ => new DefaultDeltaApplier(processReporter),
};

Expand Down
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
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ public static bool IsNetCoreApp(this ProjectGraphNode projectNode)
public static bool IsNetCoreApp(this ProjectGraphNode projectNode, Version minVersion)
=> IsNetCoreApp(projectNode) && IsTargetFrameworkVersionOrNewer(projectNode, minVersion);

public static bool IsWebApp(this ProjectGraphNode projectNode)
=> projectNode.GetCapabilities().Any(static value => value is "AspNetCore" or "WebAssembly");

public static string? GetOutputDirectory(this ProjectGraphNode projectNode)
=> projectNode.ProjectInstance.GetPropertyValue("TargetPath") is { Length: >0 } path ? Path.GetDirectoryName(Path.Combine(projectNode.ProjectInstance.Directory, path)) : null;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@
<TargetFramework>$(CurrentTargetFramework)</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<IsAspireHost>true</IsAspireHost>
<UserSecretsId>ad800ccc-954c-40cc-920b-2e09fc9eee7a</UserSecretsId>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\WatchAspire.ApiService\WatchAspire.ApiService.csproj" Watch="true" />
<ProjectReference Include="..\WatchAspire.Wasm\WatchAspire.Wasm.csproj" Watch="true" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<Router AppAssembly="@typeof(Program).Assembly" PreferExactMatches="true">
<Found Context="routeData">
<RouteView RouteData="@routeData"/>
</Found>
<NotFound>
<p>Sorry, there's nothing at this address.</p>
</NotFound>
</Router>
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
@page "/"

<h1>Hello, world!</h1>

Welcome to your new app.
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using System;

namespace standalone
{
public class Program
{
public static void Main(string[] args)
{
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": "true",
"launchBrowser": true,
"applicationUrl": "http://localhost:5000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">

<PropertyGroup>
<TargetFramework>$(CurrentTargetFramework)</TargetFramework>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="$(MicrosoftAspNetCoreAppRefPackageVersion)" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="$(MicrosoftAspNetCoreAppRefPackageVersion)" PrivateAssets="all" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
@using Microsoft.AspNetCore.Components.Routing
@using standalone
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.build { }
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<!DOCTYPE html>
<html>

<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<title>standalone</title>
<base href="/" />
<link href="css/bootstrap/bootstrap.min.css" rel="stylesheet" />
<link href="css/app.css" rel="stylesheet" />
</head>

<body>
<app>Loading...</app>

<div id="blazor-error-ui">
An unhandled error has occurred.
<a href="" class="reload">Reload</a>
<a class="dismiss">🗙</a>
</div>
<script src="_framework/blazor.webassembly.js"></script>
</body>

</html>