diff --git a/src/BuiltInTools/BrowserRefresh/WebSocketScriptInjection.js b/src/BuiltInTools/BrowserRefresh/WebSocketScriptInjection.js index 8a39b2cb55ed..3abf08327f39 100644 --- a/src/BuiltInTools/BrowserRefresh/WebSocketScriptInjection.js +++ b/src/BuiltInTools/BrowserRefresh/WebSocketScriptInjection.js @@ -99,6 +99,7 @@ setTimeout(function () { function applyBlazorDeltas(deltas) { deltas.forEach(d => window.Blazor._internal.applyHotReload(d.moduleId, d.metadataDelta, d.ilDelta)); + notifyHotReloadApplied(); } function displayDiagnostics(diagnostics) { diff --git a/src/BuiltInTools/dotnet-watch/HotReload/AspNetCoreDeltaApplier.cs b/src/BuiltInTools/dotnet-watch/HotReload/AspNetCoreDeltaApplier.cs index 22f5da05ed15..a1f2ffec2293 100644 --- a/src/BuiltInTools/dotnet-watch/HotReload/AspNetCoreDeltaApplier.cs +++ b/src/BuiltInTools/dotnet-watch/HotReload/AspNetCoreDeltaApplier.cs @@ -26,6 +26,8 @@ public AspNetCoreDeltaApplier(IReporter reporter) _reporter = reporter; } + public bool SuppressBrowserRefreshAfterApply { get; init; } + public async ValueTask InitializeAsync(DotNetWatchContext context, CancellationToken cancellationToken) { if (_pipe is not null) @@ -104,7 +106,7 @@ public async ValueTask Apply(DotNetWatchContext context, string changedFil return false; } - if (context.BrowserRefreshServer != null) + if (!SuppressBrowserRefreshAfterApply && context.BrowserRefreshServer is not null) { if (result == ApplyResult.Success_RefreshBrowser) { diff --git a/src/BuiltInTools/dotnet-watch/HotReload/BlazorWebAssemblyDeltaApplier.cs b/src/BuiltInTools/dotnet-watch/HotReload/BlazorWebAssemblyDeltaApplier.cs index 178530c85515..3691e6cb71d9 100644 --- a/src/BuiltInTools/dotnet-watch/HotReload/BlazorWebAssemblyDeltaApplier.cs +++ b/src/BuiltInTools/dotnet-watch/HotReload/BlazorWebAssemblyDeltaApplier.cs @@ -39,8 +39,8 @@ public async ValueTask Apply(DotNetWatchContext context, string changedFil Deltas = solutionUpdate.Select(c => new UpdateDelta { ModuleId = c.ModuleId, - MetadataDelta = c.MetadataDelta, - ILDelta = c.ILDelta, + MetadataDelta = c.MetadataDelta.ToArray(), + ILDelta = c.ILDelta.ToArray(), }), }; @@ -49,7 +49,18 @@ public async ValueTask Apply(DotNetWatchContext context, string changedFil return true; } - public ValueTask ReportDiagnosticsAsync(DotNetWatchContext context, IEnumerable diagnostics, CancellationToken cancellationToken) => throw new NotImplementedException(); + public async ValueTask ReportDiagnosticsAsync(DotNetWatchContext context, IEnumerable diagnostics, CancellationToken cancellationToken) + { + if (context.BrowserRefreshServer != null) + { + var message = new HotReloadDiagnostics + { + Diagnostics = diagnostics + }; + + await context.BrowserRefreshServer.SendJsonSerlialized(message, cancellationToken); + } + } public void Dispose() { @@ -65,8 +76,15 @@ private readonly struct UpdatePayload private readonly struct UpdateDelta { public Guid ModuleId { get; init; } - public ImmutableArray MetadataDelta { get; init; } - public ImmutableArray ILDelta { get; init; } + public byte[] MetadataDelta { get; init; } + public byte[] ILDelta { get; init; } + } + + public readonly struct HotReloadDiagnostics + { + public string Type => "HotReloadDiagnosticsv1"; + + public IEnumerable Diagnostics { get; init; } } } } diff --git a/src/BuiltInTools/dotnet-watch/HotReload/BlazorWebAssemblyHostedDeltaApplier.cs b/src/BuiltInTools/dotnet-watch/HotReload/BlazorWebAssemblyHostedDeltaApplier.cs new file mode 100644 index 000000000000..8544a2ef2aee --- /dev/null +++ b/src/BuiltInTools/dotnet-watch/HotReload/BlazorWebAssemblyHostedDeltaApplier.cs @@ -0,0 +1,51 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.ExternalAccess.Watch.Api; +using Microsoft.Extensions.Tools.Internal; + +namespace Microsoft.DotNet.Watcher.Tools +{ + internal class BlazorWebAssemblyHostedDeltaApplier : IDeltaApplier + { + private readonly BlazorWebAssemblyDeltaApplier _wasmApplier; + private readonly AspNetCoreDeltaApplier _hostApplier; + + public BlazorWebAssemblyHostedDeltaApplier(IReporter reporter) + { + _wasmApplier = new BlazorWebAssemblyDeltaApplier(reporter); + _hostApplier = new AspNetCoreDeltaApplier(reporter) + { + SuppressBrowserRefreshAfterApply = true, + }; + } + + public async ValueTask InitializeAsync(DotNetWatchContext context, CancellationToken cancellationToken) + { + await _wasmApplier.InitializeAsync(context, cancellationToken); + await _hostApplier.InitializeAsync(context, cancellationToken); + } + + public async ValueTask Apply(DotNetWatchContext context, string changedFile, ImmutableArray solutionUpdate, CancellationToken cancellationToken) + { + return await _hostApplier.Apply(context, changedFile, solutionUpdate, cancellationToken) && + await _wasmApplier.Apply(context, changedFile, solutionUpdate, cancellationToken); + } + + public async ValueTask ReportDiagnosticsAsync(DotNetWatchContext context, IEnumerable diagnostics, CancellationToken cancellationToken) + { + // Both WASM and Host have similar implementations for diagnostics. We could pick either to report diagnostics. + await _hostApplier.ReportDiagnosticsAsync(context, diagnostics, cancellationToken); + } + + public void Dispose() + { + _hostApplier.Dispose(); + _wasmApplier.Dispose(); + } + } +} diff --git a/src/BuiltInTools/dotnet-watch/HotReload/CompilationHandler.cs b/src/BuiltInTools/dotnet-watch/HotReload/CompilationHandler.cs index adbc3a898ec6..b3f8c19e454f 100644 --- a/src/BuiltInTools/dotnet-watch/HotReload/CompilationHandler.cs +++ b/src/BuiltInTools/dotnet-watch/HotReload/CompilationHandler.cs @@ -37,9 +37,12 @@ public async ValueTask InitializeAsync(DotNetWatchContext context, CancellationT { if (_deltaApplier is null) { - _deltaApplier = context.DefaultLaunchSettingsProfile.HotReloadProfile == "blazorwasm" ? - new BlazorWebAssemblyDeltaApplier(_reporter) : - new AspNetCoreDeltaApplier(_reporter); + _deltaApplier = context.DefaultLaunchSettingsProfile.HotReloadProfile switch + { + "blazorwasm" => new BlazorWebAssemblyDeltaApplier(_reporter), + "blazorwasmhosted" => new BlazorWebAssemblyHostedDeltaApplier(_reporter), + _ => new AspNetCoreDeltaApplier(_reporter), + }; } await _deltaApplier.InitializeAsync(context, cancellationToken);