Skip to content

Commit

Permalink
Add support for hot reload to hosted BlazorWasm apps
Browse files Browse the repository at this point in the history
* Fix a bug where deltas where being sent in the wrong format to BlazorWASM
* Allow recovering from compilation errors in a BlazorWASM app.

Fixes dotnet/aspnetcore#30815
  • Loading branch information
pranavkm committed Apr 1, 2021
1 parent 2e70c80 commit 40e38d8
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -104,7 +106,7 @@ public async ValueTask<bool> Apply(DotNetWatchContext context, string changedFil
return false;
}

if (context.BrowserRefreshServer != null)
if (!SuppressBrowserRefreshAfterApply && context.BrowserRefreshServer is not null)
{
if (result == ApplyResult.Success_RefreshBrowser)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ public async ValueTask<bool> 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(),
}),
};

Expand All @@ -49,7 +49,18 @@ public async ValueTask<bool> Apply(DotNetWatchContext context, string changedFil
return true;
}

public ValueTask ReportDiagnosticsAsync(DotNetWatchContext context, IEnumerable<string> diagnostics, CancellationToken cancellationToken) => throw new NotImplementedException();
public async ValueTask ReportDiagnosticsAsync(DotNetWatchContext context, IEnumerable<string> diagnostics, CancellationToken cancellationToken)
{
if (context.BrowserRefreshServer != null)
{
var message = new HotReloadDiagnostics
{
Diagnostics = diagnostics
};

await context.BrowserRefreshServer.SendJsonSerlialized(message, cancellationToken);
}
}

public void Dispose()
{
Expand All @@ -65,8 +76,15 @@ private readonly struct UpdatePayload
private readonly struct UpdateDelta
{
public Guid ModuleId { get; init; }
public ImmutableArray<byte> MetadataDelta { get; init; }
public ImmutableArray<byte> ILDelta { get; init; }
public byte[] MetadataDelta { get; init; }
public byte[] ILDelta { get; init; }
}

public readonly struct HotReloadDiagnostics
{
public string Type => "HotReloadDiagnosticsv1";

public IEnumerable<string> Diagnostics { get; init; }
}
}
}
Original file line number Diff line number Diff line change
@@ -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<bool> Apply(DotNetWatchContext context, string changedFile, ImmutableArray<WatchHotReloadService.Update> 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<string> 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();
}
}
}
9 changes: 6 additions & 3 deletions src/BuiltInTools/dotnet-watch/HotReload/CompilationHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down

0 comments on commit 40e38d8

Please sign in to comment.