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
12 changes: 6 additions & 6 deletions src/BuiltInTools/dotnet-watch/Browser/BrowserConnector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ private static ProjectKey GetProjectKey(ProjectGraphNode projectNode)
ProcessSpec processSpec,
EnvironmentVariablesBuilder environmentBuilder,
ProjectOptions projectOptions,
HotReloadProfile profile,
HotReloadAppModel appModel,
CancellationToken cancellationToken)
{
BrowserRefreshServer? server;
Expand All @@ -76,7 +76,7 @@ private static ProjectKey GetProjectKey(ProjectGraphNode projectNode)
hasExistingServer = _servers.TryGetValue(key, out server);
if (!hasExistingServer)
{
server = IsServerSupported(projectNode, profile) ? new BrowserRefreshServer(context.EnvironmentOptions, context.Reporter) : null;
server = IsServerSupported(projectNode, appModel) ? new BrowserRefreshServer(context.EnvironmentOptions, context.Reporter) : null;
_servers.Add(key, server);
}
}
Expand Down Expand Up @@ -272,24 +272,24 @@ private bool CanLaunchBrowser(DotNetWatchContext context, ProjectGraphNode proje
return true;
}

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

if (!projectNode.IsNetCoreApp(minVersion: s_minimumSupportedVersion))
{
context.Reporter.Report(MessageDescriptor.SkippingConfiguringBrowserRefresh_TargetFrameworkNotSupported.ToErrorWhen(profile.RequiresBrowserRefresh));
context.Reporter.Report(MessageDescriptor.SkippingConfiguringBrowserRefresh_TargetFrameworkNotSupported.ToErrorWhen(appModel.RequiresBrowserRefresh));
return false;
}

// We only want to enable browser refresh if this is a WebApp (ASP.NET Core / Blazor app).
if (!projectNode.IsWebApp())
{
context.Reporter.Report(MessageDescriptor.SkippingConfiguringBrowserRefresh_NotWebApp.ToErrorWhen(profile.RequiresBrowserRefresh));
context.Reporter.Report(MessageDescriptor.SkippingConfiguringBrowserRefresh_NotWebApp.ToErrorWhen(appModel.RequiresBrowserRefresh));
return false;
}

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, HotReloadProfile.Default, shutdownCancellationToken)
? await browserConnector.GetOrCreateBrowserRefreshServerAsync(projectRootNode, processSpec, environmentBuilder, Context.RootProjectOptions, DefaultAppModel.Instance, shutdownCancellationToken)
: null;

environmentBuilder.SetProcessEnvironmentVariables(processSpec);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.Build.Graph;

namespace Microsoft.DotNet.Watch;

internal abstract partial class HotReloadAppModel
{
/// <summary>
/// Blazor client-only WebAssembly app.
/// </summary>
internal sealed class BlazorWebAssemblyAppModel(ProjectGraphNode clientProject) : HotReloadAppModel
{
public override bool RequiresBrowserRefresh => true;

/// <summary>
/// Blazor WASM does not need dotnet applier as all changes are applied in the browser,
/// the process being launched is a dev server.
/// </summary>
public override bool InjectDeltaApplier => false;

public override DeltaApplier? CreateDeltaApplier(BrowserRefreshServer? browserRefreshServer, IReporter processReporter)
{
if (browserRefreshServer == null)
{
// error has been reported earlier
return null;
}

return new BlazorWebAssemblyDeltaApplier(processReporter, browserRefreshServer, clientProject);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public override Task<ImmutableArray<string>> GetApplyUpdateCapabilitiesAsync(Can
{
var targetFramework = project.GetTargetFrameworkVersion();

Reporter.Verbose($"Using capabilities based on target framework: '{targetFramework}'.");
Reporter.Verbose($"Using capabilities based on project '{project.GetDisplayName()}' target framework: '{targetFramework}'.");

capabilities = targetFramework?.Major switch
{
Expand All @@ -61,7 +61,7 @@ public override Task<ImmutableArray<string>> GetApplyUpdateCapabilitiesAsync(Can
}
else
{
Reporter.Verbose($"Project specifies capabilities.");
Reporter.Verbose($"Project '{project.GetDisplayName()}' specifies capabilities: '{string.Join(' ', capabilities)}'");
}

return Task.FromResult(capabilities);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.Build.Graph;

namespace Microsoft.DotNet.Watch;

internal abstract partial class HotReloadAppModel
{
/// <summary>
/// Blazor WebAssembly app hosted by an ASP.NET Core app.
/// App has a client and server projects and deltas are applied to both processes.
/// </summary>
internal sealed class BlazorWebAssemblyHostedAppModel(ProjectGraphNode clientProject) : HotReloadAppModel
{
public override bool RequiresBrowserRefresh => true;
public override bool InjectDeltaApplier => true;

public override DeltaApplier? CreateDeltaApplier(BrowserRefreshServer? browserRefreshServer, IReporter processReporter)
{
if (browserRefreshServer == null)
{
// error has been reported earlier
return null;
}

return new BlazorWebAssemblyHostedDeltaApplier(processReporter, browserRefreshServer, clientProject);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@

namespace Microsoft.DotNet.Watch
{
internal sealed class BlazorWebAssemblyHostedDeltaApplier(IReporter reporter, BrowserRefreshServer browserRefreshServer, ProjectGraphNode project) : DeltaApplier(reporter)
internal sealed class BlazorWebAssemblyHostedDeltaApplier(IReporter reporter, BrowserRefreshServer browserRefreshServer, ProjectGraphNode clientProject) : DeltaApplier(reporter)
{
private readonly BlazorWebAssemblyDeltaApplier _wasmApplier = new(reporter, browserRefreshServer, project);
private readonly BlazorWebAssemblyDeltaApplier _wasmApplier = new(reporter, browserRefreshServer, clientProject);
private readonly DefaultDeltaApplier _hostApplier = new(reporter);

public override void Dispose()
Expand Down
20 changes: 2 additions & 18 deletions src/BuiltInTools/dotnet-watch/HotReload/CompilationHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -100,26 +100,10 @@ public async ValueTask StartSessionAsync(CancellationToken cancellationToken)
_reporter.Report(MessageDescriptor.HotReloadSessionStarted);
}

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,
ProjectOptions projectOptions,
HotReloadProfile profile,
HotReloadAppModel appModel,
string namedPipeName,
BrowserRefreshServer? browserRefreshServer,
ProcessSpec processSpec,
Expand All @@ -130,7 +114,7 @@ public async ValueTask StartSessionAsync(CancellationToken cancellationToken)
{
var projectPath = projectNode.ProjectInstance.FullPath;

var deltaApplier = CreateDeltaApplier(profile, projectNode, browserRefreshServer, processReporter);
var deltaApplier = appModel.CreateDeltaApplier(browserRefreshServer, processReporter);
if (deltaApplier == null)
{
// error already reported
Expand Down
18 changes: 18 additions & 0 deletions src/BuiltInTools/dotnet-watch/HotReload/DefaultAppModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// 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;

/// <summary>
/// Default model.
/// </summary>
internal sealed class DefaultAppModel : HotReloadAppModel
{
public static readonly DefaultAppModel Instance = new();

public override bool RequiresBrowserRefresh => false;
public override bool InjectDeltaApplier => true;

public override DeltaApplier? CreateDeltaApplier(BrowserRefreshServer? browserRefreshServer, IReporter processReporter)
=> new DefaultDeltaApplier(processReporter);
}
71 changes: 71 additions & 0 deletions src/BuiltInTools/dotnet-watch/HotReload/HotReloadAppModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.Build.Execution;
using Microsoft.Build.Graph;

namespace Microsoft.DotNet.Watch;

internal abstract partial class HotReloadAppModel
{
public abstract bool RequiresBrowserRefresh { get; }

/// <summary>
/// True to inject delta applier to the process.
/// </summary>
public abstract bool InjectDeltaApplier { get; }

public abstract DeltaApplier? CreateDeltaApplier(BrowserRefreshServer? browserRefreshServer, IReporter processReporter);

public static HotReloadAppModel InferFromProject(ProjectGraphNode projectNode, IReporter reporter)
{
if (projectNode.IsWebApp())
{
var queue = new Queue<ProjectGraphNode>();
queue.Enqueue(projectNode);

ProjectInstance? aspnetCoreProject = null;

var visited = new HashSet<ProjectGraphNode>();

while (queue.Count > 0)
{
var currentNode = queue.Dequeue();
var projectCapability = currentNode.ProjectInstance.GetItems("ProjectCapability");

foreach (var item in projectCapability)
{
if (item.EvaluatedInclude == "AspNetCore")
{
aspnetCoreProject = currentNode.ProjectInstance;
break;
}

if (item.EvaluatedInclude == "WebAssembly")
{
// We saw a previous project that was AspNetCore. This must be 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 new BlazorWebAssemblyHostedAppModel(clientProject: currentNode);
}

reporter.Verbose("HotReloadProfile: BlazorWebAssembly.", emoji: "🔥");
return new BlazorWebAssemblyAppModel(clientProject: currentNode);
}
}

foreach (var project in currentNode.ProjectReferences)
{
if (visited.Add(project))
{
queue.Enqueue(project);
}
}
}
}

reporter.Verbose("HotReloadProfile: Default.", emoji: "🔥");
return DefaultAppModel.Instance;
}
}
28 changes: 0 additions & 28 deletions src/BuiltInTools/dotnet-watch/HotReload/HotReloadProfile.cs

This file was deleted.

64 changes: 0 additions & 64 deletions src/BuiltInTools/dotnet-watch/HotReload/HotReloadProfileReader.cs

This file was deleted.

Loading
Loading